1 /* modechange.c -- file mode manipulation
2 Copyright (C) 1989, 1990, 1997, 1998, 1999 Free Software Foundation, Inc.
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)
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.
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. */
18 /* Written by David MacKenzie <djm@ai.mit.edu> */
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
31 #include "modechange.h"
45 #if STAT_MACROS_BROKEN
49 #if !defined(S_ISDIR) && defined(S_IFDIR)
50 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
54 # define S_ISUID 04000
57 # define S_ISGID 04000
60 # define S_ISVTX 01000
99 /* All the mode bits that can be affected by chmod. */
100 #define CHMOD_MODE_BITS \
101 (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
103 /* Return newly allocated memory to hold one element of type TYPE. */
104 #define talloc(type) ((type *) malloc (sizeof (type)))
106 /* Create a mode_change entry with the specified `=ddd'-style
107 mode change operation, where NEW_MODE is `ddd'. Return the
108 new entry, or NULL upon failure. */
110 static struct mode_change
*
111 make_node_op_equals (mode_t new_mode
)
113 struct mode_change
*p
;
114 p
= talloc (struct mode_change
);
121 p
->affected
= CHMOD_MODE_BITS
; /* Affect all permissions. */
125 /* Append entry E to the end of the link list with the specified
129 mode_append_entry (struct mode_change
**head
,
130 struct mode_change
**tail
,
131 struct mode_change
*e
)
142 /* Return a linked list of file mode change operations created from
143 MODE_STRING, an ASCII string that contains either an octal number
144 specifying an absolute mode, or symbolic mode change operations with
146 [ugoa...][[+-=][rwxXstugo...]...][,...]
147 MASKED_OPS is a bitmask indicating which symbolic mode operators (=+-)
148 should not affect bits set in the umask when no users are given.
149 Operators not selected in MASKED_OPS ignore the umask.
151 Return MODE_INVALID if `mode_string' does not contain a valid
152 representation of file mode change operations;
153 return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */
156 mode_compile (const char *mode_string
, unsigned int masked_ops
)
158 struct mode_change
*head
; /* First element of the linked list. */
159 struct mode_change
*tail
; /* An element of the linked list. */
160 unsigned long mode_value
; /* The mode value, if octal. */
161 char *string_end
; /* Pointer to end of parsed value. */
162 mode_t umask_value
; /* The umask value (surprise). */
169 if (xstrtoul (mode_string
, &string_end
, 8, &mode_value
, "") == LONGINT_OK
)
171 struct mode_change
*p
;
172 if (mode_value
> CHMOD_MODE_BITS
)
174 p
= make_node_op_equals ((mode_t
) mode_value
);
176 return MODE_MEMORY_EXHAUSTED
;
177 mode_append_entry (&head
, &tail
, p
);
181 umask_value
= umask (0);
182 umask (umask_value
); /* Restore the old value. */
185 /* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */
188 /* Which bits in the mode are operated on. */
189 mode_t affected_bits
= 0;
190 /* `affected_bits' modified by umask. */
191 mode_t affected_masked
;
192 /* Operators to actually use umask on. */
193 unsigned ops_to_mask
= 0;
199 /* Turn on all the bits in `affected_bits' for each group given. */
200 for (++mode_string
;; ++mode_string
)
201 switch (*mode_string
)
204 affected_bits
|= S_ISUID
| S_IRWXU
;
207 affected_bits
|= S_ISGID
| S_IRWXG
;
210 affected_bits
|= S_ISVTX
| S_IRWXO
;
213 affected_bits
|= CHMOD_MODE_BITS
;
216 goto no_more_affected
;
220 /* If none specified, affect all bits, except perhaps those
227 affected_bits
= CHMOD_MODE_BITS
;
228 ops_to_mask
= masked_ops
;
231 while (*mode_string
== '=' || *mode_string
== '+' || *mode_string
== '-')
233 struct mode_change
*change
= talloc (struct mode_change
);
237 return MODE_MEMORY_EXHAUSTED
;
241 change
->op
= *mode_string
; /* One of "=+-". */
242 affected_masked
= affected_bits
;
244 /* Per the Single Unix Spec, if `who' is not specified and the
245 `=' operator is used, then clear all the bits first. */
246 if (!who_specified_p
&&
247 ops_to_mask
& (*mode_string
== '=' ? MODE_MASK_EQUALS
: 0))
249 struct mode_change
*p
= make_node_op_equals (0);
251 return MODE_MEMORY_EXHAUSTED
;
252 mode_append_entry (&head
, &tail
, p
);
255 if (ops_to_mask
& (*mode_string
== '=' ? MODE_MASK_EQUALS
256 : *mode_string
== '+' ? MODE_MASK_PLUS
258 affected_masked
&= ~umask_value
;
259 change
->affected
= affected_masked
;
263 /* Add the element to the tail of the list, so the operations
264 are performed in the correct order. */
265 mode_append_entry (&head
, &tail
, change
);
267 /* Set `value' according to the bits set in `affected_masked'. */
268 for (++mode_string
;; ++mode_string
)
269 switch (*mode_string
)
272 change
->value
|= ((S_IRUSR
| S_IRGRP
| S_IROTH
)
276 change
->value
|= ((S_IWUSR
| S_IWGRP
| S_IWOTH
)
280 change
->flags
|= MODE_X_IF_ANY_X
;
283 change
->value
|= ((S_IXUSR
| S_IXGRP
| S_IXOTH
)
287 /* Set the setuid/gid bits if `u' or `g' is selected. */
288 change
->value
|= (S_ISUID
| S_ISGID
) & affected_masked
;
291 /* Set the "save text image" bit if `o' is selected. */
292 change
->value
|= S_ISVTX
& affected_masked
;
295 /* Set the affected bits to the value of the `u' bits
299 change
->value
= S_IRWXU
;
300 change
->flags
|= MODE_COPY_EXISTING
;
303 /* Set the affected bits to the value of the `g' bits
307 change
->value
= S_IRWXG
;
308 change
->flags
|= MODE_COPY_EXISTING
;
311 /* Set the affected bits to the value of the `o' bits
315 change
->value
= S_IRWXO
;
316 change
->flags
|= MODE_COPY_EXISTING
;
323 } while (*mode_string
== ',');
324 if (*mode_string
== 0)
331 /* Return a file mode change operation that sets permissions to match those
332 of REF_FILE. Return MODE_BAD_REFERENCE if REF_FILE can't be accessed. */
335 mode_create_from_ref (const char *ref_file
)
337 struct mode_change
*change
; /* the only change element */
338 struct stat ref_stats
;
340 if (stat (ref_file
, &ref_stats
))
341 return MODE_BAD_REFERENCE
;
343 change
= talloc (struct mode_change
);
346 return MODE_MEMORY_EXHAUSTED
;
350 change
->affected
= CHMOD_MODE_BITS
;
351 change
->value
= ref_stats
.st_mode
;
357 /* Return file mode OLDMODE, adjusted as indicated by the list of change
358 operations CHANGES. If OLDMODE is a directory, the type `X'
359 change affects it even if no execute bits were set in OLDMODE.
360 The returned value has the S_IFMT bits cleared. */
363 mode_adjust (mode_t oldmode
, const struct mode_change
*changes
)
365 mode_t newmode
; /* The adjusted mode and one operand. */
366 mode_t value
; /* The other operand. */
368 newmode
= oldmode
& CHMOD_MODE_BITS
;
370 for (; changes
; changes
= changes
->next
)
372 if (changes
->flags
& MODE_COPY_EXISTING
)
374 /* Isolate in `value' the bits in `newmode' to copy, given in
375 the mask `changes->value'. */
376 value
= newmode
& changes
->value
;
378 if (changes
->value
& S_IRWXU
)
379 /* Copy `u' permissions onto `g' and `o'. */
380 value
|= ((value
& S_IRUSR
? S_IRGRP
| S_IROTH
: 0)
381 | (value
& S_IWUSR
? S_IWGRP
| S_IROTH
: 0)
382 | (value
& S_IXUSR
? S_IXGRP
| S_IXOTH
: 0));
383 else if (changes
->value
& S_IRWXG
)
384 /* Copy `g' permissions onto `u' and `o'. */
385 value
|= ((value
& S_IRGRP
? S_IRUSR
| S_IROTH
: 0)
386 | (value
& S_IWGRP
? S_IWUSR
| S_IROTH
: 0)
387 | (value
& S_IXGRP
? S_IXUSR
| S_IXOTH
: 0));
389 /* Copy `o' permissions onto `u' and `g'. */
390 value
|= ((value
& S_IROTH
? S_IRUSR
| S_IRGRP
: 0)
391 | (value
& S_IWOTH
? S_IWUSR
| S_IRGRP
: 0)
392 | (value
& S_IXOTH
? S_IXUSR
| S_IXGRP
: 0));
394 /* In order to change only `u', `g', or `o' permissions,
395 or some combination thereof, clear unselected bits.
396 This can not be done in mode_compile because the value
397 to which the `changes->affected' mask is applied depends
398 on the old mode of each file. */
399 value
&= changes
->affected
;
403 value
= changes
->value
;
404 /* If `X', do not affect the execute bits if the file is not a
405 directory and no execute bits are already set. */
406 if ((changes
->flags
& MODE_X_IF_ANY_X
)
407 && !S_ISDIR (oldmode
)
408 && (newmode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)) == 0)
409 /* Clear the execute bits. */
410 value
&= ~ (S_IXUSR
| S_IXGRP
| S_IXOTH
);
416 /* Preserve the previous values in `newmode' of bits that are
417 not affected by this change operation. */
418 newmode
= (newmode
& ~changes
->affected
) | value
;
431 /* Free the memory used by the list of file mode change operations
435 mode_free (register struct mode_change
*changes
)
437 register struct mode_change
*next
;
441 next
= changes
->next
;