a209c3ae12
This patch fixes most of current reasons to generate compiler warnings. The changes consist of: - adding missing casts - hiding or unhiding function declarations - including headers where missing - add __UNCONST when assigning a const char * to a char * - adding missing return statements - changing some types from unsigned to signed, as the code seems to want signed ints - converting old-style function definitions to current style (i.e., void func(param1, param2) short param1, param2; {...} to void func (short param1, short param2) {...}) - making the compiler silent about signed vs unsigned comparisons. We have too many of those in the new libc to fix. A number of bugs in the test set were fixed. These bugs were never triggered with our old libc. Consequently, these tests are now forced to link with the new libc or they will generate errors (in particular tests 43 and 55). Most changes in NetBSD libc are limited to moving aroudn "#ifndef __minix" or stuff related to Minix-specific things (code in sys-minix or gen/minix).
487 lines
11 KiB
C
487 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++;
|
|
}
|
|
}
|
|
}
|