source: mainline/uspace/app/ash/setmode.c@ c28a023

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c28a023 was c28a023, checked in by Josef Cejka <malyzelenyhnus@…>, 17 years ago

Initial commit of ash shell.
It cannot be compiled yet.

  • Property mode set to 100644
File size: 11.4 KB
RevLine 
[c28a023]1/* $NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $ */
2
3/*
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Dave Borman at Cray Research, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40#if defined(LIBC_SCCS) && !defined(lint)
41#if 0
42static char sccsid[] = "@(#)setmode.c 8.2 (Berkeley) 3/25/94";
43#else
44__RCSID("$NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $");
45#endif
46#endif /* LIBC_SCCS and not lint */
47
48#include <sys/types.h>
49#include <sys/stat.h>
50
51#include <assert.h>
52#include <ctype.h>
53#include <errno.h>
54#include <signal.h>
55#include <stdlib.h>
56#include <unistd.h>
57
58#ifdef SETMODE_DEBUG
59#include <stdio.h>
60#endif
61
62#ifdef __weak_alias
63__weak_alias(getmode,_getmode)
64__weak_alias(setmode,_setmode)
65#endif
66
67#ifdef __GLIBC__
68#define S_ISTXT __S_ISVTX
69#endif
70
71#define SET_LEN 6 /* initial # of bitcmd struct to malloc */
72#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
73
74typedef struct bitcmd {
75 char cmd;
76 char cmd2;
77 mode_t bits;
78} BITCMD;
79
80#define CMD2_CLR 0x01
81#define CMD2_SET 0x02
82#define CMD2_GBITS 0x04
83#define CMD2_OBITS 0x08
84#define CMD2_UBITS 0x10
85
86static BITCMD *addcmd (BITCMD *, int, int, int, u_int);
87static void compress_mode (BITCMD *);
88#ifdef SETMODE_DEBUG
89static void dumpmode (BITCMD *);
90#endif
91
92/*
93 * Given the old mode and an array of bitcmd structures, apply the operations
94 * described in the bitcmd structures to the old mode, and return the new mode.
95 * Note that there is no '=' command; a strict assignment is just a '-' (clear
96 * bits) followed by a '+' (set bits).
97 */
98mode_t
99getmode(bbox, omode)
100 const void *bbox;
101 mode_t omode;
102{
103 const BITCMD *set;
104 mode_t clrval, newmode, value;
105
106 _DIAGASSERT(bbox != NULL);
107
108 set = (const BITCMD *)bbox;
109 newmode = omode;
110 for (value = 0;; set++)
111 switch(set->cmd) {
112 /*
113 * When copying the user, group or other bits around, we "know"
114 * where the bits are in the mode so that we can do shifts to
115 * copy them around. If we don't use shifts, it gets real
116 * grundgy with lots of single bit checks and bit sets.
117 */
118 case 'u':
119 value = (newmode & S_IRWXU) >> 6;
120 goto common;
121
122 case 'g':
123 value = (newmode & S_IRWXG) >> 3;
124 goto common;
125
126 case 'o':
127 value = newmode & S_IRWXO;
128common: if (set->cmd2 & CMD2_CLR) {
129 clrval =
130 (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
131 if (set->cmd2 & CMD2_UBITS)
132 newmode &= ~((clrval<<6) & set->bits);
133 if (set->cmd2 & CMD2_GBITS)
134 newmode &= ~((clrval<<3) & set->bits);
135 if (set->cmd2 & CMD2_OBITS)
136 newmode &= ~(clrval & set->bits);
137 }
138 if (set->cmd2 & CMD2_SET) {
139 if (set->cmd2 & CMD2_UBITS)
140 newmode |= (value<<6) & set->bits;
141 if (set->cmd2 & CMD2_GBITS)
142 newmode |= (value<<3) & set->bits;
143 if (set->cmd2 & CMD2_OBITS)
144 newmode |= value & set->bits;
145 }
146 break;
147
148 case '+':
149 newmode |= set->bits;
150 break;
151
152 case '-':
153 newmode &= ~set->bits;
154 break;
155
156 case 'X':
157 if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
158 newmode |= set->bits;
159 break;
160
161 case '\0':
162 default:
163#ifdef SETMODE_DEBUG
164 (void)printf("getmode:%04o -> %04o\n", omode, newmode);
165#endif
166 return (newmode);
167 }
168}
169
170#define ADDCMD(a, b, c, d) do { \
171 if (set >= endset) { \
172 BITCMD *newset; \
173 setlen += SET_LEN_INCR; \
174 newset = realloc(saveset, sizeof(BITCMD) * setlen); \
175 if (newset == NULL) { \
176 free(saveset); \
177 return (NULL); \
178 } \
179 set = newset + (set - saveset); \
180 saveset = newset; \
181 endset = newset + (setlen - 2); \
182 } \
183 set = addcmd(set, (a), (b), (c), (d)); \
184} while (/*CONSTCOND*/0)
185
186#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
187
188void *
189setmode(p)
190 const char *p;
191{
192 int perm, who;
193 char op, *ep;
194 BITCMD *set, *saveset, *endset;
195 sigset_t sigset, sigoset;
196 mode_t mask;
197 int equalopdone = 0; /* pacify gcc */
198 int permXbits, setlen;
199
200 if (!*p)
201 return (NULL);
202
203 /*
204 * Get a copy of the mask for the permissions that are mask relative.
205 * Flip the bits, we want what's not set. Since it's possible that
206 * the caller is opening files inside a signal handler, protect them
207 * as best we can.
208 */
209 sigfillset(&sigset);
210 (void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);
211 (void)umask(mask = umask(0));
212 mask = ~mask;
213 (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
214
215 setlen = SET_LEN + 2;
216
217 if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
218 return (NULL);
219 saveset = set;
220 endset = set + (setlen - 2);
221
222 /*
223 * If an absolute number, get it and return; disallow non-octal digits
224 * or illegal bits.
225 */
226 if (isdigit((unsigned char)*p)) {
227 perm = (mode_t)strtol(p, &ep, 8);
228 if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
229 free(saveset);
230 return (NULL);
231 }
232 ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
233 set->cmd = 0;
234 return (saveset);
235 }
236
237 /*
238 * Build list of structures to set/clear/copy bits as described by
239 * each clause of the symbolic mode.
240 */
241 for (;;) {
242 /* First, find out which bits might be modified. */
243 for (who = 0;; ++p) {
244 switch (*p) {
245 case 'a':
246 who |= STANDARD_BITS;
247 break;
248 case 'u':
249 who |= S_ISUID|S_IRWXU;
250 break;
251 case 'g':
252 who |= S_ISGID|S_IRWXG;
253 break;
254 case 'o':
255 who |= S_IRWXO;
256 break;
257 default:
258 goto getop;
259 }
260 }
261
262getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
263 free(saveset);
264 return (NULL);
265 }
266 if (op == '=')
267 equalopdone = 0;
268
269 who &= ~S_ISTXT;
270 for (perm = 0, permXbits = 0;; ++p) {
271 switch (*p) {
272 case 'r':
273 perm |= S_IRUSR|S_IRGRP|S_IROTH;
274 break;
275 case 's':
276 /*
277 * If specific bits where requested and
278 * only "other" bits ignore set-id.
279 */
280 if (who == 0 || (who & ~S_IRWXO))
281 perm |= S_ISUID|S_ISGID;
282 break;
283 case 't':
284 /*
285 * If specific bits where requested and
286 * only "other" bits ignore set-id.
287 */
288 if (who == 0 || (who & ~S_IRWXO)) {
289 who |= S_ISTXT;
290 perm |= S_ISTXT;
291 }
292 break;
293 case 'w':
294 perm |= S_IWUSR|S_IWGRP|S_IWOTH;
295 break;
296 case 'X':
297 permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
298 break;
299 case 'x':
300 perm |= S_IXUSR|S_IXGRP|S_IXOTH;
301 break;
302 case 'u':
303 case 'g':
304 case 'o':
305 /*
306 * When ever we hit 'u', 'g', or 'o', we have
307 * to flush out any partial mode that we have,
308 * and then do the copying of the mode bits.
309 */
310 if (perm) {
311 ADDCMD(op, who, perm, mask);
312 perm = 0;
313 }
314 if (op == '=')
315 equalopdone = 1;
316 if (op == '+' && permXbits) {
317 ADDCMD('X', who, permXbits, mask);
318 permXbits = 0;
319 }
320 ADDCMD(*p, who, op, mask);
321 break;
322
323 default:
324 /*
325 * Add any permissions that we haven't already
326 * done.
327 */
328 if (perm || (op == '=' && !equalopdone)) {
329 if (op == '=')
330 equalopdone = 1;
331 ADDCMD(op, who, perm, mask);
332 perm = 0;
333 }
334 if (permXbits) {
335 ADDCMD('X', who, permXbits, mask);
336 permXbits = 0;
337 }
338 goto apply;
339 }
340 }
341
342apply: if (!*p)
343 break;
344 if (*p != ',')
345 goto getop;
346 ++p;
347 }
348 set->cmd = 0;
349#ifdef SETMODE_DEBUG
350 (void)printf("Before compress_mode()\n");
351 dumpmode(saveset);
352#endif
353 compress_mode(saveset);
354#ifdef SETMODE_DEBUG
355 (void)printf("After compress_mode()\n");
356 dumpmode(saveset);
357#endif
358 return (saveset);
359}
360
361static BITCMD *
362addcmd(set, op, who, oparg, mask)
363 BITCMD *set;
364 int oparg, who;
365 int op;
366 u_int mask;
367{
368
369 _DIAGASSERT(set != NULL);
370
371 switch (op) {
372 case '=':
373 set->cmd = '-';
374 set->bits = who ? who : STANDARD_BITS;
375 set++;
376
377 op = '+';
378 /* FALLTHROUGH */
379 case '+':
380 case '-':
381 case 'X':
382 set->cmd = op;
383 set->bits = (who ? who : mask) & oparg;
384 break;
385
386 case 'u':
387 case 'g':
388 case 'o':
389 set->cmd = op;
390 if (who) {
391 set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
392 ((who & S_IRGRP) ? CMD2_GBITS : 0) |
393 ((who & S_IROTH) ? CMD2_OBITS : 0);
394 set->bits = (mode_t)~0;
395 } else {
396 set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
397 set->bits = mask;
398 }
399
400 if (oparg == '+')
401 set->cmd2 |= CMD2_SET;
402 else if (oparg == '-')
403 set->cmd2 |= CMD2_CLR;
404 else if (oparg == '=')
405 set->cmd2 |= CMD2_SET|CMD2_CLR;
406 break;
407 }
408 return (set + 1);
409}
410
411#ifdef SETMODE_DEBUG
412static void
413dumpmode(set)
414 BITCMD *set;
415{
416
417 _DIAGASSERT(set != NULL);
418
419 for (; set->cmd; ++set)
420 (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
421 set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
422 set->cmd2 & CMD2_CLR ? " CLR" : "",
423 set->cmd2 & CMD2_SET ? " SET" : "",
424 set->cmd2 & CMD2_UBITS ? " UBITS" : "",
425 set->cmd2 & CMD2_GBITS ? " GBITS" : "",
426 set->cmd2 & CMD2_OBITS ? " OBITS" : "");
427}
428#endif
429
430/*
431 * Given an array of bitcmd structures, compress by compacting consecutive
432 * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
433 * 'g' and 'o' commands continue to be separate. They could probably be
434 * compacted, but it's not worth the effort.
435 */
436static void
437compress_mode(set)
438 BITCMD *set;
439{
440 BITCMD *nset;
441 int setbits, clrbits, Xbits, op;
442
443 _DIAGASSERT(set != NULL);
444
445 for (nset = set;;) {
446 /* Copy over any 'u', 'g' and 'o' commands. */
447 while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
448 *set++ = *nset++;
449 if (!op)
450 return;
451 }
452
453 for (setbits = clrbits = Xbits = 0;; nset++) {
454 if ((op = nset->cmd) == '-') {
455 clrbits |= nset->bits;
456 setbits &= ~nset->bits;
457 Xbits &= ~nset->bits;
458 } else if (op == '+') {
459 setbits |= nset->bits;
460 clrbits &= ~nset->bits;
461 Xbits &= ~nset->bits;
462 } else if (op == 'X')
463 Xbits |= nset->bits & ~setbits;
464 else
465 break;
466 }
467 if (clrbits) {
468 set->cmd = '-';
469 set->cmd2 = 0;
470 set->bits = clrbits;
471 set++;
472 }
473 if (setbits) {
474 set->cmd = '+';
475 set->cmd2 = 0;
476 set->bits = setbits;
477 set++;
478 }
479 if (Xbits) {
480 set->cmd = 'X';
481 set->cmd2 = 0;
482 set->bits = Xbits;
483 set++;
484 }
485 }
486}
Note: See TracBrowser for help on using the repository browser.