summaryrefslogtreecommitdiffstats
path: root/usr/utils/file_mode.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/utils/file_mode.c')
-rw-r--r--usr/utils/file_mode.c143
1 files changed, 143 insertions, 0 deletions
diff --git a/usr/utils/file_mode.c b/usr/utils/file_mode.c
new file mode 100644
index 0000000..48f7f43
--- /dev/null
+++ b/usr/utils/file_mode.c
@@ -0,0 +1,143 @@
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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;
+}