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