1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #if defined(LIBC_SCCS) && !defined(lint) 35 static char sccsid[] = "@(#)setmode.c 5.6 (Berkeley) 5/27/91"; 36 #endif /* LIBC_SCCS and not lint */ 37 38 #include <sys/param.h> 39 #include <sys/stat.h> 40 #include <sys/errno.h> 41 #ifdef SETMODE_DEBUG 42 #include <stdio.h> 43 #endif 44 #include <stdlib.h> 45 #include <ctype.h> 46 47 #define SET_LEN 6 /* initial # of bitcmd struct to malloc */ 48 #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ 49 50 struct bitcmd { 51 char cmd; 52 char cmd2; 53 mode_t bits; 54 }; 55 56 #define CMD2_CLR 0x01 57 #define CMD2_SET 0x02 58 #define CMD2_GBITS 0x04 59 #define CMD2_OBITS 0x08 60 #define CMD2_UBITS 0x10 61 62 /* 63 * Given the old mode and an array of bitcmd structures, apply the operations 64 * described in the bitcmd structures to the old mode, and return the new mode. 65 * Note that there is no '=' command; a strict assignment is just a '-' (clear 66 * bits) followed by a '+' (set bits). 67 */ 68 mode_t 69 getmode(bbox, omode) 70 void *bbox; 71 mode_t omode; 72 { 73 register struct bitcmd *set; 74 register mode_t newmode, value; 75 76 set = (struct bitcmd *)bbox; 77 newmode = omode; 78 for (value = 0;; set++) 79 switch(set->cmd) { 80 /* 81 * When copying the user, group or other bits around, we "know" 82 * where the bit are in the mode so that we can do shifts to 83 * copy them around. If we don't use shifts, it gets real 84 * grundgy with lots of single bit checks and bit sets. 85 */ 86 case 'u': 87 value = (newmode & S_IRWXU) >> 6; 88 goto common; 89 90 case 'g': 91 value = (newmode & S_IRWXG) >> 3; 92 goto common; 93 94 case 'o': 95 value = newmode & S_IRWXO; 96 common: 97 if (set->cmd2 & CMD2_CLR) { 98 if (set->cmd2 & CMD2_UBITS) 99 newmode &= ~(S_IRWXU & set->bits); 100 if (set->cmd2 & CMD2_GBITS) 101 newmode &= ~(S_IRWXG & set->bits); 102 if (set->cmd2 & CMD2_OBITS) 103 newmode &= ~(S_IRWXO & set->bits); 104 } 105 if (set->cmd2 & CMD2_SET) { 106 if (set->cmd2 & CMD2_UBITS) 107 newmode |= (value<<6) & set->bits; 108 if (set->cmd2 & CMD2_GBITS) 109 newmode |= (value<<3) & set->bits; 110 if (set->cmd2 & CMD2_OBITS) 111 newmode |= value & set->bits; 112 } 113 break; 114 115 case '+': 116 newmode |= set->bits; 117 break; 118 119 case '-': 120 newmode &= ~set->bits; 121 break; 122 123 case 'X': 124 if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) 125 newmode |= set->bits; 126 break; 127 128 case '\0': 129 default: 130 #ifdef SETMODE_DEBUG 131 (void)printf("getmode(, %04o) -> %04o\n", 132 omode, newmode); 133 #endif 134 return(newmode); 135 } 136 } 137 138 #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 139 140 static struct bitcmd * 141 addcmd(set, op, who, oparg, mask) 142 struct bitcmd *set; 143 register int oparg, who; 144 register int op; 145 mode_t mask; 146 { 147 switch (op) { 148 case '+': 149 case 'X': 150 set->cmd = op; 151 set->bits = (who ? who : mask) & oparg; 152 break; 153 154 case '-': 155 set->cmd = '-'; 156 set->bits = (who ? who : (S_IRWXU|S_IRWXG|S_IRWXO)) & oparg; 157 break; 158 159 case '=': 160 set->cmd = '-'; 161 if (!who) { 162 set->bits = STANDARD_BITS; 163 who = mask; 164 } else 165 set->bits = who; 166 set++; 167 168 set->cmd = '+'; 169 set->bits = who & oparg; 170 break; 171 case 'u': 172 case 'g': 173 case 'o': 174 set->cmd = op; 175 if (who) { 176 set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | 177 ((who & S_IRGRP) ? CMD2_GBITS : 0) | 178 ((who & S_IROTH) ? CMD2_OBITS : 0); 179 set->bits = ~0; 180 } else { 181 set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; 182 set->bits = mask; 183 } 184 185 if (oparg == '+') 186 set->cmd2 |= CMD2_SET; 187 else if (oparg == '-') 188 set->cmd2 |= CMD2_CLR; 189 else if (oparg == '=') 190 set->cmd2 |= CMD2_SET|CMD2_CLR; 191 break; 192 } 193 return(set+1); 194 } 195 196 #define ADDCMD(a, b, c, d) \ 197 if (set >= endset) { \ 198 register struct bitcmd *newset; \ 199 setlen += SET_LEN_INCR; \ 200 newset = realloc(saveset, sizeof(struct bitcmd) * setlen); \ 201 if (!saveset) \ 202 return(NULL); \ 203 set = newset + (set - saveset); \ 204 saveset = newset; \ 205 endset = newset + (setlen - 2); \ 206 } \ 207 set = addcmd(set, (a), (b), (c), (d)) 208 209 void * 210 setmode(p) 211 register char *p; 212 { 213 register int perm, who; 214 register char op; 215 mode_t mask; 216 struct bitcmd *set, *saveset, *endset; 217 int permXbits, setlen; 218 static int compress_mode(); 219 220 /* 221 * Get a copy of the mask for the permissions that are mask relative. 222 * Flip the bits, we want what's not set. 223 */ 224 (void)umask(mask = umask(0)); 225 mask = ~mask; 226 227 setlen = SET_LEN + 2; 228 229 set = (struct bitcmd *)malloc((u_int)(sizeof(struct bitcmd) * setlen)); 230 if (!set) 231 return(NULL); 232 saveset = set; 233 endset = set + (setlen - 2); 234 235 /* 236 * If an absolute number, get it and return; disallow non-octal digits 237 * or illegal bits. 238 */ 239 if (isdigit(*p)) { 240 perm = (mode_t)strtol(p, (char **)0, 8); 241 if (perm & ~(STANDARD_BITS|S_ISTXT)) { 242 free(saveset); 243 return(NULL); 244 } 245 while (*++p) 246 if (*p < '0' || *p > '7') { 247 free(saveset); 248 return(NULL); 249 } 250 ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); 251 return((void *)saveset); 252 } 253 254 if (!*p) { 255 free(saveset); 256 return(NULL); 257 } 258 /* 259 * Build list of structures to set/clear/copy bits as described by 260 * each clause of the symbolic mode. 261 */ 262 for (;;) { 263 /* First, find out which bits might be modified. */ 264 for (who = 0;; ++p) { 265 switch (*p) { 266 case 'a': 267 who |= STANDARD_BITS; 268 break; 269 case 'u': 270 who |= S_ISUID|S_IRWXU; 271 break; 272 case 'g': 273 who |= S_ISGID|S_IRWXG; 274 break; 275 case 'o': 276 who |= S_IRWXO; 277 break; 278 default: 279 goto getop; 280 } 281 } 282 getop: 283 284 if ((op = *p++) != '+' && op != '-' && op != '=') { 285 free(saveset); 286 return(NULL); 287 } 288 289 who &= ~S_ISTXT; 290 for (perm = 0, permXbits = 0;; ++p) { 291 switch (*p) { 292 case 'r': 293 perm |= S_IRUSR|S_IRGRP|S_IROTH; 294 break; 295 case 's': 296 /* If only "other" bits ignore set-id. */ 297 if (who & ~S_IRWXO) 298 perm |= S_ISUID|S_ISGID; 299 break; 300 case 't': 301 /* If only "other" bits ignore sticky. */ 302 if (who & ~S_IRWXO) { 303 who |= S_ISTXT; 304 perm |= S_ISTXT; 305 } 306 break; 307 case 'w': 308 perm |= S_IWUSR|S_IWGRP|S_IWOTH; 309 break; 310 case 'X': 311 permXbits = S_IXUSR|S_IXGRP|S_IXOTH; 312 break; 313 case 'x': 314 perm |= S_IXUSR|S_IXGRP|S_IXOTH; 315 break; 316 case 'u': 317 case 'g': 318 case 'o': 319 /* 320 * When ever we hit 'u', 'g', or 'o', we have 321 * to flush out any partial mode that we have, 322 * and then do the copying of the mode bits. 323 */ 324 if (perm) { 325 ADDCMD(op, who, perm, mask); 326 perm = 0; 327 } 328 if (op == '+' && permXbits) { 329 ADDCMD('X', who, permXbits, mask); 330 permXbits = 0; 331 } 332 ADDCMD(*p, who, op, mask); 333 break; 334 335 default: 336 /* 337 * Add any permissions that we haven't already 338 * done. 339 */ 340 if (perm) { 341 ADDCMD(op, who, perm, mask); 342 perm = 0; 343 } 344 if (permXbits) { 345 ADDCMD('X', who, permXbits, mask); 346 permXbits = 0; 347 } 348 goto apply; 349 } 350 } 351 352 apply: if (!*p) 353 break; 354 if (*p != ',') 355 goto getop; 356 ++p; 357 } 358 set->cmd = 0; 359 #ifdef SETMODE_DEBUG 360 (void)printf("Before compress_mode()\n"); 361 dumpmode(saveset); 362 #endif 363 compress_mode(saveset); 364 #ifdef SETMODE_DEBUG 365 (void)printf("After compress_mode()\n"); 366 dumpmode(saveset); 367 #endif 368 return((void *)saveset); 369 } 370 371 #ifdef SETMODE_DEBUG 372 dumpmode(set) 373 register struct bitcmd *set; 374 { 375 for (; set->cmd; ++set) 376 (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", 377 set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", 378 set->cmd2 & CMD2_CLR ? " CLR" : "", 379 set->cmd2 & CMD2_SET ? " SET" : "", 380 set->cmd2 & CMD2_UBITS ? " UBITS" : "", 381 set->cmd2 & CMD2_GBITS ? " GBITS" : "", 382 set->cmd2 & CMD2_OBITS ? " OBITS" : ""); 383 } 384 #endif 385 386 /* 387 * Given an array of bitcmd structures, compress by compacting consecutive 388 * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', 389 * 'g' and 'o' commands continue to be separate. They could probably be 390 * compacted, but it's not worth the effort. 391 */ 392 static 393 compress_mode(set) 394 register struct bitcmd *set; 395 { 396 register struct bitcmd *nset; 397 register int setbits, clrbits, Xbits, op; 398 399 for (nset = set;;) { 400 /* Copy over any 'u', 'g' and 'o' commands. */ 401 while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { 402 *set++ = *nset++; 403 if (!op) 404 return; 405 } 406 407 for (setbits = clrbits = Xbits = 0;; nset++) { 408 if ((op = nset->cmd) == '-') { 409 clrbits |= nset->bits; 410 setbits &= ~nset->bits; 411 Xbits &= ~nset->bits; 412 } else if (op == '+') { 413 setbits |= nset->bits; 414 clrbits &= ~nset->bits; 415 Xbits &= ~nset->bits; 416 } else if (op == 'X') 417 Xbits |= nset->bits & ~setbits; 418 else 419 break; 420 } 421 if (clrbits) { 422 set->cmd = '-'; 423 set->cmd2 = 0; 424 set->bits = clrbits; 425 set++; 426 } 427 if (setbits) { 428 set->cmd = '+'; 429 set->cmd2 = 0; 430 set->bits = setbits; 431 set++; 432 } 433 if (Xbits) { 434 set->cmd = 'X'; 435 set->cmd2 = 0; 436 set->bits = Xbits; 437 set++; 438 } 439 } 440 } 441