From d9b6a5820f195ef681d7cd15d70a184265b37a94 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Tue, 24 Dec 2019 14:27:51 +0000 Subject: Add posix-umask; prepare for 2.6.0.0 --- src/posix/deps-exe/posix-umask | 1 + src/posix/posix-umask.c | 174 +++++++++++++++++++++++++++++++++++++++++ src/posix/posix-umask.txt | 37 +++++++++ 3 files changed, 212 insertions(+) create mode 100644 src/posix/deps-exe/posix-umask create mode 100644 src/posix/posix-umask.c create mode 100644 src/posix/posix-umask.txt (limited to 'src/posix') diff --git a/src/posix/deps-exe/posix-umask b/src/posix/deps-exe/posix-umask new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/posix/deps-exe/posix-umask @@ -0,0 +1 @@ +-lskarnet diff --git a/src/posix/posix-umask.c b/src/posix/posix-umask.c new file mode 100644 index 0000000..b4b9b4b --- /dev/null +++ b/src/posix/posix-umask.c @@ -0,0 +1,174 @@ +/* ISC license. */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "posix-umask [ -S ] [ mask ] [ prog... ]" +#define dieusage() strerr_dieusage(100, USAGE) +#define dieout() strerr_diefu1sys(111, "write to stdout") + + + /* well, unlike posix-cd, at least this one was fun to write */ + +static inline int output (int sym) +{ + mode_t mode = umask(0) ; + size_t m = 0 ; + char fmt[18] ; + if (sym) + { + unsigned int i = 3 ; + while (i--) + { + unsigned int mask = ~(mode >> (3*i)) ; + fmt[m++] = "ogu"[i] ; fmt[m++] = '=' ; + if (mask & 4) fmt[m++] = 'r' ; + if (mask & 2) fmt[m++] = 'w' ; + if (mask & 1) fmt[m++] = 'x' ; + if (i) fmt[m++] = ',' ; + } + } + else m += uint0_ofmt(fmt, mode, 4) ; + fmt[m++] = '\n' ; + if (buffer_putflush(buffer_1, fmt, m) < 0) dieout() ; + return 0 ; +} + +static void diesyntax (char const *) gccattr_noreturn ; +static void diesyntax (char const *s) +{ + strerr_dief3x(101, "internal parsing error: bad ", s, ". Please submit a bug-report.") ; +} + +static inline uint8_t cclass (char c) +{ + /* char tables may be more efficient, but this is way more readable */ + switch (c) + { + case 0 : return 0 ; + case ',' : return 1 ; + case '+' : + case '-' : + case '=' : return 2 ; + case 'u' : + case 'g' : + case 'o' : return 3 ; + case 'a' : return 4 ; + case 'r' : + case 'w' : + case 'x' : + case 'X' : + case 's' : + case 't' : return 5 ; + default : return 6 ; + } +} + +static inline uint8_t who_value (char c) +{ + switch (c) + { + case 'u' : return 4 ; + case 'g' : return 2 ; + case 'o' : return 1 ; + case 'a' : + case '+' : /* shortcut for when who is empty */ + case '-' : + case '=' : return 7 ; + default : diesyntax("who") ; + } +} + +static inline uint8_t perm_value (char c) +{ + switch (c) + { + case 'r' : return 4 ; + case 'w' : return 2 ; + case 'x' : + case 'X' : return 1 ; + case 's' : + case 't' : return 0 ; + default : diesyntax("perm") ; + } +} + +static inline unsigned int parsemode (char const *s) +{ + static uint16_t const table[5][7] = + { + { 0x005, 0x000, 0x064, 0x021, 0x021, 0x006, 0x006 }, + { 0x005, 0x006, 0x042, 0x021, 0x021, 0x006, 0x006 }, + { 0x005, 0x200, 0x042, 0x083, 0x006, 0x104, 0x006 }, + { 0x805, 0xe00, 0xc42, 0x006, 0x006, 0x006, 0x006 }, + { 0x805, 0xe00, 0xc42, 0x006, 0x006, 0x104, 0x006 } + } ; + unsigned int oldmode = ~umask(0) ; + uint8_t modes[3] = { oldmode & 7, (oldmode >> 3) & 7, (oldmode >> 6) & 7 } ; + uint8_t who = 0 ; + uint8_t perm = 0 ; + uint8_t state = 0 ; + char op = 0 ; + while (state < 5) + { + char c = *s++ ; + uint16_t what = table[state][cclass(c)] ; + state = what & 7 ; + if (what & 0x020) who |= who_value(c) ; + if (what & 0x080) perm = modes[byte_chr("ogu", 3, c)] ; + if (what & 0x100) perm |= perm_value(c) ; + if (what & 0x800) + { + unsigned int i = 3 ; + while (i--) if (who & (1 << i)) + switch (op) + { + case '-' : modes[i] &= ~perm ; break ; + case '+' : modes[i] |= perm ; break ; + case '=' : modes[i] = perm ; break ; + default : diesyntax("op") ; + } + } + if (what & 0x040) op = c ; + if (what & 0x200) who = 0 ; + if (what & 0x400) perm = 0 ; + } + if (state > 5) strerr_dief1x(1, "invalid mode string") ; + return ((unsigned int)modes[2] << 6) | ((unsigned int)modes[1] << 3) | modes[0] ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + int sym = 0 ; + unsigned int mode ; + PROG = "posix-umask" ; + setlocale(LC_ALL, "") ; /* totally supported, I swear */ + + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "S", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'S' : sym = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) return output(sym) ; + if (!uint0_oscan(argv[0], &mode)) mode = ~parsemode(argv[0]) ; + umask(mode & 00777) ; + xpathexec0_run(argv+1, envp) ; +} diff --git a/src/posix/posix-umask.txt b/src/posix/posix-umask.txt new file mode 100644 index 0000000..5bf49a2 --- /dev/null +++ b/src/posix/posix-umask.txt @@ -0,0 +1,37 @@ + + parsemode() function for posix-umask + + goal: parse the "u+r,g-wx,o=u" symbolic mode string and convert it +to a numeric value suitable for umask(). + In the purest skarnet.org tradition, we implement the parser via a DFA. + +class | 0 1 2 3 4 5 6 +st\ev | \0 , +-= ugo a rwxXst other +------------------------------------------------------------------------ +START | wo w w +0 | END START OP WHO WHO X X + +WHO | o w w +1 | END X OP WHO WHO X X + +OP | r o c p +2 | END START OP PERMCPY X PERM X + +PERMCPY | ! !rR !Ro +3 | END START OP X X X X + +PERM | ! !rR !Ro p +4 | END START OP X X PERM X +------------------------------------------------------------------------ + +END=5, X=6. -> states: 3 bits +7 actions -> 10 bits total, need uint16_t + + w: 0x020: who |= c + o: 0x040: store op + c: 0x080: copy perm from c + p: 0x100: perm |= c + r: 0x200: reset who + R: 0x400: reset perm + !: 0x800: apply (who, op, perm) change + -- cgit v1.2.3