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;
signature.asc
Description: This is a digitally signed message part.