- The orig_umask value is now a mode_t.
[rsync/rsync.git] / chmod.c
CommitLineData
0c983c1f
WD
1#include "rsync.h"
2
a41d1106 3extern mode_t orig_umask;
0c983c1f
WD
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
15b03ab1
WD
24 * pairs in a linked list. We return a pointer to new items on succcess
25 * (appending the items to the specified list), or NULL on error. */
26struct chmod_mode_struct *parse_chmod(const char *modestr,
27 struct chmod_mode_struct **root_mode_ptr)
0c983c1f
WD
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,
cf9b4794 32 *prev_mode = NULL;
0c983c1f
WD
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:
a41d1106 59 curr_mode->ModeAND = CHMOD_BITS;
0c983c1f
WD
60 curr_mode->ModeOR = bits + topoct;
61 break;
62 case CHMOD_SUB:
a41d1106 63 curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
0c983c1f
WD
64 curr_mode->ModeOR = 0;
65 break;
66 case CHMOD_EQ:
a41d1106 67 curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
0c983c1f
WD
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);
15b03ab1 157 return NULL;
0c983c1f 158 }
cf9b4794 159
81b096fe
WD
160 if (!(curr_mode = *root_mode_ptr))
161 *root_mode_ptr = first_mode;
162 else {
163 while (curr_mode->next)
164 curr_mode = curr_mode->next;
165 curr_mode->next = first_mode;
cf9b4794
WD
166 }
167
15b03ab1 168 return first_mode;
0c983c1f
WD
169}
170
171
172/* Takes an existing file permission and a list of AND/OR changes, and
173 * create a new permissions. */
174int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
175{
176 int IsX = mode & 0111;
a41d1106 177 int NonPerm = mode & ~CHMOD_BITS;
0c983c1f
WD
178
179 for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
180 if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
181 continue;
182 if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
183 continue;
184 mode &= chmod_modes->ModeAND;
185 if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
186 mode |= chmod_modes->ModeOR & ~0111;
187 else
188 mode |= chmod_modes->ModeOR;
189 }
190
191 return mode | NonPerm;
192}
193
194/* Free the linked list created by parse_chmod. */
195int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
196{
197 struct chmod_mode_struct *next;
198
199 while (chmod_modes) {
200 next = chmod_modes->next;
201 free(chmod_modes);
202 chmod_modes = next;
203 }
204 return 0;
205}