xref: /csrg-svn/lib/libc/gen/setmode.c (revision 45901)
139978Sbostic /*
239978Sbostic  * Copyright (c) 1989 The Regents of the University of California.
339978Sbostic  * All rights reserved.
439978Sbostic  *
542626Sbostic  * %sccs.include.redist.c%
639978Sbostic  */
739978Sbostic 
839978Sbostic #if defined(LIBC_SCCS) && !defined(lint)
9*45901Sbostic static char sccsid[] = "@(#)setmode.c	5.4 (Berkeley) 01/09/91";
1039978Sbostic #endif /* LIBC_SCCS and not lint */
1139978Sbostic 
12*45901Sbostic #include <sys/param.h>
1339978Sbostic #include <sys/stat.h>
14*45901Sbostic #include <sys/errno.h>
15*45901Sbostic #ifdef SETMODE_DEBUG
1641579Sbostic #include <stdio.h>
17*45901Sbostic #endif
18*45901Sbostic #include <stdlib.h>
1939978Sbostic 
20*45901Sbostic #define	SET_LEN	6		/* initial # of bitcmd struct to malloc */
21*45901Sbostic #define	SET_LEN_INCR 4		/* # of bitcmd structs to add as needed */
2239978Sbostic 
23*45901Sbostic struct bitcmd {
24*45901Sbostic 	char	cmd;
25*45901Sbostic 	char	cmd2;
26*45901Sbostic 	mode_t	bits;
27*45901Sbostic };
28*45901Sbostic 
29*45901Sbostic #define	CMD2_CLR	0x01
30*45901Sbostic #define	CMD2_SET	0x02
31*45901Sbostic #define	CMD2_GBITS	0x04
32*45901Sbostic #define	CMD2_OBITS	0x08
33*45901Sbostic #define	CMD2_UBITS	0x10
34*45901Sbostic 
35*45901Sbostic /*
36*45901Sbostic  * Given the old mode and an array of bitcmd structures, apply the operations
37*45901Sbostic  * described in the bitcmd structures to the old mode, and return the new mode.
38*45901Sbostic  * Note that there is no '=' command; a strict assignment is just a '-' (clear
39*45901Sbostic  * bits) followed by a '+' (set bits).
40*45901Sbostic  */
4139978Sbostic mode_t
4241579Sbostic getmode(set, omode)
43*45901Sbostic 	register struct bitcmd *set;
44*45901Sbostic 	mode_t omode;
4539978Sbostic {
46*45901Sbostic 	register mode_t newmode, value;
4739978Sbostic 
48*45901Sbostic 	newmode = omode;
49*45901Sbostic 	for (value = 0;; set++)
50*45901Sbostic 		switch(set->cmd) {
51*45901Sbostic 		/*
52*45901Sbostic 		 * When copying the user, group or other bits around, we "know"
53*45901Sbostic 		 * where the bit are in the mode so that we can do shifts to
54*45901Sbostic 		 * copy them around.  If we don't use shifts, it gets real
55*45901Sbostic 		 * grundgy with lots of single bit checks and bit sets.
56*45901Sbostic 		 */
57*45901Sbostic 		case 'u':
58*45901Sbostic 			value = (newmode & S_IRWXU) >> 6;
59*45901Sbostic 			goto common;
60*45901Sbostic 
61*45901Sbostic 		case 'g':
62*45901Sbostic 			value = (newmode & S_IRWXG) >> 3;
63*45901Sbostic 			goto common;
64*45901Sbostic 
65*45901Sbostic 		case 'o':
66*45901Sbostic 			value = newmode & S_IRWXO;
67*45901Sbostic 		common:
68*45901Sbostic 			if (set->cmd2 & CMD2_CLR) {
69*45901Sbostic 				if (set->cmd2 & CMD2_UBITS)
70*45901Sbostic 					newmode &= ~(S_IRWXU & set->bits);
71*45901Sbostic 				if (set->cmd2 & CMD2_GBITS)
72*45901Sbostic 					newmode &= ~(S_IRWXG & set->bits);
73*45901Sbostic 				if (set->cmd2 & CMD2_OBITS)
74*45901Sbostic 					newmode &= ~(S_IRWXO & set->bits);
75*45901Sbostic 			}
76*45901Sbostic 			if (set->cmd2 & CMD2_SET) {
77*45901Sbostic 				if (set->cmd2 & CMD2_UBITS)
78*45901Sbostic 					newmode |= (value<<6) & set->bits;
79*45901Sbostic 				if (set->cmd2 & CMD2_GBITS)
80*45901Sbostic 					newmode |= (value<<3) & set->bits;
81*45901Sbostic 				if (set->cmd2 & CMD2_OBITS)
82*45901Sbostic 					newmode |= value & set->bits;
83*45901Sbostic 			}
84*45901Sbostic 			break;
85*45901Sbostic 
86*45901Sbostic 		case '+':
87*45901Sbostic 			newmode |= set->bits;
88*45901Sbostic 			break;
89*45901Sbostic 
90*45901Sbostic 		case '-':
91*45901Sbostic 			newmode &= ~set->bits;
92*45901Sbostic 			break;
93*45901Sbostic 
94*45901Sbostic 		case 'X':
95*45901Sbostic 			if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
96*45901Sbostic 				newmode |= set->bits;
97*45901Sbostic 			break;
98*45901Sbostic 
99*45901Sbostic 		case '\0':
100*45901Sbostic 		default:
101*45901Sbostic #ifdef SETMODE_DEBUG
102*45901Sbostic 			(void)printf("getmode(, %04o) -> %04o\n",
103*45901Sbostic 			    omode, newmode);
104*45901Sbostic #endif
105*45901Sbostic 			return(newmode);
106*45901Sbostic 		}
10739978Sbostic }
10839978Sbostic 
10939978Sbostic #define	STANDARD_BITS	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
11039978Sbostic 
111*45901Sbostic static struct bitcmd *
112*45901Sbostic addcmd(set, op, who, oparg, mask)
113*45901Sbostic 	struct bitcmd *set;
114*45901Sbostic 	register int oparg, who;
115*45901Sbostic 	register int op;
116*45901Sbostic 	mode_t mask;
117*45901Sbostic {
118*45901Sbostic 	switch (op) {
119*45901Sbostic 	case '+':
120*45901Sbostic 	case 'X':
121*45901Sbostic 		set->cmd = op;
122*45901Sbostic 		set->bits = (who ? who : mask) & oparg;
123*45901Sbostic 		break;
124*45901Sbostic 
125*45901Sbostic 	case '-':
126*45901Sbostic 		set->cmd = '-';
127*45901Sbostic 		set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg;
128*45901Sbostic 		break;
129*45901Sbostic 
130*45901Sbostic 	case '=':
131*45901Sbostic 		set->cmd = '-';
132*45901Sbostic 		if (!who) {
133*45901Sbostic 			set->bits = STANDARD_BITS;
134*45901Sbostic 			who = mask;
135*45901Sbostic 		} else
136*45901Sbostic 			set->bits = who;
137*45901Sbostic 		set++;
138*45901Sbostic 
139*45901Sbostic 		set->cmd = '+';
140*45901Sbostic 		set->bits = who & oparg;
141*45901Sbostic 		break;
142*45901Sbostic 	case 'u':
143*45901Sbostic 	case 'g':
144*45901Sbostic 	case 'o':
145*45901Sbostic 		set->cmd = op;
146*45901Sbostic 		if (who) {
147*45901Sbostic 			set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
148*45901Sbostic 				    ((who & S_IRGRP) ? CMD2_GBITS : 0) |
149*45901Sbostic 				    ((who & S_IROTH) ? CMD2_OBITS : 0);
150*45901Sbostic 			set->bits = ~0;
151*45901Sbostic 		} else {
152*45901Sbostic 			set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
153*45901Sbostic 			set->bits = mask;
154*45901Sbostic 		}
155*45901Sbostic 
156*45901Sbostic 		if (oparg == '+')
157*45901Sbostic 			set->cmd2 |= CMD2_SET;
158*45901Sbostic 		else if (oparg == '-')
159*45901Sbostic 			set->cmd2 |= CMD2_CLR;
160*45901Sbostic 		else if (oparg == '=')
161*45901Sbostic 			set->cmd2 |= CMD2_SET|CMD2_CLR;
162*45901Sbostic 		break;
163*45901Sbostic 	}
164*45901Sbostic 	return(set+1);
165*45901Sbostic }
166*45901Sbostic 
167*45901Sbostic #define	ADDCMD(a, b, c, d) \
168*45901Sbostic 	if (set >= endset) { \
169*45901Sbostic 		register struct bitcmd *newset; \
170*45901Sbostic 		setlen += SET_LEN_INCR; \
171*45901Sbostic 		newset = realloc(saveset, sizeof(struct bitcmd) * setlen); \
172*45901Sbostic 		if (!saveset) \
173*45901Sbostic 			return(NULL); \
174*45901Sbostic 		set = newset + (set - saveset); \
175*45901Sbostic 		saveset = newset; \
176*45901Sbostic 		endset = newset + (setlen - 2); \
177*45901Sbostic 	} \
178*45901Sbostic 	set = addcmd(set, (a), (b), (c), (d))
179*45901Sbostic 
180*45901Sbostic struct bitcmd *
18139978Sbostic setmode(p)
18239978Sbostic 	register char *p;
18339978Sbostic {
18439978Sbostic 	register int perm, who;
18539978Sbostic 	register char op;
186*45901Sbostic 	mode_t mask;
187*45901Sbostic 	struct bitcmd *set, *saveset, *endset;
188*45901Sbostic 	int permXbits, setlen;
18939978Sbostic 
19039978Sbostic 	/*
191*45901Sbostic 	 * Get a copy of the mask for the permissions that are mask relative.
192*45901Sbostic 	 * Flip the bits, we want what's not set.
19339978Sbostic 	 */
19439978Sbostic 	(void)umask(mask = umask(0));
19539978Sbostic 	mask = ~mask;
19639978Sbostic 
197*45901Sbostic 	setlen = SET_LEN + 2;
198*45901Sbostic 
199*45901Sbostic 	set = (struct bitcmd *)malloc((u_int)(sizeof(struct bitcmd) * setlen));
200*45901Sbostic 	if (!set)
20141579Sbostic 		return(NULL);
202*45901Sbostic 	saveset = set;
203*45901Sbostic 	endset = set + (setlen - 2);
20441579Sbostic 
20539978Sbostic 	/*
206*45901Sbostic 	 * If an absolute number, get it and return; disallow non-octal digits
207*45901Sbostic 	 * or illegal bits.
20839978Sbostic 	 */
20939978Sbostic 	if (isdigit(*p)) {
210*45901Sbostic 		perm = (mode_t)strtol(p, (char **)0, 8);
211*45901Sbostic 		if (perm & ~(STANDARD_BITS|S_ISTXT)) {
212*45901Sbostic 			free(saveset);
213*45901Sbostic 			return(NULL);
214*45901Sbostic 		}
21539978Sbostic 		while (*++p)
216*45901Sbostic 			if (*p < '0' || *p > '7') {
217*45901Sbostic 				free(saveset);
21841579Sbostic 				return(NULL);
219*45901Sbostic 			}
220*45901Sbostic 		ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
221*45901Sbostic 		return(saveset);
22239978Sbostic 	}
22339978Sbostic 
224*45901Sbostic 	if (!*p) {
225*45901Sbostic 		free(saveset);
22641579Sbostic 		return(NULL);
227*45901Sbostic 	}
22839978Sbostic 	/*
229*45901Sbostic 	 * Build list of structures to set/clear/copy bits as described by
230*45901Sbostic 	 * each clause of the symbolic mode.
23139978Sbostic 	 */
23239978Sbostic 	for (;;) {
233*45901Sbostic 		/* First, find out which bits might be modified. */
234*45901Sbostic 		for (who = 0;; ++p) {
23539978Sbostic 			switch (*p) {
23639978Sbostic 			case 'a':
23739978Sbostic 				who |= STANDARD_BITS;
23839978Sbostic 				break;
23939978Sbostic 			case 'u':
24039978Sbostic 				who |= S_ISUID|S_IRWXU;
24139978Sbostic 				break;
24239978Sbostic 			case 'g':
24339978Sbostic 				who |= S_ISGID|S_IRWXG;
24439978Sbostic 				break;
24539978Sbostic 			case 'o':
24639978Sbostic 				who |= S_IRWXO;
24739978Sbostic 				break;
24839978Sbostic 			default:
24939978Sbostic 				goto getop;
25039978Sbostic 			}
251*45901Sbostic 		}
252*45901Sbostic 	getop:
25339978Sbostic 
254*45901Sbostic 		if ((op = *p++) != '+' && op != '-' && op != '=') {
255*45901Sbostic 			free(saveset);
25641579Sbostic 			return(NULL);
257*45901Sbostic 		}
25839978Sbostic 
25939978Sbostic 		who &= ~S_ISTXT;
260*45901Sbostic 		for (perm = 0, permXbits = 0;; ++p) {
26139978Sbostic 			switch (*p) {
26239978Sbostic 			case 'r':
26339978Sbostic 				perm |= S_IRUSR|S_IRGRP|S_IROTH;
26439978Sbostic 				break;
26539978Sbostic 			case 's':
266*45901Sbostic 				/* If only "other" bits ignore set-id. */
26739978Sbostic 				if (who & ~S_IRWXO)
26839978Sbostic 					perm |= S_ISUID|S_ISGID;
26939978Sbostic 				break;
27039978Sbostic 			case 't':
271*45901Sbostic 				/* If only "other" bits ignore sticky. */
27239978Sbostic 				if (who & ~S_IRWXO) {
27339978Sbostic 					who |= S_ISTXT;
27439978Sbostic 					perm |= S_ISTXT;
27539978Sbostic 				}
27639978Sbostic 				break;
27739978Sbostic 			case 'w':
27839978Sbostic 				perm |= S_IWUSR|S_IWGRP|S_IWOTH;
27939978Sbostic 				break;
28039978Sbostic 			case 'X':
28139978Sbostic 				permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
28239978Sbostic 				break;
28339978Sbostic 			case 'x':
28439978Sbostic 				perm |= S_IXUSR|S_IXGRP|S_IXOTH;
28539978Sbostic 				break;
286*45901Sbostic 			case 'u':
287*45901Sbostic 			case 'g':
288*45901Sbostic 			case 'o':
289*45901Sbostic 				/*
290*45901Sbostic 				 * When ever we hit 'u', 'g', or 'o', we have
291*45901Sbostic 				 * to flush out any partial mode that we have,
292*45901Sbostic 				 * and then do the copying of the mode bits.
293*45901Sbostic 				 */
294*45901Sbostic 				if (perm) {
295*45901Sbostic 					ADDCMD(op, who, perm, mask);
296*45901Sbostic 					perm = 0;
297*45901Sbostic 				}
298*45901Sbostic 				if (op == '+' && permXbits) {
299*45901Sbostic 					ADDCMD('X', who, permXbits, mask);
300*45901Sbostic 					permXbits = 0;
301*45901Sbostic 				}
302*45901Sbostic 				ADDCMD(*p, who, op, mask);
303*45901Sbostic 				break;
304*45901Sbostic 
30539978Sbostic 			default:
306*45901Sbostic 				/*
307*45901Sbostic 				 * Add any permissions that we haven't already
308*45901Sbostic 				 * done.
309*45901Sbostic 				 */
310*45901Sbostic 				if (perm) {
311*45901Sbostic 					ADDCMD(op, who, perm, mask);
312*45901Sbostic 					perm = 0;
313*45901Sbostic 				}
314*45901Sbostic 				if (permXbits) {
315*45901Sbostic 					ADDCMD('X', who, permXbits, mask);
316*45901Sbostic 					permXbits = 0;
317*45901Sbostic 				}
31839978Sbostic 				goto apply;
31939978Sbostic 			}
32039978Sbostic 		}
32139978Sbostic 
322*45901Sbostic apply:		if (!*p)
32339978Sbostic 			break;
32439978Sbostic 		if (*p != ',')
32539978Sbostic 			goto getop;
32639978Sbostic 		++p;
32739978Sbostic 	}
328*45901Sbostic 	set->cmd = 0;
329*45901Sbostic #ifdef SETMODE_DEBUG
330*45901Sbostic 	(void)printf("Before compress_mode()\n");
331*45901Sbostic 	dumpmode(saveset);
332*45901Sbostic #endif
333*45901Sbostic 	compress_mode(saveset);
334*45901Sbostic #ifdef SETMODE_DEBUG
335*45901Sbostic 	(void)printf("After compress_mode()\n");
336*45901Sbostic 	dumpmode(saveset);
337*45901Sbostic #endif
338*45901Sbostic 	return(saveset);
33939978Sbostic }
340*45901Sbostic 
341*45901Sbostic #ifdef SETMODE_DEBUG
342*45901Sbostic dumpmode(set)
343*45901Sbostic 	register struct bitcmd *set;
344*45901Sbostic {
345*45901Sbostic 	for (; set->cmd; ++set)
346*45901Sbostic 		(void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
347*45901Sbostic 		    set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
348*45901Sbostic 		    set->cmd2 & CMD2_CLR ? " CLR" : "",
349*45901Sbostic 		    set->cmd2 & CMD2_SET ? " SET" : "",
350*45901Sbostic 		    set->cmd2 & CMD2_UBITS ? " UBITS" : "",
351*45901Sbostic 		    set->cmd2 & CMD2_GBITS ? " GBITS" : "",
352*45901Sbostic 		    set->cmd2 & CMD2_OBITS ? " OBITS" : "");
353*45901Sbostic }
354*45901Sbostic #endif
355*45901Sbostic 
356*45901Sbostic /*
357*45901Sbostic  * Given an array of bitcmd structures, compress by compacting consecutive
358*45901Sbostic  * '+', '-' and 'X' commands into at most 3 commands, one of each.  The 'u',
359*45901Sbostic  * 'g' and 'o' commands continue to be separate.  They could probably be
360*45901Sbostic  * compacted, but it's not worth the effort.
361*45901Sbostic  */
362*45901Sbostic static
363*45901Sbostic compress_mode(set)
364*45901Sbostic 	register struct bitcmd *set;
365*45901Sbostic {
366*45901Sbostic 	register struct bitcmd *nset;
367*45901Sbostic 	register int setbits, clrbits, Xbits, op;
368*45901Sbostic 
369*45901Sbostic 	for (nset = set;;) {
370*45901Sbostic 		/* Copy over any 'u', 'g' and 'o' commands. */
371*45901Sbostic 		while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
372*45901Sbostic 			*set++ = *nset++;
373*45901Sbostic 			if (!op)
374*45901Sbostic 				return;
375*45901Sbostic 		}
376*45901Sbostic 
377*45901Sbostic 		for (setbits = clrbits = Xbits = 0;; nset++) {
378*45901Sbostic 			if ((op = nset->cmd) == '-') {
379*45901Sbostic 				clrbits |= nset->bits;
380*45901Sbostic 				setbits &= ~nset->bits;
381*45901Sbostic 				Xbits &= ~nset->bits;
382*45901Sbostic 			} else if (op == '+') {
383*45901Sbostic 				setbits |= nset->bits;
384*45901Sbostic 				clrbits &= ~nset->bits;
385*45901Sbostic 				Xbits &= ~nset->bits;
386*45901Sbostic 			} else if (op == 'X')
387*45901Sbostic 				Xbits |= nset->bits & ~setbits;
388*45901Sbostic 			else
389*45901Sbostic 				break;
390*45901Sbostic 		}
391*45901Sbostic 		if (clrbits) {
392*45901Sbostic 			set->cmd = '-';
393*45901Sbostic 			set->cmd2 = 0;
394*45901Sbostic 			set->bits = clrbits;
395*45901Sbostic 			set++;
396*45901Sbostic 		}
397*45901Sbostic 		if (setbits) {
398*45901Sbostic 			set->cmd = '+';
399*45901Sbostic 			set->cmd2 = 0;
400*45901Sbostic 			set->bits = setbits;
401*45901Sbostic 			set++;
402*45901Sbostic 		}
403*45901Sbostic 		if (Xbits) {
404*45901Sbostic 			set->cmd = 'X';
405*45901Sbostic 			set->cmd2 = 0;
406*45901Sbostic 			set->bits = Xbits;
407*45901Sbostic 			set++;
408*45901Sbostic 		}
409*45901Sbostic 	}
410*45901Sbostic }
411