Blame SOURCES/coreutils-8.22-mv-hardlinksrace.patch

fc4c3c
diff -urNp coreutils-8.22-orig/src/copy.c coreutils-8.22/src/copy.c
fc4c3c
--- coreutils-8.22-orig/src/copy.c	2015-07-03 14:42:56.829772551 +0200
fc4c3c
+++ coreutils-8.22/src/copy.c	2015-07-03 14:51:05.371383675 +0200
fc4c3c
@@ -1292,20 +1292,12 @@ close_src_desc:
fc4c3c
    copy a regular file onto a symlink that points to it.
fc4c3c
    Try to minimize the cost of this function in the common case.
fc4c3c
    Set *RETURN_NOW if we've determined that the caller has no more
fc4c3c
-   work to do and should return successfully, right away.
fc4c3c
-
fc4c3c
-   Set *UNLINK_SRC if we've determined that the caller wants to do
fc4c3c
-   'rename (a, b)' where 'a' and 'b' are distinct hard links to the same
fc4c3c
-   file. In that case, the caller should try to unlink 'a' and then return
fc4c3c
-   successfully.  Ideally, we wouldn't have to do that, and we'd be
fc4c3c
-   able to rely on rename to remove the source file.  However, POSIX
fc4c3c
-   mistakenly requires that such a rename call do *nothing* and return
fc4c3c
-   successfully.  */
fc4c3c
+   work to do and should return successfully, right away.  */
fc4c3c
 
fc4c3c
 static bool
