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