setmode.c revision 1.1.1.2 1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Dave Borman at Cray Research, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #if defined(LIBC_SCCS) && !defined(lint)
38 static char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94";
39 #endif /* LIBC_SCCS and not lint */
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43
44 #include <ctype.h>
45 #include <errno.h>
46 #include <signal.h>
47 #include <stddef.h>
48 #include <stdlib.h>
49
50 #ifdef SETMODE_DEBUG
51 #include <stdio.h>
52 #endif
53
54 #define SET_LEN 6 /* initial # of bitcmd struct to malloc */
55 #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
56
57 typedef struct bitcmd {
58 char cmd;
59 char cmd2;
60 mode_t bits;
61 } BITCMD;
62
63 #define CMD2_CLR 0x01
64 #define CMD2_SET 0x02
65 #define CMD2_GBITS 0x04
66 #define CMD2_OBITS 0x08
67 #define CMD2_UBITS 0x10
68
69 static BITCMD *addcmd __P((BITCMD *, int, int, int, u_int));
70 static int compress_mode __P((BITCMD *));
71 #ifdef SETMODE_DEBUG
72 static void dumpmode __P((BITCMD *));
73 #endif
74
75 /*
76 * Given the old mode and an array of bitcmd structures, apply the operations
77 * described in the bitcmd structures to the old mode, and return the new mode.
78 * Note that there is no '=' command; a strict assignment is just a '-' (clear
79 * bits) followed by a '+' (set bits).
80 */
81 mode_t
82 getmode(bbox, omode)
83 void *bbox;
84 mode_t omode;
85 {
86 register BITCMD *set;
87 register mode_t clrval, newmode, value;
88
89 set = (BITCMD *)bbox;
90 newmode = omode;
91 for (value = 0;; set++)
92 switch(set->cmd) {
93 /*
94 * When copying the user, group or other bits around, we "know"
95 * where the bits are in the mode so that we can do shifts to
96 * copy them around. If we don't use shifts, it gets real
97 * grundgy with lots of single bit checks and bit sets.
98 */
99 case 'u':
100 value = (newmode & S_IRWXU) >> 6;
101 goto common;
102
103 case 'g':
104 value = (newmode & S_IRWXG) >> 3;
105 goto common;
106
107 case 'o':
108 value = newmode & S_IRWXO;
109 common: if (set->cmd2 & CMD2_CLR) {
110 clrval =
111 (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
112 if (set->cmd2 & CMD2_UBITS)
113 newmode &= ~((clrval<<6) & set->bits);
114 if (set->cmd2 & CMD2_GBITS)
115 newmode &= ~((clrval<<3) & set->bits);
116 if (set->cmd2 & CMD2_OBITS)
117 newmode &= ~(clrval & set->bits);
118 }
119 if (set->cmd2 & CMD2_SET) {
120 if (set->cmd2 & CMD2_UBITS)
121 newmode |= (value<<6) & set->bits;
122 if (set->cmd2 & CMD2_GBITS)
123 newmode |= (value<<3) & set->bits;
124 if (set->cmd2 & CMD2_OBITS)
125 newmode |= value & set->bits;
126 }
127 break;
128
129 case '+':
130 newmode |= set->bits;
131 break;
132
133 case '-':
134 newmode &= ~set->bits;
135 break;
136
137 case 'X':
138 if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
139 newmode |= set->bits;
140 break;
141
142 case '\0':
143 default:
144 #ifdef SETMODE_DEBUG
145 (void)printf("getmode:%04o -> %04o\n", omode, newmode);
146 #endif
147 return (newmode);
148 }
149 }
150
151 #define ADDCMD(a, b, c, d) \
152 if (set >= endset) { \
153 register BITCMD *newset; \
154 setlen += SET_LEN_INCR; \
155 newset = realloc(saveset, sizeof(BITCMD) * setlen); \
156 if (!saveset) \
157 return (NULL); \
158 set = newset + (set - saveset); \
159 saveset = newset; \
160 endset = newset + (setlen - 2); \
161 } \
162 set = addcmd(set, (a), (b), (c), (d))
163
164 #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
165
166 void *
167 setmode(p)
168 register char *p;
169 {
170 register int perm, who;
171 register char op;
172 BITCMD *set, *saveset, *endset;
173 sigset_t sigset, sigoset;
174 mode_t mask;
175 int equalopdone, permXbits, setlen;
176
177 if (!*p)
178 return (NULL);
179
180 /*
181 * Get a copy of the mask for the permissions that are mask relative.
182 * Flip the bits, we want what's not set. Since it's possible that
183 * the caller is opening files inside a signal handler, protect them
184 * as best we can.
185 */
186 sigfillset(&sigset);
187 (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);
188 (void)umask(mask = umask(0));
189 mask = ~mask;
190 (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
191
192 setlen = SET_LEN + 2;
193
194 if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
195 return (NULL);
196 saveset = set;
197 endset = set + (setlen - 2);
198
199 /*
200 * If an absolute number, get it and return; disallow non-octal digits
201 * or illegal bits.
202 */
203 if (isdigit(*p)) {
204 perm = (mode_t)strtol(p, NULL, 8);
205 if (perm & ~(STANDARD_BITS|S_ISTXT)) {
206 free(saveset);
207 return (NULL);
208 }
209 while (*++p)
210 if (*p < '0' || *p > '7') {
211 free(saveset);
212 return (NULL);
213 }
214 ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
215 return (saveset);
216 }
217
218 /*
219 * Build list of structures to set/clear/copy bits as described by
220 * each clause of the symbolic mode.
221 */
222 for (;;) {
223 /* First, find out which bits might be modified. */
224 for (who = 0;; ++p) {
225 switch (*p) {
226 case 'a':
227 who |= STANDARD_BITS;
228 break;
229 case 'u':
230 who |= S_ISUID|S_IRWXU;
231 break;
232 case 'g':
233 who |= S_ISGID|S_IRWXG;
234 break;
235 case 'o':
236 who |= S_IRWXO;
237 break;
238 default:
239 goto getop;
240 }
241 }
242
243 getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
244 free(saveset);
245 return (NULL);
246 }
247 if (op == '=')
248 equalopdone = 0;
249
250 who &= ~S_ISTXT;
251 for (perm = 0, permXbits = 0;; ++p) {
252 switch (*p) {
253 case 'r':
254 perm |= S_IRUSR|S_IRGRP|S_IROTH;
255 break;
256 case 's':
257 /* If only "other" bits ignore set-id. */
258 if (who & ~S_IRWXO)
259 perm |= S_ISUID|S_ISGID;
260 break;
261 case 't':
262 /* If only "other" bits ignore sticky. */
263 if (who & ~S_IRWXO) {
264 who |= S_ISTXT;
265 perm |= S_ISTXT;
266 }
267 break;
268 case 'w':
269 perm |= S_IWUSR|S_IWGRP|S_IWOTH;
270 break;
271 case 'X':
272 permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
273 break;
274 case 'x':
275 perm |= S_IXUSR|S_IXGRP|S_IXOTH;
276 break;
277 case 'u':
278 case 'g':
279 case 'o':
280 /*
281 * When ever we hit 'u', 'g', or 'o', we have
282 * to flush out any partial mode that we have,
283 * and then do the copying of the mode bits.
284 */
285 if (perm) {
286 ADDCMD(op, who, perm, mask);
287 perm = 0;
288 }
289 if (op == '=')
290 equalopdone = 1;
291 if (op == '+' && permXbits) {
292 ADDCMD('X', who, permXbits, mask);
293 permXbits = 0;
294 }
295 ADDCMD(*p, who, op, mask);
296 break;
297
298 default:
299 /*
300 * Add any permissions that we haven't already
301 * done.
302 */
303 if (perm || (op == '=' && !equalopdone)) {
304 if (op == '=')
305 equalopdone = 1;
306 ADDCMD(op, who, perm, mask);
307 perm = 0;
308 }
309 if (permXbits) {
310 ADDCMD('X', who, permXbits, mask);
311 permXbits = 0;
312 }
313 goto apply;
314 }
315 }
316
317 apply: if (!*p)
318 break;
319 if (*p != ',')
320 goto getop;
321 ++p;
322 }
323 set->cmd = 0;
324 #ifdef SETMODE_DEBUG
325 (void)printf("Before compress_mode()\n");
326 dumpmode(saveset);
327 #endif
328 compress_mode(saveset);
329 #ifdef SETMODE_DEBUG
330 (void)printf("After compress_mode()\n");
331 dumpmode(saveset);
332 #endif
333 return (saveset);
334 }
335
336 static BITCMD *
337 addcmd(set, op, who, oparg, mask)
338 BITCMD *set;
339 register int oparg, who;
340 register int op;
341 u_int mask;
342 {
343 switch (op) {
344 case '=':
345 set->cmd = '-';
346 set->bits = who ? who : STANDARD_BITS;
347 set++;
348
349 op = '+';
350 /* FALLTHROUGH */
351 case '+':
352 case '-':
353 case 'X':
354 set->cmd = op;
355 set->bits = (who ? who : mask) & oparg;
356 break;
357
358 case 'u':
359 case 'g':
360 case 'o':
361 set->cmd = op;
362 if (who) {
363 set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
364 ((who & S_IRGRP) ? CMD2_GBITS : 0) |
365 ((who & S_IROTH) ? CMD2_OBITS : 0);
366 set->bits = ~0;
367 } else {
368 set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
369 set->bits = mask;
370 }
371
372 if (oparg == '+')
373 set->cmd2 |= CMD2_SET;
374 else if (oparg == '-')
375 set->cmd2 |= CMD2_CLR;
376 else if (oparg == '=')
377 set->cmd2 |= CMD2_SET|CMD2_CLR;
378 break;
379 }
380 return (set + 1);
381 }
382
383 #ifdef SETMODE_DEBUG
384 static void
385 dumpmode(set)
386 register BITCMD *set;
387 {
388 for (; set->cmd; ++set)
389 (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
390 set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
391 set->cmd2 & CMD2_CLR ? " CLR" : "",
392 set->cmd2 & CMD2_SET ? " SET" : "",
393 set->cmd2 & CMD2_UBITS ? " UBITS" : "",
394 set->cmd2 & CMD2_GBITS ? " GBITS" : "",
395 set->cmd2 & CMD2_OBITS ? " OBITS" : "");
396 }
397 #endif
398
399 /*
400 * Given an array of bitcmd structures, compress by compacting consecutive
401 * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
402 * 'g' and 'o' commands continue to be separate. They could probably be
403 * compacted, but it's not worth the effort.
404 */
405 static int
406 compress_mode(set)
407 register BITCMD *set;
408 {
409 register BITCMD *nset;
410 register int setbits, clrbits, Xbits, op;
411
412 for (nset = set;;) {
413 /* Copy over any 'u', 'g' and 'o' commands. */
414 while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
415 *set++ = *nset++;
416 if (!op)
417 return;
418 }
419
420 for (setbits = clrbits = Xbits = 0;; nset++) {
421 if ((op = nset->cmd) == '-') {
422 clrbits |= nset->bits;
423 setbits &= ~nset->bits;
424 Xbits &= ~nset->bits;
425 } else if (op == '+') {
426 setbits |= nset->bits;
427 clrbits &= ~nset->bits;
428 Xbits &= ~nset->bits;
429 } else if (op == 'X')
430 Xbits |= nset->bits & ~setbits;
431 else
432 break;
433 }
434 if (clrbits) {
435 set->cmd = '-';
436 set->cmd2 = 0;
437 set->bits = clrbits;
438 set++;
439 }
440 if (setbits) {
441 set->cmd = '+';
442 set->cmd2 = 0;
443 set->bits = setbits;
444 set++;
445 }
446 if (Xbits) {
447 set->cmd = 'X';
448 set->cmd2 = 0;
449 set->bits = Xbits;
450 set++;
451 }
452 }
453 }
454