Added extern for io_write_phase.
[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. */
26struct chmod_mode_struct *parse_chmod(const char *modestr,
27 struct chmod_mode_struct *append_to)
28{
29 int state = STATE_1ST_HALF;
30 int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
31 struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
32 *prev_mode = NULL;
33
34 while (state != STATE_ERROR) {
35 if (!*modestr || *modestr == ',') {
36 int bits;
37
38 if (!op) {
39 state = STATE_ERROR;
40 break;
41 }
42 prev_mode = curr_mode;
43 curr_mode = new_array(struct chmod_mode_struct, 1);
44 if (prev_mode)
45 prev_mode->next = curr_mode;
46 else
47 first_mode = curr_mode;
48 curr_mode->next = NULL;
49
50 if (where)
51 bits = where * what;
52 else {
53 where = 0111;
54 bits = (where * what) & ~orig_umask;
55 }
56
57 switch (op) {
58 case CHMOD_ADD:
59 curr_mode->ModeAND = 07777;
60 curr_mode->ModeOR = bits + topoct;
61 break;
62 case CHMOD_SUB:
63 curr_mode->ModeAND = 07777 - bits - topoct;
64 curr_mode->ModeOR = 0;
65 break;
66 case CHMOD_EQ:
67 curr_mode->ModeAND = 07777 - (where * 7) - (topoct ? topbits : 0);
68 curr_mode->ModeOR = bits + topoct;
69 break;
70 }
71
72 curr_mode->flags = flags;
73
74 if (!*modestr)
75 break;
76 modestr++;
77
78 state = STATE_1ST_HALF;
79 where = what = op = topoct = topbits = flags = 0;
80 }
81
82 if (state != STATE_2ND_HALF) {
83 switch (*modestr) {
84 case 'D':
85 if (flags & FLAG_FILES_ONLY)
86 state = STATE_ERROR;
87 flags |= FLAG_DIRS_ONLY;
88 break;
89 case 'F':
90 if (flags & FLAG_DIRS_ONLY)
91 state = STATE_ERROR;
92 flags |= FLAG_FILES_ONLY;
93 break;
94 case 'u':
95 where |= 0100;
96 topbits |= 04000;
97 break;
98 case 'g':
99 where |= 0010;
100 topbits |= 02000;
101 break;
102 case 'o':
103 where |= 0001;
104 break;
105 case 'a':
106 where |= 0111;
107 break;
108 case '+':
109 op = CHMOD_ADD;
110 state = STATE_2ND_HALF;
111 break;
112 case '-':
113 op = CHMOD_SUB;
114 state = STATE_2ND_HALF;
115 break;
116 case '=':
117 op = CHMOD_EQ;
118 state = STATE_2ND_HALF;
119 break;
120 default:
121 state = STATE_ERROR;
122 break;
123 }
124 } else {
125 switch (*modestr) {
126 case 'r':
127 what |= 4;
128 break;
129 case 'w':
130 what |= 2;
131 break;
132 case 'X':
133 flags |= FLAG_X_KEEP;
134 /* FALL THROUGH */
135 case 'x':
136 what |= 1;
137 break;
138 case 's':
139 if (topbits)
140 topoct |= topbits;
141 else
142 topoct = 04000;
143 break;
144 case 't':
145 topoct |= 01000;
146 break;
147 default:
148 state = STATE_ERROR;
149 break;
150 }
151 }
152 modestr++;
153 }
154
155 if (state == STATE_ERROR) {
156 free_chmod_mode(first_mode);
157 return NULL;
158 }
159
160 if (append_to) {
161 for (prev_mode = append_to; prev_mode->next; )
162 prev_mode = prev_mode->next;
163 prev_mode->next = first_mode;
164 return append_to;
165 }
166
167 return first_mode;
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}