Annotation of src/lib/libc/gen/setmode.c, Revision 1.27
1.27 ! mycroft 1: /* $NetBSD: setmode.c,v 1.26 2000/01/22 22:19:12 mycroft Exp $ */
1.10 cgd 2:
1.1 cgd 3: /*
1.9 cgd 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.
1.1 cgd 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:
1.16 christos 39: #include <sys/cdefs.h>
1.1 cgd 40: #if defined(LIBC_SCCS) && !defined(lint)
1.10 cgd 41: #if 0
42: static char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94";
43: #else
1.27 ! mycroft 44: __RCSID("$NetBSD: setmode.c,v 1.26 2000/01/22 22:19:12 mycroft Exp $");
1.10 cgd 45: #endif
1.1 cgd 46: #endif /* LIBC_SCCS and not lint */
47:
1.17 jtc 48: #include "namespace.h"
1.6 cgd 49: #include <sys/types.h>
1.1 cgd 50: #include <sys/stat.h>
1.9 cgd 51:
1.22 lukem 52: #include <assert.h>
1.9 cgd 53: #include <ctype.h>
54: #include <errno.h>
1.6 cgd 55: #include <signal.h>
1.9 cgd 56: #include <stdlib.h>
1.14 cgd 57: #include <unistd.h>
1.9 cgd 58:
1.1 cgd 59: #ifdef SETMODE_DEBUG
60: #include <stdio.h>
1.17 jtc 61: #endif
62:
63: #ifdef __weak_alias
1.26 mycroft 64: __weak_alias(getmode,_getmode)
65: __weak_alias(setmode,_setmode)
1.1 cgd 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:
1.6 cgd 71: typedef struct bitcmd {
1.1 cgd 72: char cmd;
73: char cmd2;
74: mode_t bits;
1.6 cgd 75: } BITCMD;
1.1 cgd 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:
1.6 cgd 83: static BITCMD *addcmd __P((BITCMD *, int, int, int, u_int));
1.13 jtc 84: static void compress_mode __P((BITCMD *));
1.6 cgd 85: #ifdef SETMODE_DEBUG
86: static void dumpmode __P((BITCMD *));
87: #endif
88:
1.1 cgd 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)
1.14 cgd 97: const void *bbox;
1.1 cgd 98: mode_t omode;
99: {
1.19 perry 100: const BITCMD *set;
101: mode_t clrval, newmode, value;
1.1 cgd 102:
1.22 lukem 103: _DIAGASSERT(bbox != NULL);
104:
1.14 cgd 105: set = (const BITCMD *)bbox;
1.1 cgd 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"
1.6 cgd 111: * where the bits are in the mode so that we can do shifts to
1.1 cgd 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;
1.6 cgd 125: common: if (set->cmd2 & CMD2_CLR) {
1.8 cgd 126: clrval =
127: (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
1.1 cgd 128: if (set->cmd2 & CMD2_UBITS)
1.8 cgd 129: newmode &= ~((clrval<<6) & set->bits);
1.1 cgd 130: if (set->cmd2 & CMD2_GBITS)
1.8 cgd 131: newmode &= ~((clrval<<3) & set->bits);
1.1 cgd 132: if (set->cmd2 & CMD2_OBITS)
1.8 cgd 133: newmode &= ~(clrval & set->bits);
1.1 cgd 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
1.6 cgd 161: (void)printf("getmode:%04o -> %04o\n", omode, newmode);
1.1 cgd 162: #endif
1.6 cgd 163: return (newmode);
1.1 cgd 164: }
165: }
166:
1.25 enami 167: #define ADDCMD(a, b, c, d) do { \
1.9 cgd 168: if (set >= endset) { \
1.25 enami 169: BITCMD *newset; \
1.9 cgd 170: setlen += SET_LEN_INCR; \
171: newset = realloc(saveset, sizeof(BITCMD) * setlen); \
1.25 enami 172: if (newset == NULL) { \
173: free(saveset); \
1.9 cgd 174: return (NULL); \
1.25 enami 175: } \
1.9 cgd 176: set = newset + (set - saveset); \
177: saveset = newset; \
178: endset = newset + (setlen - 2); \
179: } \
1.25 enami 180: set = addcmd(set, (a), (b), (c), (d)); \
1.27 ! mycroft 181: } while (/*CONSTCOND*/0)
1.1 cgd 182:
1.6 cgd 183: #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
184:
1.1 cgd 185: void *
186: setmode(p)
1.19 perry 187: const char *p;
1.1 cgd 188: {
1.19 perry 189: int perm, who;
1.24 mycroft 190: char op, *ep;
1.6 cgd 191: BITCMD *set, *saveset, *endset;
192: sigset_t sigset, sigoset;
1.1 cgd 193: mode_t mask;
1.16 christos 194: int equalopdone = 0; /* pacify gcc */
195: int permXbits, setlen;
1.6 cgd 196:
197: if (!*p)
198: return (NULL);
1.1 cgd 199:
200: /*
201: * Get a copy of the mask for the permissions that are mask relative.
1.6 cgd 202: * Flip the bits, we want what's not set. Since it's possible that
203: * the caller is opening files inside a signal handler, protect them
204: * as best we can.
1.1 cgd 205: */
1.6 cgd 206: sigfillset(&sigset);
207: (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);
1.1 cgd 208: (void)umask(mask = umask(0));
209: mask = ~mask;
1.6 cgd 210: (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
1.1 cgd 211:
212: setlen = SET_LEN + 2;
213:
1.6 cgd 214: if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
215: return (NULL);
1.1 cgd 216: saveset = set;
217: endset = set + (setlen - 2);
218:
219: /*
220: * If an absolute number, get it and return; disallow non-octal digits
221: * or illegal bits.
222: */
1.21 christos 223: if (isdigit((unsigned char)*p)) {
1.24 mycroft 224: perm = (mode_t)strtol(p, &ep, 8);
225: if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
1.1 cgd 226: free(saveset);
1.6 cgd 227: return (NULL);
1.1 cgd 228: }
229: ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
1.18 mycroft 230: set->cmd = 0;
1.6 cgd 231: return (saveset);
1.1 cgd 232: }
233:
234: /*
235: * Build list of structures to set/clear/copy bits as described by
236: * each clause of the symbolic mode.
237: */
238: for (;;) {
239: /* First, find out which bits might be modified. */
240: for (who = 0;; ++p) {
241: switch (*p) {
242: case 'a':
243: who |= STANDARD_BITS;
244: break;
245: case 'u':
246: who |= S_ISUID|S_IRWXU;
247: break;
248: case 'g':
249: who |= S_ISGID|S_IRWXG;
250: break;
251: case 'o':
252: who |= S_IRWXO;
253: break;
254: default:
255: goto getop;
256: }
257: }
258:
1.6 cgd 259: getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
1.1 cgd 260: free(saveset);
1.6 cgd 261: return (NULL);
1.1 cgd 262: }
1.7 cgd 263: if (op == '=')
264: equalopdone = 0;
1.1 cgd 265:
266: who &= ~S_ISTXT;
267: for (perm = 0, permXbits = 0;; ++p) {
268: switch (*p) {
269: case 'r':
270: perm |= S_IRUSR|S_IRGRP|S_IROTH;
271: break;
272: case 's':
1.15 christos 273: /*
274: * If specific bits where requested and
275: * only "other" bits ignore set-id.
276: */
277: if (who == 0 || (who & ~S_IRWXO))
1.1 cgd 278: perm |= S_ISUID|S_ISGID;
279: break;
280: case 't':
1.15 christos 281: /*
282: * If specific bits where requested and
283: * only "other" bits ignore set-id.
284: */
285: if (who == 0 || (who & ~S_IRWXO)) {
1.1 cgd 286: who |= S_ISTXT;
287: perm |= S_ISTXT;
288: }
289: break;
290: case 'w':
291: perm |= S_IWUSR|S_IWGRP|S_IWOTH;
292: break;
293: case 'X':
294: permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
295: break;
296: case 'x':
297: perm |= S_IXUSR|S_IXGRP|S_IXOTH;
298: break;
299: case 'u':
300: case 'g':
301: case 'o':
302: /*
303: * When ever we hit 'u', 'g', or 'o', we have
304: * to flush out any partial mode that we have,
305: * and then do the copying of the mode bits.
306: */
1.8 cgd 307: if (perm) {
1.1 cgd 308: ADDCMD(op, who, perm, mask);
309: perm = 0;
310: }
1.8 cgd 311: if (op == '=')
312: equalopdone = 1;
1.1 cgd 313: if (op == '+' && permXbits) {
314: ADDCMD('X', who, permXbits, mask);
315: permXbits = 0;
316: }
317: ADDCMD(*p, who, op, mask);
318: break;
319:
320: default:
321: /*
322: * Add any permissions that we haven't already
323: * done.
324: */
1.7 cgd 325: if (perm || (op == '=' && !equalopdone)) {
1.8 cgd 326: if (op == '=')
327: equalopdone = 1;
1.1 cgd 328: ADDCMD(op, who, perm, mask);
329: perm = 0;
330: }
331: if (permXbits) {
332: ADDCMD('X', who, permXbits, mask);
333: permXbits = 0;
334: }
335: goto apply;
336: }
337: }
338:
339: apply: if (!*p)
340: break;
341: if (*p != ',')
342: goto getop;
343: ++p;
344: }
345: set->cmd = 0;
346: #ifdef SETMODE_DEBUG
347: (void)printf("Before compress_mode()\n");
348: dumpmode(saveset);
349: #endif
350: compress_mode(saveset);
351: #ifdef SETMODE_DEBUG
352: (void)printf("After compress_mode()\n");
353: dumpmode(saveset);
354: #endif
1.6 cgd 355: return (saveset);
356: }
357:
358: static BITCMD *
359: addcmd(set, op, who, oparg, mask)
360: BITCMD *set;
1.19 perry 361: int oparg, who;
362: int op;
1.6 cgd 363: u_int mask;
364: {
1.22 lukem 365:
366: _DIAGASSERT(set != NULL);
367:
1.6 cgd 368: switch (op) {
1.9 cgd 369: case '=':
370: set->cmd = '-';
371: set->bits = who ? who : STANDARD_BITS;
372: set++;
373:
374: op = '+';
375: /* FALLTHROUGH */
1.6 cgd 376: case '+':
1.9 cgd 377: case '-':
1.6 cgd 378: case 'X':
379: set->cmd = op;
380: set->bits = (who ? who : mask) & oparg;
381: break;
382:
383: case 'u':
384: case 'g':
385: case 'o':
386: set->cmd = op;
387: if (who) {
388: set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
389: ((who & S_IRGRP) ? CMD2_GBITS : 0) |
390: ((who & S_IROTH) ? CMD2_OBITS : 0);
1.20 perry 391: set->bits = (mode_t)~0;
1.6 cgd 392: } else {
393: set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
1.9 cgd 394: set->bits = mask;
1.6 cgd 395: }
396:
397: if (oparg == '+')
398: set->cmd2 |= CMD2_SET;
399: else if (oparg == '-')
400: set->cmd2 |= CMD2_CLR;
401: else if (oparg == '=')
402: set->cmd2 |= CMD2_SET|CMD2_CLR;
403: break;
404: }
405: return (set + 1);
1.1 cgd 406: }
407:
408: #ifdef SETMODE_DEBUG
1.6 cgd 409: static void
1.1 cgd 410: dumpmode(set)
1.19 perry 411: BITCMD *set;
1.1 cgd 412: {
1.22 lukem 413:
414: _DIAGASSERT(set != NULL);
415:
1.1 cgd 416: for (; set->cmd; ++set)
417: (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
418: set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
419: set->cmd2 & CMD2_CLR ? " CLR" : "",
420: set->cmd2 & CMD2_SET ? " SET" : "",
421: set->cmd2 & CMD2_UBITS ? " UBITS" : "",
422: set->cmd2 & CMD2_GBITS ? " GBITS" : "",
423: set->cmd2 & CMD2_OBITS ? " OBITS" : "");
424: }
425: #endif
426:
427: /*
428: * Given an array of bitcmd structures, compress by compacting consecutive
429: * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
430: * 'g' and 'o' commands continue to be separate. They could probably be
431: * compacted, but it's not worth the effort.
432: */
1.13 jtc 433: static void
1.1 cgd 434: compress_mode(set)
1.19 perry 435: BITCMD *set;
1.1 cgd 436: {
1.19 perry 437: BITCMD *nset;
438: int setbits, clrbits, Xbits, op;
1.22 lukem 439:
440: _DIAGASSERT(set != NULL);
1.1 cgd 441:
442: for (nset = set;;) {
443: /* Copy over any 'u', 'g' and 'o' commands. */
444: while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
445: *set++ = *nset++;
446: if (!op)
447: return;
448: }
449:
450: for (setbits = clrbits = Xbits = 0;; nset++) {
451: if ((op = nset->cmd) == '-') {
452: clrbits |= nset->bits;
453: setbits &= ~nset->bits;
454: Xbits &= ~nset->bits;
455: } else if (op == '+') {
456: setbits |= nset->bits;
457: clrbits &= ~nset->bits;
458: Xbits &= ~nset->bits;
459: } else if (op == 'X')
460: Xbits |= nset->bits & ~setbits;
461: else
462: break;
463: }
464: if (clrbits) {
465: set->cmd = '-';
466: set->cmd2 = 0;
467: set->bits = clrbits;
468: set++;
469: }
470: if (setbits) {
471: set->cmd = '+';
472: set->cmd2 = 0;
473: set->bits = setbits;
474: set++;
475: }
476: if (Xbits) {
477: set->cmd = 'X';
478: set->cmd2 = 0;
479: set->bits = Xbits;
480: set++;
481: }
482: }
483: }
CVSweb <webmaster@jp.NetBSD.org>