7132c76ef9ba44ae186b2f4b3c7f0071664bd435
[platform/upstream/busybox.git] / 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  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  */
22
23 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
24
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <sys/stat.h>
28 #include "libbb.h"
29
30 #define FILEMODEBITS    (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
31
32 extern int bb_parse_mode(const char *s, mode_t *current_mode)
33 {
34         static const mode_t who_mask[] = { 
35                 S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
36                 S_ISUID | S_IRWXU,              /* u */
37                 S_ISGID | S_IRWXG,              /* g */
38                 S_IRWXO                                 /* o */
39         };
40
41         static const mode_t perm_mask[] = {
42                 S_IRUSR | S_IRGRP | S_IROTH, /* r */
43                 S_IWUSR | S_IWGRP | S_IWOTH, /* w */
44                 S_IXUSR | S_IXGRP | S_IXOTH, /* x */
45                 S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
46                 S_ISUID | S_ISGID,              /* s */
47                 S_ISVTX                                 /* t */
48         };
49
50         static const char who_chars[] = "augo";
51         static const char perm_chars[] = "rwxXst";
52
53         const char *p;
54
55         mode_t wholist;
56         mode_t permlist;
57         mode_t mask;
58         mode_t new_mode;
59         char op;
60
61         assert(s);
62
63         if (((unsigned int)(*s - '0')) < 8) {
64                 unsigned long tmp;
65                 char *e;
66
67                 tmp = strtol(s, &e, 8);
68                 if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
69                         return 0;
70                 }
71                 *current_mode = tmp;
72                 return 1;
73         }
74
75         mask = umask(0);
76         umask(mask);
77
78         new_mode = *current_mode;
79
80         /* Note: We allow empty clauses, and hence empty modes.
81          * We treat an empty mode as no change to perms. */
82
83         while (*s) {    /* Process clauses. */
84
85                 if (*s == ',') {        /* We allow empty clauses. */
86                         ++s;
87                         continue;
88                 }
89
90                 /* Get a wholist. */
91                 wholist = 0;
92
93         WHO_LIST:
94                 p = who_chars;
95                 do {
96                         if (*p == *s) {
97                                 wholist |= who_mask[(int)(p-who_chars)];
98                                 if (!*++s) {
99                                         return 0;
100                                 }
101                                 goto WHO_LIST;
102                         }
103                 } while (*++p);
104
105                 do {    /* Process action list. */
106                         if ((*s != '+') && (*s != '-')) {
107                                 if (*s != '=') {
108                                         return 0;
109                                 }
110                                 /* Since op is '=', clear all bits corresponding to the
111                                  * wholist, of all file bits if wholist is empty. */
112                                 permlist = ~FILEMODEBITS;
113                                 if (wholist) {
114                                         permlist = ~wholist;
115                                 }
116                                 new_mode &= permlist;
117                         }
118                         op = *s++;
119
120                         /* Check for permcopy. */
121                         p = who_chars + 1;      /* Skip 'a' entry. */
122                         do {
123                                 if (*p == *s) {
124                                         int i = 0;
125                                         permlist = who_mask[(int)(p-who_chars)]
126                                                          & (S_IRWXU | S_IRWXG | S_IRWXO)
127                                                          & new_mode;
128                                         do {
129                                                 if (permlist & perm_mask[i]) {
130                                                         permlist |= perm_mask[i];
131                                                 }
132                                         } while (++i < 3);
133                                         ++s;
134                                         goto GOT_ACTION;
135                                 }
136                         } while (*++p);
137
138                         /* It was not a permcopy, so get a permlist. */
139                         permlist = 0;
140
141                 PERM_LIST:
142                         p = perm_chars;
143                         do {
144                                 if (*p == *s) {
145                                         if ((*p != 'X')
146                                                 || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
147                                         ) {
148                                                 permlist |= perm_mask[(int)(p-perm_chars)];
149                                         }
150                                         if (!*++s) {
151                                                 break;
152                                         }
153                                         goto PERM_LIST;
154                                 }
155                         } while (*++p);
156
157                 GOT_ACTION:
158                         if (permlist) { /* The permlist was nonempty. */
159                                 mode_t tmp = ~mask;
160                                 if (wholist) {
161                                         tmp = wholist;
162                                 }
163                                 permlist &= tmp;
164
165                                 if (op == '-') {
166                                         new_mode &= ~permlist;
167                                 } else {
168                                         new_mode |= permlist;
169                                 }
170                         }
171                 } while (*s && (*s != ','));
172         }
173
174         *current_mode = new_mode;
175
176         return 1;
177 }