|
|
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 |
|