xref: /csrg-svn/lib/libc/gen/setmode.c (revision 49896)
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*49896Sbostic static char sccsid[] = "@(#)setmode.c	5.6 (Berkeley) 05/27/91";
1039978Sbostic #endif /* LIBC_SCCS and not lint */
1139978Sbostic 
1245901Sbostic #include <sys/param.h>
1339978Sbostic #include <sys/stat.h>
1445901Sbostic #include <sys/errno.h>
1545901Sbostic #ifdef SETMODE_DEBUG
1641579Sbostic #include <stdio.h>
1745901Sbostic #endif
1845901Sbostic #include <stdlib.h>
19*49896Sbostic #include <ctype.h>
2039978Sbostic 
2145901Sbostic #define	SET_LEN	6		/* initial # of bitcmd struct to malloc */
2245901Sbostic #define	SET_LEN_INCR 4		/* # of bitcmd structs to add as needed */
2339978Sbostic 
2445901Sbostic struct bitcmd {
2545901Sbostic 	char	cmd;
2645901Sbostic 	char	cmd2;
2745901Sbostic 	mode_t	bits;
2845901Sbostic };
2945901Sbostic 
3045901Sbostic #define	CMD2_CLR	0x01
3145901Sbostic #define	CMD2_SET	0x02
3245901Sbostic #define	CMD2_GBITS	0x04
3345901Sbostic #define	CMD2_OBITS	0x08
3445901Sbostic #define	CMD2_UBITS	0x10
3545901Sbostic 
3645901Sbostic /*
3745901Sbostic  * Given the old mode and an array of bitcmd structures, apply the operations
3845901Sbostic  * described in the bitcmd structures to the old mode, and return the new mode.
3945901Sbostic  * Note that there is no '=' command; a strict assignment is just a '-' (clear
4045901Sbostic  * bits) followed by a '+' (set bits).
4145901Sbostic  */
4239978Sbostic mode_t
4346576Sbostic getmode(bbox, omode)
4446576Sbostic 	void *bbox;
4545901Sbostic 	mode_t omode;
4639978Sbostic {
4746576Sbostic 	register struct bitcmd *set;
4845901Sbostic 	register mode_t newmode, value;
4939978Sbostic 
5046576Sbostic 	set = (struct bitcmd *)bbox;
5145901Sbostic 	newmode = omode;
5245901Sbostic 	for (value = 0;; set++)
5345901Sbostic 		switch(set->cmd) {
5445901Sbostic 		/*
5545901Sbostic 		 * When copying the user, group or other bits around, we "know"
5645901Sbostic 		 * where the bit are in the mode so that we can do shifts to
5745901Sbostic 		 * copy them around.  If we don't use shifts, it gets real
5845901Sbostic 		 * grundgy with lots of single bit checks and bit sets.
5945901Sbostic 		 */
6045901Sbostic 		case 'u':
6145901Sbostic 			value = (newmode & S_IRWXU) >> 6;
6245901Sbostic 			goto common;
6345901Sbostic 
6445901Sbostic 		case 'g':
6545901Sbostic 			value = (newmode & S_IRWXG) >> 3;
6645901Sbostic 			goto common;
6745901Sbostic 
6845901Sbostic 		case 'o':
6945901Sbostic 			value = newmode & S_IRWXO;
7045901Sbostic 		common:
7145901Sbostic 			if (set->cmd2 & CMD2_CLR) {
7245901Sbostic 				if (set->cmd2 & CMD2_UBITS)
7345901Sbostic 					newmode &= ~(S_IRWXU & set->bits);
7445901Sbostic 				if (set->cmd2 & CMD2_GBITS)
7545901Sbostic 					newmode &= ~(S_IRWXG & set->bits);
7645901Sbostic 				if (set->cmd2 & CMD2_OBITS)
7745901Sbostic 					newmode &= ~(S_IRWXO & set->bits);
7845901Sbostic 			}
7945901Sbostic 			if (set->cmd2 & CMD2_SET) {
8045901Sbostic 				if (set->cmd2 & CMD2_UBITS)
8145901Sbostic 					newmode |= (value<<6) & set->bits;
8245901Sbostic 				if (set->cmd2 & CMD2_GBITS)
8345901Sbostic 					newmode |= (value<<3) & set->bits;
8445901Sbostic 				if (set->cmd2 & CMD2_OBITS)
8545901Sbostic 					newmode |= value & set->bits;
8645901Sbostic 			}
8745901Sbostic 			break;
8845901Sbostic 
8945901Sbostic 		case '+':
9045901Sbostic 			newmode |= set->bits;
9145901Sbostic 			break;
9245901Sbostic 
9345901Sbostic 		case '-':
9445901Sbostic 			newmode &= ~set->bits;
9545901Sbostic 			break;
9645901Sbostic 
9745901Sbostic 		case 'X':
9845901Sbostic 			if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
9945901Sbostic 				newmode |= set->bits;
10045901Sbostic 			break;
10145901Sbostic 
10245901Sbostic 		case '\0':
10345901Sbostic 		default:
10445901Sbostic #ifdef SETMODE_DEBUG
10545901Sbostic 			(void)printf("getmode(, %04o) -> %04o\n",
10645901Sbostic 			    omode, newmode);
10745901Sbostic #endif
10845901Sbostic 			return(newmode);
10945901Sbostic 		}
11039978Sbostic }
11139978Sbostic 
11239978Sbostic #define	STANDARD_BITS	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
11339978Sbostic 
11445901Sbostic static struct bitcmd *
11545901Sbostic addcmd(set, op, who, oparg, mask)
11645901Sbostic 	struct bitcmd *set;
11745901Sbostic 	register int oparg, who;
11845901Sbostic 	register int op;
11945901Sbostic 	mode_t mask;
12045901Sbostic {
12145901Sbostic 	switch (op) {
12245901Sbostic 	case '+':
12345901Sbostic 	case 'X':
12445901Sbostic 		set->cmd = op;
12545901Sbostic 		set->bits = (who ? who : mask) & oparg;
12645901Sbostic 		break;
12745901Sbostic 
12845901Sbostic 	case '-':
12945901Sbostic 		set->cmd = '-';
13045901Sbostic 		set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg;
13145901Sbostic 		break;
13245901Sbostic 
13345901Sbostic 	case '=':
13445901Sbostic 		set->cmd = '-';
13545901Sbostic 		if (!who) {
13645901Sbostic 			set->bits = STANDARD_BITS;
13745901Sbostic 			who = mask;
13845901Sbostic 		} else
13945901Sbostic 			set->bits = who;
14045901Sbostic 		set++;
14145901Sbostic 
14245901Sbostic 		set->cmd = '+';
14345901Sbostic 		set->bits = who & oparg;
14445901Sbostic 		break;
14545901Sbostic 	case 'u':
14645901Sbostic 	case 'g':
14745901Sbostic 	case 'o':
14845901Sbostic 		set->cmd = op;
14945901Sbostic 		if (who) {
15045901Sbostic 			set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
15145901Sbostic 				    ((who & S_IRGRP) ? CMD2_GBITS : 0) |
15245901Sbostic 				    ((who & S_IROTH) ? CMD2_OBITS : 0);
15345901Sbostic 			set->bits = ~0;
15445901Sbostic 		} else {
15545901Sbostic 			set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
15645901Sbostic 			set->bits = mask;
15745901Sbostic 		}
15845901Sbostic 
15945901Sbostic 		if (oparg == '+')
16045901Sbostic 			set->cmd2 |= CMD2_SET;
16145901Sbostic 		else if (oparg == '-')
16245901Sbostic 			set->cmd2 |= CMD2_CLR;
16345901Sbostic 		else if (oparg == '=')
16445901Sbostic 			set->cmd2 |= CMD2_SET|CMD2_CLR;
16545901Sbostic 		break;
16645901Sbostic 	}
16745901Sbostic 	return(set+1);
16845901Sbostic }
16945901Sbostic 
17045901Sbostic #define	ADDCMD(a, b, c, d) \
17145901Sbostic 	if (set >= endset) { \
17245901Sbostic 		register struct bitcmd *newset; \
17345901Sbostic 		setlen += SET_LEN_INCR; \
17445901Sbostic 		newset = realloc(saveset, sizeof(struct bitcmd) * setlen); \
17545901Sbostic 		if (!saveset) \
17645901Sbostic 			return(NULL); \
17745901Sbostic 		set = newset + (set - saveset); \
17845901Sbostic 		saveset = newset; \
17945901Sbostic 		endset = newset + (setlen - 2); \
18045901Sbostic 	} \
18145901Sbostic 	set = addcmd(set, (a), (b), (c), (d))
18245901Sbostic 
18346576Sbostic void *
18439978Sbostic setmode(p)
18539978Sbostic 	register char *p;
18639978Sbostic {
18739978Sbostic 	register int perm, who;
18839978Sbostic 	register char op;
18945901Sbostic 	mode_t mask;
19045901Sbostic 	struct bitcmd *set, *saveset, *endset;
19145901Sbostic 	int permXbits, setlen;
19246576Sbostic 	static int compress_mode();
19339978Sbostic 
19439978Sbostic 	/*
19545901Sbostic 	 * Get a copy of the mask for the permissions that are mask relative.
19645901Sbostic 	 * Flip the bits, we want what's not set.
19739978Sbostic 	 */
19839978Sbostic 	(void)umask(mask = umask(0));
19939978Sbostic 	mask = ~mask;
20039978Sbostic 
20145901Sbostic 	setlen = SET_LEN + 2;
20245901Sbostic 
20345901Sbostic 	set = (struct bitcmd *)malloc((u_int)(sizeof(struct bitcmd) * setlen));
20445901Sbostic 	if (!set)
20541579Sbostic 		return(NULL);
20645901Sbostic 	saveset = set;
20745901Sbostic 	endset = set + (setlen - 2);
20841579Sbostic 
20939978Sbostic 	/*
21045901Sbostic 	 * If an absolute number, get it and return; disallow non-octal digits
21145901Sbostic 	 * or illegal bits.
21239978Sbostic 	 */
21339978Sbostic 	if (isdigit(*p)) {
21445901Sbostic 		perm = (mode_t)strtol(p, (char **)0, 8);
21545901Sbostic 		if (perm & ~(STANDARD_BITS|S_ISTXT)) {
21645901Sbostic 			free(saveset);
21745901Sbostic 			return(NULL);
21845901Sbostic 		}
21939978Sbostic 		while (*++p)
22045901Sbostic 			if (*p < '0' || *p > '7') {
22145901Sbostic 				free(saveset);
22241579Sbostic 				return(NULL);
22345901Sbostic 			}
22445901Sbostic 		ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
22546576Sbostic 		return((void *)saveset);
22639978Sbostic 	}
22739978Sbostic 
22845901Sbostic 	if (!*p) {
22945901Sbostic 		free(saveset);
23041579Sbostic 		return(NULL);
23145901Sbostic 	}
23239978Sbostic 	/*
23345901Sbostic 	 * Build list of structures to set/clear/copy bits as described by
23445901Sbostic 	 * each clause of the symbolic mode.
23539978Sbostic 	 */
23639978Sbostic 	for (;;) {
23745901Sbostic 		/* First, find out which bits might be modified. */
23845901Sbostic 		for (who = 0;; ++p) {
23939978Sbostic 			switch (*p) {
24039978Sbostic 			case 'a':
24139978Sbostic 				who |= STANDARD_BITS;
24239978Sbostic 				break;
24339978Sbostic 			case 'u':
24439978Sbostic 				who |= S_ISUID|S_IRWXU;
24539978Sbostic 				break;
24639978Sbostic 			case 'g':
24739978Sbostic 				who |= S_ISGID|S_IRWXG;
24839978Sbostic 				break;
24939978Sbostic 			case 'o':
25039978Sbostic 				who |= S_IRWXO;
25139978Sbostic 				break;
25239978Sbostic 			default:
25339978Sbostic 				goto getop;
25439978Sbostic 			}
25545901Sbostic 		}
25645901Sbostic 	getop:
25739978Sbostic 
25845901Sbostic 		if ((op = *p++) != '+' && op != '-' && op != '=') {
25945901Sbostic 			free(saveset);
26041579Sbostic 			return(NULL);
26145901Sbostic 		}
26239978Sbostic 
26339978Sbostic 		who &= ~S_ISTXT;
26445901Sbostic 		for (perm = 0, permXbits = 0;; ++p) {
26539978Sbostic 			switch (*p) {
26639978Sbostic 			case 'r':
26739978Sbostic 				perm |= S_IRUSR|S_IRGRP|S_IROTH;
26839978Sbostic 				break;
26939978Sbostic 			case 's':
27045901Sbostic 				/* If only "other" bits ignore set-id. */
27139978Sbostic 				if (who & ~S_IRWXO)
27239978Sbostic 					perm |= S_ISUID|S_ISGID;
27339978Sbostic 				break;
27439978Sbostic 			case 't':
27545901Sbostic 				/* If only "other" bits ignore sticky. */
27639978Sbostic 				if (who & ~S_IRWXO) {
27739978Sbostic 					who |= S_ISTXT;
27839978Sbostic 					perm |= S_ISTXT;
27939978Sbostic 				}
28039978Sbostic 				break;
28139978Sbostic 			case 'w':
28239978Sbostic 				perm |= S_IWUSR|S_IWGRP|S_IWOTH;
28339978Sbostic 				break;
28439978Sbostic 			case 'X':
28539978Sbostic 				permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
28639978Sbostic 				break;
28739978Sbostic 			case 'x':
28839978Sbostic 				perm |= S_IXUSR|S_IXGRP|S_IXOTH;
28939978Sbostic 				break;
29045901Sbostic 			case 'u':
29145901Sbostic 			case 'g':
29245901Sbostic 			case 'o':
29345901Sbostic 				/*
29445901Sbostic 				 * When ever we hit 'u', 'g', or 'o', we have
29545901Sbostic 				 * to flush out any partial mode that we have,
29645901Sbostic 				 * and then do the copying of the mode bits.
29745901Sbostic 				 */
29845901Sbostic 				if (perm) {
29945901Sbostic 					ADDCMD(op, who, perm, mask);
30045901Sbostic 					perm = 0;
30145901Sbostic 				}
30245901Sbostic 				if (op == '+' && permXbits) {
30345901Sbostic 					ADDCMD('X', who, permXbits, mask);
30445901Sbostic 					permXbits = 0;
30545901Sbostic 				}
30645901Sbostic 				ADDCMD(*p, who, op, mask);
30745901Sbostic 				break;
30845901Sbostic 
30939978Sbostic 			default:
31045901Sbostic 				/*
31145901Sbostic 				 * Add any permissions that we haven't already
31245901Sbostic 				 * done.
31345901Sbostic 				 */
31445901Sbostic 				if (perm) {
31545901Sbostic 					ADDCMD(op, who, perm, mask);
31645901Sbostic 					perm = 0;
31745901Sbostic 				}
31845901Sbostic 				if (permXbits) {
31945901Sbostic 					ADDCMD('X', who, permXbits, mask);
32045901Sbostic 					permXbits = 0;
32145901Sbostic 				}
32239978Sbostic 				goto apply;
32339978Sbostic 			}
32439978Sbostic 		}
32539978Sbostic 
32645901Sbostic apply:		if (!*p)
32739978Sbostic 			break;
32839978Sbostic 		if (*p != ',')
32939978Sbostic 			goto getop;
33039978Sbostic 		++p;
33139978Sbostic 	}
33245901Sbostic 	set->cmd = 0;
33345901Sbostic #ifdef SETMODE_DEBUG
33445901Sbostic 	(void)printf("Before compress_mode()\n");
33545901Sbostic 	dumpmode(saveset);
33645901Sbostic #endif
33745901Sbostic 	compress_mode(saveset);
33845901Sbostic #ifdef SETMODE_DEBUG
33945901Sbostic 	(void)printf("After compress_mode()\n");
34045901Sbostic 	dumpmode(saveset);
34145901Sbostic #endif
34246576Sbostic 	return((void *)saveset);
34339978Sbostic }
34445901Sbostic 
34545901Sbostic #ifdef SETMODE_DEBUG
34645901Sbostic dumpmode(set)
34745901Sbostic 	register struct bitcmd *set;
34845901Sbostic {
34945901Sbostic 	for (; set->cmd; ++set)
35045901Sbostic 		(void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
35145901Sbostic 		    set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
35245901Sbostic 		    set->cmd2 & CMD2_CLR ? " CLR" : "",
35345901Sbostic 		    set->cmd2 & CMD2_SET ? " SET" : "",
35445901Sbostic 		    set->cmd2 & CMD2_UBITS ? " UBITS" : "",
35545901Sbostic 		    set->cmd2 & CMD2_GBITS ? " GBITS" : "",
35645901Sbostic 		    set->cmd2 & CMD2_OBITS ? " OBITS" : "");
35745901Sbostic }
35845901Sbostic #endif
35945901Sbostic 
36045901Sbostic /*
36145901Sbostic  * Given an array of bitcmd structures, compress by compacting consecutive
36245901Sbostic  * '+', '-' and 'X' commands into at most 3 commands, one of each.  The 'u',
36345901Sbostic  * 'g' and 'o' commands continue to be separate.  They could probably be
36445901Sbostic  * compacted, but it's not worth the effort.
36545901Sbostic  */
36645901Sbostic static
36745901Sbostic compress_mode(set)
36845901Sbostic 	register struct bitcmd *set;
36945901Sbostic {
37045901Sbostic 	register struct bitcmd *nset;
37145901Sbostic 	register int setbits, clrbits, Xbits, op;
37245901Sbostic 
37345901Sbostic 	for (nset = set;;) {
37445901Sbostic 		/* Copy over any 'u', 'g' and 'o' commands. */
37545901Sbostic 		while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
37645901Sbostic 			*set++ = *nset++;
37745901Sbostic 			if (!op)
37845901Sbostic 				return;
37945901Sbostic 		}
38045901Sbostic 
38145901Sbostic 		for (setbits = clrbits = Xbits = 0;; nset++) {
38245901Sbostic 			if ((op = nset->cmd) == '-') {
38345901Sbostic 				clrbits |= nset->bits;
38445901Sbostic 				setbits &= ~nset->bits;
38545901Sbostic 				Xbits &= ~nset->bits;
38645901Sbostic 			} else if (op == '+') {
38745901Sbostic 				setbits |= nset->bits;
38845901Sbostic 				clrbits &= ~nset->bits;
38945901Sbostic 				Xbits &= ~nset->bits;
39045901Sbostic 			} else if (op == 'X')
39145901Sbostic 				Xbits |= nset->bits & ~setbits;
39245901Sbostic 			else
39345901Sbostic 				break;
39445901Sbostic 		}
39545901Sbostic 		if (clrbits) {
39645901Sbostic 			set->cmd = '-';
39745901Sbostic 			set->cmd2 = 0;
39845901Sbostic 			set->bits = clrbits;
39945901Sbostic 			set++;
40045901Sbostic 		}
40145901Sbostic 		if (setbits) {
40245901Sbostic 			set->cmd = '+';
40345901Sbostic 			set->cmd2 = 0;
40445901Sbostic 			set->bits = setbits;
40545901Sbostic 			set++;
40645901Sbostic 		}
40745901Sbostic 		if (Xbits) {
40845901Sbostic 			set->cmd = 'X';
40945901Sbostic 			set->cmd2 = 0;
41045901Sbostic 			set->bits = Xbits;
41145901Sbostic 			set++;
41245901Sbostic 		}
41345901Sbostic 	}
41445901Sbostic }
415