Created
November 12, 2014 16:49
-
-
Save clausecker/1b35a38570c66ed25fc7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*- | |
| * Copyright (c) 2014 Robert Clausecker | |
| */ | |
| /* | |
| * parse_mode() - parse an octal or symbolic mode string into a mode_t | |
| */ | |
| #define _XOPEN_SOURCE 700 | |
| #include <stdlib.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include "perm.h" | |
| /* some enumerations for parse_symbolic() */ | |
| enum op { | |
| OP_NONE, /* no op seen yet */ | |
| OP_ADD, /* + and = */ | |
| OP_SUB, /* - */ | |
| }; | |
| enum { | |
| /* all permission bits, equal to 07777 */ | |
| PERMBITS = S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX, | |
| }; | |
| /* compose user group and other permissions from bits */ | |
| #define MAKEBITS(bits, who) (((bits) | (bits) << 3 | (bits) << 6) & (who)) | |
| static int parse_octal(const char*, mode_t*); | |
| static int parse_symbolic(const char*, mode_t, mode_t*); | |
| /* | |
| * Parse an octal or symbolic mode string as specified in IEEE Std 1003.1 2008 | |
| * Edition for the command chmod from mstr and apply the result to mode, | |
| * applying mask in the process. Notice that this function evaluates the file | |
| * type of mode and does the right thing for both files and directories. Return | |
| * 0 if parsing was successful, 1 otherwise. If parsing was not succesful, the | |
| * content of mode is undefined, otherwise it contains the result of applying | |
| * the mode described in mstr onto mode. | |
| */ | |
| extern int | |
| parse_mode(const char *mstr, mode_t mask, mode_t *mode) | |
| { | |
| if (parse_octal(mstr, mode)) | |
| return (0); | |
| return (parse_symbolic(mstr, mask, mode)); | |
| } | |
| /* | |
| * Check if mstr contains an octal mode. Return 1 if mstr is an octal mode | |
| * string, 0 otherwise. If mstr contains an octal mode, update mode to said | |
| * mode. | |
| */ | |
| static int | |
| parse_octal(const char *mstr, mode_t *mode) | |
| { | |
| char *endptr; | |
| unsigned long newmode; | |
| if (*mstr == '\0') | |
| return (0); | |
| newmode = strtoul(mstr, &endptr, 8); | |
| if (*endptr != '\0' || newmode > PERMBITS) | |
| return (0); | |
| *mode &= ~PERMBITS; | |
| *mode |= newmode; | |
| return (1); | |
| } | |
| /* | |
| * Parse one clause of a symbolic mode string and apply it to mode respecting | |
| * mask. If the clause cannot be parsed, return 1, else 0. For | |
| */ | |
| static int | |
| parse_symbolic(const char *mstr, mode_t mask, mode_t *mode) | |
| { | |
| /* | |
| * In a permission string like +w which does not contain a wholist, the | |
| * receiver of the new permission is taken from the umask instead. For | |
| * this reason, it is neccessary to record if we have seen parts of a | |
| * wholist: The algorithm I use sets the who bits from the mask first | |
| * and clears them when it encounters the first character of a wholist. | |
| */ | |
| int who_seen = 0; /* Have we seen a wholist? */ | |
| mode_t who = ~mask; /* Whom to apply permission changes to */ | |
| enum op op = OP_NONE; | |
| mode_t m; | |
| for (;; mstr++) | |
| if (op == OP_NONE) | |
| switch (*mstr) { | |
| case '=': | |
| *mode &= ~PERMBITS; | |
| op = OP_ADD; | |
| break; | |
| case '+': | |
| op = OP_ADD; | |
| break; | |
| case '-': | |
| op = OP_SUB; | |
| break; | |
| case 'u': | |
| if (!who_seen) | |
| who = 0; | |
| who_seen = 1; | |
| who |= S_IRWXU | S_ISUID; | |
| break; | |
| case 'g': | |
| if (!who_seen) | |
| who = 0; | |
| who_seen = 1; | |
| who |= S_IRWXG | S_ISGID; | |
| break; | |
| case 'o': | |
| if (!who_seen) | |
| who = 0; | |
| who_seen = 1; | |
| who |= S_IRWXO | S_ISVTX; | |
| break; | |
| case 'a': | |
| who_seen = 1; | |
| who = PERMBITS; | |
| break; | |
| default: | |
| return (1); | |
| } | |
| else | |
| switch (*mstr) { | |
| case '\0': | |
| return (0); | |
| case ',': | |
| who_seen = 0; | |
| who = ~mask; | |
| op = OP_NONE; | |
| break; | |
| case '=': | |
| *mode &= ~PERMBITS; | |
| op = OP_ADD; | |
| break; | |
| case '+': | |
| op = OP_ADD; | |
| break; | |
| case '-': | |
| op = OP_SUB; | |
| break; | |
| case 'u': | |
| m = (*mode & S_IRWXU); | |
| m = MAKEBITS(m, who); | |
| goto apply_perm; | |
| case 'g': | |
| m = (*mode & S_IRWXG) >> 3; | |
| m = MAKEBITS(m, who); | |
| goto apply_perm; | |
| case 'o': | |
| m = (*mode & S_IRWXO) >> 0; | |
| m = MAKEBITS(m, who); | |
| goto apply_perm; | |
| case 'r': | |
| m = MAKEBITS(S_IROTH, who); | |
| goto apply_perm; | |
| case 'w': | |
| m = MAKEBITS(S_IWOTH, who); | |
| goto apply_perm; | |
| case 'X': | |
| if (!S_ISDIR(*mode) && (*mode | |
| & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) | |
| break; | |
| /* FALLTHROUGH */ | |
| case 'x': | |
| m = MAKEBITS(S_IXOTH, who); | |
| goto apply_perm; | |
| case 's': | |
| m = (S_ISUID | S_ISGID) & who; | |
| goto apply_perm; | |
| case 't': | |
| m = S_ISVTX & who; | |
| goto apply_perm; | |
| default: | |
| return (1); | |
| apply_perm: | |
| /* remember: op cannot be OP_NONE here */ | |
| if (op == OP_ADD) | |
| *mode |= m; | |
| else | |
| *mode &= ~m; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment