Initial public busybox upstream commit
[busybox4maemo] / libbb / parse_mode.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * parse_mode implementation for busybox
4  *
5  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
11
12 #include "libbb.h"
13
14 /* This function is used from NOFORK applets. It must not allocate anything */
15
16 #define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
17
18 int bb_parse_mode(const char *s, mode_t *current_mode)
19 {
20         static const mode_t who_mask[] = {
21                 S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
22                 S_ISUID | S_IRWXU,           /* u */
23                 S_ISGID | S_IRWXG,           /* g */
24                 S_IRWXO                      /* o */
25         };
26         static const mode_t perm_mask[] = {
27                 S_IRUSR | S_IRGRP | S_IROTH, /* r */
28                 S_IWUSR | S_IWGRP | S_IWOTH, /* w */
29                 S_IXUSR | S_IXGRP | S_IXOTH, /* x */
30                 S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
31                 S_ISUID | S_ISGID,           /* s */
32                 S_ISVTX                      /* t */
33         };
34         static const char who_chars[] ALIGN1 = "augo";
35         static const char perm_chars[] ALIGN1 = "rwxXst";
36
37         const char *p;
38         mode_t wholist;
39         mode_t permlist;
40         mode_t new_mode;
41         char op;
42
43         if (((unsigned int)(*s - '0')) < 8) {
44                 unsigned long tmp;
45                 char *e;
46
47                 tmp = strtoul(s, &e, 8);
48                 if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
49                         return 0;
50                 }
51                 *current_mode = tmp;
52                 return 1;
53         }
54
55         new_mode = *current_mode;
56
57         /* Note: we allow empty clauses, and hence empty modes.
58          * We treat an empty mode as no change to perms. */
59
60         while (*s) {    /* Process clauses. */
61                 if (*s == ',') {        /* We allow empty clauses. */
62                         ++s;
63                         continue;
64                 }
65
66                 /* Get a wholist. */
67                 wholist = 0;
68  WHO_LIST:
69                 p = who_chars;
70                 do {
71                         if (*p == *s) {
72                                 wholist |= who_mask[(int)(p-who_chars)];
73                                 if (!*++s) {
74                                         return 0;
75                                 }
76                                 goto WHO_LIST;
77                         }
78                 } while (*++p);
79
80                 do {    /* Process action list. */
81                         if ((*s != '+') && (*s != '-')) {
82                                 if (*s != '=') {
83                                         return 0;
84                                 }
85                                 /* Since op is '=', clear all bits corresponding to the
86                                  * wholist, or all file bits if wholist is empty. */
87                                 permlist = ~FILEMODEBITS;
88                                 if (wholist) {
89                                         permlist = ~wholist;
90                                 }
91                                 new_mode &= permlist;
92                         }
93                         op = *s++;
94
95                         /* Check for permcopy. */
96                         p = who_chars + 1;      /* Skip 'a' entry. */
97                         do {
98                                 if (*p == *s) {
99                                         int i = 0;
100                                         permlist = who_mask[(int)(p-who_chars)]
101                                                          & (S_IRWXU | S_IRWXG | S_IRWXO)
102                                                          & new_mode;
103                                         do {
104                                                 if (permlist & perm_mask[i]) {
105                                                         permlist |= perm_mask[i];
106                                                 }
107                                         } while (++i < 3);
108                                         ++s;
109                                         goto GOT_ACTION;
110                                 }
111                         } while (*++p);
112
113                         /* It was not a permcopy, so get a permlist. */
114                         permlist = 0;
115  PERM_LIST:
116                         p = perm_chars;
117                         do {
118                                 if (*p == *s) {
119                                         if ((*p != 'X')
120                                          || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
121                                         ) {
122                                                 permlist |= perm_mask[(int)(p-perm_chars)];
123                                         }
124                                         if (!*++s) {
125                                                 break;
126                                         }
127                                         goto PERM_LIST;
128                                 }
129                         } while (*++p);
130  GOT_ACTION:
131                         if (permlist) { /* The permlist was nonempty. */
132                                 mode_t tmp = wholist;
133                                 if (!wholist) {
134                                         mode_t u_mask = umask(0);
135                                         umask(u_mask);
136                                         tmp = ~u_mask;
137                                 }
138                                 permlist &= tmp;
139                                 if (op == '-') {
140                                         new_mode &= ~permlist;
141                                 } else {
142                                         new_mode |= permlist;
143                                 }
144                         }
145                 } while (*s && (*s != ','));
146         }
147
148         *current_mode = new_mode;
149         return 1;
150 }