minix/lib/libc/gen/setmode.c
Ben Gras 2fe8fb192f Full switch to clang/ELF. Drop ack. Simplify.
There is important information about booting non-ack images in
docs/UPDATING. ack/aout-format images can't be built any more, and
booting clang/ELF-format ones is a little different. Updating to the
new boot monitor is recommended.

Changes in this commit:

	. drop boot monitor -> allowing dropping ack support
	. facility to copy ELF boot files to /boot so that old boot monitor
	  can still boot fairly easily, see UPDATING
	. no more ack-format libraries -> single-case libraries
	. some cleanup of OBJECT_FMT, COMPILER_TYPE, etc cases
	. drop several ack toolchain commands, but not all support
	  commands (e.g. aal is gone but acksize is not yet).
	. a few libc files moved to netbsd libc dir
	. new /bin/date as minix date used code in libc/
	. test compile fix
	. harmonize includes
	. /usr/lib is no longer special: without ack, /usr/lib plays no
	  kind of special bootstrapping role any more and bootstrapping
	  is done exclusively through packages, so releases depend even
	  less on the state of the machine making them now.
	. rename nbsd_lib* to lib*
	. reduce mtree
2012-02-14 14:52:02 +01:00

488 lines
11 KiB
C

/* $NetBSD: setmode.c,v 1.31 2005/10/01 20:08:01 christos 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. 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.31 2005/10/01 20:08:01 christos Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#ifdef SETMODE_DEBUG
#include <stdio.h>
#endif
#ifdef __weak_alias
__weak_alias(getmode,_getmode)
__weak_alias(setmode,_setmode)
#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 __P((BITCMD *, mode_t, mode_t, mode_t, mode_t));
static void compress_mode __P((BITCMD *));
#ifdef SETMODE_DEBUG
static void dumpmode __P((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(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) \
goto out; \
set = newset + (set - saveset); \
saveset = newset; \
endset = newset + (setlen - 2); \
} \
set = addcmd(set, (mode_t)(a), (mode_t)(b), (mode_t)(c), (d)); \
} while (/*CONSTCOND*/0)
#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
void *
setmode(const char *p)
{
int serrno;
char op, *ep;
BITCMD *set, *saveset, *endset;
sigset_t signset, sigoset;
mode_t mask, perm, permXbits, who;
long lval;
int equalopdone = 0; /* pacify gcc */
int setlen;
if (!*p) {
errno = EINVAL;
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(&signset);
(void)sigprocmask(SIG_BLOCK, &signset, &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)) {
errno = 0;
lval = strtol(p, &ep, 8);
if (*ep) {
errno = EINVAL;
goto out;
}
if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
goto out;
if (lval & ~(STANDARD_BITS|S_ISTXT)) {
errno = EINVAL;
goto out;
}
perm = (mode_t)lval;
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 != '=') {
errno = EINVAL;
goto out;
}
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);
out:
serrno = errno;
free(saveset);
errno = serrno;
return NULL;
}
static BITCMD *
addcmd(BITCMD *set, mode_t op, mode_t who, mode_t oparg, mode_t 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++;
}
}
}