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-2013 Free Software
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
32 #include "modechange.h"
34 #include "stat-macros.h"
38 /* The traditional octal values corresponding to each mode bit. */
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. */
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)
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. */
82 /* For the sentinel at the end of the mode changes array. */
85 /* The typical case. */
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
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. */
99 /* Description of a 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);
118 p->flag = MODE_ORDINARY_CHANGE;
119 p->affected = CHMOD_MODE_BITS;
121 p->mentioned = mentioned;
122 p[1].flag = MODE_DONE;
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
130 [ugoa...][[+-=][rwxXstugo...]...][,...]
132 Return NULL if 'mode_string' does not contain a valid
133 representation of file mode change operations. */
136 mode_compile (char const *mode_string)
138 /* The array of mode-change directives to be returned. */
139 struct mode_change *mc;
143 if ('0' <= *mode_string && *mode_string < '8')
145 unsigned int octal_mode = 0;
152 octal_mode = 8 * octal_mode + *p++ - '0';
153 if (ALLM < octal_mode)
156 while ('0' <= *p && *p < '8');
161 mode = octal_to_mode (octal_mode);
162 mentioned = (p - mode_string < 5
163 ? (mode & (S_ISUID | S_ISGID)) | S_ISVTX | S_IRWXUGO
165 return make_node_op_equals (mode, mentioned);
168 /* Allocate enough space to hold the result. */
171 for (p = mode_string; *p; p++)
172 needed += (*p == '=' || *p == '+' || *p == '-');
173 mc = xnmalloc (needed, sizeof *mc);
176 /* One loop iteration for each
177 '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+'. */
178 for (p = mode_string; ; p++)
180 /* Which bits in the mode are operated on. */
183 /* Turn on all the bits in 'affected' for each group given. */
190 affected |= S_ISUID | S_IRWXU;
193 affected |= S_ISGID | S_IRWXG;
196 affected |= S_ISVTX | S_IRWXO;
199 affected |= CHMOD_MODE_BITS;
201 case '=': case '+': case '-':
202 goto no_more_affected;
210 mode_t mentioned = 0;
211 char flag = MODE_COPY_EXISTING;
212 struct mode_change *change;
216 case '0': case '1': case '2': case '3':
217 case '4': case '5': case '6': case '7':
219 unsigned int octal_mode = 0;
223 octal_mode = 8 * octal_mode + *p++ - '0';
224 if (ALLM < octal_mode)
227 while ('0' <= *p && *p < '8');
229 if (affected || (*p && *p != ','))
231 affected = mentioned = CHMOD_MODE_BITS;
232 value = octal_to_mode (octal_mode);
233 flag = MODE_ORDINARY_CHANGE;
238 /* Set the affected bits to the value of the "u" bits
244 /* Set the affected bits to the value of the "g" bits
250 /* Set the affected bits to the value of the "o" bits
258 flag = MODE_ORDINARY_CHANGE;
264 value |= S_IRUSR | S_IRGRP | S_IROTH;
267 value |= S_IWUSR | S_IWGRP | S_IWOTH;
270 value |= S_IXUSR | S_IXGRP | S_IXOTH;
273 flag = MODE_X_IF_ANY_X;
276 /* Set the setuid/gid bits if 'u' or 'g' is selected. */
277 value |= S_ISUID | S_ISGID;
280 /* Set the "save text image" bit if 'o' is selected. */
289 change = &mc[used++];
292 change->affected = affected;
293 change->value = value;
295 (mentioned ? mentioned : affected ? affected & value : value);
297 while (*p == '=' || *p == '+' || *p == '-');
305 mc[used].flag = MODE_DONE;
314 /* Return a file mode change operation that sets permissions to match those
315 of REF_FILE. Return NULL (setting errno) if REF_FILE can't be accessed. */
318 mode_create_from_ref (const char *ref_file)
320 struct stat ref_stats;
322 if (stat (ref_file, &ref_stats) != 0)
324 return make_node_op_equals (ref_stats.st_mode, CHMOD_MODE_BITS);
327 /* Return the file mode bits of OLDMODE (which is the mode of a
328 directory if DIR), assuming the umask is UMASK_VALUE, adjusted as
329 indicated by the list of change operations CHANGES. If DIR, the
330 type 'X' change affects the returned value even if no execute bits
331 were set in OLDMODE, and set user and group ID bits are preserved
332 unless CHANGES mentioned them. If PMODE_BITS is not null, store into
333 *PMODE_BITS a mask denoting file mode bits that are affected by
336 The returned value and *PMODE_BITS contain only file mode bits.
337 For example, they have the S_IFMT bits cleared on a standard
341 mode_adjust (mode_t oldmode, bool dir, mode_t umask_value,
342 struct mode_change const *changes, mode_t *pmode_bits)
344 /* The adjusted mode. */
345 mode_t newmode = oldmode & CHMOD_MODE_BITS;
347 /* File mode bits that CHANGES cares about. */
348 mode_t mode_bits = 0;
350 for (; changes->flag != MODE_DONE; changes++)
352 mode_t affected = changes->affected;
354 (dir ? S_ISUID | S_ISGID : 0) & ~ changes->mentioned;
355 mode_t value = changes->value;
357 switch (changes->flag)
359 case MODE_ORDINARY_CHANGE:
362 case MODE_COPY_EXISTING:
363 /* Isolate in 'value' the bits in 'newmode' to copy. */
366 /* Copy the isolated bits to the other two parts. */
367 value |= ((value & (S_IRUSR | S_IRGRP | S_IROTH)
368 ? S_IRUSR | S_IRGRP | S_IROTH : 0)
369 | (value & (S_IWUSR | S_IWGRP | S_IWOTH)
370 ? S_IWUSR | S_IWGRP | S_IWOTH : 0)
371 | (value & (S_IXUSR | S_IXGRP | S_IXOTH)
372 ? S_IXUSR | S_IXGRP | S_IXOTH : 0));
375 case MODE_X_IF_ANY_X:
376 /* Affect the execute bits if execute bits are already set
377 or if the file is a directory. */
378 if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) | dir)
379 value |= S_IXUSR | S_IXGRP | S_IXOTH;
383 /* If WHO was specified, limit the change to the affected bits.
384 Otherwise, apply the umask. Either way, omit changes as
386 value &= (affected ? affected : ~umask_value) & ~ omit_change;
391 /* If WHO was specified, preserve the previous values of
392 bits that are not affected by this change operation.
393 Otherwise, clear all the bits. */
395 mode_t preserved = (affected ? ~affected : 0) | omit_change;
396 mode_bits |= CHMOD_MODE_BITS & ~preserved;
397 newmode = (newmode & preserved) | value;
414 *pmode_bits = mode_bits;