From df570102af7830457de7e6acaba300c4b3b5b7fc Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Mon, 28 Apr 2025 17:39:27 -0500 Subject: [PATCH] Implement chmod -cv --- tests/chmod.test | 34 ++++++++++++++++++++-------------- toys/posix/chmod.c | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/tests/chmod.test b/tests/chmod.test index 8c4f5e63..31233ab9 100755 --- a/tests/chmod.test +++ b/tests/chmod.test @@ -29,27 +29,27 @@ for U in $(seq 0 7); do for G in 0 3 6; do for O in 0 7; do for T in dir file; d rm -rf $T if [ "$T" == file ]; then touch file - C=- + D=- else mkdir dir - C=d + D=d fi - testing "$U$G$O $T" "chmod $U$G$O $T && ls -ld $T | cut -d' ' -f 1" \ - "${C}$(num2perm $U$G$O)\n" "" "" + testcmd "$U$G$O $T" "$U$G$O $T && ls -ld $T | cut -d' ' -f 1" \ + "${D}$(num2perm $U$G$O)\n" "" "" done; done; done; done -unset U G O T C +unset U G O T D rm -rf dir file && mkdir dir && touch file 640 -testing "750 dir 640 file" "chmod 750 dir 640 file && +testcmd "750 dir 640 file" "750 dir 640 file && ls -ld 640 dir file | cut -d' ' -f 1 | cut -d. -f 1" \ "-rwxr-x---\ndrwxr-x---\n-rwxr-x---\n" "" "" chtest() { - chmod -fR 700 dir file 2>/dev/null + chmod -fR 700 dir file rm -rf dir file && mkdir dir && touch file - testing "$1 dir file" \ - "chmod $1 dir file && ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" \ + testcmd "$1 dir file" \ + "$1 dir file && ls -ld dir file | cut -d' ' -f 1 | cut -d. -f 1" \ "$2" "" "" } @@ -103,9 +103,7 @@ chtest -x "drw-r--r--\n-rw-r--r--\n" chtest a-w,a+x "dr-xr-xr-x\n-r-xr-xr-x\n" # macOS doesn't allow +s in /tmp -touch s-supported -chmod +s s-supported 2>/dev/null || SKIP=99 -rm s-supported +[ "$(uname)" == Darwin ] && SKIP=99 chtest g+s "drwxr-sr-x\n-rw-r-Sr--\n" chtest u+s "drwsr-xr-x\n-rwSr--r--\n" chtest +s "drwsr-sr-x\n-rwSr-Sr--\n" @@ -125,9 +123,17 @@ testing "+X" \ 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" "" "" +testcmd "-R symlink arg" '-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" "" "" +testcmd "-R symlink recurse" '-R 750 foo; echo $?' "0\n" "" "" +touch one +testcmd '-c' '-c u+x one && chmod -c u+x one && chmod -c u+x one' \ + "mode of 'one' changed from 0644 (rw-r--r--) to 0744 (rwxr--r--)\n" "" "" +rm one; touch one +testcmd '-v' '-v u+x one && chmod -v u+x one && chmod -v u+x one' \ + "mode of 'one' changed from 0644 (rw-r--r--) to 0744 (rwxr--r--) +mode of 'one' retained as 0744 (rwxr--r--) +mode of 'one' retained as 0744 (rwxr--r--)\n" "" "" # Removing test files for cleanup purpose rm -rf dir file diff --git a/toys/posix/chmod.c b/toys/posix/chmod.c index ef74c4fa..dc9630e3 100644 --- a/toys/posix/chmod.c +++ b/toys/posix/chmod.c @@ -3,8 +3,10 @@ * Copyright 2012 Rob Landley * * See http://opengroup.org/onlinepubs/9699919799/utilities/chmod.html + * + * Deviations from posix: -cfv -USE_CHMOD(NEWTOY(chmod, "<2?vfR[-vf]", TOYFLAG_BIN)) +USE_CHMOD(NEWTOY(chmod, "<2?cvfR[-cvf]", TOYFLAG_BIN)) config CHMOD bool "chmod" @@ -12,9 +14,16 @@ config CHMOD help usage: chmod [-R] MODE FILE... - Change mode of listed file[s] (recursively with -R). + Change permission mode of listed file[s] to be readable, writeable, and/or + executable to the current user, the file's group, or other (everyone else). + You can also set/clear the suid, guid, and sticky bits. + + -c Print changes + -f Don't print errors + -R Recursive + -v Verbose - MODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo] + MODE can be octal or (comma-separated) stanzas of [ugoa][+-=][rwxstXugo] Stanzas are applied in order: For each category (u = user, g = group, o = other, a = all three, if none specified default is a), @@ -25,7 +34,7 @@ config CHMOD X = x for directories or if any category already has x set. Or MODE can be an octal value up to 7777 ug uuugggooo top + - bit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom + bit 1 = o+x, bit 1<<8 = u+r, 1<<10 = g+s sstrwxrwxrwx bottom Examples: chmod u+w file - allow owner of "file" to write to it. @@ -41,7 +50,8 @@ GLOBALS( static int do_chmod(struct dirtree *try) { - mode_t mode; + mode_t mode, old; + char *s, *ss; if (!dirtree_notdotdot(try)) return 0; @@ -51,11 +61,18 @@ static int do_chmod(struct dirtree *try) // but that's what you asked for in that case. } else { mode = string_to_mode(TT.mode, try->st.st_mode) & ~S_IFMT; - if (FLAG(v)) { - char *s = dirtree_path(try, 0); - - printf("chmod '%s' to %s\n", s, TT.mode); - free(s); + if (FLAG(v)||FLAG(c)) { + mode_to_string(old = try->st.st_mode&07777, ss = toybuf+64); + *toybuf = 0; + if (old!=mode) + sprintf(toybuf, "changed from %04o (%s) to", old, ss+(*ss=='-')); + else if (FLAG(v)) strcpy(toybuf, "retained as"); + if (*toybuf) { + s = dirtree_path(try, 0); + mode_to_string(mode, ss); + printf("mode of '%s' %s %04o (%s)\n", s, toybuf, mode, ss+(*ss=='-')); + free(s); + } } wfchmodat(dirtree_parentfd(try), try->name, mode); } @@ -68,5 +85,13 @@ void chmod_main(void) TT.mode = *toys.optargs; char **file; + // -f means discard stderr, so replace it with the read end of a pipe + if (FLAG(f)) { + int pp[2]; + + pipe(pp); + dup2(*pp, 2); + } + for (file = toys.optargs+1; *file; file++) dirtree_read(*file, do_chmod); } -- 2.39.5