Subject: bug#17035: [PATCH] chmod -c -R produces errors with special permissions

From: Dylan Alex Simon <[email-masked]>
Date: Tue, 18 Mar 2014 12:06:31 -0400

chmod sometimes tries to stat files in the wrong directory when reporting
changes:

> mkdir -p a/b
> chmod -R g+s a/b # requires some special permission bit set first
> chmod -c -R g+w a
chmod: getting new attributes of 'b': No such file or directory

A fix is attached.
>From 8e4f39ea022c9305d43ebd2823cf07b5daf682a9 Mon Sep 17 00:00:00 2001
From: Dylan Simon <[email-masked]>
Date: Tue, 18 Mar 2014 11:50:29 -0400
Subject: [PATCH] chmod: fix mode_changed check

* src/chmod.c (mode_changed): Use fts->fts_cwd_fd with fstatat rather
than stat. All callers changed.
---
 src/chmod.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/chmod.c b/src/chmod.c
index 81bf4b2..f9debde 100644
--- a/src/chmod.c
+++ b/src/chmod.c
@@ -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 dirfd, 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,10 @@ 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 (dirfd, 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 +284,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)
         {
-- 
1.9.0