setmode.c revision 111273
138032Speter/*
2111826Sgshapiro * Copyright (c) 1989, 1993, 1994
364565Sgshapiro *	The Regents of the University of California.  All rights reserved.
438032Speter *
538032Speter * This code is derived from software contributed to Berkeley by
638032Speter * Dave Borman at Cray Research, Inc.
738032Speter *
838032Speter * Redistribution and use in source and binary forms, with or without
938032Speter * modification, are permitted provided that the following conditions
1038032Speter * are met:
1138032Speter * 1. Redistributions of source code must retain the above copyright
12120259Sgshapiro *    notice, this list of conditions and the following disclaimer.
1338032Speter * 2. Redistributions in binary form must reproduce the above copyright
1438032Speter *    notice, this list of conditions and the following disclaimer in the
1564565Sgshapiro *    documentation and/or other materials provided with the distribution.
1664565Sgshapiro * 3. All advertising materials mentioning features or use of this software
17141862Sgshapiro *    must display the following acknowledgement:
1864565Sgshapiro *	This product includes software developed by the University of
1964565Sgshapiro *	California, Berkeley and its contributors.
2064565Sgshapiro * 4. Neither the name of the University nor the names of its contributors
2164565Sgshapiro *    may be used to endorse or promote products derived from this software
2264565Sgshapiro *    without specific prior written permission.
2338032Speter *
2438032Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2564565Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2664565Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2764565Sgshapiro * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2864565Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2964565Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3090795Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3164565Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3238032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3338032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3438032Speter * SUCH DAMAGE.
3538032Speter */
3638032Speter
3738032Speter#if defined(LIBC_SCCS) && !defined(lint)
3838032Speterstatic char sccsid[] = "@(#)setmode.c	8.2 (Berkeley) 3/25/94";
3938032Speter#endif /* LIBC_SCCS and not lint */
4038032Speter#include <sys/cdefs.h>
4138032Speter__FBSDID("$FreeBSD: head/lib/libc/gen/setmode.c 111273 2003-02-23 00:06:35Z mikeh $");
4238032Speter
4338032Speter#include "namespace.h"
4438032Speter#include <sys/types.h>
4538032Speter#include <sys/stat.h>
4638032Speter
4738032Speter#include <ctype.h>
4838032Speter#include <signal.h>
4938032Speter#include <stddef.h>
5038032Speter#include <stdlib.h>
5138032Speter#include <unistd.h>
5238032Speter
5338032Speter#ifdef SETMODE_DEBUG
5438032Speter#include <stdio.h>
5538032Speter#endif
5638032Speter#include "un-namespace.h"
5738032Speter
5838032Speter#define	SET_LEN	6		/* initial # of bitcmd struct to malloc */
5938032Speter#define	SET_LEN_INCR 4		/* # of bitcmd structs to add as needed */
6038032Speter
6138032Spetertypedef struct bitcmd {
6238032Speter	char	cmd;
6364565Sgshapiro	char	cmd2;
6438032Speter	mode_t	bits;
6590795Sgshapiro} BITCMD;
6638032Speter
6738032Speter#define	CMD2_CLR	0x01
6838032Speter#define	CMD2_SET	0x02
6938032Speter#define	CMD2_GBITS	0x04
7038032Speter#define	CMD2_OBITS	0x08
7138032Speter#define	CMD2_UBITS	0x10
7238032Speter
7338032Speterstatic BITCMD	*addcmd(BITCMD *, int, int, int, u_int);
7438032Speterstatic void	 compress_mode(BITCMD *);
7538032Speter#ifdef SETMODE_DEBUG
7638032Speterstatic void	 dumpmode(BITCMD *);
7738032Speter#endif
7838032Speter
7938032Speter/*
8038032Speter * Given the old mode and an array of bitcmd structures, apply the operations
8138032Speter * described in the bitcmd structures to the old mode, and return the new mode.
8238032Speter * Note that there is no '=' command; a strict assignment is just a '-' (clear
8338032Speter * bits) followed by a '+' (set bits).
8438032Speter */
8538032Spetermode_t
8638032Spetergetmode(bbox, omode)
8738032Speter	const void *bbox;
8838032Speter	mode_t omode;
8938032Speter{
9038032Speter	const BITCMD *set;
9138032Speter	mode_t clrval, newmode, value;
9238032Speter
9338032Speter	set = (const BITCMD *)bbox;
9438032Speter	newmode = omode;
9538032Speter	for (value = 0;; set++)
9638032Speter		switch(set->cmd) {
9738032Speter		/*
9838032Speter		 * When copying the user, group or other bits around, we "know"
9938032Speter		 * where the bits are in the mode so that we can do shifts to
10038032Speter		 * copy them around.  If we don't use shifts, it gets real
10138032Speter		 * grundgy with lots of single bit checks and bit sets.
10238032Speter		 */
10338032Speter		case 'u':
10490795Sgshapiro			value = (newmode & S_IRWXU) >> 6;
10538032Speter			goto common;
10638032Speter
10790795Sgshapiro		case 'g':
10890795Sgshapiro			value = (newmode & S_IRWXG) >> 3;
10938032Speter			goto common;
11038032Speter
11164565Sgshapiro		case 'o':
11290795Sgshapiro			value = newmode & S_IRWXO;
11390795Sgshapirocommon:			if (set->cmd2 & CMD2_CLR) {
11438032Speter				clrval =
11538032Speter				    (set->cmd2 & CMD2_SET) ?  S_IRWXO : value;
11638032Speter				if (set->cmd2 & CMD2_UBITS)
11738032Speter					newmode &= ~((clrval<<6) & set->bits);
11890795Sgshapiro				if (set->cmd2 & CMD2_GBITS)
11938032Speter					newmode &= ~((clrval<<3) & set->bits);
12038032Speter				if (set->cmd2 & CMD2_OBITS)
12138032Speter					newmode &= ~(clrval & set->bits);
12238032Speter			}
12338032Speter			if (set->cmd2 & CMD2_SET) {
12438032Speter				if (set->cmd2 & CMD2_UBITS)
12538032Speter					newmode |= (value<<6) & set->bits;
12638032Speter				if (set->cmd2 & CMD2_GBITS)
12738032Speter					newmode |= (value<<3) & set->bits;
12838032Speter				if (set->cmd2 & CMD2_OBITS)
12938032Speter					newmode |= value & set->bits;
13038032Speter			}
13138032Speter			break;
13238032Speter
13338032Speter		case '+':
13438032Speter			newmode |= set->bits;
13538032Speter			break;
13638032Speter
13738032Speter		case '-':
13838032Speter			newmode &= ~set->bits;
13938032Speter			break;
14038032Speter
14138032Speter		case 'X':
14238032Speter			if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
14338032Speter				newmode |= set->bits;
14438032Speter			break;
14538032Speter
14690795Sgshapiro		case '\0':
14764565Sgshapiro		default:
14864565Sgshapiro#ifdef SETMODE_DEBUG
14938032Speter			(void)printf("getmode:%04o -> %04o\n", omode, newmode);
15038032Speter#endif
15138032Speter			return (newmode);
15238032Speter		}
15338032Speter}
15438032Speter
15538032Speter#define	ADDCMD(a, b, c, d)						\
15638032Speter	if (set >= endset) {						\
15738032Speter		BITCMD *newset;						\
15838032Speter		setlen += SET_LEN_INCR;					\
15938032Speter		newset = realloc(saveset, sizeof(BITCMD) * setlen);	\
16038032Speter		if (!saveset)						\
16194337Sgshapiro			return (NULL);					\
16264565Sgshapiro		set = newset + (set - saveset);				\
16364565Sgshapiro		saveset = newset;					\
16464565Sgshapiro		endset = newset + (setlen - 2);				\
16564565Sgshapiro	}								\
16638032Speter	set = addcmd(set, (a), (b), (c), (d))
16764565Sgshapiro
16838032Speter#define	STANDARD_BITS	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
16964565Sgshapiro
17064565Sgshapirovoid *
17190795Sgshapirosetmode(p)
17238032Speter	const char *p;
17338032Speter{
17438032Speter	int perm, who;
17538032Speter	char op;
17638032Speter	BITCMD *set, *saveset, *endset;
17738032Speter	sigset_t sigset, sigoset;
17838032Speter	mode_t mask;
17938032Speter	int equalopdone=0, permXbits, setlen;
18038032Speter
18190795Sgshapiro	if (!*p)
18238032Speter		return (NULL);
18338032Speter
18438032Speter	/*
18538032Speter	 * Get a copy of the mask for the permissions that are mask relative.
18638032Speter	 * Flip the bits, we want what's not set.  Since it's possible that
18738032Speter	 * the caller is opening files inside a signal handler, protect them
18890795Sgshapiro	 * as best we can.
18990795Sgshapiro	 */
19038032Speter	sigfillset(&sigset);
19138032Speter        (void)_sigprocmask(SIG_BLOCK, &sigset, &sigoset);
19238032Speter	(void)umask(mask = umask(0));
19338032Speter	mask = ~mask;
19438032Speter        (void)_sigprocmask(SIG_SETMASK, &sigoset, NULL);
19538032Speter
19664565Sgshapiro	setlen = SET_LEN + 2;
19738032Speter
19838032Speter	if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
19938032Speter		return (NULL);
20038032Speter	saveset = set;
20138032Speter	endset = set + (setlen - 2);
20238032Speter
20338032Speter	/*
20438032Speter	 * If an absolute number, get it and return; disallow non-octal digits
20538032Speter	 * or illegal bits.
20638032Speter	 */
20738032Speter	if (isdigit((unsigned char)*p)) {
20838032Speter		perm = (mode_t)strtol(p, NULL, 8);
20938032Speter		if (perm & ~(STANDARD_BITS|S_ISTXT)) {
21038032Speter			free(saveset);
21138032Speter			return (NULL);
21238032Speter		}
21338032Speter		while (*++p)
21490795Sgshapiro			if (*p < '0' || *p > '7') {
21590795Sgshapiro				free(saveset);
21690795Sgshapiro				return (NULL);
21738032Speter			}
21838032Speter		ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
21964565Sgshapiro		return (saveset);
22090795Sgshapiro	}
22190795Sgshapiro
22238032Speter	/*
22364565Sgshapiro	 * Build list of structures to set/clear/copy bits as described by
22438032Speter	 * each clause of the symbolic mode.
22538032Speter	 */
22638032Speter	for (;;) {
22738032Speter		/* First, find out which bits might be modified. */
22838032Speter		for (who = 0;; ++p) {
22938032Speter			switch (*p) {
23038032Speter			case 'a':
23138032Speter				who |= STANDARD_BITS;
23238032Speter				break;
23390795Sgshapiro			case 'u':
23438032Speter				who |= S_ISUID|S_IRWXU;
23590795Sgshapiro				break;
23638032Speter			case 'g':
23738032Speter				who |= S_ISGID|S_IRWXG;
23838032Speter				break;
23938032Speter			case 'o':
24090795Sgshapiro				who |= S_IRWXO;
24138032Speter				break;
24290795Sgshapiro			default:
24338032Speter				goto getop;
24438032Speter			}
24538032Speter		}
24638032Speter
24738032Spetergetop:		if ((op = *p++) != '+' && op != '-' && op != '=') {
24890795Sgshapiro			free(saveset);
24990795Sgshapiro			return (NULL);
25090795Sgshapiro		}
25190795Sgshapiro		if (op == '=')
25290795Sgshapiro			equalopdone = 0;
25390795Sgshapiro
25438032Speter		who &= ~S_ISTXT;
25590795Sgshapiro		for (perm = 0, permXbits = 0;; ++p) {
25690795Sgshapiro			switch (*p) {
25790795Sgshapiro			case 'r':
25890795Sgshapiro				perm |= S_IRUSR|S_IRGRP|S_IROTH;
25990795Sgshapiro				break;
26090795Sgshapiro			case 's':
26190795Sgshapiro				/* If only "other" bits ignore set-id. */
26290795Sgshapiro				if (!who || who & ~S_IRWXO)
26390795Sgshapiro					perm |= S_ISUID|S_ISGID;
26490795Sgshapiro				break;
26538032Speter			case 't':
26690795Sgshapiro				/* If only "other" bits ignore sticky. */
26738032Speter				if (!who || who & ~S_IRWXO) {
26838032Speter					who |= S_ISTXT;
26938032Speter					perm |= S_ISTXT;
27090795Sgshapiro				}
27190795Sgshapiro				break;
27238032Speter			case 'w':
27338032Speter				perm |= S_IWUSR|S_IWGRP|S_IWOTH;
27438032Speter				break;
27538032Speter			case 'X':
27638032Speter				permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
27738032Speter				break;
27838032Speter			case 'x':
27938032Speter				perm |= S_IXUSR|S_IXGRP|S_IXOTH;
28038032Speter				break;
28138032Speter			case 'u':
28238032Speter			case 'g':
28338032Speter			case 'o':
28438032Speter				/*
28538032Speter				 * When ever we hit 'u', 'g', or 'o', we have
28638032Speter				 * to flush out any partial mode that we have,
28738032Speter				 * and then do the copying of the mode bits.
28838032Speter				 */
28971348Sgshapiro				if (perm) {
29038032Speter					ADDCMD(op, who, perm, mask);
29138032Speter					perm = 0;
29271348Sgshapiro				}
29338032Speter				if (op == '=')
29490795Sgshapiro					equalopdone = 1;
29538032Speter				if (op == '+' && permXbits) {
29690795Sgshapiro					ADDCMD('X', who, permXbits, mask);
29790795Sgshapiro					permXbits = 0;
29890795Sgshapiro				}
29990795Sgshapiro				ADDCMD(*p, who, op, mask);
30090795Sgshapiro				break;
30190795Sgshapiro
30290795Sgshapiro			default:
30338032Speter				/*
30438032Speter				 * Add any permissions that we haven't already
30538032Speter				 * done.
30638032Speter				 */
30738032Speter				if (perm || (op == '=' && !equalopdone)) {
30838032Speter					if (op == '=')
30938032Speter						equalopdone = 1;
31038032Speter					ADDCMD(op, who, perm, mask);
31138032Speter					perm = 0;
31238032Speter				}
31338032Speter				if (permXbits) {
31438032Speter					ADDCMD('X', who, permXbits, mask);
31564565Sgshapiro					permXbits = 0;
31638032Speter				}
31738032Speter				goto apply;
31838032Speter			}
31938032Speter		}
32038032Speter
32171348Sgshapiroapply:		if (!*p)
32264565Sgshapiro			break;
32338032Speter		if (*p != ',')
32438032Speter			goto getop;
32538032Speter		++p;
32690795Sgshapiro	}
32790795Sgshapiro	set->cmd = 0;
32890795Sgshapiro#ifdef SETMODE_DEBUG
32990795Sgshapiro	(void)printf("Before compress_mode()\n");
33090795Sgshapiro	dumpmode(saveset);
33190795Sgshapiro#endif
33290795Sgshapiro	compress_mode(saveset);
33390795Sgshapiro#ifdef SETMODE_DEBUG
33490795Sgshapiro	(void)printf("After compress_mode()\n");
33590795Sgshapiro	dumpmode(saveset);
33690795Sgshapiro#endif
33790795Sgshapiro	return (saveset);
33890795Sgshapiro}
33964565Sgshapiro
34090795Sgshapirostatic BITCMD *
34164565Sgshapiroaddcmd(set, op, who, oparg, mask)
34264565Sgshapiro	BITCMD *set;
34364565Sgshapiro	int oparg, who;
34464565Sgshapiro	int op;
34564565Sgshapiro	u_int mask;
34664565Sgshapiro{
34738032Speter	switch (op) {
34838032Speter	case '=':
34990795Sgshapiro		set->cmd = '-';
35064565Sgshapiro		set->bits = who ? who : STANDARD_BITS;
35138032Speter		set++;
35238032Speter
35338032Speter		op = '+';
35438032Speter		/* FALLTHROUGH */
35538032Speter	case '+':
35638032Speter	case '-':
35738032Speter	case 'X':
35838032Speter		set->cmd = op;
35990795Sgshapiro		set->bits = (who ? who : mask) & oparg;
36038032Speter		break;
36138032Speter
36238032Speter	case 'u':
36338032Speter	case 'g':
36438032Speter	case 'o':
36538032Speter		set->cmd = op;
36638032Speter		if (who) {
36738032Speter			set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
36838032Speter				    ((who & S_IRGRP) ? CMD2_GBITS : 0) |
36938032Speter				    ((who & S_IROTH) ? CMD2_OBITS : 0);
37038032Speter			set->bits = (mode_t)~0;
37138032Speter		} else {
37238032Speter			set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
37338032Speter			set->bits = mask;
37438032Speter		}
37538032Speter
37638032Speter		if (oparg == '+')
37738032Speter			set->cmd2 |= CMD2_SET;
37890795Sgshapiro		else if (oparg == '-')
37938032Speter			set->cmd2 |= CMD2_CLR;
38038032Speter		else if (oparg == '=')
38138032Speter			set->cmd2 |= CMD2_SET|CMD2_CLR;
38290795Sgshapiro		break;
38390795Sgshapiro	}
38438032Speter	return (set + 1);
38538032Speter}
38638032Speter
38738032Speter#ifdef SETMODE_DEBUG
38838032Speterstatic void
38938032Speterdumpmode(set)
39038032Speter	BITCMD *set;
39138032Speter{
39294337Sgshapiro	for (; set->cmd; ++set)
39338032Speter		(void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
39438032Speter		    set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
39538032Speter		    set->cmd2 & CMD2_CLR ? " CLR" : "",
39638032Speter		    set->cmd2 & CMD2_SET ? " SET" : "",
39738032Speter		    set->cmd2 & CMD2_UBITS ? " UBITS" : "",
39838032Speter		    set->cmd2 & CMD2_GBITS ? " GBITS" : "",
39938032Speter		    set->cmd2 & CMD2_OBITS ? " OBITS" : "");
40038032Speter}
40138032Speter#endif
402141887Sgshapiro
40390795Sgshapiro/*
404141887Sgshapiro * Given an array of bitcmd structures, compress by compacting consecutive
405141887Sgshapiro * '+', '-' and 'X' commands into at most 3 commands, one of each.  The 'u',
406141887Sgshapiro * 'g' and 'o' commands continue to be separate.  They could probably be
407141887Sgshapiro * compacted, but it's not worth the effort.
408141887Sgshapiro */
409141887Sgshapirostatic void
410141887Sgshapirocompress_mode(set)
411141887Sgshapiro	BITCMD *set;
412141887Sgshapiro{
413141887Sgshapiro	BITCMD *nset;
414141887Sgshapiro	int setbits, clrbits, Xbits, op;
415141887Sgshapiro
416141887Sgshapiro	for (nset = set;;) {
417141887Sgshapiro		/* Copy over any 'u', 'g' and 'o' commands. */
418141887Sgshapiro		while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
419141887Sgshapiro			*set++ = *nset++;
420141887Sgshapiro			if (!op)
421141887Sgshapiro				return;
422141887Sgshapiro		}
423141887Sgshapiro
424141887Sgshapiro		for (setbits = clrbits = Xbits = 0;; nset++) {
425141887Sgshapiro			if ((op = nset->cmd) == '-') {
426141887Sgshapiro				clrbits |= nset->bits;
427141887Sgshapiro				setbits &= ~nset->bits;
428141887Sgshapiro				Xbits &= ~nset->bits;
429141887Sgshapiro			} else if (op == '+') {
430141887Sgshapiro				setbits |= nset->bits;
431141887Sgshapiro				clrbits &= ~nset->bits;
432141887Sgshapiro				Xbits &= ~nset->bits;
433141887Sgshapiro			} else if (op == 'X')
434141887Sgshapiro				Xbits |= nset->bits & ~setbits;
435141887Sgshapiro			else
436141887Sgshapiro				break;
437141887Sgshapiro		}
438141887Sgshapiro		if (clrbits) {
439141887Sgshapiro			set->cmd = '-';
440141887Sgshapiro			set->cmd2 = 0;
441141887Sgshapiro			set->bits = clrbits;
442141887Sgshapiro			set++;
443141887Sgshapiro		}
444141887Sgshapiro		if (setbits) {
445141887Sgshapiro			set->cmd = '+';
446141887Sgshapiro			set->cmd2 = 0;
447141887Sgshapiro			set->bits = setbits;
448141887Sgshapiro			set++;
449141887Sgshapiro		}
450141887Sgshapiro		if (Xbits) {
451141887Sgshapiro			set->cmd = 'X';
452141887Sgshapiro			set->cmd2 = 0;
453141887Sgshapiro			set->bits = Xbits;
45490795Sgshapiro			set++;
45590795Sgshapiro		}
45690795Sgshapiro	}
45790795Sgshapiro}
45890795Sgshapiro