Merge from vendor branch BIND:
[dragonfly.git] / contrib / tar / lib / modechange.c
1 /* modechange.c -- file mode manipulation
2    Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by David MacKenzie <djm@ai.mit.edu> */
19
20 /* The ASCII mode string is compiled into a linked list of `struct
21    modechange', which can then be applied to each file to be changed.
22    We do this instead of re-parsing the ASCII string for each file
23    because the compiled form requires less computation to use; when
24    changing the mode of many files, this probably results in a
25    performance gain. */
26
27 #if HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include "modechange.h"
32 #include <sys/stat.h>
33 #include "xstrtol.h"
34
35 #if STDC_HEADERS
36 # include <stdlib.h>
37 #else
38 char *malloc ();
39 #endif
40
41 #ifndef NULL
42 # define NULL 0
43 #endif
44
45 #if STAT_MACROS_BROKEN
46 # undef S_ISDIR
47 #endif
48
49 #if !defined(S_ISDIR) && defined(S_IFDIR)
50 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
51 #endif
52
53 /* The traditional octal values corresponding to each mode bit.  */
54 #define SUID 04000
55 #define SGID 02000
56 #define SVTX 01000
57 #define RUSR 00400
58 #define WUSR 00200
59 #define XUSR 00100
60 #define RGRP 00040
61 #define WGRP 00020
62 #define XGRP 00010
63 #define ROTH 00004
64 #define WOTH 00002
65 #define XOTH 00001
66 #define ALLM 07777 /* all octal mode bits */
67
68 #ifndef S_ISUID
69 # define S_ISUID SUID
70 #endif
71 #ifndef S_ISGID
72 # define S_ISGID SGID
73 #endif
74 #ifndef S_ISVTX
75 # define S_ISVTX SVTX
76 #endif
77 #ifndef S_IRUSR
78 # define S_IRUSR RUSR
79 #endif
80 #ifndef S_IWUSR
81 # define S_IWUSR WUSR
82 #endif
83 #ifndef S_IXUSR
84 # define S_IXUSR XUSR
85 #endif
86 #ifndef S_IRGRP
87 # define S_IRGRP RGRP
88 #endif
89 #ifndef S_IWGRP
90 # define S_IWGRP WGRP
91 #endif
92 #ifndef S_IXGRP
93 # define S_IXGRP XGRP
94 #endif
95 #ifndef S_IROTH
96 # define S_IROTH ROTH
97 #endif
98 #ifndef S_IWOTH
99 # define S_IWOTH WOTH
100 #endif
101 #ifndef S_IXOTH
102 # define S_IXOTH XOTH
103 #endif
104 #ifndef S_IRWXU
105 # define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
106 #endif
107 #ifndef S_IRWXG
108 # define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
109 #endif
110 #ifndef S_IRWXO
111 # define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
112 #endif
113
114 /* All the mode bits that can be affected by chmod.  */
115 #define CHMOD_MODE_BITS \
116   (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
117
118 /* Return newly allocated memory to hold one element of type TYPE. */
119 #define talloc(type) ((type *) malloc (sizeof (type)))
120
121 /* Create a mode_change entry with the specified `=ddd'-style
122    mode change operation, where NEW_MODE is `ddd'.  Return the
123    new entry, or NULL upon failure.  */
124
125 static struct mode_change *
126 make_node_op_equals (mode_t new_mode)
127 {
128   struct mode_change *p;
129   p = talloc (struct mode_change);
130   if (p == NULL)
131     return p;
132   p->next = NULL;
133   p->op = '=';
134   p->flags = 0;
135   p->value = new_mode;
136   p->affected = CHMOD_MODE_BITS;        /* Affect all permissions. */
137   return p;
138 }
139
140 /* Append entry E to the end of the link list with the specified
141    HEAD and TAIL.  */
142
143 static void
144 mode_append_entry (struct mode_change **head,
145                    struct mode_change **tail,
146                    struct mode_change *e)
147 {
148   if (*head == NULL)
149     *head = *tail = e;
150   else
151     {
152       (*tail)->next = e;
153       *tail = e;
154     }
155 }
156
157 /* Return a linked list of file mode change operations created from
158    MODE_STRING, an ASCII string that contains either an octal number
159    specifying an absolute mode, or symbolic mode change operations with
160    the form:
161    [ugoa...][[+-=][rwxXstugo...]...][,...]
162    MASKED_OPS is a bitmask indicating which symbolic mode operators (=+-)
163    should not affect bits set in the umask when no users are given.
164    Operators not selected in MASKED_OPS ignore the umask.
165
166    Return MODE_INVALID if `mode_string' does not contain a valid
167    representation of file mode change operations;
168    return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */
169
170 struct mode_change *
171 mode_compile (const char *mode_string, unsigned int masked_ops)
172 {
173   struct mode_change *head;     /* First element of the linked list. */
174   struct mode_change *tail;     /* An element of the linked list. */
175   unsigned long octal_value;    /* The mode value, if octal.  */
176   mode_t umask_value;           /* The umask value (surprise). */
177
178   head = NULL;
179 #ifdef lint
180   tail = NULL;
181 #endif
182
183   if (xstrtoul (mode_string, NULL, 8, &octal_value, "") == LONGINT_OK)
184     {
185       struct mode_change *p;
186       mode_t mode;
187       if (octal_value != (octal_value & ALLM))
188         return MODE_INVALID;
189
190       /* Help the compiler optimize the usual case where mode_t uses
191          the traditional octal representation.  */
192       mode = ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
193                && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
194                && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
195                && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
196               ? octal_value
197               : ((octal_value & SUID ? S_ISUID : 0)
198                  | (octal_value & SGID ? S_ISGID : 0)
199                  | (octal_value & SVTX ? S_ISVTX : 0)
200                  | (octal_value & RUSR ? S_IRUSR : 0)
201                  | (octal_value & WUSR ? S_IWUSR : 0)
202                  | (octal_value & XUSR ? S_IXUSR : 0)
203                  | (octal_value & RGRP ? S_IRGRP : 0)
204                  | (octal_value & WGRP ? S_IWGRP : 0)
205                  | (octal_value & XGRP ? S_IXGRP : 0)
206                  | (octal_value & ROTH ? S_IROTH : 0)
207                  | (octal_value & WOTH ? S_IWOTH : 0)
208                  | (octal_value & XOTH ? S_IXOTH : 0)));
209
210       p = make_node_op_equals (mode);
211       if (p == NULL)
212         return MODE_MEMORY_EXHAUSTED;
213       mode_append_entry (&head, &tail, p);
214       return head;
215     }
216
217   umask_value = umask (0);
218   umask (umask_value);          /* Restore the old value. */
219   --mode_string;
220
221   /* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */
222   do
223     {
224       /* Which bits in the mode are operated on. */
225       mode_t affected_bits = 0;
226       /* `affected_bits' modified by umask. */
227       mode_t affected_masked;
228       /* Operators to actually use umask on. */
229       unsigned ops_to_mask = 0;
230
231       int who_specified_p;
232
233       affected_bits = 0;
234       ops_to_mask = 0;
235       /* Turn on all the bits in `affected_bits' for each group given. */
236       for (++mode_string;; ++mode_string)
237         switch (*mode_string)
238           {
239           case 'u':
240             affected_bits |= S_ISUID | S_IRWXU;
241             break;
242           case 'g':
243             affected_bits |= S_ISGID | S_IRWXG;
244             break;
245           case 'o':
246             affected_bits |= S_ISVTX | S_IRWXO;
247             break;
248           case 'a':
249             affected_bits |= CHMOD_MODE_BITS;
250             break;
251           default:
252             goto no_more_affected;
253           }
254
255     no_more_affected:
256       /* If none specified, affect all bits, except perhaps those
257          set in the umask. */
258       if (affected_bits)
259         who_specified_p = 1;
260       else
261         {
262           who_specified_p = 0;
263           affected_bits = CHMOD_MODE_BITS;
264           ops_to_mask = masked_ops;
265         }
266
267       while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-')
268         {
269           struct mode_change *change = talloc (struct mode_change);
270           if (change == NULL)
271             {
272               mode_free (head);
273               return MODE_MEMORY_EXHAUSTED;
274             }
275
276           change->next = NULL;
277           change->op = *mode_string;    /* One of "=+-". */
278           affected_masked = affected_bits;
279
280           /* Per the Single Unix Spec, if `who' is not specified and the
281              `=' operator is used, then clear all the bits first.  */
282           if (!who_specified_p &&
283               ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS : 0))
284             {
285               struct mode_change *p = make_node_op_equals (0);
286               if (p == NULL)
287                 return MODE_MEMORY_EXHAUSTED;
288               mode_append_entry (&head, &tail, p);
289             }
290
291           if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS
292                              : *mode_string == '+' ? MODE_MASK_PLUS
293                              : MODE_MASK_MINUS))
294             affected_masked &= ~umask_value;
295           change->affected = affected_masked;
296           change->value = 0;
297           change->flags = 0;
298
299           /* Add the element to the tail of the list, so the operations
300              are performed in the correct order. */
301           mode_append_entry (&head, &tail, change);
302
303           /* Set `value' according to the bits set in `affected_masked'. */
304           for (++mode_string;; ++mode_string)
305             switch (*mode_string)
306               {
307               case 'r':
308                 change->value |= ((S_IRUSR | S_IRGRP | S_IROTH)
309                                   & affected_masked);
310                 break;
311               case 'w':
312                 change->value |= ((S_IWUSR | S_IWGRP | S_IWOTH)
313                                   & affected_masked);
314                 break;
315               case 'X':
316                 change->flags |= MODE_X_IF_ANY_X;
317                 /* Fall through. */
318               case 'x':
319                 change->value |= ((S_IXUSR | S_IXGRP | S_IXOTH)
320                                   & affected_masked);
321                 break;
322               case 's':
323                 /* Set the setuid/gid bits if `u' or `g' is selected. */
324                 change->value |= (S_ISUID | S_ISGID) & affected_masked;
325                 break;
326               case 't':
327                 /* Set the "save text image" bit if `o' is selected. */
328                 change->value |= S_ISVTX & affected_masked;
329                 break;
330               case 'u':
331                 /* Set the affected bits to the value of the `u' bits
332                    on the same file.  */
333                 if (change->value)
334                   goto invalid;
335                 change->value = S_IRWXU;
336                 change->flags |= MODE_COPY_EXISTING;
337                 break;
338               case 'g':
339                 /* Set the affected bits to the value of the `g' bits
340                    on the same file.  */
341                 if (change->value)
342                   goto invalid;
343                 change->value = S_IRWXG;
344                 change->flags |= MODE_COPY_EXISTING;
345                 break;
346               case 'o':
347                 /* Set the affected bits to the value of the `o' bits
348                    on the same file.  */
349                 if (change->value)
350                   goto invalid;
351                 change->value = S_IRWXO;
352                 change->flags |= MODE_COPY_EXISTING;
353                 break;
354               default:
355                 goto no_more_values;
356               }
357         no_more_values:;
358         }
359   } while (*mode_string == ',');
360   if (*mode_string == 0)
361     return head;
362 invalid:
363   mode_free (head);
364   return MODE_INVALID;
365 }
366
367 /* Return a file mode change operation that sets permissions to match those
368    of REF_FILE.  Return MODE_BAD_REFERENCE if REF_FILE can't be accessed.  */
369
370 struct mode_change *
371 mode_create_from_ref (const char *ref_file)
372 {
373   struct mode_change *change;   /* the only change element */
374   struct stat ref_stats;
375
376   if (stat (ref_file, &ref_stats))
377     return MODE_BAD_REFERENCE;
378
379   change = talloc (struct mode_change);
380
381   if (change == NULL)
382     return MODE_MEMORY_EXHAUSTED;
383
384   change->op = '=';
385   change->flags = 0;
386   change->affected = CHMOD_MODE_BITS;
387   change->value = ref_stats.st_mode;
388   change->next = NULL;
389
390   return change;
391 }
392
393 /* Return file mode OLDMODE, adjusted as indicated by the list of change
394    operations CHANGES.  If OLDMODE is a directory, the type `X'
395    change affects it even if no execute bits were set in OLDMODE.
396    The returned value has the S_IFMT bits cleared. */
397
398 mode_t
399 mode_adjust (mode_t oldmode, const struct mode_change *changes)
400 {
401   mode_t newmode;       /* The adjusted mode and one operand. */
402   mode_t value;         /* The other operand. */
403
404   newmode = oldmode & CHMOD_MODE_BITS;
405
406   for (; changes; changes = changes->next)
407     {
408       if (changes->flags & MODE_COPY_EXISTING)
409         {
410           /* Isolate in `value' the bits in `newmode' to copy, given in
411              the mask `changes->value'. */
412           value = newmode & changes->value;
413
414           if (changes->value & S_IRWXU)
415             /* Copy `u' permissions onto `g' and `o'. */
416             value |= ((value & S_IRUSR ? S_IRGRP | S_IROTH : 0)
417                       | (value & S_IWUSR ? S_IWGRP | S_IROTH : 0)
418                       | (value & S_IXUSR ? S_IXGRP | S_IXOTH : 0));
419           else if (changes->value & S_IRWXG)
420             /* Copy `g' permissions onto `u' and `o'. */
421             value |= ((value & S_IRGRP ? S_IRUSR | S_IROTH : 0)
422                       | (value & S_IWGRP ? S_IWUSR | S_IROTH : 0)
423                       | (value & S_IXGRP ? S_IXUSR | S_IXOTH : 0));
424           else
425             /* Copy `o' permissions onto `u' and `g'. */
426             value |= ((value & S_IROTH ? S_IRUSR | S_IRGRP : 0)
427                       | (value & S_IWOTH ? S_IWUSR | S_IRGRP : 0)
428                       | (value & S_IXOTH ? S_IXUSR | S_IXGRP : 0));
429
430           /* In order to change only `u', `g', or `o' permissions,
431              or some combination thereof, clear unselected bits.
432              This cannot be done in mode_compile because the value
433              to which the `changes->affected' mask is applied depends
434              on the old mode of each file. */
435           value &= changes->affected;
436         }
437       else
438         {
439           value = changes->value;
440           /* If `X', do not affect the execute bits if the file is not a
441              directory and no execute bits are already set. */
442           if ((changes->flags & MODE_X_IF_ANY_X)
443               && !S_ISDIR (oldmode)
444               && (newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
445             /* Clear the execute bits. */
446             value &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
447         }
448
449       switch (changes->op)
450         {
451         case '=':
452           /* Preserve the previous values in `newmode' of bits that are
453              not affected by this change operation. */
454           newmode = (newmode & ~changes->affected) | value;
455           break;
456         case '+':
457           newmode |= value;
458           break;
459         case '-':
460           newmode &= ~value;
461           break;
462         }
463     }
464   return newmode;
465 }
466
467 /* Free the memory used by the list of file mode change operations
468    CHANGES. */
469
470 void
471 mode_free (register struct mode_change *changes)
472 {
473   register struct mode_change *next;
474
475   while (changes)
476     {
477       next = changes->next;
478       free (changes);
479       changes = next;
480     }
481 }