diff options
-rw-r--r-- | sysif.c | 163 | ||||
-rw-r--r-- | txr.1 | 64 |
2 files changed, 216 insertions, 11 deletions
@@ -543,21 +543,170 @@ static int get_fd(val stream, val self) #if HAVE_CHMOD +#define CHM_O 4 +#define CHM_G 2 +#define CHM_U 1 + +enum chm_state { chm_who, chm_perm, chm_nxtop, chm_comma }; +enum chm_op { chm_add, chm_sub, chm_set }; + +static val stat_wrap(val path); + static val chmod_wrap(val target, val mode) { val self = lit("chmod"); - cnum cmode = c_num(mode); - int err; + cnum cmode = 0; + int err = 0; + char *u8path = if3(stringp(target), utf8_dup_to(c_str(target)), 0); + int fd = if3(u8path, -1, get_fd(target, self)); + + if (integerp(mode)) { + cmode = c_num(mode); + } else if (stringp(mode)) { +#if HAVE_SYS_STAT + struct stat st; + unsigned who = 0; + enum chm_state cs = chm_who; + enum chm_op op = chm_add; - if (stringp(target)) { - char *u8path = utf8_dup_to(c_str(target)); - err = chmod(u8path, cmode); + if (u8path) + err = stat(u8path, &st); + else + err = fstat(fd, &st); + + if (err == 0) { + const wchar_t *cm = c_str(mode); + wchar_t ch; + mode_t srcm = 0; + + cmode = st.st_mode; + + while ((ch = *cm++) != 0) { + switch (cs) { + case chm_who: + switch (ch) { + case 'u': who |= CHM_U; continue; + case 'g': who |= CHM_G; continue; + case 'o': who |= CHM_O; continue; + case 'a': who |= CHM_U | CHM_G | CHM_O; continue; + case '+': op = chm_add; cs = chm_perm; continue; + case '-': op = chm_sub; cs = chm_perm; continue; + case '=': op = chm_set; cs = chm_perm; break; + default: + goto inval; + } + break; + case chm_nxtop: + srcm = 0; + switch (ch) { + case '+': op = chm_add; cs = chm_perm; continue; + case '-': op = chm_sub; cs = chm_perm; continue; + case '=': op = chm_set; cs = chm_perm; break; + default: goto perm; + } + break; + perm: + case chm_perm: + switch (ch) { + case 'u': srcm |= (cmode & 0700) >> 6; cs = chm_comma; break; + case 'g': srcm |= (cmode & 0070) >> 3; cs = chm_comma; break; + case 'o': srcm |= (cmode & 0007); cs = chm_comma; break; + case 'r': srcm |= 4; cs = chm_nxtop; break; + case 'w': srcm |= 2; cs = chm_nxtop; break; + case 'x': srcm |= 1; cs = chm_nxtop; break; + case 's': srcm |= 010; cs = chm_nxtop; break; + case 't': srcm |= 020; cs = chm_nxtop; break; + case 'X': srcm |= ((cmode & 0111) != 0 || + S_ISDIR(cmode)); cs = chm_nxtop; break; + case ',': srcm = 0; who = 0; cs = chm_who; continue; + default: + goto inval; + } + break; + case chm_comma: + if (ch != ',') + goto inval; + cs = chm_who; + break; + } + + { + mode_t bits = 0; + mode_t mask = 0; + int do_um = (who == 0); + + if (do_um) + who = CHM_U | CHM_G | CHM_O; + + if ((srcm & 020)) + bits |= S_ISVTX; + + if ((who & CHM_U) != 0) { + mask |= 0700; + if ((srcm & 010)) + bits |= S_ISUID; + bits |= (srcm & 7) << 6; + } + + if ((who & CHM_G) != 0) { + mask |= 0070; + if ((srcm & 010)) + bits |= S_ISGID; + bits |= (srcm & 7) << 3; + } + + if ((who & CHM_O) != 0) { + mask |= 0007; + bits |= (srcm & 7); + } + + if (do_um) { + mode_t um = umask(0777); + umask(um); + bits &= ~um; + } + + switch (op) { + case chm_add: cmode |= bits; break; + case chm_sub: cmode &= ~bits; break; + case chm_set: + cmode &= ~mask; + if ((who & CHM_O) != 0) + bits &= ~S_ISVTX; /* GNU Coreutils 8.28 chmod behavior */ + if (!S_ISDIR(cmode)) + cmode &= ~(S_ISUID | S_ISGID); + cmode |= bits; + break; + } + } + } + + if (cs == chm_who) + goto inval; + } +#else free(u8path); + uw_throwf(file_error_s, lit("~s: ~s mode requires stat"), + self, mode, nao); +#endif } else { - int fd = get_fd(target, self); - err = fchmod(fd, cmode); +inval: + free(u8path); + uw_throwf(file_error_s, lit("~s: invalid mode ~s"), + self, mode, nao); } + if (err == 0) { + if (u8path) { + err = chmod(u8path, cmode); + } else { + int fd = get_fd(target, self); + err = fchmod(fd, cmode); + } + } + + free(u8path); + if (err < 0) { int eno = errno; uw_throwf(errno_to_file_error(eno), lit("~a ~a #o~o: ~d/~s"), @@ -58010,14 +58010,59 @@ It is implemented in terms of the POSIX functions .code chmod and .codn fchmod . +If +.meta mode +is a character string representing a symbolic mode, then the function +also makes use of +.code stat +or +.code fstat +and +.codn umask . + The permissions are specified by .metn mode , -an integer argument. +which must be an integer or a string. -The existing permissions may be obtained using the -.code stat +An integer +.meta mode +is a bitwise combination of permission mode bits. The value is passed +directly to the POSIX +.code chmod +or +.code fchmod function. +Note: to construct a mode value, applications may use +.code logior +to combine the values +of the variables like +.code s-irusr +or +.code s-ixoth +or take advantage of the well-known numeric structure of POSIX +permissions to express them octal in octal notation. For instance the mode +.code #o750 +denotes that the owner has read, write and execute permissions, +the group owner has read and execute, others have no permission. +This value may also be calculated using +.codn "(logior s-irwxu s-irgrp s-ixgrp)" . + +If the argument to +.meta mode +is a string, it is interpreted according to the symbolic syntax +of the POSIX +.code chmod +utility. For instance, a +.meta mode +value of +.str a+w,-s +means to give all users (owner, group and others) write permission, +and remove the setuid and setgid bits. + +The full syntax and semantics of symbolic +.meta mode +strings is given in the POSIX standard IEEE 1003.1. The function throws a .code file-error @@ -58057,6 +58102,14 @@ function. s-iroth)) .brev +Implementation note: The implementation of the symbolic +.meta mode +processing is based on the descriptions given in IEEE 1003.1-2018, +Issue 7 and also on the +.code chmod +program from from GNU Coreutils 8.28: and experiments with its behavior, +and its documentation. + .coNP Functions @ chown and @ lchown .synb .mets (chown < target < id << gid ) @@ -58718,7 +58771,10 @@ The function alters the permission of each object that is not a symbolic link using the .code chmod -function. Each object which is a symbolic link is ignored. +function, and +.meta mode +is interpreted accordingly: it may be an integer or string. +Each object which is a symbolic link is ignored. The .code chown-rec |