2 * Implement the core of the --chmod option.
4 * Copyright (C) 2002 Scott Howard
5 * Copyright (C) 2005-2008 Wayne Davison
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, visit the http://fsf.org website.
23 extern mode_t orig_umask
;
25 #define FLAG_X_KEEP (1<<0)
26 #define FLAG_DIRS_ONLY (1<<1)
27 #define FLAG_FILES_ONLY (1<<2)
29 struct chmod_mode_struct
{
30 struct chmod_mode_struct
*next
;
40 #define STATE_1ST_HALF 1
41 #define STATE_2ND_HALF 2
43 /* Parse a chmod-style argument, and break it down into one or more AND/OR
44 * pairs in a linked list. We return a pointer to new items on succcess
45 * (appending the items to the specified list), or NULL on error. */
46 struct chmod_mode_struct
*parse_chmod(const char *modestr
,
47 struct chmod_mode_struct
**root_mode_ptr
)
49 int state
= STATE_1ST_HALF
;
50 int where
= 0, what
= 0, op
= 0, topbits
= 0, topoct
= 0, flags
= 0;
51 struct chmod_mode_struct
*first_mode
= NULL
, *curr_mode
= NULL
,
54 while (state
!= STATE_ERROR
) {
55 if (!*modestr
|| *modestr
== ',') {
62 prev_mode
= curr_mode
;
63 curr_mode
= new_array(struct chmod_mode_struct
, 1);
65 prev_mode
->next
= curr_mode
;
67 first_mode
= curr_mode
;
68 curr_mode
->next
= NULL
;
74 bits
= (where
* what
) & ~orig_umask
;
79 curr_mode
->ModeAND
= CHMOD_BITS
;
80 curr_mode
->ModeOR
= bits
+ topoct
;
83 curr_mode
->ModeAND
= CHMOD_BITS
- bits
- topoct
;
84 curr_mode
->ModeOR
= 0;
87 curr_mode
->ModeAND
= CHMOD_BITS
- (where
* 7) - (topoct
? topbits
: 0);
88 curr_mode
->ModeOR
= bits
+ topoct
;
92 curr_mode
->flags
= flags
;
98 state
= STATE_1ST_HALF
;
99 where
= what
= op
= topoct
= topbits
= flags
= 0;
102 if (state
!= STATE_2ND_HALF
) {
105 if (flags
& FLAG_FILES_ONLY
)
107 flags
|= FLAG_DIRS_ONLY
;
110 if (flags
& FLAG_DIRS_ONLY
)
112 flags
|= FLAG_FILES_ONLY
;
130 state
= STATE_2ND_HALF
;
134 state
= STATE_2ND_HALF
;
138 state
= STATE_2ND_HALF
;
153 flags
|= FLAG_X_KEEP
;
175 if (state
== STATE_ERROR
) {
176 free_chmod_mode(first_mode
);
180 if (!(curr_mode
= *root_mode_ptr
))
181 *root_mode_ptr
= first_mode
;
183 while (curr_mode
->next
)
184 curr_mode
= curr_mode
->next
;
185 curr_mode
->next
= first_mode
;
192 /* Takes an existing file permission and a list of AND/OR changes, and
193 * create a new permissions. */
194 int tweak_mode(int mode
, struct chmod_mode_struct
*chmod_modes
)
196 int IsX
= mode
& 0111;
197 int NonPerm
= mode
& ~CHMOD_BITS
;
199 for ( ; chmod_modes
; chmod_modes
= chmod_modes
->next
) {
200 if ((chmod_modes
->flags
& FLAG_DIRS_ONLY
) && !S_ISDIR(NonPerm
))
202 if ((chmod_modes
->flags
& FLAG_FILES_ONLY
) && S_ISDIR(NonPerm
))
204 mode
&= chmod_modes
->ModeAND
;
205 if ((chmod_modes
->flags
& FLAG_X_KEEP
) && !IsX
&& !S_ISDIR(NonPerm
))
206 mode
|= chmod_modes
->ModeOR
& ~0111;
208 mode
|= chmod_modes
->ModeOR
;
211 return mode
| NonPerm
;
214 /* Free the linked list created by parse_chmod. */
215 int free_chmod_mode(struct chmod_mode_struct
*chmod_modes
)
217 struct chmod_mode_struct
*next
;
219 while (chmod_modes
) {
220 next
= chmod_modes
->next
;