diff --git a/chmod.1 b/chmod.1 index 4805ce0..fa4131d 100644 --- a/chmod.1 +++ b/chmod.1 @@ -47,7 +47,7 @@ If .Ar mode is .Em symbolic -"[ugoa]*[+-=][rwxst]*" +"[ugoa]*[+-=][rwxXst]*" .Bl -tag -width Ds .It u|g|o|a owner | group | other (non-group) | everyone @@ -55,6 +55,8 @@ owner | group | other (non-group) | everyone add | remove | set .It r|w|x|s|t read | write | execute | setuid and setgid | sticky +.It X +execute, if directory or at least one execute bit is already set .El .Sh OPTIONS .Bl -tag -width Ds diff --git a/chmod.c b/chmod.c index 2a0085d..512a7ea 100644 --- a/chmod.c +++ b/chmod.c @@ -13,7 +13,7 @@ chmodr(const char *path, struct stat *st, void *data, struct recursor *r) { mode_t m; - m = parsemode(modestr, st->st_mode & ~S_IFMT, mask); + m = parsemode(modestr, st->st_mode, mask); if (chmod(path, m) < 0) { weprintf("chmod %s:", path); ret = 1; @@ -50,8 +50,8 @@ main(int argc, char *argv[]) case 'P': r.follow = (*argv)[i]; break; - case 'r': case 'w': case 'x': case 's': case 't': - /* -[rwxst] are valid modes, so we're done */ + case 'r': case 'w': case 'x': case 'X': case 's': case 't': + /* -[rwxXst] are valid modes, so we're done */ if (i == 1) goto done; /* fallthrough */ diff --git a/libutil/mode.c b/libutil/mode.c index 5ba8eb1..b3632ad 100644 --- a/libutil/mode.c +++ b/libutil/mode.c @@ -113,6 +113,10 @@ next: case 'x': perm |= S_IXUSR|S_IXGRP|S_IXOTH; break; + case 'X': + if (S_ISDIR(mode) || mode & (S_IXUSR|S_IXGRP|S_IXOTH)) + perm |= S_IXUSR|S_IXGRP|S_IXOTH; + break; case 's': perm |= S_ISUID|S_ISGID; break; @@ -144,5 +148,5 @@ apply: goto next; } } - return mode; + return mode & ~S_IFMT; }