#include #include #include #include #include #include #include #include "file_mode.h" extern char *progname; mode_t parse_file_mode(char *arg, mode_t mode, mode_t sumask) { char *clause; if (isdigit(*arg) && *arg < '8') { unsigned long num; num = strtoul(arg, NULL, 8); if ((num == ULONG_MAX && errno == ERANGE) || num > 07777) { fprintf(stderr, "%s: invalid mode `%s'\n", progname, arg); exit(255); } return (mode_t) num; } while ((clause = strsep(&arg, ",")) != NULL) { mode_t who = 0; int action; char *p = clause; /* * Parse the who list. Optional. */ while (1) { switch (*p++) { case 'u': who |= S_IRWXU | S_ISUID; continue; case 'g': who |= S_IRWXG | S_ISGID; continue; case 'o': who |= S_IRWXO | S_ISVTX; continue; case 'a': who = S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX; continue; } /* undo the increment above */ p--; break; } if (who == 0) who = (~sumask) | S_ISVTX; /* * Parse an action list. Must be at least one action. */ while (*p) { mode_t perm = 0; /* * Parse the action */ action = *p; if (action == '+' || action == '-' || action == '=') p++; /* * Parse perm */ while (*p) { switch (*p++) { case 'r': perm |= S_IRUSR | S_IRGRP | S_IROTH; continue; case 'w': perm |= S_IWUSR | S_IWGRP | S_IWOTH; continue; case 'x': perm |= S_IXUSR | S_IXGRP | S_IXOTH; continue; case 'X': perm |= S_ISVTX; continue; case 's': perm |= S_ISUID | S_ISGID; continue; case 'u': perm = mode & S_IRWXU; perm |= perm >> 3 | perm >> 6; if (mode & S_ISUID) perm |= S_ISGID; continue; case 'g': perm = mode & S_IRWXG; perm |= perm << 3 | perm >> 3; if (mode & S_ISGID) perm |= S_ISUID; continue; case 'o': perm = mode & S_IRWXO; perm |= perm << 6 | perm << 3; continue; } /* undo the increment above */ p--; break; } perm &= who; switch (action) { case '+': mode |= perm; continue; case '-': mode &= ~perm; continue; case '=': mode &= ~who; mode |= perm; continue; } if (!action) break; fprintf(stderr, "%s: invalid mode `%s'\n", progname, clause); exit(255); } } return mode; }