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