Start 1.33.0 development cycle
[oweals/busybox.git] / libbb / parse_mode.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * parse_mode implementation for busybox
4  *
5  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9 #include "libbb.h"
10
11 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
12
13 /* This function is used from NOFORK applets. It must not allocate anything */
14
15 #define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
16
17 int FAST_FUNC bb_parse_mode(const char *s, unsigned current_mode)
18 {
19         static const mode_t who_mask[] = {
20                 S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
21                 S_ISUID | S_IRWXU,           /* u */
22                 S_ISGID | S_IRWXG,           /* g */
23                 S_IRWXO                      /* o */
24         };
25         static const mode_t perm_mask[] = {
26                 S_IRUSR | S_IRGRP | S_IROTH, /* r */
27                 S_IWUSR | S_IWGRP | S_IWOTH, /* w */
28                 S_IXUSR | S_IXGRP | S_IXOTH, /* x */
29                 S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
30                 S_ISUID | S_ISGID,           /* s */
31                 S_ISVTX                      /* t */
32         };
33         static const char who_chars[] ALIGN1 = "augo";
34         static const char perm_chars[] ALIGN1 = "rwxXst";
35
36         const char *p;
37         mode_t wholist;
38         mode_t permlist;
39         mode_t new_mode;
40         char op;
41
42         if ((unsigned char)(*s - '0') < 8) {
43                 unsigned long tmp;
44                 char *e;
45
46                 tmp = strtoul(s, &e, 8);
47                 if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
48                         return -1;
49                 }
50                 return tmp;
51         }
52
53         new_mode = current_mode;
54
55         /* Note: we allow empty clauses, and hence empty modes.
56          * We treat an empty mode as no change to perms. */
57
58         while (*s) {  /* Process clauses. */
59                 if (*s == ',') {  /* We allow empty clauses. */
60                         ++s;
61                         continue;
62                 }
63
64                 /* Get a wholist. */
65                 wholist = 0;
66  WHO_LIST:
67                 p = who_chars;
68                 do {
69                         if (*p == *s) {
70                                 wholist |= who_mask[(int)(p-who_chars)];
71                                 if (!*++s) {
72                                         return -1;
73                                 }
74                                 goto WHO_LIST;
75                         }
76                 } while (*++p);
77
78                 do {    /* Process action list. */
79                         if ((*s != '+') && (*s != '-')) {
80                                 if (*s != '=') {
81                                         return -1;
82                                 }
83                                 /* Since op is '=', clear all bits corresponding to the
84                                  * wholist, or all file bits if wholist is empty. */
85                                 permlist = ~FILEMODEBITS;
86                                 if (wholist) {
87                                         permlist = ~wholist;
88                                 }
89                                 new_mode &= permlist;
90                         }
91                         op = *s++;
92
93                         /* Check for permcopy. */
94                         p = who_chars + 1;  /* Skip 'a' entry. */
95                         do {
96                                 if (*p == *s) {
97                                         int i = 0;
98                                         permlist = who_mask[(int)(p-who_chars)]
99                                                          & (S_IRWXU | S_IRWXG | S_IRWXO)
100                                                          & new_mode;
101                                         do {
102                                                 if (permlist & perm_mask[i]) {
103                                                         permlist |= perm_mask[i];
104                                                 }
105                                         } while (++i < 3);
106                                         ++s;
107                                         goto GOT_ACTION;
108                                 }
109                         } while (*++p);
110
111                         /* It was not a permcopy, so get a permlist. */
112                         permlist = 0;
113  PERM_LIST:
114                         p = perm_chars;
115                         do {
116                                 if (*p == *s) {
117                                         if ((*p != 'X')
118                                          || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
119                                         ) {
120                                                 permlist |= perm_mask[(int)(p-perm_chars)];
121                                         }
122                                         if (!*++s) {
123                                                 break;
124                                         }
125                                         goto PERM_LIST;
126                                 }
127                         } while (*++p);
128  GOT_ACTION:
129                         if (permlist) { /* The permlist was nonempty. */
130                                 mode_t tmp = wholist;
131                                 if (!wholist) {
132                                         mode_t u_mask = umask(0);
133                                         umask(u_mask);
134                                         tmp = ~u_mask;
135                                 }
136                                 permlist &= tmp;
137                                 if (op == '-') {
138                                         new_mode &= ~permlist;
139                                 } else {
140                                         new_mode |= permlist;
141                                 }
142                         }
143                 } while (*s && (*s != ','));
144         }
145
146         return new_mode;
147 }