139978Sbostic /*
2*66446Sbostic * Copyright (c) 1989, 1993, 1994
361111Sbostic * The Regents of the University of California. 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*66446Sbostic static char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 03/25/94";
1339978Sbostic #endif /* LIBC_SCCS and not lint */
1439978Sbostic
1552791Sbostic #include <sys/types.h>
1639978Sbostic #include <sys/stat.h>
17*66446Sbostic
18*66446Sbostic #include <ctype.h>
19*66446Sbostic #include <errno.h>
2053131Sbostic #include <signal.h>
2152970Sbostic #include <stddef.h>
22*66446Sbostic #include <stdlib.h>
23*66446Sbostic
2445901Sbostic #ifdef SETMODE_DEBUG
2541579Sbostic #include <stdio.h>
2645901Sbostic #endif
2739978Sbostic
2845901Sbostic #define SET_LEN 6 /* initial # of bitcmd struct to malloc */
2945901Sbostic #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
3039978Sbostic
3152791Sbostic typedef struct bitcmd {
3245901Sbostic char cmd;
3345901Sbostic char cmd2;
3445901Sbostic mode_t bits;
3552791Sbostic } BITCMD;
3645901Sbostic
3745901Sbostic #define CMD2_CLR 0x01
3845901Sbostic #define CMD2_SET 0x02
3945901Sbostic #define CMD2_GBITS 0x04
4045901Sbostic #define CMD2_OBITS 0x08
4145901Sbostic #define CMD2_UBITS 0x10
4245901Sbostic
4352791Sbostic static BITCMD *addcmd __P((BITCMD *, int, int, int, u_int));
4452791Sbostic static int compress_mode __P((BITCMD *));
4552791Sbostic #ifdef SETMODE_DEBUG
4652791Sbostic static void dumpmode __P((BITCMD *));
4752791Sbostic #endif
4852791Sbostic
4945901Sbostic /*
5045901Sbostic * Given the old mode and an array of bitcmd structures, apply the operations
5145901Sbostic * described in the bitcmd structures to the old mode, and return the new mode.
5245901Sbostic * Note that there is no '=' command; a strict assignment is just a '-' (clear
5345901Sbostic * bits) followed by a '+' (set bits).
5445901Sbostic */
5539978Sbostic mode_t
getmode(bbox,omode)5646576Sbostic getmode(bbox, omode)
5746576Sbostic void *bbox;
5845901Sbostic mode_t omode;
5939978Sbostic {
6052791Sbostic register BITCMD *set;
61*66446Sbostic register mode_t clrval, newmode, value;
6239978Sbostic
6352791Sbostic set = (BITCMD *)bbox;
6445901Sbostic newmode = omode;
6545901Sbostic for (value = 0;; set++)
6645901Sbostic switch(set->cmd) {
6745901Sbostic /*
6845901Sbostic * When copying the user, group or other bits around, we "know"
6952791Sbostic * where the bits are in the mode so that we can do shifts to
7045901Sbostic * copy them around. If we don't use shifts, it gets real
7145901Sbostic * grundgy with lots of single bit checks and bit sets.
7245901Sbostic */
7345901Sbostic case 'u':
7445901Sbostic value = (newmode & S_IRWXU) >> 6;
7545901Sbostic goto common;
7645901Sbostic
7745901Sbostic case 'g':
7845901Sbostic value = (newmode & S_IRWXG) >> 3;
7945901Sbostic goto common;
8045901Sbostic
8145901Sbostic case 'o':
8245901Sbostic value = newmode & S_IRWXO;
8352791Sbostic common: if (set->cmd2 & CMD2_CLR) {
84*66446Sbostic clrval =
85*66446Sbostic (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
8645901Sbostic if (set->cmd2 & CMD2_UBITS)
87*66446Sbostic newmode &= ~((clrval<<6) & set->bits);
8845901Sbostic if (set->cmd2 & CMD2_GBITS)
89*66446Sbostic newmode &= ~((clrval<<3) & set->bits);
9045901Sbostic if (set->cmd2 & CMD2_OBITS)
91*66446Sbostic newmode &= ~(clrval & set->bits);
9245901Sbostic }
9345901Sbostic if (set->cmd2 & CMD2_SET) {
9445901Sbostic if (set->cmd2 & CMD2_UBITS)
9545901Sbostic newmode |= (value<<6) & set->bits;
9645901Sbostic if (set->cmd2 & CMD2_GBITS)
9745901Sbostic newmode |= (value<<3) & set->bits;
9845901Sbostic if (set->cmd2 & CMD2_OBITS)
9945901Sbostic newmode |= value & set->bits;
10045901Sbostic }
10145901Sbostic break;
10245901Sbostic
10345901Sbostic case '+':
10445901Sbostic newmode |= set->bits;
10545901Sbostic break;
10645901Sbostic
10745901Sbostic case '-':
10845901Sbostic newmode &= ~set->bits;
10945901Sbostic break;
11045901Sbostic
11145901Sbostic case 'X':
11245901Sbostic if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
11345901Sbostic newmode |= set->bits;
11445901Sbostic break;
11545901Sbostic
11645901Sbostic case '\0':
11745901Sbostic default:
11845901Sbostic #ifdef SETMODE_DEBUG
11952791Sbostic (void)printf("getmode:%04o -> %04o\n", omode, newmode);
12045901Sbostic #endif
12152791Sbostic return (newmode);
12245901Sbostic }
12339978Sbostic }
12439978Sbostic
125*66446Sbostic #define ADDCMD(a, b, c, d) \
126*66446Sbostic if (set >= endset) { \
127*66446Sbostic register BITCMD *newset; \
128*66446Sbostic setlen += SET_LEN_INCR; \
129*66446Sbostic newset = realloc(saveset, sizeof(BITCMD) * setlen); \
130*66446Sbostic if (!saveset) \
131*66446Sbostic return (NULL); \
132*66446Sbostic set = newset + (set - saveset); \
133*66446Sbostic saveset = newset; \
134*66446Sbostic endset = newset + (setlen - 2); \
135*66446Sbostic } \
13645901Sbostic set = addcmd(set, (a), (b), (c), (d))
13745901Sbostic
13852791Sbostic #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
13952791Sbostic
14046576Sbostic void *
setmode(p)14139978Sbostic setmode(p)
14239978Sbostic register char *p;
14339978Sbostic {
14439978Sbostic register int perm, who;
14539978Sbostic register char op;
14652791Sbostic BITCMD *set, *saveset, *endset;
14753131Sbostic sigset_t sigset, sigoset;
14845901Sbostic mode_t mask;
149*66446Sbostic int equalopdone, permXbits, setlen;
15039978Sbostic
15152791Sbostic if (!*p)
15252791Sbostic return (NULL);
15352791Sbostic
15439978Sbostic /*
15545901Sbostic * Get a copy of the mask for the permissions that are mask relative.
15653131Sbostic * Flip the bits, we want what's not set. Since it's possible that
15753131Sbostic * the caller is opening files inside a signal handler, protect them
15853131Sbostic * as best we can.
15939978Sbostic */
16053131Sbostic sigfillset(&sigset);
16153131Sbostic (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);
16239978Sbostic (void)umask(mask = umask(0));
16339978Sbostic mask = ~mask;
16453131Sbostic (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
16539978Sbostic
16645901Sbostic setlen = SET_LEN + 2;
16745901Sbostic
16852791Sbostic if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
16952791Sbostic return (NULL);
17045901Sbostic saveset = set;
17145901Sbostic endset = set + (setlen - 2);
17241579Sbostic
17339978Sbostic /*
17445901Sbostic * If an absolute number, get it and return; disallow non-octal digits
17545901Sbostic * or illegal bits.
17639978Sbostic */
17739978Sbostic if (isdigit(*p)) {
178*66446Sbostic perm = (mode_t)strtol(p, NULL, 8);
17945901Sbostic if (perm & ~(STANDARD_BITS|S_ISTXT)) {
18045901Sbostic free(saveset);
18152791Sbostic return (NULL);
18245901Sbostic }
18339978Sbostic while (*++p)
18445901Sbostic if (*p < '0' || *p > '7') {
18545901Sbostic free(saveset);
18652791Sbostic return (NULL);
18745901Sbostic }
18845901Sbostic ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
18952791Sbostic return (saveset);
19039978Sbostic }
19139978Sbostic
19239978Sbostic /*
19345901Sbostic * Build list of structures to set/clear/copy bits as described by
19445901Sbostic * each clause of the symbolic mode.
19539978Sbostic */
19639978Sbostic for (;;) {
19745901Sbostic /* First, find out which bits might be modified. */
19845901Sbostic for (who = 0;; ++p) {
19939978Sbostic switch (*p) {
20039978Sbostic case 'a':
20139978Sbostic who |= STANDARD_BITS;
20239978Sbostic break;
20339978Sbostic case 'u':
20439978Sbostic who |= S_ISUID|S_IRWXU;
20539978Sbostic break;
20639978Sbostic case 'g':
20739978Sbostic who |= S_ISGID|S_IRWXG;
20839978Sbostic break;
20939978Sbostic case 'o':
21039978Sbostic who |= S_IRWXO;
21139978Sbostic break;
21239978Sbostic default:
21339978Sbostic goto getop;
21439978Sbostic }
21545901Sbostic }
21639978Sbostic
21752791Sbostic getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
21845901Sbostic free(saveset);
21952791Sbostic return (NULL);
22045901Sbostic }
221*66446Sbostic if (op == '=')
222*66446Sbostic equalopdone = 0;
22339978Sbostic
22439978Sbostic who &= ~S_ISTXT;
22545901Sbostic for (perm = 0, permXbits = 0;; ++p) {
22639978Sbostic switch (*p) {
22739978Sbostic case 'r':
22839978Sbostic perm |= S_IRUSR|S_IRGRP|S_IROTH;
22939978Sbostic break;
23039978Sbostic case 's':
23145901Sbostic /* If only "other" bits ignore set-id. */
23239978Sbostic if (who & ~S_IRWXO)
23339978Sbostic perm |= S_ISUID|S_ISGID;
23439978Sbostic break;
23539978Sbostic case 't':
23645901Sbostic /* If only "other" bits ignore sticky. */
23739978Sbostic if (who & ~S_IRWXO) {
23839978Sbostic who |= S_ISTXT;
23939978Sbostic perm |= S_ISTXT;
24039978Sbostic }
24139978Sbostic break;
24239978Sbostic case 'w':
24339978Sbostic perm |= S_IWUSR|S_IWGRP|S_IWOTH;
24439978Sbostic break;
24539978Sbostic case 'X':
24639978Sbostic permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
24739978Sbostic break;
24839978Sbostic case 'x':
24939978Sbostic perm |= S_IXUSR|S_IXGRP|S_IXOTH;
25039978Sbostic break;
25145901Sbostic case 'u':
25245901Sbostic case 'g':
25345901Sbostic case 'o':
25445901Sbostic /*
25545901Sbostic * When ever we hit 'u', 'g', or 'o', we have
25645901Sbostic * to flush out any partial mode that we have,
25745901Sbostic * and then do the copying of the mode bits.
25845901Sbostic */
259*66446Sbostic if (perm) {
26045901Sbostic ADDCMD(op, who, perm, mask);
26145901Sbostic perm = 0;
26245901Sbostic }
263*66446Sbostic if (op == '=')
264*66446Sbostic equalopdone = 1;
26545901Sbostic if (op == '+' && permXbits) {
26645901Sbostic ADDCMD('X', who, permXbits, mask);
26745901Sbostic permXbits = 0;
26845901Sbostic }
26945901Sbostic ADDCMD(*p, who, op, mask);
27045901Sbostic break;
27145901Sbostic
27239978Sbostic default:
27345901Sbostic /*
27445901Sbostic * Add any permissions that we haven't already
27545901Sbostic * done.
27645901Sbostic */
277*66446Sbostic if (perm || (op == '=' && !equalopdone)) {
278*66446Sbostic if (op == '=')
279*66446Sbostic equalopdone = 1;
28045901Sbostic ADDCMD(op, who, perm, mask);
28145901Sbostic perm = 0;
28245901Sbostic }
28345901Sbostic if (permXbits) {
28445901Sbostic ADDCMD('X', who, permXbits, mask);
28545901Sbostic permXbits = 0;
28645901Sbostic }
28739978Sbostic goto apply;
28839978Sbostic }
28939978Sbostic }
29039978Sbostic
29145901Sbostic apply: if (!*p)
29239978Sbostic break;
29339978Sbostic if (*p != ',')
29439978Sbostic goto getop;
29539978Sbostic ++p;
29639978Sbostic }
29745901Sbostic set->cmd = 0;
29845901Sbostic #ifdef SETMODE_DEBUG
29945901Sbostic (void)printf("Before compress_mode()\n");
30045901Sbostic dumpmode(saveset);
30145901Sbostic #endif
30245901Sbostic compress_mode(saveset);
30345901Sbostic #ifdef SETMODE_DEBUG
30445901Sbostic (void)printf("After compress_mode()\n");
30545901Sbostic dumpmode(saveset);
30645901Sbostic #endif
30752791Sbostic return (saveset);
30839978Sbostic }
30945901Sbostic
31052791Sbostic static BITCMD *
addcmd(set,op,who,oparg,mask)31152791Sbostic addcmd(set, op, who, oparg, mask)
31252791Sbostic BITCMD *set;
31352791Sbostic register int oparg, who;
31452791Sbostic register int op;
31552791Sbostic u_int mask;
31652791Sbostic {
31752791Sbostic switch (op) {
318*66446Sbostic case '=':
319*66446Sbostic set->cmd = '-';
320*66446Sbostic set->bits = who ? who : STANDARD_BITS;
321*66446Sbostic set++;
322*66446Sbostic
323*66446Sbostic op = '+';
324*66446Sbostic /* FALLTHROUGH */
32552791Sbostic case '+':
326*66446Sbostic case '-':
32752791Sbostic case 'X':
32852791Sbostic set->cmd = op;
32952791Sbostic set->bits = (who ? who : mask) & oparg;
33052791Sbostic break;
33152791Sbostic
33252791Sbostic case 'u':
33352791Sbostic case 'g':
33452791Sbostic case 'o':
33552791Sbostic set->cmd = op;
33652791Sbostic if (who) {
33752791Sbostic set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
33852791Sbostic ((who & S_IRGRP) ? CMD2_GBITS : 0) |
33952791Sbostic ((who & S_IROTH) ? CMD2_OBITS : 0);
34052791Sbostic set->bits = ~0;
34152791Sbostic } else {
34252791Sbostic set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
34352791Sbostic set->bits = mask;
34452791Sbostic }
34552791Sbostic
34652791Sbostic if (oparg == '+')
34752791Sbostic set->cmd2 |= CMD2_SET;
34852791Sbostic else if (oparg == '-')
34952791Sbostic set->cmd2 |= CMD2_CLR;
35052791Sbostic else if (oparg == '=')
35152791Sbostic set->cmd2 |= CMD2_SET|CMD2_CLR;
35252791Sbostic break;
35352791Sbostic }
35452791Sbostic return (set + 1);
35552791Sbostic }
35652791Sbostic
35745901Sbostic #ifdef SETMODE_DEBUG
35852791Sbostic static void
dumpmode(set)35945901Sbostic dumpmode(set)
36052791Sbostic register BITCMD *set;
36145901Sbostic {
36245901Sbostic for (; set->cmd; ++set)
36345901Sbostic (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
36445901Sbostic set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
36545901Sbostic set->cmd2 & CMD2_CLR ? " CLR" : "",
36645901Sbostic set->cmd2 & CMD2_SET ? " SET" : "",
36745901Sbostic set->cmd2 & CMD2_UBITS ? " UBITS" : "",
36845901Sbostic set->cmd2 & CMD2_GBITS ? " GBITS" : "",
36945901Sbostic set->cmd2 & CMD2_OBITS ? " OBITS" : "");
37045901Sbostic }
37145901Sbostic #endif
37245901Sbostic
37345901Sbostic /*
37445901Sbostic * Given an array of bitcmd structures, compress by compacting consecutive
37545901Sbostic * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
37645901Sbostic * 'g' and 'o' commands continue to be separate. They could probably be
37745901Sbostic * compacted, but it's not worth the effort.
37845901Sbostic */
37952791Sbostic static int
compress_mode(set)38045901Sbostic compress_mode(set)
38152791Sbostic register BITCMD *set;
38245901Sbostic {
38352791Sbostic register BITCMD *nset;
38445901Sbostic register int setbits, clrbits, Xbits, op;
38545901Sbostic
38645901Sbostic for (nset = set;;) {
38745901Sbostic /* Copy over any 'u', 'g' and 'o' commands. */
38845901Sbostic while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
38945901Sbostic *set++ = *nset++;
39045901Sbostic if (!op)
39145901Sbostic return;
39245901Sbostic }
39345901Sbostic
39445901Sbostic for (setbits = clrbits = Xbits = 0;; nset++) {
39545901Sbostic if ((op = nset->cmd) == '-') {
39645901Sbostic clrbits |= nset->bits;
39745901Sbostic setbits &= ~nset->bits;
39845901Sbostic Xbits &= ~nset->bits;
39945901Sbostic } else if (op == '+') {
40045901Sbostic setbits |= nset->bits;
40145901Sbostic clrbits &= ~nset->bits;
40245901Sbostic Xbits &= ~nset->bits;
40345901Sbostic } else if (op == 'X')
40445901Sbostic Xbits |= nset->bits & ~setbits;
40545901Sbostic else
40645901Sbostic break;
40745901Sbostic }
40845901Sbostic if (clrbits) {
40945901Sbostic set->cmd = '-';
41045901Sbostic set->cmd2 = 0;
41145901Sbostic set->bits = clrbits;
41245901Sbostic set++;
41345901Sbostic }
41445901Sbostic if (setbits) {
41545901Sbostic set->cmd = '+';
41645901Sbostic set->cmd2 = 0;
41745901Sbostic set->bits = setbits;
41845901Sbostic set++;
41945901Sbostic }
42045901Sbostic if (Xbits) {
42145901Sbostic set->cmd = 'X';
42245901Sbostic set->cmd2 = 0;
42345901Sbostic set->bits = Xbits;
42445901Sbostic set++;
42545901Sbostic }
42645901Sbostic }
42745901Sbostic }
428