Path: blob/main/core/posix-wasm/src/lib/bsd/setmode.c
1067 views
/* $OpenBSD: setmode.c,v 1.22 2014/10/11 04:14:35 deraadt Exp $ */1/* $NetBSD: setmode.c,v 1.15 1997/02/07 22:21:06 christos Exp $ */23/*4* Copyright (c) 1989, 1993, 19945* The Regents of the University of California. All rights reserved.6*7* This code is derived from software contributed to Berkeley by8* Dave Borman at Cray Research, Inc.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18* 3. Neither the name of the University nor the names of its contributors19* may be used to endorse or promote products derived from this software20* without specific prior written permission.21*22* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND23* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE24* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE25* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE26* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL27* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS28* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)29* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT30* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY31* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF32* SUCH DAMAGE.33*/3435#include "posix-wasm.h"36#include <sys/types.h>37#include <sys/stat.h>3839#include <ctype.h>40#include <errno.h>41#include <signal.h>42#include <stdlib.h>43#include <unistd.h>4445#ifdef SETMODE_DEBUG46#include <stdio.h>47#endif4849#define SET_LEN 6 /* initial # of bitcmd struct to malloc */50#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */5152typedef struct bitcmd {53char cmd;54char cmd2;55mode_t bits;56} BITCMD;5758#define CMD2_CLR 0x0159#define CMD2_SET 0x0260#define CMD2_GBITS 0x0461#define CMD2_OBITS 0x0862#define CMD2_UBITS 0x106364static BITCMD *addcmd(BITCMD *, int, int, int, u_int);65static void compress_mode(BITCMD *);66#ifdef SETMODE_DEBUG67static void dumpmode(BITCMD *);68#endif6970/*71* Given the old mode and an array of bitcmd structures, apply the operations72* described in the bitcmd structures to the old mode, and return the new mode.73* Note that there is no '=' command; a strict assignment is just a '-' (clear74* bits) followed by a '+' (set bits).75*/76mode_t77getmode(const void *bbox, mode_t omode)78{79const BITCMD *set;80mode_t clrval, newmode, value;8182set = (const BITCMD *)bbox;83newmode = omode;84for (value = 0;; set++)85switch(set->cmd) {86/*87* When copying the user, group or other bits around, we "know"88* where the bits are in the mode so that we can do shifts to89* copy them around. If we don't use shifts, it gets real90* grundgy with lots of single bit checks and bit sets.91*/92case 'u':93value = (newmode & S_IRWXU) >> 6;94goto common;9596case 'g':97value = (newmode & S_IRWXG) >> 3;98goto common;99100case 'o':101value = newmode & S_IRWXO;102common: if (set->cmd2 & CMD2_CLR) {103clrval =104(set->cmd2 & CMD2_SET) ? S_IRWXO : value;105if (set->cmd2 & CMD2_UBITS)106newmode &= ~((clrval<<6) & set->bits);107if (set->cmd2 & CMD2_GBITS)108newmode &= ~((clrval<<3) & set->bits);109if (set->cmd2 & CMD2_OBITS)110newmode &= ~(clrval & set->bits);111}112if (set->cmd2 & CMD2_SET) {113if (set->cmd2 & CMD2_UBITS)114newmode |= (value<<6) & set->bits;115if (set->cmd2 & CMD2_GBITS)116newmode |= (value<<3) & set->bits;117if (set->cmd2 & CMD2_OBITS)118newmode |= value & set->bits;119}120break;121122case '+':123newmode |= set->bits;124break;125126case '-':127newmode &= ~set->bits;128break;129130case 'X':131if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))132newmode |= set->bits;133break;134135case '\0':136default:137#ifdef SETMODE_DEBUG138(void)printf("getmode:%04o -> %04o\n", omode, newmode);139#endif140return (newmode);141}142}143144#define ADDCMD(a, b, c, d) \145if (set >= endset) { \146BITCMD *newset; \147setlen += SET_LEN_INCR; \148newset = reallocarray(saveset, setlen, sizeof(BITCMD)); \149if (newset == NULL) { \150free(saveset); \151return (NULL); \152} \153set = newset + (set - saveset); \154saveset = newset; \155endset = newset + (setlen - 2); \156} \157set = addcmd(set, (a), (b), (c), (d))158159#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)160161void *162setmode(const char *p)163{164char op, *ep;165BITCMD *set, *saveset, *endset;166sigset_t sigset, sigoset;167mode_t mask, perm, permXbits, who;168int equalopdone, setlen;169u_long perml;170171if (!*p) {172errno = EINVAL;173return (NULL);174}175176/*177* Get a copy of the mask for the permissions that are mask relative.178* Flip the bits, we want what's not set. Since it's possible that179* the caller is opening files inside a signal handler, protect them180* as best we can.181*/182sigfillset(&sigset);183(void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);184(void)umask(mask = umask(0));185mask = ~mask;186(void)sigprocmask(SIG_SETMASK, &sigoset, NULL);187188setlen = SET_LEN + 2;189190if ((set = calloc((u_int)sizeof(BITCMD), setlen)) == NULL)191return (NULL);192saveset = set;193endset = set + (setlen - 2);194195/*196* If an absolute number, get it and return; disallow non-octal digits197* or illegal bits.198*/199if (isdigit((unsigned char)*p)) {200perml = strtoul(p, &ep, 8);201/* The test on perml will also catch overflow. */202if (*ep != '\0' || (perml & ~(STANDARD_BITS|S_ISTXT))) {203free(saveset);204errno = ERANGE;205return (NULL);206}207perm = (mode_t)perml;208ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);209set->cmd = 0;210return (saveset);211}212213/*214* Build list of structures to set/clear/copy bits as described by215* each clause of the symbolic mode.216*/217for (;;) {218/* First, find out which bits might be modified. */219for (who = 0;; ++p) {220switch (*p) {221case 'a':222who |= STANDARD_BITS;223break;224case 'u':225who |= S_ISUID|S_IRWXU;226break;227case 'g':228who |= S_ISGID|S_IRWXG;229break;230case 'o':231who |= S_IRWXO;232break;233default:234goto getop;235}236}237238getop: if ((op = *p++) != '+' && op != '-' && op != '=') {239free(saveset);240errno = EINVAL;241return (NULL);242}243if (op == '=')244equalopdone = 0;245246who &= ~S_ISTXT;247for (perm = 0, permXbits = 0;; ++p) {248switch (*p) {249case 'r':250perm |= S_IRUSR|S_IRGRP|S_IROTH;251break;252case 's':253/*254* If specific bits where requested and255* only "other" bits ignore set-id.256*/257if (who == 0 || (who & ~S_IRWXO))258perm |= S_ISUID|S_ISGID;259break;260case 't':261/*262* If specific bits where requested and263* only "other" bits ignore sticky.264*/265if (who == 0 || (who & ~S_IRWXO)) {266who |= S_ISTXT;267perm |= S_ISTXT;268}269break;270case 'w':271perm |= S_IWUSR|S_IWGRP|S_IWOTH;272break;273case 'X':274permXbits = S_IXUSR|S_IXGRP|S_IXOTH;275break;276case 'x':277perm |= S_IXUSR|S_IXGRP|S_IXOTH;278break;279case 'u':280case 'g':281case 'o':282/*283* When ever we hit 'u', 'g', or 'o', we have284* to flush out any partial mode that we have,285* and then do the copying of the mode bits.286*/287if (perm) {288ADDCMD(op, who, perm, mask);289perm = 0;290}291if (op == '=')292equalopdone = 1;293if (op == '+' && permXbits) {294ADDCMD('X', who, permXbits, mask);295permXbits = 0;296}297ADDCMD(*p, who, op, mask);298break;299300default:301/*302* Add any permissions that we haven't already303* done.304*/305if (perm || (op == '=' && !equalopdone)) {306if (op == '=')307equalopdone = 1;308ADDCMD(op, who, perm, mask);309perm = 0;310}311if (permXbits) {312ADDCMD('X', who, permXbits, mask);313permXbits = 0;314}315goto apply;316}317}318319apply: if (!*p)320break;321if (*p != ',')322goto getop;323++p;324}325set->cmd = 0;326#ifdef SETMODE_DEBUG327(void)printf("Before compress_mode()\n");328dumpmode(saveset);329#endif330compress_mode(saveset);331#ifdef SETMODE_DEBUG332(void)printf("After compress_mode()\n");333dumpmode(saveset);334#endif335return (saveset);336}337338static BITCMD *339addcmd(BITCMD *set, int op, int who, int oparg, u_int mask)340{341switch (op) {342case '=':343set->cmd = '-';344set->bits = who ? who : STANDARD_BITS;345set++;346347op = '+';348/* FALLTHROUGH */349case '+':350case '-':351case 'X':352set->cmd = op;353set->bits = (who ? who : mask) & oparg;354break;355356case 'u':357case 'g':358case 'o':359set->cmd = op;360if (who) {361set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |362((who & S_IRGRP) ? CMD2_GBITS : 0) |363((who & S_IROTH) ? CMD2_OBITS : 0);364set->bits = (mode_t)~0;365} else {366set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;367set->bits = mask;368}369370if (oparg == '+')371set->cmd2 |= CMD2_SET;372else if (oparg == '-')373set->cmd2 |= CMD2_CLR;374else if (oparg == '=')375set->cmd2 |= CMD2_SET|CMD2_CLR;376break;377}378return (set + 1);379}380381#ifdef SETMODE_DEBUG382static void383dumpmode(BITCMD *set)384{385for (; set->cmd; ++set)386(void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",387set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",388set->cmd2 & CMD2_CLR ? " CLR" : "",389set->cmd2 & CMD2_SET ? " SET" : "",390set->cmd2 & CMD2_UBITS ? " UBITS" : "",391set->cmd2 & CMD2_GBITS ? " GBITS" : "",392set->cmd2 & CMD2_OBITS ? " OBITS" : "");393}394#endif395396/*397* Given an array of bitcmd structures, compress by compacting consecutive398* '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',399* 'g' and 'o' commands continue to be separate. They could probably be400* compacted, but it's not worth the effort.401*/402static void403compress_mode(BITCMD *set)404{405BITCMD *nset;406int setbits, clrbits, Xbits, op;407408for (nset = set;;) {409/* Copy over any 'u', 'g' and 'o' commands. */410while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {411*set++ = *nset++;412if (!op)413return;414}415416for (setbits = clrbits = Xbits = 0;; nset++) {417if ((op = nset->cmd) == '-') {418clrbits |= nset->bits;419setbits &= ~nset->bits;420Xbits &= ~nset->bits;421} else if (op == '+') {422setbits |= nset->bits;423clrbits &= ~nset->bits;424Xbits &= ~nset->bits;425} else if (op == 'X')426Xbits |= nset->bits & ~setbits;427else428break;429}430if (clrbits) {431set->cmd = '-';432set->cmd2 = 0;433set->bits = clrbits;434set++;435}436if (setbits) {437set->cmd = '+';438set->cmd2 = 0;439set->bits = setbits;440set++;441}442if (Xbits) {443set->cmd = 'X';444set->cmd2 = 0;445set->bits = Xbits;446set++;447}448}449}450451452