]> git.cworth.org Git - tar/blob - gnu/modechange.c
upstream: Fix extraction of device nodes.
[tar] / gnu / modechange.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* modechange.c -- file mode manipulation
4
5    Copyright (C) 1989-1990, 1997-1999, 2001, 2003-2006, 2009-2010 Free Software
6    Foundation, Inc.
7
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21 /* Written by David MacKenzie <djm@ai.mit.edu> */
22
23 /* The ASCII mode string is compiled into an array of `struct
24    modechange', which can then be applied to each file to be changed.
25    We do this instead of re-parsing the ASCII string for each file
26    because the compiled form requires less computation to use; when
27    changing the mode of many files, this probably results in a
28    performance gain.  */
29
30 #include <config.h>
31
32 #include "modechange.h"
33 #include <sys/stat.h>
34 #include "stat-macros.h"
35 #include "xalloc.h"
36 #include <stdlib.h>
37
38 /* The traditional octal values corresponding to each mode bit.  */
39 #define SUID 04000
40 #define SGID 02000
41 #define SVTX 01000
42 #define RUSR 00400
43 #define WUSR 00200
44 #define XUSR 00100
45 #define RGRP 00040
46 #define WGRP 00020
47 #define XGRP 00010
48 #define ROTH 00004
49 #define WOTH 00002
50 #define XOTH 00001
51 #define ALLM 07777 /* all octal mode bits */
52
53 /* Convert OCTAL, which uses one of the traditional octal values, to
54    an internal mode_t value.  */
55 static mode_t
56 octal_to_mode (unsigned int octal)
57 {
58   /* Help the compiler optimize the usual case where mode_t uses
59      the traditional octal representation.  */
60   return ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
61            && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
62            && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
63            && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
64           ? octal
65           : (mode_t) ((octal & SUID ? S_ISUID : 0)
66                       | (octal & SGID ? S_ISGID : 0)
67                       | (octal & SVTX ? S_ISVTX : 0)
68                       | (octal & RUSR ? S_IRUSR : 0)
69                       | (octal & WUSR ? S_IWUSR : 0)
70                       | (octal & XUSR ? S_IXUSR : 0)
71                       | (octal & RGRP ? S_IRGRP : 0)
72                       | (octal & WGRP ? S_IWGRP : 0)
73                       | (octal & XGRP ? S_IXGRP : 0)
74                       | (octal & ROTH ? S_IROTH : 0)
75                       | (octal & WOTH ? S_IWOTH : 0)
76                       | (octal & XOTH ? S_IXOTH : 0)));
77 }
78
79 /* Special operations flags.  */
80 enum
81   {
82     /* For the sentinel at the end of the mode changes array.  */
83     MODE_DONE,
84
85     /* The typical case.  */
86     MODE_ORDINARY_CHANGE,
87
88     /* In addition to the typical case, affect the execute bits if at
89        least one execute bit is set already, or if the file is a
90        directory.  */
91     MODE_X_IF_ANY_X,
92
93     /* Instead of the typical case, copy some existing permissions for
94        u, g, or o onto the other two.  Which of u, g, or o is copied
95        is determined by which bits are set in the `value' field.  */
96     MODE_COPY_EXISTING
97   };
98
99 /* Description of a mode change.  */
100 struct mode_change
101 {
102   char op;                      /* One of "=+-".  */
103   char flag;                    /* Special operations flag.  */
104   mode_t affected;              /* Set for u, g, o, or a.  */
105   mode_t value;                 /* Bits to add/remove.  */
106   mode_t mentioned;             /* Bits explicitly mentioned.  */
107 };
108
109 /* Return a mode_change array with the specified `=ddd'-style
110    mode change operation, where NEW_MODE is `ddd' and MENTIONED
111    contains the bits explicitly mentioned in the mode are MENTIONED.  */
112
113 static struct mode_change *
114 make_node_op_equals (mode_t new_mode, mode_t mentioned)
115 {
116   struct mode_change *p = xmalloc (2 * sizeof *p);
117   p->op = '=';
118   p->flag = MODE_ORDINARY_CHANGE;
119   p->affected = CHMOD_MODE_BITS;
120   p->value = new_mode;
121   p->mentioned = mentioned;
122   p[1].flag = MODE_DONE;
123   return p;
124 }
125
126 /* Return a pointer to an array of file mode change operations created from
127    MODE_STRING, an ASCII string that contains either an octal number
128    specifying an absolute mode, or symbolic mode change operations with
129    the form:
130    [ugoa...][[+-=][rwxXstugo...]...][,...]
131
132    Return NULL if `mode_string' does not contain a valid
133    representation of file mode change operations.  */
134
135 struct mode_change *
136 mode_compile (char const *mode_string)
137 {
138   /* The array of mode-change directives to be returned.  */
139   struct mode_change *mc;
140   size_t used = 0;
141
142   if ('0' <= *mode_string && *mode_string < '8')
143     {
144       unsigned int octal_mode = 0;
145       mode_t mode;
146       mode_t mentioned;
147
148       do
149         {
150           octal_mode = 8 * octal_mode + *mode_string++ - '0';
151           if (ALLM < octal_mode)
152             return NULL;
153         }
154       while ('0' <= *mode_string && *mode_string < '8');
155
156       if (*mode_string)
157         return NULL;
158
159       mode = octal_to_mode (octal_mode);
160       mentioned = (mode & (S_ISUID | S_ISGID)) | S_ISVTX | S_IRWXUGO;
161       return make_node_op_equals (mode, mentioned);
162     }
163
164   /* Allocate enough space to hold the result.  */
165   {
166     size_t needed = 1;
167     char const *p;
168     for (p = mode_string; *p; p++)
169       needed += (*p == '=' || *p == '+' || *p == '-');
170     mc = xnmalloc (needed, sizeof *mc);
171   }
172
173   /* One loop iteration for each `[ugoa]*([-+=]([rwxXst]*|[ugo]))+'.  */
174   for (;; mode_string++)
175     {
176       /* Which bits in the mode are operated on.  */
177       mode_t affected = 0;
178
179       /* Turn on all the bits in `affected' for each group given.  */
180       for (;; mode_string++)
181         switch (*mode_string)
182           {
183           default:
184             goto invalid;
185           case 'u':
186             affected |= S_ISUID | S_IRWXU;
187             break;
188           case 'g':
189             affected |= S_ISGID | S_IRWXG;
190             break;
191           case 'o':
192             affected |= S_ISVTX | S_IRWXO;
193             break;
194           case 'a':
195             affected |= CHMOD_MODE_BITS;
196             break;
197           case '=': case '+': case '-':
198             goto no_more_affected;
199           }
200     no_more_affected:;
201
202       do
203         {
204           char op = *mode_string++;
205           mode_t value;
206           char flag = MODE_COPY_EXISTING;
207           struct mode_change *change;
208
209           switch (*mode_string++)
210             {
211             case 'u':
212               /* Set the affected bits to the value of the `u' bits
213                  on the same file.  */
214               value = S_IRWXU;
215               break;
216             case 'g':
217               /* Set the affected bits to the value of the `g' bits
218                  on the same file.  */
219               value = S_IRWXG;
220               break;
221             case 'o':
222               /* Set the affected bits to the value of the `o' bits
223                  on the same file.  */
224               value = S_IRWXO;
225               break;
226
227             default:
228               value = 0;
229               flag = MODE_ORDINARY_CHANGE;
230
231               for (mode_string--;; mode_string++)
232                 switch (*mode_string)
233                   {
234                   case 'r':
235                     value |= S_IRUSR | S_IRGRP | S_IROTH;
236                     break;
237                   case 'w':
238                     value |= S_IWUSR | S_IWGRP | S_IWOTH;
239                     break;
240                   case 'x':
241                     value |= S_IXUSR | S_IXGRP | S_IXOTH;
242                     break;
243                   case 'X':
244                     flag = MODE_X_IF_ANY_X;
245                     break;
246                   case 's':
247                     /* Set the setuid/gid bits if `u' or `g' is selected.  */
248                     value |= S_ISUID | S_ISGID;
249                     break;
250                   case 't':
251                     /* Set the "save text image" bit if `o' is selected.  */
252                     value |= S_ISVTX;
253                     break;
254                   default:
255                     goto no_more_values;
256                   }
257             no_more_values:;
258             }
259
260           change = &mc[used++];
261           change->op = op;
262           change->flag = flag;
263           change->affected = affected;
264           change->value = value;
265           change->mentioned = (affected ? affected & value : value);
266         }
267       while (*mode_string == '=' || *mode_string == '+'
268              || *mode_string == '-');
269
270       if (*mode_string != ',')
271         break;
272     }
273
274   if (*mode_string == 0)
275     {
276       mc[used].flag = MODE_DONE;
277       return mc;
278     }
279
280 invalid:
281   free (mc);
282   return NULL;
283 }
284
285 /* Return a file mode change operation that sets permissions to match those
286    of REF_FILE.  Return NULL (setting errno) if REF_FILE can't be accessed.  */
287
288 struct mode_change *
289 mode_create_from_ref (const char *ref_file)
290 {
291   struct stat ref_stats;
292
293   if (stat (ref_file, &ref_stats) != 0)
294     return NULL;
295   return make_node_op_equals (ref_stats.st_mode, CHMOD_MODE_BITS);
296 }
297
298 /* Return the file mode bits of OLDMODE (which is the mode of a
299    directory if DIR), assuming the umask is UMASK_VALUE, adjusted as
300    indicated by the list of change operations CHANGES.  If DIR, the
301    type 'X' change affects the returned value even if no execute bits
302    were set in OLDMODE, and set user and group ID bits are preserved
303    unless CHANGES mentioned them.  If PMODE_BITS is not null, store into
304    *PMODE_BITS a mask denoting file mode bits that are affected by
305    CHANGES.
306
307    The returned value and *PMODE_BITS contain only file mode bits.
308    For example, they have the S_IFMT bits cleared on a standard
309    Unix-like host.  */
310
311 mode_t
312 mode_adjust (mode_t oldmode, bool dir, mode_t umask_value,
313              struct mode_change const *changes, mode_t *pmode_bits)
314 {
315   /* The adjusted mode.  */
316   mode_t newmode = oldmode & CHMOD_MODE_BITS;
317
318   /* File mode bits that CHANGES cares about.  */
319   mode_t mode_bits = 0;
320
321   for (; changes->flag != MODE_DONE; changes++)
322     {
323       mode_t affected = changes->affected;
324       mode_t omit_change =
325         (dir ? S_ISUID | S_ISGID : 0) & ~ changes->mentioned;
326       mode_t value = changes->value;
327
328       switch (changes->flag)
329         {
330         case MODE_ORDINARY_CHANGE:
331           break;
332
333         case MODE_COPY_EXISTING:
334           /* Isolate in `value' the bits in `newmode' to copy.  */
335           value &= newmode;
336
337           /* Copy the isolated bits to the other two parts.  */
338           value |= ((value & (S_IRUSR | S_IRGRP | S_IROTH)
339                      ? S_IRUSR | S_IRGRP | S_IROTH : 0)
340                     | (value & (S_IWUSR | S_IWGRP | S_IWOTH)
341                        ? S_IWUSR | S_IWGRP | S_IWOTH : 0)
342                     | (value & (S_IXUSR | S_IXGRP | S_IXOTH)
343                        ? S_IXUSR | S_IXGRP | S_IXOTH : 0));
344           break;
345
346         case MODE_X_IF_ANY_X:
347           /* Affect the execute bits if execute bits are already set
348              or if the file is a directory.  */
349           if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) | dir)
350             value |= S_IXUSR | S_IXGRP | S_IXOTH;
351           break;
352         }
353
354       /* If WHO was specified, limit the change to the affected bits.
355          Otherwise, apply the umask.  Either way, omit changes as
356          requested.  */
357       value &= (affected ? affected : ~umask_value) & ~ omit_change;
358
359       switch (changes->op)
360         {
361         case '=':
362           /* If WHO was specified, preserve the previous values of
363              bits that are not affected by this change operation.
364              Otherwise, clear all the bits.  */
365           {
366             mode_t preserved = (affected ? ~affected : 0) | omit_change;
367             mode_bits |= CHMOD_MODE_BITS & ~preserved;
368             newmode = (newmode & preserved) | value;
369             break;
370           }
371
372         case '+':
373           mode_bits |= value;
374           newmode |= value;
375           break;
376
377         case '-':
378           mode_bits |= value;
379           newmode &= ~value;
380           break;
381         }
382     }
383
384   if (pmode_bits)
385     *pmode_bits = mode_bits;
386   return newmode;
387 }