]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
cp: with --force; replace self referential symlinks
authorPádraig Brady <P@draigBrady.com>
Wed, 16 May 2018 06:41:36 +0000 (23:41 -0700)
committerPádraig Brady <P@draigBrady.com>
Fri, 18 May 2018 03:52:09 +0000 (20:52 -0700)
* src/copy.c (copy_internal): Don't fail immediately upon
getting ELOOP when running stat() on the destination,
rather proceeding if -f specified, allowing the link
to be removed.  If the loop is not in the final component
of the destination path, we still fail but at the
subsequent unlink() stage.
* doc/coreutils.texi (cp invocation): Adjust wording to say
that --force doesn't work with dangling links, rather than
all links that can't be traversed.
* tests/cp/thru-dangling.sh: Add a test case.
* NEWS: Mention the change in behavior.
Discussed in https://bugs.gnu.org/31335

NEWS
doc/coreutils.texi
src/copy.c
tests/cp/thru-dangling.sh

diff --git a/NEWS b/NEWS
index f981596ba905c5d331b64a741dcb55616de7b8b1..4c63b6d7cbea821e34dce23a821b6b500c0ebdb8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   now silently does nothing if A exists.
   [bug introduced with coreutils-7.1]
 
+** Changes in behavior
+
+  'cp --force file symlink' now removes the symlink even if
+  it is self referential.
 
 ** Improvements
 
index c8d9bd9798083be499b7a53d6e9feb9967542c3e..c28b8d0435f8056a73de0cc15b2f5d8af3e94c47 100644 (file)
@@ -8535,7 +8535,7 @@ When copying without this option and an existing destination file cannot
 be opened for writing, the copy fails.  However, with @option{--force},
 when a destination file cannot be opened, @command{cp} then
 tries to recreate the file by first removing it.  Note @option{--force}
-alone will not remove symlinks that can't be traversed.
+alone will not remove dangling symlinks.
 When this option is combined with
 @option{--link} (@option{-l}) or @option{--symbolic-link}
 (@option{-s}), the destination link is replaced, and unless
index 0407c5689b8e7eb9837ba9900e8b6eb1c79e929f..f4c92d7d7fcd7b73f55371076b9c22314a1d5954 100644 (file)
@@ -1949,12 +1949,15 @@ copy_internal (char const *src_name, char const *dst_name,
             }
           else
             {
-              if (errno != ENOENT)
+              if (errno == ELOOP && x->unlink_dest_after_failed_open)
+                /* leave new_dst=false so we unlink later.  */;
+              else if (errno != ENOENT)
                 {
                   error (0, errno, _("cannot stat %s"), quoteaf (dst_name));
                   return false;
                 }
-              new_dst = true;
+              else
+                new_dst = true;
             }
         }
 
index 8fd452e8143f214b0453d76ea0e8aab3bada98ce..e4335255f3b6534f4a7e6cef3e4594ece9f20986 100755 (executable)
@@ -28,15 +28,24 @@ echo "cp: not writing through dangling symlink 'dangle'" \
 
 # Starting with 6.9.90, this usage fails, by default:
 for opt in '' '-f'; do
-  cp $opt f dangle > err 2>&1 && fail=1
+  returns_ 1 cp $opt f dangle > err 2>&1 || fail=1
   compare exp-err err || fail=1
   test -f no-such && fail=1
 done
 
+
 # But you can set POSIXLY_CORRECT to get the historical behavior.
 env POSIXLY_CORRECT=1 cp f dangle > out 2>&1 || fail=1
 cat no-such >> out || fail=1
-
 compare exp out || fail=1
 
+
+# Starting with 8.30 we treat ELOOP as existing and so
+# remove the symlink
+ln -s loop loop || framework_failure_
+cp -f f loop > err 2>&1 || fail=1
+compare /dev/null err || fail=1
+compare exp loop || fail=1
+test -f loop || fail=1
+
 Exit $fail