diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:23:35 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:23:35 +0300 |
| commit | 729788b336bbcf127179ac655ccf3b3e366f4ca6 (patch) | |
| tree | acf314b904cfc6231ac170b47229b7deeffd86bf /corebinutils/chmod/mode.c | |
| parent | ccef0af007fdeea4d0bcc3c182629d580f05892b (diff) | |
| parent | abd723dd0477f5009c8e77bc85412ca8917bf662 (diff) | |
| download | Project-Tick-729788b336bbcf127179ac655ccf3b3e366f4ca6.tar.gz Project-Tick-729788b336bbcf127179ac655ccf3b3e366f4ca6.zip | |
Add 'corebinutils/chmod/' from commit 'abd723dd0477f5009c8e77bc85412ca8917bf662'
git-subtree-dir: corebinutils/chmod
git-subtree-mainline: ccef0af007fdeea4d0bcc3c182629d580f05892b
git-subtree-split: abd723dd0477f5009c8e77bc85412ca8917bf662
Diffstat (limited to 'corebinutils/chmod/mode.c')
| -rw-r--r-- | corebinutils/chmod/mode.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/corebinutils/chmod/mode.c b/corebinutils/chmod/mode.c new file mode 100644 index 0000000000..03f3538f0b --- /dev/null +++ b/corebinutils/chmod/mode.c @@ -0,0 +1,424 @@ +#include "mode.h" + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> + +typedef struct bitcmd { + char cmd; + char cmd2; + mode_t bits; +} bitcmd_t; + +#define SET_LEN 6 +#define SET_LEN_INCR 4 + +#define CMD2_CLR 0x01 +#define CMD2_SET 0x02 +#define CMD2_GBITS 0x04 +#define CMD2_OBITS 0x08 +#define CMD2_UBITS 0x10 + +#define STANDARD_BITS (S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO) + +static mode_t get_current_umask(void); +static bitcmd_t *addcmd(bitcmd_t *set, mode_t op, mode_t who, mode_t oparg, + mode_t mask); +static void compress_mode(bitcmd_t *set); + +mode_t +mode_apply(const void *compiled, mode_t old_mode) +{ + const bitcmd_t *set; + mode_t clear_value, new_mode, value; + + set = compiled; + new_mode = old_mode; + for (value = 0;; set++) { + switch (set->cmd) { + case 'u': + value = (new_mode & S_IRWXU) >> 6; + goto common; + case 'g': + value = (new_mode & S_IRWXG) >> 3; + goto common; + case 'o': + value = new_mode & S_IRWXO; +common: + if (set->cmd2 & CMD2_CLR) { + clear_value = (set->cmd2 & CMD2_SET) ? S_IRWXO : value; + if (set->cmd2 & CMD2_UBITS) + new_mode &= ~((clear_value << 6) & set->bits); + if (set->cmd2 & CMD2_GBITS) + new_mode &= ~((clear_value << 3) & set->bits); + if (set->cmd2 & CMD2_OBITS) + new_mode &= ~(clear_value & set->bits); + } + if (set->cmd2 & CMD2_SET) { + if (set->cmd2 & CMD2_UBITS) + new_mode |= (value << 6) & set->bits; + if (set->cmd2 & CMD2_GBITS) + new_mode |= (value << 3) & set->bits; + if (set->cmd2 & CMD2_OBITS) + new_mode |= value & set->bits; + } + break; + case '+': + new_mode |= set->bits; + break; + case '-': + new_mode &= ~set->bits; + break; + case 'X': + if (old_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH)) + new_mode |= set->bits; + break; + case '\0': + default: + return (new_mode); + } + } +} + +void * +mode_compile(const char *mode_string) +{ + char op, *endp; + bitcmd_t *endset, *saveset, *set; + mode_t mask, perm, perm_xbits, who; + long perm_value; + unsigned int setlen; + int saved_errno; + int equal_done; + + if (*mode_string == '\0') { + errno = EINVAL; + return (NULL); + } + + mask = ~get_current_umask(); + setlen = SET_LEN + 2; + set = malloc(setlen * sizeof(*set)); + if (set == NULL) + return (NULL); + saveset = set; + endset = set + (setlen - 2); + + if (isdigit((unsigned char)*mode_string)) { + errno = 0; + perm_value = strtol(mode_string, &endp, 8); + if (*endp != '\0') { + errno = EINVAL; + goto fail; + } + if (errno == ERANGE && + (perm_value == LONG_MAX || perm_value == LONG_MIN)) { + goto fail; + } + if (perm_value & ~(STANDARD_BITS | S_ISVTX)) { + errno = EINVAL; + goto fail; + } + perm = (mode_t)perm_value; + set = addcmd(set, '=', STANDARD_BITS | S_ISVTX, perm, mask); + set->cmd = '\0'; + return (saveset); + } + + equal_done = 0; + for (;;) { + for (who = 0;; mode_string++) { + switch (*mode_string) { + case 'a': + who |= STANDARD_BITS; + break; + case 'u': + who |= S_ISUID | S_IRWXU; + break; + case 'g': + who |= S_ISGID | S_IRWXG; + break; + case 'o': + who |= S_IRWXO; + break; + default: + goto getop; + } + } +getop: + op = *mode_string++; + if (op != '+' && op != '-' && op != '=') { + errno = EINVAL; + goto fail; + } + if (op == '=') + equal_done = 0; + + who &= ~S_ISVTX; + for (perm = 0, perm_xbits = 0;; mode_string++) { + switch (*mode_string) { + case 'r': + perm |= S_IRUSR | S_IRGRP | S_IROTH; + break; + case 'w': + perm |= S_IWUSR | S_IWGRP | S_IWOTH; + break; + case 'x': + perm |= S_IXUSR | S_IXGRP | S_IXOTH; + break; + case 'X': + perm_xbits = S_IXUSR | S_IXGRP | S_IXOTH; + break; + case 's': + if (who == 0 || (who & ~S_IRWXO) != 0) + perm |= S_ISUID | S_ISGID; + break; + case 't': + if (who == 0 || (who & ~S_IRWXO) != 0) { + who |= S_ISVTX; + perm |= S_ISVTX; + } + break; + case 'u': + case 'g': + case 'o': + if (perm != 0) { + set = addcmd(set, op, who, perm, mask); + perm = 0; + } + if (op == '=') + equal_done = 1; + if (op == '+' && perm_xbits != 0) { + set = addcmd(set, 'X', who, perm_xbits, mask); + perm_xbits = 0; + } + set = addcmd(set, *mode_string, who, op, mask); + break; + default: + if (perm != 0 || (op == '=' && !equal_done)) { + if (op == '=') + equal_done = 1; + set = addcmd(set, op, who, perm, mask); + perm = 0; + } + if (perm_xbits != 0) { + set = addcmd(set, 'X', who, perm_xbits, mask); + perm_xbits = 0; + } + goto apply; + } + + if (set >= endset) { + ptrdiff_t offset; + bitcmd_t *newset; + + offset = set - saveset; + setlen += SET_LEN_INCR; + newset = realloc(saveset, setlen * sizeof(*newset)); + if (newset == NULL) + goto fail; + saveset = newset; + set = newset + offset; + endset = newset + (setlen - 2); + } + } +apply: + if (*mode_string == '\0') + break; + if (*mode_string != ',') { + errno = EINVAL; + goto fail; + } + mode_string++; + } + + set->cmd = '\0'; + compress_mode(saveset); + return (saveset); + +fail: + saved_errno = errno; + free(saveset); + errno = saved_errno; + return (NULL); +} + +void +mode_free(void *compiled) +{ + free(compiled); +} + +void +strmode(mode_t mode, char *buf) +{ + switch (mode & S_IFMT) { + case S_IFDIR: + *buf++ = 'd'; + break; + case S_IFCHR: + *buf++ = 'c'; + break; + case S_IFBLK: + *buf++ = 'b'; + break; + case S_IFREG: + *buf++ = '-'; + break; + case S_IFLNK: + *buf++ = 'l'; + break; + case S_IFSOCK: + *buf++ = 's'; + break; +#ifdef S_IFIFO + case S_IFIFO: + *buf++ = 'p'; + break; +#endif + default: + *buf++ = '?'; + break; + } + + *buf++ = (mode & S_IRUSR) ? 'r' : '-'; + *buf++ = (mode & S_IWUSR) ? 'w' : '-'; + switch (mode & (S_IXUSR | S_ISUID)) { + case 0: *buf++ = '-'; break; + case S_IXUSR: *buf++ = 'x'; break; + case S_ISUID: *buf++ = 'S'; break; + default: *buf++ = 's'; break; + } + *buf++ = (mode & S_IRGRP) ? 'r' : '-'; + *buf++ = (mode & S_IWGRP) ? 'w' : '-'; + switch (mode & (S_IXGRP | S_ISGID)) { + case 0: *buf++ = '-'; break; + case S_IXGRP: *buf++ = 'x'; break; + case S_ISGID: *buf++ = 'S'; break; + default: *buf++ = 's'; break; + } + *buf++ = (mode & S_IROTH) ? 'r' : '-'; + *buf++ = (mode & S_IWOTH) ? 'w' : '-'; + switch (mode & (S_IXOTH | S_ISVTX)) { + case 0: *buf++ = '-'; break; + case S_IXOTH: *buf++ = 'x'; break; + case S_ISVTX: *buf++ = 'T'; break; + default: *buf++ = 't'; break; + } + *buf++ = ' '; + *buf = '\0'; +} + +static mode_t +get_current_umask(void) +{ + sigset_t all_signals, original_signals; + mode_t mask; + + sigfillset(&all_signals); + (void)sigprocmask(SIG_BLOCK, &all_signals, &original_signals); + mask = umask(0); + (void)umask(mask); + (void)sigprocmask(SIG_SETMASK, &original_signals, NULL); + return (mask); +} + +static bitcmd_t * +addcmd(bitcmd_t *set, mode_t op, mode_t who, mode_t oparg, mode_t mask) +{ + switch (op) { + case '=': + set->cmd = '-'; + set->bits = who ? who : STANDARD_BITS; + set++; + op = '+'; + /* FALLTHROUGH */ + case '+': + case '-': + case 'X': + set->cmd = op; + set->cmd2 = 0; + set->bits = (who ? who : mask) & oparg; + break; + case 'u': + case 'g': + case 'o': + set->cmd = op; + if (who != 0) { + set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | + ((who & S_IRGRP) ? CMD2_GBITS : 0) | + ((who & S_IROTH) ? CMD2_OBITS : 0); + set->bits = (mode_t)~0; + } else { + set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; + set->bits = mask; + } + + if (oparg == '+') + set->cmd2 |= CMD2_SET; + else if (oparg == '-') + set->cmd2 |= CMD2_CLR; + else if (oparg == '=') + set->cmd2 |= CMD2_SET | CMD2_CLR; + break; + } + + return (set + 1); +} + +static void +compress_mode(bitcmd_t *set) +{ + bitcmd_t *next_set; + int clear_bits, op, set_bits, x_bits; + + for (next_set = set;;) { + while ((op = next_set->cmd) != '+' && op != '-' && op != 'X') { + *set++ = *next_set++; + if (op == '\0') + return; + } + + for (set_bits = clear_bits = x_bits = 0;; next_set++) { + op = next_set->cmd; + if (op == '-') { + clear_bits |= next_set->bits; + set_bits &= ~next_set->bits; + x_bits &= ~next_set->bits; + } else if (op == '+') { + set_bits |= next_set->bits; + clear_bits &= ~next_set->bits; + x_bits &= ~next_set->bits; + } else if (op == 'X') { + x_bits |= next_set->bits & ~set_bits; + } else { + break; + } + } + + if (clear_bits != 0) { + set->cmd = '-'; + set->cmd2 = 0; + set->bits = clear_bits; + set++; + } + if (set_bits != 0) { + set->cmd = '+'; + set->cmd2 = 0; + set->bits = set_bits; + set++; + } + if (x_bits != 0) { + set->cmd = 'X'; + set->cmd2 = 0; + set->bits = x_bits; + set++; + } + } +} |