fc4c3c
 same_file_ok (char const *src_name, struct stat const *src_sb,
fc4c3c
               char const *dst_name, struct stat const *dst_sb,
fc4c3c
-              const struct cp_options *x, bool *return_now, bool *unlink_src)
fc4c3c
+              const struct cp_options *x, bool *return_now)
fc4c3c
 {
fc4c3c
   const struct stat *src_sb_link;
fc4c3c
   const struct stat *dst_sb_link;
fc4c3c
@@ -1316,7 +1308,6 @@ same_file_ok (char const *src_name, stru
fc4c3c
   bool same = SAME_INODE (*src_sb, *dst_sb);
fc4c3c
 
fc4c3c
   *return_now = false;
fc4c3c
-  *unlink_src = false;
fc4c3c
 
fc4c3c
   /* FIXME: this should (at the very least) be moved into the following
fc4c3c
      if-block.  More likely, it should be removed, because it inhibits
fc4c3c
@@ -1348,14 +1339,11 @@ same_file_ok (char const *src_name, stru
fc4c3c
               /* Here we have two symlinks that are hard-linked together,
fc4c3c
                  and we're not making backups.  In this unusual case, simply
fc4c3c
                  returning true would lead to mv calling "rename(A,B)",
fc4c3c
-                 which would do nothing and return 0.  I.e., A would
fc4c3c
-                 not be removed.  Hence, the solution is to tell the
fc4c3c
-                 caller that all it must do is unlink A and return.  */
fc4c3c
+                 which would do nothing and return 0.  */
fc4c3c
               if (same_link)
fc4c3c
                 {
fc4c3c
-                  *unlink_src = true;
fc4c3c
                   *return_now = true;
fc4c3c
-                  return true;
fc4c3c
+                  return ! x->move_mode;
fc4c3c
                 }
fc4c3c
             }
fc4c3c
 
fc4c3c
@@ -1443,26 +1431,22 @@ same_file_ok (char const *src_name, stru
fc4c3c
     return true;
fc4c3c
 #endif
fc4c3c
 
fc4c3c
-  /* They may refer to the same file if we're in move mode and the
fc4c3c
-     target is a symlink.  That is ok, since we remove any existing
fc4c3c
-     destination file before opening it -- via 'rename' if they're on
fc4c3c
-     the same file system, via 'unlink (DST_NAME)' otherwise.
fc4c3c
-     It's also ok if they're distinct hard links to the same file.  */
fc4c3c
   if (x->move_mode || x->unlink_dest_before_opening)
fc4c3c
     {
fc4c3c
+     /* They may refer to the same file if we're in move mode and the
fc4c3c
+        target is a symlink.  That is ok, since we remove any existing
fc4c3c
+        destination file before opening it -- via 'rename' if they're on
fc4c3c
+        the same file system, via 'unlink (DST_NAME)' otherwise.  */
fc4c3c
       if (S_ISLNK (dst_sb_link->st_mode))
fc4c3c
         return true;
fc4c3c
 
fc4c3c
+      /* It's not ok if they're distinct hard links to the same file as
fc4c3c
+         this causes a race condition and we may lose data in this case.  */
fc4c3c
       if (same_link
fc4c3c
           && 1 < dst_sb_link->st_nlink
fc4c3c
           && ! same_name (src_name, dst_name))
fc4c3c
         {
fc4c3c
-          if (x->move_mode)
fc4c3c
-            {
fc4c3c
-              *unlink_src = true;
fc4c3c
-              *return_now = true;
fc4c3c
-            }
fc4c3c
-          return true;
fc4c3c
+          return ! x->move_mode;
fc4c3c
         }
fc4c3c
     }
fc4c3c
 
fc4c3c
@@ -1820,11 +1804,10 @@ copy_internal (char const *src_name, cha
fc4c3c
         { /* Here, we know that dst_name exists, at least to the point
fc4c3c
              that it is stat'able or lstat'able.  */
fc4c3c
           bool return_now;
fc4c3c
-          bool unlink_src;
fc4c3c
 
fc4c3c
           have_dst_lstat = !use_stat;
fc4c3c
           if (! same_file_ok (src_name, &src_sb, dst_name, &dst_sb,
fc4c3c
-                              x, &return_now, &unlink_src))
fc4c3c
+                              x, &return_now))
fc4c3c
             {
fc4c3c
               error (0, 0, _("%s and %s are the same file"),
fc4c3c
                      quote_n (0, src_name), quote_n (1, dst_name));
fc4c3c
@@ -1883,22 +1866,14 @@ copy_internal (char const *src_name, cha
fc4c3c
              cp and mv treat -i and -f differently.  */
fc4c3c
           if (x->move_mode)
fc4c3c
             {
fc4c3c
-              if (abandon_move (x, dst_name, &dst_sb)
fc4c3c
-                  || (unlink_src && unlink (src_name) == 0))
fc4c3c
+              if (abandon_move (x, dst_name, &dst_sb))
fc4c3c
                 {
fc4c3c
                   /* Pretend the rename succeeded, so the caller (mv)
fc4c3c
                      doesn't end up removing the source file.  */
fc4c3c
                   if (rename_succeeded)
fc4c3c
                     *rename_succeeded = true;
fc4c3c
-                  if (unlink_src && x->verbose)
fc4c3c
-                    printf (_("removed %s\n"), quote (src_name));
fc4c3c
                   return true;
fc4c3c
                 }
fc4c3c
-              if (unlink_src)
fc4c3c
-                {
fc4c3c
-                  error (0, errno, _("cannot remove %s"), quote (src_name));
fc4c3c
-                  return false;
fc4c3c
-                }
fc4c3c
             }
fc4c3c
           else
fc4c3c
             {
fc4c3c
diff -urNp coreutils-8.22-orig/tests/cp/same-file.sh coreutils-8.22/tests/cp/same-file.sh
fc4c3c
--- coreutils-8.22-orig/tests/cp/same-file.sh	2013-12-04 15:48:30.000000000 +0100
fc4c3c
+++ coreutils-8.22/tests/cp/same-file.sh	2015-07-03 14:54:12.539772880 +0200
fc4c3c
@@ -36,7 +36,7 @@ ln dangling-slink hard-link > /dev/null 2>&1 \
fc4c3c
 rm -f no-such dangling-slink hard-link
fc4c3c
 
fc4c3c
 test $hard_link_to_symlink_does_the_deref = yes \
fc4c3c
-    && remove_these_sed='/^0 -[bf]*l .*sl1 ->/d' \
fc4c3c
+    && remove_these_sed='/^0 -[bf]*l .*sl1 ->/d; /hlsl/d' \
fc4c3c
     || remove_these_sed='/^ELIDE NO TEST OUTPUT/d'
fc4c3c
 
fc4c3c
 exec 3>&1 1> actual
fc4c3c
@@ -44,7 +44,8 @@ exec 3>&1 1> actual
fc4c3c
 # FIXME: This should be bigger: like more than 8k
fc4c3c
 contents=XYZ
fc4c3c
 
fc4c3c
-for args in 'foo symlink' 'symlink foo' 'foo foo' 'sl1 sl2' 'foo hardlink'; do
fc4c3c
+for args in 'foo symlink' 'symlink foo' 'foo foo' 'sl1 sl2' \
fc4c3c
+  'foo hardlink' 'hlsl sl2'; do
fc4c3c
   for options in '' -d -f -df --rem -b -bd -bf -bdf \
fc4c3c
                  -l -dl -fl -dfl -bl -bdl -bfl -bdfl; do
fc4c3c
     case $args$options in
fc4c3c
@@ -76,6 +77,8 @@ for args in 'foo symlink' 'symlink foo'
fc4c3c
         continue ;;
fc4c3c
       'yes:sl1 sl2:-bfl')
fc4c3c
         continue ;;
fc4c3c
+      yes:hlsl*)
fc4c3c
+        continue ;;
fc4c3c
     esac
fc4c3c
 
fc4c3c
     rm -rf dir
fc4c3c
@@ -86,6 +87,7 @@ for args in 'foo symlink' 'symlink foo'
fc4c3c
     case "$args" in *hardlink*) ln foo hardlink ;; esac
fc4c3c
     case "$args" in *sl1*) ln -s foo sl1;; esac
fc4c3c
     case "$args" in *sl2*) ln -s foo sl2;; esac
