diff --git a/src/shared/copy.c b/src/shared/copy.c index 8389774db75..ac1a9b5933a 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -41,7 +41,12 @@ #include "user-util.h" #include "xattr-util.h" -#define COPY_BUFFER_SIZE (16U*1024U) +/* If we copy via a userspace buffer, size it to 64K */ +#define COPY_BUFFER_SIZE (64U*U64_KB) + +/* If a byte progress function is specified during copying, never try to copy more than 1M, so that we can + * reasonably call the progress function still */ +#define PROGRESS_STEP_SIZE (1U*U64_MB) /* A safety net for descending recursively into file system trees to copy. On Linux PATH_MAX is 4096, which means the * deepest valid path one can build is around 2048, which we hence use as a safety net here, to not spin endlessly in @@ -162,9 +167,9 @@ int copy_bytes_full( void *userdata) { _cleanup_close_ int fdf_opened = -EBADF, fdt_opened = -EBADF; - bool try_cfr = true, try_sendfile = true, try_splice = true, copied_something = false; + bool try_cfr = true, try_sendfile = true, try_splice = true; + uint64_t copied_total = 0; int r, nonblock_pipe = -1; - size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */ assert(fdf >= 0); assert(fdt >= 0); @@ -264,6 +269,7 @@ int copy_bytes_full( for (;;) { ssize_t n; + size_t m; if (max_bytes <= 0) break; @@ -272,9 +278,20 @@ int copy_bytes_full( if (r < 0) return r; + /* sendfile() accepts at most SSIZE_MAX-offset bytes to copy, hence let's subtract how much + * copied so far from SSIZE_MAX as maximum of what we want to copy. */ + if (try_sendfile) { + assert(copied_total < SSIZE_MAX); + m = (uint64_t) SSIZE_MAX - copied_total; + } else + m = SSIZE_MAX; + if (max_bytes != UINT64_MAX && m > max_bytes) m = max_bytes; + if (progress && m > PROGRESS_STEP_SIZE) + m = PROGRESS_STEP_SIZE; + if (copy_flags & COPY_HOLES) { off_t c, e; @@ -342,7 +359,7 @@ int copy_bytes_full( /* use fallback below */ } else if (n == 0) { /* likely EOF */ - if (copied_something) + if (copied_total > 0) break; /* So, we hit EOF immediately, without having copied a single byte. This @@ -369,7 +386,7 @@ int copy_bytes_full( /* use fallback below */ } else if (n == 0) { /* likely EOF */ - if (copied_something) + if (copied_total > 0) break; try_sendfile = try_splice = false; /* same logic as above for copy_file_range() */ @@ -432,7 +449,7 @@ int copy_bytes_full( /* use fallback below */ } else if (n == 0) { /* likely EOF */ - if (copied_something) + if (copied_total > 0) break; try_splice = false; /* same logic as above for copy_file_range() + sendfile() */ @@ -483,6 +500,12 @@ int copy_bytes_full( } next: + copied_total += n; + + /* Disable sendfile() in case we are getting too close to it's SSIZE_MAX-offset limit */ + if (copied_total > SSIZE_MAX - COPY_BUFFER_SIZE) + try_sendfile = false; + if (progress) { r = progress(n, userdata); if (r < 0) @@ -493,13 +516,6 @@ int copy_bytes_full( assert(max_bytes >= (uint64_t) n); max_bytes -= n; } - - /* sendfile accepts at most SSIZE_MAX-offset bytes to copy, so reduce our maximum by the - * amount we already copied, but don't go below our copy buffer size, unless we are close the - * limit of bytes we are allowed to copy. */ - m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n); - - copied_something = true; } if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {