Don't cap splice() amounts to the transfer buffer size, since the transfer buffer isn't being used.

This commit is contained in:
Andrew Wood
2026-05-26 23:32:38 +01:00
parent e74cab6985
commit f72486d4e8
+50 -30
View File
@@ -401,8 +401,10 @@ static ssize_t pv__transfer__splice_via_intermediate(pvstate_t state, int input_
* Splice bytes from file descriptor "input_fd" to "output_fd", possibly via
* an intermediate input pipe if one is active.
*
* The number of bytes spliced is capped to "count", or if "max_to_write" is
* greater than zero and less than "count", capped to "max_to_write".
* The number of bytes spliced is capped to "max_to_read", or if
* "max_to_write" is greater than zero and less than "max_to_read", capped
* to "max_to_write". A "max_to_read" of less than zero indicates no
* maximum.
*
* If state->control.rate_limit_active is true and "max_to_write" is zero,
* performs no action and returns zero. Since this looks the same as EOF,
@@ -417,18 +419,25 @@ static ssize_t pv__transfer__splice_via_intermediate(pvstate_t state, int input_
*
* If splice() could not be used, sets state->transfer.splice_failed_fd to
* fd so splice() won't be tried again until the next input file, and then
* calls pv__transfer__read_repeated() to read into the buffer, returning
* its result.
* calls pv__transfer__read_repeated() to read into the buffer, first
* capping "max_to_read" to "max_buffer_available", returning its result.
*
* Returns the total number of bytes transferred, or negative on error.
*/
static ssize_t pv__transfer__splice_repeated(pvstate_t state, int input_fd, int output_fd, char *buf, size_t count,
off_t max_to_write)
static ssize_t pv__transfer__splice_repeated(pvstate_t state, int input_fd, int output_fd, char *buf,
size_t max_buffer_available, off_t max_to_read, off_t max_to_write)
{
struct timespec start_time;
size_t bytes_to_splice;
ssize_t total_spliced;
bool use_intermediate_pipe;
size_t fallback_read_amount;
/* Amount to read_repeated() if splice() can't be used. */
fallback_read_amount = max_buffer_available;
if ((max_to_read >= 0) && (fallback_read_amount > (size_t) max_to_read)) {
fallback_read_amount = (size_t) max_to_read;
}
/*
* Early return via pv__transfer__read_repeated() if splice() is
@@ -439,7 +448,7 @@ static ssize_t pv__transfer__splice_repeated(pvstate_t state, int input_fd, int
if (state->control.no_splice || state->control.linemode || (output_fd < 0)
|| (input_fd == state->transfer.splice_failed_fd)
|| (state->transfer.to_write > 0)) {
return pv__transfer__read_repeated(input_fd, buf, count);
return pv__transfer__read_repeated(input_fd, buf, fallback_read_amount);
}
/*
@@ -450,11 +459,16 @@ static ssize_t pv__transfer__splice_repeated(pvstate_t state, int input_fd, int
* SIZE_MAX before trying a comparison otherwise on 32-bit systems
* it might appear to be 0.
*/
bytes_to_splice = count;
/*@-unrecog@ */
if ((state->control.rate_limit_active || max_to_write != 0)
&& (max_to_write >= 0) && ((unsigned long) max_to_write <= (unsigned long) SIZE_MAX)
&& ((off_t) bytes_to_splice > max_to_write)) {
bytes_to_splice = SIZE_MAX;
if ((max_to_read >= 0) && ((unsigned long) max_to_read <= (unsigned long) SIZE_MAX)) {
bytes_to_splice = (size_t) max_to_read;
}
if ((state->control.rate_limit_active || max_to_write > 0)
&& ((unsigned long) max_to_write <= (unsigned long) SIZE_MAX)
&& ((max_to_read < 0) || ((unsigned long) max_to_read >= (unsigned long) SIZE_MAX)
|| (max_to_read > max_to_write))
) {
bytes_to_splice = (size_t) max_to_write;
}
/*@+unrecog@ *//* splint doesn't know of SIZE_MAX. */
@@ -535,7 +549,7 @@ static ssize_t pv__transfer__splice_repeated(pvstate_t state, int input_fd, int
}
state->transfer.splice_failed_fd = input_fd;
if (0 == total_spliced) {
return pv__transfer__read_repeated(input_fd, buf, count);
return pv__transfer__read_repeated(input_fd, buf, fallback_read_amount);
} else {
return total_spliced;
}
@@ -554,7 +568,7 @@ static ssize_t pv__transfer__splice_repeated(pvstate_t state, int input_fd, int
/* Early return on EOF. */
if (0 == nspliced) {
debug("%s %d: %s (%ld/%ld)", "fd", input_fd, "reached EOF", total_spliced, bytes_to_splice);
debug("%s %d: %s (%lu/%lu)", "fd", input_fd, "reached EOF", total_spliced, bytes_to_splice);
return total_spliced;
}
@@ -574,7 +588,7 @@ static ssize_t pv__transfer__splice_repeated(pvstate_t state, int input_fd, int
}
if (bytes_to_splice > 0) {
debug("%s %d: %s (%ld %s, %ld %s)", "fd", input_fd,
debug("%s %d: %s (%ld %s, %lu %s)", "fd", input_fd,
"trying another splice", nspliced, "transferred this time", bytes_to_splice, "remaining");
if (is_data_ready(input_fd, NULL, -1, NULL, 0) < 1)
break;
@@ -630,7 +644,8 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
{
bool do_not_skip_errors;
bool zero_transfer_cap;
size_t bytes_can_read;
size_t max_buffer_available;
off_t max_to_read;
off_t amount_to_skip, amount_skipped, orig_offset, skip_offset;
ssize_t nread;
#ifdef HAVE_SPLICE
@@ -645,7 +660,8 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
if (0 == state->control.skip_errors)
do_not_skip_errors = true;
bytes_can_read = state->transfer.buffer_size - state->transfer.read_position;
max_buffer_available = state->transfer.buffer_size - state->transfer.read_position;
max_to_read = -1;
/*
* Don't read past control.size if stop_at_size is true (issue
@@ -654,18 +670,12 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
* This isn't workable in line mode.
*/
if (state->control.stop_at_size && !state->control.linemode) {
off_t bytes_remaining_to_read = state->control.size - state->transfer.total_bytes_read;
if ((long long) bytes_can_read > (long long) bytes_remaining_to_read) {
debug("%lld > (%lld-%lld=%lld): %s", (long long) bytes_can_read,
(long long) (state->control.size), (long long) state->transfer.total_bytes_read,
(long long) bytes_remaining_to_read, "truncating for stop-at-size");
bytes_can_read = (size_t) bytes_remaining_to_read;
}
max_to_read = state->control.size - state->transfer.total_bytes_read;
}
nread = 0;
zero_transfer_cap = false;
if (0 == bytes_can_read)
if (0 == max_to_read)
zero_transfer_cap = true;
#ifdef HAVE_SPLICE
@@ -682,7 +692,7 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
nread =
pv__transfer__splice_repeated(state, fd, output_fd,
state->transfer.transfer_buffer + state->transfer.read_position,
bytes_can_read, max_to_write);
max_buffer_available, max_to_read, max_to_write);
#ifdef HAVE_FDATASYNC
if (nread > 0 && state->control.sync_after_write) {
/*
@@ -704,9 +714,14 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
#endif /* HAVE_FDATASYNC */
}
#else /* !HAVE_SPLICE */
if (max_to_read < 0) {
max_to_read = max_buffer_available;
}
if (max_to_read < 0)
max_to_read = 0;
nread =
pv__transfer__read_repeated(fd, state->transfer.transfer_buffer + state->transfer.read_position,
bytes_can_read);
(size_t) max_to_read);
#endif /* HAVE_SPLICE */
if (0 == nread) {
@@ -853,11 +868,16 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
}
/*
* Trim the skip amount to keep within bytes_can_read so as not to
* read more than permitted.
* Trim the skip amount to keep within max_to_read so as not to read
* more than permitted.
*/
if (amount_to_skip > (off_t) bytes_can_read)
amount_to_skip = (off_t) bytes_can_read;
if (max_to_read < 0) {
max_to_read = (off_t) max_buffer_available;
}
if (max_to_read < 0)
max_to_read = 0;
if (amount_to_skip > (off_t) max_to_read)
amount_to_skip = (off_t) max_to_read;
/*@+longintegral@ */
/* splint complains about __off_t vs off_t */