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*45901Sbostic static char sccsid[] = "@(#)setmode.c 5.4 (Berkeley) 01/09/91"; 1039978Sbostic #endif /* LIBC_SCCS and not lint */ 1139978Sbostic 12*45901Sbostic #include <sys/param.h> 1339978Sbostic #include <sys/stat.h> 14*45901Sbostic #include <sys/errno.h> 15*45901Sbostic #ifdef SETMODE_DEBUG 1641579Sbostic #include <stdio.h> 17*45901Sbostic #endif 18*45901Sbostic #include <stdlib.h> 1939978Sbostic 20*45901Sbostic #define SET_LEN 6 /* initial # of bitcmd struct to malloc */ 21*45901Sbostic #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ 2239978Sbostic 23*45901Sbostic struct bitcmd { 24*45901Sbostic char cmd; 25*45901Sbostic char cmd2; 26*45901Sbostic mode_t bits; 27*45901Sbostic }; 28*45901Sbostic 29*45901Sbostic #define CMD2_CLR 0x01 30*45901Sbostic #define CMD2_SET 0x02 31*45901Sbostic #define CMD2_GBITS 0x04 32*45901Sbostic #define CMD2_OBITS 0x08 33*45901Sbostic #define CMD2_UBITS 0x10 34*45901Sbostic 35*45901Sbostic /* 36*45901Sbostic * Given the old mode and an array of bitcmd structures, apply the operations 37*45901Sbostic * described in the bitcmd structures to the old mode, and return the new mode. 38*45901Sbostic * Note that there is no '=' command; a strict assignment is just a '-' (clear 39*45901Sbostic * bits) followed by a '+' (set bits). 40*45901Sbostic */ 4139978Sbostic mode_t 4241579Sbostic getmode(set, omode) 43*45901Sbostic register struct bitcmd *set; 44*45901Sbostic mode_t omode; 4539978Sbostic { 46*45901Sbostic register mode_t newmode, value; 4739978Sbostic 48*45901Sbostic newmode = omode; 49*45901Sbostic for (value = 0;; set++) 50*45901Sbostic switch(set->cmd) { 51*45901Sbostic /* 52*45901Sbostic * When copying the user, group or other bits around, we "know" 53*45901Sbostic * where the bit are in the mode so that we can do shifts to 54*45901Sbostic * copy them around. If we don't use shifts, it gets real 55*45901Sbostic * grundgy with lots of single bit checks and bit sets. 56*45901Sbostic */ 57*45901Sbostic case 'u': 58*45901Sbostic value = (newmode & S_IRWXU) >> 6; 59*45901Sbostic goto common; 60*45901Sbostic 61*45901Sbostic case 'g': 62*45901Sbostic value = (newmode & S_IRWXG) >> 3; 63*45901Sbostic goto common; 64*45901Sbostic 65*45901Sbostic case 'o': 66*45901Sbostic value = newmode & S_IRWXO; 67*45901Sbostic common: 68*45901Sbostic if (set->cmd2 & CMD2_CLR) { 69*45901Sbostic if (set->cmd2 & CMD2_UBITS) 70*45901Sbostic newmode &= ~(S_IRWXU & set->bits); 71*45901Sbostic if (set->cmd2 & CMD2_GBITS) 72*45901Sbostic newmode &= ~(S_IRWXG & set->bits); 73*45901Sbostic if (set->cmd2 & CMD2_OBITS) 74*45901Sbostic newmode &= ~(S_IRWXO & set->bits); 75*45901Sbostic } 76*45901Sbostic if (set->cmd2 & CMD2_SET) { 77*45901Sbostic if (set->cmd2 & CMD2_UBITS) 78*45901Sbostic newmode |= (value<<6) & set->bits; 79*45901Sbostic if (set->cmd2 & CMD2_GBITS) 80*45901Sbostic newmode |= (value<<3) & set->bits; 81*45901Sbostic if (set->cmd2 & CMD2_OBITS) 82*45901Sbostic newmode |= value & set->bits; 83*45901Sbostic } 84*45901Sbostic break; 85*45901Sbostic 86*45901Sbostic case '+': 87*45901Sbostic newmode |= set->bits; 88*45901Sbostic break; 89*45901Sbostic 90*45901Sbostic case '-': 91*45901Sbostic newmode &= ~set->bits; 92*45901Sbostic break; 93*45901Sbostic 94*45901Sbostic case 'X': 95*45901Sbostic if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) 96*45901Sbostic newmode |= set->bits; 97*45901Sbostic break; 98*45901Sbostic 99*45901Sbostic case '\0': 100*45901Sbostic default: 101*45901Sbostic #ifdef SETMODE_DEBUG 102*45901Sbostic (void)printf("getmode(, %04o) -> %04o\n", 103*45901Sbostic omode, newmode); 104*45901Sbostic #endif 105*45901Sbostic return(newmode); 106*45901Sbostic } 10739978Sbostic } 10839978Sbostic 10939978Sbostic #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 11039978Sbostic 111*45901Sbostic static struct bitcmd * 112*45901Sbostic addcmd(set, op, who, oparg, mask) 113*45901Sbostic struct bitcmd *set; 114*45901Sbostic register int oparg, who; 115*45901Sbostic register int op; 116*45901Sbostic mode_t mask; 117*45901Sbostic { 118*45901Sbostic switch (op) { 119*45901Sbostic case '+': 120*45901Sbostic case 'X': 121*45901Sbostic set->cmd = op; 122*45901Sbostic set->bits = (who ? who : mask) & oparg; 123*45901Sbostic break; 124*45901Sbostic 125*45901Sbostic case '-': 126*45901Sbostic set->cmd = '-'; 127*45901Sbostic set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg; 128*45901Sbostic break; 129*45901Sbostic 130*45901Sbostic case '=': 131*45901Sbostic set->cmd = '-'; 132*45901Sbostic if (!who) { 133*45901Sbostic set->bits = STANDARD_BITS; 134*45901Sbostic who = mask; 135*45901Sbostic } else 136*45901Sbostic set->bits = who; 137*45901Sbostic set++; 138*45901Sbostic 139*45901Sbostic set->cmd = '+'; 140*45901Sbostic set->bits = who & oparg; 141*45901Sbostic break; 142*45901Sbostic case 'u': 143*45901Sbostic case 'g': 144*45901Sbostic case 'o': 145*45901Sbostic set->cmd = op; 146*45901Sbostic if (who) { 147*45901Sbostic set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | 148*45901Sbostic ((who & S_IRGRP) ? CMD2_GBITS : 0) | 149*45901Sbostic ((who & S_IROTH) ? CMD2_OBITS : 0); 150*45901Sbostic set->bits = ~0; 151*45901Sbostic } else { 152*45901Sbostic set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; 153*45901Sbostic set->bits = mask; 154*45901Sbostic } 155*45901Sbostic 156*45901Sbostic if (oparg == '+') 157*45901Sbostic set->cmd2 |= CMD2_SET; 158*45901Sbostic else if (oparg == '-') 159*45901Sbostic set->cmd2 |= CMD2_CLR; 160*45901Sbostic else if (oparg == '=') 161*45901Sbostic set->cmd2 |= CMD2_SET|CMD2_CLR; 162*45901Sbostic break; 163*45901Sbostic } 164*45901Sbostic return(set+1); 165*45901Sbostic } 166*45901Sbostic 167*45901Sbostic #define ADDCMD(a, b, c, d) \ 168*45901Sbostic if (set >= endset) { \ 169*45901Sbostic register struct bitcmd *newset; \ 170*45901Sbostic setlen += SET_LEN_INCR; \ 171*45901Sbostic newset = realloc(saveset, sizeof(struct bitcmd) * setlen); \ 172*45901Sbostic if (!saveset) \ 173*45901Sbostic return(NULL); \ 174*45901Sbostic set = newset + (set - saveset); \ 175*45901Sbostic saveset = newset; \ 176*45901Sbostic endset = newset + (setlen - 2); \ 177*45901Sbostic } \ 178*45901Sbostic set = addcmd(set, (a), (b), (c), (d)) 179*45901Sbostic 180*45901Sbostic struct bitcmd * 18139978Sbostic setmode(p) 18239978Sbostic register char *p; 18339978Sbostic { 18439978Sbostic register int perm, who; 18539978Sbostic register char op; 186*45901Sbostic mode_t mask; 187*45901Sbostic struct bitcmd *set, *saveset, *endset; 188*45901Sbostic int permXbits, setlen; 18939978Sbostic 19039978Sbostic /* 191*45901Sbostic * Get a copy of the mask for the permissions that are mask relative. 192*45901Sbostic * Flip the bits, we want what's not set. 19339978Sbostic */ 19439978Sbostic (void)umask(mask = umask(0)); 19539978Sbostic mask = ~mask; 19639978Sbostic 197*45901Sbostic setlen = SET_LEN + 2; 198*45901Sbostic 199*45901Sbostic set = (struct bitcmd *)malloc((u_int)(sizeof(struct bitcmd) * setlen)); 200*45901Sbostic if (!set) 20141579Sbostic return(NULL); 202*45901Sbostic saveset = set; 203*45901Sbostic endset = set + (setlen - 2); 20441579Sbostic 20539978Sbostic /* 206*45901Sbostic * If an absolute number, get it and return; disallow non-octal digits 207*45901Sbostic * or illegal bits. 20839978Sbostic */ 20939978Sbostic if (isdigit(*p)) { 210*45901Sbostic perm = (mode_t)strtol(p, (char **)0, 8); 211*45901Sbostic if (perm & ~(STANDARD_BITS|S_ISTXT)) { 212*45901Sbostic free(saveset); 213*45901Sbostic return(NULL); 214*45901Sbostic } 21539978Sbostic while (*++p) 216*45901Sbostic if (*p < '0' || *p > '7') { 217*45901Sbostic free(saveset); 21841579Sbostic return(NULL); 219*45901Sbostic } 220*45901Sbostic ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); 221*45901Sbostic return(saveset); 22239978Sbostic } 22339978Sbostic 224*45901Sbostic if (!*p) { 225*45901Sbostic free(saveset); 22641579Sbostic return(NULL); 227*45901Sbostic } 22839978Sbostic /* 229*45901Sbostic * Build list of structures to set/clear/copy bits as described by 230*45901Sbostic * each clause of the symbolic mode. 23139978Sbostic */ 23239978Sbostic for (;;) { 233*45901Sbostic /* First, find out which bits might be modified. */ 234*45901Sbostic for (who = 0;; ++p) { 23539978Sbostic switch (*p) { 23639978Sbostic case 'a': 23739978Sbostic who |= STANDARD_BITS; 23839978Sbostic break; 23939978Sbostic case 'u': 24039978Sbostic who |= S_ISUID|S_IRWXU; 24139978Sbostic break; 24239978Sbostic case 'g': 24339978Sbostic who |= S_ISGID|S_IRWXG; 24439978Sbostic break; 24539978Sbostic case 'o': 24639978Sbostic who |= S_IRWXO; 24739978Sbostic break; 24839978Sbostic default: 24939978Sbostic goto getop; 25039978Sbostic } 251*45901Sbostic } 252*45901Sbostic getop: 25339978Sbostic 254*45901Sbostic if ((op = *p++) != '+' && op != '-' && op != '=') { 255*45901Sbostic free(saveset); 25641579Sbostic return(NULL); 257*45901Sbostic } 25839978Sbostic 25939978Sbostic who &= ~S_ISTXT; 260*45901Sbostic for (perm = 0, permXbits = 0;; ++p) { 26139978Sbostic switch (*p) { 26239978Sbostic case 'r': 26339978Sbostic perm |= S_IRUSR|S_IRGRP|S_IROTH; 26439978Sbostic break; 26539978Sbostic case 's': 266*45901Sbostic /* If only "other" bits ignore set-id. */ 26739978Sbostic if (who & ~S_IRWXO) 26839978Sbostic perm |= S_ISUID|S_ISGID; 26939978Sbostic break; 27039978Sbostic case 't': 271*45901Sbostic /* If only "other" bits ignore sticky. */ 27239978Sbostic if (who & ~S_IRWXO) { 27339978Sbostic who |= S_ISTXT; 27439978Sbostic perm |= S_ISTXT; 27539978Sbostic } 27639978Sbostic break; 27739978Sbostic case 'w': 27839978Sbostic perm |= S_IWUSR|S_IWGRP|S_IWOTH; 27939978Sbostic break; 28039978Sbostic case 'X': 28139978Sbostic permXbits = S_IXUSR|S_IXGRP|S_IXOTH; 28239978Sbostic break; 28339978Sbostic case 'x': 28439978Sbostic perm |= S_IXUSR|S_IXGRP|S_IXOTH; 28539978Sbostic break; 286*45901Sbostic case 'u': 287*45901Sbostic case 'g': 288*45901Sbostic case 'o': 289*45901Sbostic /* 290*45901Sbostic * When ever we hit 'u', 'g', or 'o', we have 291*45901Sbostic * to flush out any partial mode that we have, 292*45901Sbostic * and then do the copying of the mode bits. 293*45901Sbostic */ 294*45901Sbostic if (perm) { 295*45901Sbostic ADDCMD(op, who, perm, mask); 296*45901Sbostic perm = 0; 297*45901Sbostic } 298*45901Sbostic if (op == '+' && permXbits) { 299*45901Sbostic ADDCMD('X', who, permXbits, mask); 300*45901Sbostic permXbits = 0; 301*45901Sbostic } 302*45901Sbostic ADDCMD(*p, who, op, mask); 303*45901Sbostic break; 304*45901Sbostic 30539978Sbostic default: 306*45901Sbostic /* 307*45901Sbostic * Add any permissions that we haven't already 308*45901Sbostic * done. 309*45901Sbostic */ 310*45901Sbostic if (perm) { 311*45901Sbostic ADDCMD(op, who, perm, mask); 312*45901Sbostic perm = 0; 313*45901Sbostic } 314*45901Sbostic if (permXbits) { 315*45901Sbostic ADDCMD('X', who, permXbits, mask); 316*45901Sbostic permXbits = 0; 317*45901Sbostic } 31839978Sbostic goto apply; 31939978Sbostic } 32039978Sbostic } 32139978Sbostic 322*45901Sbostic apply: if (!*p) 32339978Sbostic break; 32439978Sbostic if (*p != ',') 32539978Sbostic goto getop; 32639978Sbostic ++p; 32739978Sbostic } 328*45901Sbostic set->cmd = 0; 329*45901Sbostic #ifdef SETMODE_DEBUG 330*45901Sbostic (void)printf("Before compress_mode()\n"); 331*45901Sbostic dumpmode(saveset); 332*45901Sbostic #endif 333*45901Sbostic compress_mode(saveset); 334*45901Sbostic #ifdef SETMODE_DEBUG 335*45901Sbostic (void)printf("After compress_mode()\n"); 336*45901Sbostic dumpmode(saveset); 337*45901Sbostic #endif 338*45901Sbostic return(saveset); 33939978Sbostic } 340*45901Sbostic 341*45901Sbostic #ifdef SETMODE_DEBUG 342*45901Sbostic dumpmode(set) 343*45901Sbostic register struct bitcmd *set; 344*45901Sbostic { 345*45901Sbostic for (; set->cmd; ++set) 346*45901Sbostic (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", 347*45901Sbostic set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", 348*45901Sbostic set->cmd2 & CMD2_CLR ? " CLR" : "", 349*45901Sbostic set->cmd2 & CMD2_SET ? " SET" : "", 350*45901Sbostic set->cmd2 & CMD2_UBITS ? " UBITS" : "", 351*45901Sbostic set->cmd2 & CMD2_GBITS ? " GBITS" : "", 352*45901Sbostic set->cmd2 & CMD2_OBITS ? " OBITS" : ""); 353*45901Sbostic } 354*45901Sbostic #endif 355*45901Sbostic 356*45901Sbostic /* 357*45901Sbostic * Given an array of bitcmd structures, compress by compacting consecutive 358*45901Sbostic * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', 359*45901Sbostic * 'g' and 'o' commands continue to be separate. They could probably be 360*45901Sbostic * compacted, but it's not worth the effort. 361*45901Sbostic */ 362*45901Sbostic static 363*45901Sbostic compress_mode(set) 364*45901Sbostic register struct bitcmd *set; 365*45901Sbostic { 366*45901Sbostic register struct bitcmd *nset; 367*45901Sbostic register int setbits, clrbits, Xbits, op; 368*45901Sbostic 369*45901Sbostic for (nset = set;;) { 370*45901Sbostic /* Copy over any 'u', 'g' and 'o' commands. */ 371*45901Sbostic while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { 372*45901Sbostic *set++ = *nset++; 373*45901Sbostic if (!op) 374*45901Sbostic return; 375*45901Sbostic } 376*45901Sbostic 377*45901Sbostic for (setbits = clrbits = Xbits = 0;; nset++) { 378*45901Sbostic if ((op = nset->cmd) == '-') { 379*45901Sbostic clrbits |= nset->bits; 380*45901Sbostic setbits &= ~nset->bits; 381*45901Sbostic Xbits &= ~nset->bits; 382*45901Sbostic } else if (op == '+') { 383*45901Sbostic setbits |= nset->bits; 384*45901Sbostic clrbits &= ~nset->bits; 385*45901Sbostic Xbits &= ~nset->bits; 386*45901Sbostic } else if (op == 'X') 387*45901Sbostic Xbits |= nset->bits & ~setbits; 388*45901Sbostic else 389*45901Sbostic break; 390*45901Sbostic } 391*45901Sbostic if (clrbits) { 392*45901Sbostic set->cmd = '-'; 393*45901Sbostic set->cmd2 = 0; 394*45901Sbostic set->bits = clrbits; 395*45901Sbostic set++; 396*45901Sbostic } 397*45901Sbostic if (setbits) { 398*45901Sbostic set->cmd = '+'; 399*45901Sbostic set->cmd2 = 0; 400*45901Sbostic set->bits = setbits; 401*45901Sbostic set++; 402*45901Sbostic } 403*45901Sbostic if (Xbits) { 404*45901Sbostic set->cmd = 'X'; 405*45901Sbostic set->cmd2 = 0; 406*45901Sbostic set->bits = Xbits; 407*45901Sbostic set++; 408*45901Sbostic } 409*45901Sbostic } 410*45901Sbostic } 411