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*52970Sbostic static char sccsid[] = "@(#)setmode.c 5.10 (Berkeley) 03/16/92"; 1339978Sbostic #endif /* LIBC_SCCS and not lint */ 1439978Sbostic 1552791Sbostic #include <sys/types.h> 1639978Sbostic #include <sys/stat.h> 1752791Sbostic #include <errno.h> 18*52970Sbostic #include <stddef.h> 1945901Sbostic #ifdef SETMODE_DEBUG 2041579Sbostic #include <stdio.h> 2145901Sbostic #endif 2245901Sbostic #include <stdlib.h> 2349896Sbostic #include <ctype.h> 2439978Sbostic 2545901Sbostic #define SET_LEN 6 /* initial # of bitcmd struct to malloc */ 2645901Sbostic #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ 2739978Sbostic 2852791Sbostic typedef struct bitcmd { 2945901Sbostic char cmd; 3045901Sbostic char cmd2; 3145901Sbostic mode_t bits; 3252791Sbostic } BITCMD; 3345901Sbostic 3445901Sbostic #define CMD2_CLR 0x01 3545901Sbostic #define CMD2_SET 0x02 3645901Sbostic #define CMD2_GBITS 0x04 3745901Sbostic #define CMD2_OBITS 0x08 3845901Sbostic #define CMD2_UBITS 0x10 3945901Sbostic 4052791Sbostic static BITCMD *addcmd __P((BITCMD *, int, int, int, u_int)); 4152791Sbostic static int compress_mode __P((BITCMD *)); 4252791Sbostic #ifdef SETMODE_DEBUG 4352791Sbostic static void dumpmode __P((BITCMD *)); 4452791Sbostic #endif 4552791Sbostic 4645901Sbostic /* 4745901Sbostic * Given the old mode and an array of bitcmd structures, apply the operations 4845901Sbostic * described in the bitcmd structures to the old mode, and return the new mode. 4945901Sbostic * Note that there is no '=' command; a strict assignment is just a '-' (clear 5045901Sbostic * bits) followed by a '+' (set bits). 5145901Sbostic */ 5239978Sbostic mode_t 5346576Sbostic getmode(bbox, omode) 5446576Sbostic void *bbox; 5545901Sbostic mode_t omode; 5639978Sbostic { 5752791Sbostic register BITCMD *set; 5845901Sbostic register mode_t newmode, value; 5939978Sbostic 6052791Sbostic set = (BITCMD *)bbox; 6145901Sbostic newmode = omode; 6245901Sbostic for (value = 0;; set++) 6345901Sbostic switch(set->cmd) { 6445901Sbostic /* 6545901Sbostic * When copying the user, group or other bits around, we "know" 6652791Sbostic * where the bits are in the mode so that we can do shifts to 6745901Sbostic * copy them around. If we don't use shifts, it gets real 6845901Sbostic * grundgy with lots of single bit checks and bit sets. 6945901Sbostic */ 7045901Sbostic case 'u': 7145901Sbostic value = (newmode & S_IRWXU) >> 6; 7245901Sbostic goto common; 7345901Sbostic 7445901Sbostic case 'g': 7545901Sbostic value = (newmode & S_IRWXG) >> 3; 7645901Sbostic goto common; 7745901Sbostic 7845901Sbostic case 'o': 7945901Sbostic value = newmode & S_IRWXO; 8052791Sbostic common: if (set->cmd2 & CMD2_CLR) { 8145901Sbostic if (set->cmd2 & CMD2_UBITS) 8245901Sbostic newmode &= ~(S_IRWXU & set->bits); 8345901Sbostic if (set->cmd2 & CMD2_GBITS) 8445901Sbostic newmode &= ~(S_IRWXG & set->bits); 8545901Sbostic if (set->cmd2 & CMD2_OBITS) 8645901Sbostic newmode &= ~(S_IRWXO & set->bits); 8745901Sbostic } 8845901Sbostic if (set->cmd2 & CMD2_SET) { 8945901Sbostic if (set->cmd2 & CMD2_UBITS) 9045901Sbostic newmode |= (value<<6) & set->bits; 9145901Sbostic if (set->cmd2 & CMD2_GBITS) 9245901Sbostic newmode |= (value<<3) & set->bits; 9345901Sbostic if (set->cmd2 & CMD2_OBITS) 9445901Sbostic newmode |= value & set->bits; 9545901Sbostic } 9645901Sbostic break; 9745901Sbostic 9845901Sbostic case '+': 9945901Sbostic newmode |= set->bits; 10045901Sbostic break; 10145901Sbostic 10245901Sbostic case '-': 10345901Sbostic newmode &= ~set->bits; 10445901Sbostic break; 10545901Sbostic 10645901Sbostic case 'X': 10745901Sbostic if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) 10845901Sbostic newmode |= set->bits; 10945901Sbostic break; 11045901Sbostic 11145901Sbostic case '\0': 11245901Sbostic default: 11345901Sbostic #ifdef SETMODE_DEBUG 11452791Sbostic (void)printf("getmode:%04o -> %04o\n", omode, newmode); 11545901Sbostic #endif 11652791Sbostic return (newmode); 11745901Sbostic } 11839978Sbostic } 11939978Sbostic 12045901Sbostic #define ADDCMD(a, b, c, d) \ 12145901Sbostic if (set >= endset) { \ 12252791Sbostic register BITCMD *newset; \ 12345901Sbostic setlen += SET_LEN_INCR; \ 12452791Sbostic newset = realloc(saveset, sizeof(BITCMD) * setlen); \ 12545901Sbostic if (!saveset) \ 12652791Sbostic return (NULL); \ 12745901Sbostic set = newset + (set - saveset); \ 12845901Sbostic saveset = newset; \ 12945901Sbostic endset = newset + (setlen - 2); \ 13045901Sbostic } \ 13145901Sbostic set = addcmd(set, (a), (b), (c), (d)) 13245901Sbostic 13352791Sbostic #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 13452791Sbostic 13546576Sbostic void * 13639978Sbostic setmode(p) 13739978Sbostic register char *p; 13839978Sbostic { 13939978Sbostic register int perm, who; 14039978Sbostic register char op; 14152791Sbostic BITCMD *set, *saveset, *endset; 14245901Sbostic mode_t mask; 14345901Sbostic int permXbits, setlen; 14439978Sbostic 14552791Sbostic if (!*p) 14652791Sbostic return (NULL); 14752791Sbostic 14839978Sbostic /* 14945901Sbostic * Get a copy of the mask for the permissions that are mask relative. 15045901Sbostic * Flip the bits, we want what's not set. 15139978Sbostic */ 15239978Sbostic (void)umask(mask = umask(0)); 15339978Sbostic mask = ~mask; 15439978Sbostic 15545901Sbostic setlen = SET_LEN + 2; 15645901Sbostic 15752791Sbostic if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL) 15852791Sbostic return (NULL); 15945901Sbostic saveset = set; 16045901Sbostic endset = set + (setlen - 2); 16141579Sbostic 16239978Sbostic /* 16345901Sbostic * If an absolute number, get it and return; disallow non-octal digits 16445901Sbostic * or illegal bits. 16539978Sbostic */ 16639978Sbostic if (isdigit(*p)) { 16745901Sbostic perm = (mode_t)strtol(p, (char **)0, 8); 16845901Sbostic if (perm & ~(STANDARD_BITS|S_ISTXT)) { 16945901Sbostic free(saveset); 17052791Sbostic return (NULL); 17145901Sbostic } 17239978Sbostic while (*++p) 17345901Sbostic if (*p < '0' || *p > '7') { 17445901Sbostic free(saveset); 17552791Sbostic return (NULL); 17645901Sbostic } 17745901Sbostic ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); 17852791Sbostic return (saveset); 17939978Sbostic } 18039978Sbostic 18139978Sbostic /* 18245901Sbostic * Build list of structures to set/clear/copy bits as described by 18345901Sbostic * each clause of the symbolic mode. 18439978Sbostic */ 18539978Sbostic for (;;) { 18645901Sbostic /* First, find out which bits might be modified. */ 18745901Sbostic for (who = 0;; ++p) { 18839978Sbostic switch (*p) { 18939978Sbostic case 'a': 19039978Sbostic who |= STANDARD_BITS; 19139978Sbostic break; 19239978Sbostic case 'u': 19339978Sbostic who |= S_ISUID|S_IRWXU; 19439978Sbostic break; 19539978Sbostic case 'g': 19639978Sbostic who |= S_ISGID|S_IRWXG; 19739978Sbostic break; 19839978Sbostic case 'o': 19939978Sbostic who |= S_IRWXO; 20039978Sbostic break; 20139978Sbostic default: 20239978Sbostic goto getop; 20339978Sbostic } 20445901Sbostic } 20539978Sbostic 20652791Sbostic getop: if ((op = *p++) != '+' && op != '-' && op != '=') { 20745901Sbostic free(saveset); 20852791Sbostic return (NULL); 20945901Sbostic } 21039978Sbostic 21139978Sbostic who &= ~S_ISTXT; 21245901Sbostic for (perm = 0, permXbits = 0;; ++p) { 21339978Sbostic switch (*p) { 21439978Sbostic case 'r': 21539978Sbostic perm |= S_IRUSR|S_IRGRP|S_IROTH; 21639978Sbostic break; 21739978Sbostic case 's': 21845901Sbostic /* If only "other" bits ignore set-id. */ 21939978Sbostic if (who & ~S_IRWXO) 22039978Sbostic perm |= S_ISUID|S_ISGID; 22139978Sbostic break; 22239978Sbostic case 't': 22345901Sbostic /* If only "other" bits ignore sticky. */ 22439978Sbostic if (who & ~S_IRWXO) { 22539978Sbostic who |= S_ISTXT; 22639978Sbostic perm |= S_ISTXT; 22739978Sbostic } 22839978Sbostic break; 22939978Sbostic case 'w': 23039978Sbostic perm |= S_IWUSR|S_IWGRP|S_IWOTH; 23139978Sbostic break; 23239978Sbostic case 'X': 23339978Sbostic permXbits = S_IXUSR|S_IXGRP|S_IXOTH; 23439978Sbostic break; 23539978Sbostic case 'x': 23639978Sbostic perm |= S_IXUSR|S_IXGRP|S_IXOTH; 23739978Sbostic break; 23845901Sbostic case 'u': 23945901Sbostic case 'g': 24045901Sbostic case 'o': 24145901Sbostic /* 24245901Sbostic * When ever we hit 'u', 'g', or 'o', we have 24345901Sbostic * to flush out any partial mode that we have, 24445901Sbostic * and then do the copying of the mode bits. 24545901Sbostic */ 24652801Sbostic if (perm || op == '=') { 24745901Sbostic ADDCMD(op, who, perm, mask); 24845901Sbostic perm = 0; 24945901Sbostic } 25045901Sbostic if (op == '+' && permXbits) { 25145901Sbostic ADDCMD('X', who, permXbits, mask); 25245901Sbostic permXbits = 0; 25345901Sbostic } 25445901Sbostic ADDCMD(*p, who, op, mask); 25545901Sbostic break; 25645901Sbostic 25739978Sbostic default: 25845901Sbostic /* 25945901Sbostic * Add any permissions that we haven't already 26045901Sbostic * done. 26145901Sbostic */ 26252801Sbostic if (perm || op == '=') { 26345901Sbostic ADDCMD(op, who, perm, mask); 26445901Sbostic perm = 0; 26545901Sbostic } 26645901Sbostic if (permXbits) { 26745901Sbostic ADDCMD('X', who, permXbits, mask); 26845901Sbostic permXbits = 0; 26945901Sbostic } 27039978Sbostic goto apply; 27139978Sbostic } 27239978Sbostic } 27339978Sbostic 27445901Sbostic apply: if (!*p) 27539978Sbostic break; 27639978Sbostic if (*p != ',') 27739978Sbostic goto getop; 27839978Sbostic ++p; 27939978Sbostic } 28045901Sbostic set->cmd = 0; 28145901Sbostic #ifdef SETMODE_DEBUG 28245901Sbostic (void)printf("Before compress_mode()\n"); 28345901Sbostic dumpmode(saveset); 28445901Sbostic #endif 28545901Sbostic compress_mode(saveset); 28645901Sbostic #ifdef SETMODE_DEBUG 28745901Sbostic (void)printf("After compress_mode()\n"); 28845901Sbostic dumpmode(saveset); 28945901Sbostic #endif 29052791Sbostic return (saveset); 29139978Sbostic } 29245901Sbostic 29352791Sbostic static BITCMD * 29452791Sbostic addcmd(set, op, who, oparg, mask) 29552791Sbostic BITCMD *set; 29652791Sbostic register int oparg, who; 29752791Sbostic register int op; 29852791Sbostic u_int mask; 29952791Sbostic { 30052791Sbostic switch (op) { 30152791Sbostic case '+': 30252791Sbostic case 'X': 30352791Sbostic set->cmd = op; 30452791Sbostic set->bits = (who ? who : mask) & oparg; 30552791Sbostic break; 30652791Sbostic 30752791Sbostic case '-': 30852791Sbostic set->cmd = '-'; 30952791Sbostic set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg; 31052791Sbostic break; 31152791Sbostic 31252791Sbostic case '=': 31352791Sbostic set->cmd = '-'; 31452791Sbostic if (!who) { 31552791Sbostic set->bits = STANDARD_BITS; 31652791Sbostic who = mask; 31752791Sbostic } else 31852791Sbostic set->bits = who; 31952791Sbostic set++; 32052791Sbostic 32152791Sbostic set->cmd = '+'; 32252791Sbostic set->bits = who & oparg; 32352791Sbostic break; 32452791Sbostic case 'u': 32552791Sbostic case 'g': 32652791Sbostic case 'o': 32752791Sbostic set->cmd = op; 32852791Sbostic if (who) { 32952791Sbostic set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | 33052791Sbostic ((who & S_IRGRP) ? CMD2_GBITS : 0) | 33152791Sbostic ((who & S_IROTH) ? CMD2_OBITS : 0); 33252791Sbostic set->bits = ~0; 33352791Sbostic } else { 33452791Sbostic set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; 33552791Sbostic set->bits = mask; 33652791Sbostic } 33752791Sbostic 33852791Sbostic if (oparg == '+') 33952791Sbostic set->cmd2 |= CMD2_SET; 34052791Sbostic else if (oparg == '-') 34152791Sbostic set->cmd2 |= CMD2_CLR; 34252791Sbostic else if (oparg == '=') 34352791Sbostic set->cmd2 |= CMD2_SET|CMD2_CLR; 34452791Sbostic break; 34552791Sbostic } 34652791Sbostic return (set + 1); 34752791Sbostic } 34852791Sbostic 34945901Sbostic #ifdef SETMODE_DEBUG 35052791Sbostic static void 35145901Sbostic dumpmode(set) 35252791Sbostic register BITCMD *set; 35345901Sbostic { 35445901Sbostic for (; set->cmd; ++set) 35545901Sbostic (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", 35645901Sbostic set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", 35745901Sbostic set->cmd2 & CMD2_CLR ? " CLR" : "", 35845901Sbostic set->cmd2 & CMD2_SET ? " SET" : "", 35945901Sbostic set->cmd2 & CMD2_UBITS ? " UBITS" : "", 36045901Sbostic set->cmd2 & CMD2_GBITS ? " GBITS" : "", 36145901Sbostic set->cmd2 & CMD2_OBITS ? " OBITS" : ""); 36245901Sbostic } 36345901Sbostic #endif 36445901Sbostic 36545901Sbostic /* 36645901Sbostic * Given an array of bitcmd structures, compress by compacting consecutive 36745901Sbostic * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', 36845901Sbostic * 'g' and 'o' commands continue to be separate. They could probably be 36945901Sbostic * compacted, but it's not worth the effort. 37045901Sbostic */ 37152791Sbostic static int 37245901Sbostic compress_mode(set) 37352791Sbostic register BITCMD *set; 37445901Sbostic { 37552791Sbostic register BITCMD *nset; 37645901Sbostic register int setbits, clrbits, Xbits, op; 37745901Sbostic 37845901Sbostic for (nset = set;;) { 37945901Sbostic /* Copy over any 'u', 'g' and 'o' commands. */ 38045901Sbostic while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { 38145901Sbostic *set++ = *nset++; 38245901Sbostic if (!op) 38345901Sbostic return; 38445901Sbostic } 38545901Sbostic 38645901Sbostic for (setbits = clrbits = Xbits = 0;; nset++) { 38745901Sbostic if ((op = nset->cmd) == '-') { 38845901Sbostic clrbits |= nset->bits; 38945901Sbostic setbits &= ~nset->bits; 39045901Sbostic Xbits &= ~nset->bits; 39145901Sbostic } else if (op == '+') { 39245901Sbostic setbits |= nset->bits; 39345901Sbostic clrbits &= ~nset->bits; 39445901Sbostic Xbits &= ~nset->bits; 39545901Sbostic } else if (op == 'X') 39645901Sbostic Xbits |= nset->bits & ~setbits; 39745901Sbostic else 39845901Sbostic break; 39945901Sbostic } 40045901Sbostic if (clrbits) { 40145901Sbostic set->cmd = '-'; 40245901Sbostic set->cmd2 = 0; 40345901Sbostic set->bits = clrbits; 40445901Sbostic set++; 40545901Sbostic } 40645901Sbostic if (setbits) { 40745901Sbostic set->cmd = '+'; 40845901Sbostic set->cmd2 = 0; 40945901Sbostic set->bits = setbits; 41045901Sbostic set++; 41145901Sbostic } 41245901Sbostic if (Xbits) { 41345901Sbostic set->cmd = 'X'; 41445901Sbostic set->cmd2 = 0; 41545901Sbostic set->bits = Xbits; 41645901Sbostic set++; 41745901Sbostic } 41845901Sbostic } 41945901Sbostic } 420