2 * Implement the core of the --chmod option.
4 * Copyright (C) 2002 Scott Howard
5 * Copyright (C) 2005-2020 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.
24 extern mode_t orig_umask
;
26 #define FLAG_X_KEEP (1<<0)
27 #define FLAG_DIRS_ONLY (1<<1)
28 #define FLAG_FILES_ONLY (1<<2)
30 struct chmod_mode_struct
{
31 struct chmod_mode_struct
*next
;
42 #define STATE_1ST_HALF 1
43 #define STATE_2ND_HALF 2
44 #define STATE_OCTAL_NUM 3
46 /* Parse a chmod-style argument, and break it down into one or more AND/OR
47 * pairs in a linked list. We return a pointer to new items on success
48 * (appending the items to the specified list), or NULL on error. */
49 struct chmod_mode_struct
*parse_chmod(const char *modestr
,
50 struct chmod_mode_struct
**root_mode_ptr
)
52 int state
= STATE_1ST_HALF
;
53 int where
= 0, what
= 0, op
= 0, topbits
= 0, topoct
= 0, flags
= 0;
54 struct chmod_mode_struct
*first_mode
= NULL
, *curr_mode
= NULL
,
57 while (state
!= STATE_ERROR
) {
58 if (!*modestr
|| *modestr
== ',') {
65 prev_mode
= curr_mode
;
66 curr_mode
= new_array(struct chmod_mode_struct
, 1);
68 prev_mode
->next
= curr_mode
;
70 first_mode
= curr_mode
;
71 curr_mode
->next
= NULL
;
77 bits
= (where
* what
) & ~orig_umask
;
82 curr_mode
->ModeAND
= CHMOD_BITS
;
83 curr_mode
->ModeOR
= bits
+ topoct
;
86 curr_mode
->ModeAND
= CHMOD_BITS
- bits
- topoct
;
87 curr_mode
->ModeOR
= 0;
90 curr_mode
->ModeAND
= CHMOD_BITS
- (where
* 7) - (topoct
? topbits
: 0);
91 curr_mode
->ModeOR
= bits
+ topoct
;
94 curr_mode
->ModeAND
= 0;
95 curr_mode
->ModeOR
= bits
;
99 curr_mode
->flags
= flags
;
105 state
= STATE_1ST_HALF
;
106 where
= what
= op
= topoct
= topbits
= flags
= 0;
113 if (flags
& FLAG_FILES_ONLY
)
115 flags
|= FLAG_DIRS_ONLY
;
118 if (flags
& FLAG_DIRS_ONLY
)
120 flags
|= FLAG_FILES_ONLY
;
138 state
= STATE_2ND_HALF
;
142 state
= STATE_2ND_HALF
;
146 state
= STATE_2ND_HALF
;
149 if (isDigit(modestr
) && *modestr
< '8' && !where
) {
151 state
= STATE_OCTAL_NUM
;
153 what
= *modestr
- '0';
168 flags
|= FLAG_X_KEEP
;
187 case STATE_OCTAL_NUM
:
188 if (isDigit(modestr
) && *modestr
< '8') {
189 what
= what
*8 + *modestr
- '0';
190 if (what
> CHMOD_BITS
)
199 if (state
== STATE_ERROR
) {
200 free_chmod_mode(first_mode
);
204 if (!(curr_mode
= *root_mode_ptr
))
205 *root_mode_ptr
= first_mode
;
207 while (curr_mode
->next
)
208 curr_mode
= curr_mode
->next
;
209 curr_mode
->next
= first_mode
;
216 /* Takes an existing file permission and a list of AND/OR changes, and
217 * create a new permissions. */
218 int tweak_mode(int mode
, struct chmod_mode_struct
*chmod_modes
)
220 int IsX
= mode
& 0111;
221 int NonPerm
= mode
& ~CHMOD_BITS
;
223 for ( ; chmod_modes
; chmod_modes
= chmod_modes
->next
) {
224 if ((chmod_modes
->flags
& FLAG_DIRS_ONLY
) && !S_ISDIR(NonPerm
))
226 if ((chmod_modes
->flags
& FLAG_FILES_ONLY
) && S_ISDIR(NonPerm
))
228 mode
&= chmod_modes
->ModeAND
;
229 if ((chmod_modes
->flags
& FLAG_X_KEEP
) && !IsX
&& !S_ISDIR(NonPerm
))
230 mode
|= chmod_modes
->ModeOR
& ~0111;
232 mode
|= chmod_modes
->ModeOR
;
235 return mode
| NonPerm
;
238 /* Free the linked list created by parse_chmod. */
239 int free_chmod_mode(struct chmod_mode_struct
*chmod_modes
)
241 struct chmod_mode_struct
*next
;
243 while (chmod_modes
) {
244 next
= chmod_modes
->next
;