diff --git a/coreutils-8.32-cp-file-range.patch b/coreutils-8.32-cp-file-range.patch
new file mode 100644
index 0000000..8d84040
--- /dev/null
+++ b/coreutils-8.32-cp-file-range.patch
@@ -0,0 +1,978 @@
+From 5f2dac18054d9d9b3d84e7fba8c2a6e750d2c245 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?P=C3=A1draig=20Brady?=
+Date: Wed, 1 Apr 2020 12:51:34 +0100
+Subject: [PATCH 1/6] cp: ensure --attributes-only doesn't remove files
+
+* src/copy.c (copy_internal): Ensure we don't unlink the destination
+unless explicitly requested.
+* tests/cp/attr-existing.sh: Add test cases.
+* NEWS: Mention the bug fix.
+Fixes https://bugs.gnu.org/40352
+
+Upstream-commit: 7b5f0fa47cd04c84975250d5b5da7c98e097e99f
+Signed-off-by: Kamil Dudka
+---
+ src/copy.c | 9 +++++----
+ tests/cp/attr-existing.sh | 21 ++++++++++++++++++---
+ 2 files changed, 23 insertions(+), 7 deletions(-)
+
+diff --git a/src/copy.c b/src/copy.c
+index 6e5efc7..54601ce 100644
+--- a/src/copy.c
++++ b/src/copy.c
+@@ -2211,10 +2211,11 @@ copy_internal (char const *src_name, char const *dst_name,
+ /* Never unlink dst_name when in move mode. */
+ && ! x->move_mode
+ && (x->unlink_dest_before_opening
+- || (x->preserve_links && 1 < dst_sb.st_nlink)
+- || (x->dereference == DEREF_NEVER
+- && ! S_ISREG (src_sb.st_mode))
+- ))
++ || (x->data_copy_required
++ && ((x->preserve_links && 1 < dst_sb.st_nlink)
++ || (x->dereference == DEREF_NEVER
++ && ! S_ISREG (src_sb.st_mode))))
++ ))
+ {
+ if (unlink (dst_name) != 0 && errno != ENOENT)
+ {
+diff --git a/tests/cp/attr-existing.sh b/tests/cp/attr-existing.sh
+index 59ce641..14fc844 100755
+--- a/tests/cp/attr-existing.sh
++++ b/tests/cp/attr-existing.sh
+@@ -19,11 +19,26 @@
+ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+ print_ver_ cp
+
+-printf '1' > file1
+-printf '2' > file2
+-printf '2' > file2.exp
++printf '1' > file1 || framework_failure_
++printf '2' > file2 || framework_failure_
++printf '2' > file2.exp || framework_failure_
+
+ cp --attributes-only file1 file2 || fail=1
+ cmp file2 file2.exp || fail=1
+
++# coreutils v8.32 and before would remove destination files
++# if hardlinked or the source was not a regular file.
++ln file2 link2 || framework_failure_
++cp -a --attributes-only file1 file2 || fail=1
++cmp file2 file2.exp || fail=1
++
++ln -s file1 sym1 || framework_failure_
++returns_ 1 cp -a --attributes-only sym1 file2 || fail=1
++cmp file2 file2.exp || fail=1
++
++# One can still force removal though
++cp -a --remove-destination --attributes-only sym1 file2 || fail=1
++test -L file2 || fail=1
++cmp file1 file2 || fail=1
++
+ Exit $fail
+--
+2.26.3
+
+
+From c728747b06e71894c96d1f27434f2484af992c75 Mon Sep 17 00:00:00 2001
+From: Paul Eggert
+Date: Tue, 23 Jun 2020 19:18:04 -0700
+Subject: [PATCH 2/6] cp: refactor extent_copy
+
+* src/copy.c (extent_copy): New arg SCAN, replacing
+REQUIRE_NORMAL_COPY. All callers changed.
+(enum scantype): New type.
+(infer_scantype): Rename from is_probably_sparse and return
+the new type. Add args FD and SCAN. All callers changed.
+
+Upstream-commit: 761ba28400a04ee24eefe9cd4973ec8850cd7a52
+Signed-off-by: Kamil Dudka
+---
+ src/copy.c | 119 +++++++++++++++++++++++++----------------------------
+ 1 file changed, 55 insertions(+), 64 deletions(-)
+
+diff --git a/src/copy.c b/src/copy.c
+index 54601ce..f694f91 100644
+--- a/src/copy.c
++++ b/src/copy.c
+@@ -422,9 +422,8 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ size_t hole_size, off_t src_total_size,
+ enum Sparse_type sparse_mode,
+ char const *src_name, char const *dst_name,
+- bool *require_normal_copy)
++ struct extent_scan *scan)
+ {
+- struct extent_scan scan;
+ off_t last_ext_start = 0;
+ off_t last_ext_len = 0;
+
+@@ -432,45 +431,25 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ We may need this at the end, for a final ftruncate. */
+ off_t dest_pos = 0;
+
+- extent_scan_init (src_fd, &scan);
+-
+- *require_normal_copy = false;
+ bool wrote_hole_at_eof = true;
+- do
++ while (true)
+ {
+- bool ok = extent_scan_read (&scan);
+- if (! ok)
+- {
+- if (scan.hit_final_extent)
+- break;
+-
+- if (scan.initial_scan_failed)
+- {
+- *require_normal_copy = true;
+- return false;
+- }
+-
+- error (0, errno, _("%s: failed to get extents info"),
+- quotef (src_name));
+- return false;
+- }
+-
+ bool empty_extent = false;
+- for (unsigned int i = 0; i < scan.ei_count || empty_extent; i++)
++ for (unsigned int i = 0; i < scan->ei_count || empty_extent; i++)
+ {
+ off_t ext_start;
+ off_t ext_len;
+ off_t ext_hole_size;
+
+- if (i < scan.ei_count)
++ if (i < scan->ei_count)
+ {
+- ext_start = scan.ext_info[i].ext_logical;
+- ext_len = scan.ext_info[i].ext_length;
++ ext_start = scan->ext_info[i].ext_logical;
++ ext_len = scan->ext_info[i].ext_length;
+ }
+ else /* empty extent at EOF. */
+ {
+ i--;
+- ext_start = last_ext_start + scan.ext_info[i].ext_length;
++ ext_start = last_ext_start + scan->ext_info[i].ext_length;
+ ext_len = 0;
+ }
+
+@@ -498,7 +477,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ {
+ error (0, errno, _("cannot lseek %s"), quoteaf (src_name));
+ fail:
+- extent_scan_free (&scan);
++ extent_scan_free (scan);
+ return false;
+ }
+
+@@ -539,7 +518,7 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ /* For now, do not treat FIEMAP_EXTENT_UNWRITTEN specially,
+ because that (in combination with no sync) would lead to data
+ loss at least on XFS and ext4 when using 2.6.39-rc3 kernels. */
+- if (0 && (scan.ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN))
++ if (0 && (scan->ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN))
+ {
+ empty_extent = true;
+ last_ext_len = 0;
+@@ -571,16 +550,23 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ extents beyond the apparent size. */
+ if (dest_pos == src_total_size)
+ {
+- scan.hit_final_extent = true;
++ scan->hit_final_extent = true;
+ break;
+ }
+ }
+
+ /* Release the space allocated to scan->ext_info. */
+- extent_scan_free (&scan);
++ extent_scan_free (scan);
+
++ if (scan->hit_final_extent)
++ break;
++ if (! extent_scan_read (scan) && ! scan->hit_final_extent)
++ {
++ error (0, errno, _("%s: failed to get extents info"),
++ quotef (src_name));
++ return false;
++ }
+ }
+- while (! scan.hit_final_extent);
+
+ /* When the source file ends with a hole, we have to do a little more work,
+ since the above copied only up to and including the final extent.
+@@ -1021,16 +1007,35 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode)
+ # define HAVE_STRUCT_STAT_ST_BLOCKS 0
+ #endif
+
++/* Type of scan being done on the input when looking for sparseness. */
++enum scantype
++ {
++ /* No fancy scanning; just read and write. */
++ PLAIN_SCANTYPE,
++
++ /* Read and examine data looking for zero blocks; useful when
++ attempting to create sparse output. */
++ ZERO_SCANTYPE,
++
++ /* Extent information is available. */
++ EXTENT_SCANTYPE
++ };
++
+ /* Use a heuristic to determine whether stat buffer SB comes from a file
+ with sparse blocks. If the file has fewer blocks than would normally
+ be needed for a file of its size, then at least one of the blocks in
+ the file is a hole. In that case, return true. */
+-static bool
+-is_probably_sparse (struct stat const *sb)
++static enum scantype
++infer_scantype (int fd, struct stat const *sb, struct extent_scan *scan)
+ {
+- return (HAVE_STRUCT_STAT_ST_BLOCKS
+- && S_ISREG (sb->st_mode)
+- && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE);
++ if (! (HAVE_STRUCT_STAT_ST_BLOCKS
++ && S_ISREG (sb->st_mode)
++ && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE))
++ return PLAIN_SCANTYPE;
++
++ extent_scan_init (fd, scan);
++ extent_scan_read (scan);
++ return scan->initial_scan_failed ? ZERO_SCANTYPE : EXTENT_SCANTYPE;
+ }
+
+
+@@ -1061,6 +1066,7 @@ copy_reg (char const *src_name, char const *dst_name,
+ mode_t src_mode = src_sb->st_mode;
+ struct stat sb;
+ struct stat src_open_sb;
++ struct extent_scan scan;
+ bool return_val = true;
+ bool data_copy_required = x->data_copy_required;
+
+@@ -1260,23 +1266,13 @@ copy_reg (char const *src_name, char const *dst_name,
+ fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL);
+
+ /* Deal with sparse files. */
+- bool make_holes = false;
+- bool sparse_src = is_probably_sparse (&src_open_sb);
+-
+- if (S_ISREG (sb.st_mode))
+- {
+- /* Even with --sparse=always, try to create holes only
+- if the destination is a regular file. */
+- if (x->sparse_mode == SPARSE_ALWAYS)
+- make_holes = true;
+-
+- /* Use a heuristic to determine whether SRC_NAME contains any sparse
+- blocks. If the file has fewer blocks than would normally be
+- needed for a file of its size, then at least one of the blocks in
+- the file is a hole. */
+- if (x->sparse_mode == SPARSE_AUTO && sparse_src)
+- make_holes = true;
+- }
++ enum scantype scantype = infer_scantype (source_desc, &src_open_sb,
++ &scan);
++ bool make_holes
++ = (S_ISREG (sb.st_mode)
++ && (x->sparse_mode == SPARSE_ALWAYS
++ || (x->sparse_mode == SPARSE_AUTO
++ && scantype != PLAIN_SCANTYPE)));
+
+ /* If not making a sparse file, try to use a more-efficient
+ buffer size. */
+@@ -1305,10 +1301,8 @@ copy_reg (char const *src_name, char const *dst_name,
+ buf_alloc = xmalloc (buf_size + buf_alignment);
+ buf = ptr_align (buf_alloc, buf_alignment);
+
+- if (sparse_src)
++ if (scantype == EXTENT_SCANTYPE)
+ {
+- bool normal_copy_required;
+-
+ /* Perform an efficient extent-based copy, falling back to the
+ standard copy only if the initial extent scan fails. If the
+ '--sparse=never' option is specified, write all data but use
+@@ -1316,14 +1310,11 @@ copy_reg (char const *src_name, char const *dst_name,
+ if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
+ src_open_sb.st_size,
+ make_holes ? x->sparse_mode : SPARSE_NEVER,
+- src_name, dst_name, &normal_copy_required))
++ src_name, dst_name, &scan))
+ goto preserve_metadata;
+
+- if (! normal_copy_required)
+- {
+- return_val = false;
+- goto close_src_and_dst_desc;
+- }
++ return_val = false;
++ goto close_src_and_dst_desc;
+ }
+
+ off_t n_read;
+--
+2.26.3
+
+
+From ed7ff81de507bef46991f4caac550f41ab65e3ed Mon Sep 17 00:00:00 2001
+From: Paul Eggert
+Date: Wed, 24 Jun 2020 17:05:20 -0700
+Subject: [PATCH 3/6] cp: avoid copy_reg goto
+
+* src/copy.c (copy_reg): Redo to avoid label and goto.
+
+Upstream-commit: 2fcd0f3328f5181a2986905fa5469a0152c67279
+Signed-off-by: Kamil Dudka
+---
+ src/copy.c | 34 +++++++++++-----------------------
+ 1 file changed, 11 insertions(+), 23 deletions(-)
+
+diff --git a/src/copy.c b/src/copy.c
+index f694f91..b382cfa 100644
+--- a/src/copy.c
++++ b/src/copy.c
+@@ -1301,29 +1301,18 @@ copy_reg (char const *src_name, char const *dst_name,
+ buf_alloc = xmalloc (buf_size + buf_alignment);
+ buf = ptr_align (buf_alloc, buf_alignment);
+
+- if (scantype == EXTENT_SCANTYPE)
+- {
+- /* Perform an efficient extent-based copy, falling back to the
+- standard copy only if the initial extent scan fails. If the
+- '--sparse=never' option is specified, write all data but use
+- any extents to read more efficiently. */
+- if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
+- src_open_sb.st_size,
+- make_holes ? x->sparse_mode : SPARSE_NEVER,
+- src_name, dst_name, &scan))
+- goto preserve_metadata;
+-
+- return_val = false;
+- goto close_src_and_dst_desc;
+- }
+-
+ off_t n_read;
+- bool wrote_hole_at_eof;
+- if (! sparse_copy (source_desc, dest_desc, buf, buf_size,
+- make_holes ? hole_size : 0,
+- x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name,
+- UINTMAX_MAX, &n_read,
+- &wrote_hole_at_eof))
++ bool wrote_hole_at_eof = false;
++ if (! (scantype == EXTENT_SCANTYPE
++ ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
++ src_open_sb.st_size,
++ make_holes ? x->sparse_mode : SPARSE_NEVER,
++ src_name, dst_name, &scan)
++ : sparse_copy (source_desc, dest_desc, buf, buf_size,
++ make_holes ? hole_size : 0,
++ x->sparse_mode == SPARSE_ALWAYS,
++ src_name, dst_name, UINTMAX_MAX, &n_read,
++ &wrote_hole_at_eof)))
+ {
+ return_val = false;
+ goto close_src_and_dst_desc;
+@@ -1336,7 +1325,6 @@ copy_reg (char const *src_name, char const *dst_name,
+ }
+ }
+
+-preserve_metadata:
+ if (x->preserve_timestamps)
+ {
+ struct timespec timespec[2];
+--
+2.26.3
+
+
+From 5631bded3a385ca0bbd77456b50767fe5580240c Mon Sep 17 00:00:00 2001
+From: Paul Eggert
+Date: Thu, 25 Jun 2020 16:31:44 -0700
+Subject: [PATCH 4/6] cp: use SEEK_DATA/SEEK_HOLE if available
+
+If it works, prefer lseek with SEEK_DATA and SEEK_HOLE to FIEMAP,
+as lseek is simpler and more portable (will be in next POSIX).
+Problem reported in 2011 by Jeff Liu (Bug#8061).
+* NEWS: Mention this.
+* src/copy.c (lseek_copy) [SEEK_HOLE]: New function.
+(enum scantype): New constants ERROR_SCANTYPE, LSEEK_SCANTYPE.
+(union scan_inference): New type.
+(infer_scantype): Last arg is now union scan_inference *,
+not struct extent_scan *. All callers changed.
+Prefer SEEK_HOLE to FIEMAP if both work, since
+SEEK_HOLE is simpler and more portable.
+(copy_reg): Do the fdadvise after initial scan, in case the scan
+fails. Report an error if the initial scan fails.
+(copy_reg) [SEEK_HOLE]: Use lseek_copy if scantype says so.
+
+Upstream-commit: a6eaee501f6ec0c152abe88640203a64c390993e
+Signed-off-by: Kamil Dudka
+---
+ src/copy.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 198 insertions(+), 11 deletions(-)
+
+diff --git a/src/copy.c b/src/copy.c
+index b382cfa..d88f8cf 100644
+--- a/src/copy.c
++++ b/src/copy.c
+@@ -416,7 +416,12 @@ write_zeros (int fd, off_t n_bytes)
+ Upon a successful copy, return true. If the initial extent scan
+ fails, set *NORMAL_COPY_REQUIRED to true and return false.
+ Upon any other failure, set *NORMAL_COPY_REQUIRED to false and
+- return false. */
++ return false.
++
++ FIXME: Once we no longer need to support Linux kernel versions
++ before 3.1 (2011), this function can be retired as it is superseded
++ by lseek_copy. That is, we no longer need extent-scan.h and can
++ remove any of the code that uses it. */
+ static bool
+ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ size_t hole_size, off_t src_total_size,
+@@ -595,6 +600,150 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ return true;
+ }
+
++#ifdef SEEK_HOLE
++/* Perform an efficient extent copy, if possible. This avoids
++ the overhead of detecting holes in hole-introducing/preserving
++ copy, and thus makes copying sparse files much more efficient.
++ Copy from SRC_FD to DEST_FD, using BUF (of size BUF_SIZE) for a buffer.
++ Look for holes of size HOLE_SIZE in the input.
++ The input file is of size SRC_TOTAL_SIZE.
++ Use SPARSE_MODE to determine whether to create holes in the output.
++ SRC_NAME and DST_NAME are the input and output file names.
++ Return true if successful, false (with a diagnostic) otherwise. */
++
++static bool
++lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
++ size_t hole_size, off_t ext_start, off_t src_total_size,
++ enum Sparse_type sparse_mode,
++ char const *src_name, char const *dst_name)
++{
++ off_t last_ext_start = 0;
++ off_t last_ext_len = 0;
++ off_t dest_pos = 0;
++ bool wrote_hole_at_eof = true;
++
++ while (0 <= ext_start)
++ {
++ off_t ext_end = lseek (src_fd, ext_start, SEEK_HOLE);
++ if (ext_end < 0)
++ {
++ if (errno != ENXIO)
++ goto cannot_lseek;
++ ext_end = src_total_size;
++ if (ext_end <= ext_start)
++ {
++ /* The input file grew; get its current size. */
++ src_total_size = lseek (src_fd, 0, SEEK_END);
++ if (src_total_size < 0)
++ goto cannot_lseek;
++
++ /* If the input file shrank after growing, stop copying. */
++ if (src_total_size <= ext_start)
++ break;
++
++ ext_end = src_total_size;
++ }
++ }
++ /* If the input file must have grown, increase its measured size. */
++ if (src_total_size < ext_end)
++ src_total_size = ext_end;
++
++ if (lseek (src_fd, ext_start, SEEK_SET) < 0)
++ goto cannot_lseek;
++
++ wrote_hole_at_eof = false;
++ off_t ext_hole_size = ext_start - last_ext_start - last_ext_len;
++
++ if (ext_hole_size)
++ {
++ if (sparse_mode != SPARSE_NEVER)
++ {
++ if (! create_hole (dest_fd, dst_name,
++ sparse_mode == SPARSE_ALWAYS,
++ ext_hole_size))
++ return false;
++ wrote_hole_at_eof = true;
++ }
++ else
++ {
++ /* When not inducing holes and when there is a hole between
++ the end of the previous extent and the beginning of the
++ current one, write zeros to the destination file. */
++ if (! write_zeros (dest_fd, ext_hole_size))
++ {
++ error (0, errno, _("%s: write failed"),
++ quotef (dst_name));
++ return false;
++ }
++ }
++ }
++
++ off_t ext_len = ext_end - ext_start;
++ last_ext_start = ext_start;
++ last_ext_len = ext_len;
++
++ /* Copy this extent, looking for further opportunities to not
++ bother to write zeros unless --sparse=never, since SEEK_HOLE
++ is conservative and may miss some holes. */
++ off_t n_read;
++ bool read_hole;
++ if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
++ sparse_mode == SPARSE_NEVER ? 0 : hole_size,
++ true, src_name, dst_name, ext_len, &n_read,
++ &read_hole))
++ return false;
++
++ dest_pos = ext_start + n_read;
++ if (n_read)
++ wrote_hole_at_eof = read_hole;
++ if (n_read < ext_len)
++ {
++ /* The input file shrank. */
++ src_total_size = dest_pos;
++ break;
++ }
++
++ ext_start = lseek (src_fd, dest_pos, SEEK_DATA);
++ if (ext_start < 0)
++ {
++ if (errno != ENXIO)
++ goto cannot_lseek;
++ break;
++ }
++ }
++
++ /* When the source file ends with a hole, we have to do a little more work,
++ since the above copied only up to and including the final extent.
++ In order to complete the copy, we may have to insert a hole or write
++ zeros in the destination corresponding to the source file's hole-at-EOF.
++
++ In addition, if the final extent was a block of zeros at EOF and we've
++ just converted them to a hole in the destination, we must call ftruncate
++ here in order to record the proper length in the destination. */
++ if ((dest_pos < src_total_size || wrote_hole_at_eof)
++ && ! (sparse_mode == SPARSE_NEVER
++ ? write_zeros (dest_fd, src_total_size - dest_pos)
++ : ftruncate (dest_fd, src_total_size) == 0))
++ {
++ error (0, errno, _("failed to extend %s"), quoteaf (dst_name));
++ return false;
++ }
++
++ if (sparse_mode == SPARSE_ALWAYS && dest_pos < src_total_size
++ && punch_hole (dest_fd, dest_pos, src_total_size - dest_pos) < 0)
++ {
++ error (0, errno, _("error deallocating %s"), quoteaf (dst_name));
++ return false;
++ }
++
++ return true;
++
++ cannot_lseek:
++ error (0, errno, _("cannot lseek %s"), quoteaf (src_name));
++ return false;
++}
++#endif
++
+ /* FIXME: describe */
+ /* FIXME: rewrite this to use a hash table so we avoid the quadratic
+ performance hit that's probably noticeable only on trees deeper
+@@ -1010,6 +1159,9 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode)
+ /* Type of scan being done on the input when looking for sparseness. */
+ enum scantype
+ {
++ /* An error was found when determining scantype. */
++ ERROR_SCANTYPE,
++
+ /* No fancy scanning; just read and write. */
+ PLAIN_SCANTYPE,
+
+@@ -1017,22 +1169,44 @@ enum scantype
+ attempting to create sparse output. */
+ ZERO_SCANTYPE,
+
++ /* lseek information is available. */
++ LSEEK_SCANTYPE,
++
+ /* Extent information is available. */
+ EXTENT_SCANTYPE
+ };
+
+-/* Use a heuristic to determine whether stat buffer SB comes from a file
+- with sparse blocks. If the file has fewer blocks than would normally
+- be needed for a file of its size, then at least one of the blocks in
+- the file is a hole. In that case, return true. */
++/* Result of infer_scantype. */
++union scan_inference
++{
++ /* Used if infer_scantype returns LSEEK_SCANTYPE. This is the
++ offset of the first data block, or -1 if the file has no data. */
++ off_t ext_start;
++
++ /* Used if infer_scantype returns EXTENT_SCANTYPE. */
++ struct extent_scan extent_scan;
++};
++
++/* Return how to scan a file with descriptor FD and stat buffer SB.
++ Store any information gathered into *SCAN. */
+ static enum scantype
+-infer_scantype (int fd, struct stat const *sb, struct extent_scan *scan)
++infer_scantype (int fd, struct stat const *sb,
++ union scan_inference *scan_inference)
+ {
+ if (! (HAVE_STRUCT_STAT_ST_BLOCKS
+ && S_ISREG (sb->st_mode)
+ && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE))
+ return PLAIN_SCANTYPE;
+
++#ifdef SEEK_HOLE
++ scan_inference->ext_start = lseek (fd, 0, SEEK_DATA);
++ if (0 <= scan_inference->ext_start)
++ return LSEEK_SCANTYPE;
++ else if (errno != EINVAL && errno != ENOTSUP)
++ return errno == ENXIO ? LSEEK_SCANTYPE : ERROR_SCANTYPE;
++#endif
++
++ struct extent_scan *scan = &scan_inference->extent_scan;
+ extent_scan_init (fd, scan);
+ extent_scan_read (scan);
+ return scan->initial_scan_failed ? ZERO_SCANTYPE : EXTENT_SCANTYPE;
+@@ -1066,7 +1240,7 @@ copy_reg (char const *src_name, char const *dst_name,
+ mode_t src_mode = src_sb->st_mode;
+ struct stat sb;
+ struct stat src_open_sb;
+- struct extent_scan scan;
++ union scan_inference scan_inference;
+ bool return_val = true;
+ bool data_copy_required = x->data_copy_required;
+
+@@ -1263,17 +1437,23 @@ copy_reg (char const *src_name, char const *dst_name,
+ size_t buf_size = io_blksize (sb);
+ size_t hole_size = ST_BLKSIZE (sb);
+
+- fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL);
+-
+ /* Deal with sparse files. */
+ enum scantype scantype = infer_scantype (source_desc, &src_open_sb,
+- &scan);
++ &scan_inference);
++ if (scantype == ERROR_SCANTYPE)
++ {
++ error (0, errno, _("cannot lseek %s"), quoteaf (src_name));
++ return_val = false;
++ goto close_src_and_dst_desc;
++ }
+ bool make_holes
+ = (S_ISREG (sb.st_mode)
+ && (x->sparse_mode == SPARSE_ALWAYS
+ || (x->sparse_mode == SPARSE_AUTO
+ && scantype != PLAIN_SCANTYPE)));
+
++ fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL);
++
+ /* If not making a sparse file, try to use a more-efficient
+ buffer size. */
+ if (! make_holes)
+@@ -1307,7 +1487,14 @@ copy_reg (char const *src_name, char const *dst_name,
+ ? extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
+ src_open_sb.st_size,
+ make_holes ? x->sparse_mode : SPARSE_NEVER,
+- src_name, dst_name, &scan)
++ src_name, dst_name, &scan_inference.extent_scan)
++#ifdef SEEK_HOLE
++ : scantype == LSEEK_SCANTYPE
++ ? lseek_copy (source_desc, dest_desc, buf, buf_size, hole_size,
++ scan_inference.ext_start, src_open_sb.st_size,
++ make_holes ? x->sparse_mode : SPARSE_NEVER,
++ src_name, dst_name)
++#endif
+ : sparse_copy (source_desc, dest_desc, buf, buf_size,
+ make_holes ? hole_size : 0,
+ x->sparse_mode == SPARSE_ALWAYS,
+--
+2.26.3
+
+
+From be7466be92d779cfbece418d4de33191ae52ab4a Mon Sep 17 00:00:00 2001
+From: Kamil Dudka
+Date: Wed, 24 Mar 2021 16:06:53 +0100
+Subject: [PATCH 5/6] import the copy-file-range module from gnulib
+
+---
+ aclocal.m4 | 1 +
+ lib/config.hin | 3 +++
+ lib/copy-file-range.c | 33 +++++++++++++++++++++++++++++++++
+ lib/gnulib.mk | 10 ++++++++++
+ m4/copy-file-range.m4 | 36 ++++++++++++++++++++++++++++++++++++
+ m4/gnulib-comp.m4 | 8 ++++++++
+ 6 files changed, 91 insertions(+)
+ create mode 100644 lib/copy-file-range.c
+ create mode 100644 m4/copy-file-range.m4
+
+diff --git a/aclocal.m4 b/aclocal.m4
+index 713f7c5..09a7ea8 100644
+--- a/aclocal.m4
++++ b/aclocal.m4
+@@ -1163,6 +1163,7 @@ m4_include([m4/closedir.m4])
+ m4_include([m4/codeset.m4])
+ m4_include([m4/config-h.m4])
+ m4_include([m4/configmake.m4])
++m4_include([m4/copy-file-range.m4])
+ m4_include([m4/ctype.m4])
+ m4_include([m4/cycle-check.m4])
+ m4_include([m4/d-ino.m4])
+diff --git a/lib/config.hin b/lib/config.hin
+index 9769c39..bf9f9f8 100644
+--- a/lib/config.hin
++++ b/lib/config.hin
+@@ -370,6 +370,9 @@
+ /* Define to 1 when the gnulib module connect should be tested. */
+ #undef GNULIB_TEST_CONNECT
+
++/* Define to 1 when the gnulib module copy-file-range should be tested. */
++#undef GNULIB_TEST_COPY_FILE_RANGE
++
+ /* Define to 1 when the gnulib module dirfd should be tested. */
+ #undef GNULIB_TEST_DIRFD
+
+diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c
+new file mode 100644
+index 0000000..069f144
+--- /dev/null
++++ b/lib/copy-file-range.c
+@@ -0,0 +1,33 @@
++/* Stub for copy_file_range
++ Copyright 2019-2020 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see . */
++
++#include
++
++#include
++
++#include
++
++ssize_t
++copy_file_range (int infd, off_t *pinoff,
++ int outfd, off_t *poutoff,
++ size_t length, unsigned int flags)
++{
++ /* There is little need to emulate copy_file_range with read+write,
++ since programs that use copy_file_range must fall back on
++ read+write anyway. */
++ errno = ENOSYS;
++ return -1;
++}
+diff --git a/lib/gnulib.mk b/lib/gnulib.mk
+index b3633b8..86829f3 100644
+--- a/lib/gnulib.mk
++++ b/lib/gnulib.mk
+@@ -65,6 +65,7 @@
+ # closeout \
+ # config-h \
+ # configmake \
++# copy-file-range \
+ # crypto/md5 \
+ # crypto/sha1 \
+ # crypto/sha256 \
+@@ -800,6 +801,15 @@ CLEANFILES += lib/configmake.h lib/configmake.h-t
+
+ ## end gnulib module configmake
+
++## begin gnulib module copy-file-range
++
++
++EXTRA_DIST += lib/copy-file-range.c
++
++EXTRA_lib_libcoreutils_a_SOURCES += lib/copy-file-range.c
++
++## end gnulib module copy-file-range
++
+ ## begin gnulib module count-leading-zeros
+
+ lib_libcoreutils_a_SOURCES += lib/count-leading-zeros.c
+diff --git a/m4/copy-file-range.m4 b/m4/copy-file-range.m4
+new file mode 100644
+index 0000000..5c5a274
+--- /dev/null
++++ b/m4/copy-file-range.m4
+@@ -0,0 +1,36 @@
++# copy-file-range.m4
++dnl Copyright 2019-2020 Free Software Foundation, Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++AC_DEFUN([gl_FUNC_COPY_FILE_RANGE],
++[
++ AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
++
++ dnl Persuade glibc to declare copy_file_range.
++ AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
++
++ dnl Use AC_LINK_IFELSE, rather than AC_CHECK_FUNCS or a variant,
++ dnl since we don't want AC_CHECK_FUNCS's checks for glibc stubs.
++ dnl Programs that use copy_file_range must fall back on read+write
++ dnl anyway, and there's little point to substituting the Gnulib stub
++ dnl for a glibc stub.
++ AC_CACHE_CHECK([for copy_file_range], [gl_cv_func_copy_file_range],
++ [AC_LINK_IFELSE(
++ [AC_LANG_PROGRAM(
++ [[#include
++ ]],
++ [[ssize_t (*func) (int, off_t *, int, off_t, size_t, unsigned)
++ = copy_file_range;
++ return func (0, 0, 0, 0, 0, 0) & 127;
++ ]])
++ ],
++ [gl_cv_func_copy_file_range=yes],
++ [gl_cv_func_copy_file_range=no])
++ ])
++
++ if test "$gl_cv_func_copy_file_range" != yes; then
++ HAVE_COPY_FILE_RANGE=0
++ fi
++])
+diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
+index dead90e..953e7f0 100644
+--- a/m4/gnulib-comp.m4
++++ b/m4/gnulib-comp.m4
+@@ -129,6 +129,7 @@ AC_DEFUN([gl_EARLY],
+ # Code from module configmake:
+ # Code from module connect:
+ # Code from module connect-tests:
++ # Code from module copy-file-range:
+ # Code from module count-leading-zeros:
+ # Code from module count-leading-zeros-tests:
+ # Code from module crypto/af_alg:
+@@ -977,6 +978,11 @@ AC_DEFUN([gl_INIT],
+ gl_DIRENT_MODULE_INDICATOR([closedir])
+ gl_CONFIG_H
+ gl_CONFIGMAKE_PREP
++ gl_FUNC_COPY_FILE_RANGE
++ if test $HAVE_COPY_FILE_RANGE = 0; then
++ AC_LIBOBJ([copy-file-range])
++ fi
++ gl_UNISTD_MODULE_INDICATOR([copy-file-range])
+ gl_AF_ALG
+ AC_DEFINE([GL_COMPILE_CRYPTO_STREAM], 1, [Compile Gnulib crypto stream ops.])
+ AC_REQUIRE([AC_C_RESTRICT])
+@@ -2746,6 +2752,7 @@ AC_DEFUN([gl_FILE_LIST], [
+ lib/closeout.c
+ lib/closeout.h
+ lib/copy-acl.c
++ lib/copy-file-range.c
+ lib/count-leading-zeros.c
+ lib/count-leading-zeros.h
+ lib/creat-safer.c
+@@ -3438,6 +3445,7 @@ AC_DEFUN([gl_FILE_LIST], [
+ m4/codeset.m4
+ m4/config-h.m4
+ m4/configmake.m4
++ m4/copy-file-range.m4
+ m4/ctype.m4
+ m4/cycle-check.m4
+ m4/d-ino.m4
+--
+2.26.3
+
+
+From 48370c95bcf7c25ce021fbd2145062d3d29ae6d5 Mon Sep 17 00:00:00 2001
+From: Paul Eggert
+Date: Thu, 25 Jun 2020 17:34:23 -0700
+Subject: [PATCH 6/6] cp: use copy_file_range if available
+
+* NEWS: Mention this.
+* bootstrap.conf (gnulib_modules): Add copy-file-range.
+* src/copy.c (sparse_copy): Try copy_file_range if not
+looking for holes.
+
+Upstream-commit: 4b04a0c3b792d27909670a81d21f2a3b3e0ea563
+Signed-off-by: Kamil Dudka
+---
+ bootstrap.conf | 1 +
+ src/copy.c | 40 ++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 41 insertions(+)
+
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 2a342c1..7d53e28 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -54,6 +54,7 @@ gnulib_modules="
+ closeout
+ config-h
+ configmake
++ copy-file-range
+ crypto/md5
+ crypto/sha1
+ crypto/sha256
+diff --git a/src/copy.c b/src/copy.c
+index d88f8cf..4050f69 100644
+--- a/src/copy.c
++++ b/src/copy.c
+@@ -265,6 +265,46 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ {
+ *last_write_made_hole = false;
+ *total_n_read = 0;
++
++ /* If not looking for holes, use copy_file_range if available. */
++ if (!hole_size)
++ while (max_n_read)
++ {
++ /* Copy at most COPY_MAX bytes at a time; this is min
++ (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
++ surely aligned well. */
++ ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
++ ptrdiff_t copy_max = MIN (ssize_max, SIZE_MAX) >> 30 << 30;
++ ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
++ MIN (max_n_read, copy_max), 0);
++ if (n_copied == 0)
++ {
++ /* copy_file_range incorrectly returns 0 when reading from
++ the proc file system on the Linux kernel through at
++ least 5.6.19 (2020), so fall back on 'read' if the
++ input file seems empty. */
++ if (*total_n_read == 0)
++ break;
++ return true;
++ }
++ if (n_copied < 0)
++ {
++ if (errno == ENOSYS || errno == EINVAL
++ || errno == EBADF || errno == EXDEV)
++ break;
++ if (errno == EINTR)
++ n_copied = 0;
++ else
++ {
++ error (0, errno, _("error copying %s to %s"),
++ quoteaf_n (0, src_name), quoteaf_n (1, dst_name));
++ return false;
++ }
++ }
++ max_n_read -= n_copied;
++ *total_n_read += n_copied;
++ }
++
+ bool make_hole = false;
+ off_t psize = 0;
+
+--
+2.26.3
+
diff --git a/coreutils-8.32-mem-leaks.patch b/coreutils-8.32-mem-leaks.patch
new file mode 100644
index 0000000..78a90fc
--- /dev/null
+++ b/coreutils-8.32-mem-leaks.patch
@@ -0,0 +1,186 @@
+From be77b4ab7cb68fd2daf9de90bd75d844392788ac Mon Sep 17 00:00:00 2001
+From: Kamil Dudka
+Date: Thu, 25 Mar 2021 11:57:56 +0100
+Subject: [PATCH 1/4] ln: fix memory leaks in do_link
+
+* src/ln.c (do_link): Free memory allocated by convert_abs_rel
+on all code paths (Bug#47373).
+
+Upstream-commit: 6e98f67758260579d7d44ea5f2df4c82d28c9f58
+Signed-off-by: Kamil Dudka
+---
+ src/ln.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/src/ln.c b/src/ln.c
+index ffa278e..9b52602 100644
+--- a/src/ln.c
++++ b/src/ln.c
+@@ -229,14 +229,14 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ if (errno != ENOENT)
+ {
+ error (0, errno, _("failed to access %s"), quoteaf (dest));
+- return false;
++ goto fail;
+ }
+ force = false;
+ }
+ else if (S_ISDIR (dest_stats.st_mode))
+ {
+ error (0, 0, _("%s: cannot overwrite directory"), quotef (dest));
+- return false;
++ goto fail;
+ }
+ else if (seen_file (dest_set, dest, &dest_stats))
+ {
+@@ -245,7 +245,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ error (0, 0,
+ _("will not overwrite just-created %s with %s"),
+ quoteaf_n (0, dest), quoteaf_n (1, source));
+- return false;
++ goto fail;
+ }
+ else
+ {
+@@ -274,7 +274,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ {
+ error (0, 0, _("%s and %s are the same file"),
+ quoteaf_n (0, source), quoteaf_n (1, dest));
+- return false;
++ goto fail;
+ }
+ }
+
+@@ -285,7 +285,10 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ fprintf (stderr, _("%s: replace %s? "),
+ program_name, quoteaf (dest));
+ if (!yesno ())
+- return true;
++ {
++ free(rel_source);
++ return true;
++ }
+ }
+
+ if (backup_type != no_backups)
+@@ -304,7 +307,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ {
+ error (0, rename_errno, _("cannot backup %s"),
+ quoteaf (dest));
+- return false;
++ goto fail;
+ }
+ force = false;
+ }
+@@ -397,6 +400,10 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ free (backup_base);
+ free (rel_source);
+ return link_errno <= 0;
++
++fail:
++ free (rel_source);
++ return false;
+ }
+
+ void
+--
+2.26.3
+
+
+From c051578e69bd8acf8f8a469566ae34e855345532 Mon Sep 17 00:00:00 2001
+From: Paul Eggert
+Date: Thu, 25 Mar 2021 09:15:50 -0700
+Subject: [PATCH 2/4] maint: indenting
+
+* src/ln.c: Fix indenting.
+
+Upstream-commit: 8980b7c898046d899646da01c296fd15f0cced21
+Signed-off-by: Kamil Dudka
+---
+ src/ln.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/ln.c b/src/ln.c
+index 9b52602..8881d6a 100644
+--- a/src/ln.c
++++ b/src/ln.c
+@@ -286,7 +286,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ program_name, quoteaf (dest));
+ if (!yesno ())
+ {
+- free(rel_source);
++ free (rel_source);
+ return true;
+ }
+ }
+@@ -304,7 +304,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
+ free (backup_base);
+ backup_base = NULL;
+ if (rename_errno != ENOENT)
+- {
++ {
+ error (0, rename_errno, _("cannot backup %s"),
+ quoteaf (dest));
+ goto fail;
+--
+2.26.3
+
+
+From 0d6a4afe5bee0e397fb2fc3b205a29b32a69af9d Mon Sep 17 00:00:00 2001
+From: Paul Eggert
+Date: Thu, 25 Mar 2021 09:16:36 -0700
+Subject: [PATCH 3/4] hostname: use puts
+
+* src/hostname.c (main): Prefer puts to printf "%s\n".
+
+Upstream-commit: c7a588ac3632aae21642d4d568497177950d36bf
+Signed-off-by: Kamil Dudka
+---
+ src/hostname.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/hostname.c b/src/hostname.c
+index 0b5c0cf..62cc98c 100644
+--- a/src/hostname.c
++++ b/src/hostname.c
+@@ -103,7 +103,7 @@ main (int argc, char **argv)
+ hostname = xgethostname ();
+ if (hostname == NULL)
+ die (EXIT_FAILURE, errno, _("cannot determine hostname"));
+- printf ("%s\n", hostname);
++ puts (hostname);
+ }
+
+ if (optind + 1 < argc)
+--
+2.26.3
+
+
+From 19c98d2080251edbaad9fb271aa10ad34f953500 Mon Sep 17 00:00:00 2001
+From: Paul Eggert
+Date: Thu, 25 Mar 2021 11:20:18 -0700
+Subject: [PATCH 4/4] hostname: pacify valgrind
+
+* src/hostname.c (main) [IF_LINT]: Free hostname (Bug#47384).
+
+Upstream-commit: 4698e284f37844bc9b9f63f00eb556ccaaed5030
+Signed-off-by: Kamil Dudka
+---
+ src/hostname.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/hostname.c b/src/hostname.c
+index 62cc98c..7210248 100644
+--- a/src/hostname.c
++++ b/src/hostname.c
+@@ -104,6 +104,7 @@ main (int argc, char **argv)
+ if (hostname == NULL)
+ die (EXIT_FAILURE, errno, _("cannot determine hostname"));
+ puts (hostname);
++ IF_LINT (free (hostname));
+ }
+
+ if (optind + 1 < argc)
+--
+2.26.3
+
diff --git a/coreutils.spec b/coreutils.spec
index 83c0e52..113d46f 100644
--- a/coreutils.spec
+++ b/coreutils.spec
@@ -1,7 +1,7 @@
Summary: A set of basic GNU tools commonly used in shell scripts
Name: coreutils
Version: 8.32
-Release: 19%{?dist}
+Release: 21%{?dist}
License: GPLv3+
Url: https://www.gnu.org/software/coreutils/
Source0: https://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.xz
@@ -43,6 +43,12 @@ Patch9: coreutils-8.32-ls-scontext-crash.patch
# stat: add support for the exfat file system (#1921427)
Patch10: coreutils-8.32-stat-exfat.patch
+# cp: use copy_file_range if available
+Patch11: coreutils-8.32-cp-file-range.patch
+
+# hostname,ln: fix memory leaks detected by Coverity
+Patch12: coreutils-8.32-mem-leaks.patch
+
# disable the test-lock gnulib test prone to deadlock
Patch100: coreutils-8.26-test-lock.patch
@@ -300,6 +306,12 @@ rm -f $RPM_BUILD_ROOT%{_infodir}/dir
%license COPYING
%changelog
+* Fri Mar 26 2021 Kamil Dudka - 8.32-21
+- hostname,ln: fix memory leaks detected by Coverity
+
+* Wed Mar 24 2021 Kamil Dudka - 8.32-20
+- cp: use copy_file_range if available
+
* Thu Feb 18 2021 Kamil Dudka - 8.32-19
- stat: add support for the exfat file system (#1921427)