setmode.c revision 1573
118334Speter/* 290075Sobrien * Copyright (c) 1989, 1993, 1994 3102780Skan * The Regents of the University of California. All rights reserved. 418334Speter * 518334Speter * This code is derived from software contributed to Berkeley by 618334Speter * Dave Borman at Cray Research, Inc. 718334Speter * 818334Speter * Redistribution and use in source and binary forms, with or without 918334Speter * modification, are permitted provided that the following conditions 1018334Speter * are met: 1118334Speter * 1. Redistributions of source code must retain the above copyright 1218334Speter * notice, this list of conditions and the following disclaimer. 1318334Speter * 2. Redistributions in binary form must reproduce the above copyright 1418334Speter * notice, this list of conditions and the following disclaimer in the 1518334Speter * documentation and/or other materials provided with the distribution. 1618334Speter * 3. All advertising materials mentioning features or use of this software 1718334Speter * must display the following acknowledgement: 1818334Speter * This product includes software developed by the University of 1918334Speter * California, Berkeley and its contributors. 2018334Speter * 4. Neither the name of the University nor the names of its contributors 2118334Speter * may be used to endorse or promote products derived from this software 2218334Speter * without specific prior written permission. 2350397Sobrien * 2418334Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2518334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2650397Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2718334Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2818334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3018334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3118334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3218334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3350397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3450397Sobrien * SUCH DAMAGE. 3590075Sobrien */ 3618334Speter 3790075Sobrien#if defined(LIBC_SCCS) && !defined(lint) 3890075Sobrienstatic char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94"; 3990075Sobrien#endif /* LIBC_SCCS and not lint */ 4090075Sobrien 4190075Sobrien#include <sys/types.h> 4290075Sobrien#include <sys/stat.h> 4390075Sobrien 4490075Sobrien#include <ctype.h> 4590075Sobrien#include <errno.h> 4690075Sobrien#include <signal.h> 4790075Sobrien#include <stddef.h> 4890075Sobrien#include <stdlib.h> 4990075Sobrien 5090075Sobrien#ifdef SETMODE_DEBUG 5190075Sobrien#include <stdio.h> 5290075Sobrien#endif 5390075Sobrien 5490075Sobrien#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ 5590075Sobrien#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ 5690075Sobrien 5790075Sobrientypedef struct bitcmd { 5818334Speter char cmd; 5918334Speter char cmd2; 6018334Speter mode_t bits; 6118334Speter} BITCMD; 6290075Sobrien 6318334Speter#define CMD2_CLR 0x01 6418334Speter#define CMD2_SET 0x02 6518334Speter#define CMD2_GBITS 0x04 6618334Speter#define CMD2_OBITS 0x08 6718334Speter#define CMD2_UBITS 0x10 6818334Speter 6918334Speterstatic BITCMD *addcmd __P((BITCMD *, int, int, int, u_int)); 7090075Sobrienstatic int compress_mode __P((BITCMD *)); 7190075Sobrien#ifdef SETMODE_DEBUG 7290075Sobrienstatic void dumpmode __P((BITCMD *)); 7318334Speter#endif 7418334Speter 7590075Sobrien/* 7690075Sobrien * Given the old mode and an array of bitcmd structures, apply the operations 7718334Speter * described in the bitcmd structures to the old mode, and return the new mode. 7818334Speter * Note that there is no '=' command; a strict assignment is just a '-' (clear 7990075Sobrien * bits) followed by a '+' (set bits). 8090075Sobrien */ 8190075Sobrienmode_t 8290075Sobriengetmode(bbox, omode) 8390075Sobrien void *bbox; 8490075Sobrien mode_t omode; 8590075Sobrien{ 8690075Sobrien register BITCMD *set; 8718334Speter register mode_t clrval, newmode, value; 8890075Sobrien 8990075Sobrien set = (BITCMD *)bbox; 9090075Sobrien newmode = omode; 9190075Sobrien for (value = 0;; set++) 9290075Sobrien switch(set->cmd) { 9390075Sobrien /* 9490075Sobrien * When copying the user, group or other bits around, we "know" 9590075Sobrien * where the bits are in the mode so that we can do shifts to 9690075Sobrien * copy them around. If we don't use shifts, it gets real 9790075Sobrien * grundgy with lots of single bit checks and bit sets. 9890075Sobrien */ 9990075Sobrien case 'u': 10090075Sobrien value = (newmode & S_IRWXU) >> 6; 10118334Speter goto common; 10290075Sobrien 10390075Sobrien case 'g': 10418334Speter value = (newmode & S_IRWXG) >> 3; 10590075Sobrien goto common; 10690075Sobrien 10790075Sobrien case 'o': 10890075Sobrien value = newmode & S_IRWXO; 10950397Sobriencommon: if (set->cmd2 & CMD2_CLR) { 11090075Sobrien clrval = 11190075Sobrien (set->cmd2 & CMD2_SET) ? S_IRWXO : value; 11290075Sobrien if (set->cmd2 & CMD2_UBITS) 11390075Sobrien newmode &= ~((clrval<<6) & set->bits); 11490075Sobrien if (set->cmd2 & CMD2_GBITS) 11596263Sobrien newmode &= ~((clrval<<3) & set->bits); 11696263Sobrien if (set->cmd2 & CMD2_OBITS) 11796263Sobrien newmode &= ~(clrval & set->bits); 11896263Sobrien } 11990075Sobrien if (set->cmd2 & CMD2_SET) { 12090075Sobrien if (set->cmd2 & CMD2_UBITS) 12190075Sobrien newmode |= (value<<6) & set->bits; 12290075Sobrien if (set->cmd2 & CMD2_GBITS) 12390075Sobrien newmode |= (value<<3) & set->bits; 12490075Sobrien if (set->cmd2 & CMD2_OBITS) 12590075Sobrien newmode |= value & set->bits; 12690075Sobrien } 12790075Sobrien break; 12890075Sobrien 12990075Sobrien case '+': 13090075Sobrien newmode |= set->bits; 13190075Sobrien break; 13290075Sobrien 13390075Sobrien case '-': 13490075Sobrien newmode &= ~set->bits; 13590075Sobrien break; 13690075Sobrien 13790075Sobrien case 'X': 13890075Sobrien if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) 13990075Sobrien newmode |= set->bits; 14090075Sobrien break; 14190075Sobrien 14290075Sobrien case '\0': 14390075Sobrien default: 14490075Sobrien#ifdef SETMODE_DEBUG 14590075Sobrien (void)printf("getmode:%04o -> %04o\n", omode, newmode); 14690075Sobrien#endif 14790075Sobrien return (newmode); 14890075Sobrien } 14990075Sobrien} 15090075Sobrien 15190075Sobrien#define ADDCMD(a, b, c, d) \ 15290075Sobrien if (set >= endset) { \ 15390075Sobrien register BITCMD *newset; \ 15490075Sobrien setlen += SET_LEN_INCR; \ 15590075Sobrien newset = realloc(saveset, sizeof(BITCMD) * setlen); \ 15690075Sobrien if (!saveset) \ 15790075Sobrien return (NULL); \ 15890075Sobrien set = newset + (set - saveset); \ 15918334Speter saveset = newset; \ 16090075Sobrien endset = newset + (setlen - 2); \ 16190075Sobrien } \ 16218334Speter set = addcmd(set, (a), (b), (c), (d)) 16390075Sobrien 16490075Sobrien#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 16518334Speter 16690075Sobrienvoid * 16790075Sobriensetmode(p) 16890075Sobrien register char *p; 16990075Sobrien{ 17090075Sobrien register int perm, who; 17190075Sobrien register char op; 17290075Sobrien BITCMD *set, *saveset, *endset; 17390075Sobrien sigset_t sigset, sigoset; 17490075Sobrien mode_t mask; 17590075Sobrien int equalopdone, permXbits, setlen; 17690075Sobrien 17790075Sobrien if (!*p) 17890075Sobrien return (NULL); 179102780Skan 180102780Skan /* 181102780Skan * Get a copy of the mask for the permissions that are mask relative. 182102780Skan * Flip the bits, we want what's not set. Since it's possible that 183102780Skan * the caller is opening files inside a signal handler, protect them 184102780Skan * as best we can. 185102780Skan */ 186102780Skan sigfillset(&sigset); 187102780Skan (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset); 188102780Skan (void)umask(mask = umask(0)); 189102780Skan mask = ~mask; 190102780Skan (void)sigprocmask(SIG_SETMASK, &sigoset, NULL); 191102780Skan 192102780Skan setlen = SET_LEN + 2; 193102780Skan 194102780Skan if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL) 195102780Skan return (NULL); 196102780Skan saveset = set; 197102780Skan endset = set + (setlen - 2); 198102780Skan 199102780Skan /* 200102780Skan * If an absolute number, get it and return; disallow non-octal digits 201102780Skan * or illegal bits. 202102780Skan */ 203102780Skan if (isdigit(*p)) { 204102780Skan perm = (mode_t)strtol(p, NULL, 8); 205102780Skan if (perm & ~(STANDARD_BITS|S_ISTXT)) { 206102780Skan free(saveset); 207102780Skan return (NULL); 208102780Skan } 209102780Skan while (*++p) 210102780Skan if (*p < '0' || *p > '7') { 211102780Skan free(saveset); 212102780Skan return (NULL); 213102780Skan } 214102780Skan ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); 215102780Skan return (saveset); 216102780Skan } 21790075Sobrien 21890075Sobrien /* 21990075Sobrien * Build list of structures to set/clear/copy bits as described by 22090075Sobrien * each clause of the symbolic mode. 22190075Sobrien */ 22290075Sobrien for (;;) { 22390075Sobrien /* First, find out which bits might be modified. */ 22490075Sobrien for (who = 0;; ++p) { 22590075Sobrien switch (*p) { 22690075Sobrien case 'a': 22790075Sobrien who |= STANDARD_BITS; 22890075Sobrien break; 22990075Sobrien case 'u': 23090075Sobrien who |= S_ISUID|S_IRWXU; 23190075Sobrien break; 23290075Sobrien case 'g': 23390075Sobrien who |= S_ISGID|S_IRWXG; 23490075Sobrien break; 23590075Sobrien case 'o': 23690075Sobrien who |= S_IRWXO; 23790075Sobrien break; 23890075Sobrien default: 23990075Sobrien goto getop; 24090075Sobrien } 24190075Sobrien } 24290075Sobrien 24390075Sobriengetop: if ((op = *p++) != '+' && op != '-' && op != '=') { 244102780Skan free(saveset); 24518334Speter return (NULL); 24690075Sobrien } 24718334Speter if (op == '=') 24850397Sobrien equalopdone = 0; 24918334Speter 25090075Sobrien who &= ~S_ISTXT; 25190075Sobrien for (perm = 0, permXbits = 0;; ++p) { 25218334Speter switch (*p) { 25318334Speter case 'r': 25418334Speter perm |= S_IRUSR|S_IRGRP|S_IROTH; 25518334Speter break; 25618334Speter case 's': 25790075Sobrien /* If only "other" bits ignore set-id. */ 25850397Sobrien if (who & ~S_IRWXO) 25990075Sobrien perm |= S_ISUID|S_ISGID; 26090075Sobrien break; 26190075Sobrien case 't': 26290075Sobrien /* If only "other" bits ignore sticky. */ 26390075Sobrien if (who & ~S_IRWXO) { 26490075Sobrien who |= S_ISTXT; 26590075Sobrien perm |= S_ISTXT; 26618334Speter } 26790075Sobrien break; 26890075Sobrien case 'w': 26990075Sobrien perm |= S_IWUSR|S_IWGRP|S_IWOTH; 27090075Sobrien break; 27190075Sobrien case 'X': 27290075Sobrien permXbits = S_IXUSR|S_IXGRP|S_IXOTH; 27390075Sobrien break; 27490075Sobrien case 'x': 27590075Sobrien perm |= S_IXUSR|S_IXGRP|S_IXOTH; 27690075Sobrien break; 27718334Speter case 'u': 27818334Speter case 'g': 27990075Sobrien case 'o': 28018334Speter /* 28118334Speter * When ever we hit 'u', 'g', or 'o', we have 28218334Speter * to flush out any partial mode that we have, 28318334Speter * and then do the copying of the mode bits. 28418334Speter */ 28518334Speter if (perm) { 28618334Speter ADDCMD(op, who, perm, mask); 28718334Speter perm = 0; 28890075Sobrien } 28918334Speter if (op == '=') 29018334Speter equalopdone = 1; 29190075Sobrien if (op == '+' && permXbits) { 29218334Speter ADDCMD('X', who, permXbits, mask); 29318334Speter permXbits = 0; 29418334Speter } 29518334Speter ADDCMD(*p, who, op, mask); 29618334Speter break; 29718334Speter 29818334Speter default: 29990075Sobrien /* 30090075Sobrien * Add any permissions that we haven't already 30190075Sobrien * done. 30290075Sobrien */ 30390075Sobrien if (perm || (op == '=' && !equalopdone)) { 30418334Speter if (op == '=') 30518334Speter equalopdone = 1; 30690075Sobrien ADDCMD(op, who, perm, mask); 30790075Sobrien perm = 0; 30818334Speter } 30918334Speter if (permXbits) { 31018334Speter ADDCMD('X', who, permXbits, mask); 31118334Speter permXbits = 0; 31218334Speter } 31318334Speter goto apply; 31418334Speter } 31518334Speter } 31618334Speter 31718334Speterapply: if (!*p) 31818334Speter break; 31918334Speter if (*p != ',') 32018334Speter goto getop; 32118334Speter ++p; 32290075Sobrien } 32390075Sobrien set->cmd = 0; 32418334Speter#ifdef SETMODE_DEBUG 32518334Speter (void)printf("Before compress_mode()\n"); 32690075Sobrien dumpmode(saveset); 32718334Speter#endif 32850397Sobrien compress_mode(saveset); 32950397Sobrien#ifdef SETMODE_DEBUG 33090075Sobrien (void)printf("After compress_mode()\n"); 33150397Sobrien dumpmode(saveset); 33290075Sobrien#endif 33318334Speter return (saveset); 33418334Speter} 33518334Speter 33690075Sobrienstatic BITCMD * 33790075Sobrienaddcmd(set, op, who, oparg, mask) 33890075Sobrien BITCMD *set; 33950397Sobrien register int oparg, who; 34090075Sobrien register int op; 34190075Sobrien u_int mask; 34290075Sobrien{ 34390075Sobrien switch (op) { 34490075Sobrien case '=': 34590075Sobrien set->cmd = '-'; 34690075Sobrien set->bits = who ? who : STANDARD_BITS; 34790075Sobrien set++; 34890075Sobrien 34990075Sobrien op = '+'; 35090075Sobrien /* FALLTHROUGH */ 351107590Sobrien case '+': 352107590Sobrien case '-': 35390075Sobrien case 'X': 35490075Sobrien set->cmd = op; 35590075Sobrien set->bits = (who ? who : mask) & oparg; 35690075Sobrien break; 35790075Sobrien 35890075Sobrien case 'u': 35990075Sobrien case 'g': 36090075Sobrien case 'o': 36190075Sobrien set->cmd = op; 36290075Sobrien if (who) { 36390075Sobrien set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | 36490075Sobrien ((who & S_IRGRP) ? CMD2_GBITS : 0) | 36590075Sobrien ((who & S_IROTH) ? CMD2_OBITS : 0); 36690075Sobrien set->bits = ~0; 36790075Sobrien } else { 36890075Sobrien set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; 36990075Sobrien set->bits = mask; 37090075Sobrien } 37190075Sobrien 37290075Sobrien if (oparg == '+') 37390075Sobrien set->cmd2 |= CMD2_SET; 37490075Sobrien else if (oparg == '-') 37590075Sobrien set->cmd2 |= CMD2_CLR; 37690075Sobrien else if (oparg == '=') 37790075Sobrien set->cmd2 |= CMD2_SET|CMD2_CLR; 37890075Sobrien break; 37990075Sobrien } 38090075Sobrien return (set + 1); 38190075Sobrien} 38218334Speter 38390075Sobrien#ifdef SETMODE_DEBUG 38490075Sobrienstatic void 38518334Speterdumpmode(set) 38618334Speter register BITCMD *set; 38790075Sobrien{ 38890075Sobrien for (; set->cmd; ++set) 38990075Sobrien (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", 39090075Sobrien set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", 39190075Sobrien set->cmd2 & CMD2_CLR ? " CLR" : "", 39290075Sobrien set->cmd2 & CMD2_SET ? " SET" : "", 39318334Speter set->cmd2 & CMD2_UBITS ? " UBITS" : "", 39418334Speter set->cmd2 & CMD2_GBITS ? " GBITS" : "", 39590075Sobrien set->cmd2 & CMD2_OBITS ? " OBITS" : ""); 39618334Speter} 39790075Sobrien#endif 39818334Speter 39990075Sobrien/* 40090075Sobrien * Given an array of bitcmd structures, compress by compacting consecutive 40190075Sobrien * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', 40290075Sobrien * 'g' and 'o' commands continue to be separate. They could probably be 40318334Speter * compacted, but it's not worth the effort. 40490075Sobrien */ 40590075Sobrienstatic int 40690075Sobriencompress_mode(set) 40790075Sobrien register BITCMD *set; 40890075Sobrien{ 40990075Sobrien register BITCMD *nset; 41090075Sobrien register int setbits, clrbits, Xbits, op; 41118334Speter 41290075Sobrien for (nset = set;;) { 41390075Sobrien /* Copy over any 'u', 'g' and 'o' commands. */ 41418334Speter while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { 41590075Sobrien *set++ = *nset++; 41690075Sobrien if (!op) 41790075Sobrien return; 41818334Speter } 41990075Sobrien 42090075Sobrien for (setbits = clrbits = Xbits = 0;; nset++) { 42190075Sobrien if ((op = nset->cmd) == '-') { 42290075Sobrien clrbits |= nset->bits; 42390075Sobrien setbits &= ~nset->bits; 42490075Sobrien Xbits &= ~nset->bits; 42590075Sobrien } else if (op == '+') { 42690075Sobrien setbits |= nset->bits; 42790075Sobrien clrbits &= ~nset->bits; 42818334Speter Xbits &= ~nset->bits; 42990075Sobrien } else if (op == 'X') 43090075Sobrien Xbits |= nset->bits & ~setbits; 43190075Sobrien else 43290075Sobrien break; 43390075Sobrien } 43490075Sobrien if (clrbits) { 43590075Sobrien set->cmd = '-'; 43690075Sobrien set->cmd2 = 0; 43790075Sobrien set->bits = clrbits; 43890075Sobrien set++; 43990075Sobrien } 44090075Sobrien if (setbits) { 44190075Sobrien set->cmd = '+'; 44290075Sobrien set->cmd2 = 0; 44390075Sobrien set->bits = setbits; 44490075Sobrien set++; 44590075Sobrien } 44690075Sobrien if (Xbits) { 44790075Sobrien set->cmd = 'X'; 44890075Sobrien set->cmd2 = 0; 44990075Sobrien set->bits = Xbits; 45090075Sobrien set++; 45190075Sobrien } 45290075Sobrien } 45390075Sobrien} 45490075Sobrien