The sendfile *offset parameter refers to the input file offest, so it cannot be used in the same way as the copy_file_range *off_out parameter. Therefore, add sf_wrapper function which implements the *off_out behavior for sendfile.
Bug: https://bugs.gentoo.org/635126 --- [PATCH v3] adds an additional lseek call to ensure that the output file is positioned at the correct offset src/portage_util_file_copy_reflink_linux.c | 43 +++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/portage_util_file_copy_reflink_linux.c b/src/portage_util_file_copy_reflink_linux.c index 4be9e0568..35a197590 100644 --- a/src/portage_util_file_copy_reflink_linux.c +++ b/src/portage_util_file_copy_reflink_linux.c @@ -56,7 +56,7 @@ initreflink_linux(void) /** * cfr_wrapper - A copy_file_range syscall wrapper function, having a - * function signature that is compatible with sendfile. + * function signature that is compatible with sf_wrapper. * @fd_out: output file descriptor * @fd_in: input file descriptor * @off_out: offset of the output file @@ -79,6 +79,37 @@ cfr_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len) } /** + * sf_wrapper - A sendfile wrapper function, having a function signature + * that is compatible with cfr_wrapper. + * @fd_out: output file descriptor + * @fd_in: input file descriptor + * @off_out: offset of the output file + * @len: number of bytes to copy between the file descriptors + * + * Return: Number of bytes written to out_fd on success, -1 on failure + * (errno is set appropriately). + */ +static ssize_t +sf_wrapper(int fd_out, int fd_in, off_t *off_out, size_t len) +{ + ssize_t ret; + if (lseek(fd_out, *off_out, SEEK_SET) < 0) + return -1; + ret = sendfile(fd_out, fd_in, NULL, len); + if (ret > 0) { + /* The sendfile docs do not guarantee that the offset of fd_in + * will be adjusted by the number of bytes written, so use + * lseek to guarantee it. + */ + if (lseek(fd_in, (*off_out) + ret, SEEK_SET) < 0) + return -1; + (*off_out) += ret; + } + return ret; +} + + +/** * do_lseek_data - Adjust file offsets to the next location containing * data, creating sparse empty blocks in the output file as needed. * @fd_in: input file descriptor @@ -250,7 +281,7 @@ _reflink_linux_file_copy(PyObject *self, PyObject *args) * syscall is not available (less than Linux 4.5). */ error = 0; - copyfunc = sendfile; + copyfunc = sf_wrapper; copyfunc_ret = copyfunc(fd_out, fd_in, &offset_out, @@ -293,10 +324,10 @@ _reflink_linux_file_copy(PyObject *self, PyObject *args) error = errno; } else { while (offset_out < stat_in.st_size) { - copyfunc_ret = sendfile(fd_out, - fd_in, - &offset_out, - stat_in.st_size - offset_out); + copyfunc_ret = sf_wrapper(fd_out, + fd_in, + &offset_out, + stat_in.st_size - offset_out); if (copyfunc_ret < 0) { error = errno; -- 2.13.5