Preparing for release of 2.6.7pre2
[rsync/rsync.git] / chmod.c
... / ...
CommitLineData
1#include "rsync.h"
2
3extern int orig_umask;
4
5#define FLAG_X_KEEP (1<<0)
6#define FLAG_DIRS_ONLY (1<<1)
7#define FLAG_FILES_ONLY (1<<2)
8
9struct chmod_mode_struct {
10 struct chmod_mode_struct *next;
11 int ModeAND, ModeOR;
12 char flags;
13};
14
15#define CHMOD_ADD 1
16#define CHMOD_SUB 2
17#define CHMOD_EQ 3
18
19#define STATE_ERROR 0
20#define STATE_1ST_HALF 1
21#define STATE_2ND_HALF 2
22
23/* Parse a chmod-style argument, and break it down into one or more AND/OR
24 * pairs in a linked list. We use a state machine to walk through the
25 * options. */
26int parse_chmod(const char *modestr, struct chmod_mode_struct **root_mode_ptr)
27{
28 int state = STATE_1ST_HALF;
29 int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
30 struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
31 *prev_mode = NULL;
32
33 while (state != STATE_ERROR) {
34 if (!*modestr || *modestr == ',') {
35 int bits;
36
37 if (!op) {
38 state = STATE_ERROR;
39 break;
40 }
41 prev_mode = curr_mode;
42 curr_mode = new_array(struct chmod_mode_struct, 1);
43 if (prev_mode)
44 prev_mode->next = curr_mode;
45 else
46 first_mode = curr_mode;
47 curr_mode->next = NULL;
48
49 if (where)
50 bits = where * what;
51 else {
52 where = 0111;
53 bits = (where * what) & ~orig_umask;
54 }
55
56 switch (op) {
57 case CHMOD_ADD:
58 curr_mode->ModeAND = 07777;
59 curr_mode->ModeOR = bits + topoct;
60 break;
61 case CHMOD_SUB:
62 curr_mode->ModeAND = 07777 - bits - topoct;
63 curr_mode->ModeOR = 0;
64 break;
65 case CHMOD_EQ:
66 curr_mode->ModeAND = 07777 - (where * 7) - (topoct ? topbits : 0);
67 curr_mode->ModeOR = bits + topoct;
68 break;
69 }
70
71 curr_mode->flags = flags;
72
73 if (!*modestr)
74 break;
75 modestr++;
76
77 state = STATE_1ST_HALF;
78 where = what = op = topoct = topbits = flags = 0;
79 }
80
81 if (state != STATE_2ND_HALF) {
82 switch (*modestr) {
83 case 'D':
84 if (flags & FLAG_FILES_ONLY)
85 state = STATE_ERROR;
86 flags |= FLAG_DIRS_ONLY;
87 break;
88 case 'F':
89 if (flags & FLAG_DIRS_ONLY)
90 state = STATE_ERROR;
91 flags |= FLAG_FILES_ONLY;
92 break;
93 case 'u':
94 where |= 0100;
95 topbits |= 04000;
96 break;
97 case 'g':
98 where |= 0010;
99 topbits |= 02000;
100 break;
101 case 'o':
102 where |= 0001;
103 break;
104 case 'a':
105 where |= 0111;
106 break;
107 case '+':
108 op = CHMOD_ADD;
109 state = STATE_2ND_HALF;
110 break;
111 case '-':
112 op = CHMOD_SUB;
113 state = STATE_2ND_HALF;
114 break;
115 case '=':
116 op = CHMOD_EQ;
117 state = STATE_2ND_HALF;
118 break;
119 default:
120 state = STATE_ERROR;
121 break;
122 }
123 } else {
124 switch (*modestr) {
125 case 'r':
126 what |= 4;
127 break;
128 case 'w':
129 what |= 2;
130 break;
131 case 'X':
132 flags |= FLAG_X_KEEP;
133 /* FALL THROUGH */
134 case 'x':
135 what |= 1;
136 break;
137 case 's':
138 if (topbits)
139 topoct |= topbits;
140 else
141 topoct = 04000;
142 break;
143 case 't':
144 topoct |= 01000;
145 break;
146 default:
147 state = STATE_ERROR;
148 break;
149 }
150 }
151 modestr++;
152 }
153
154 if (state == STATE_ERROR) {
155 free_chmod_mode(first_mode);
156 return 0;
157 }
158
159 if (!(curr_mode = *root_mode_ptr))
160 *root_mode_ptr = first_mode;
161 else {
162 while (curr_mode->next)
163 curr_mode = curr_mode->next;
164 curr_mode->next = first_mode;
165 }
166
167 return 1;
168}
169
170
171/* Takes an existing file permission and a list of AND/OR changes, and
172 * create a new permissions. */
173int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
174{
175 int IsX = mode & 0111;
176 int NonPerm = mode & ~07777;
177
178 for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
179 if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
180 continue;
181 if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
182 continue;
183 mode &= chmod_modes->ModeAND;
184 if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
185 mode |= chmod_modes->ModeOR & ~0111;
186 else
187 mode |= chmod_modes->ModeOR;
188 }
189
190 return mode | NonPerm;
191}
192
193/* Free the linked list created by parse_chmod. */
194int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
195{
196 struct chmod_mode_struct *next;
197
198 while (chmod_modes) {
199 next = chmod_modes->next;
200 free(chmod_modes);
201 chmod_modes = next;
202 }
203 return 0;
204}