]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
cp: use copy_file_range if available
authorPaul Eggert <eggert@cs.ucla.edu>
Fri, 26 Jun 2020 00:34:23 +0000 (17:34 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Fri, 26 Jun 2020 01:53:43 +0000 (18:53 -0700)
* NEWS: Mention this.
* bootstrap.conf (gnulib_modules): Add copy-file-range.
* src/copy.c (sparse_copy): Try copy_file_range if not
looking for holes.

NEWS
bootstrap.conf
src/copy.c

diff --git a/NEWS b/NEWS
index 63cb47d105d6b162a896c29d96efa92a9326e032..1c3f6378d642c248a6803ca83c7c44855f19989b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,8 +17,9 @@ GNU coreutils NEWS                                    -*- outline -*-
 
   cp and install now default to copy-on-write (COW) if available.
 
-  cp, install and mv now prefer lseek+SEEK_HOLE to ioctl+FS_IOC_FIEMAP
-  on sparse files, as lseek is simpler and more portable.
+  cp, install and mv now use the copy_file_range syscall if available.
+  Also, they prefer lseek+SEEK_HOLE to ioctl+FS_IOC_FIEMAP on sparse
+  files, as lseek is simpler and more portable.
 
   On GNU/Linux systems, ls no longer issues an error message on a
   directory merely because it was removed.  This reverts a change
index 12e2d831a3f90d16e46d6d890a9076f402335aa3..2506f0db47bba73873ae47f7d7508b60a74800a7 100644 (file)
@@ -54,6 +54,7 @@ gnulib_modules="
   closeout
   config-h
   configmake
+  copy-file-range
   crypto/md5
   crypto/sha1
   crypto/sha256
index d88f8cf930c76b18529a1d5e10baabfa272f32c0..4050f695364d244a7a1451b151f61efee665f357 100644 (file)
@@ -265,6 +265,46 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
 {
   *last_write_made_hole = false;
   *total_n_read = 0;
+
+  /* If not looking for holes, use copy_file_range if available.  */
+  if (!hole_size)
+    while (max_n_read)
+      {
+        /* Copy at most COPY_MAX bytes at a time; this is min
+           (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
+           surely aligned well.  */
+        ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
+        ptrdiff_t copy_max = MIN (ssize_max, SIZE_MAX) >> 30 << 30;
+        ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
+                                            MIN (max_n_read, copy_max), 0);
+        if (n_copied == 0)
+          {
+            /* copy_file_range incorrectly returns 0 when reading from
+               the proc file system on the Linux kernel through at
+               least 5.6.19 (2020), so fall back on 'read' if the
+               input file seems empty.  */
+            if (*total_n_read == 0)
+              break;
+            return true;
+          }
+        if (n_copied < 0)
+          {
+            if (errno == ENOSYS || errno == EINVAL
+                || errno == EBADF || errno == EXDEV)
+              break;
+            if (errno == EINTR)
+              n_copied = 0;
+            else
+              {
+                error (0, errno, _("error copying %s to %s"),
+                       quoteaf_n (0, src_name), quoteaf_n (1, dst_name));
+                return false;
+              }
+          }
+        max_n_read -= n_copied;
+        *total_n_read += n_copied;
+      }
+
   bool make_hole = false;
   off_t psize = 0;