2 * Implement the core of the --chmod option.
4 * Copyright (C) 2002 Scott Howard
5 * Copyright (C) 2005, 2006 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 2 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, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
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;
41 #define STATE_1ST_HALF 1
42 #define STATE_2ND_HALF 2
44 /* Parse a chmod-style argument, and break it down into one or more AND/OR
45 * pairs in a linked list. We return a pointer to new items on succcess
46 * (appending the items to the specified list), or NULL on error. */
47 struct chmod_mode_struct *parse_chmod(const char *modestr,
48 struct chmod_mode_struct **root_mode_ptr)
50 int state = STATE_1ST_HALF;
51 int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
52 struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
55 while (state != STATE_ERROR) {
56 if (!*modestr || *modestr == ',') {
63 prev_mode = curr_mode;
64 curr_mode = new_array(struct chmod_mode_struct, 1);
66 prev_mode->next = curr_mode;
68 first_mode = curr_mode;
69 curr_mode->next = NULL;
75 bits = (where * what) & ~orig_umask;
80 curr_mode->ModeAND = CHMOD_BITS;
81 curr_mode->ModeOR = bits + topoct;
84 curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
85 curr_mode->ModeOR = 0;
88 curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
89 curr_mode->ModeOR = bits + topoct;
93 curr_mode->flags = flags;
99 state = STATE_1ST_HALF;
100 where = what = op = topoct = topbits = flags = 0;
103 if (state != STATE_2ND_HALF) {
106 if (flags & FLAG_FILES_ONLY)
108 flags |= FLAG_DIRS_ONLY;
111 if (flags & FLAG_DIRS_ONLY)
113 flags |= FLAG_FILES_ONLY;
131 state = STATE_2ND_HALF;
135 state = STATE_2ND_HALF;
139 state = STATE_2ND_HALF;
154 flags |= FLAG_X_KEEP;
176 if (state == STATE_ERROR) {
177 free_chmod_mode(first_mode);
181 if (!(curr_mode = *root_mode_ptr))
182 *root_mode_ptr = first_mode;
184 while (curr_mode->next)
185 curr_mode = curr_mode->next;
186 curr_mode->next = first_mode;
193 /* Takes an existing file permission and a list of AND/OR changes, and
194 * create a new permissions. */
195 int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
197 int IsX = mode & 0111;
198 int NonPerm = mode & ~CHMOD_BITS;
200 for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
201 if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
203 if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
205 mode &= chmod_modes->ModeAND;
206 if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
207 mode |= chmod_modes->ModeOR & ~0111;
209 mode |= chmod_modes->ModeOR;
212 return mode | NonPerm;
215 /* Free the linked list created by parse_chmod. */
216 int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
218 struct chmod_mode_struct *next;
220 while (chmod_modes) {
221 next = chmod_modes->next;