]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
chmod: fix erroneous warnings with -R --changes
authorDylan Simon <dylan@dylex.net>
Tue, 18 Mar 2014 15:50:29 +0000 (11:50 -0400)
committerPádraig Brady <P@draigBrady.com>
Wed, 19 Mar 2014 02:33:44 +0000 (02:33 +0000)
For files with "special" bits set, we would stat the relative
file name in the wrong directory, giving an erroneous ENOENT diagnostic.
This issue was introduced with commit v5.92-653-gc1994c1
which changed fts to not change directory on traversal.

* src/chmod.c (mode_changed): Use fts->fts_cwd_fd with fstatat rather
than stat.  All callers changed.
* tests/chmod/c-option.sh: Add a test case.
* NEWS: Mention the fix.
Fixes http://bugs.gnu.org/17035

NEWS
src/chmod.c
tests/chmod/c-option.sh

diff --git a/NEWS b/NEWS
index 35d48e52b26d1d2963c853924c4c595f557445d9..c2caa427edb3a3f4bbae5a25d2d5496dac5d024a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,9 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Bug fixes
 
+  chmod -Rc no longer issues erroneous warnings for files with special bits set.
+  [bug introduced in coreutils-6.0]
+
   cp -a, mv, and install --preserve-context, once again set the correct SELinux
   context for existing directories in the destination.  Previously they set
   the context of an existing directory to that of its last copied descendent.
index 81bf4b227721cea01675ac42f89b7958a71ea5d4..756ec5a46fd25c58613911cf6421698c48c9c841 100644 (file)
@@ -111,7 +111,8 @@ static struct option const long_options[] =
    The old mode was OLD_MODE, but it was changed to NEW_MODE.  */
 
 static bool
-mode_changed (char const *file, mode_t old_mode, mode_t new_mode)
+mode_changed (int dir_fd, char const *file, char const *file_full_name,
+              mode_t old_mode, mode_t new_mode)
 {
   if (new_mode & (S_ISUID | S_ISGID | S_ISVTX))
     {
@@ -120,10 +121,11 @@ mode_changed (char const *file, mode_t old_mode, mode_t new_mode)
 
       struct stat new_stats;
 
-      if (stat (file, &new_stats) != 0)
+      if (fstatat (dir_fd, file, &new_stats, 0) != 0)
         {
           if (! force_silent)
-            error (0, errno, _("getting new attributes of %s"), quote (file));
+            error (0, errno, _("getting new attributes of %s"),
+                   quote (file_full_name));
           return false;
         }
 
@@ -283,7 +285,8 @@ process_file (FTS *fts, FTSENT *ent)
   if (verbosity != V_off)
     {
       bool changed = (chmod_succeeded
-                      && mode_changed (file, old_mode, new_mode));
+                      && mode_changed (fts->fts_cwd_fd, file, file_full_name,
+                                       old_mode, new_mode));
 
       if (changed || verbosity == V_high)
         {
index 1dd9b9ea7a3ce68df781821cb7d6f951ac65ad36..a1782c3d8a54e66d4d9e703eaf3f26281fdf37de 100755 (executable)
@@ -37,4 +37,15 @@ case "$(cat out)" in
   *) cat out; fail=1 ;;
 esac
 
+# From V5.1.0 to 8.22 this would stat the wrong file and
+# give an erroneous ENOENT diagnostic
+mkdir -p a/b || framework_failure_
+# chmod g+s might fail as detailed in setgid.sh
+# but we don't care about those edge cases here
+chmod g+s a/b
+# This should never warn, but it did when special
+# bits are set on b (the common case under test)
+chmod -c -R g+w a 2>err
+compare /dev/null err || fail=1
+
 Exit $fail