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