Subject: bug#40352: closed (Re: bug#40352: cp -a --attributes-only zeroes a file with more than one link)

From: [email-masked] (GNU bug Tracking System)
Date: Wed, 01 Apr 2020 12:11:02 +0000

Your bug report

#40352: cp -a --attributes-only zeroes a file with more than one link

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
40352: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=40352
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#40352: cp -a --attributes-only zeroes a file with more than one link
From: Pádraig Brady <[email-masked]>
To: Matt Kloss <[email-masked]>, [email-masked]
Date: Wed, 1 Apr 2020 13:10:02 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

On 31/03/2020 13:32, Matt Kloss wrote:
>     Hello,
> 
>     When you cp -a --attributes-only a [dest] file which has a more than one
>     “hardlink”, it zeroes the file:

The attached fixes this, which I'll push later.
Marking this as done.
thanks!

Pádraig
>From af51efbe11aeba9fc2609dd69d622cc170b484a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Wed, 1 Apr 2020 12:51:34 +0100
Subject: [PATCH] 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
---
 NEWS                      |  7 +++++++
 src/copy.c                |  9 +++++----
 tests/cp/attr-existing.sh | 21 ++++++++++++++++++---
 3 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/NEWS b/NEWS
index 653e7178b..b8a17c276 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,13 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 * Noteworthy changes in release ?.? (????-??-??) [?]
 
+** Bug fixes
+
+  cp -a --attributes-only now never removes destination files,
+  even if the destination files are hardlinked, or the source
+  is a non regular file.
+  [bug introduced in coreutils-8.6]
+
 ** Changes in behavior
 
   On GNU/Linux systems, ls no longer issues an error message on
diff --git a/src/copy.c b/src/copy.c
index 6e5efc708..54601ce07 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 59ce64183..14fc8445c 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.24.1

Subject: cp -a --attributes-only zeroes a file with more than one link
From: Matt Kloss <[email-masked]>
To: "[email-masked]" <[email-masked]>
Date: Tue, 31 Mar 2020 14:32:13 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

When you cp -a --attributes-only a file which has a more than one “hardlink”, it zeroes the file:

$ echo hello > aaa

$ cp aaa foo

$ chmod 777 aaa

$ ls -li

total 8

407584 -rwxrwxrwx 1 matt matt 6 mars  31 14:19 aaa

399313 -rw-r--r-- 1 matt matt 6 mars  31 14:19 foo

$ cp -a --attributes-only aaa foo   # this is fine

$ ls -li

total 8

407584 -rwxrwxrwx 1 matt matt 6 mars  31 14:19 aaa

399313 -rwxrwxrwx 1 matt matt 6 mars  31 14:19 foo

$ ln foo foo2

$ cp -a --attributes-only aaa foo   # here comes the bug

$ ls -li

total 8

407584 -rwxrwxrwx 1 matt matt 6 mars  31 14:19 aaa

407740 -rwxrwxrwx 1 matt matt 0 mars  31 14:19 foo

399313 -rwxrwxrwx 1 matt matt 6 mars  31 14:19 foo2

foo has a size of 0 bytes!

The user thinks he's replicating timestamp and permissions, and he's actually deleting data. (Well, the data is still in foo2, but...)

coreutils 8.30

Matt