]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
cp: don’t remove nonempty cloned dest
authorPaul Eggert <eggert@cs.ucla.edu>
Wed, 6 Jul 2022 19:29:12 +0000 (14:29 -0500)
committerPaul Eggert <eggert@cs.ucla.edu>
Wed, 6 Jul 2022 19:31:32 +0000 (14:31 -0500)
This follows up on comments by Pádraig Brady (bug#56391).
* src/copy.c (copy_reg): When --reflink=always removes a file
due to an FICLONE failure, do not remove a nonempty file.

NEWS
src/copy.c

diff --git a/NEWS b/NEWS
index a3a55541e08868872dd2c6deb9fd702c5db86854..b4e3cf83ad26e3b98cd69ae66308034bee42dabd 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,9 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Changes in behavior
 
+  'cp --reflink=always A B' no longer leaves behind a newly created
+  empty file B merely because copy-on-write clones are not supported.
+
   'ls -v' and 'sort -V' go back to sorting ".0" before ".A",
   reverting to the behavior in coreutils-9.0 and earlier.
   This behavior is now documented.
index eaed148b4f07124d58104c00ab2dab3cd7768dae..e465271efac43d9d51486b874c3f6f4d7cb55330 100644 (file)
@@ -1279,9 +1279,17 @@ copy_reg (char const *src_name, char const *dst_name,
         {
           error (0, errno, _("failed to clone %s from %s"),
                  quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
-          if (*new_dst && unlinkat (dst_dirfd, dst_relname, 0) != 0
-              && errno != ENOENT)
+
+          /* Remove the destination if cp --reflink=always created it
+             but cloned no data.  If clone_file failed with
+             EOPNOTSUPP, EXDEV or EINVAL no data were copied so do not
+             go to the expense of lseeking.  */
+          if (*new_dst
+              && (is_ENOTSUP (errno) || errno == EXDEV || errno == EINVAL
+                  || lseek (dest_desc, 0, SEEK_END) == 0)
+              && unlinkat (dst_dirfd, dst_relname, 0) != 0 && errno != ENOENT)
             error (0, errno, _("cannot remove %s"), quoteaf (dst_name));
+
           return_val = false;
           goto close_src_and_dst_desc;
         }