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