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