aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2020-06-09 10:09:03 -0700
committerRob Landley <rob@landley.net>2020-06-09 15:51:17 -0500
commita01cee764f620c154fcccb688d7b1a742f7f687e (patch)
tree79da293c0967092d0b92e5e880d03a9b6f2b6aaf
parent53b2ceac9af6c4bfd120a170617e3bded526b427 (diff)
downloadtoybox-a01cee764f620c154fcccb688d7b1a742f7f687e.tar.gz
chmod: fix -R and dangling symlinks.
Found trying to run the libc++ tests. For coreutils, `info chmod` says: 'chmod' ignores symbolic links encountered during recursive directory traversals. Bug: http://b/155809792
-rwxr-xr-xtests/chmod.test7
-rw-r--r--toys/posix/chmod.c20
2 files changed, 20 insertions, 7 deletions
diff --git a/tests/chmod.test b/tests/chmod.test
index b2b5a488..cbc32805 100755
--- a/tests/chmod.test
+++ b/tests/chmod.test
@@ -112,5 +112,12 @@ chtest u+s "drwsr-xr-x\n-rwSr--r--\n"
chtest o+s "drwxr-xr-x\n-rw-r--r--\n"
chtest +t "drwxr-xr-t\n-rw-r--r-T\n"
+mkdir foo
+ln -s bar foo/baz
+# If you explicitly ask us, we'll try (and fail) to chmod a symlink.
+testing "-R symlink arg" 'chmod -R 750 foo/baz 2>/dev/null; echo $?' "1\n" "" ""
+# If you only imply that you might want us to do that, we'll skip it.
+testing "-R symlink recurse" 'chmod -R 750 foo; echo $?' "0\n" "" ""
+
# Removing test files for cleanup purpose
rm -rf dir file
diff --git a/toys/posix/chmod.c b/toys/posix/chmod.c
index ac53957c..3645ebc8 100644
--- a/toys/posix/chmod.c
+++ b/toys/posix/chmod.c
@@ -45,14 +45,20 @@ static int do_chmod(struct dirtree *try)
if (!dirtree_notdotdot(try)) return 0;
- mode = string_to_mode(TT.mode, try->st.st_mode);
- if (FLAG(v)) {
- char *s = dirtree_path(try, 0);
-
- printf("chmod '%s' to %s\n", s, TT.mode);
- free(s);
+ if (FLAG(R) && try->parent && S_ISLNK(try->st.st_mode)) {
+ // Ignore symlinks found during recursion. We'll only try to modify
+ // symlinks mentioned directly as arguments. We'll fail, of course,
+ // but that's what you asked for in that case.
+ } else {
+ mode = string_to_mode(TT.mode, try->st.st_mode);
+ if (FLAG(v)) {
+ char *s = dirtree_path(try, 0);
+
+ printf("chmod '%s' to %s\n", s, TT.mode);
+ free(s);
+ }
+ wfchmodat(dirtree_parentfd(try), try->name, mode);
}
- wfchmodat(dirtree_parentfd(try), try->name, mode);
return FLAG(R)*DIRTREE_RECURSE;
}