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