From 26723ba0dc8a1a94fc7031d7a0973d35cc0b4519 Mon Sep 17 00:00:00 2001 From: Connor Lane Smith Date: Sat, 11 Jun 2011 00:30:07 +0100 Subject: [PATCH] symbolic chmod, thanks pancake --- LICENSE | 1 + chmod.1 | 63 ++++++++++++++++++++++++++++++++----- chmod.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 138 insertions(+), 24 deletions(-) diff --git a/LICENSE b/LICENSE index 803da60..333f75d 100644 --- a/LICENSE +++ b/LICENSE @@ -5,6 +5,7 @@ MIT/X Consortium License © 2011 stateless © 2011 Rob Pilling © 2011 Hiltjo Posthuma +© 2011 pancake Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/chmod.1 b/chmod.1 index 67d8b42..ee7a849 100644 --- a/chmod.1 +++ b/chmod.1 @@ -4,18 +4,65 @@ chmod \- change file mode .SH SYNOPSIS .B chmod .RB [ \-Rr ] -.RI mode +.I octal +.RI [ file ...] +.P +.B chmod +.RB [ \-Rr ] +.RB [ ugoa ][ +-= ][ rwxs ] .RI [ file ...] .SH DESCRIPTION .B chmod -changes the file mode for the given files. The -.I mode -is a four digit octal number derived from its comprising bits. +changes the file mode for the given files. .P -The first digit defines the setuid (4) and setgid (2) attributes. The second -digit defines the owner's permissions: read (4), write (2), and execute (1); the -third defines permissions for others in the file's group; and the fourth for all -other users. Leading zeroes may be omitted. +If the mode is an +.I octal +number, the modes are set according to that number's comprising bits. The first +digit defines the setuid (4) and setgid (2) attributes. The second digit +defines the owner's permissions: read (4), write (2), and execute (1); the third +defines permissions for others in the file's group; and the fourth for all other +users. Leading zeroes may be omitted. +.P +Alternatively the mode may be symbolic. The symbol meanings are: +.TP +.B u +modifies owner permissions. +.PD 0 +.TP +.B g +modifies group permissions. +.TP +.B o +modifies other user permissions. +.TP +.B a +modifies all user permissions. +.PD +.TP +.B + +adds the given permissions to the mode. +.PD 0 +.TP +.B - +removes the given permissions from the mode. +.TP +.B = +sets the mode to the given permissions. +.PD +.TP +.B r +read permissions. +.PD 0 +.TP +.B w +write permissions. +.TP +.B x +execute permissions. +.TP +.B s +setuid and setgid attributes. +.PD .SH OPTIONS .TP .B \-R, \-r diff --git a/chmod.c b/chmod.c index 1b56b1e..d3c77e9 100644 --- a/chmod.c +++ b/chmod.c @@ -7,15 +7,16 @@ #include "util.h" static void chmodr(const char *); +static void parsemode(const char *); static bool rflag = false; +static char oper = '='; static mode_t mode = 0; int main(int argc, char *argv[]) { char c; - int octal; while((c = getopt(argc, argv, "Rr")) != -1) switch(c) { @@ -28,21 +29,8 @@ main(int argc, char *argv[]) } if(optind == argc) eprintf("usage: %s [-Rr] mode [file...]\n", argv[0]); - octal = estrtol(argv[optind++], 8); - - /* posix doesn't specify modal bits */ - if(octal & 04000) mode |= S_ISUID; - if(octal & 02000) mode |= S_ISGID; - if(octal & 00400) mode |= S_IRUSR; - if(octal & 00200) mode |= S_IWUSR; - if(octal & 00100) mode |= S_IXUSR; - if(octal & 00040) mode |= S_IRGRP; - if(octal & 00020) mode |= S_IWGRP; - if(octal & 00010) mode |= S_IXGRP; - if(octal & 00004) mode |= S_IROTH; - if(octal & 00002) mode |= S_IWOTH; - if(octal & 00001) mode |= S_IXOTH; + parsemode(argv[optind++]); for(; optind < argc; optind++) chmodr(argv[optind]); return EXIT_SUCCESS; @@ -51,8 +39,86 @@ main(int argc, char *argv[]) void chmodr(const char *path) { - if(chmod(path, mode) == -1) + struct stat st; + + if(stat(path, &st) == -1) + eprintf("stat %s:", path); + + switch(oper) { + case '+': + st.st_mode |= mode; + break; + case '-': + st.st_mode &= ~mode; + break; + } + if(chmod(path, st.st_mode) == -1) eprintf("chmod %s:", path); if(rflag) recurse(path, chmodr); } + +void +parsemode(const char *str) +{ + char *end; + const char *p; + int octal; + mode_t mask = 0; + + octal = strtol(str, &end, 8); + if(*end == '\0') { + if(octal & 04000) mode |= S_ISUID; + if(octal & 02000) mode |= S_ISGID; + if(octal & 00400) mode |= S_IRUSR; + if(octal & 00200) mode |= S_IWUSR; + if(octal & 00100) mode |= S_IXUSR; + if(octal & 00040) mode |= S_IRGRP; + if(octal & 00020) mode |= S_IWGRP; + if(octal & 00010) mode |= S_IXGRP; + if(octal & 00004) mode |= S_IROTH; + if(octal & 00002) mode |= S_IWOTH; + if(octal & 00001) mode |= S_IXOTH; + return; + } + for(p = str; *p; p++) + switch(*p) { + /* masks */ + case 'u': + mask |= S_IRWXU; + break; + case 'g': + mask |= S_IRWXG; + break; + case 'o': + mask |= S_IRWXO; + break; + case 'a': + mask |= S_IRWXU|S_IRWXG|S_IRWXO; + break; + /* opers */ + case '+': + case '-': + case '=': + oper = *p; + break; + /* modes */ + case 'r': + mode |= S_IRUSR|S_IRGRP|S_IROTH; + break; + case 'w': + mode |= S_IWUSR|S_IWGRP|S_IWOTH; + break; + case 'x': + mode |= S_IXUSR|S_IXGRP|S_IXOTH; + break; + case 's': + mode |= S_ISUID|S_ISGID; + break; + /* error */ + default: + eprintf("%s: invalid mode\n", str); + } + if(mask) + mode &= mask; +}