xref: /csrg-svn/lib/libc/gen/setmode.c (revision 53131)
139978Sbostic /*
239978Sbostic  * Copyright (c) 1989 The Regents of the University of California.
339978Sbostic  * All rights reserved.
439978Sbostic  *
552801Sbostic  * This code is derived from software contributed to Berkeley by
652801Sbostic  * Dave Borman at Cray Research, Inc.
752801Sbostic  *
842626Sbostic  * %sccs.include.redist.c%
939978Sbostic  */
1039978Sbostic 
1139978Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*53131Sbostic static char sccsid[] = "@(#)setmode.c	5.11 (Berkeley) 04/07/92";
1339978Sbostic #endif /* LIBC_SCCS and not lint */
1439978Sbostic 
1552791Sbostic #include <sys/types.h>
1639978Sbostic #include <sys/stat.h>
17*53131Sbostic #include <signal.h>
1852791Sbostic #include <errno.h>
1952970Sbostic #include <stddef.h>
2045901Sbostic #ifdef SETMODE_DEBUG
2141579Sbostic #include <stdio.h>
2245901Sbostic #endif
2345901Sbostic #include <stdlib.h>
2449896Sbostic #include <ctype.h>
2539978Sbostic 
2645901Sbostic #define	SET_LEN	6		/* initial # of bitcmd struct to malloc */
2745901Sbostic #define	SET_LEN_INCR 4		/* # of bitcmd structs to add as needed */
2839978Sbostic 
2952791Sbostic typedef struct bitcmd {
3045901Sbostic 	char	cmd;
3145901Sbostic 	char	cmd2;
3245901Sbostic 	mode_t	bits;
3352791Sbostic } BITCMD;
3445901Sbostic 
3545901Sbostic #define	CMD2_CLR	0x01
3645901Sbostic #define	CMD2_SET	0x02
3745901Sbostic #define	CMD2_GBITS	0x04
3845901Sbostic #define	CMD2_OBITS	0x08
3945901Sbostic #define	CMD2_UBITS	0x10
4045901Sbostic 
4152791Sbostic static BITCMD	*addcmd __P((BITCMD *, int, int, int, u_int));
4252791Sbostic static int	 compress_mode __P((BITCMD *));
4352791Sbostic #ifdef SETMODE_DEBUG
4452791Sbostic static void	 dumpmode __P((BITCMD *));
4552791Sbostic #endif
4652791Sbostic 
4745901Sbostic /*
4845901Sbostic  * Given the old mode and an array of bitcmd structures, apply the operations
4945901Sbostic  * described in the bitcmd structures to the old mode, and return the new mode.
5045901Sbostic  * Note that there is no '=' command; a strict assignment is just a '-' (clear
5145901Sbostic  * bits) followed by a '+' (set bits).
5245901Sbostic  */
5339978Sbostic mode_t
5446576Sbostic getmode(bbox, omode)
5546576Sbostic 	void *bbox;
5645901Sbostic 	mode_t omode;
5739978Sbostic {
5852791Sbostic 	register BITCMD *set;
5945901Sbostic 	register mode_t newmode, value;
6039978Sbostic 
6152791Sbostic 	set = (BITCMD *)bbox;
6245901Sbostic 	newmode = omode;
6345901Sbostic 	for (value = 0;; set++)
6445901Sbostic 		switch(set->cmd) {
6545901Sbostic 		/*
6645901Sbostic 		 * When copying the user, group or other bits around, we "know"
6752791Sbostic 		 * where the bits are in the mode so that we can do shifts to
6845901Sbostic 		 * copy them around.  If we don't use shifts, it gets real
6945901Sbostic 		 * grundgy with lots of single bit checks and bit sets.
7045901Sbostic 		 */
7145901Sbostic 		case 'u':
7245901Sbostic 			value = (newmode & S_IRWXU) >> 6;
7345901Sbostic 			goto common;
7445901Sbostic 
7545901Sbostic 		case 'g':
7645901Sbostic 			value = (newmode & S_IRWXG) >> 3;
7745901Sbostic 			goto common;
7845901Sbostic 
7945901Sbostic 		case 'o':
8045901Sbostic 			value = newmode & S_IRWXO;
8152791Sbostic common:			if (set->cmd2 & CMD2_CLR) {
8245901Sbostic 				if (set->cmd2 & CMD2_UBITS)
8345901Sbostic 					newmode &= ~(S_IRWXU & set->bits);
8445901Sbostic 				if (set->cmd2 & CMD2_GBITS)
8545901Sbostic 					newmode &= ~(S_IRWXG & set->bits);
8645901Sbostic 				if (set->cmd2 & CMD2_OBITS)
8745901Sbostic 					newmode &= ~(S_IRWXO & set->bits);
8845901Sbostic 			}
8945901Sbostic 			if (set->cmd2 & CMD2_SET) {
9045901Sbostic 				if (set->cmd2 & CMD2_UBITS)
9145901Sbostic 					newmode |= (value<<6) & set->bits;
9245901Sbostic 				if (set->cmd2 & CMD2_GBITS)
9345901Sbostic 					newmode |= (value<<3) & set->bits;
9445901Sbostic 				if (set->cmd2 & CMD2_OBITS)
9545901Sbostic 					newmode |= value & set->bits;
9645901Sbostic 			}
9745901Sbostic 			break;
9845901Sbostic 
9945901Sbostic 		case '+':
10045901Sbostic 			newmode |= set->bits;
10145901Sbostic 			break;
10245901Sbostic 
10345901Sbostic 		case '-':
10445901Sbostic 			newmode &= ~set->bits;
10545901Sbostic 			break;
10645901Sbostic 
10745901Sbostic 		case 'X':
10845901Sbostic 			if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
10945901Sbostic 				newmode |= set->bits;
11045901Sbostic 			break;
11145901Sbostic 
11245901Sbostic 		case '\0':
11345901Sbostic 		default:
11445901Sbostic #ifdef SETMODE_DEBUG
11552791Sbostic 			(void)printf("getmode:%04o -> %04o\n", omode, newmode);
11645901Sbostic #endif
11752791Sbostic 			return (newmode);
11845901Sbostic 		}
11939978Sbostic }
12039978Sbostic 
12145901Sbostic #define	ADDCMD(a, b, c, d) \
12245901Sbostic 	if (set >= endset) { \
12352791Sbostic 		register BITCMD *newset; \
12445901Sbostic 		setlen += SET_LEN_INCR; \
12552791Sbostic 		newset = realloc(saveset, sizeof(BITCMD) * setlen); \
12645901Sbostic 		if (!saveset) \
12752791Sbostic 			return (NULL); \
12845901Sbostic 		set = newset + (set - saveset); \
12945901Sbostic 		saveset = newset; \
13045901Sbostic 		endset = newset + (setlen - 2); \
13145901Sbostic 	} \
13245901Sbostic 	set = addcmd(set, (a), (b), (c), (d))
13345901Sbostic 
13452791Sbostic #define	STANDARD_BITS	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
13552791Sbostic 
13646576Sbostic void *
13739978Sbostic setmode(p)
13839978Sbostic 	register char *p;
13939978Sbostic {
14039978Sbostic 	register int perm, who;
14139978Sbostic 	register char op;
14252791Sbostic 	BITCMD *set, *saveset, *endset;
143*53131Sbostic 	sigset_t sigset, sigoset;
14445901Sbostic 	mode_t mask;
14545901Sbostic 	int permXbits, setlen;
14639978Sbostic 
14752791Sbostic 	if (!*p)
14852791Sbostic 		return (NULL);
14952791Sbostic 
15039978Sbostic 	/*
15145901Sbostic 	 * Get a copy of the mask for the permissions that are mask relative.
152*53131Sbostic 	 * Flip the bits, we want what's not set.  Since it's possible that
153*53131Sbostic 	 * the caller is opening files inside a signal handler, protect them
154*53131Sbostic 	 * as best we can.
15539978Sbostic 	 */
156*53131Sbostic 	sigfillset(&sigset);
157*53131Sbostic         (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);
15839978Sbostic 	(void)umask(mask = umask(0));
15939978Sbostic 	mask = ~mask;
160*53131Sbostic         (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
16139978Sbostic 
16245901Sbostic 	setlen = SET_LEN + 2;
16345901Sbostic 
16452791Sbostic 	if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
16552791Sbostic 		return (NULL);
16645901Sbostic 	saveset = set;
16745901Sbostic 	endset = set + (setlen - 2);
16841579Sbostic 
16939978Sbostic 	/*
17045901Sbostic 	 * If an absolute number, get it and return; disallow non-octal digits
17145901Sbostic 	 * or illegal bits.
17239978Sbostic 	 */
17339978Sbostic 	if (isdigit(*p)) {
17445901Sbostic 		perm = (mode_t)strtol(p, (char **)0, 8);
17545901Sbostic 		if (perm & ~(STANDARD_BITS|S_ISTXT)) {
17645901Sbostic 			free(saveset);
17752791Sbostic 			return (NULL);
17845901Sbostic 		}
17939978Sbostic 		while (*++p)
18045901Sbostic 			if (*p < '0' || *p > '7') {
18145901Sbostic 				free(saveset);
18252791Sbostic 				return (NULL);
18345901Sbostic 			}
18445901Sbostic 		ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
18552791Sbostic 		return (saveset);
18639978Sbostic 	}
18739978Sbostic 
18839978Sbostic 	/*
18945901Sbostic 	 * Build list of structures to set/clear/copy bits as described by
19045901Sbostic 	 * each clause of the symbolic mode.
19139978Sbostic 	 */
19239978Sbostic 	for (;;) {
19345901Sbostic 		/* First, find out which bits might be modified. */
19445901Sbostic 		for (who = 0;; ++p) {
19539978Sbostic 			switch (*p) {
19639978Sbostic 			case 'a':
19739978Sbostic 				who |= STANDARD_BITS;
19839978Sbostic 				break;
19939978Sbostic 			case 'u':
20039978Sbostic 				who |= S_ISUID|S_IRWXU;
20139978Sbostic 				break;
20239978Sbostic 			case 'g':
20339978Sbostic 				who |= S_ISGID|S_IRWXG;
20439978Sbostic 				break;
20539978Sbostic 			case 'o':
20639978Sbostic 				who |= S_IRWXO;
20739978Sbostic 				break;
20839978Sbostic 			default:
20939978Sbostic 				goto getop;
21039978Sbostic 			}
21145901Sbostic 		}
21239978Sbostic 
21352791Sbostic getop:		if ((op = *p++) != '+' && op != '-' && op != '=') {
21445901Sbostic 			free(saveset);
21552791Sbostic 			return (NULL);
21645901Sbostic 		}
21739978Sbostic 
21839978Sbostic 		who &= ~S_ISTXT;
21945901Sbostic 		for (perm = 0, permXbits = 0;; ++p) {
22039978Sbostic 			switch (*p) {
22139978Sbostic 			case 'r':
22239978Sbostic 				perm |= S_IRUSR|S_IRGRP|S_IROTH;
22339978Sbostic 				break;
22439978Sbostic 			case 's':
22545901Sbostic 				/* If only "other" bits ignore set-id. */
22639978Sbostic 				if (who & ~S_IRWXO)
22739978Sbostic 					perm |= S_ISUID|S_ISGID;
22839978Sbostic 				break;
22939978Sbostic 			case 't':
23045901Sbostic 				/* If only "other" bits ignore sticky. */
23139978Sbostic 				if (who & ~S_IRWXO) {
23239978Sbostic 					who |= S_ISTXT;
23339978Sbostic 					perm |= S_ISTXT;
23439978Sbostic 				}
23539978Sbostic 				break;
23639978Sbostic 			case 'w':
23739978Sbostic 				perm |= S_IWUSR|S_IWGRP|S_IWOTH;
23839978Sbostic 				break;
23939978Sbostic 			case 'X':
24039978Sbostic 				permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
24139978Sbostic 				break;
24239978Sbostic 			case 'x':
24339978Sbostic 				perm |= S_IXUSR|S_IXGRP|S_IXOTH;
24439978Sbostic 				break;
24545901Sbostic 			case 'u':
24645901Sbostic 			case 'g':
24745901Sbostic 			case 'o':
24845901Sbostic 				/*
24945901Sbostic 				 * When ever we hit 'u', 'g', or 'o', we have
25045901Sbostic 				 * to flush out any partial mode that we have,
25145901Sbostic 				 * and then do the copying of the mode bits.
25245901Sbostic 				 */
25352801Sbostic 				if (perm || op == '=') {
25445901Sbostic 					ADDCMD(op, who, perm, mask);
25545901Sbostic 					perm = 0;
25645901Sbostic 				}
25745901Sbostic 				if (op == '+' && permXbits) {
25845901Sbostic 					ADDCMD('X', who, permXbits, mask);
25945901Sbostic 					permXbits = 0;
26045901Sbostic 				}
26145901Sbostic 				ADDCMD(*p, who, op, mask);
26245901Sbostic 				break;
26345901Sbostic 
26439978Sbostic 			default:
26545901Sbostic 				/*
26645901Sbostic 				 * Add any permissions that we haven't already
26745901Sbostic 				 * done.
26845901Sbostic 				 */
26952801Sbostic 				if (perm || op == '=') {
27045901Sbostic 					ADDCMD(op, who, perm, mask);
27145901Sbostic 					perm = 0;
27245901Sbostic 				}
27345901Sbostic 				if (permXbits) {
27445901Sbostic 					ADDCMD('X', who, permXbits, mask);
27545901Sbostic 					permXbits = 0;
27645901Sbostic 				}
27739978Sbostic 				goto apply;
27839978Sbostic 			}
27939978Sbostic 		}
28039978Sbostic 
28145901Sbostic apply:		if (!*p)
28239978Sbostic 			break;
28339978Sbostic 		if (*p != ',')
28439978Sbostic 			goto getop;
28539978Sbostic 		++p;
28639978Sbostic 	}
28745901Sbostic 	set->cmd = 0;
28845901Sbostic #ifdef SETMODE_DEBUG
28945901Sbostic 	(void)printf("Before compress_mode()\n");
29045901Sbostic 	dumpmode(saveset);
29145901Sbostic #endif
29245901Sbostic 	compress_mode(saveset);
29345901Sbostic #ifdef SETMODE_DEBUG
29445901Sbostic 	(void)printf("After compress_mode()\n");
29545901Sbostic 	dumpmode(saveset);
29645901Sbostic #endif
29752791Sbostic 	return (saveset);
29839978Sbostic }
29945901Sbostic 
30052791Sbostic static BITCMD *
30152791Sbostic addcmd(set, op, who, oparg, mask)
30252791Sbostic 	BITCMD *set;
30352791Sbostic 	register int oparg, who;
30452791Sbostic 	register int op;
30552791Sbostic 	u_int mask;
30652791Sbostic {
30752791Sbostic 	switch (op) {
30852791Sbostic 	case '+':
30952791Sbostic 	case 'X':
31052791Sbostic 		set->cmd = op;
31152791Sbostic 		set->bits = (who ? who : mask) & oparg;
31252791Sbostic 		break;
31352791Sbostic 
31452791Sbostic 	case '-':
31552791Sbostic 		set->cmd = '-';
31652791Sbostic 		set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg;
31752791Sbostic 		break;
31852791Sbostic 
31952791Sbostic 	case '=':
32052791Sbostic 		set->cmd = '-';
32152791Sbostic 		if (!who) {
32252791Sbostic 			set->bits = STANDARD_BITS;
32352791Sbostic 			who = mask;
32452791Sbostic 		} else
32552791Sbostic 			set->bits = who;
32652791Sbostic 		set++;
32752791Sbostic 
32852791Sbostic 		set->cmd = '+';
32952791Sbostic 		set->bits = who & oparg;
33052791Sbostic 		break;
33152791Sbostic 	case 'u':
33252791Sbostic 	case 'g':
33352791Sbostic 	case 'o':
33452791Sbostic 		set->cmd = op;
33552791Sbostic 		if (who) {
33652791Sbostic 			set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
33752791Sbostic 				    ((who & S_IRGRP) ? CMD2_GBITS : 0) |
33852791Sbostic 				    ((who & S_IROTH) ? CMD2_OBITS : 0);
33952791Sbostic 			set->bits = ~0;
34052791Sbostic 		} else {
34152791Sbostic 			set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
34252791Sbostic 			set->bits = mask;
34352791Sbostic 		}
34452791Sbostic 
34552791Sbostic 		if (oparg == '+')
34652791Sbostic 			set->cmd2 |= CMD2_SET;
34752791Sbostic 		else if (oparg == '-')
34852791Sbostic 			set->cmd2 |= CMD2_CLR;
34952791Sbostic 		else if (oparg == '=')
35052791Sbostic 			set->cmd2 |= CMD2_SET|CMD2_CLR;
35152791Sbostic 		break;
35252791Sbostic 	}
35352791Sbostic 	return (set + 1);
35452791Sbostic }
35552791Sbostic 
35645901Sbostic #ifdef SETMODE_DEBUG
35752791Sbostic static void
35845901Sbostic dumpmode(set)
35952791Sbostic 	register BITCMD *set;
36045901Sbostic {
36145901Sbostic 	for (; set->cmd; ++set)
36245901Sbostic 		(void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
36345901Sbostic 		    set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
36445901Sbostic 		    set->cmd2 & CMD2_CLR ? " CLR" : "",
36545901Sbostic 		    set->cmd2 & CMD2_SET ? " SET" : "",
36645901Sbostic 		    set->cmd2 & CMD2_UBITS ? " UBITS" : "",
36745901Sbostic 		    set->cmd2 & CMD2_GBITS ? " GBITS" : "",
36845901Sbostic 		    set->cmd2 & CMD2_OBITS ? " OBITS" : "");
36945901Sbostic }
37045901Sbostic #endif
37145901Sbostic 
37245901Sbostic /*
37345901Sbostic  * Given an array of bitcmd structures, compress by compacting consecutive
37445901Sbostic  * '+', '-' and 'X' commands into at most 3 commands, one of each.  The 'u',
37545901Sbostic  * 'g' and 'o' commands continue to be separate.  They could probably be
37645901Sbostic  * compacted, but it's not worth the effort.
37745901Sbostic  */
37852791Sbostic static int
37945901Sbostic compress_mode(set)
38052791Sbostic 	register BITCMD *set;
38145901Sbostic {
38252791Sbostic 	register BITCMD *nset;
38345901Sbostic 	register int setbits, clrbits, Xbits, op;
38445901Sbostic 
38545901Sbostic 	for (nset = set;;) {
38645901Sbostic 		/* Copy over any 'u', 'g' and 'o' commands. */
38745901Sbostic 		while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
38845901Sbostic 			*set++ = *nset++;
38945901Sbostic 			if (!op)
39045901Sbostic 				return;
39145901Sbostic 		}
39245901Sbostic 
39345901Sbostic 		for (setbits = clrbits = Xbits = 0;; nset++) {
39445901Sbostic 			if ((op = nset->cmd) == '-') {
39545901Sbostic 				clrbits |= nset->bits;
39645901Sbostic 				setbits &= ~nset->bits;
39745901Sbostic 				Xbits &= ~nset->bits;
39845901Sbostic 			} else if (op == '+') {
39945901Sbostic 				setbits |= nset->bits;
40045901Sbostic 				clrbits &= ~nset->bits;
40145901Sbostic 				Xbits &= ~nset->bits;
40245901Sbostic 			} else if (op == 'X')
40345901Sbostic 				Xbits |= nset->bits & ~setbits;
40445901Sbostic 			else
40545901Sbostic 				break;
40645901Sbostic 		}
40745901Sbostic 		if (clrbits) {
40845901Sbostic 			set->cmd = '-';
40945901Sbostic 			set->cmd2 = 0;
41045901Sbostic 			set->bits = clrbits;
41145901Sbostic 			set++;
41245901Sbostic 		}
41345901Sbostic 		if (setbits) {
41445901Sbostic 			set->cmd = '+';
41545901Sbostic 			set->cmd2 = 0;
41645901Sbostic 			set->bits = setbits;
41745901Sbostic 			set++;
41845901Sbostic 		}
41945901Sbostic 		if (Xbits) {
42045901Sbostic 			set->cmd = 'X';
42145901Sbostic 			set->cmd2 = 0;
42245901Sbostic 			set->bits = Xbits;
42345901Sbostic 			set++;
42445901Sbostic 		}
42545901Sbostic 	}
42645901Sbostic }
427