- Updated the address for the FSF in the opening comment.
[rsync/rsync.git] / chmod.c
CommitLineData
0f78b815
WD
1/*
2 * Implement the core of the --chmod option.
3 *
4 * Copyright (C) 2002 Scott Howard
5 * Copyright (C) 2005, 2006 Wayne Davison
6 *
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.
11 *
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.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
0c983c1f
WD
22#include "rsync.h"
23
a41d1106 24extern mode_t orig_umask;
0c983c1f
WD
25
26#define FLAG_X_KEEP (1<<0)
27#define FLAG_DIRS_ONLY (1<<1)
28#define FLAG_FILES_ONLY (1<<2)
29
30struct chmod_mode_struct {
31 struct chmod_mode_struct *next;
32 int ModeAND, ModeOR;
33 char flags;
34};
35
36#define CHMOD_ADD 1
37#define CHMOD_SUB 2
38#define CHMOD_EQ 3
39
40#define STATE_ERROR 0
41#define STATE_1ST_HALF 1
42#define STATE_2ND_HALF 2
43
44/* Parse a chmod-style argument, and break it down into one or more AND/OR
15b03ab1
WD
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. */
47struct chmod_mode_struct *parse_chmod(const char *modestr,
48 struct chmod_mode_struct **root_mode_ptr)
0c983c1f
WD
49{
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,
cf9b4794 53 *prev_mode = NULL;
0c983c1f
WD
54
55 while (state != STATE_ERROR) {
56 if (!*modestr || *modestr == ',') {
57 int bits;
58
59 if (!op) {
60 state = STATE_ERROR;
61 break;
62 }
63 prev_mode = curr_mode;
64 curr_mode = new_array(struct chmod_mode_struct, 1);
65 if (prev_mode)
66 prev_mode->next = curr_mode;
67 else
68 first_mode = curr_mode;
69 curr_mode->next = NULL;
70
71 if (where)
72 bits = where * what;
73 else {
74 where = 0111;
75 bits = (where * what) & ~orig_umask;
76 }
77
78 switch (op) {
79 case CHMOD_ADD:
a41d1106 80 curr_mode->ModeAND = CHMOD_BITS;
0c983c1f
WD
81 curr_mode->ModeOR = bits + topoct;
82 break;
83 case CHMOD_SUB:
a41d1106 84 curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
0c983c1f
WD
85 curr_mode->ModeOR = 0;
86 break;
87 case CHMOD_EQ:
a41d1106 88 curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
0c983c1f
WD
89 curr_mode->ModeOR = bits + topoct;
90 break;
91 }
92
93 curr_mode->flags = flags;
94
95 if (!*modestr)
96 break;
97 modestr++;
98
99 state = STATE_1ST_HALF;
100 where = what = op = topoct = topbits = flags = 0;
101 }
102
103 if (state != STATE_2ND_HALF) {
104 switch (*modestr) {
105 case 'D':
106 if (flags & FLAG_FILES_ONLY)
107 state = STATE_ERROR;
108 flags |= FLAG_DIRS_ONLY;
109 break;
110 case 'F':
111 if (flags & FLAG_DIRS_ONLY)
112 state = STATE_ERROR;
113 flags |= FLAG_FILES_ONLY;
114 break;
115 case 'u':
116 where |= 0100;
117 topbits |= 04000;
118 break;
119 case 'g':
120 where |= 0010;
121 topbits |= 02000;
122 break;
123 case 'o':
124 where |= 0001;
125 break;
126 case 'a':
127 where |= 0111;
128 break;
129 case '+':
130 op = CHMOD_ADD;
131 state = STATE_2ND_HALF;
132 break;
133 case '-':
134 op = CHMOD_SUB;
135 state = STATE_2ND_HALF;
136 break;
137 case '=':
138 op = CHMOD_EQ;
139 state = STATE_2ND_HALF;
140 break;
141 default:
142 state = STATE_ERROR;
143 break;
144 }
145 } else {
146 switch (*modestr) {
147 case 'r':
148 what |= 4;
149 break;
150 case 'w':
151 what |= 2;
152 break;
153 case 'X':
154 flags |= FLAG_X_KEEP;
155 /* FALL THROUGH */
156 case 'x':
157 what |= 1;
158 break;
159 case 's':
160 if (topbits)
161 topoct |= topbits;
162 else
163 topoct = 04000;
164 break;
165 case 't':
166 topoct |= 01000;
167 break;
168 default:
169 state = STATE_ERROR;
170 break;
171 }
172 }
173 modestr++;
174 }
175
176 if (state == STATE_ERROR) {
177 free_chmod_mode(first_mode);
15b03ab1 178 return NULL;
0c983c1f 179 }
cf9b4794 180
81b096fe
WD
181 if (!(curr_mode = *root_mode_ptr))
182 *root_mode_ptr = first_mode;
183 else {
184 while (curr_mode->next)
185 curr_mode = curr_mode->next;
186 curr_mode->next = first_mode;
cf9b4794
WD
187 }
188
15b03ab1 189 return first_mode;
0c983c1f
WD
190}
191
192
193/* Takes an existing file permission and a list of AND/OR changes, and
194 * create a new permissions. */
195int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
196{
197 int IsX = mode & 0111;
a41d1106 198 int NonPerm = mode & ~CHMOD_BITS;
0c983c1f
WD
199
200 for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
201 if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
202 continue;
203 if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
204 continue;
205 mode &= chmod_modes->ModeAND;
206 if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
207 mode |= chmod_modes->ModeOR & ~0111;
208 else
209 mode |= chmod_modes->ModeOR;
210 }
211
212 return mode | NonPerm;
213}
214
215/* Free the linked list created by parse_chmod. */
216int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
217{
218 struct chmod_mode_struct *next;
219
220 while (chmod_modes) {
221 next = chmod_modes->next;
222 free(chmod_modes);
223 chmod_modes = next;
224 }
225 return 0;
226}