summaryrefslogtreecommitdiff
path: root/src/basic/copy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/copy.c')
-rw-r--r--src/basic/copy.c54
1 files changed, 36 insertions, 18 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c
index e2d356d676..b20c178727 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -29,30 +29,34 @@
#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;
}
@@ -69,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)
@@ -89,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) {
@@ -136,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;
@@ -355,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);
@@ -467,8 +487,7 @@ int copy_xattr(int fdf, int fdt) {
sza *= 2;
- free(bufa);
- bufa = NULL;
+ bufa = mfree(bufa);
}
p = bufa;
@@ -491,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;
}