0,0 → 1,486 |
/* $NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $ */ |
|
/* |
* Copyright (c) 1989, 1993, 1994 |
* The Regents of the University of California. All rights reserved. |
* |
* This code is derived from software contributed to Berkeley by |
* Dave Borman at Cray Research, Inc. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* 3. All advertising materials mentioning features or use of this software |
* must display the following acknowledgement: |
* This product includes software developed by the University of |
* California, Berkeley and its contributors. |
* 4. Neither the name of the University nor the names of its contributors |
* may be used to endorse or promote products derived from this software |
* without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
*/ |
|
#include <sys/cdefs.h> |
#if defined(LIBC_SCCS) && !defined(lint) |
#if 0 |
static char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94"; |
#else |
__RCSID("$NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $"); |
#endif |
#endif /* LIBC_SCCS and not lint */ |
|
#include <sys/types.h> |
#include <sys/stat.h> |
|
#include <assert.h> |
#include <ctype.h> |
#include <errno.h> |
#include <signal.h> |
#include <stdlib.h> |
#include <unistd.h> |
|
#ifdef SETMODE_DEBUG |
#include <stdio.h> |
#endif |
|
#ifdef __weak_alias |
__weak_alias(getmode,_getmode) |
__weak_alias(setmode,_setmode) |
#endif |
|
#ifdef __GLIBC__ |
#define S_ISTXT __S_ISVTX |
#endif |
|
#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ |
#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ |
|
typedef struct bitcmd { |
char cmd; |
char cmd2; |
mode_t bits; |
} BITCMD; |
|
#define CMD2_CLR 0x01 |
#define CMD2_SET 0x02 |
#define CMD2_GBITS 0x04 |
#define CMD2_OBITS 0x08 |
#define CMD2_UBITS 0x10 |
|
static BITCMD *addcmd (BITCMD *, int, int, int, u_int); |
static void compress_mode (BITCMD *); |
#ifdef SETMODE_DEBUG |
static void dumpmode (BITCMD *); |
#endif |
|
/* |
* Given the old mode and an array of bitcmd structures, apply the operations |
* described in the bitcmd structures to the old mode, and return the new mode. |
* Note that there is no '=' command; a strict assignment is just a '-' (clear |
* bits) followed by a '+' (set bits). |
*/ |
mode_t |
getmode(bbox, omode) |
const void *bbox; |
mode_t omode; |
{ |
const BITCMD *set; |
mode_t clrval, newmode, value; |
|
_DIAGASSERT(bbox != NULL); |
|
set = (const BITCMD *)bbox; |
newmode = omode; |
for (value = 0;; set++) |
switch(set->cmd) { |
/* |
* When copying the user, group or other bits around, we "know" |
* where the bits are in the mode so that we can do shifts to |
* copy them around. If we don't use shifts, it gets real |
* grundgy with lots of single bit checks and bit sets. |
*/ |
case 'u': |
value = (newmode & S_IRWXU) >> 6; |
goto common; |
|
case 'g': |
value = (newmode & S_IRWXG) >> 3; |
goto common; |
|
case 'o': |
value = newmode & S_IRWXO; |
common: if (set->cmd2 & CMD2_CLR) { |
clrval = |
(set->cmd2 & CMD2_SET) ? S_IRWXO : value; |
if (set->cmd2 & CMD2_UBITS) |
newmode &= ~((clrval<<6) & set->bits); |
if (set->cmd2 & CMD2_GBITS) |
newmode &= ~((clrval<<3) & set->bits); |
if (set->cmd2 & CMD2_OBITS) |
newmode &= ~(clrval & set->bits); |
} |
if (set->cmd2 & CMD2_SET) { |
if (set->cmd2 & CMD2_UBITS) |
newmode |= (value<<6) & set->bits; |
if (set->cmd2 & CMD2_GBITS) |
newmode |= (value<<3) & set->bits; |
if (set->cmd2 & CMD2_OBITS) |
newmode |= value & set->bits; |
} |
break; |
|
case '+': |
newmode |= set->bits; |
break; |
|
case '-': |
newmode &= ~set->bits; |
break; |
|
case 'X': |
if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) |
newmode |= set->bits; |
break; |
|
case '\0': |
default: |
#ifdef SETMODE_DEBUG |
(void)printf("getmode:%04o -> %04o\n", omode, newmode); |
#endif |
return (newmode); |
} |
} |
|
#define ADDCMD(a, b, c, d) do { \ |
if (set >= endset) { \ |
BITCMD *newset; \ |
setlen += SET_LEN_INCR; \ |
newset = realloc(saveset, sizeof(BITCMD) * setlen); \ |
if (newset == NULL) { \ |
free(saveset); \ |
return (NULL); \ |
} \ |
set = newset + (set - saveset); \ |
saveset = newset; \ |
endset = newset + (setlen - 2); \ |
} \ |
set = addcmd(set, (a), (b), (c), (d)); \ |
} while (/*CONSTCOND*/0) |
|
#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) |
|
void * |
setmode(p) |
const char *p; |
{ |
int perm, who; |
char op, *ep; |
BITCMD *set, *saveset, *endset; |
sigset_t sigset, sigoset; |
mode_t mask; |
int equalopdone = 0; /* pacify gcc */ |
int permXbits, setlen; |
|
if (!*p) |
return (NULL); |
|
/* |
* Get a copy of the mask for the permissions that are mask relative. |
* Flip the bits, we want what's not set. Since it's possible that |
* the caller is opening files inside a signal handler, protect them |
* as best we can. |
*/ |
sigfillset(&sigset); |
(void)sigprocmask(SIG_BLOCK, &sigset, &sigoset); |
(void)umask(mask = umask(0)); |
mask = ~mask; |
(void)sigprocmask(SIG_SETMASK, &sigoset, NULL); |
|
setlen = SET_LEN + 2; |
|
if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL) |
return (NULL); |
saveset = set; |
endset = set + (setlen - 2); |
|
/* |
* If an absolute number, get it and return; disallow non-octal digits |
* or illegal bits. |
*/ |
if (isdigit((unsigned char)*p)) { |
perm = (mode_t)strtol(p, &ep, 8); |
if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) { |
free(saveset); |
return (NULL); |
} |
ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask); |
set->cmd = 0; |
return (saveset); |
} |
|
/* |
* Build list of structures to set/clear/copy bits as described by |
* each clause of the symbolic mode. |
*/ |
for (;;) { |
/* First, find out which bits might be modified. */ |
for (who = 0;; ++p) { |
switch (*p) { |
case 'a': |
who |= STANDARD_BITS; |
break; |
case 'u': |
who |= S_ISUID|S_IRWXU; |
break; |
case 'g': |
who |= S_ISGID|S_IRWXG; |
break; |
case 'o': |
who |= S_IRWXO; |
break; |
default: |
goto getop; |
} |
} |
|
getop: if ((op = *p++) != '+' && op != '-' && op != '=') { |
free(saveset); |
return (NULL); |
} |
if (op == '=') |
equalopdone = 0; |
|
who &= ~S_ISTXT; |
for (perm = 0, permXbits = 0;; ++p) { |
switch (*p) { |
case 'r': |
perm |= S_IRUSR|S_IRGRP|S_IROTH; |
break; |
case 's': |
/* |
* If specific bits where requested and |
* only "other" bits ignore set-id. |
*/ |
if (who == 0 || (who & ~S_IRWXO)) |
perm |= S_ISUID|S_ISGID; |
break; |
case 't': |
/* |
* If specific bits where requested and |
* only "other" bits ignore set-id. |
*/ |
if (who == 0 || (who & ~S_IRWXO)) { |
who |= S_ISTXT; |
perm |= S_ISTXT; |
} |
break; |
case 'w': |
perm |= S_IWUSR|S_IWGRP|S_IWOTH; |
break; |
case 'X': |
permXbits = S_IXUSR|S_IXGRP|S_IXOTH; |
break; |
case 'x': |
perm |= S_IXUSR|S_IXGRP|S_IXOTH; |
break; |
case 'u': |
case 'g': |
case 'o': |
/* |
* When ever we hit 'u', 'g', or 'o', we have |
* to flush out any partial mode that we have, |
* and then do the copying of the mode bits. |
*/ |
if (perm) { |
ADDCMD(op, who, perm, mask); |
perm = 0; |
} |
if (op == '=') |
equalopdone = 1; |
if (op == '+' && permXbits) { |
ADDCMD('X', who, permXbits, mask); |
permXbits = 0; |
} |
ADDCMD(*p, who, op, mask); |
break; |
|
default: |
/* |
* Add any permissions that we haven't already |
* done. |
*/ |
if (perm || (op == '=' && !equalopdone)) { |
if (op == '=') |
equalopdone = 1; |
ADDCMD(op, who, perm, mask); |
perm = 0; |
} |
if (permXbits) { |
ADDCMD('X', who, permXbits, mask); |
permXbits = 0; |
} |
goto apply; |
} |
} |
|
apply: if (!*p) |
break; |
if (*p != ',') |
goto getop; |
++p; |
} |
set->cmd = 0; |
#ifdef SETMODE_DEBUG |
(void)printf("Before compress_mode()\n"); |
dumpmode(saveset); |
#endif |
compress_mode(saveset); |
#ifdef SETMODE_DEBUG |
(void)printf("After compress_mode()\n"); |
dumpmode(saveset); |
#endif |
return (saveset); |
} |
|
static BITCMD * |
addcmd(set, op, who, oparg, mask) |
BITCMD *set; |
int oparg, who; |
int op; |
u_int mask; |
{ |
|
_DIAGASSERT(set != NULL); |
|
switch (op) { |
case '=': |
set->cmd = '-'; |
set->bits = who ? who : STANDARD_BITS; |
set++; |
|
op = '+'; |
/* FALLTHROUGH */ |
case '+': |
case '-': |
case 'X': |
set->cmd = op; |
set->bits = (who ? who : mask) & oparg; |
break; |
|
case 'u': |
case 'g': |
case 'o': |
set->cmd = op; |
if (who) { |
set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | |
((who & S_IRGRP) ? CMD2_GBITS : 0) | |
((who & S_IROTH) ? CMD2_OBITS : 0); |
set->bits = (mode_t)~0; |
} else { |
set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; |
set->bits = mask; |
} |
|
if (oparg == '+') |
set->cmd2 |= CMD2_SET; |
else if (oparg == '-') |
set->cmd2 |= CMD2_CLR; |
else if (oparg == '=') |
set->cmd2 |= CMD2_SET|CMD2_CLR; |
break; |
} |
return (set + 1); |
} |
|
#ifdef SETMODE_DEBUG |
static void |
dumpmode(set) |
BITCMD *set; |
{ |
|
_DIAGASSERT(set != NULL); |
|
for (; set->cmd; ++set) |
(void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", |
set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", |
set->cmd2 & CMD2_CLR ? " CLR" : "", |
set->cmd2 & CMD2_SET ? " SET" : "", |
set->cmd2 & CMD2_UBITS ? " UBITS" : "", |
set->cmd2 & CMD2_GBITS ? " GBITS" : "", |
set->cmd2 & CMD2_OBITS ? " OBITS" : ""); |
} |
#endif |
|
/* |
* Given an array of bitcmd structures, compress by compacting consecutive |
* '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', |
* 'g' and 'o' commands continue to be separate. They could probably be |
* compacted, but it's not worth the effort. |
*/ |
static void |
compress_mode(set) |
BITCMD *set; |
{ |
BITCMD *nset; |
int setbits, clrbits, Xbits, op; |
|
_DIAGASSERT(set != NULL); |
|
for (nset = set;;) { |
/* Copy over any 'u', 'g' and 'o' commands. */ |
while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { |
*set++ = *nset++; |
if (!op) |
return; |
} |
|
for (setbits = clrbits = Xbits = 0;; nset++) { |
if ((op = nset->cmd) == '-') { |
clrbits |= nset->bits; |
setbits &= ~nset->bits; |
Xbits &= ~nset->bits; |
} else if (op == '+') { |
setbits |= nset->bits; |
clrbits &= ~nset->bits; |
Xbits &= ~nset->bits; |
} else if (op == 'X') |
Xbits |= nset->bits & ~setbits; |
else |
break; |
} |
if (clrbits) { |
set->cmd = '-'; |
set->cmd2 = 0; |
set->bits = clrbits; |
set++; |
} |
if (setbits) { |
set->cmd = '+'; |
set->cmd2 = 0; |
set->bits = setbits; |
set++; |
} |
if (Xbits) { |
set->cmd = 'X'; |
set->cmd2 = 0; |
set->bits = Xbits; |
set++; |
} |
} |
} |