fc4c3c
+    case "$args" in *hlsl*) ln sl2 hlsl;;esac
fc4c3c
     (
fc4c3c
       (
fc4c3c
         # echo 1>&2 cp $options $args
fc4c3c
@@ -211,6 +213,24 @@ cat <<\EOF | sed "$remove_these_sed" > e
fc4c3c
 0 -bfl (foo hardlink)
fc4c3c
 0 -bdfl (foo hardlink)
fc4c3c
 
fc4c3c
+1 [cp: 'hlsl' and 'sl2' are the same file] (foo hlsl -> foo sl2 -> foo)
fc4c3c
+0 -d (foo hlsl -> foo sl2 -> foo)
fc4c3c
+1 -f [cp: 'hlsl' and 'sl2' are the same file] (foo hlsl -> foo sl2 -> foo)
fc4c3c
+0 -df (foo hlsl -> foo sl2 -> foo)
fc4c3c
+0 --rem (foo hlsl -> foo sl2)
fc4c3c
+0 -b (foo hlsl -> foo sl2 sl2.~1~ -> foo)
fc4c3c
+0 -bd (foo hlsl -> foo sl2 -> foo sl2.~1~ -> foo)
fc4c3c
+0 -bf (foo hlsl -> foo sl2 sl2.~1~ -> foo)
fc4c3c
+0 -bdf (foo hlsl -> foo sl2 -> foo sl2.~1~ -> foo)
fc4c3c
+1 -l [cp: cannot create hard link 'sl2' to 'hlsl'] (foo hlsl -> foo sl2 -> foo)
fc4c3c
+0 -dl (foo hlsl -> foo sl2 -> foo)
fc4c3c
+0 -fl (foo hlsl -> foo sl2)
fc4c3c
+0 -dfl (foo hlsl -> foo sl2 -> foo)
fc4c3c
+0 -bl (foo hlsl -> foo sl2 sl2.~1~ -> foo)
fc4c3c
+0 -bdl (foo hlsl -> foo sl2 -> foo)
fc4c3c
+0 -bfl (foo hlsl -> foo sl2 sl2.~1~ -> foo)
fc4c3c
+0 -bdfl (foo hlsl -> foo sl2 -> foo)
fc4c3c
+
fc4c3c
 EOF
fc4c3c
 
fc4c3c
 exec 1>&3 3>&-
fc4c3c
diff -urNp coreutils-8.22-orig/tests/local.mk coreutils-8.22/tests/local.mk
fc4c3c
--- coreutils-8.22-orig/tests/local.mk	2015-07-03 14:42:56.820772485 +0200
fc4c3c
+++ coreutils-8.22/tests/local.mk	2015-07-03 14:55:07.060176869 +0200
fc4c3c
@@ -591,7 +591,6 @@ all_tests =					\
fc4c3c
   tests/mv/hard-3.sh				\
fc4c3c
   tests/mv/hard-4.sh				\
fc4c3c
   tests/mv/hard-link-1.sh			\
fc4c3c
-  tests/mv/hard-verbose.sh			\
fc4c3c
   tests/mv/i-1.pl				\
fc4c3c
   tests/mv/i-2.sh				\
fc4c3c
   tests/mv/i-3.sh				\
fc4c3c
diff -urNp coreutils-8.22-orig/tests/mv/force.sh coreutils-8.22/tests/mv/force.sh
fc4c3c
--- coreutils-8.22-orig/tests/mv/force.sh	2013-12-04 15:48:30.000000000 +0100
fc4c3c
+++ coreutils-8.22/tests/mv/force.sh	2015-07-03 14:56:42.840885931 +0200
fc4c3c
@@ -25,18 +25,19 @@ ff2=mvforce2
fc4c3c
 echo force-contents > $ff || framework_failure_
fc4c3c
 ln $ff $ff2 || framework_failure_
fc4c3c
 
fc4c3c
-# This mv command should exit nonzero.
fc4c3c
-mv $ff $ff > out 2>&1 && fail=1
fc4c3c
+# mv should fail for the same name, or separate hardlinks as in
fc4c3c
+# both cases rename() will do nothing and return success.
fc4c3c
+# One could unlink(src) in the hardlink case, but that would
fc4c3c
+# introduce races with overlapping mv instances removing both hardlinks.
fc4c3c
 
fc4c3c
-cat > exp <
fc4c3c
-mv: '$ff' and '$ff' are the same file
fc4c3c
-EOF
fc4c3c
+for dest in $ff $ff2; do
fc4c3c
+  # This mv command should exit nonzero.
fc4c3c
+  mv $ff $dest > out 2>&1 && fail=1
fc4c3c
 
fc4c3c
-compare exp out || fail=1
fc4c3c
-test $(cat $ff) = force-contents || fail=1
fc4c3c
+  printf "mv: '$ff' and '$dest' are the same file\n" > exp
fc4c3c
+  compare exp out || fail=1
fc4c3c
 
fc4c3c
-# This should succeed, even though the source and destination
fc4c3c
-# device and inodes are the same.
fc4c3c
-mv $ff $ff2 || fail=1
fc4c3c
+  test $(cat $ff) = force-contents || fail=1
fc4c3c
+done
fc4c3c
 
fc4c3c
 Exit $fail
fc4c3c
diff -urNp coreutils-8.22-orig/tests/mv/hard-4.sh coreutils-8.22/tests/mv/hard-4.sh
fc4c3c
--- coreutils-8.22-orig/tests/mv/hard-4.sh	2013-12-04 15:48:30.000000000 +0100
fc4c3c
+++ coreutils-8.22/tests/mv/hard-4.sh	2015-07-03 14:58:31.179687188 +0200
fc4c3c
@@ -1,5 +1,5 @@
fc4c3c
 #!/bin/sh
fc4c3c
-# ensure that mv removes a in this case: touch a; ln a b; mv a b
fc4c3c
+# ensure that mv maintains a in this case: touch a; ln a b; mv a b
fc4c3c
 
fc4c3c
 # Copyright (C) 2003-2013 Free Software Foundation, Inc.
fc4c3c
 
fc4c3c
@@ -21,15 +21,19 @@ print_ver_ mv
fc4c3c
 touch a || framework_failure_
fc4c3c
 ln a b || framework_failure_
fc4c3c
 
fc4c3c
+# Between coreutils-5.0 and coreutils-8.24, 'a' would be removed.
fc4c3c
+# Before coreutils-5.0.1 the issue would not have been diagnosed.
fc4c3c
+# We don't emulate the rename(a,b) with unlink(a) as that would
fc4c3c
+# introduce races with overlapping mv instances removing both links.
fc4c3c
+mv a b 2>err && fail=1
fc4c3c
+printf "mv: 'a' and 'b' are the same file\n" > exp
fc4c3c
+compare exp err || fail=1
fc4c3c
 
fc4c3c
-mv a b || fail=1
fc4c3c
 
fc4c3c
-# In coreutils-5.0 and earlier, a would not be removed.
fc4c3c
-test -r a && fail=1
fc4c3c
+test -r a || fail=1
fc4c3c
 test -r b || fail=1
fc4c3c
 
fc4c3c
-# Make sure it works also with --backup.
fc4c3c
-ln b a
fc4c3c
+# Make sure it works with --backup.
fc4c3c
 mv --backup=simple a b || fail=1
fc4c3c
 test -r a && fail=1
fc4c3c
 test -r b || fail=1
fc4c3c
diff -urNp coreutils-8.22-orig/tests/mv/i-4.sh coreutils-8.22/tests/mv/i-4.sh
fc4c3c
--- coreutils-8.22-orig/tests/mv/i-4.sh	2013-12-04 15:48:30.000000000 +0100
fc4c3c
+++ coreutils-8.22/tests/mv/i-4.sh	2015-07-03 15:00:39.533718254 +0200
fc4c3c
@@ -23,6 +23,7 @@ for i in a b; do
fc4c3c
   echo $i > $i || framework_failure_
fc4c3c
 done
fc4c3c
 echo y > y || framework_failure_
fc4c3c
+echo n > n || framework_failure_
fc4c3c
 
fc4c3c
 mv -i a b < y >/dev/null 2>&1 || fail=1
fc4c3c
 
fc4c3c
@@ -32,18 +33,15 @@ case "$(cat b)" in
fc4c3c
   *) fail=1 ;;
fc4c3c
 esac
fc4c3c
 
fc4c3c
-# Ensure that mv -i a b works properly with 'n' and 'y'
fc4c3c
-# responses, even when a and b are hard links to the same file.
fc4c3c
-# This 'n' test would fail (no prompt) for coreutils-5.0.1 through 5.3.0.
fc4c3c
-echo n > n
fc4c3c
+# Ensure that mv -i a b works properly with 'n' and 'y' responses,
fc4c3c
+# when a and b are hard links to the same file.
fc4c3c
 rm -f a b
fc4c3c
 echo a > a
fc4c3c
 ln a b
fc4c3c
-mv -i a b < n >/dev/null 2>&1 || fail=1
fc4c3c
+mv -i a b < y 2>err && fail=1
fc4c3c
 test -r a || fail=1
fc4c3c
 test -r b || fail=1
fc4c3c
-mv -i a b < y >/dev/null 2>&1 || fail=1
fc4c3c
-test -r a && fail=1
fc4c3c
-test -r b || fail=1
fc4c3c
+printf "mv: 'a' and 'b' are the same file\n" > exp
fc4c3c
+compare exp err || fail=1
fc4c3c
 
fc4c3c
 Exit $fail
fc4c3c
diff -urNp coreutils-8.22-orig/tests/mv/symlink-onto-hardlink-to-self.sh coreutils-8.22/tests/mv/symlink-onto-hardlink-to-self.sh
fc4c3c
--- coreutils-8.22-orig/tests/mv/symlink-onto-hardlink-to-self.sh	2013-12-04 15:48:30.000000000 +0100
fc4c3c
+++ coreutils-8.22/tests/mv/symlink-onto-hardlink-to-self.sh	2015-07-03 15:01:39.209190741 +0200
fc4c3c
@@ -1,10 +1,10 @@
fc4c3c
 #!/bin/sh
fc4c3c
-# Demonstrate that when moving a symlink onto a hardlink-to-that-symlink, the
fc4c3c
-# source symlink is removed.  Depending on your kernel (e.g., Linux, Solaris,
fc4c3c
+# Demonstrate that when moving a symlink onto a hardlink-to-that-symlink,
fc4c3c
+# an error is presented.  Depending on your kernel (e.g., Linux, Solaris,
fc4c3c
 # but not NetBSD), prior to coreutils-8.16, the mv would successfully perform
fc4c3c
 # a no-op.  I.e., surprisingly, mv s1 s2 would succeed, yet fail to remove s1.
fc4c3c
 
fc4c3c
-# Copyright (C) 2012-2013 Free Software Foundation, Inc.
fc4c3c
+# Copyright (C) 2012-2014 Free Software Foundation, Inc.
fc4c3c
 
fc4c3c
 # This program is free software: you can redistribute it and/or modify
fc4c3c
 # it under the terms of the GNU General Public License as published by
fc4c3c
@@ -26,27 +26,34 @@ print_ver_ mv
fc4c3c
 touch f || framework_failure_
fc4c3c
 ln -s f s2 || framework_failure_
fc4c3c
 
fc4c3c
-for opt in '' --backup; do
fc4c3c
+# Attempt to create a hard link to that symlink.
fc4c3c
+# On some systems, it's not possible: they create a hard link to the referent.
fc4c3c
+ln s2 s1 || framework_failure_
fc4c3c
+
fc4c3c
+# If s1 is not a symlink, skip this test.
fc4c3c
+test -h s1 \
fc4c3c
+  || skip_ your kernel or file system cannot create a hard link to a symlink
fc4c3c
 
fc4c3c
-  # Attempt to create a hard link to that symlink.
fc4c3c
-  # On some systems, it's not possible: they create a hard link to the referent.
fc4c3c
-  ln s2 s1 || framework_failure_
fc4c3c
-
fc4c3c
-  # If s1 is not a symlink, skip this test.
fc4c3c
-  test -h s1 \
fc4c3c
-    || skip_ your kernel or file system cannot create a hard link to a symlink
fc4c3c
+for opt in '' --backup; do
fc4c3c
 
fc4c3c
-  mv $opt s1 s2 > out 2>&1 || fail=1
fc4c3c
-  compare /dev/null out || fail=1
fc4c3c
+  if test "$opt" = --backup; then
fc4c3c
+    mv $opt s1 s2 > out 2>&1 || fail=1
fc4c3c
+    compare /dev/null out || fail=1
fc4c3c
 
fc4c3c
-  # Ensure that s1 is gone.
fc4c3c
-  test -e s1 && fail=1
fc4c3c
+    # Ensure that s1 is gone.
fc4c3c
+    test -e s1 && fail=1
fc4c3c
 
fc4c3c
-  if test "$opt" = --backup; then
fc4c3c
     # With --backup, ensure that the backup file was created.
fc4c3c
     ref=$(readlink s2~) || fail=1
fc4c3c
     test "$ref" = f || fail=1
fc4c3c
   else
fc4c3c
+    echo "mv: 's1' and 's2' are the same file" > exp
fc4c3c
+    mv $opt s1 s2 2>err && fail=1
fc4c3c
+    compare exp err || fail=1
fc4c3c
+
fc4c3c
+    # Ensure that s1 is still present.
fc4c3c
+    test -e s1 || fail=1
fc4c3c
+
fc4c3c
     # Without --backup, ensure there is no backup file.
fc4c3c
     test -e s2~ && fail=1
fc4c3c
   fi