forked from enlightenment/efl
efl: add eina_file_copy()
it's useful to copy file from one place to another and this will be used in eio' s implementation. NOTE: did not use mmap here as mmap faults may be cumbersome to handle (Eina_File itself does that, but in a nasty way) and the implementation would be severely different as there is no Eina_File from FD, and there is no way to inject custom memory/fd into the Eina_File's fault handling. The performance would not be that different anyways and the splice() is already in there for systems with good performance (read: Linux). SVN revision: 81942
This commit is contained in:
parent
678727aae4
commit
6ae6f925a4
|
@ -4,6 +4,7 @@
|
||||||
* Added eina_xattr_fd_get(), eina_xattr_fd_set(),
|
* Added eina_xattr_fd_get(), eina_xattr_fd_set(),
|
||||||
eina_xattr_del(), eina_xattr_fd_del(), eina_xattr_copy() and
|
eina_xattr_del(), eina_xattr_fd_del(), eina_xattr_copy() and
|
||||||
eina_xattr_fd_copy()
|
eina_xattr_fd_copy()
|
||||||
|
* Added eina_file_copy()
|
||||||
|
|
||||||
2012-12-24 Mike Blumenkrantz
|
2012-12-24 Mike Blumenkrantz
|
||||||
|
|
||||||
|
|
1
NEWS
1
NEWS
|
@ -46,6 +46,7 @@ Additions:
|
||||||
* Added eina_xattr_fd_get(), eina_xattr_fd_set(),
|
* Added eina_xattr_fd_get(), eina_xattr_fd_set(),
|
||||||
eina_xattr_del(), eina_xattr_fd_del(), eina_xattr_copy() and
|
eina_xattr_del(), eina_xattr_fd_del(), eina_xattr_copy() and
|
||||||
eina_xattr_fd_copy()
|
eina_xattr_fd_copy()
|
||||||
|
* Added eina_file_copy()
|
||||||
|
|
||||||
Deprecations:
|
Deprecations:
|
||||||
* ecore_x:
|
* ecore_x:
|
||||||
|
|
|
@ -13,6 +13,7 @@ eina_array_01.c \
|
||||||
eina_array_02.c \
|
eina_array_02.c \
|
||||||
eina_error_01.c \
|
eina_error_01.c \
|
||||||
eina_file_01.c \
|
eina_file_01.c \
|
||||||
|
eina_file_02.c \
|
||||||
eina_hash_01.c \
|
eina_hash_01.c \
|
||||||
eina_hash_02.c \
|
eina_hash_02.c \
|
||||||
eina_hash_03.c \
|
eina_hash_03.c \
|
||||||
|
@ -56,6 +57,7 @@ eina_array_01 \
|
||||||
eina_array_02 \
|
eina_array_02 \
|
||||||
eina_error_01 \
|
eina_error_01 \
|
||||||
eina_file_01 \
|
eina_file_01 \
|
||||||
|
eina_file_02 \
|
||||||
eina_hash_01 \
|
eina_hash_01 \
|
||||||
eina_hash_02 \
|
eina_hash_02 \
|
||||||
eina_hash_03 \
|
eina_hash_03 \
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
//Compile with:
|
||||||
|
//gcc -g eina_file_02.c -o eina_file_02 `pkg-config --cflags --libs eina`
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <Eina.h>
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_progress_cb(void *data, unsigned long long done, unsigned long long total)
|
||||||
|
{
|
||||||
|
const char **files = data;
|
||||||
|
printf("%5llu/%llu of copy '%s' to '%s'\n", done, total, files[0], files[1]);
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Eina_Bool ret;
|
||||||
|
|
||||||
|
if (argc != 3)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <src> <dst>\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
eina_init();
|
||||||
|
|
||||||
|
ret = eina_file_copy(argv[1], argv[2],
|
||||||
|
EINA_FILE_COPY_PERMISSION | EINA_FILE_COPY_XATTR,
|
||||||
|
_progress_cb, argv + 1);
|
||||||
|
|
||||||
|
printf("copy finished: %s\n", ret ? "success" : "failure");
|
||||||
|
|
||||||
|
eina_shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -54,6 +54,7 @@ void *alloca (size_t);
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#define PATH_DELIM '/'
|
#define PATH_DELIM '/'
|
||||||
|
#define COPY_BLOCKSIZE (4 * 1024 * 1024)
|
||||||
|
|
||||||
#include "eina_config.h"
|
#include "eina_config.h"
|
||||||
#include "eina_private.h"
|
#include "eina_private.h"
|
||||||
|
@ -95,6 +96,11 @@ void *alloca (size_t);
|
||||||
#endif
|
#endif
|
||||||
#define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
|
#define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
|
||||||
|
|
||||||
|
#ifdef INF
|
||||||
|
#undef INF
|
||||||
|
#endif
|
||||||
|
#define INF(...) EINA_LOG_DOM_INFO(_eina_file_log_dom, __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef DBG
|
#ifdef DBG
|
||||||
#undef DBG
|
#undef DBG
|
||||||
#endif
|
#endif
|
||||||
|
@ -1518,3 +1524,256 @@ eina_file_statat(void *container, Eina_File_Direct_Info *info, Eina_Stat *st)
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_eina_file_copy_write_internal(int fd, char *buf, size_t size)
|
||||||
|
{
|
||||||
|
size_t done = 0;
|
||||||
|
while (done < size)
|
||||||
|
{
|
||||||
|
ssize_t w = write(fd, buf + done, size - done);
|
||||||
|
if (w >= 0)
|
||||||
|
done += w;
|
||||||
|
else if ((errno != EAGAIN) && (errno != EINTR))
|
||||||
|
{
|
||||||
|
ERR("Error writing destination file during copy: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_eina_file_copy_read_internal(int fd, char *buf, off_t bufsize, ssize_t *readsize)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ssize_t r = read(fd, buf, bufsize);
|
||||||
|
if (r == 0)
|
||||||
|
{
|
||||||
|
ERR("Premature end of source file during copy.");
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
else if (r < 0)
|
||||||
|
{
|
||||||
|
if ((errno != EAGAIN) && (errno != EINTR))
|
||||||
|
{
|
||||||
|
ERR("Error reading source file during copy: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*readsize = r;
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_eina_file_copy_write_splice_internal(int fd, int pipefd, size_t size)
|
||||||
|
{
|
||||||
|
size_t done = 0;
|
||||||
|
while (done < size)
|
||||||
|
{
|
||||||
|
ssize_t w = splice(pipefd, NULL, fd, NULL, size - done, SPLICE_F_MORE);
|
||||||
|
if (w >= 0)
|
||||||
|
done += w;
|
||||||
|
else if (errno == EINVAL)
|
||||||
|
{
|
||||||
|
INF("Splicing is not supported for destination file");
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
else if ((errno != EAGAIN) && (errno != EINTR))
|
||||||
|
{
|
||||||
|
ERR("Error splicing to destination file during copy: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_eina_file_copy_read_splice_internal(int fd, int pipefd, off_t bufsize, ssize_t *readsize)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ssize_t r = splice(fd, NULL, pipefd, NULL, bufsize, SPLICE_F_MORE);
|
||||||
|
if (r == 0)
|
||||||
|
{
|
||||||
|
ERR("Premature end of source file during splice.");
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
else if (r < 0)
|
||||||
|
{
|
||||||
|
if (errno == EINVAL)
|
||||||
|
{
|
||||||
|
INF("Splicing is not supported for source file");
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
else if ((errno != EAGAIN) && (errno != EINTR))
|
||||||
|
{
|
||||||
|
ERR("Error splicing from source file during copy: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*readsize = r;
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_eina_file_copy_splice_internal(int s, int d, off_t total, Eina_File_Copy_Progress cb, const void *cb_data, Eina_Bool *splice_unsupported)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SPLICE
|
||||||
|
off_t bufsize = COPY_BLOCKSIZE;
|
||||||
|
off_t done;
|
||||||
|
Eina_Bool ret;
|
||||||
|
int pipefd[2];
|
||||||
|
|
||||||
|
*splice_unsupported = EINA_TRUE;
|
||||||
|
|
||||||
|
if (pipe(pipefd) < 0) return EINA_FALSE;
|
||||||
|
|
||||||
|
done = 0;
|
||||||
|
ret = EINA_TRUE;
|
||||||
|
while (done < total)
|
||||||
|
{
|
||||||
|
size_t todo;
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
if (done + bufsize < total)
|
||||||
|
todo = bufsize;
|
||||||
|
else
|
||||||
|
todo = total - done;
|
||||||
|
|
||||||
|
printf("loop done=%lld, total=%lld, todo=%zd\n", done, total, todo);
|
||||||
|
|
||||||
|
ret = _eina_file_copy_read_splice_internal(s, pipefd[1], todo, &r);
|
||||||
|
if (!ret) break;
|
||||||
|
|
||||||
|
ret = _eina_file_copy_write_splice_internal(d, pipefd[0], r);
|
||||||
|
if (!ret) break;
|
||||||
|
|
||||||
|
*splice_unsupported = EINA_FALSE;
|
||||||
|
done += r;
|
||||||
|
|
||||||
|
if (cb)
|
||||||
|
{
|
||||||
|
ret = cb((void *)cb_data, done, total);
|
||||||
|
if (!ret) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(pipefd[0]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
*splice_unsupported = EINA_TRUE;
|
||||||
|
return EINA_FALSE;
|
||||||
|
(void)s;
|
||||||
|
(void)d;
|
||||||
|
(void)total;
|
||||||
|
(void)cb;
|
||||||
|
(void)cb_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_eina_file_copy_internal(int s, int d, off_t total, Eina_File_Copy_Progress cb, const void *cb_data)
|
||||||
|
{
|
||||||
|
void *buf = NULL;
|
||||||
|
off_t bufsize = COPY_BLOCKSIZE;
|
||||||
|
off_t done;
|
||||||
|
Eina_Bool ret, splice_unsupported;
|
||||||
|
|
||||||
|
ret = _eina_file_copy_splice_internal(s, d, total, cb, cb_data,
|
||||||
|
&splice_unsupported);
|
||||||
|
if (ret)
|
||||||
|
return EINA_TRUE;
|
||||||
|
else if (!splice_unsupported) /* splice works, but copy failed anyway */
|
||||||
|
return EINA_FALSE;
|
||||||
|
|
||||||
|
/* make sure splice didn't change the position */
|
||||||
|
lseek(s, 0, SEEK_SET);
|
||||||
|
lseek(d, 0, SEEK_SET);
|
||||||
|
|
||||||
|
while ((bufsize > 0) && ((buf = malloc(bufsize)) == NULL))
|
||||||
|
bufsize /= 128;
|
||||||
|
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(buf, EINA_FALSE);
|
||||||
|
|
||||||
|
done = 0;
|
||||||
|
ret = EINA_TRUE;
|
||||||
|
while (done < total)
|
||||||
|
{
|
||||||
|
size_t todo;
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
if (done + bufsize < total)
|
||||||
|
todo = bufsize;
|
||||||
|
else
|
||||||
|
todo = total - done;
|
||||||
|
|
||||||
|
ret = _eina_file_copy_read_internal(s, buf, todo, &r);
|
||||||
|
if (!ret) break;
|
||||||
|
|
||||||
|
ret = _eina_file_copy_write_internal(d, buf, r);
|
||||||
|
if (!ret) break;
|
||||||
|
|
||||||
|
done += r;
|
||||||
|
|
||||||
|
if (cb)
|
||||||
|
{
|
||||||
|
ret = cb((void *)cb_data, done, total);
|
||||||
|
if (!ret) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
EAPI Eina_Bool
|
||||||
|
eina_file_copy(const char *src, const char *dst, Eina_File_Copy_Flags flags, Eina_File_Copy_Progress cb, const void *cb_data)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
int s, d;
|
||||||
|
Eina_Bool success;
|
||||||
|
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
|
||||||
|
|
||||||
|
s = open(src, O_RDONLY);
|
||||||
|
EINA_SAFETY_ON_TRUE_RETURN_VAL (s < 0, EINA_FALSE);
|
||||||
|
|
||||||
|
success = (fstat(s, &st) == 0);
|
||||||
|
EINA_SAFETY_ON_FALSE_GOTO(success, end);
|
||||||
|
|
||||||
|
d = open(dst, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||||
|
EINA_SAFETY_ON_TRUE_GOTO(d < 0, end);
|
||||||
|
|
||||||
|
success = _eina_file_copy_internal(s, d, st.st_size, cb, cb_data);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
if (flags & EINA_FILE_COPY_PERMISSION)
|
||||||
|
fchmod(d, st.st_mode);
|
||||||
|
if (flags & EINA_FILE_COPY_XATTR)
|
||||||
|
eina_xattr_fd_copy(s, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
close(s);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
unlink(dst);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
|
@ -369,6 +369,46 @@ EAPI Eina_Iterator *eina_file_direct_ls(const char *dir) EINA_WARN_UNUSED_RESULT
|
||||||
*/
|
*/
|
||||||
EAPI char *eina_file_path_sanitize(const char *path);
|
EAPI char *eina_file_path_sanitize(const char *path);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Eina_File_Copy_Progress
|
||||||
|
* used to report progress during eina_file_copy(), where @c done
|
||||||
|
* is the bytes already copied and @c size is the total file size.
|
||||||
|
*
|
||||||
|
* If returns #EINA_FALSE, it will stop the copy.
|
||||||
|
*/
|
||||||
|
typedef Eina_Bool (*Eina_File_Copy_Progress)(void *data, unsigned long long done, unsigned long long total);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Eina_File_Copy_Flags
|
||||||
|
* what to copy from file.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
EINA_FILE_COPY_DATA = 0,
|
||||||
|
EINA_FILE_COPY_PERMISSION = (1 << 0),
|
||||||
|
EINA_FILE_COPY_XATTR = (1 << 1)
|
||||||
|
} Eina_File_Copy_Flags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy one file to another using the fastest possible way, report progress.
|
||||||
|
*
|
||||||
|
* This function will try splice if it is available. It
|
||||||
|
* will block until the whole file is copied or it fails.
|
||||||
|
*
|
||||||
|
* During the progress it may call back @a cb with the progress summary.
|
||||||
|
*
|
||||||
|
* @param src the source file.
|
||||||
|
* @param dst the destination file.
|
||||||
|
* @param flags controls what is copied (data is always copied).
|
||||||
|
* @param cb if provided will be called with file copy progress information.
|
||||||
|
* @param cb_data context data to provide to @a cb during copy.
|
||||||
|
* @return #EINA_TRUE on success, #EINA_FALSE otherwise (and @a dst
|
||||||
|
* will be deleted)
|
||||||
|
*/
|
||||||
|
EAPI Eina_Bool eina_file_copy(const char *src, const char *dst, Eina_File_Copy_Flags flags, Eina_File_Copy_Progress cb, const void *cb_data) EINA_ARG_NONNULL(1, 2);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get a read-only handler to a file.
|
* @brief Get a read-only handler to a file.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue