Blame SOURCES/coreutils-8.32-cp-file-range.patch

e63663
From 5f2dac18054d9d9b3d84e7fba8c2a6e750d2c245 Mon Sep 17 00:00:00 2001
e63663
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
e63663
Date: Wed, 1 Apr 2020 12:51:34 +0100
e63663
Subject: [PATCH 01/12] cp: ensure --attributes-only doesn't remove files
e63663
e63663
* src/copy.c (copy_internal): Ensure we don't unlink the destination
e63663
unless explicitly requested.
e63663
* tests/cp/attr-existing.sh: Add test cases.
e63663
* NEWS: Mention the bug fix.
e63663
Fixes https://bugs.gnu.org/40352
e63663
e63663
Upstream-commit: 7b5f0fa47cd04c84975250d5b5da7c98e097e99f
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c                |  9 +++++----
e63663
 tests/cp/attr-existing.sh | 21 ++++++++++++++++++---
e63663
 2 files changed, 23 insertions(+), 7 deletions(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index 6e5efc7..54601ce 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -2211,10 +2211,11 @@ copy_internal (char const *src_name, char const *dst_name,
e63663
                    /* Never unlink dst_name when in move mode.  */
e63663
                    && ! x->move_mode
e63663
                    && (x->unlink_dest_before_opening
e63663
-                       || (x->preserve_links && 1 < dst_sb.st_nlink)
e63663
-                       || (x->dereference == DEREF_NEVER
e63663
-                           && ! S_ISREG (src_sb.st_mode))
e63663
-                       ))
e63663
+                       || (x->data_copy_required
e63663
+                           && ((x->preserve_links && 1 < dst_sb.st_nlink)
e63663
+                               || (x->dereference == DEREF_NEVER
e63663
+                                   && ! S_ISREG (src_sb.st_mode))))
e63663
+                      ))
e63663
             {
e63663
               if (unlink (dst_name) != 0 && errno != ENOENT)
e63663
                 {
e63663
diff --git a/tests/cp/attr-existing.sh b/tests/cp/attr-existing.sh
e63663
index 59ce641..14fc844 100755
e63663
--- a/tests/cp/attr-existing.sh
e63663
+++ b/tests/cp/attr-existing.sh
e63663
@@ -19,11 +19,26 @@
e63663
 . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
e63663
 print_ver_ cp
e63663
 
e63663
-printf '1' > file1
e63663
-printf '2' > file2
e63663
-printf '2' > file2.exp
e63663
+printf '1' > file1 || framework_failure_
e63663
+printf '2' > file2 || framework_failure_
e63663
+printf '2' > file2.exp || framework_failure_
e63663
 
e63663
 cp --attributes-only file1 file2 || fail=1
e63663
 cmp file2 file2.exp || fail=1
e63663
 
e63663
+# coreutils v8.32 and before would remove destination files
e63663
+# if hardlinked or the source was not a regular file.
e63663
+ln file2 link2 || framework_failure_
e63663
+cp -a --attributes-only file1 file2 || fail=1
e63663
+cmp file2 file2.exp || fail=1
e63663
+
e63663
+ln -s file1 sym1 || framework_failure_
e63663
+returns_ 1 cp -a --attributes-only sym1 file2 || fail=1
e63663
+cmp file2 file2.exp || fail=1
e63663
+
e63663
+# One can still force removal though
e63663
+cp -a --remove-destination --attributes-only sym1 file2 || fail=1
e63663
+test -L file2 || fail=1
e63663
+cmp file1 file2 || fail=1
e63663
+
e63663
 Exit $fail
e63663
-- 
e63663
2.26.3
e63663
e63663
e63663
From c728747b06e71894c96d1f27434f2484af992c75 Mon Sep 17 00:00:00 2001
e63663
From: Paul Eggert <eggert@cs.ucla.edu>
e63663
Date: Tue, 23 Jun 2020 19:18:04 -0700
e63663
Subject: [PATCH 02/12] cp: refactor extent_copy
e63663
e63663
* src/copy.c (extent_copy): New arg SCAN, replacing
e63663
REQUIRE_NORMAL_COPY.  All callers changed.
e63663
(enum scantype): New type.
e63663
(infer_scantype): Rename from is_probably_sparse and return
e63663
the new type.  Add args FD and SCAN.  All callers changed.
e63663
e63663
Upstream-commit: 761ba28400a04ee24eefe9cd4973ec8850cd7a52
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 119 +++++++++++++++++++++++++----------------------------
e63663
 1 file changed, 55 insertions(+), 64 deletions(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index 54601ce..f694f91 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -422,9 +422,8 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
              size_t hole_size, off_t src_total_size,
e63663
              enum Sparse_type sparse_mode,
e63663
              char const *src_name, char const *dst_name,
e63663
-             bool *require_normal_copy)
e63663
+             struct extent_scan *scan)
e63663
 {
e63663
-  struct extent_scan scan;
e63663
   off_t last_ext_start = 0;
e63663
   off_t last_ext_len = 0;
e63663
 
e63663
@@ -432,45 +431,25 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
      We may need this at the end, for a final ftruncate.  */
e63663
   off_t dest_pos = 0;
e63663
 
e63663
-  extent_scan_init (src_fd, &scan;;
e63663
-
e63663
-  *require_normal_copy = false;
e63663
   bool wrote_hole_at_eof = true;
e63663
-  do
e63663
+  while (true)
e63663
     {
e63663
-      bool ok = extent_scan_read (&scan;;
e63663
-      if (! ok)
e63663
-        {
e63663
-          if (scan.hit_final_extent)
e63663
-            break;
e63663
-
e63663
-          if (scan.initial_scan_failed)
e63663
-            {
e63663
-              *require_normal_copy = true;
e63663
-              return false;
e63663
-            }
e63663
-
e63663
-          error (0, errno, _("%s: failed to get extents info"),
e63663
-                 quotef (src_name));
e63663
-          return false;
e63663
-        }
e63663
-
e63663
       bool empty_extent = false;
e63663
-      for (unsigned int i = 0; i < scan.ei_count || empty_extent; i++)
e63663
+      for (unsigned int i = 0; i < scan->ei_count || empty_extent; i++)
e63663
         {
e63663
           off_t ext_start;
e63663
           off_t ext_len;
e63663
           off_t ext_hole_size;
e63663
 
e63663
-          if (i < scan.ei_count)
e63663
+          if (i < scan->ei_count)
e63663
             {
e63663
-              ext_start = scan.ext_info[i].ext_logical;
e63663
-              ext_len = scan.ext_info[i].ext_length;
e63663
+              ext_start = scan->ext_info[i].ext_logical;
e63663
+              ext_len = scan->ext_info[i].ext_length;
e63663
             }
e63663
           else /* empty extent at EOF.  */
e63663
             {
e63663
               i--;
e63663
-              ext_start = last_ext_start + scan.ext_info[i].ext_length;
e63663
+              ext_start = last_ext_start + scan->ext_info[i].ext_length;
e63663
               ext_len = 0;
e63663
             }
e63663
 
e63663
@@ -498,7 +477,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
                 {
e63663
                   error (0, errno, _("cannot lseek %s"), quoteaf (src_name));
e63663
                 fail:
e63663
-                  extent_scan_free (&scan;;
e63663
+                  extent_scan_free (scan);
e63663
                   return false;
e63663
                 }
e63663
 
e63663
@@ -539,7 +518,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
           /* For now, do not treat FIEMAP_EXTENT_UNWRITTEN specially,
e63663
              because that (in combination with no sync) would lead to data
e63663
              loss at least on XFS and ext4 when using 2.6.39-rc3 kernels.  */
e63663
-          if (0 && (scan.ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN))
e63663
+          if (0 && (scan->ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN))
e63663
             {
e63663
               empty_extent = true;
e63663
               last_ext_len = 0;
e63663
@@ -571,16 +550,23 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
              extents beyond the apparent size.  */
e63663
           if (dest_pos == src_total_size)
e63663
             {
e63663
-              scan.hit_final_extent = true;
e63663
+              scan->hit_final_extent = true;
e63663
               break;
e63663
             }
e63663
         }
e63663
 
e63663
       /* Release the space allocated to scan->ext_info.  */
e63663
-      extent_scan_free (&scan;;
e63663
+      extent_scan_free (scan);
e63663
 
e63663
+      if (scan->hit_final_extent)
e63663
+        break;
e63663
+      if (! extent_scan_read (scan) && ! scan->hit_final_extent)
e63663
+        {
e63663
+          error (0, errno, _("%s: failed to get extents info"),
e63663
+                 quotef (src_name));
e63663
+          return false;
e63663
+        }
e63663
     }
e63663
-  while (! scan.hit_final_extent);
e63663
 
e63663
   /* When the source file ends with a hole, we have to do a little more work,
e63663
      since the above copied only up to and including the final extent.
e63663
@@ -1021,16 +1007,35 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode)
e63663
 # define HAVE_STRUCT_STAT_ST_BLOCKS 0
e63663
 #endif
e63663
 
e63663
+/* Type of scan being done on the input when looking for sparseness.  */
e63663
+enum scantype
e63663
+  {
e63663
+   /* No fancy scanning; just read and write.  */
e63663
+   PLAIN_SCANTYPE,
e63663
+
e63663
+   /* Read and examine data looking for zero blocks; useful when
e63663
+      attempting to create sparse output.  */
e63663
+   ZERO_SCANTYPE,
e63663
+
e63663
+   /* Extent information is available.  */
e63663
+   EXTENT_SCANTYPE
e63663
+  };
e63663
+
e63663
 /* Use a heuristic to determine whether stat buffer SB comes from a file
e63663
    with sparse blocks.  If the file has fewer blocks than would normally
e63663
    be needed for a file of its size, then at least one of the blocks in
e63663
    the file is a hole.  In that case, return true.  */
e63663
-static bool
e63663
-is_probably_sparse (struct stat const *sb)
e63663
+static enum scantype
e63663
+infer_scantype (int fd, struct stat const *sb, struct extent_scan *scan)
e63663
 {
e63663
-  return (HAVE_STRUCT_STAT_ST_BLOCKS
e63663
-          && S_ISREG (sb->st_mode)
e63663
-          && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE);
e63663
+  if (! (HAVE_STRUCT_STAT_ST_BLOCKS
e63663
+         && S_ISREG (sb->st_mode)
e63663
+         && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE))
e63663
+    return PLAIN_SCANTYPE;
e63663
+
e63663
+  extent_scan_init (fd, scan);
e63663
+  extent_scan_read (scan);
e63663
+  return scan->initial_scan_failed ? ZERO_SCANTYPE : EXTENT_SCANTYPE;
e63663
 }
e63663
 
e63663
 
e63663
@@ -1061,6 +1066,7 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
   mode_t src_mode = src_sb->st_mode;
e63663
   struct stat sb;
e63663
   struct stat src_open_sb;
e63663
+  struct extent_scan scan;
e63663
   bool return_val = true;
e63663
   bool data_copy_required = x->data_copy_required;
e63663
 
e63663
@@ -1260,23 +1266,13 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
       fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL);
e63663
 
e63663
       /* Deal with sparse files.  */
e63663
-      bool make_holes = false;
e63663
-      bool sparse_src = is_probably_sparse (&src_open_sb);
e63663
-
e63663
-      if (S_ISREG (sb.st_mode))
e63663
-        {
e63663
-          /* Even with --sparse=always, try to create holes only
e63663
-             if the destination is a regular file.  */
e63663
-          if (x->sparse_mode == SPARSE_ALWAYS)
e63663
-            make_holes = true;
e63663
-
e63663
-          /* Use a heuristic to determine whether SRC_NAME contains any sparse
e63663
-             blocks.  If the file has fewer blocks than would normally be
e63663
-             needed for a file of its size, then at least one of the blocks in
e63663
-             the file is a hole.  */
e63663
-          if (x->sparse_mode == SPARSE_AUTO && sparse_src)
e63663
-            make_holes = true;
e63663
-        }
e63663
+      enum scantype scantype = infer_scantype (source_desc, &src_open_sb,
e63663
+                                               &scan;;
e63663
+      bool make_holes
e63663
+        = (S_ISREG (sb.st_mode)
e63663
+           && (x->sparse_mode == SPARSE_ALWAYS
e63663
+               || (x->sparse_mode == SPARSE_AUTO
e63663
+                   && scantype != PLAIN_SCANTYPE)));
e63663
 
e63663
       /* If not making a sparse file, try to use a more-efficient
e63663
          buffer size.  */
e63663
@@ -1305,10 +1301,8 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
       buf_alloc = xmalloc (buf_size + buf_alignment);
e63663
       buf = ptr_align (buf_alloc, buf_alignment);
e63663
 
e63663
-      if (sparse_src)
e63663
+      if (scantype == EXTENT_SCANTYPE)
e63663
         {
e63663
-          bool normal_copy_required;
e63663
-
e63663
           /* Perform an efficient extent-based copy, falling back to the
e63663
              standard copy only if the initial extent scan fails.  If the
e63663
              '--sparse=never' option is specified, write all data but use
e63663
@@ -1316,14 +1310,11 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
           if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
e63663
                            src_open_sb.st_size,
e63663
                            make_holes ? x->sparse_mode : SPARSE_NEVER,
e63663
-                           src_name, dst_name, &normal_copy_required))
e63663
+                           src_name, dst_name, &scan))
e63663
             goto preserve_metadata;
e63663
 
e63663
-          if (! normal_copy_required)
e63663
-            {
e63663
-              return_val = false;
e63663
-              goto close_src_and_dst_desc;
e63663
-            }
e63663
+          return_val = false;
e63663
+          goto close_src_and_dst_desc;
e63663
         }
e63663
 
e63663
       off_t n_read;
e63663
-- 
e63663
2.26.3
e63663
e63663
e63663
From ed7ff81de507bef46991f4caac550f41ab65e3ed Mon Sep 17 00:00:00 2001
e63663
From: Paul Eggert <eggert@cs.ucla.edu>
e63663
Date: Wed, 24 Jun 2020 17:05:20 -0700
e63663
Subject: [PATCH 03/12] cp: avoid copy_reg goto
e63663
e63663
* src/copy.c (copy_reg): Redo to avoid label and goto.
e63663
e63663
Upstream-commit: 2fcd0f3328f5181a2986905fa5469a0152c67279
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 34 +++++++++++-----------------------
e63663
 1 file changed, 11 insertions(+), 23 deletions(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index f694f91..b382cfa 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -1301,29 +1301,18 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
       buf_alloc = xmalloc (buf_size + buf_alignment);
e63663
       buf = ptr_align (buf_alloc, buf_alignment);
e63663
 
e63663
-      if (scantype == EXTENT_SCANTYPE)
e63663
-        {
e63663
-          /* Perform an efficient extent-based copy, falling back to the
e63663
-             standard copy only if the initial extent scan fails.  If the
e63663
-             '--sparse=never' option is specified, write all data but use
e63663
-             any extents to read more efficiently.  */
e63663
-          if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
e63663
-                           src_open_sb.st_size,
e63663
-                           make_holes ? x->sparse_mode : SPARSE_NEVER,
e63663
-                           src_name, dst_name, &scan))
e63663
-            goto preserve_metadata;
e63663
-
e63663
-          return_val = false;
e63663
-          goto close_src_and_dst_desc;
e63663
-        }
e63663
-
e63663
       off_t n_read;
e63663
-      bool wrote_hole_at_eof;
e63663
-      if (! sparse_copy (source_desc, dest_desc, buf, buf_size,
e63663
-                         make_holes ? hole_size : 0,
e63663
-                         x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name,
e63663
-                         UINTMAX_MAX, &n_read,
e63663
-                         &wrote_hole_at_eof))
e63663
+      bool wrote_hole_at_eof = false;
e63663
+      if (! (scantype == EXTENT_SCANTYPE
e63663
+             ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
e63663
+                            src_open_sb.st_size,
e63663
+                            make_holes ? x->sparse_mode : SPARSE_NEVER,
e63663
+                            src_name, dst_name, &scan)
e63663
+             : sparse_copy (source_desc, dest_desc, buf, buf_size,
e63663
+                            make_holes ? hole_size : 0,
e63663
+                            x->sparse_mode == SPARSE_ALWAYS,
e63663
+                            src_name, dst_name, UINTMAX_MAX, &n_read,
e63663
+                            &wrote_hole_at_eof)))
e63663
         {
e63663
           return_val = false;
e63663
           goto close_src_and_dst_desc;
e63663
@@ -1336,7 +1325,6 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
         }
e63663
     }
e63663
 
e63663
-preserve_metadata:
e63663
   if (x->preserve_timestamps)
e63663
     {
e63663
       struct timespec timespec[2];
e63663
-- 
e63663
2.26.3
e63663
e63663
e63663
From 5631bded3a385ca0bbd77456b50767fe5580240c Mon Sep 17 00:00:00 2001
e63663
From: Paul Eggert <eggert@cs.ucla.edu>
e63663
Date: Thu, 25 Jun 2020 16:31:44 -0700
e63663
Subject: [PATCH 04/12] cp: use SEEK_DATA/SEEK_HOLE if available
e63663
e63663
If it works, prefer lseek with SEEK_DATA and SEEK_HOLE to FIEMAP,
e63663
as lseek is simpler and more portable (will be in next POSIX).
e63663
Problem reported in 2011 by Jeff Liu (Bug#8061).
e63663
* NEWS: Mention this.
e63663
* src/copy.c (lseek_copy) [SEEK_HOLE]: New function.
e63663
(enum scantype): New constants ERROR_SCANTYPE, LSEEK_SCANTYPE.
e63663
(union scan_inference): New type.
e63663
(infer_scantype): Last arg is now union scan_inference *,
e63663
not struct extent_scan *.  All callers changed.
e63663
Prefer SEEK_HOLE to FIEMAP if both work, since
e63663
SEEK_HOLE is simpler and more portable.
e63663
(copy_reg): Do the fdadvise after initial scan, in case the scan
e63663
fails.  Report an error if the initial scan fails.
e63663
(copy_reg) [SEEK_HOLE]: Use lseek_copy if scantype says so.
e63663
e63663
Upstream-commit: a6eaee501f6ec0c152abe88640203a64c390993e
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++---
e63663
 1 file changed, 198 insertions(+), 11 deletions(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index b382cfa..d88f8cf 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -416,7 +416,12 @@ write_zeros (int fd, off_t n_bytes)
e63663
    Upon a successful copy, return true.  If the initial extent scan
e63663
    fails, set *NORMAL_COPY_REQUIRED to true and return false.
e63663
    Upon any other failure, set *NORMAL_COPY_REQUIRED to false and
e63663
-   return false.  */
e63663
+   return false.
e63663
+
e63663
+   FIXME: Once we no longer need to support Linux kernel versions
e63663
+   before 3.1 (2011), this function can be retired as it is superseded
e63663
+   by lseek_copy.  That is, we no longer need extent-scan.h and can
e63663
+   remove any of the code that uses it.  */
e63663
 static bool
e63663
 extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
              size_t hole_size, off_t src_total_size,
e63663
@@ -595,6 +600,150 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
   return true;
e63663
 }
e63663
 
e63663
+#ifdef SEEK_HOLE
e63663
+/* Perform an efficient extent copy, if possible.  This avoids
e63663
+   the overhead of detecting holes in hole-introducing/preserving
e63663
+   copy, and thus makes copying sparse files much more efficient.
e63663
+   Copy from SRC_FD to DEST_FD, using BUF (of size BUF_SIZE) for a buffer.
e63663
+   Look for holes of size HOLE_SIZE in the input.
e63663
+   The input file is of size SRC_TOTAL_SIZE.
e63663
+   Use SPARSE_MODE to determine whether to create holes in the output.
e63663
+   SRC_NAME and DST_NAME are the input and output file names.
e63663
+   Return true if successful, false (with a diagnostic) otherwise.  */
e63663
+
e63663
+static bool
e63663
+lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
+            size_t hole_size, off_t ext_start, off_t src_total_size,
e63663
+            enum Sparse_type sparse_mode,
e63663
+            char const *src_name, char const *dst_name)
e63663
+{
e63663
+  off_t last_ext_start = 0;
e63663
+  off_t last_ext_len = 0;
e63663
+  off_t dest_pos = 0;
e63663
+  bool wrote_hole_at_eof = true;
e63663
+
e63663
+  while (0 <= ext_start)
e63663
+    {
e63663
+      off_t ext_end = lseek (src_fd, ext_start, SEEK_HOLE);
e63663
+      if (ext_end < 0)
e63663
+        {
e63663
+          if (errno != ENXIO)
e63663
+            goto cannot_lseek;
e63663
+          ext_end = src_total_size;
e63663
+          if (ext_end <= ext_start)
e63663
+            {
e63663
+              /* The input file grew; get its current size.  */
e63663
+              src_total_size = lseek (src_fd, 0, SEEK_END);
e63663
+              if (src_total_size < 0)
e63663
+                goto cannot_lseek;
e63663
+
e63663
+              /* If the input file shrank after growing, stop copying.  */
e63663
+              if (src_total_size <= ext_start)
e63663
+                break;
e63663
+
e63663
+              ext_end = src_total_size;
e63663
+            }
e63663
+        }
e63663
+      /* If the input file must have grown, increase its measured size.  */
e63663
+      if (src_total_size < ext_end)
e63663
+        src_total_size = ext_end;
e63663
+
e63663
+      if (lseek (src_fd, ext_start, SEEK_SET) < 0)
e63663
+        goto cannot_lseek;
e63663
+
e63663
+      wrote_hole_at_eof = false;
e63663
+      off_t ext_hole_size = ext_start - last_ext_start - last_ext_len;
e63663
+
e63663
+      if (ext_hole_size)
e63663
+        {
e63663
+          if (sparse_mode != SPARSE_NEVER)
e63663
+            {
e63663
+              if (! create_hole (dest_fd, dst_name,
e63663
+                                 sparse_mode == SPARSE_ALWAYS,
e63663
+                                 ext_hole_size))
e63663
+                return false;
e63663
+              wrote_hole_at_eof = true;
e63663
+            }
e63663
+          else
e63663
+            {
e63663
+              /* When not inducing holes and when there is a hole between
e63663
+                 the end of the previous extent and the beginning of the
e63663
+                 current one, write zeros to the destination file.  */
e63663
+              if (! write_zeros (dest_fd, ext_hole_size))
e63663
+                {
e63663
+                  error (0, errno, _("%s: write failed"),
e63663
+                         quotef (dst_name));
e63663
+                  return false;
e63663
+                }
e63663
+            }
e63663
+        }
e63663
+
e63663
+      off_t ext_len = ext_end - ext_start;
e63663
+      last_ext_start = ext_start;
e63663
+      last_ext_len = ext_len;
e63663
+
e63663
+      /* Copy this extent, looking for further opportunities to not
e63663
+         bother to write zeros unless --sparse=never, since SEEK_HOLE
e63663
+         is conservative and may miss some holes.  */
e63663
+      off_t n_read;
e63663
+      bool read_hole;
e63663
+      if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
e63663
+                          sparse_mode == SPARSE_NEVER ? 0 : hole_size,
e63663
+                          true, src_name, dst_name, ext_len, &n_read,
e63663
+                          &read_hole))
e63663
+        return false;
e63663
+
e63663
+      dest_pos = ext_start + n_read;
e63663
+      if (n_read)
e63663
+        wrote_hole_at_eof = read_hole;
e63663
+      if (n_read < ext_len)
e63663
+        {
e63663
+          /* The input file shrank.  */
e63663
+          src_total_size = dest_pos;
e63663
+          break;
e63663
+        }
e63663
+
e63663
+      ext_start = lseek (src_fd, dest_pos, SEEK_DATA);
e63663
+      if (ext_start < 0)
e63663
+        {
e63663
+          if (errno != ENXIO)
e63663
+            goto cannot_lseek;
e63663
+          break;
e63663
+        }
e63663
+    }
e63663
+
e63663
+  /* When the source file ends with a hole, we have to do a little more work,
e63663
+     since the above copied only up to and including the final extent.
e63663
+     In order to complete the copy, we may have to insert a hole or write
e63663
+     zeros in the destination corresponding to the source file's hole-at-EOF.
e63663
+
e63663
+     In addition, if the final extent was a block of zeros at EOF and we've
e63663
+     just converted them to a hole in the destination, we must call ftruncate
e63663
+     here in order to record the proper length in the destination.  */
e63663
+  if ((dest_pos < src_total_size || wrote_hole_at_eof)
e63663
+      && ! (sparse_mode == SPARSE_NEVER
e63663
+            ? write_zeros (dest_fd, src_total_size - dest_pos)
e63663
+            : ftruncate (dest_fd, src_total_size) == 0))
e63663
+    {
e63663
+      error (0, errno, _("failed to extend %s"), quoteaf (dst_name));
e63663
+      return false;
e63663
+    }
e63663
+
e63663
+  if (sparse_mode == SPARSE_ALWAYS && dest_pos < src_total_size
e63663
+      && punch_hole (dest_fd, dest_pos, src_total_size - dest_pos) < 0)
e63663
+    {
e63663
+      error (0, errno, _("error deallocating %s"), quoteaf (dst_name));
e63663
+      return false;
e63663
+    }
e63663
+
e63663
+  return true;
e63663
+
e63663
+ cannot_lseek:
e63663
+  error (0, errno, _("cannot lseek %s"), quoteaf (src_name));
e63663
+  return false;
e63663
+}
e63663
+#endif
e63663
+
e63663
 /* FIXME: describe */
e63663
 /* FIXME: rewrite this to use a hash table so we avoid the quadratic
e63663
    performance hit that's probably noticeable only on trees deeper
e63663
@@ -1010,6 +1159,9 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode)
e63663
 /* Type of scan being done on the input when looking for sparseness.  */
e63663
 enum scantype
e63663
   {
e63663
+   /* An error was found when determining scantype.  */
e63663
+   ERROR_SCANTYPE,
e63663
+
e63663
    /* No fancy scanning; just read and write.  */
e63663
    PLAIN_SCANTYPE,
e63663
 
e63663
@@ -1017,22 +1169,44 @@ enum scantype
e63663
       attempting to create sparse output.  */
e63663
    ZERO_SCANTYPE,
e63663
 
e63663
+   /* lseek information is available.  */
e63663
+   LSEEK_SCANTYPE,
e63663
+
e63663
    /* Extent information is available.  */
e63663
    EXTENT_SCANTYPE
e63663
   };
e63663
 
e63663
-/* Use a heuristic to determine whether stat buffer SB comes from a file
e63663
-   with sparse blocks.  If the file has fewer blocks than would normally
e63663
-   be needed for a file of its size, then at least one of the blocks in
e63663
-   the file is a hole.  In that case, return true.  */
e63663
+/* Result of infer_scantype.  */
e63663
+union scan_inference
e63663
+{
e63663
+  /* Used if infer_scantype returns LSEEK_SCANTYPE.  This is the
e63663
+     offset of the first data block, or -1 if the file has no data.  */
e63663
+  off_t ext_start;
e63663
+
e63663
+  /* Used if infer_scantype returns EXTENT_SCANTYPE.  */
e63663
+  struct extent_scan extent_scan;
e63663
+};
e63663
+
e63663
+/* Return how to scan a file with descriptor FD and stat buffer SB.
e63663
+   Store any information gathered into *SCAN.  */
e63663
 static enum scantype
e63663
-infer_scantype (int fd, struct stat const *sb, struct extent_scan *scan)
e63663
+infer_scantype (int fd, struct stat const *sb,
e63663
+                union scan_inference *scan_inference)
e63663
 {
e63663
   if (! (HAVE_STRUCT_STAT_ST_BLOCKS
e63663
          && S_ISREG (sb->st_mode)
e63663
          && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE))
e63663
     return PLAIN_SCANTYPE;
e63663
 
e63663
+#ifdef SEEK_HOLE
e63663
+  scan_inference->ext_start = lseek (fd, 0, SEEK_DATA);
e63663
+  if (0 <= scan_inference->ext_start)
e63663
+    return LSEEK_SCANTYPE;
e63663
+  else if (errno != EINVAL && errno != ENOTSUP)
e63663
+    return errno == ENXIO ? LSEEK_SCANTYPE : ERROR_SCANTYPE;
e63663
+#endif
e63663
+
e63663
+  struct extent_scan *scan = &scan_inference->extent_scan;
e63663
   extent_scan_init (fd, scan);
e63663
   extent_scan_read (scan);
e63663
   return scan->initial_scan_failed ? ZERO_SCANTYPE : EXTENT_SCANTYPE;
e63663
@@ -1066,7 +1240,7 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
   mode_t src_mode = src_sb->st_mode;
e63663
   struct stat sb;
e63663
   struct stat src_open_sb;
e63663
-  struct extent_scan scan;
e63663
+  union scan_inference scan_inference;
e63663
   bool return_val = true;
e63663
   bool data_copy_required = x->data_copy_required;
e63663
 
e63663
@@ -1263,17 +1437,23 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
       size_t buf_size = io_blksize (sb);
e63663
       size_t hole_size = ST_BLKSIZE (sb);
e63663
 
e63663
-      fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL);
e63663
-
e63663
       /* Deal with sparse files.  */
e63663
       enum scantype scantype = infer_scantype (source_desc, &src_open_sb,
e63663
-                                               &scan;;
e63663
+                                               &scan_inference);
e63663
+      if (scantype == ERROR_SCANTYPE)
e63663
+        {
e63663
+          error (0, errno, _("cannot lseek %s"), quoteaf (src_name));
e63663
+          return_val = false;
e63663
+          goto close_src_and_dst_desc;
e63663
+        }
e63663
       bool make_holes
e63663
         = (S_ISREG (sb.st_mode)
e63663
            && (x->sparse_mode == SPARSE_ALWAYS
e63663
                || (x->sparse_mode == SPARSE_AUTO
e63663
                    && scantype != PLAIN_SCANTYPE)));
e63663
 
e63663
+      fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL);
e63663
+
e63663
       /* If not making a sparse file, try to use a more-efficient
e63663
          buffer size.  */
e63663
       if (! make_holes)
e63663
@@ -1307,7 +1487,14 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
              ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
e63663
                             src_open_sb.st_size,
e63663
                             make_holes ? x->sparse_mode : SPARSE_NEVER,
e63663
-                            src_name, dst_name, &scan)
e63663
+                            src_name, dst_name, &scan_inference.extent_scan)
e63663
+#ifdef SEEK_HOLE
e63663
+             : scantype == LSEEK_SCANTYPE
e63663
+             ? lseek_copy (source_desc, dest_desc, buf, buf_size, hole_size,
e63663
+                           scan_inference.ext_start, src_open_sb.st_size,
e63663
+                           make_holes ? x->sparse_mode : SPARSE_NEVER,
e63663
+                           src_name, dst_name)
e63663
+#endif
e63663
              : sparse_copy (source_desc, dest_desc, buf, buf_size,
e63663
                             make_holes ? hole_size : 0,
e63663
                             x->sparse_mode == SPARSE_ALWAYS,
e63663
-- 
e63663
2.26.3
e63663
e63663
e63663
From be7466be92d779cfbece418d4de33191ae52ab4a Mon Sep 17 00:00:00 2001
e63663
From: Kamil Dudka <kdudka@redhat.com>
e63663
Date: Wed, 24 Mar 2021 16:06:53 +0100
e63663
Subject: [PATCH 05/12] import the copy-file-range module from gnulib
e63663
e63663
---
e63663
 aclocal.m4            |  1 +
e63663
 lib/config.hin        |  3 +++
e63663
 lib/copy-file-range.c | 33 +++++++++++++++++++++++++++++++++
e63663
 lib/gnulib.mk         | 10 ++++++++++
e63663
 m4/copy-file-range.m4 | 36 ++++++++++++++++++++++++++++++++++++
e63663
 m4/gnulib-comp.m4     |  8 ++++++++
e63663
 6 files changed, 91 insertions(+)
e63663
 create mode 100644 lib/copy-file-range.c
e63663
 create mode 100644 m4/copy-file-range.m4
e63663
e63663
diff --git a/aclocal.m4 b/aclocal.m4
e63663
index 713f7c5..09a7ea8 100644
e63663
--- a/aclocal.m4
e63663
+++ b/aclocal.m4
e63663
@@ -1165,6 +1165,7 @@ m4_include([m4/closedir.m4])
e63663
 m4_include([m4/codeset.m4])
e63663
 m4_include([m4/config-h.m4])
e63663
 m4_include([m4/configmake.m4])
e63663
+m4_include([m4/copy-file-range.m4])
e63663
 m4_include([m4/ctype.m4])
e63663
 m4_include([m4/cycle-check.m4])
e63663
 m4_include([m4/d-ino.m4])
e63663
diff --git a/lib/config.hin b/lib/config.hin
e63663
index 9769c39..bf9f9f8 100644
e63663
--- a/lib/config.hin
e63663
+++ b/lib/config.hin
e63663
@@ -370,6 +370,9 @@
e63663
 /* Define to 1 when the gnulib module connect should be tested. */
e63663
 #undef GNULIB_TEST_CONNECT
e63663
 
e63663
+/* Define to 1 when the gnulib module copy-file-range should be tested. */
e63663
+#undef GNULIB_TEST_COPY_FILE_RANGE
e63663
+
e63663
 /* Define to 1 when the gnulib module dirfd should be tested. */
e63663
 #undef GNULIB_TEST_DIRFD
e63663
 
e63663
diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c
e63663
new file mode 100644
e63663
index 0000000..069f144
e63663
--- /dev/null
e63663
+++ b/lib/copy-file-range.c
e63663
@@ -0,0 +1,33 @@
e63663
+/* Stub for copy_file_range
e63663
+   Copyright 2019-2020 Free Software Foundation, Inc.
e63663
+
e63663
+   This program is free software: you can redistribute it and/or modify
e63663
+   it under the terms of the GNU General Public License as published by
e63663
+   the Free Software Foundation; either version 3 of the License, or
e63663
+   (at your option) any later version.
e63663
+
e63663
+   This program is distributed in the hope that it will be useful,
e63663
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e63663
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
e63663
+   GNU General Public License for more details.
e63663
+
e63663
+   You should have received a copy of the GNU General Public License
e63663
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
e63663
+
e63663
+#include <config.h>
e63663
+
e63663
+#include <unistd.h>
e63663
+
e63663
+#include <errno.h>
e63663
+
e63663
+ssize_t
e63663
+copy_file_range (int infd, off_t *pinoff,
e63663
+                 int outfd, off_t *poutoff,
e63663
+                 size_t length, unsigned int flags)
e63663
+{
e63663
+  /* There is little need to emulate copy_file_range with read+write,
e63663
+     since programs that use copy_file_range must fall back on
e63663
+     read+write anyway.  */
e63663
+  errno = ENOSYS;
e63663
+  return -1;
e63663
+}
e63663
diff --git a/lib/gnulib.mk b/lib/gnulib.mk
e63663
index b3633b8..86829f3 100644
e63663
--- a/lib/gnulib.mk
e63663
+++ b/lib/gnulib.mk
e63663
@@ -65,6 +65,7 @@
e63663
 #  closeout \
e63663
 #  config-h \
e63663
 #  configmake \
e63663
+#  copy-file-range \
e63663
 #  crypto/md5 \
e63663
 #  crypto/sha1 \
e63663
 #  crypto/sha256 \
e63663
@@ -800,6 +801,15 @@ CLEANFILES += lib/configmake.h lib/configmake.h-t
e63663
 
e63663
 ## end   gnulib module configmake
e63663
 
e63663
+## begin gnulib module copy-file-range
e63663
+
e63663
+
e63663
+EXTRA_DIST += lib/copy-file-range.c
e63663
+
e63663
+EXTRA_lib_libcoreutils_a_SOURCES += lib/copy-file-range.c
e63663
+
e63663
+## end   gnulib module copy-file-range
e63663
+
e63663
 ## begin gnulib module count-leading-zeros
e63663
 
e63663
 lib_libcoreutils_a_SOURCES += lib/count-leading-zeros.c
e63663
diff --git a/m4/copy-file-range.m4 b/m4/copy-file-range.m4
e63663
new file mode 100644
e63663
index 0000000..5c5a274
e63663
--- /dev/null
e63663
+++ b/m4/copy-file-range.m4
e63663
@@ -0,0 +1,36 @@
e63663
+# copy-file-range.m4
e63663
+dnl Copyright 2019-2020 Free Software Foundation, Inc.
e63663
+dnl This file is free software; the Free Software Foundation
e63663
+dnl gives unlimited permission to copy and/or distribute it,
e63663
+dnl with or without modifications, as long as this notice is preserved.
e63663
+
e63663
+AC_DEFUN([gl_FUNC_COPY_FILE_RANGE],
e63663
+[
e63663
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
e63663
+
e63663
+  dnl Persuade glibc <unistd.h> to declare copy_file_range.
e63663
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
e63663
+
e63663
+  dnl Use AC_LINK_IFELSE, rather than AC_CHECK_FUNCS or a variant,
e63663
+  dnl since we don't want AC_CHECK_FUNCS's checks for glibc stubs.
e63663
+  dnl Programs that use copy_file_range must fall back on read+write
e63663
+  dnl anyway, and there's little point to substituting the Gnulib stub
e63663
+  dnl for a glibc stub.
e63663
+  AC_CACHE_CHECK([for copy_file_range], [gl_cv_func_copy_file_range],
e63663
+    [AC_LINK_IFELSE(
e63663
+       [AC_LANG_PROGRAM(
e63663
+          [[#include <unistd.h>
e63663
+          ]],
e63663
+          [[ssize_t (*func) (int, off_t *, int, off_t, size_t, unsigned)
e63663
+              = copy_file_range;
e63663
+            return func (0, 0, 0, 0, 0, 0) & 127;
e63663
+          ]])
e63663
+       ],
e63663
+       [gl_cv_func_copy_file_range=yes],
e63663
+       [gl_cv_func_copy_file_range=no])
e63663
+    ])
e63663
+
e63663
+  if test "$gl_cv_func_copy_file_range" != yes; then
e63663
+    HAVE_COPY_FILE_RANGE=0
e63663
+  fi
e63663
+])
e63663
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
e63663
index dead90e..953e7f0 100644
e63663
--- a/m4/gnulib-comp.m4
e63663
+++ b/m4/gnulib-comp.m4
e63663
@@ -129,6 +129,7 @@ AC_DEFUN([gl_EARLY],
e63663
   # Code from module configmake:
e63663
   # Code from module connect:
e63663
   # Code from module connect-tests:
e63663
+  # Code from module copy-file-range:
e63663
   # Code from module count-leading-zeros:
e63663
   # Code from module count-leading-zeros-tests:
e63663
   # Code from module crypto/af_alg:
e63663
@@ -977,6 +978,11 @@ AC_DEFUN([gl_INIT],
e63663
   gl_DIRENT_MODULE_INDICATOR([closedir])
e63663
   gl_CONFIG_H
e63663
   gl_CONFIGMAKE_PREP
e63663
+  gl_FUNC_COPY_FILE_RANGE
e63663
+  if test $HAVE_COPY_FILE_RANGE = 0; then
e63663
+    AC_LIBOBJ([copy-file-range])
e63663
+  fi
e63663
+  gl_UNISTD_MODULE_INDICATOR([copy-file-range])
e63663
   gl_AF_ALG
e63663
   AC_DEFINE([GL_COMPILE_CRYPTO_STREAM], 1, [Compile Gnulib crypto stream ops.])
e63663
   AC_REQUIRE([AC_C_RESTRICT])
e63663
@@ -2746,6 +2752,7 @@ AC_DEFUN([gl_FILE_LIST], [
e63663
   lib/closeout.c
e63663
   lib/closeout.h
e63663
   lib/copy-acl.c
e63663
+  lib/copy-file-range.c
e63663
   lib/count-leading-zeros.c
e63663
   lib/count-leading-zeros.h
e63663
   lib/creat-safer.c
e63663
@@ -3438,6 +3445,7 @@ AC_DEFUN([gl_FILE_LIST], [
e63663
   m4/codeset.m4
e63663
   m4/config-h.m4
e63663
   m4/configmake.m4
e63663
+  m4/copy-file-range.m4
e63663
   m4/ctype.m4
e63663
   m4/cycle-check.m4
e63663
   m4/d-ino.m4
e63663
-- 
e63663
2.26.3
e63663
e63663
e63663
From 48370c95bcf7c25ce021fbd2145062d3d29ae6d5 Mon Sep 17 00:00:00 2001
e63663
From: Paul Eggert <eggert@cs.ucla.edu>
e63663
Date: Thu, 25 Jun 2020 17:34:23 -0700
e63663
Subject: [PATCH 06/12] cp: use copy_file_range if available
e63663
e63663
* NEWS: Mention this.
e63663
* bootstrap.conf (gnulib_modules): Add copy-file-range.
e63663
* src/copy.c (sparse_copy): Try copy_file_range if not
e63663
looking for holes.
e63663
e63663
Upstream-commit: 4b04a0c3b792d27909670a81d21f2a3b3e0ea563
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 bootstrap.conf |  1 +
e63663
 src/copy.c     | 40 ++++++++++++++++++++++++++++++++++++++++
e63663
 2 files changed, 41 insertions(+)
e63663
e63663
diff --git a/bootstrap.conf b/bootstrap.conf
e63663
index 2a342c1..7d53e28 100644
e63663
--- a/bootstrap.conf
e63663
+++ b/bootstrap.conf
e63663
@@ -54,6 +54,7 @@ gnulib_modules="
e63663
   closeout
e63663
   config-h
e63663
   configmake
e63663
+  copy-file-range
e63663
   crypto/md5
e63663
   crypto/sha1
e63663
   crypto/sha256
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index d88f8cf..4050f69 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -265,6 +265,46 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
 {
e63663
   *last_write_made_hole = false;
e63663
   *total_n_read = 0;
e63663
+
e63663
+  /* If not looking for holes, use copy_file_range if available.  */
e63663
+  if (!hole_size)
e63663
+    while (max_n_read)
e63663
+      {
e63663
+        /* Copy at most COPY_MAX bytes at a time; this is min
e63663
+           (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
e63663
+           surely aligned well.  */
e63663
+        ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
e63663
+        ptrdiff_t copy_max = MIN (ssize_max, SIZE_MAX) >> 30 << 30;
e63663
+        ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
e63663
+                                            MIN (max_n_read, copy_max), 0);
e63663
+        if (n_copied == 0)
e63663
+          {
e63663
+            /* copy_file_range incorrectly returns 0 when reading from
e63663
+               the proc file system on the Linux kernel through at
e63663
+               least 5.6.19 (2020), so fall back on 'read' if the
e63663
+               input file seems empty.  */
e63663
+            if (*total_n_read == 0)
e63663
+              break;
e63663
+            return true;
e63663
+          }
e63663
+        if (n_copied < 0)
e63663
+          {
e63663
+            if (errno == ENOSYS || errno == EINVAL
e63663
+                || errno == EBADF || errno == EXDEV)
e63663
+              break;
e63663
+            if (errno == EINTR)
e63663
+              n_copied = 0;
e63663
+            else
e63663
+              {
e63663
+                error (0, errno, _("error copying %s to %s"),
e63663
+                       quoteaf_n (0, src_name), quoteaf_n (1, dst_name));
e63663
+                return false;
e63663
+              }
e63663
+          }
e63663
+        max_n_read -= n_copied;
e63663
+        *total_n_read += n_copied;
e63663
+      }
e63663
+
e63663
   bool make_hole = false;
e63663
   off_t psize = 0;
e63663
 
e63663
-- 
e63663
2.26.3
e63663
e63663
e63663
From 23ea1ba463d33e268f35847059e637a5935e4581 Mon Sep 17 00:00:00 2001
e63663
From: Zorro Lang <zlang@redhat.com>
e63663
Date: Mon, 26 Apr 2021 17:25:18 +0200
e63663
Subject: [PATCH 07/12] copy: do not refuse to copy a swap file
e63663
e63663
* src/copy.c (sparse_copy): Fallback to read() if copy_file_range()
e63663
fails with ETXTBSY.  Otherwise it would be impossible to copy files
e63663
that are being used as swap.  This used to work before introducing
e63663
the support for copy_file_range() in coreutils.  (Bug#48036)
e63663
e63663
Upstream-commit: 785478013b416cde50794be35475c0c4fdbb48b4
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 2 +-
e63663
 1 file changed, 1 insertion(+), 1 deletion(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index 4050f69..1798bb7 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -290,7 +290,7 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
         if (n_copied < 0)
e63663
           {
e63663
             if (errno == ENOSYS || errno == EINVAL
e63663
-                || errno == EBADF || errno == EXDEV)
e63663
+                || errno == EBADF || errno == EXDEV || errno == ETXTBSY)
e63663
               break;
e63663
             if (errno == EINTR)
e63663
               n_copied = 0;
e63663
-- 
e63663
2.31.1
e63663
e63663
e63663
From cd7c7a6b5ad89ef0a61722552d532901fc1bed05 Mon Sep 17 00:00:00 2001
e63663
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
e63663
Date: Sun, 2 May 2021 21:27:17 +0100
e63663
Subject: [PATCH 08/12] copy: ensure we enforce --reflink=never
e63663
e63663
* src/copy.c (sparse_copy): Don't use copy_file_range()
e63663
with --reflink=never as copy_file_range() may implicitly
e63663
use acceleration techniques like reflinking.
e63663
(extent_copy): Pass through whether we allow reflinking.
e63663
(lseek_copy): Likewise.
e63663
Fixes https://bugs.gnu.org/48164
e63663
e63663
Upstream-commit: ea9af99234031ab8d5169c8a669434e2a6b4f864
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 20 +++++++++++++-------
e63663
 1 file changed, 13 insertions(+), 7 deletions(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index 4050f69..0337538 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -258,7 +258,7 @@ create_hole (int fd, char const *name, bool punch_holes, off_t size)
e63663
    bytes read.  */
e63663
 static bool
e63663
 sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
-             size_t hole_size, bool punch_holes,
e63663
+             size_t hole_size, bool punch_holes, bool allow_reflink,
e63663
              char const *src_name, char const *dst_name,
e63663
              uintmax_t max_n_read, off_t *total_n_read,
e63663
              bool *last_write_made_hole)
e63663
@@ -266,8 +266,9 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
   *last_write_made_hole = false;
e63663
   *total_n_read = 0;
e63663
 
e63663
-  /* If not looking for holes, use copy_file_range if available.  */
e63663
-  if (!hole_size)
e63663
+  /* If not looking for holes, use copy_file_range if available,
e63663
+     but don't use if reflink disallowed as that may be implicit.  */
e63663
+  if ((! hole_size) && allow_reflink)
e63663
     while (max_n_read)
e63663
       {
e63663
         /* Copy at most COPY_MAX bytes at a time; this is min
e63663
@@ -466,6 +467,7 @@ static bool
e63663
 extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
              size_t hole_size, off_t src_total_size,
e63663
              enum Sparse_type sparse_mode,
e63663
+             bool allow_reflink,
e63663
              char const *src_name, char const *dst_name,
e63663
              struct extent_scan *scan)
e63663
 {
e63663
@@ -579,8 +581,8 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
 
e63663
               if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
e63663
                                   sparse_mode == SPARSE_ALWAYS ? hole_size: 0,
e63663
-                                  true, src_name, dst_name, ext_len, &n_read,
e63663
-                                  &read_hole))
e63663
+                                  true, allow_reflink, src_name, dst_name,
e63663
+                                  ext_len, &n_read, &read_hole))
e63663
                 goto fail;
e63663
 
e63663
               dest_pos = ext_start + n_read;
e63663
@@ -655,6 +657,7 @@ static bool
e63663
 lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
             size_t hole_size, off_t ext_start, off_t src_total_size,
e63663
             enum Sparse_type sparse_mode,
e63663
+            bool allow_reflink,
e63663
             char const *src_name, char const *dst_name)
e63663
 {
e63663
   off_t last_ext_start = 0;
e63663
@@ -729,8 +732,8 @@ lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
       bool read_hole;
e63663
       if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
e63663
                           sparse_mode == SPARSE_NEVER ? 0 : hole_size,
e63663
-                          true, src_name, dst_name, ext_len, &n_read,
e63663
-                          &read_hole))
e63663
+                          true, allow_reflink, src_name, dst_name,
e63663
+                          ext_len, &n_read, &read_hole))
e63663
         return false;
e63663
 
e63663
       dest_pos = ext_start + n_read;
e63663
@@ -1527,17 +1530,20 @@ copy_reg (char const *src_name, char const *dst_name,
e63663
              ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
e63663
                             src_open_sb.st_size,
e63663
                             make_holes ? x->sparse_mode : SPARSE_NEVER,
e63663
+                            x->reflink_mode != REFLINK_NEVER,
e63663
                             src_name, dst_name, &scan_inference.extent_scan)
e63663
 #ifdef SEEK_HOLE
e63663
              : scantype == LSEEK_SCANTYPE
e63663
              ? lseek_copy (source_desc, dest_desc, buf, buf_size, hole_size,
e63663
                            scan_inference.ext_start, src_open_sb.st_size,
e63663
                            make_holes ? x->sparse_mode : SPARSE_NEVER,
e63663
+                           x->reflink_mode != REFLINK_NEVER,
e63663
                            src_name, dst_name)
e63663
 #endif
e63663
              : sparse_copy (source_desc, dest_desc, buf, buf_size,
e63663
                             make_holes ? hole_size : 0,
e63663
                             x->sparse_mode == SPARSE_ALWAYS,
e63663
+                            x->reflink_mode != REFLINK_NEVER,
e63663
                             src_name, dst_name, UINTMAX_MAX, &n_read,
e63663
                             &wrote_hole_at_eof)))
e63663
         {
e63663
-- 
e63663
2.30.2
e63663
e63663
e63663
From 7978f1de88dcdb17b67db9268038930e9c71154f Mon Sep 17 00:00:00 2001
e63663
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
e63663
Date: Sat, 8 May 2021 17:18:54 +0100
e63663
Subject: [PATCH 09/12] copy: handle ENOTSUP from copy_file_range()
e63663
e63663
* src/copy.c (sparse_copy): Ensure we fall back to
e63663
a standard copy if copy_file_range() returns ENOTSUP.
e63663
This generally is best checked when checking ENOSYS,
e63663
but it also seems to be a practical concern on Centos 7,
e63663
as a quick search gave https://bugzilla.redhat.com/1840284
e63663
e63663
Upstream-commit: 8ec0d1799e19a079b8a661c6bb69f6c58e52f1aa
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 5 +++--
e63663
 1 file changed, 3 insertions(+), 2 deletions(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index 9977193..e3977cd 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -290,8 +290,9 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
           }
e63663
         if (n_copied < 0)
e63663
           {
e63663
-            if (errno == ENOSYS || errno == EINVAL
e63663
-                || errno == EBADF || errno == EXDEV || errno == ETXTBSY)
e63663
+            if (errno == ENOSYS || is_ENOTSUP (errno)
e63663
+                || errno == EINVAL || errno == EBADF
e63663
+                || errno == EXDEV || errno == ETXTBSY)
e63663
               break;
e63663
             if (errno == EINTR)
e63663
               n_copied = 0;
e63663
-- 
e63663
2.31.1
e63663
e63663
e63663
From d8d3edbfc13ff13c185f23436209b788f906aa41 Mon Sep 17 00:00:00 2001
e63663
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
e63663
Date: Sun, 9 May 2021 21:55:22 +0100
e63663
Subject: [PATCH 10/12] copy: handle EOPNOTSUPP from SEEK_DATA
e63663
e63663
* src/copy.c (infer_scantype): Ensure we don't error out
e63663
if SEEK_DATA returns EOPNOTSUPP, on systems where this value
e63663
is distinct from ENOTSUP.  Generally both of these should be checked.
e63663
e63663
Upstream-commit: 017877bd088284d515753d78b81ca6e6a88c1350
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 2 +-
e63663
 1 file changed, 1 insertion(+), 1 deletion(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index e3977cd..de8030d 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -1246,7 +1246,7 @@ infer_scantype (int fd, struct stat const *sb,
e63663
   scan_inference->ext_start = lseek (fd, 0, SEEK_DATA);
e63663
   if (0 <= scan_inference->ext_start)
e63663
     return LSEEK_SCANTYPE;
e63663
-  else if (errno != EINVAL && errno != ENOTSUP)
e63663
+  else if (errno != EINVAL && !is_ENOTSUP (errno))
e63663
     return errno == ENXIO ? LSEEK_SCANTYPE : ERROR_SCANTYPE;
e63663
 #endif
e63663
 
e63663
-- 
e63663
2.31.1
e63663
e63663
e63663
From 1daf8c0fc9a5766c22b7ea84bea8c88c86a0c495 Mon Sep 17 00:00:00 2001
e63663
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
e63663
Date: Sat, 8 May 2021 19:23:20 +0100
e63663
Subject: [PATCH 11/12] copy: handle system security config issues with
e63663
 copy_file_range()
e63663
e63663
* src/copy.c (sparse_copy): Upon EPERM from copy_file_range(),
e63663
fall back to a standard copy, which will give a more accurate
e63663
error as to whether the issue is with the source or destination.
e63663
Also this will avoid the issue where seccomp or apparmor are
e63663
not configured to handle copy_file_range(), in which case
e63663
the fall back standard copy would succeed without issue.
e63663
This specific issue with seccomp was noticed for example in:
e63663
https://github.com/golang/go/issues/40900
e63663
e63663
Upstream-commit: 2e66e1732fced7af20fa76c60e636d39a1767d48
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 9 +++++++++
e63663
 1 file changed, 9 insertions(+)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index de8030d..62eec7b 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -294,6 +294,15 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
                 || errno == EINVAL || errno == EBADF
e63663
                 || errno == EXDEV || errno == ETXTBSY)
e63663
               break;
e63663
+
e63663
+            /* copy_file_range might not be enabled in seccomp filters,
e63663
+               so retry with a standard copy.  EPERM can also occur
e63663
+               for immutable files, but that would only be in the edge case
e63663
+               where the file is made immutable after creating/truncating,
e63663
+               in which case the (more accurate) error is still shown.  */
e63663
+            if (errno == EPERM && *total_n_read == 0)
e63663
+              break;
e63663
+
e63663
             if (errno == EINTR)
e63663
               n_copied = 0;
e63663
             else
e63663
-- 
e63663
2.31.1
e63663
e63663
e63663
From 42c9e598f61ba6bc27a615e39e40023a676a523b Mon Sep 17 00:00:00 2001
e63663
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <P@draigBrady.com>
e63663
Date: Wed, 12 May 2021 23:47:38 +0100
e63663
Subject: [PATCH 12/12] copy: disallow copy_file_range() on Linux kernels
e63663
 before 5.3
e63663
e63663
copy_file_range() before Linux kernel release 5.3 had many issues,
e63663
as described at https://lwn.net/Articles/789527/, which was
e63663
referenced from https://lwn.net/Articles/846403/; a more general
e63663
article discussing the generality of copy_file_range().
e63663
Linux kernel 5.3 was released in September 2019, which is new enough
e63663
that we need to actively avoid older kernels.
e63663
e63663
* src/copy.c (functional_copy_file_range): A new function
e63663
that returns false for Linux kernels before version 5.3.
e63663
(sparse_copy): Call this new function to gate use of
e63663
copy_file_range().
e63663
e63663
Upstream-commit: ba5e6885d2c255648cddb87b4e795659c1990374
e63663
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
e63663
---
e63663
 src/copy.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
e63663
 1 file changed, 45 insertions(+), 2 deletions(-)
e63663
e63663
diff --git a/src/copy.c b/src/copy.c
e63663
index 62eec7b..2e1699b 100644
e63663
--- a/src/copy.c
e63663
+++ b/src/copy.c
e63663
@@ -21,6 +21,7 @@
e63663
 #include <assert.h>
e63663
 #include <sys/ioctl.h>
e63663
 #include <sys/types.h>
e63663
+#include <sys/utsname.h>
e63663
 #include <selinux/selinux.h>
e63663
 
e63663
 #if HAVE_HURD_H
e63663
@@ -64,6 +65,7 @@
e63663
 #include "write-any-file.h"
e63663
 #include "areadlink.h"
e63663
 #include "yesno.h"
e63663
+#include "xstrtol.h"
e63663
 #include "selinux.h"
e63663
 
e63663
 #if USE_XATTR
e63663
@@ -244,6 +246,47 @@ create_hole (int fd, char const *name, bool punch_holes, off_t size)
e63663
   return true;
e63663
 }
e63663
 
e63663
+/* copy_file_range() before Linux kernel release 5.3 had many issues,
e63663
+   as described at https://lwn.net/Articles/789527/,
e63663
+   so return FALSE for Linux kernels earlier than that.
e63663
+   This function can be removed when such kernels (released before Sep 2019)
e63663
+   are no longer a consideration.  */
e63663
+
e63663
+static bool
e63663
+functional_copy_file_range (void)
e63663
+{
e63663
+#ifdef __linux__
e63663
+  static int version_allowed = -1;
e63663
+
e63663
+  if (version_allowed == -1)
e63663
+    version_allowed = 0;
e63663
+  else
e63663
+    return version_allowed;
e63663
+
e63663
+  struct utsname name;
e63663
+  if (uname (&name) == -1)
e63663
+    return version_allowed;
e63663
+
e63663
+  char *p = name.release;
e63663
+  uintmax_t ver[2] = {0, 0};
e63663
+  size_t iver = 0;
e63663
+
e63663
+  do
e63663
+    {
e63663
+      strtol_error err = xstrtoumax (p, &p, 10, &ver[iver], NULL);
e63663
+      if (err != LONGINT_OK || *p++ != '.')
e63663
+        break;
e63663
+    }
e63663
+  while (++iver < ARRAY_CARDINALITY (ver));
e63663
+
e63663
+  version_allowed = (ver[0] > 5 || (ver[0] == 5 && ver[1] >= 3));
e63663
+
e63663
+  return version_allowed;
e63663
+#else
e63663
+  return true;
e63663
+#endif
e63663
+
e63663
+}
e63663
 
e63663
 /* Copy the regular file open on SRC_FD/SRC_NAME to DST_FD/DST_NAME,
e63663
    honoring the MAKE_HOLES setting and using the BUF_SIZE-byte buffer
e63663
@@ -266,9 +309,9 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
e63663
   *last_write_made_hole = false;
e63663
   *total_n_read = 0;
e63663
 
e63663
-  /* If not looking for holes, use copy_file_range if available,
e63663
+  /* If not looking for holes, use copy_file_range if functional,
e63663
      but don't use if reflink disallowed as that may be implicit.  */
e63663
-  if ((! hole_size) && allow_reflink)
e63663
+  if ((! hole_size) && allow_reflink && functional_copy_file_range ())
e63663
     while (max_n_read)
e63663
       {
e63663
         /* Copy at most COPY_MAX bytes at a time; this is min
e63663
-- 
e63663
2.31.1
e63663