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


Reply via email to