139978Sbostic /* 239978Sbostic * Copyright (c) 1989 The Regents of the University of California. 339978Sbostic * All rights reserved. 439978Sbostic * 542626Sbostic * %sccs.include.redist.c% 639978Sbostic */ 739978Sbostic 839978Sbostic #if defined(LIBC_SCCS) && !defined(lint) 9*49896Sbostic static char sccsid[] = "@(#)setmode.c 5.6 (Berkeley) 05/27/91"; 1039978Sbostic #endif /* LIBC_SCCS and not lint */ 1139978Sbostic 1245901Sbostic #include <sys/param.h> 1339978Sbostic #include <sys/stat.h> 1445901Sbostic #include <sys/errno.h> 1545901Sbostic #ifdef SETMODE_DEBUG 1641579Sbostic #include <stdio.h> 1745901Sbostic #endif 1845901Sbostic #include <stdlib.h> 19*49896Sbostic #include <ctype.h> 2039978Sbostic 2145901Sbostic #define SET_LEN 6 /* initial # of bitcmd struct to malloc */ 2245901Sbostic #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ 2339978Sbostic 2445901Sbostic struct bitcmd { 2545901Sbostic char cmd; 2645901Sbostic char cmd2; 2745901Sbostic mode_t bits; 2845901Sbostic }; 2945901Sbostic 3045901Sbostic #define CMD2_CLR 0x01 3145901Sbostic #define CMD2_SET 0x02 3245901Sbostic #define CMD2_GBITS 0x04 3345901Sbostic #define CMD2_OBITS 0x08 3445901Sbostic #define CMD2_UBITS 0x10 3545901Sbostic 3645901Sbostic /* 3745901Sbostic * Given the old mode and an array of bitcmd structures, apply the operations 3845901Sbostic * described in the bitcmd structures to the old mode, and return the new mode. 3945901Sbostic * Note that there is no '=' command; a strict assignment is just a '-' (clear 4045901Sbostic * bits) followed by a '+' (set bits). 4145901Sbostic */ 4239978Sbostic mode_t 4346576Sbostic getmode(bbox, omode) 4446576Sbostic void *bbox; 4545901Sbostic mode_t omode; 4639978Sbostic { 4746576Sbostic register struct bitcmd *set; 4845901Sbostic register mode_t newmode, value; 4939978Sbostic 5046576Sbostic set = (struct bitcmd *)bbox; 5145901Sbostic newmode = omode; 5245901Sbostic for (value = 0;; set++) 5345901Sbostic switch(set->cmd) { 5445901Sbostic /* 5545901Sbostic * When copying the user, group or other bits around, we "know" 5645901Sbostic * where the bit are in the mode so that we can do shifts to 5745901Sbostic * copy them around. If we don't use shifts, it gets real 5845901Sbostic * grundgy with lots of single bit checks and bit sets. 5945901Sbostic */ 6045901Sbostic case 'u': 6145901Sbostic value = (newmode & S_IRWXU) >> 6; 6245901Sbostic goto common; 6345901Sbostic 6445901Sbostic case 'g': 6545901Sbostic value = (newmode & S_IRWXG) >> 3; 6645901Sbostic goto common; 6745901Sbostic 6845901Sbostic case 'o': 6945901Sbostic value = newmode & S_IRWXO; 7045901Sbostic common: 7145901Sbostic if (set->cmd2 & CMD2_CLR) { 7245901Sbostic if (set->cmd2 & CMD2_UBITS) 7345901Sbostic newmode &= ~(S_IRWXU & set->bits); 7445901Sbostic if (set->cmd2 & CMD2_GBITS) 7545901Sbostic newmode &= ~(S_IRWXG & set->bits); 7645901Sbostic if (set->cmd2 & CMD2_OBITS) 7745901Sbostic newmode &= ~(S_IRWXO & set->bits); 7845901Sbostic } 7945901Sbostic if (set->cmd2 & CMD2_SET) { 8045901Sbostic if (set->cmd2 & CMD2_UBITS) 8145901Sbostic newmode |= (value<<6) & set->bits; 8245901Sbostic if (set->cmd2 & CMD2_GBITS) 8345901Sbostic newmode |= (value<<3) & set->bits; 8445901Sbostic if (set->cmd2 & CMD2_OBITS) 8545901Sbostic newmode |= value & set->bits; 8645901Sbostic } 8745901Sbostic break; 8845901Sbostic 8945901Sbostic case '+': 9045901Sbostic newmode |= set->bits; 9145901Sbostic break; 9245901Sbostic 9345901Sbostic case '-': 9445901Sbostic newmode &= ~set->bits; 9545901Sbostic break; 9645901Sbostic 9745901Sbostic case 'X': 9845901Sbostic if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) 9945901Sbostic newmode |= set->bits; 10045901Sbostic break; 10145901Sbostic 10245901Sbostic case '\0': 10345901Sbostic default: 10445901Sbostic #ifdef SETMODE_DEBUG 10545901Sbostic (void)printf("getmode(, %04o) -> %04o\n", 10645901Sbostic omode, newmode); 10745901Sbostic #endif 10845901Sbostic return(newmode); 10945901Sbostic } 11039978Sbostic } 11139978Sbostic 11239978Sbostic #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 11339978Sbostic 11445901Sbostic static struct bitcmd * 11545901Sbostic addcmd(set, op, who, oparg, mask) 11645901Sbostic struct bitcmd *set; 11745901Sbostic register int oparg, who; 11845901Sbostic register int op; 11945901Sbostic mode_t mask; 12045901Sbostic { 12145901Sbostic switch (op) { 12245901Sbostic case '+': 12345901Sbostic case 'X': 12445901Sbostic set->cmd = op; 12545901Sbostic set->bits = (who ? who : mask) & oparg; 12645901Sbostic break; 12745901Sbostic 12845901Sbostic case '-': 12945901Sbostic set->cmd = '-'; 13045901Sbostic set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg; 13145901Sbostic break; 13245901Sbostic 13345901Sbostic case '=': 13445901Sbostic set->cmd = '-'; 13545901Sbostic if (!who) { 13645901Sbostic set->bits = STANDARD_BITS; 13745901Sbostic who = mask; 13845901Sbostic } else 13945901Sbostic set->bits = who; 14045901Sbostic set++; 14145901Sbostic 14245901Sbostic set->cmd = '+'; 14345901Sbostic set->bits = who & oparg; 14445901Sbostic break; 14545901Sbostic case 'u': 14645901Sbostic case 'g': 14745901Sbostic case 'o': 14845901Sbostic set->cmd = op; 14945901Sbostic if (who) { 15045901Sbostic set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | 15145901Sbostic ((who & S_IRGRP) ? CMD2_GBITS : 0) | 15245901Sbostic ((who & S_IROTH) ? CMD2_OBITS : 0); 15345901Sbostic set->bits = ~0; 15445901Sbostic } else { 15545901Sbostic set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; 15645901Sbostic set->bits = mask; 15745901Sbostic } 15845901Sbostic 15945901Sbostic if (oparg == '+') 16045901Sbostic set->cmd2 |= CMD2_SET; 16145901Sbostic else if (oparg == '-') 16245901Sbostic set->cmd2 |= CMD2_CLR; 16345901Sbostic else if (oparg == '=') 16445901Sbostic set->cmd2 |= CMD2_SET|CMD2_CLR; 16545901Sbostic break; 16645901Sbostic } 16745901Sbostic return(set+1); 16845901Sbostic } 16945901Sbostic 17045901Sbostic #define ADDCMD(a, b, c, d) \ 17145901Sbostic if (set >= endset) { \ 17245901Sbostic register struct bitcmd *newset; \ 17345901Sbostic setlen += SET_LEN_INCR; \ 17445901Sbostic newset = realloc(saveset, sizeof(struct bitcmd) * setlen); \ 17545901Sbostic if (!saveset) \ 17645901Sbostic return(NULL); \ 17745901Sbostic set = newset + (set - saveset); \ 17845901Sbostic saveset = newset; \ 17945901Sbostic endset = newset + (setlen - 2); \ 18045901Sbostic } \ 18145901Sbostic set = addcmd(set, (a), (b), (c), (d)) 18245901Sbostic 18346576Sbostic void * 18439978Sbostic setmode(p) 18539978Sbostic register char *p; 18639978Sbostic { 18739978Sbostic register int perm, who; 18839978Sbostic register char op; 18945901Sbostic mode_t mask; 19045901Sbostic struct bitcmd *set, *saveset, *endset; 19145901Sbostic int permXbits, setlen; 19246576Sbostic static int compress_mode(); 19339978Sbostic 19439978Sbostic /* 19545901Sbostic * Get a copy of the mask for the permissions that are mask relative. 19645901Sbostic * Flip the bits, we want what's not set. 19739978Sbostic */ 19839978Sbostic (void)umask(mask = umask(0)); 19939978Sbostic mask = ~mask; 20039978Sbostic 20145901Sbostic setlen = SET_LEN + 2; 20245901Sbostic 20345901Sbostic set = (struct bitcmd *)malloc((u_int)(sizeof(struct bitcmd) * setlen)); 20445901Sbostic if (!set) 20541579Sbostic return(NULL); 20645901Sbostic saveset = set; 20745901Sbostic endset = set + (setlen - 2); 20841579Sbostic 20939978Sbostic /* 21045901Sbostic * If an absolute number, get it and return; disallow non-octal digits 21145901Sbostic * or illegal bits. 21239978Sbostic */ 21339978Sbostic if (isdigit(*p)) { 21445901Sbostic perm = (mode_t)strtol(p, (char **)0, 8); 21545901Sbostic if (perm & ~(STANDARD_BITS|S_ISTXT)) { 21645901Sbostic free(saveset); 21745901Sbostic return(NULL); 21845901Sbostic } 21939978Sbostic while (*++p) 22045901Sbostic if (*p < '0' || *p > '7') { 22145901Sbostic free(saveset); 22241579Sbostic return(NULL); 22345901Sbostic } 22445901Sbostic ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); 22546576Sbostic return((void *)saveset); 22639978Sbostic } 22739978Sbostic 22845901Sbostic if (!*p) { 22945901Sbostic free(saveset); 23041579Sbostic return(NULL); 23145901Sbostic } 23239978Sbostic /* 23345901Sbostic * Build list of structures to set/clear/copy bits as described by 23445901Sbostic * each clause of the symbolic mode. 23539978Sbostic */ 23639978Sbostic for (;;) { 23745901Sbostic /* First, find out which bits might be modified. */ 23845901Sbostic for (who = 0;; ++p) { 23939978Sbostic switch (*p) { 24039978Sbostic case 'a': 24139978Sbostic who |= STANDARD_BITS; 24239978Sbostic break; 24339978Sbostic case 'u': 24439978Sbostic who |= S_ISUID|S_IRWXU; 24539978Sbostic break; 24639978Sbostic case 'g': 24739978Sbostic who |= S_ISGID|S_IRWXG; 24839978Sbostic break; 24939978Sbostic case 'o': 25039978Sbostic who |= S_IRWXO; 25139978Sbostic break; 25239978Sbostic default: 25339978Sbostic goto getop; 25439978Sbostic } 25545901Sbostic } 25645901Sbostic getop: 25739978Sbostic 25845901Sbostic if ((op = *p++) != '+' && op != '-' && op != '=') { 25945901Sbostic free(saveset); 26041579Sbostic return(NULL); 26145901Sbostic } 26239978Sbostic 26339978Sbostic who &= ~S_ISTXT; 26445901Sbostic for (perm = 0, permXbits = 0;; ++p) { 26539978Sbostic switch (*p) { 26639978Sbostic case 'r': 26739978Sbostic perm |= S_IRUSR|S_IRGRP|S_IROTH; 26839978Sbostic break; 26939978Sbostic case 's': 27045901Sbostic /* If only "other" bits ignore set-id. */ 27139978Sbostic if (who & ~S_IRWXO) 27239978Sbostic perm |= S_ISUID|S_ISGID; 27339978Sbostic break; 27439978Sbostic case 't': 27545901Sbostic /* If only "other" bits ignore sticky. */ 27639978Sbostic if (who & ~S_IRWXO) { 27739978Sbostic who |= S_ISTXT; 27839978Sbostic perm |= S_ISTXT; 27939978Sbostic } 28039978Sbostic break; 28139978Sbostic case 'w': 28239978Sbostic perm |= S_IWUSR|S_IWGRP|S_IWOTH; 28339978Sbostic break; 28439978Sbostic case 'X': 28539978Sbostic permXbits = S_IXUSR|S_IXGRP|S_IXOTH; 28639978Sbostic break; 28739978Sbostic case 'x': 28839978Sbostic perm |= S_IXUSR|S_IXGRP|S_IXOTH; 28939978Sbostic break; 29045901Sbostic case 'u': 29145901Sbostic case 'g': 29245901Sbostic case 'o': 29345901Sbostic /* 29445901Sbostic * When ever we hit 'u', 'g', or 'o', we have 29545901Sbostic * to flush out any partial mode that we have, 29645901Sbostic * and then do the copying of the mode bits. 29745901Sbostic */ 29845901Sbostic if (perm) { 29945901Sbostic ADDCMD(op, who, perm, mask); 30045901Sbostic perm = 0; 30145901Sbostic } 30245901Sbostic if (op == '+' && permXbits) { 30345901Sbostic ADDCMD('X', who, permXbits, mask); 30445901Sbostic permXbits = 0; 30545901Sbostic } 30645901Sbostic ADDCMD(*p, who, op, mask); 30745901Sbostic break; 30845901Sbostic 30939978Sbostic default: 31045901Sbostic /* 31145901Sbostic * Add any permissions that we haven't already 31245901Sbostic * done. 31345901Sbostic */ 31445901Sbostic if (perm) { 31545901Sbostic ADDCMD(op, who, perm, mask); 31645901Sbostic perm = 0; 31745901Sbostic } 31845901Sbostic if (permXbits) { 31945901Sbostic ADDCMD('X', who, permXbits, mask); 32045901Sbostic permXbits = 0; 32145901Sbostic } 32239978Sbostic goto apply; 32339978Sbostic } 32439978Sbostic } 32539978Sbostic 32645901Sbostic apply: if (!*p) 32739978Sbostic break; 32839978Sbostic if (*p != ',') 32939978Sbostic goto getop; 33039978Sbostic ++p; 33139978Sbostic } 33245901Sbostic set->cmd = 0; 33345901Sbostic #ifdef SETMODE_DEBUG 33445901Sbostic (void)printf("Before compress_mode()\n"); 33545901Sbostic dumpmode(saveset); 33645901Sbostic #endif 33745901Sbostic compress_mode(saveset); 33845901Sbostic #ifdef SETMODE_DEBUG 33945901Sbostic (void)printf("After compress_mode()\n"); 34045901Sbostic dumpmode(saveset); 34145901Sbostic #endif 34246576Sbostic return((void *)saveset); 34339978Sbostic } 34445901Sbostic 34545901Sbostic #ifdef SETMODE_DEBUG 34645901Sbostic dumpmode(set) 34745901Sbostic register struct bitcmd *set; 34845901Sbostic { 34945901Sbostic for (; set->cmd; ++set) 35045901Sbostic (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", 35145901Sbostic set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", 35245901Sbostic set->cmd2 & CMD2_CLR ? " CLR" : "", 35345901Sbostic set->cmd2 & CMD2_SET ? " SET" : "", 35445901Sbostic set->cmd2 & CMD2_UBITS ? " UBITS" : "", 35545901Sbostic set->cmd2 & CMD2_GBITS ? " GBITS" : "", 35645901Sbostic set->cmd2 & CMD2_OBITS ? " OBITS" : ""); 35745901Sbostic } 35845901Sbostic #endif 35945901Sbostic 36045901Sbostic /* 36145901Sbostic * Given an array of bitcmd structures, compress by compacting consecutive 36245901Sbostic * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', 36345901Sbostic * 'g' and 'o' commands continue to be separate. They could probably be 36445901Sbostic * compacted, but it's not worth the effort. 36545901Sbostic */ 36645901Sbostic static 36745901Sbostic compress_mode(set) 36845901Sbostic register struct bitcmd *set; 36945901Sbostic { 37045901Sbostic register struct bitcmd *nset; 37145901Sbostic register int setbits, clrbits, Xbits, op; 37245901Sbostic 37345901Sbostic for (nset = set;;) { 37445901Sbostic /* Copy over any 'u', 'g' and 'o' commands. */ 37545901Sbostic while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { 37645901Sbostic *set++ = *nset++; 37745901Sbostic if (!op) 37845901Sbostic return; 37945901Sbostic } 38045901Sbostic 38145901Sbostic for (setbits = clrbits = Xbits = 0;; nset++) { 38245901Sbostic if ((op = nset->cmd) == '-') { 38345901Sbostic clrbits |= nset->bits; 38445901Sbostic setbits &= ~nset->bits; 38545901Sbostic Xbits &= ~nset->bits; 38645901Sbostic } else if (op == '+') { 38745901Sbostic setbits |= nset->bits; 38845901Sbostic clrbits &= ~nset->bits; 38945901Sbostic Xbits &= ~nset->bits; 39045901Sbostic } else if (op == 'X') 39145901Sbostic Xbits |= nset->bits & ~setbits; 39245901Sbostic else 39345901Sbostic break; 39445901Sbostic } 39545901Sbostic if (clrbits) { 39645901Sbostic set->cmd = '-'; 39745901Sbostic set->cmd2 = 0; 39845901Sbostic set->bits = clrbits; 39945901Sbostic set++; 40045901Sbostic } 40145901Sbostic if (setbits) { 40245901Sbostic set->cmd = '+'; 40345901Sbostic set->cmd2 = 0; 40445901Sbostic set->bits = setbits; 40545901Sbostic set++; 40645901Sbostic } 40745901Sbostic if (Xbits) { 40845901Sbostic set->cmd = 'X'; 40945901Sbostic set->cmd2 = 0; 41045901Sbostic set->bits = Xbits; 41145901Sbostic set++; 41245901Sbostic } 41345901Sbostic } 41445901Sbostic } 415