Link Here
|
1 |
diff --git a/acls.c b/acls.c |
2 |
index 4303c2a7..a2f966a1 100644 |
3 |
--- a/acls.c |
4 |
+++ b/acls.c |
5 |
@@ -31,6 +31,7 @@ extern int list_only; |
6 |
extern int orig_umask; |
7 |
extern int numeric_ids; |
8 |
extern int inc_recurse; |
9 |
+extern int protocol_version; |
10 |
extern int preserve_devices; |
11 |
extern int preserve_specials; |
12 |
|
13 |
@@ -78,20 +79,35 @@ typedef struct rsync_acl { |
14 |
uchar other_obj; |
15 |
} rsync_acl; |
16 |
|
17 |
+typedef struct nfs4_acl { |
18 |
+ char *nfs4_acl_text; |
19 |
+ ssize_t nfs4_acl_len; |
20 |
+} nfs4_acl; |
21 |
+ |
22 |
typedef struct { |
23 |
rsync_acl racl; |
24 |
SMB_ACL_T sacl; |
25 |
} acl_duo; |
26 |
|
27 |
+typedef struct { |
28 |
+ nfs4_acl nacl; |
29 |
+ SMB_ACL_T sacl; |
30 |
+} nfs4_duo; |
31 |
+ |
32 |
static const rsync_acl empty_rsync_acl = { |
33 |
{NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY |
34 |
}; |
35 |
+static const nfs4_acl empty_nfs4_acl = { |
36 |
+ NULL, -1 |
37 |
+}; |
38 |
|
39 |
static item_list access_acl_list = EMPTY_ITEM_LIST; |
40 |
static item_list default_acl_list = EMPTY_ITEM_LIST; |
41 |
+static item_list nfs4_acl_list = EMPTY_ITEM_LIST; |
42 |
|
43 |
static size_t prior_access_count = (size_t)-1; |
44 |
static size_t prior_default_count = (size_t)-1; |
45 |
+static size_t prior_nfs4_count = (size_t)-1; |
46 |
|
47 |
/* === Calculations on ACL types === */ |
48 |
|
49 |
@@ -112,6 +128,18 @@ static const char *str_acl_type(SMB_ACL_TYPE_T type) |
50 |
return "unknown ACL type!"; |
51 |
} |
52 |
|
53 |
+#define OTHER_TYPE(t) (SMB_ACL_TYPE_ACCESS+SMB_ACL_TYPE_DEFAULT-(t)) |
54 |
+#define BUMP_TYPE(t) ((t = OTHER_TYPE(t)) == SMB_ACL_TYPE_DEFAULT) |
55 |
+ |
56 |
+static int old_count_racl_entries(const rsync_acl *racl) |
57 |
+{ |
58 |
+ return racl->names.count |
59 |
+ + (racl->user_obj != NO_ENTRY) |
60 |
+ + (racl->group_obj != NO_ENTRY) |
61 |
+ + (racl->mask_obj != NO_ENTRY) |
62 |
+ + (racl->other_obj != NO_ENTRY); |
63 |
+} |
64 |
+ |
65 |
static int calc_sacl_entries(const rsync_acl *racl) |
66 |
{ |
67 |
/* A System ACL always gets user/group/other permission entries. */ |
68 |
@@ -173,6 +201,17 @@ static rsync_acl *create_racl(void) |
69 |
return racl; |
70 |
} |
71 |
|
72 |
+static nfs4_acl *create_nfs4_acl(void) |
73 |
+{ |
74 |
+ nfs4_acl *nacl = new(nfs4_acl); |
75 |
+ |
76 |
+ if (!nacl) |
77 |
+ out_of_memory("create_nfs4_acl"); |
78 |
+ *nacl = empty_nfs4_acl; |
79 |
+ |
80 |
+ return nacl; |
81 |
+} |
82 |
+ |
83 |
static BOOL ida_entries_equal(const ida_entries *ial1, const ida_entries *ial2) |
84 |
{ |
85 |
id_access *ida1, *ida2; |
86 |
@@ -197,6 +236,11 @@ static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2) |
87 |
&& ida_entries_equal(&racl1->names, &racl2->names); |
88 |
} |
89 |
|
90 |
+static BOOL nfs4_acl_equal(const nfs4_acl *nacl1, const nfs4_acl *nacl2) |
91 |
+{ |
92 |
+ return (strcmp(nacl1->nfs4_acl_text, nacl2->nfs4_acl_text) == 0); |
93 |
+} |
94 |
+ |
95 |
/* Are the extended (non-permission-bit) entries equal? If so, the rest of |
96 |
* the ACL will be handled by the normal mode-preservation code. This is |
97 |
* only meaningful for access ACLs! Note: the 1st arg is a fully-populated |
98 |
@@ -230,6 +274,13 @@ static void rsync_acl_free(rsync_acl *racl) |
99 |
*racl = empty_rsync_acl; |
100 |
} |
101 |
|
102 |
+static void nfs4_acl_free(nfs4_acl *nacl) |
103 |
+{ |
104 |
+ if (nacl->nfs4_acl_text) |
105 |
+ free(nacl->nfs4_acl_text); |
106 |
+ *nacl = empty_nfs4_acl; |
107 |
+} |
108 |
+ |
109 |
void free_acl(stat_x *sxp) |
110 |
{ |
111 |
if (sxp->acc_acl) { |
112 |
@@ -242,6 +293,11 @@ void free_acl(stat_x *sxp) |
113 |
free(sxp->def_acl); |
114 |
sxp->def_acl = NULL; |
115 |
} |
116 |
+ if (sxp->nfs4_acl) { |
117 |
+ nfs4_acl_free(sxp->nfs4_acl); |
118 |
+ free(sxp->nfs4_acl); |
119 |
+ sxp->nfs4_acl = NULL; |
120 |
+ } |
121 |
} |
122 |
|
123 |
#ifdef SMB_ACL_NEED_SORT |
124 |
@@ -469,6 +525,26 @@ static int find_matching_rsync_acl(const rsync_acl *racl, SMB_ACL_TYPE_T type, |
125 |
return *match; |
126 |
} |
127 |
|
128 |
+static int find_matching_nfs4_acl(const nfs4_acl *nacl, const item_list *nfs4_acl_list) |
129 |
+{ |
130 |
+ static int nfs4_match = -1; |
131 |
+ int *match = &nfs4_match; |
132 |
+ size_t count = nfs4_acl_list->count; |
133 |
+ |
134 |
+ if (*match == -1) |
135 |
+ *match = nfs4_acl_list->count - 1; |
136 |
+ while (count--) { |
137 |
+ nfs4_acl *base = nfs4_acl_list->items; |
138 |
+ if (nfs4_acl_equal(base + *match, nacl)) |
139 |
+ return *match; |
140 |
+ if (!(*match)--) |
141 |
+ *match = nfs4_acl_list->count - 1; |
142 |
+ } |
143 |
+ |
144 |
+ *match = -1; |
145 |
+ return *match; |
146 |
+} |
147 |
+ |
148 |
static int get_rsync_acl(const char *fname, rsync_acl *racl, |
149 |
SMB_ACL_TYPE_T type, mode_t mode) |
150 |
{ |
151 |
@@ -537,6 +613,21 @@ static int get_rsync_acl(const char *fname, rsync_acl *racl, |
152 |
/* Return the Access Control List for the given filename. */ |
153 |
int get_acl(const char *fname, stat_x *sxp) |
154 |
{ |
155 |
+ if (sys_acl_get_brand_file(fname, &sxp->brand) < 0) |
156 |
+ return -1; |
157 |
+ |
158 |
+ if (sxp->brand == SMB_ACL_BRAND_NFS4) { |
159 |
+ SMB_ACL_T sacl; |
160 |
+ if ((sacl = sys_acl_get_file(fname, SMB_ACL_TYPE_NFS4)) == NULL) |
161 |
+ return -1; |
162 |
+ |
163 |
+ sxp->nfs4_acl = create_nfs4_acl(); |
164 |
+ sxp->nfs4_acl->nfs4_acl_text = acl_to_text(sacl, &sxp->nfs4_acl->nfs4_acl_len); |
165 |
+ |
166 |
+ sys_acl_free_acl(sacl); |
167 |
+ return 0; |
168 |
+ } |
169 |
+ |
170 |
sxp->acc_acl = create_racl(); |
171 |
|
172 |
if (S_ISREG(sxp->st.st_mode) || S_ISDIR(sxp->st.st_mode)) { |
173 |
@@ -574,6 +665,96 @@ int get_acl(const char *fname, stat_x *sxp) |
174 |
return 0; |
175 |
} |
176 |
|
177 |
+/* === OLD Send functions === */ |
178 |
+ |
179 |
+/* Send the ida list over the file descriptor. */ |
180 |
+static void old_send_ida_entries(int f, const ida_entries *idal, char tag_char) |
181 |
+{ |
182 |
+ id_access *ida; |
183 |
+ size_t count = idal->count; |
184 |
+ for (ida = idal->idas; count--; ida++) { |
185 |
+ if (tag_char == 'U') { |
186 |
+ if (!(ida->access & NAME_IS_USER)) |
187 |
+ continue; |
188 |
+ add_uid(ida->id); |
189 |
+ } else { |
190 |
+ if (ida->access & NAME_IS_USER) |
191 |
+ continue; |
192 |
+ add_gid(ida->id); |
193 |
+ } |
194 |
+ write_byte(f, tag_char); |
195 |
+ write_byte(f, ida->access); |
196 |
+ write_int(f, ida->id); |
197 |
+ } |
198 |
+} |
199 |
+ |
200 |
+/* Send an rsync ACL over the file descriptor. */ |
201 |
+static void old_send_rsync_acl(int f, const rsync_acl *racl) |
202 |
+{ |
203 |
+ size_t count = old_count_racl_entries(racl); |
204 |
+ write_int(f, count); |
205 |
+ if (racl->user_obj != NO_ENTRY) { |
206 |
+ write_byte(f, 'u'); |
207 |
+ write_byte(f, racl->user_obj); |
208 |
+ } |
209 |
+ old_send_ida_entries(f, &racl->names, 'U'); |
210 |
+ if (racl->group_obj != NO_ENTRY) { |
211 |
+ write_byte(f, 'g'); |
212 |
+ write_byte(f, racl->group_obj); |
213 |
+ } |
214 |
+ old_send_ida_entries(f, &racl->names, 'G'); |
215 |
+ if (racl->mask_obj != NO_ENTRY) { |
216 |
+ write_byte(f, 'm'); |
217 |
+ write_byte(f, racl->mask_obj); |
218 |
+ } |
219 |
+ if (racl->other_obj != NO_ENTRY) { |
220 |
+ write_byte(f, 'o'); |
221 |
+ write_byte(f, racl->other_obj); |
222 |
+ } |
223 |
+} |
224 |
+ |
225 |
+/* Send the ACL from the stat_x structure down the indicated file descriptor. |
226 |
+ * This also frees the ACL data. */ |
227 |
+void old_send_acl(stat_x *sxp, int f) |
228 |
+{ |
229 |
+ SMB_ACL_TYPE_T type; |
230 |
+ rsync_acl *racl, *new_racl; |
231 |
+ item_list *racl_list; |
232 |
+ int ndx; |
233 |
+ |
234 |
+ type = SMB_ACL_TYPE_ACCESS; |
235 |
+ racl = sxp->acc_acl; |
236 |
+ racl_list = &access_acl_list; |
237 |
+ do { |
238 |
+ if (!racl) { |
239 |
+ racl = new(rsync_acl); |
240 |
+ if (!racl) |
241 |
+ out_of_memory("send_acl"); |
242 |
+ *racl = empty_rsync_acl; |
243 |
+ if (type == SMB_ACL_TYPE_ACCESS) { |
244 |
+ rsync_acl_fake_perms(racl, sxp->st.st_mode); |
245 |
+ sxp->acc_acl = racl; |
246 |
+ } else |
247 |
+ sxp->def_acl = racl; |
248 |
+ } |
249 |
+ |
250 |
+ if ((ndx = find_matching_rsync_acl(racl, type, racl_list)) != -1) { |
251 |
+ write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'a' : 'd'); |
252 |
+ write_int(f, ndx); |
253 |
+ } else { |
254 |
+ new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000); |
255 |
+ write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'A' : 'D'); |
256 |
+ old_send_rsync_acl(f, racl); |
257 |
+ *new_racl = *racl; |
258 |
+ *racl = empty_rsync_acl; |
259 |
+ } |
260 |
+ racl = sxp->def_acl; |
261 |
+ racl_list = &default_acl_list; |
262 |
+ } while (BUMP_TYPE(type) && S_ISDIR(sxp->st.st_mode)); |
263 |
+ |
264 |
+ free_acl(sxp); |
265 |
+} |
266 |
+ |
267 |
/* === Send functions === */ |
268 |
|
269 |
/* Send the ida list over the file descriptor. */ |
270 |
@@ -645,10 +826,40 @@ static void send_rsync_acl(int f, rsync_acl *racl, SMB_ACL_TYPE_T type, |
271 |
} |
272 |
} |
273 |
|
274 |
+static void send_nfs4_acl(int f, nfs4_acl *nacl, item_list *nfs4_list) |
275 |
+{ |
276 |
+ int ndx = find_matching_nfs4_acl(nacl, nfs4_list); |
277 |
+ |
278 |
+ /* Send 0 (-1 + 1) to indicate that literal ACL data follows. */ |
279 |
+ write_varint(f, ndx + 1); |
280 |
+ |
281 |
+ if (ndx < 0) { |
282 |
+ nfs4_acl *new_nacl = EXPAND_ITEM_LIST(&nfs4_acl_list, nfs4_acl, 1000); |
283 |
+ |
284 |
+ write_varint(f, nacl->nfs4_acl_len); |
285 |
+ write_buf(f, nacl->nfs4_acl_text, nacl->nfs4_acl_len); |
286 |
+ |
287 |
+ *new_nacl = *nacl; |
288 |
+ *nacl = empty_nfs4_acl; |
289 |
+ } |
290 |
+} |
291 |
+ |
292 |
+ |
293 |
/* Send the ACL from the stat_x structure down the indicated file descriptor. |
294 |
* This also frees the ACL data. */ |
295 |
void send_acl(int f, stat_x *sxp) |
296 |
{ |
297 |
+ if (protocol_version < 30) { |
298 |
+ old_send_acl(sxp, f); |
299 |
+ return; |
300 |
+ } |
301 |
+ |
302 |
+ if (sxp->brand == SMB_ACL_BRAND_NFS4) { |
303 |
+ write_varint(f, SMB_ACL_TYPE_NFS4); |
304 |
+ send_nfs4_acl(f, sxp->nfs4_acl, &nfs4_acl_list); |
305 |
+ return; |
306 |
+ } |
307 |
+ |
308 |
if (!sxp->acc_acl) { |
309 |
sxp->acc_acl = create_racl(); |
310 |
rsync_acl_fake_perms(sxp->acc_acl, sxp->st.st_mode); |
311 |
@@ -656,16 +867,172 @@ void send_acl(int f, stat_x *sxp) |
312 |
/* Avoid sending values that can be inferred from other data. */ |
313 |
rsync_acl_strip_perms(sxp); |
314 |
|
315 |
+ write_varint(f, SMB_ACL_TYPE_ACCESS); |
316 |
send_rsync_acl(f, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list); |
317 |
|
318 |
if (S_ISDIR(sxp->st.st_mode)) { |
319 |
if (!sxp->def_acl) |
320 |
sxp->def_acl = create_racl(); |
321 |
|
322 |
+ write_varint(f, SMB_ACL_TYPE_DEFAULT); |
323 |
send_rsync_acl(f, sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list); |
324 |
} |
325 |
} |
326 |
|
327 |
+/* === OLD Receive functions */ |
328 |
+ |
329 |
+static void old_recv_rsync_acl(rsync_acl *racl, int f) |
330 |
+{ |
331 |
+ static item_list temp_ida_list = EMPTY_ITEM_LIST; |
332 |
+ SMB_ACL_TAG_T tag_type = 0; |
333 |
+ uchar computed_mask_bits = 0; |
334 |
+ id_access *ida; |
335 |
+ size_t count; |
336 |
+ |
337 |
+ if (!(count = read_int(f))) |
338 |
+ return; |
339 |
+ |
340 |
+ while (count--) { |
341 |
+ char tag = read_byte(f); |
342 |
+ uchar access = read_byte(f); |
343 |
+ if (access & ~ (4 | 2 | 1)) { |
344 |
+ rprintf(FERROR, "old_recv_rsync_acl: bogus permset %o\n", |
345 |
+ access); |
346 |
+ exit_cleanup(RERR_STREAMIO); |
347 |
+ } |
348 |
+ switch (tag) { |
349 |
+ case 'u': |
350 |
+ if (racl->user_obj != NO_ENTRY) { |
351 |
+ rprintf(FERROR, "old_recv_rsync_acl: error: duplicate USER_OBJ entry\n"); |
352 |
+ exit_cleanup(RERR_STREAMIO); |
353 |
+ } |
354 |
+ racl->user_obj = access; |
355 |
+ continue; |
356 |
+ case 'U': |
357 |
+ tag_type = SMB_ACL_USER; |
358 |
+ break; |
359 |
+ case 'g': |
360 |
+ if (racl->group_obj != NO_ENTRY) { |
361 |
+ rprintf(FERROR, "old_recv_rsync_acl: error: duplicate GROUP_OBJ entry\n"); |
362 |
+ exit_cleanup(RERR_STREAMIO); |
363 |
+ } |
364 |
+ racl->group_obj = access; |
365 |
+ continue; |
366 |
+ case 'G': |
367 |
+ tag_type = SMB_ACL_GROUP; |
368 |
+ break; |
369 |
+ case 'm': |
370 |
+ if (racl->mask_obj != NO_ENTRY) { |
371 |
+ rprintf(FERROR, "old_recv_rsync_acl: error: duplicate MASK entry\n"); |
372 |
+ exit_cleanup(RERR_STREAMIO); |
373 |
+ } |
374 |
+ racl->mask_obj = access; |
375 |
+ continue; |
376 |
+ case 'o': |
377 |
+ if (racl->other_obj != NO_ENTRY) { |
378 |
+ rprintf(FERROR, "old_recv_rsync_acl: error: duplicate OTHER entry\n"); |
379 |
+ exit_cleanup(RERR_STREAMIO); |
380 |
+ } |
381 |
+ racl->other_obj = access; |
382 |
+ continue; |
383 |
+ default: |
384 |
+ rprintf(FERROR, "old_recv_rsync_acl: unknown tag %c\n", |
385 |
+ tag); |
386 |
+ exit_cleanup(RERR_STREAMIO); |
387 |
+ } |
388 |
+ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10); |
389 |
+ ida->access = access | (tag_type == SMB_ACL_USER ? NAME_IS_USER : 0); |
390 |
+ ida->id = read_int(f); |
391 |
+ computed_mask_bits |= access; |
392 |
+ } |
393 |
+ |
394 |
+ /* Transfer the count id_access items out of the temp_ida_list |
395 |
+ * into the names ida_entries list in racl. */ |
396 |
+ if (temp_ida_list.count) { |
397 |
+#ifdef SMB_ACL_NEED_SORT |
398 |
+ if (temp_ida_list.count > 1) { |
399 |
+ qsort(temp_ida_list.items, temp_ida_list.count, |
400 |
+ sizeof (id_access), id_access_sorter); |
401 |
+ } |
402 |
+#endif |
403 |
+ if (!(racl->names.idas = new_array(id_access, temp_ida_list.count))) |
404 |
+ out_of_memory("unpack_smb_acl"); |
405 |
+ memcpy(racl->names.idas, temp_ida_list.items, |
406 |
+ temp_ida_list.count * sizeof (id_access)); |
407 |
+ } else |
408 |
+ racl->names.idas = NULL; |
409 |
+ |
410 |
+ racl->names.count = temp_ida_list.count; |
411 |
+ |
412 |
+ /* Truncate the temporary list now that its idas have been saved. */ |
413 |
+ temp_ida_list.count = 0; |
414 |
+ |
415 |
+ if (!racl->names.count) { |
416 |
+ /* If we received a superfluous mask, throw it away. */ |
417 |
+ if (racl->mask_obj != NO_ENTRY) { |
418 |
+ /* Mask off the group perms with it first. */ |
419 |
+ racl->group_obj &= racl->mask_obj | NO_ENTRY; |
420 |
+ racl->mask_obj = NO_ENTRY; |
421 |
+ } |
422 |
+ } else if (racl->mask_obj == NO_ENTRY) /* Must be non-empty with lists. */ |
423 |
+ racl->mask_obj = (computed_mask_bits | racl->group_obj) & 7; |
424 |
+} |
425 |
+ |
426 |
+/* Receive the ACL info the sender has included for this file-list entry. */ |
427 |
+void old_recv_acl(struct file_struct *file, int f) |
428 |
+{ |
429 |
+ SMB_ACL_TYPE_T type; |
430 |
+ item_list *racl_list; |
431 |
+ |
432 |
+ if (S_ISLNK(file->mode)) |
433 |
+ return; |
434 |
+ |
435 |
+ type = SMB_ACL_TYPE_ACCESS; |
436 |
+ racl_list = &access_acl_list; |
437 |
+ do { |
438 |
+ char tag = read_byte(f); |
439 |
+ int ndx; |
440 |
+ |
441 |
+ if (tag == 'A' || tag == 'a') { |
442 |
+ if (type != SMB_ACL_TYPE_ACCESS) { |
443 |
+ rprintf(FERROR, "receive_acl %s: duplicate access ACL\n", |
444 |
+ f_name(file, NULL)); |
445 |
+ exit_cleanup(RERR_STREAMIO); |
446 |
+ } |
447 |
+ } else if (tag == 'D' || tag == 'd') { |
448 |
+ if (type == SMB_ACL_TYPE_ACCESS) { |
449 |
+ rprintf(FERROR, "receive_acl %s: expecting access ACL; got default\n", |
450 |
+ f_name(file, NULL)); |
451 |
+ exit_cleanup(RERR_STREAMIO); |
452 |
+ } |
453 |
+ } else { |
454 |
+ rprintf(FERROR, "receive_acl %s: unknown ACL type tag: %c\n", |
455 |
+ f_name(file, NULL), tag); |
456 |
+ exit_cleanup(RERR_STREAMIO); |
457 |
+ } |
458 |
+ if (tag == 'A' || tag == 'D') { |
459 |
+ acl_duo *duo_item; |
460 |
+ ndx = racl_list->count; |
461 |
+ duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000); |
462 |
+ duo_item->racl = empty_rsync_acl; |
463 |
+ old_recv_rsync_acl(&duo_item->racl, f); |
464 |
+ duo_item->sacl = NULL; |
465 |
+ } else { |
466 |
+ ndx = read_int(f); |
467 |
+ if (ndx < 0 || (size_t)ndx >= racl_list->count) { |
468 |
+ rprintf(FERROR, "receive_acl %s: %s ACL index %d out of range\n", |
469 |
+ f_name(file, NULL), str_acl_type(type), ndx); |
470 |
+ exit_cleanup(RERR_STREAMIO); |
471 |
+ } |
472 |
+ } |
473 |
+ if (type == SMB_ACL_TYPE_ACCESS) |
474 |
+ F_ACL(file) = ndx; |
475 |
+ else |
476 |
+ F_DIR_DEFACL(file) = ndx; |
477 |
+ racl_list = &default_acl_list; |
478 |
+ } while (BUMP_TYPE(type) && S_ISDIR(file->mode)); |
479 |
+} |
480 |
+ |
481 |
/* === Receive functions === */ |
482 |
|
483 |
static uint32 recv_acl_access(int f, uchar *name_follows_ptr) |
484 |
@@ -779,10 +1146,58 @@ static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode |
485 |
return ndx; |
486 |
} |
487 |
|
488 |
+ |
489 |
+static int recv_nfs4_acl(int f, item_list *nfs4_acl_list, struct file_struct *file __unused) |
490 |
+{ |
491 |
+ nfs4_duo *duo_item; |
492 |
+ int ndx = read_varint(f); |
493 |
+ |
494 |
+ if (ndx < 0 || (size_t)ndx > nfs4_acl_list->count) { |
495 |
+ rprintf(FERROR_XFER, "recv_nfs4_index: %s ACL index %d > %d\n", |
496 |
+ str_acl_type(SMB_ACL_TYPE_NFS4), ndx, (int)nfs4_acl_list->count); |
497 |
+ exit_cleanup(RERR_STREAMIO); |
498 |
+ } |
499 |
+ |
500 |
+ if (ndx != 0) |
501 |
+ return ndx - 1; |
502 |
+ |
503 |
+ ndx = nfs4_acl_list->count; |
504 |
+ duo_item = EXPAND_ITEM_LIST(nfs4_acl_list, nfs4_duo, 1000); |
505 |
+ duo_item->nacl = empty_nfs4_acl; |
506 |
+ |
507 |
+ duo_item->nacl.nfs4_acl_len = read_varint(f); |
508 |
+ duo_item->nacl.nfs4_acl_text = new_array(char, duo_item->nacl.nfs4_acl_len + 1); |
509 |
+ if (!duo_item->nacl.nfs4_acl_text) |
510 |
+ out_of_memory("recv_nfs4_acl"); |
511 |
+ |
512 |
+ read_buf(f, duo_item->nacl.nfs4_acl_text, duo_item->nacl.nfs4_acl_len); |
513 |
+ duo_item->nacl.nfs4_acl_text[duo_item->nacl.nfs4_acl_len] = 0; |
514 |
+ |
515 |
+ duo_item->sacl = NULL; |
516 |
+ return ndx; |
517 |
+} |
518 |
+ |
519 |
+ |
520 |
/* Receive the ACL info the sender has included for this file-list entry. */ |
521 |
void receive_acl(int f, struct file_struct *file) |
522 |
{ |
523 |
- F_ACL(file) = recv_rsync_acl(f, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode); |
524 |
+ int ndx; |
525 |
+ SMB_ACL_TYPE_T type; |
526 |
+ |
527 |
+ if (protocol_version < 30) { |
528 |
+ old_recv_acl(file, f); |
529 |
+ return; |
530 |
+ } |
531 |
+ |
532 |
+ type = read_varint(f); |
533 |
+ if (type == SMB_ACL_TYPE_NFS4){ |
534 |
+ ndx = recv_nfs4_acl(f, &nfs4_acl_list, file); |
535 |
+ F_ACL(file) = ndx; |
536 |
+ return; |
537 |
+ } |
538 |
+ |
539 |
+ ndx = recv_rsync_acl(f, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode); |
540 |
+ F_ACL(file) = ndx; |
541 |
|
542 |
if (S_ISDIR(file->mode)) |
543 |
F_DIR_DEFACL(file) = recv_rsync_acl(f, &default_acl_list, SMB_ACL_TYPE_DEFAULT, 0); |
544 |
@@ -806,10 +1221,37 @@ static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type, item_list *racl |
545 |
return ndx; |
546 |
} |
547 |
|
548 |
+static int cache_nfs4_acl(nfs4_acl *nacl, item_list *nfs4_list) |
549 |
+{ |
550 |
+ int ndx; |
551 |
+ |
552 |
+ if (!nacl) |
553 |
+ ndx = -1; |
554 |
+ else if ((ndx = find_matching_nfs4_acl(nacl, nfs4_list)) == -1) { |
555 |
+ nfs4_duo *new_duo; |
556 |
+ ndx = nfs4_list->count; |
557 |
+ new_duo = EXPAND_ITEM_LIST(nfs4_list, nfs4_duo, 1000); |
558 |
+ new_duo->nacl = *nacl; |
559 |
+ new_duo->sacl = NULL; |
560 |
+ *nacl = empty_nfs4_acl; |
561 |
+ } |
562 |
+ |
563 |
+ return ndx; |
564 |
+} |
565 |
+ |
566 |
+ |
567 |
/* Turn the ACL data in stat_x into cached ACL data, setting the index |
568 |
* values in the file struct. */ |
569 |
void cache_tmp_acl(struct file_struct *file, stat_x *sxp) |
570 |
{ |
571 |
+ if (sxp->brand == SMB_ACL_BRAND_NFS4) { |
572 |
+ if (prior_nfs4_count == (size_t)-1) |
573 |
+ prior_nfs4_count = nfs4_acl_list.count; |
574 |
+ |
575 |
+ F_ACL(file) = cache_nfs4_acl(sxp->nfs4_acl, &nfs4_acl_list); |
576 |
+ return; |
577 |
+ } |
578 |
+ |
579 |
if (prior_access_count == (size_t)-1) |
580 |
prior_access_count = access_acl_list.count; |
581 |
|
582 |
@@ -837,6 +1279,21 @@ static void uncache_duo_acls(item_list *duo_list, size_t start) |
583 |
} |
584 |
} |
585 |
|
586 |
+static void uncache_nfs4_acls(item_list *nfs4_list, size_t start) |
587 |
+{ |
588 |
+ nfs4_duo *nfs4_item = nfs4_list->items; |
589 |
+ nfs4_duo *nfs4_start = nfs4_item + start; |
590 |
+ |
591 |
+ nfs4_item += nfs4_list->count; |
592 |
+ nfs4_list->count = start; |
593 |
+ |
594 |
+ while (nfs4_item-- > nfs4_start) { |
595 |
+ nfs4_acl_free(&nfs4_item->nacl); |
596 |
+ if (nfs4_item->sacl) |
597 |
+ sys_acl_free_acl(nfs4_item->sacl); |
598 |
+ } |
599 |
+} |
600 |
+ |
601 |
void uncache_tmp_acls(void) |
602 |
{ |
603 |
if (prior_access_count != (size_t)-1) { |
604 |
@@ -848,6 +1305,10 @@ void uncache_tmp_acls(void) |
605 |
uncache_duo_acls(&default_acl_list, prior_default_count); |
606 |
prior_default_count = (size_t)-1; |
607 |
} |
608 |
+ if (prior_nfs4_count != (size_t)-1) { |
609 |
+ uncache_nfs4_acls(&nfs4_acl_list, prior_nfs4_count); |
610 |
+ prior_nfs4_count = (size_t)-1; |
611 |
+ } |
612 |
} |
613 |
|
614 |
#ifndef HAVE_OSX_ACLS |
615 |
@@ -999,6 +1460,7 @@ static int set_rsync_acl(const char *fname, acl_duo *duo_item, |
616 |
return 0; |
617 |
} |
618 |
|
619 |
+ |
620 |
/* Given a fname, this sets extended access ACL entries, the default ACL (for a |
621 |
* dir), and the regular mode bits on the file. Call this with fname set to |
622 |
* NULL to just check if the ACL is different. |
623 |
@@ -1018,6 +1480,32 @@ int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp, mode |
624 |
return -1; |
625 |
} |
626 |
|
627 |
+ if (sxp->brand == SMB_ACL_BRAND_NFS4) { |
628 |
+ ndx = F_ACL(file); |
629 |
+ if (ndx >= 0 && (size_t)ndx < nfs4_acl_list.count) { |
630 |
+ nfs4_duo *duo_item = nfs4_acl_list.items; |
631 |
+ duo_item += ndx; |
632 |
+ changed = 1; |
633 |
+ |
634 |
+ if (!duo_item->sacl) { |
635 |
+ duo_item->sacl = acl_from_text(duo_item->nacl.nfs4_acl_text); |
636 |
+ if (!duo_item->sacl) |
637 |
+ return -1; |
638 |
+ } |
639 |
+ |
640 |
+ if (!dry_run && fname) { |
641 |
+ if (sys_acl_set_file(fname, SMB_ACL_TYPE_NFS4, duo_item->sacl) < 0) { |
642 |
+ rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_set_file(%s, %s)", |
643 |
+ fname, str_acl_type(SMB_ACL_TYPE_NFS4)); |
644 |
+ return -1; |
645 |
+ } |
646 |
+ |
647 |
+ return changed; |
648 |
+ } |
649 |
+ } |
650 |
+ } |
651 |
+ |
652 |
+ |
653 |
ndx = F_ACL(file); |
654 |
if (ndx >= 0 && (size_t)ndx < access_acl_list.count) { |
655 |
acl_duo *duo_item = access_acl_list.items; |
656 |
diff --git a/hlink.c b/hlink.c |
657 |
index adec89b0..8d3982b2 100644 |
658 |
--- a/hlink.c |
659 |
+++ b/hlink.c |
660 |
@@ -422,7 +422,9 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname, |
661 |
else { |
662 |
sxp->acc_acl = alt_sx.acc_acl; |
663 |
sxp->def_acl = alt_sx.def_acl; |
664 |
+ sxp->nfs4_acl = alt_sx.nfs4_acl; |
665 |
alt_sx.acc_acl = alt_sx.def_acl = NULL; |
666 |
+ alt_sx.nfs4_acl = NULL; |
667 |
} |
668 |
} |
669 |
#endif |
670 |
diff --git a/ifuncs.h b/ifuncs.h |
671 |
index 4037639b..fd3afb61 100644 |
672 |
--- a/ifuncs.h |
673 |
+++ b/ifuncs.h |
674 |
@@ -77,6 +77,7 @@ init_stat_x(stat_x *sx_p) |
675 |
{ |
676 |
#ifdef SUPPORT_ACLS |
677 |
sx_p->acc_acl = sx_p->def_acl = NULL; |
678 |
+ sx_p->nfs4_acl = NULL; |
679 |
#endif |
680 |
#ifdef SUPPORT_XATTRS |
681 |
sx_p->xattr = NULL; |
682 |
diff --git a/lib/sysacls.c b/lib/sysacls.c |
683 |
index c23864fe..f049fbad 100644 |
684 |
--- a/lib/sysacls.c |
685 |
+++ b/lib/sysacls.c |
686 |
@@ -80,12 +80,36 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type) |
687 |
return acl_get_file(path_p, type); |
688 |
} |
689 |
|
690 |
-#if 0 |
691 |
SMB_ACL_T sys_acl_get_fd(int fd) |
692 |
{ |
693 |
return acl_get_fd(fd); |
694 |
} |
695 |
-#endif |
696 |
+ |
697 |
+int sys_acl_get_brand( SMB_ACL_T the_acl, int *brand_p) |
698 |
+{ |
699 |
+ return acl_get_brand_np(the_acl, brand_p); |
700 |
+} |
701 |
+ |
702 |
+int sys_acl_get_brand_file( const char *path_p, int *brand_p) |
703 |
+{ |
704 |
+ int fd; |
705 |
+ acl_t acl; |
706 |
+ |
707 |
+ if ((fd = open(path_p, O_RDONLY|O_NONBLOCK)) < 0) |
708 |
+ return -1; |
709 |
+ if ((acl = acl_get_fd(fd)) == NULL) { |
710 |
+ close(fd); |
711 |
+ return -1; |
712 |
+ } |
713 |
+ close(fd); |
714 |
+ if (acl_get_brand_np(acl, brand_p) < 0) { |
715 |
+ acl_free(acl); |
716 |
+ return -1; |
717 |
+ } |
718 |
+ |
719 |
+ acl_free(acl); |
720 |
+ return 0; |
721 |
+} |
722 |
|
723 |
#if defined(HAVE_ACL_GET_PERM_NP) |
724 |
#define acl_get_perm(p, b) acl_get_perm_np(p, b) |
725 |
diff --git a/lib/sysacls.h b/lib/sysacls.h |
726 |
index 8865dae4..76a6e31f 100644 |
727 |
--- a/lib/sysacls.h |
728 |
+++ b/lib/sysacls.h |
729 |
@@ -48,6 +48,7 @@ |
730 |
#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ |
731 |
#define SMB_ACL_OTHER ACL_OTHER |
732 |
#define SMB_ACL_MASK ACL_MASK |
733 |
+#define SMB_ACL_EVERYONE ACL_EVERYONE |
734 |
|
735 |
#define SMB_ACL_T acl_t |
736 |
|
737 |
@@ -58,6 +59,11 @@ |
738 |
|
739 |
#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS |
740 |
#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT |
741 |
+#define SMB_ACL_TYPE_NFS4 ACL_TYPE_NFS4 |
742 |
+ |
743 |
+#define SMB_ACL_BRAND_UNKNOWN ACL_BRAND_UNKNOWN |
744 |
+#define SMB_ACL_BRAND_POSIX ACL_BRAND_POSIX |
745 |
+#define SMB_ACL_BRAND_NFS4 ACL_BRAND_NFS4 |
746 |
|
747 |
#define SMB_ACL_VALID_NAME_BITS (4 | 2 | 1) |
748 |
#define SMB_ACL_VALID_OBJ_BITS (4 | 2 | 1) |
749 |
@@ -294,6 +300,8 @@ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *b |
750 |
SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type); |
751 |
SMB_ACL_T sys_acl_get_fd(int fd); |
752 |
SMB_ACL_T sys_acl_init(int count); |
753 |
+int sys_acl_get_brand( SMB_ACL_T the_acl, int *brand_p); |
754 |
+int sys_acl_get_brand_file( const char *path_p, int *brand_p); |
755 |
int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry); |
756 |
int sys_acl_set_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype, uint32 bits, id_t u_g_id); |
757 |
int sys_acl_set_access_bits(SMB_ACL_ENTRY_T entry, uint32 bits); |
758 |
diff --git a/rsync.c b/rsync.c |
759 |
index e7f1f96a..41818ea4 100644 |
760 |
--- a/rsync.c |
761 |
+++ b/rsync.c |
762 |
@@ -627,19 +627,6 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, |
763 |
} |
764 |
#endif |
765 |
|
766 |
-#ifdef SUPPORT_ACLS |
767 |
- /* It's OK to call set_acl() now, even for a dir, as the generator |
768 |
- * will enable owner-writability using chmod, if necessary. |
769 |
- * |
770 |
- * If set_acl() changes permission bits in the process of setting |
771 |
- * an access ACL, it changes sxp->st.st_mode so we know whether we |
772 |
- * need to chmod(). */ |
773 |
- if (preserve_acls && !S_ISLNK(new_mode)) { |
774 |
- if (set_acl(fname, file, sxp, new_mode) > 0) |
775 |
- updated |= UPDATED_ACLS; |
776 |
- } |
777 |
-#endif |
778 |
- |
779 |
#ifdef HAVE_CHMOD |
780 |
if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) { |
781 |
int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode); |
782 |
@@ -654,6 +641,19 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, |
783 |
} |
784 |
#endif |
785 |
|
786 |
+#ifdef SUPPORT_ACLS |
787 |
+ /* It's OK to call set_acl() now, even for a dir, as the generator |
788 |
+ * will enable owner-writability using chmod, if necessary. |
789 |
+ * |
790 |
+ * If set_acl() changes permission bits in the process of setting |
791 |
+ * an access ACL, it changes sxp->st.st_mode so we know whether we |
792 |
+ * need to chmod(). */ |
793 |
+ if (preserve_acls && !S_ISLNK(new_mode)) { |
794 |
+ if (set_acl(fname, file, sxp, new_mode) > 0) |
795 |
+ updated |= UPDATED_ACLS; |
796 |
+ } |
797 |
+#endif |
798 |
+ |
799 |
if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) { |
800 |
if (updated) |
801 |
rprintf(FCLIENT, "%s\n", fname); |
802 |
diff --git a/rsync.h b/rsync.h |
803 |
index 0f5304ee..45dd050b 100644 |
804 |
--- a/rsync.h |
805 |
+++ b/rsync.h |
806 |
@@ -1119,13 +1119,22 @@ typedef struct { |
807 |
#ifdef SUPPORT_ACLS |
808 |
struct rsync_acl *acc_acl; /* access ACL */ |
809 |
struct rsync_acl *def_acl; /* default ACL */ |
810 |
+ struct nfs4_acl *nfs4_acl; /* NFSv4 ACL */ |
811 |
+ int brand; |
812 |
#endif |
813 |
#ifdef SUPPORT_XATTRS |
814 |
item_list *xattr; |
815 |
#endif |
816 |
} stat_x; |
817 |
|
818 |
-#define ACL_READY(sx) ((sx).acc_acl != NULL) |
819 |
+#ifdef SUPPORT_ACLS |
820 |
+#include "lib/sysacls.h" |
821 |
+#endif |
822 |
+ |
823 |
+#define ACL_READY_POSIX(sx) ((sx).acc_acl != NULL) |
824 |
+#define ACL_READY_NFS4(sx) ((sx).nfs4_acl != NULL) |
825 |
+#define ACL_READY(sx) (((sx).brand == SMB_ACL_BRAND_NFS4) ? (ACL_READY_NFS4(sx)) : (ACL_READY_POSIX(sx))) |
826 |
+ |
827 |
#define XATTR_READY(sx) ((sx).xattr != NULL) |
828 |
|
829 |
#define CLVL_NOT_SPECIFIED INT_MIN |