[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Chmod: -L option
From: |
Jakub Martisko |
Subject: |
Re: Chmod: -L option |
Date: |
Mon, 28 Nov 2016 09:51:18 +0100 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.6.0 |
I've modified the patch form the link mentioned in the
original post and added few tests. Any comments are welcomed.
JM
On 23.11.2016 08:41, Jakub Martisko wrote:
> Hello, according to the man page: "In contrast, chmod
> ignores symbolic links encountered during recursive
> directory traversals". Is there any reason for this? I've
> done a bit of mailing list archaeology and found this [1]
> thread with patch. I might try to rewrite the patch for
> current version and add some tests if there is no reason
> that would discourage from having this functionality.
>
> JM
>
>
> [1]
> http://lists.gnu.org/archive/html/bug-coreutils/2011-12/msg00089.html
>
>From dca59f52c45d54601ecb8862a3755ddb1bea900c Mon Sep 17 00:00:00 2001
From: Jakub Martisko <address@hidden>
Date: Mon, 28 Nov 2016 09:13:39 +0100
Subject: [PATCH] chmod: add support for -L -P -H -h --dereference options
* src/chmod.c support for options mentioned above.
Achieved by usage of FTS_ bitflags. Patch based on:
lists.gnu.org/archive/html/bug-coreutils/2011-12/msg00089.html
* tests/chmod/symlinks.sh add test cases for the new
options.
* tests/local.mk add the new tests.
* man/chmod.x new options mentioned.
---
man/chmod.x | 4 +-
src/chmod.c | 108 +++++++++++++++++++++++++++++++++++++++++++-----
tests/chmod/symlinks.sh | 93 +++++++++++++++++++++++++++++++++++++++++
tests/local.mk | 1 +
4 files changed, 195 insertions(+), 11 deletions(-)
create mode 100644 tests/chmod/symlinks.sh
diff --git a/man/chmod.x b/man/chmod.x
index 5761d36..de0f773 100644
--- a/man/chmod.x
+++ b/man/chmod.x
@@ -70,7 +70,9 @@ changes the permissions of the pointed-to file.
In contrast,
.B chmod
ignores symbolic links encountered during recursive directory
-traversals.
+traversals. Options that modify this behavior are described in the
+.B OPTIONS
+section.
.SH "SETUID AND SETGID BITS"
.B chmod
clears the set-group-ID bit of a
diff --git a/src/chmod.c b/src/chmod.c
index 89e2232..5cd0970 100644
--- a/src/chmod.c
+++ b/src/chmod.c
@@ -68,6 +68,10 @@ static mode_t umask_value;
/* If true, change the modes of directories recursively. */
static bool recurse;
+/* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
+ specified. */
+static int dereference = -1;
+
/* If true, force silence (suppress most of error messages). */
static bool force_silent;
@@ -87,7 +91,8 @@ static struct dev_ino *root_dev_ino;
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- NO_PRESERVE_ROOT = CHAR_MAX + 1,
+ DEREFERENCE_OPTION = CHAR_MAX + 1,
+ NO_PRESERVE_ROOT,
PRESERVE_ROOT,
REFERENCE_FILE_OPTION
};
@@ -95,7 +100,9 @@ enum
static struct option const long_options[] =
{
{"changes", no_argument, NULL, 'c'},
+ {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
{"recursive", no_argument, NULL, 'R'},
+ {"no-dereference", no_argument, NULL, 'h'},
{"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
{"preserve-root", no_argument, NULL, PRESERVE_ROOT},
{"quiet", no_argument, NULL, 'f'},
@@ -190,11 +197,13 @@ process_file (FTS *fts, FTSENT *ent)
char const *file_full_name = ent->fts_path;
char const *file = ent->fts_accpath;
const struct stat *file_stats = ent->fts_statp;
+ struct stat stat_buf;
mode_t old_mode IF_LINT ( = 0);
mode_t new_mode IF_LINT ( = 0);
bool ok = true;
bool chmod_succeeded = false;
+
switch (ent->fts_info)
{
case FTS_DP:
@@ -234,10 +243,29 @@ process_file (FTS *fts, FTSENT *ent)
break;
case FTS_SLNONE:
- if (! force_silent)
- error (0, 0, _("cannot operate on dangling symlink %s"),
- quoteaf (file_full_name));
- ok = false;
+ if (dereference)
+ {
+ if (! force_silent)
+ error (0, 0, _("cannot operate on dangling symlink %s"),
+ quoteaf (file_full_name));
+ ok = false;
+
+ }
+ break;
+
+ case FTS_SL:
+ if (dereference == 1)
+ {
+ if (fstatat (fts->fts_cwd_fd, file, &stat_buf, 0) != 0)
+ {
+ if (! force_silent)
+ error (0, errno, _("cannot dereference %s"),
+ quote (file_full_name));
+ ok = false;
+ }
+
+ file_stats = &stat_buf;
+ }
break;
case FTS_DC: /* directory that causes cycles */
@@ -270,7 +298,8 @@ process_file (FTS *fts, FTSENT *ent)
if (! S_ISLNK (old_mode))
{
- if (chmodat (fts->fts_cwd_fd, file, new_mode) == 0)
+ if (fchmodat (fts->fts_cwd_fd, file, new_mode,
+ dereference ? 0 : AT_SYMLINK_NOFOLLOW) == 0)
chmod_succeeded = true;
else
{
@@ -388,7 +417,15 @@ With --reference, change the mode of each FILE to that of
RFILE.\n\
-v, --verbose output a diagnostic for every file processed\n\
"), stdout);
fputs (_("\
- --no-preserve-root do not treat '/' specially (the default)\n\
+ --dereference affect the referent of each symbolic link (this is\n\
+ the default), rather than the symbolic link itself\n\
+ -h, --no-dereference affect each symbolic link instead of any referenced\n\
+ file (useful only on systems that can change the\n\
+ ownership of a symlink)\n\
+ "), stdout);
+
+ fputs (_("\
+ --no-preserve-root do not treat '/' specially (the default)\n\
--preserve-root fail to operate recursively on '/'\n\
"), stdout);
fputs (_("\
@@ -396,6 +433,20 @@ With --reference, change the mode of each FILE to that of
RFILE.\n\
"), stdout);
fputs (_("\
-R, --recursive change files and directories recursively\n\
+\n\
+"), stdout);
+ fputs (_("\
+The following options modify how a hierarchy is traversed when the -R\n\
+option is also specified. If more than one is specified, only the final\n\
+one takes effect.\n\
+\n\
+ -H if a command line argument is a symbolic link\n\
+ to a directory, traverse it (this is the\n\
+ default)\n\
+ -L traverse every symbolic link to a directory\n\
+ encountered\n\
+ -P do not traverse any symbolic links\n\
+\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -421,6 +472,8 @@ main (int argc, char **argv)
bool preserve_root = false;
char const *reference_file = NULL;
int c;
+ int bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
+
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -433,13 +486,35 @@ 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::,::+::=::"
+ ("HLPRcfhvr::w::x::X::s::t::u::g::o::a::,::+::=::"
"0::1::2::3::4::5::6::7::"),
long_options, NULL))
!= -1)
{
switch (c)
{
+
+ case 'H': /* Traverse command-line symlinks-to-directories. */
+ bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
+ break;
+
+ case 'L': /* Traverse all symlinks-to-directories. */
+ bit_flags = FTS_LOGICAL;
+ break;
+
+ case 'P': /* Traverse no symlinks-to-directories. */
+ bit_flags = FTS_PHYSICAL;
+ break;
+
+ case 'h': /* --no-dereference: affect symlinks */
+ dereference = 0;
+ break;
+
+ case DEREFERENCE_OPTION: /* --dereference: affect the referent
+ of each symlink */
+ dereference = 1;
+ break;
+
case 'r':
case 'w':
case 'x':
@@ -509,6 +584,18 @@ main (int argc, char **argv)
}
}
+ if (recurse)
+ {
+ if (bit_flags == FTS_PHYSICAL)
+ {
+ if (dereference == 1)
+ error (EXIT_FAILURE, 0,
+ _("-R --dereference requires either -H or -L"));
+ dereference = 0;
+ }
+ }
+
+
if (reference_file)
{
if (mode)
@@ -563,8 +650,9 @@ main (int argc, char **argv)
root_dev_ino = NULL;
}
- ok = process_files (argv + optind,
- FTS_COMFOLLOW | FTS_PHYSICAL | FTS_DEFER_STAT);
+ bit_flags |= FTS_DEFER_STAT;
+
+ ok = process_files (argv + optind, bit_flags);
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/tests/chmod/symlinks.sh b/tests/chmod/symlinks.sh
new file mode 100644
index 0000000..354f4ae
--- /dev/null
+++ b/tests/chmod/symlinks.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+# Verify that chmod works correctly with odd option combinations.
+
+# Copyright (C) 2004-2016 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ chmod
+
+mkdir -p ./a/b
+mkdir -p ./a/c
+
+touch ./a/b/file
+touch ./a/c/file
+
+#dangling link
+ln -s ./a/foo ./a/link
+
+#link to file
+ln -s ../b/file ./a/c/link
+#link to folder
+ln -s ./a/b/ ./a/folderlink
+
+#set initial modes
+chmod 777 ./a/b ./a/c ./a/b/file ./a/c/file
+#try using -R (with default -H)
+chmod 755 -R ./a/c
+ls -l ./a/b | grep -c "rwxr-xr-x">b_out
+ls -lR ./a/c | grep -c "rwxr-xr-x">c_out
+
+echo "0">b_exp
+echo "1">c_exp
+
+compare b_exp b_out || fail=1
+compare c_exp c_out || fail=1
+
+#reset
+chmod 777 ./a/b ./a/c ./a/b/file ./a/c/file
+# set ./a/c ./a/c/file and ./a/b/file (through symlink) to 755
+chmod 755 -LR ./a/c
+
+echo "$( ls -l ./a/b ) $( ls -lR ./a/c |grep file) $( ls -l ./a )"|grep -o
"rwxr-xr-x"|wc -l > out
+echo "3">exp
+
+compare exp out || fail=1
+
+
+#reset
+chmod 777 ./a/b ./a/c ./a/b/file ./a/c/file
+# set /a/file through symlink (should try to chmod the link itself)
+chmod 755 -RP ./a/c/
+ls -l ./a/b | grep -c "rwxr-xr-x">out
+echo "0">exp
+
+compare exp out || fail=1
+
+#reset
+chmod 777 ./a/b ./a/c ./a/b/file ./a/c/file
+# set /a/b/file through symlink
+chmod 755 --dereference ./a/c/link
+ls -l ./a/b | grep -c "rwxr-xr-x">out
+echo "1">exp
+
+compare exp out || fail=1
+
+#reset
+chmod 777 ./a/b ./a/c ./a/b/file ./a/c/file
+# set /a/b/file through symlink (should try to chmod the link itself)
+chmod 755 --no-dereference ./a/c/link 2>./err
+ls -l ./a/b | grep -c "rwxr-xr-x">out
+echo "0">exp
+
+compare exp out || fail=1
+
+if [ $(uname) == "Linux" ]
+then
+echo "chmod: changing permissions of './a/c/link': Operation not supported"
>exp
+compare exp err || fail=1
+fi
+
+Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
index 3335002..e1b607e 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -433,6 +433,7 @@ all_tests = \
tests/chmod/thru-dangling.sh \
tests/chmod/umask-x.sh \
tests/chmod/usage.sh \
+ tests/chmod/symlinks.sh \
tests/chown/deref.sh \
tests/chown/preserve-root.sh \
tests/chown/separator.sh \
--
2.5.5
- Chmod: -L option, Jakub Martisko, 2016/11/23
- Re: Chmod: -L option,
Jakub Martisko <=