add ext4,vfat and tar.bz2
[u-tools.git] / u-tools / apps / tar / gnu / modechange.c
blob97a8d1a5b22d8b5004829fb8b40f81aa00b1a060
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* modechange.c -- file mode manipulation
5 Copyright (C) 1989-1990, 1997-1999, 2001, 2003-2006, 2009-2011 Free Software
6 Foundation, Inc.
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.
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.
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/>. */
21 /* Written by David MacKenzie <djm@ai.mit.edu> */
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. */
30 #include <config.h>
32 #include "modechange.h"
33 #include <sys/stat.h>
34 #include "stat-macros.h"
35 #include "xalloc.h"
36 #include <stdlib.h>
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 */
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)
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)));
79 /* Special operations flags. */
80 enum
82 /* For the sentinel at the end of the mode changes array. */
83 MODE_DONE,
85 /* The typical case. */
86 MODE_ORDINARY_CHANGE,
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,
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
99 /* Description of a mode change. */
100 struct mode_change
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. */
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. */
113 static struct mode_change *
114 make_node_op_equals (mode_t new_mode, mode_t mentioned)
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;
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...]...][,...]
132 Return NULL if `mode_string' does not contain a valid
133 representation of file mode change operations. */
135 struct mode_change *
136 mode_compile (char const *mode_string)
138 /* The array of mode-change directives to be returned. */
139 struct mode_change *mc;
140 size_t used = 0;
142 if ('0' <= *mode_string && *mode_string < '8')
144 unsigned int octal_mode = 0;
145 mode_t mode;
146 mode_t mentioned;
150 octal_mode = 8 * octal_mode + *mode_string++ - '0';
151 if (ALLM < octal_mode)
152 return NULL;
154 while ('0' <= *mode_string && *mode_string < '8');
156 if (*mode_string)
157 return NULL;
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);
164 /* Allocate enough space to hold the result. */
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);
173 /* One loop iteration for each `[ugoa]*([-+=]([rwxXst]*|[ugo]))+'. */
174 for (;; mode_string++)
176 /* Which bits in the mode are operated on. */
177 mode_t affected = 0;
179 /* Turn on all the bits in `affected' for each group given. */
180 for (;; mode_string++)
181 switch (*mode_string)
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;
200 no_more_affected:;
204 char op = *mode_string++;
205 mode_t value;
206 char flag = MODE_COPY_EXISTING;
207 struct mode_change *change;
209 switch (*mode_string++)
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;
227 default:
228 value = 0;
229 flag = MODE_ORDINARY_CHANGE;
231 for (mode_string--;; mode_string++)
232 switch (*mode_string)
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;
257 no_more_values:;
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);
267 while (*mode_string == '=' || *mode_string == '+'
268 || *mode_string == '-');
270 if (*mode_string != ',')
271 break;
274 if (*mode_string == 0)
276 mc[used].flag = MODE_DONE;
277 return mc;
280 invalid:
281 free (mc);
282 return NULL;
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. */
288 struct mode_change *
289 mode_create_from_ref (const char *ref_file)
291 struct stat ref_stats;
293 if (stat (ref_file, &ref_stats) != 0)
294 return NULL;
295 return make_node_op_equals (ref_stats.st_mode, CHMOD_MODE_BITS);
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.
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. */
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)
315 /* The adjusted mode. */
316 mode_t newmode = oldmode & CHMOD_MODE_BITS;
318 /* File mode bits that CHANGES cares about. */
319 mode_t mode_bits = 0;
321 for (; changes->flag != MODE_DONE; changes++)
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;
328 switch (changes->flag)
330 case MODE_ORDINARY_CHANGE:
331 break;
333 case MODE_COPY_EXISTING:
334 /* Isolate in `value' the bits in `newmode' to copy. */
335 value &= newmode;
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;
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;
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;
359 switch (changes->op)
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. */
366 mode_t preserved = (affected ? ~affected : 0) | omit_change;
367 mode_bits |= CHMOD_MODE_BITS & ~preserved;
368 newmode = (newmode & preserved) | value;
369 break;
372 case '+':
373 mode_bits |= value;
374 newmode |= value;
375 break;
377 case '-':
378 mode_bits |= value;
379 newmode &= ~value;
380 break;
384 if (pmode_bits)
385 *pmode_bits = mode_bits;
386 return newmode;