#include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include "../util.h" mode_t getumask(void) { mode_t mask = umask(0); umask(mask); return mask; } mode_t parsemode(const char *str, mode_t mode, mode_t mask) { char *end; const char *p = str; int octal, op; mode_t who, perm, clear; octal = strtol(str, &end, 8); if (*end == '\0') { if (octal < 0 || octal > 07777) { eprintf("%s: invalid mode\n", str); return -1; } mode = 0; if (octal & 04000) mode |= S_ISUID; if (octal & 02000) mode |= S_ISGID; if (octal & 01000) mode |= S_ISVTX; 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 mode; } next: /* first, determine which bits we will be modifying */ for (who = 0; *p; p++) { switch (*p) { /* masks */ case 'u': who |= S_IRWXU|S_ISUID; continue; case 'g': who |= S_IRWXG|S_ISGID; continue; case 'o': who |= S_IRWXO; continue; case 'a': who |= S_IRWXU|S_ISUID|S_IRWXG|S_ISGID|S_IRWXO; continue; } break; } if (who) { clear = who; } else { clear = S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO; who = ~mask; } while (*p) { switch (*p) { /* opers */ case '=': case '+': case '-': op = (int)*p; break; default: eprintf("%s: invalid mode\n", str); return -1; } perm = 0; switch (*++p) { /* copy */ case 'u': if (mode & S_IRUSR) perm |= S_IRUSR|S_IRGRP|S_IROTH; if (mode & S_IWUSR) perm |= S_IWUSR|S_IWGRP|S_IWOTH; if (mode & S_IXUSR) perm |= S_IXUSR|S_IXGRP|S_IXOTH; if (mode & S_ISUID) perm |= S_ISUID|S_ISGID; p++; break; case 'g': if (mode & S_IRGRP) perm |= S_IRUSR|S_IRGRP|S_IROTH; if (mode & S_IWGRP) perm |= S_IWUSR|S_IWGRP|S_IWOTH; if (mode & S_IXGRP) perm |= S_IXUSR|S_IXGRP|S_IXOTH; if (mode & S_ISGID) perm |= S_ISUID|S_ISGID; p++; break; case 'o': if (mode & S_IROTH) perm |= S_IRUSR|S_IRGRP|S_IROTH; if (mode & S_IWOTH) perm |= S_IWUSR|S_IWGRP|S_IWOTH; if (mode & S_IXOTH) perm |= S_IXUSR|S_IXGRP|S_IXOTH; p++; break; default: for (; *p; p++) { switch (*p) { /* modes */ 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 's': perm |= S_ISUID|S_ISGID; break; case 't': perm |= S_ISVTX; break; default: goto apply; } } } apply: /* apply */ switch (op) { case '=': mode &= ~clear; /* fallthrough */ case '+': mode |= perm & who; break; case '-': mode &= ~(perm & who); break; } /* if we hit a comma, move on to the next clause */ if (*p == ',') { p++; goto next; } } return mode; }