1 /* modechange.c -- file mode manipulation
3 Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001, 2003, 2004, 2005,
4 2006 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
20 /* Written by David MacKenzie <djm@ai.mit.edu> */
22 /* The ASCII mode string is compiled into an array of `struct
23 modechange', which can then be applied to each file to be changed.
24 We do this instead of re-parsing the ASCII string for each file
25 because the compiled form requires less computation to use; when
26 changing the mode of many files, this probably results in a
33 #include "modechange.h"
35 #include "stat-macros.h"
39 /* The traditional octal values corresponding to each mode bit. */
52 #define ALLM 07777 /* all octal mode bits */
54 /* Convert OCTAL, which uses one of the traditional octal values, to
55 an internal mode_t value. */
57 octal_to_mode (unsigned int octal)
59 /* Help the compiler optimize the usual case where mode_t uses
60 the traditional octal representation. */
61 return ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
62 && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
63 && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
64 && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
66 : (mode_t) ((octal & SUID ? S_ISUID : 0)
67 | (octal & SGID ? S_ISGID : 0)
68 | (octal & SVTX ? S_ISVTX : 0)
69 | (octal & RUSR ? S_IRUSR : 0)
70 | (octal & WUSR ? S_IWUSR : 0)
71 | (octal & XUSR ? S_IXUSR : 0)
72 | (octal & RGRP ? S_IRGRP : 0)
73 | (octal & WGRP ? S_IWGRP : 0)
74 | (octal & XGRP ? S_IXGRP : 0)
75 | (octal & ROTH ? S_IROTH : 0)
76 | (octal & WOTH ? S_IWOTH : 0)
77 | (octal & XOTH ? S_IXOTH : 0)));
80 /* Special operations flags. */
83 /* For the sentinel at the end of the mode changes array. */
86 /* The typical case. */
89 /* In addition to the typical case, affect the execute bits if at
90 least one execute bit is set already, or if the file is a
94 /* Instead of the typical case, copy some existing permissions for
95 u, g, or o onto the other two. Which of u, g, or o is copied
96 is determined by which bits are set in the `value' field. */
100 /* Description of a mode change. */
103 char op; /* One of "=+-". */
104 char flag; /* Special operations flag. */
105 mode_t affected; /* Set for u, g, o, or a. */
106 mode_t value; /* Bits to add/remove. */
107 mode_t mentioned; /* Bits explicitly mentioned. */
110 /* Return a mode_change array with the specified `=ddd'-style
111 mode change operation, where NEW_MODE is `ddd' and MENTIONED
112 contains the bits explicitly mentioned in the mode are MENTIONED. */
114 static struct mode_change *
115 make_node_op_equals (mode_t new_mode, mode_t mentioned)
117 struct mode_change *p = xmalloc (2 * sizeof *p);
119 p->flag = MODE_ORDINARY_CHANGE;
120 p->affected = CHMOD_MODE_BITS;
122 p->mentioned = mentioned;
123 p[1].flag = MODE_DONE;
127 /* Return a pointer to an array of file mode change operations created from
128 MODE_STRING, an ASCII string that contains either an octal number
129 specifying an absolute mode, or symbolic mode change operations with
131 [ugoa...][[+-=][rwxXstugo...]...][,...]
133 Return NULL if `mode_string' does not contain a valid
134 representation of file mode change operations. */
137 mode_compile (char const *mode_string)
139 /* The array of mode-change directives to be returned. */
140 struct mode_change *mc;
143 if ('0' <= *mode_string && *mode_string < '8')
145 unsigned int octal_mode = 0;
151 octal_mode = 8 * octal_mode + *mode_string++ - '0';
152 if (ALLM < octal_mode)
155 while ('0' <= *mode_string && *mode_string < '8');
160 mode = octal_to_mode (octal_mode);
161 mentioned = (mode & (S_ISUID | S_ISGID)) | S_ISVTX | S_IRWXUGO;
162 return make_node_op_equals (mode, mentioned);
165 /* Allocate enough space to hold the result. */
169 for (p = mode_string; *p; p++)
170 needed += (*p == '=' || *p == '+' || *p == '-');
171 mc = xnmalloc (needed, sizeof *mc);
174 /* One loop iteration for each `[ugoa]*([-+=]([rwxXst]*|[ugo]))+'. */
175 for (;; mode_string++)
177 /* Which bits in the mode are operated on. */
180 /* Turn on all the bits in `affected' for each group given. */
181 for (;; mode_string++)
182 switch (*mode_string)
187 affected |= S_ISUID | S_IRWXU;
190 affected |= S_ISGID | S_IRWXG;
193 affected |= S_ISVTX | S_IRWXO;
196 affected |= CHMOD_MODE_BITS;
198 case '=': case '+': case '-':
199 goto no_more_affected;
205 char op = *mode_string++;
207 char flag = MODE_COPY_EXISTING;
208 struct mode_change *change;
210 switch (*mode_string++)
213 /* Set the affected bits to the value of the `u' bits
218 /* Set the affected bits to the value of the `g' bits
223 /* Set the affected bits to the value of the `o' bits
230 flag = MODE_ORDINARY_CHANGE;
232 for (mode_string--;; mode_string++)
233 switch (*mode_string)
236 value |= S_IRUSR | S_IRGRP | S_IROTH;
239 value |= S_IWUSR | S_IWGRP | S_IWOTH;
242 value |= S_IXUSR | S_IXGRP | S_IXOTH;
245 flag = MODE_X_IF_ANY_X;
248 /* Set the setuid/gid bits if `u' or `g' is selected. */
249 value |= S_ISUID | S_ISGID;
252 /* Set the "save text image" bit if `o' is selected. */
261 change = &mc[used++];
264 change->affected = affected;
265 change->value = value;
266 change->mentioned = (affected ? affected & value : value);
268 while (*mode_string == '=' || *mode_string == '+'
269 || *mode_string == '-');
271 if (*mode_string != ',')
275 if (*mode_string == 0)
277 mc[used].flag = MODE_DONE;
286 /* Return a file mode change operation that sets permissions to match those
287 of REF_FILE. Return NULL (setting errno) if REF_FILE can't be accessed. */
290 mode_create_from_ref (const char *ref_file)
292 struct stat ref_stats;
294 if (stat (ref_file, &ref_stats) != 0)
296 return make_node_op_equals (ref_stats.st_mode, CHMOD_MODE_BITS);
299 /* Return the file mode bits of OLDMODE (which is the mode of a
300 directory if DIR), assuming the umask is UMASK_VALUE, adjusted as
301 indicated by the list of change operations CHANGES. If DIR, the
302 type 'X' change affects the returned value even if no execute bits
303 were set in OLDMODE, and set user and group ID bits are preserved
304 unless CHANGES mentioned them. If PMODE_BITS is not null, store into
305 *PMODE_BITS a mask denoting file mode bits that are affected by
308 The returned value and *PMODE_BITS contain only file mode bits.
309 For example, they have the S_IFMT bits cleared on a standard
313 mode_adjust (mode_t oldmode, bool dir, mode_t umask_value,
314 struct mode_change const *changes, mode_t *pmode_bits)
316 /* The adjusted mode. */
317 mode_t newmode = oldmode & CHMOD_MODE_BITS;
319 /* File mode bits that CHANGES cares about. */
320 mode_t mode_bits = 0;
322 for (; changes->flag != MODE_DONE; changes++)
324 mode_t affected = changes->affected;
326 (dir ? S_ISUID | S_ISGID : 0) & ~ changes->mentioned;
327 mode_t value = changes->value;
329 switch (changes->flag)
331 case MODE_ORDINARY_CHANGE:
334 case MODE_COPY_EXISTING:
335 /* Isolate in `value' the bits in `newmode' to copy. */
338 /* Copy the isolated bits to the other two parts. */
339 value |= ((value & (S_IRUSR | S_IRGRP | S_IROTH)
340 ? S_IRUSR | S_IRGRP | S_IROTH : 0)
341 | (value & (S_IWUSR | S_IWGRP | S_IWOTH)
342 ? S_IWUSR | S_IWGRP | S_IWOTH : 0)
343 | (value & (S_IXUSR | S_IXGRP | S_IXOTH)
344 ? S_IXUSR | S_IXGRP | S_IXOTH : 0));
347 case MODE_X_IF_ANY_X:
348 /* Affect the execute bits if execute bits are already set
349 or if the file is a directory. */
350 if ((newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) | dir)
351 value |= S_IXUSR | S_IXGRP | S_IXOTH;
355 /* If WHO was specified, limit the change to the affected bits.
356 Otherwise, apply the umask. Either way, omit changes as
358 value &= (affected ? affected : ~umask_value) & ~ omit_change;
363 /* If WHO was specified, preserve the previous values of
364 bits that are not affected by this change operation.
365 Otherwise, clear all the bits. */
367 mode_t preserved = (affected ? ~affected : 0) | omit_change;
368 mode_bits |= CHMOD_MODE_BITS & ~preserved;
369 newmode = (newmode & preserved) | value;
386 *pmode_bits = mode_bits;