setmode.c revision 111273
138032Speter/* 2111826Sgshapiro * Copyright (c) 1989, 1993, 1994 364565Sgshapiro * The Regents of the University of California. All rights reserved. 438032Speter * 538032Speter * This code is derived from software contributed to Berkeley by 638032Speter * Dave Borman at Cray Research, Inc. 738032Speter * 838032Speter * Redistribution and use in source and binary forms, with or without 938032Speter * modification, are permitted provided that the following conditions 1038032Speter * are met: 1138032Speter * 1. Redistributions of source code must retain the above copyright 12120259Sgshapiro * notice, this list of conditions and the following disclaimer. 1338032Speter * 2. Redistributions in binary form must reproduce the above copyright 1438032Speter * notice, this list of conditions and the following disclaimer in the 1564565Sgshapiro * documentation and/or other materials provided with the distribution. 1664565Sgshapiro * 3. All advertising materials mentioning features or use of this software 17141862Sgshapiro * must display the following acknowledgement: 1864565Sgshapiro * This product includes software developed by the University of 1964565Sgshapiro * California, Berkeley and its contributors. 2064565Sgshapiro * 4. Neither the name of the University nor the names of its contributors 2164565Sgshapiro * may be used to endorse or promote products derived from this software 2264565Sgshapiro * without specific prior written permission. 2338032Speter * 2438032Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2564565Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2664565Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2764565Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2864565Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2964565Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3090795Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3164565Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3238032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3338032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3438032Speter * SUCH DAMAGE. 3538032Speter */ 3638032Speter 3738032Speter#if defined(LIBC_SCCS) && !defined(lint) 3838032Speterstatic char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94"; 3938032Speter#endif /* LIBC_SCCS and not lint */ 4038032Speter#include <sys/cdefs.h> 4138032Speter__FBSDID("$FreeBSD: head/lib/libc/gen/setmode.c 111273 2003-02-23 00:06:35Z mikeh $"); 4238032Speter 4338032Speter#include "namespace.h" 4438032Speter#include <sys/types.h> 4538032Speter#include <sys/stat.h> 4638032Speter 4738032Speter#include <ctype.h> 4838032Speter#include <signal.h> 4938032Speter#include <stddef.h> 5038032Speter#include <stdlib.h> 5138032Speter#include <unistd.h> 5238032Speter 5338032Speter#ifdef SETMODE_DEBUG 5438032Speter#include <stdio.h> 5538032Speter#endif 5638032Speter#include "un-namespace.h" 5738032Speter 5838032Speter#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ 5938032Speter#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ 6038032Speter 6138032Spetertypedef struct bitcmd { 6238032Speter char cmd; 6364565Sgshapiro char cmd2; 6438032Speter mode_t bits; 6590795Sgshapiro} BITCMD; 6638032Speter 6738032Speter#define CMD2_CLR 0x01 6838032Speter#define CMD2_SET 0x02 6938032Speter#define CMD2_GBITS 0x04 7038032Speter#define CMD2_OBITS 0x08 7138032Speter#define CMD2_UBITS 0x10 7238032Speter 7338032Speterstatic BITCMD *addcmd(BITCMD *, int, int, int, u_int); 7438032Speterstatic void compress_mode(BITCMD *); 7538032Speter#ifdef SETMODE_DEBUG 7638032Speterstatic void dumpmode(BITCMD *); 7738032Speter#endif 7838032Speter 7938032Speter/* 8038032Speter * Given the old mode and an array of bitcmd structures, apply the operations 8138032Speter * described in the bitcmd structures to the old mode, and return the new mode. 8238032Speter * Note that there is no '=' command; a strict assignment is just a '-' (clear 8338032Speter * bits) followed by a '+' (set bits). 8438032Speter */ 8538032Spetermode_t 8638032Spetergetmode(bbox, omode) 8738032Speter const void *bbox; 8838032Speter mode_t omode; 8938032Speter{ 9038032Speter const BITCMD *set; 9138032Speter mode_t clrval, newmode, value; 9238032Speter 9338032Speter set = (const BITCMD *)bbox; 9438032Speter newmode = omode; 9538032Speter for (value = 0;; set++) 9638032Speter switch(set->cmd) { 9738032Speter /* 9838032Speter * When copying the user, group or other bits around, we "know" 9938032Speter * where the bits are in the mode so that we can do shifts to 10038032Speter * copy them around. If we don't use shifts, it gets real 10138032Speter * grundgy with lots of single bit checks and bit sets. 10238032Speter */ 10338032Speter case 'u': 10490795Sgshapiro value = (newmode & S_IRWXU) >> 6; 10538032Speter goto common; 10638032Speter 10790795Sgshapiro case 'g': 10890795Sgshapiro value = (newmode & S_IRWXG) >> 3; 10938032Speter goto common; 11038032Speter 11164565Sgshapiro case 'o': 11290795Sgshapiro value = newmode & S_IRWXO; 11390795Sgshapirocommon: if (set->cmd2 & CMD2_CLR) { 11438032Speter clrval = 11538032Speter (set->cmd2 & CMD2_SET) ? S_IRWXO : value; 11638032Speter if (set->cmd2 & CMD2_UBITS) 11738032Speter newmode &= ~((clrval<<6) & set->bits); 11890795Sgshapiro if (set->cmd2 & CMD2_GBITS) 11938032Speter newmode &= ~((clrval<<3) & set->bits); 12038032Speter if (set->cmd2 & CMD2_OBITS) 12138032Speter newmode &= ~(clrval & set->bits); 12238032Speter } 12338032Speter if (set->cmd2 & CMD2_SET) { 12438032Speter if (set->cmd2 & CMD2_UBITS) 12538032Speter newmode |= (value<<6) & set->bits; 12638032Speter if (set->cmd2 & CMD2_GBITS) 12738032Speter newmode |= (value<<3) & set->bits; 12838032Speter if (set->cmd2 & CMD2_OBITS) 12938032Speter newmode |= value & set->bits; 13038032Speter } 13138032Speter break; 13238032Speter 13338032Speter case '+': 13438032Speter newmode |= set->bits; 13538032Speter break; 13638032Speter 13738032Speter case '-': 13838032Speter newmode &= ~set->bits; 13938032Speter break; 14038032Speter 14138032Speter case 'X': 14238032Speter if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) 14338032Speter newmode |= set->bits; 14438032Speter break; 14538032Speter 14690795Sgshapiro case '\0': 14764565Sgshapiro default: 14864565Sgshapiro#ifdef SETMODE_DEBUG 14938032Speter (void)printf("getmode:%04o -> %04o\n", omode, newmode); 15038032Speter#endif 15138032Speter return (newmode); 15238032Speter } 15338032Speter} 15438032Speter 15538032Speter#define ADDCMD(a, b, c, d) \ 15638032Speter if (set >= endset) { \ 15738032Speter BITCMD *newset; \ 15838032Speter setlen += SET_LEN_INCR; \ 15938032Speter newset = realloc(saveset, sizeof(BITCMD) * setlen); \ 16038032Speter if (!saveset) \ 16194337Sgshapiro return (NULL); \ 16264565Sgshapiro set = newset + (set - saveset); \ 16364565Sgshapiro saveset = newset; \ 16464565Sgshapiro endset = newset + (setlen - 2); \ 16564565Sgshapiro } \ 16638032Speter set = addcmd(set, (a), (b), (c), (d)) 16764565Sgshapiro 16838032Speter#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 16964565Sgshapiro 17064565Sgshapirovoid * 17190795Sgshapirosetmode(p) 17238032Speter const char *p; 17338032Speter{ 17438032Speter int perm, who; 17538032Speter char op; 17638032Speter BITCMD *set, *saveset, *endset; 17738032Speter sigset_t sigset, sigoset; 17838032Speter mode_t mask; 17938032Speter int equalopdone=0, permXbits, setlen; 18038032Speter 18190795Sgshapiro if (!*p) 18238032Speter return (NULL); 18338032Speter 18438032Speter /* 18538032Speter * Get a copy of the mask for the permissions that are mask relative. 18638032Speter * Flip the bits, we want what's not set. Since it's possible that 18738032Speter * the caller is opening files inside a signal handler, protect them 18890795Sgshapiro * as best we can. 18990795Sgshapiro */ 19038032Speter sigfillset(&sigset); 19138032Speter (void)_sigprocmask(SIG_BLOCK, &sigset, &sigoset); 19238032Speter (void)umask(mask = umask(0)); 19338032Speter mask = ~mask; 19438032Speter (void)_sigprocmask(SIG_SETMASK, &sigoset, NULL); 19538032Speter 19664565Sgshapiro setlen = SET_LEN + 2; 19738032Speter 19838032Speter if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL) 19938032Speter return (NULL); 20038032Speter saveset = set; 20138032Speter endset = set + (setlen - 2); 20238032Speter 20338032Speter /* 20438032Speter * If an absolute number, get it and return; disallow non-octal digits 20538032Speter * or illegal bits. 20638032Speter */ 20738032Speter if (isdigit((unsigned char)*p)) { 20838032Speter perm = (mode_t)strtol(p, NULL, 8); 20938032Speter if (perm & ~(STANDARD_BITS|S_ISTXT)) { 21038032Speter free(saveset); 21138032Speter return (NULL); 21238032Speter } 21338032Speter while (*++p) 21490795Sgshapiro if (*p < '0' || *p > '7') { 21590795Sgshapiro free(saveset); 21690795Sgshapiro return (NULL); 21738032Speter } 21838032Speter ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); 21964565Sgshapiro return (saveset); 22090795Sgshapiro } 22190795Sgshapiro 22238032Speter /* 22364565Sgshapiro * Build list of structures to set/clear/copy bits as described by 22438032Speter * each clause of the symbolic mode. 22538032Speter */ 22638032Speter for (;;) { 22738032Speter /* First, find out which bits might be modified. */ 22838032Speter for (who = 0;; ++p) { 22938032Speter switch (*p) { 23038032Speter case 'a': 23138032Speter who |= STANDARD_BITS; 23238032Speter break; 23390795Sgshapiro case 'u': 23438032Speter who |= S_ISUID|S_IRWXU; 23590795Sgshapiro break; 23638032Speter case 'g': 23738032Speter who |= S_ISGID|S_IRWXG; 23838032Speter break; 23938032Speter case 'o': 24090795Sgshapiro who |= S_IRWXO; 24138032Speter break; 24290795Sgshapiro default: 24338032Speter goto getop; 24438032Speter } 24538032Speter } 24638032Speter 24738032Spetergetop: if ((op = *p++) != '+' && op != '-' && op != '=') { 24890795Sgshapiro free(saveset); 24990795Sgshapiro return (NULL); 25090795Sgshapiro } 25190795Sgshapiro if (op == '=') 25290795Sgshapiro equalopdone = 0; 25390795Sgshapiro 25438032Speter who &= ~S_ISTXT; 25590795Sgshapiro for (perm = 0, permXbits = 0;; ++p) { 25690795Sgshapiro switch (*p) { 25790795Sgshapiro case 'r': 25890795Sgshapiro perm |= S_IRUSR|S_IRGRP|S_IROTH; 25990795Sgshapiro break; 26090795Sgshapiro case 's': 26190795Sgshapiro /* If only "other" bits ignore set-id. */ 26290795Sgshapiro if (!who || who & ~S_IRWXO) 26390795Sgshapiro perm |= S_ISUID|S_ISGID; 26490795Sgshapiro break; 26538032Speter case 't': 26690795Sgshapiro /* If only "other" bits ignore sticky. */ 26738032Speter if (!who || who & ~S_IRWXO) { 26838032Speter who |= S_ISTXT; 26938032Speter perm |= S_ISTXT; 27090795Sgshapiro } 27190795Sgshapiro break; 27238032Speter case 'w': 27338032Speter perm |= S_IWUSR|S_IWGRP|S_IWOTH; 27438032Speter break; 27538032Speter case 'X': 27638032Speter permXbits = S_IXUSR|S_IXGRP|S_IXOTH; 27738032Speter break; 27838032Speter case 'x': 27938032Speter perm |= S_IXUSR|S_IXGRP|S_IXOTH; 28038032Speter break; 28138032Speter case 'u': 28238032Speter case 'g': 28338032Speter case 'o': 28438032Speter /* 28538032Speter * When ever we hit 'u', 'g', or 'o', we have 28638032Speter * to flush out any partial mode that we have, 28738032Speter * and then do the copying of the mode bits. 28838032Speter */ 28971348Sgshapiro if (perm) { 29038032Speter ADDCMD(op, who, perm, mask); 29138032Speter perm = 0; 29271348Sgshapiro } 29338032Speter if (op == '=') 29490795Sgshapiro equalopdone = 1; 29538032Speter if (op == '+' && permXbits) { 29690795Sgshapiro ADDCMD('X', who, permXbits, mask); 29790795Sgshapiro permXbits = 0; 29890795Sgshapiro } 29990795Sgshapiro ADDCMD(*p, who, op, mask); 30090795Sgshapiro break; 30190795Sgshapiro 30290795Sgshapiro default: 30338032Speter /* 30438032Speter * Add any permissions that we haven't already 30538032Speter * done. 30638032Speter */ 30738032Speter if (perm || (op == '=' && !equalopdone)) { 30838032Speter if (op == '=') 30938032Speter equalopdone = 1; 31038032Speter ADDCMD(op, who, perm, mask); 31138032Speter perm = 0; 31238032Speter } 31338032Speter if (permXbits) { 31438032Speter ADDCMD('X', who, permXbits, mask); 31564565Sgshapiro permXbits = 0; 31638032Speter } 31738032Speter goto apply; 31838032Speter } 31938032Speter } 32038032Speter 32171348Sgshapiroapply: if (!*p) 32264565Sgshapiro break; 32338032Speter if (*p != ',') 32438032Speter goto getop; 32538032Speter ++p; 32690795Sgshapiro } 32790795Sgshapiro set->cmd = 0; 32890795Sgshapiro#ifdef SETMODE_DEBUG 32990795Sgshapiro (void)printf("Before compress_mode()\n"); 33090795Sgshapiro dumpmode(saveset); 33190795Sgshapiro#endif 33290795Sgshapiro compress_mode(saveset); 33390795Sgshapiro#ifdef SETMODE_DEBUG 33490795Sgshapiro (void)printf("After compress_mode()\n"); 33590795Sgshapiro dumpmode(saveset); 33690795Sgshapiro#endif 33790795Sgshapiro return (saveset); 33890795Sgshapiro} 33964565Sgshapiro 34090795Sgshapirostatic BITCMD * 34164565Sgshapiroaddcmd(set, op, who, oparg, mask) 34264565Sgshapiro BITCMD *set; 34364565Sgshapiro int oparg, who; 34464565Sgshapiro int op; 34564565Sgshapiro u_int mask; 34664565Sgshapiro{ 34738032Speter switch (op) { 34838032Speter case '=': 34990795Sgshapiro set->cmd = '-'; 35064565Sgshapiro set->bits = who ? who : STANDARD_BITS; 35138032Speter set++; 35238032Speter 35338032Speter op = '+'; 35438032Speter /* FALLTHROUGH */ 35538032Speter case '+': 35638032Speter case '-': 35738032Speter case 'X': 35838032Speter set->cmd = op; 35990795Sgshapiro set->bits = (who ? who : mask) & oparg; 36038032Speter break; 36138032Speter 36238032Speter case 'u': 36338032Speter case 'g': 36438032Speter case 'o': 36538032Speter set->cmd = op; 36638032Speter if (who) { 36738032Speter set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | 36838032Speter ((who & S_IRGRP) ? CMD2_GBITS : 0) | 36938032Speter ((who & S_IROTH) ? CMD2_OBITS : 0); 37038032Speter set->bits = (mode_t)~0; 37138032Speter } else { 37238032Speter set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; 37338032Speter set->bits = mask; 37438032Speter } 37538032Speter 37638032Speter if (oparg == '+') 37738032Speter set->cmd2 |= CMD2_SET; 37890795Sgshapiro else if (oparg == '-') 37938032Speter set->cmd2 |= CMD2_CLR; 38038032Speter else if (oparg == '=') 38138032Speter set->cmd2 |= CMD2_SET|CMD2_CLR; 38290795Sgshapiro break; 38390795Sgshapiro } 38438032Speter return (set + 1); 38538032Speter} 38638032Speter 38738032Speter#ifdef SETMODE_DEBUG 38838032Speterstatic void 38938032Speterdumpmode(set) 39038032Speter BITCMD *set; 39138032Speter{ 39294337Sgshapiro for (; set->cmd; ++set) 39338032Speter (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", 39438032Speter set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", 39538032Speter set->cmd2 & CMD2_CLR ? " CLR" : "", 39638032Speter set->cmd2 & CMD2_SET ? " SET" : "", 39738032Speter set->cmd2 & CMD2_UBITS ? " UBITS" : "", 39838032Speter set->cmd2 & CMD2_GBITS ? " GBITS" : "", 39938032Speter set->cmd2 & CMD2_OBITS ? " OBITS" : ""); 40038032Speter} 40138032Speter#endif 402141887Sgshapiro 40390795Sgshapiro/* 404141887Sgshapiro * Given an array of bitcmd structures, compress by compacting consecutive 405141887Sgshapiro * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', 406141887Sgshapiro * 'g' and 'o' commands continue to be separate. They could probably be 407141887Sgshapiro * compacted, but it's not worth the effort. 408141887Sgshapiro */ 409141887Sgshapirostatic void 410141887Sgshapirocompress_mode(set) 411141887Sgshapiro BITCMD *set; 412141887Sgshapiro{ 413141887Sgshapiro BITCMD *nset; 414141887Sgshapiro int setbits, clrbits, Xbits, op; 415141887Sgshapiro 416141887Sgshapiro for (nset = set;;) { 417141887Sgshapiro /* Copy over any 'u', 'g' and 'o' commands. */ 418141887Sgshapiro while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { 419141887Sgshapiro *set++ = *nset++; 420141887Sgshapiro if (!op) 421141887Sgshapiro return; 422141887Sgshapiro } 423141887Sgshapiro 424141887Sgshapiro for (setbits = clrbits = Xbits = 0;; nset++) { 425141887Sgshapiro if ((op = nset->cmd) == '-') { 426141887Sgshapiro clrbits |= nset->bits; 427141887Sgshapiro setbits &= ~nset->bits; 428141887Sgshapiro Xbits &= ~nset->bits; 429141887Sgshapiro } else if (op == '+') { 430141887Sgshapiro setbits |= nset->bits; 431141887Sgshapiro clrbits &= ~nset->bits; 432141887Sgshapiro Xbits &= ~nset->bits; 433141887Sgshapiro } else if (op == 'X') 434141887Sgshapiro Xbits |= nset->bits & ~setbits; 435141887Sgshapiro else 436141887Sgshapiro break; 437141887Sgshapiro } 438141887Sgshapiro if (clrbits) { 439141887Sgshapiro set->cmd = '-'; 440141887Sgshapiro set->cmd2 = 0; 441141887Sgshapiro set->bits = clrbits; 442141887Sgshapiro set++; 443141887Sgshapiro } 444141887Sgshapiro if (setbits) { 445141887Sgshapiro set->cmd = '+'; 446141887Sgshapiro set->cmd2 = 0; 447141887Sgshapiro set->bits = setbits; 448141887Sgshapiro set++; 449141887Sgshapiro } 450141887Sgshapiro if (Xbits) { 451141887Sgshapiro set->cmd = 'X'; 452141887Sgshapiro set->cmd2 = 0; 453141887Sgshapiro set->bits = Xbits; 45490795Sgshapiro set++; 45590795Sgshapiro } 45690795Sgshapiro } 45790795Sgshapiro} 45890795Sgshapiro