diff options
Diffstat (limited to 'src/basic/copy.c')
-rw-r--r-- | src/basic/copy.c | 60 |
1 files changed, 41 insertions, 19 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c index 230e7e4d3f..b20c178727 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -24,34 +24,39 @@ #include "util.h" #include "btrfs-util.h" +#include "strv.h" #include "copy.h" #define COPY_BUFFER_SIZE (16*1024) -int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { - bool try_sendfile = true; +int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { + bool try_sendfile = true, try_splice = true; int r; assert(fdf >= 0); assert(fdt >= 0); /* Try btrfs reflinks first. */ - if (try_reflink && max_bytes == (off_t) -1) { + if (try_reflink && + max_bytes == (uint64_t) -1 && + lseek(fdf, 0, SEEK_CUR) == 0 && + lseek(fdt, 0, SEEK_CUR) == 0) { + r = btrfs_reflink(fdf, fdt); if (r >= 0) - return r; + return 0; /* we copied the whole thing, hence hit EOF, return 0 */ } for (;;) { size_t m = COPY_BUFFER_SIZE; ssize_t n; - if (max_bytes != (off_t) -1) { + if (max_bytes != (uint64_t) -1) { if (max_bytes <= 0) - return -EFBIG; + return 1; /* return > 0 if we hit the max_bytes limit */ - if ((off_t) m > max_bytes) + if ((uint64_t) m > max_bytes) m = (size_t) max_bytes; } @@ -68,13 +73,29 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { } else if (n == 0) /* EOF */ break; else if (n > 0) - /* Succcess! */ + /* Success! */ + goto next; + } + + /* The try splice, unless we already tried */ + if (try_splice) { + n = splice(fdf, NULL, fdt, NULL, m, 0); + if (n < 0) { + if (errno != EINVAL && errno != ENOSYS) + return -errno; + + try_splice = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Success! */ goto next; } /* As a fallback just copy bits by hand */ { - char buf[m]; + uint8_t buf[m]; n = read(fdf, buf, m); if (n < 0) @@ -88,13 +109,13 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { } next: - if (max_bytes != (off_t) -1) { - assert(max_bytes >= n); + if (max_bytes != (uint64_t) -1) { + assert(max_bytes >= (uint64_t) n); max_bytes -= n; } } - return 0; + return 0; /* return 0 if we hit EOF earlier than the size limit */ } static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) { @@ -135,7 +156,7 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int if (fdt < 0) return -errno; - r = copy_bytes(fdf, fdt, (off_t) -1, true); + r = copy_bytes(fdf, fdt, (uint64_t) -1, true); if (r < 0) { unlinkat(dt, to, 0); return r; @@ -262,10 +283,13 @@ static int fd_copy_directory( (void) copy_xattr(dirfd(d), fdt); } - FOREACH_DIRENT(de, d, return -errno) { + FOREACH_DIRENT_ALL(de, d, return -errno) { struct stat buf; int q; + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { r = -errno; continue; @@ -351,7 +375,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) { if (fdf < 0) return -errno; - r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink); + r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink); (void) copy_times(fdf, fdt); (void) copy_xattr(fdf, fdt); @@ -463,8 +487,7 @@ int copy_xattr(int fdf, int fdt) { sza *= 2; - free(bufa); - bufa = NULL; + bufa = mfree(bufa); } p = bufa; @@ -487,8 +510,7 @@ int copy_xattr(int fdf, int fdt) { if (m < 0) { if (errno == ERANGE) { szb *= 2; - free(bufb); - bufb = NULL; + bufb = mfree(bufb); continue; } |