Package: coreutils
Version: 8.5-1
Severity: wishlist
Tags: patch

Hi,

In order to provide proper mechanisms to avoid race conditions (by the means 
of symlinks) when using chmod, it would be great if it provided a -h/--no-
dereference option just like chown and chgrp do.

Attached patch is a quick implementation that behaves as expected. It works by 
opening (O_RDONLY) the files and using fchmod afterwards. This of course 
implies that files and directories to be chmod(1)ed need to be at least 
O_RDONLY. I couldn't figure out another way to implement this option safely.

I would appreciate your review of the patch, hoping that you also forward it 
upstream. Thanks in advance.

Regards,
-- 
Raphael Geissert - Debian Developer
www.debian.org - get.debian.net
diff -urpN coreutils-8.5-1.orig/src/chmod.c coreutils-8.5-1/src/chmod.c
--- coreutils-8.5-1.orig/src/chmod.c	2010-01-01 07:06:47.000000000 -0600
+++ coreutils-8.5-1/src/chmod.c	2010-12-14 23:35:54.000000000 -0600
@@ -83,6 +83,9 @@ static enum Verbosity verbosity = V_off;
    Otherwise NULL.  */
 static struct dev_ino *root_dev_ino;
 
+/* If true, dereference symbolic links */
+static bool dereference = true;
+
 /* For long options that have no equivalent short option, use a
    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
 enum
@@ -100,6 +103,7 @@ static struct option const long_options[
   {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
   {"quiet", no_argument, NULL, 'f'},
   {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
+  {"no-dereference", no_argument, NULL, 'h'},
   {"silent", no_argument, NULL, 'f'},
   {"verbose", no_argument, NULL, 'v'},
   {GETOPT_HELP_OPTION_DECL},
@@ -258,17 +262,49 @@ process_file (FTS *fts, FTSENT *ent)
       new_mode = mode_adjust (old_mode, S_ISDIR (old_mode) != 0, umask_value,
                               change, NULL);
 
-      if (! S_ISLNK (old_mode))
+      if (dereference)
         {
-          if (chmodat (fts->fts_cwd_fd, file, new_mode) == 0)
-            chmod_succeeded = true;
-          else
+          if (! S_ISLNK (old_mode))
+            {
+              if (chmodat (fts->fts_cwd_fd, file, new_mode) == 0)
+                chmod_succeeded = true;
+              else
+                {
+                  if (! force_silent)
+                    error (0, errno, _("changing permissions of %s"),
+                           quote (file_full_name));
+                  ok = false;
+                }
+            }
+        }
+      else
+        {
+          int fd = openat (fts->fts_cwd_fd, file,
+                           O_RDONLY | O_NOFOLLOW);
+          if (fd == -1)
             {
               if (! force_silent)
-                error (0, errno, _("changing permissions of %s"),
+                error (0, errno, _("opening %s read-only"),
                        quote (file_full_name));
               ok = false;
             }
+          else
+            {
+              if (fchmod (fd, new_mode) == 0)
+                chmod_succeeded = true;
+              else
+                {
+                  if (! force_silent)
+                    error (0, errno, _("changing permissions of %s"),
+                           quote (file_full_name));
+                  ok = false;
+                }
+              if (close (fd) != 0)
+                {
+                  error (0, errno, _("close failed"));
+                  ok = false;
+                }
+            }
         }
     }
 
@@ -377,6 +413,11 @@ Change the mode of each FILE to MODE.\n\
       --preserve-root     fail to operate recursively on `/'\n\
 "), stdout);
       fputs (_("\
+  -h, --no-dereference    affect each symbolic link instead of any referenced\n\
+                          file (useful only on systems that can change the\n\
+                          permissions of a symlink)\n\
+"), stdout);
+      fputs (_("\
   -f, --silent, --quiet   suppress most error messages\n\
   -v, --verbose           output a diagnostic for every file processed\n\
       --reference=RFILE   use RFILE's mode instead of MODE values\n\
@@ -418,7 +459,7 @@ main (int argc, char **argv)
   recurse = force_silent = diagnose_surprises = false;
 
   while ((c = getopt_long (argc, argv,
-                           "Rcfvr::w::x::X::s::t::u::g::o::a::,::+::=::",
+                           "Rcfvrh::w::x::X::s::t::u::g::o::a::,::+::=::",
                            long_options, NULL))
          != -1)
     {
@@ -481,6 +522,9 @@ main (int argc, char **argv)
         case 'f':
           force_silent = true;
           break;
+        case 'h':
+          dereference = false;
+          break;
         case 'v':
           verbosity = V_high;
           break;

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to