Allow a failure of EINVAL to mean no ACLs are available.
[rsync/rsync.git] / chmod.c
CommitLineData
0f78b815
WD
1/*
2 * Implement the core of the --chmod option.
3 *
4 * Copyright (C) 2002 Scott Howard
b3bf9b9d 5 * Copyright (C) 2005-2009 Wayne Davison
0f78b815
WD
6 *
7 * This program is free software; you can redistribute it and/or modify
8e41b68e
WD
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.
0f78b815
WD
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 *
e7c67065 17 * You should have received a copy of the GNU General Public License along
4fd842f9 18 * with this program; if not, visit the http://fsf.org website.
0f78b815
WD
19 */
20
0c983c1f 21#include "rsync.h"
aef2b8ce 22#include "itypes.h"
0c983c1f 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
aef2b8ce 39#define CHMOD_SET 4
0c983c1f
WD
40
41#define STATE_ERROR 0
42#define STATE_1ST_HALF 1
43#define STATE_2ND_HALF 2
aef2b8ce 44#define STATE_OCTAL_NUM 3
0c983c1f
WD
45
46/* Parse a chmod-style argument, and break it down into one or more AND/OR
15b03ab1
WD
47 * pairs in a linked list. We return a pointer to new items on succcess
48 * (appending the items to the specified list), or NULL on error. */
49struct chmod_mode_struct *parse_chmod(const char *modestr,
50 struct chmod_mode_struct **root_mode_ptr)
0c983c1f
WD
51{
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,
cf9b4794 55 *prev_mode = NULL;
0c983c1f
WD
56
57 while (state != STATE_ERROR) {
58 if (!*modestr || *modestr == ',') {
59 int bits;
60
61 if (!op) {
62 state = STATE_ERROR;
63 break;
64 }
65 prev_mode = curr_mode;
66 curr_mode = new_array(struct chmod_mode_struct, 1);
67 if (prev_mode)
68 prev_mode->next = curr_mode;
69 else
70 first_mode = curr_mode;
71 curr_mode->next = NULL;
72
73 if (where)
74 bits = where * what;
75 else {
76 where = 0111;
77 bits = (where * what) & ~orig_umask;
78 }
79
80 switch (op) {
81 case CHMOD_ADD:
a41d1106 82 curr_mode->ModeAND = CHMOD_BITS;
0c983c1f
WD
83 curr_mode->ModeOR = bits + topoct;
84 break;
85 case CHMOD_SUB:
a41d1106 86 curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
0c983c1f
WD
87 curr_mode->ModeOR = 0;
88 break;
89 case CHMOD_EQ:
a41d1106 90 curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
0c983c1f
WD
91 curr_mode->ModeOR = bits + topoct;
92 break;
aef2b8ce
WD
93 case CHMOD_SET:
94 curr_mode->ModeAND = 0;
95 curr_mode->ModeOR = bits;
96 break;
0c983c1f
WD
97 }
98
99 curr_mode->flags = flags;
100
101 if (!*modestr)
102 break;
103 modestr++;
104
105 state = STATE_1ST_HALF;
106 where = what = op = topoct = topbits = flags = 0;
107 }
108
aef2b8ce
WD
109 switch (state) {
110 case STATE_1ST_HALF:
0c983c1f
WD
111 switch (*modestr) {
112 case 'D':
113 if (flags & FLAG_FILES_ONLY)
114 state = STATE_ERROR;
eb8f5c74 115 flags |= FLAG_DIRS_ONLY;
0c983c1f
WD
116 break;
117 case 'F':
118 if (flags & FLAG_DIRS_ONLY)
119 state = STATE_ERROR;
eb8f5c74 120 flags |= FLAG_FILES_ONLY;
0c983c1f
WD
121 break;
122 case 'u':
123 where |= 0100;
124 topbits |= 04000;
125 break;
126 case 'g':
127 where |= 0010;
128 topbits |= 02000;
129 break;
130 case 'o':
131 where |= 0001;
132 break;
133 case 'a':
134 where |= 0111;
135 break;
136 case '+':
137 op = CHMOD_ADD;
138 state = STATE_2ND_HALF;
139 break;
140 case '-':
141 op = CHMOD_SUB;
142 state = STATE_2ND_HALF;
143 break;
144 case '=':
145 op = CHMOD_EQ;
146 state = STATE_2ND_HALF;
147 break;
148 default:
aef2b8ce
WD
149 if (isDigit(modestr) && *modestr < '8' && !where) {
150 op = CHMOD_SET;
151 state = STATE_OCTAL_NUM;
152 where = 1;
153 what = *modestr - '0';
154 } else
155 state = STATE_ERROR;
0c983c1f
WD
156 break;
157 }
aef2b8ce
WD
158 break;
159 case STATE_2ND_HALF:
0c983c1f
WD
160 switch (*modestr) {
161 case 'r':
162 what |= 4;
163 break;
164 case 'w':
165 what |= 2;
166 break;
167 case 'X':
eb8f5c74 168 flags |= FLAG_X_KEEP;
0c983c1f
WD
169 /* FALL THROUGH */
170 case 'x':
171 what |= 1;
172 break;
173 case 's':
174 if (topbits)
175 topoct |= topbits;
176 else
177 topoct = 04000;
178 break;
179 case 't':
180 topoct |= 01000;
181 break;
182 default:
183 state = STATE_ERROR;
184 break;
185 }
aef2b8ce
WD
186 break;
187 case STATE_OCTAL_NUM:
188 if (isDigit(modestr) && *modestr < '8') {
189 what = what*8 + *modestr - '0';
190 if (what > CHMOD_BITS)
191 state = STATE_ERROR;
192 } else
193 state = STATE_ERROR;
194 break;
0c983c1f
WD
195 }
196 modestr++;
197 }
198
199 if (state == STATE_ERROR) {
200 free_chmod_mode(first_mode);
15b03ab1 201 return NULL;
0c983c1f 202 }
cf9b4794 203
81b096fe
WD
204 if (!(curr_mode = *root_mode_ptr))
205 *root_mode_ptr = first_mode;
206 else {
207 while (curr_mode->next)
208 curr_mode = curr_mode->next;
209 curr_mode->next = first_mode;
cf9b4794
WD
210 }
211
15b03ab1 212 return first_mode;
0c983c1f
WD
213}
214
215
216/* Takes an existing file permission and a list of AND/OR changes, and
217 * create a new permissions. */
218int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
219{
220 int IsX = mode & 0111;
a41d1106 221 int NonPerm = mode & ~CHMOD_BITS;
0c983c1f
WD
222
223 for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
224 if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
225 continue;
226 if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
227 continue;
228 mode &= chmod_modes->ModeAND;
229 if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
230 mode |= chmod_modes->ModeOR & ~0111;
231 else
232 mode |= chmod_modes->ModeOR;
233 }
234
235 return mode | NonPerm;
236}
237
238/* Free the linked list created by parse_chmod. */
239int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
240{
241 struct chmod_mode_struct *next;
242
243 while (chmod_modes) {
244 next = chmod_modes->next;
245 free(chmod_modes);
246 chmod_modes = next;
247 }
248 return 0;
249}