bug-cpio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC] Add copy_file_range syscall support


From: Luis Henriques
Subject: [RFC] Add copy_file_range syscall support
Date: Mon, 13 Jul 2020 16:59:39 +0100

Hi!

I'm sharing a quick hack to cpio that adds support for using the linux
copy_file_range(2) syscall when doing a disk_to_disk copy ('pass-through'
mode).

My very limited testing on running this patch on a filesystem that
supports this syscall (btrfs) showed a nice performance improvement, but
this is just a very early hack to try to get some feedback on whether this
would be something other people would like to see merged in.

Cheers,
--
Luís

---
 configure.ac   |  2 +-
 src/copypass.c |  4 ++++
 src/extern.h   |  2 ++
 src/util.c     | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 9f81a4730cd1..79c1533a7a1c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,7 +48,7 @@ AC_HEADER_DIRENT
 AC_COMPILE_CHECK_RETTYPE([major], [0])
 AC_COMPILE_CHECK_RETTYPE([minor], [0])
 
-AC_CHECK_FUNCS([fchmod fchown])
+AC_CHECK_FUNCS([fchmod fchown copy_file_range])
 # This is needed for mingw build
 AC_CHECK_FUNCS([setmode getpwuid getpwnam getgrgid getgrnam pipe fork getuid 
geteuid])
 
diff --git a/src/copypass.c b/src/copypass.c
index 3b0104fef784..9987a58bd4f8 100644
--- a/src/copypass.c
+++ b/src/copypass.c
@@ -188,8 +188,12 @@ process_copy_pass ()
                  continue;
                }
 
+#ifdef HAVE_COPY_FILE_RANGE
+             copy_files_range (in_file_des, out_file_des, 
in_file_stat.st_size, input_name.ds_string);
+#else
              copy_files_disk_to_disk (in_file_des, out_file_des, 
in_file_stat.st_size, input_name.ds_string);
              disk_empty_output_buffer (out_file_des, true);
+#endif /* HAVE_COPY_FILE_RANGE */
              
              set_copypass_perms (out_file_des,
                                  output_name.ds_string, &in_file_stat);
diff --git a/src/extern.h b/src/extern.h
index ad05e78a1ae9..981045ef93ef 100644
--- a/src/extern.h
+++ b/src/extern.h
@@ -170,6 +170,8 @@ void tape_toss_input (int in_des, off_t num_bytes);
 void copy_files_tape_to_disk (int in_des, int out_des, off_t num_bytes);
 void copy_files_disk_to_tape (int in_des, int out_des, off_t num_bytes, char 
*filename);
 void copy_files_disk_to_disk (int in_des, int out_des, off_t num_bytes, char 
*filename);
+void copy_files_range (int in_des, int out_des, off_t num_bytes, char 
*filename);
+
 void warn_if_file_changed (char *file_name, off_t old_file_size,
                            time_t old_file_mtime);
 void create_all_directories (char const *name);
diff --git a/src/util.c b/src/util.c
index c44a17b2cd07..9dd0be537294 100644
--- a/src/util.c
+++ b/src/util.c
@@ -516,6 +516,57 @@ copy_files_disk_to_tape (int in_des, int out_des, off_t 
num_bytes,
       in_buff += size;
     }
 }
+
+#ifdef HAVE_COPY_FILE_RANGE
+void
+copy_files_range (int in_des, int out_des, off_t num_bytes,
+                 char *filename)
+{
+  loff_t in_off = 0, out_off = 0;
+  off_t next_start, next_end;
+  ssize_t total = 0;
+  ssize_t ret;
+
+  while (in_off < num_bytes)
+    {
+      next_start = lseek (in_des, in_off, SEEK_DATA);
+      if (next_start < 0)
+       {
+         /* Hole at the end of the file */
+         if (errno == ENXIO)
+           {
+             if (ftruncate (out_des, num_bytes) < 0)
+               error (PAXEXIT_FAILURE, errno, _("Failed ftruncate"));
+             in_off = num_bytes;
+             break;
+           }
+         error (PAXEXIT_FAILURE, errno, _("Can't seek to data in file"));
+       }
+      next_end = lseek (in_des, next_start, SEEK_HOLE);
+      if (next_end < 0)
+       error (PAXEXIT_FAILURE, errno, _("Can't seek to hole in file"));
+      in_off = out_off = next_start;
+      ret = copy_file_range (in_des, &in_off, out_des, &out_off,
+                            (next_end - next_start), 0);
+      if (ret < 0)
+       {
+         error (0, errno, _("copy_file_range failed."));
+         break;
+       }
+      total += ret;
+    }
+
+  if (in_off < num_bytes)
+    {
+      lseek (in_des, in_off, SEEK_SET);
+      lseek (out_des, in_off, SEEK_SET);
+      copy_files_disk_to_disk (in_des, out_des, (num_bytes - in_off),
+                              filename);
+      disk_empty_output_buffer (out_des, true);
+    }
+}
+#endif /* HAVE_COPY_FILE_RANGE */
+
 /* Copy a file using the input and output buffers, which may start out
    partly full.  After the copy, the files are not closed nor the last
    block flushed to output, and the input buffer may still be partly




reply via email to

[Prev in Thread] Current Thread [Next in Thread]