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