00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "rsync.h"
00025 #include "lib/sysacls.h"
00026
00027 #ifdef SUPPORT_ACLS
00028
00029 extern int am_root;
00030 extern int dry_run;
00031 extern int orig_umask;
00032 extern int preserve_acls;
00033
00034 typedef struct {
00035 id_t id;
00036 uchar access;
00037 } id_access;
00038
00039 typedef struct {
00040 size_t count;
00041 size_t malloced;
00042 id_access *idas;
00043 } ida_list;
00044
00045 #define ACL_NO_ENTRY ((uchar)0x80)
00046 typedef struct {
00047 ida_list users;
00048 ida_list groups;
00049
00050 uchar user_obj;
00051 uchar group_obj;
00052 uchar mask;
00053 uchar other;
00054 } rsync_acl;
00055
00056 static const rsync_acl rsync_acl_initializer = {
00057 {0, 0, NULL}, {0, 0, NULL},
00058 ACL_NO_ENTRY, ACL_NO_ENTRY, ACL_NO_ENTRY, ACL_NO_ENTRY
00059 };
00060
00061 #define OTHER_TYPE(t) (SMB_ACL_TYPE_ACCESS+SMB_ACL_TYPE_DEFAULT-(t))
00062 #define BUMP_TYPE(t) ((t = OTHER_TYPE(t)) == SMB_ACL_TYPE_DEFAULT)
00063
00064
00065
00066 static int count_racl_entries(const rsync_acl *racl)
00067 {
00068 return racl->users.count + racl->groups.count
00069 + (racl->user_obj != ACL_NO_ENTRY)
00070 + (racl->group_obj != ACL_NO_ENTRY)
00071 + (racl->mask != ACL_NO_ENTRY)
00072 + (racl->other != ACL_NO_ENTRY);
00073 }
00074
00075 static int calc_sacl_entries(const rsync_acl *racl)
00076 {
00077 return racl->users.count + racl->groups.count
00078 #ifdef ACLS_NEED_MASK
00079 + 4;
00080 #else
00081 + (racl->mask != ACL_NO_ENTRY) + 3;
00082 #endif
00083 }
00084
00085 static int rsync_acl_get_perms(const rsync_acl *racl)
00086 {
00087
00088 return ((racl->user_obj & 7) << 6)
00089 + (((racl->mask != ACL_NO_ENTRY ? racl->mask : racl->group_obj) & 7) << 3)
00090 + (racl->other & 7);
00091 }
00092
00093 static void rsync_acl_strip_perms(rsync_acl *racl)
00094 {
00095 racl->user_obj = ACL_NO_ENTRY;
00096 if (racl->mask == ACL_NO_ENTRY)
00097 racl->group_obj = ACL_NO_ENTRY;
00098 else
00099 racl->mask = ACL_NO_ENTRY;
00100 racl->other = ACL_NO_ENTRY;
00101 }
00102
00103 static void expand_ida_list(ida_list *idal)
00104 {
00105
00106 if (idal->malloced <= idal->count) {
00107 id_access *new_ptr;
00108 size_t new_size = idal->malloced + 10;
00109 new_ptr = realloc_array(idal->idas, id_access, new_size);
00110 if (verbose >= 4) {
00111 rprintf(FINFO, "expand rsync_acl to %.0f bytes, did%s move\n",
00112 (double) new_size * sizeof idal->idas[0],
00113 idal->idas ? "" : " not");
00114 }
00115
00116 idal->idas = new_ptr;
00117 idal->malloced = new_size;
00118
00119 if (!idal->idas)
00120 out_of_memory("expand_ida_list");
00121 }
00122 }
00123
00124 static void ida_list_free(ida_list *idal)
00125 {
00126 free(idal->idas);
00127 idal->idas = NULL;
00128 idal->count = 0;
00129 idal->malloced = 0;
00130 }
00131
00132 static void rsync_acl_free(rsync_acl *racl)
00133 {
00134 ida_list_free(&racl->users);
00135 ida_list_free(&racl->groups);
00136 }
00137
00138 static int id_access_sorter(const void *r1, const void *r2)
00139 {
00140 id_access *ida1 = (id_access *)r1;
00141 id_access *ida2 = (id_access *)r2;
00142 id_t rid1 = ida1->id, rid2 = ida2->id;
00143 return rid1 == rid2 ? 0 : rid1 < rid2 ? -1 : 1;
00144 }
00145
00146 static void sort_ida_list(ida_list *idal)
00147 {
00148 if (!idal->count)
00149 return;
00150 qsort((void **)idal->idas, idal->count, sizeof idal->idas[0],
00151 &id_access_sorter);
00152 }
00153
00154 static BOOL unpack_smb_acl(rsync_acl *racl, SMB_ACL_T sacl)
00155 {
00156 SMB_ACL_ENTRY_T entry;
00157 const char *errfun;
00158 int rc;
00159
00160 *racl = rsync_acl_initializer;
00161 errfun = "sys_acl_get_entry";
00162 for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry);
00163 rc == 1;
00164 rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {
00165 SMB_ACL_TAG_T tag_type;
00166 SMB_ACL_PERMSET_T permset;
00167 uchar access;
00168 void *qualifier;
00169 id_access *ida;
00170 ida_list *idal;
00171 if ((rc = sys_acl_get_tag_type(entry, &tag_type))) {
00172 errfun = "sys_acl_get_tag_type";
00173 break;
00174 }
00175 if ((rc = sys_acl_get_permset(entry, &permset))) {
00176 errfun = "sys_acl_get_tag_type";
00177 break;
00178 }
00179 access = (sys_acl_get_perm(permset, SMB_ACL_READ) ? 4 : 0)
00180 | (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? 2 : 0)
00181 | (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? 1 : 0);
00182
00183 switch (tag_type) {
00184 case SMB_ACL_USER_OBJ:
00185 if (racl->user_obj == ACL_NO_ENTRY)
00186 racl->user_obj = access;
00187 else
00188 rprintf(FINFO, "unpack_smb_acl: warning: duplicate USER_OBJ entry ignored\n");
00189 continue;
00190 case SMB_ACL_USER:
00191 idal = &racl->users;
00192 break;
00193 case SMB_ACL_GROUP_OBJ:
00194 if (racl->group_obj == ACL_NO_ENTRY)
00195 racl->group_obj = access;
00196 else
00197 rprintf(FINFO, "unpack_smb_acl: warning: duplicate GROUP_OBJ entry ignored\n");
00198 continue;
00199 case SMB_ACL_GROUP:
00200 idal = &racl->groups;
00201 break;
00202 case SMB_ACL_MASK:
00203 if (racl->mask == ACL_NO_ENTRY)
00204 racl->mask = access;
00205 else
00206 rprintf(FINFO, "unpack_smb_acl: warning: duplicate MASK entry ignored\n");
00207 continue;
00208 case SMB_ACL_OTHER:
00209 if (racl->other == ACL_NO_ENTRY)
00210 racl->other = access;
00211 else
00212 rprintf(FINFO, "unpack_smb_acl: warning: duplicate OTHER entry ignored\n");
00213 continue;
00214 default:
00215 rprintf(FINFO, "unpack_smb_acl: warning: entry with unrecognized tag type ignored\n");
00216 continue;
00217 }
00218 if (!(qualifier = sys_acl_get_qualifier(entry))) {
00219 errfun = "sys_acl_get_tag_type";
00220 rc = EINVAL;
00221 break;
00222 }
00223 expand_ida_list(idal);
00224 ida = &idal->idas[idal->count++];
00225 ida->id = *((id_t *)qualifier);
00226 ida->access = access;
00227 sys_acl_free_qualifier(qualifier, tag_type);
00228 }
00229 if (rc) {
00230 rprintf(FERROR, "unpack_smb_acl: %s(): %s\n",
00231 errfun, strerror(errno));
00232 rsync_acl_free(racl);
00233 return False;
00234 }
00235
00236 sort_ida_list(&racl->users);
00237 sort_ida_list(&racl->groups);
00238
00239 return True;
00240 }
00241
00242 static BOOL ida_lists_equal(const ida_list *ial1, const ida_list *ial2)
00243 {
00244 id_access *ida1, *ida2;
00245 size_t count = ial1->count;
00246 if (count != ial2->count)
00247 return False;
00248 ida1 = ial1->idas;
00249 ida2 = ial2->idas;
00250 for (; count--; ida1++, ida2++) {
00251 if (ida1->access != ida2->access || ida1->id != ida2->id)
00252 return False;
00253 }
00254 return True;
00255 }
00256
00257 static BOOL rsync_acls_equal(const rsync_acl *racl1, const rsync_acl *racl2)
00258 {
00259 return (racl1->user_obj == racl2->user_obj
00260 && racl1->group_obj == racl2->group_obj
00261 && racl1->mask == racl2->mask
00262 && racl1->other == racl2->other
00263 && ida_lists_equal(&racl1->users, &racl2->users)
00264 && ida_lists_equal(&racl1->groups, &racl2->groups));
00265 }
00266
00267 static BOOL rsync_acl_extended_parts_equal(const rsync_acl *racl1, const rsync_acl *racl2)
00268 {
00269
00270 if ((racl1->mask ^ racl2->mask) & ACL_NO_ENTRY)
00271 return False;
00272 if (racl1->mask != ACL_NO_ENTRY && racl1->group_obj != racl2->group_obj)
00273 return False;
00274 return ida_lists_equal(&racl1->users, &racl2->users)
00275 && ida_lists_equal(&racl1->groups, &racl2->groups);
00276 }
00277
00278 typedef struct {
00279 size_t count;
00280 size_t malloced;
00281 rsync_acl *racls;
00282 } rsync_acl_list;
00283
00284 static rsync_acl_list _rsync_acl_lists[] = {
00285 { 0, 0, NULL },
00286 { 0, 0, NULL }
00287 };
00288
00289 static inline rsync_acl_list *rsync_acl_lists(SMB_ACL_TYPE_T type)
00290 {
00291 return &_rsync_acl_lists[type != SMB_ACL_TYPE_ACCESS];
00292 }
00293
00294 static void expand_rsync_acl_list(rsync_acl_list *racl_list)
00295 {
00296
00297 if (racl_list->malloced <= racl_list->count) {
00298 rsync_acl *new_ptr;
00299 size_t new_size;
00300 if (racl_list->malloced < 1000)
00301 new_size = racl_list->malloced + 1000;
00302 else
00303 new_size = racl_list->malloced * 2;
00304 new_ptr = realloc_array(racl_list->racls, rsync_acl, new_size);
00305 if (verbose >= 3) {
00306 rprintf(FINFO, "expand_rsync_acl_list to %.0f bytes, did%s move\n",
00307 (double) new_size * sizeof racl_list->racls[0],
00308 racl_list->racls ? "" : " not");
00309 }
00310
00311 racl_list->racls = new_ptr;
00312 racl_list->malloced = new_size;
00313
00314 if (!racl_list->racls)
00315 out_of_memory("expand_rsync_acl_list");
00316 }
00317 }
00318
00319 static int find_matching_rsync_acl(SMB_ACL_TYPE_T type,
00320 const rsync_acl_list *racl_list,
00321 const rsync_acl *racl)
00322 {
00323 static int access_match = -1, default_match = -1;
00324 int *match = type == SMB_ACL_TYPE_ACCESS ? &access_match : &default_match;
00325 size_t count = racl_list->count;
00326
00327
00328
00329
00330 if (*match == -1)
00331 *match = racl_list->count - 1;
00332 while (count--) {
00333 if (rsync_acls_equal(&racl_list->racls[*match], racl))
00334 return *match;
00335 if (!(*match)--)
00336 *match = racl_list->count - 1;
00337 }
00338
00339 *match = -1;
00340 return *match;
00341 }
00342
00343
00344
00345
00346
00347
00348
00349 static void send_ida_list(int f, const ida_list *idal, char tag_char)
00350 {
00351 id_access *ida;
00352 size_t count = idal->count;
00353 for (ida = idal->idas; count--; ida++) {
00354 write_byte(f, tag_char);
00355 write_byte(f, ida->access);
00356 write_int(f, ida->id);
00357
00358
00359
00360 if (tag_char == 'U')
00361 add_uid(ida->id);
00362 else
00363 add_gid(ida->id);
00364 }
00365 }
00366
00367 static void send_rsync_acl(int f, const rsync_acl *racl)
00368 {
00369 size_t count = count_racl_entries(racl);
00370 write_int(f, count);
00371 if (racl->user_obj != ACL_NO_ENTRY) {
00372 write_byte(f, 'u');
00373 write_byte(f, racl->user_obj);
00374 }
00375 send_ida_list(f, &racl->users, 'U');
00376 if (racl->group_obj != ACL_NO_ENTRY) {
00377 write_byte(f, 'g');
00378 write_byte(f, racl->group_obj);
00379 }
00380 send_ida_list(f, &racl->groups, 'G');
00381 if (racl->mask != ACL_NO_ENTRY) {
00382 write_byte(f, 'm');
00383 write_byte(f, racl->mask);
00384 }
00385 if (racl->other != ACL_NO_ENTRY) {
00386 write_byte(f, 'o');
00387 write_byte(f, racl->other);
00388 }
00389 }
00390
00391 static rsync_acl _curr_rsync_acls[2];
00392
00393 static const char *str_acl_type(SMB_ACL_TYPE_T type)
00394 {
00395 return type == SMB_ACL_TYPE_ACCESS ? "SMB_ACL_TYPE_ACCESS"
00396 : type == SMB_ACL_TYPE_DEFAULT ? "SMB_ACL_TYPE_DEFAULT"
00397 : "unknown SMB_ACL_TYPE_T";
00398 }
00399
00400
00401
00402 int make_acl(const struct file_struct *file, const char *fname)
00403 {
00404 SMB_ACL_TYPE_T type;
00405 rsync_acl *curr_racl;
00406
00407 if (S_ISLNK(file->mode))
00408 return 1;
00409
00410 curr_racl = &_curr_rsync_acls[0];
00411 type = SMB_ACL_TYPE_ACCESS;
00412 do {
00413 SMB_ACL_T sacl;
00414 BOOL ok;
00415 if ((sacl = sys_acl_get_file(fname, type)) != 0) {
00416 ok = unpack_smb_acl(curr_racl, sacl);
00417 sys_acl_free_acl(sacl);
00418 if (!ok)
00419 return -1;
00420
00421 if (curr_racl->group_obj == curr_racl->mask
00422 && (preserve_acls == 1
00423 || (!curr_racl->users.count
00424 && !curr_racl->groups.count)))
00425 curr_racl->mask = ACL_NO_ENTRY;
00426
00427 if (type == SMB_ACL_TYPE_ACCESS && preserve_acls == 1)
00428 rsync_acl_strip_perms(curr_racl);
00429 } else if (errno == ENOTSUP) {
00430
00431 *curr_racl = rsync_acl_initializer;
00432 } else {
00433 rprintf(FERROR, "send_acl: sys_acl_get_file(%s, %s): %s\n",
00434 fname, str_acl_type(type), strerror(errno));
00435 return -1;
00436 }
00437 curr_racl++;
00438 } while (BUMP_TYPE(type) && S_ISDIR(file->mode));
00439
00440 return 0;
00441 }
00442
00443
00444
00445 void send_acl(const struct file_struct *file, int f)
00446 {
00447 SMB_ACL_TYPE_T type;
00448 rsync_acl *curr_racl;
00449
00450 if (S_ISLNK(file->mode))
00451 return;
00452
00453 curr_racl = &_curr_rsync_acls[0];
00454 type = SMB_ACL_TYPE_ACCESS;
00455 do {
00456 int index;
00457 rsync_acl_list *racl_list = rsync_acl_lists(type);
00458 if (f == -1) {
00459 rsync_acl_free(curr_racl);
00460 continue;
00461 }
00462 if ((index = find_matching_rsync_acl(type, racl_list, curr_racl))
00463 != -1) {
00464 write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'a' : 'd');
00465 write_int(f, index);
00466 rsync_acl_free(curr_racl);
00467 } else {
00468 write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'A' : 'D');
00469 send_rsync_acl(f, curr_racl);
00470 expand_rsync_acl_list(racl_list);
00471 racl_list->racls[racl_list->count++] = *curr_racl;
00472 }
00473 curr_racl++;
00474 } while (BUMP_TYPE(type) && S_ISDIR(file->mode));
00475 }
00476
00477
00478
00479
00480
00481
00482 typedef struct {
00483 const struct file_struct *file;
00484 int aclidx;
00485 } file_acl_index;
00486
00487 typedef struct {
00488 size_t count;
00489 size_t malloced;
00490 file_acl_index *fais;
00491 } file_acl_index_list;
00492
00493 static file_acl_index_list _file_acl_index_lists[] = {
00494 {0, 0, NULL },
00495 {0, 0, NULL }
00496 };
00497
00498 static inline file_acl_index_list *file_acl_index_lists(SMB_ACL_TYPE_T type)
00499 {
00500 return &_file_acl_index_lists[type != SMB_ACL_TYPE_ACCESS];
00501 }
00502
00503 static void expand_file_acl_index_list(file_acl_index_list *flst)
00504 {
00505
00506 if (flst->malloced <= flst->count) {
00507 file_acl_index *new_ptr;
00508 size_t new_size;
00509
00510 if (flst->malloced < 1000)
00511 new_size = flst->malloced + 1000;
00512 else
00513 new_size = flst->malloced * 2;
00514 new_ptr = realloc_array(flst->fais, file_acl_index, new_size);
00515 if (verbose >= 3) {
00516 rprintf(FINFO, "expand_file_acl_index_list to %.0f bytes, did%s move\n",
00517 (double) new_size * sizeof flst->fais[0],
00518 flst->fais ? "" : " not");
00519 }
00520
00521 flst->fais = new_ptr;
00522 flst->malloced = new_size;
00523
00524 if (!flst->fais)
00525 out_of_memory("expand_file_acl_index_list");
00526 }
00527 }
00528
00529
00530
00531 typedef struct {
00532 size_t count;
00533 size_t malloced;
00534 SMB_ACL_T *sacls;
00535 } smb_acl_list;
00536
00537 static smb_acl_list _smb_acl_lists[] = {
00538 { 0, 0, NULL },
00539 { 0, 0, NULL }
00540 };
00541
00542 static inline smb_acl_list *smb_acl_lists(SMB_ACL_TYPE_T type)
00543 {
00544 return &_smb_acl_lists[type != SMB_ACL_TYPE_ACCESS];
00545 }
00546
00547 static void expand_smb_acl_list(smb_acl_list *sacl_list)
00548 {
00549
00550 if (sacl_list->malloced <= sacl_list->count) {
00551 SMB_ACL_T *new_ptr;
00552 size_t new_size;
00553 if (sacl_list->malloced < 1000)
00554 new_size = sacl_list->malloced + 1000;
00555 else
00556 new_size = sacl_list->malloced * 2;
00557 new_ptr = realloc_array(sacl_list->sacls, SMB_ACL_T, new_size);
00558 if (verbose >= 3) {
00559 rprintf(FINFO, "expand_smb_acl_list to %.0f bytes, did%s move\n",
00560 (double) new_size * sizeof sacl_list->sacls[0],
00561 sacl_list->sacls ? "" : " not");
00562 }
00563
00564 sacl_list->sacls = new_ptr;
00565 sacl_list->malloced = new_size;
00566
00567 if (!sacl_list->sacls)
00568 out_of_memory("expand_smb_acl_list");
00569 }
00570 }
00571
00572 #define CALL_OR_ERROR(func,args,str) \
00573 do { \
00574 if (func args) { \
00575 errfun = str; \
00576 goto error_exit; \
00577 } \
00578 } while (0)
00579
00580 #define COE(func,args) CALL_OR_ERROR(func,args,#func)
00581 #define COE2(func,args) CALL_OR_ERROR(func,args,NULL)
00582
00583 static int store_access_in_entry(uchar access, SMB_ACL_ENTRY_T entry)
00584 {
00585 const char *errfun = NULL;
00586 SMB_ACL_PERMSET_T permset;
00587
00588 COE( sys_acl_get_permset,(entry, &permset) );
00589 COE( sys_acl_clear_perms,(permset) );
00590 if (access & 4)
00591 COE( sys_acl_add_perm,(permset, SMB_ACL_READ) );
00592 if (access & 2)
00593 COE( sys_acl_add_perm,(permset, SMB_ACL_WRITE) );
00594 if (access & 1)
00595 COE( sys_acl_add_perm,(permset, SMB_ACL_EXECUTE) );
00596 COE( sys_acl_set_permset,(entry, permset) );
00597
00598 return 0;
00599
00600 error_exit:
00601 rprintf(FERROR, "store_access_in_entry %s(): %s\n", errfun,
00602 strerror(errno));
00603 return -1;
00604 }
00605
00606
00607 static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl)
00608 {
00609 size_t count;
00610 id_access *ida;
00611 const char *errfun = NULL;
00612 SMB_ACL_ENTRY_T entry;
00613
00614 if (!(*smb_acl = sys_acl_init(calc_sacl_entries(racl)))) {
00615 rprintf(FERROR, "pack_smb_acl: sys_acl_init(): %s\n",
00616 strerror(errno));
00617 return False;
00618 }
00619
00620 COE( sys_acl_create_entry,(smb_acl, &entry) );
00621 COE( sys_acl_set_tag_type,(entry, SMB_ACL_USER_OBJ) );
00622 COE2( store_access_in_entry,(racl->user_obj & 7, entry) );
00623
00624 for (ida = racl->users.idas, count = racl->users.count;
00625 count--; ida++) {
00626 COE( sys_acl_create_entry,(smb_acl, &entry) );
00627 COE( sys_acl_set_tag_type,(entry, SMB_ACL_USER) );
00628 COE( sys_acl_set_qualifier,(entry, (void*)&ida->id) );
00629 COE2( store_access_in_entry,(ida->access, entry) );
00630 }
00631
00632 COE( sys_acl_create_entry,(smb_acl, &entry) );
00633 COE( sys_acl_set_tag_type,(entry, SMB_ACL_GROUP_OBJ) );
00634 COE2( store_access_in_entry,(racl->group_obj & 7, entry) );
00635
00636 for (ida = racl->groups.idas, count = racl->groups.count;
00637 count--; ida++) {
00638 COE( sys_acl_create_entry,(smb_acl, &entry) );
00639 COE( sys_acl_set_tag_type,(entry, SMB_ACL_GROUP) );
00640 COE( sys_acl_set_qualifier,(entry, (void*)&ida->id) );
00641 COE2( store_access_in_entry,(ida->access, entry) );
00642 }
00643 #ifndef ACLS_NEED_MASK
00644 if (racl->mask != ACL_NO_ENTRY) {
00645 #endif
00646 COE( sys_acl_create_entry,(smb_acl, &entry) );
00647 COE( sys_acl_set_tag_type,(entry, SMB_ACL_MASK) );
00648 COE2( store_access_in_entry,(racl->mask, entry) );
00649 #ifndef ACLS_NEED_MASK
00650 }
00651 #endif
00652
00653 COE( sys_acl_create_entry,(smb_acl, &entry) );
00654 COE( sys_acl_set_tag_type,(entry, SMB_ACL_OTHER) );
00655 COE2( store_access_in_entry,(racl->other & 7, entry) );
00656
00657 #ifdef DEBUG
00658 if (sys_acl_valid(*smb_acl) < 0)
00659 rprintf(FINFO, "pack_smb_acl: warning: system says the ACL I packed is invalid\n");
00660 #endif
00661
00662 return True;
00663
00664 error_exit:
00665 if (errfun) {
00666 rprintf(FERROR, "pack_smb_acl %s(): %s\n", errfun,
00667 strerror(errno));
00668 }
00669 sys_acl_free_acl(*smb_acl);
00670 return False;
00671 }
00672
00673 static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode, mode_t mode)
00674 {
00675 SMB_ACL_ENTRY_T entry;
00676 const char *errfun;
00677 int rc;
00678
00679 if (S_ISDIR(mode)) {
00680
00681
00682 #ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS
00683 if (mode & S_ISVTX)
00684 mode &= ~0077;
00685 #else
00686 if (mode & S_ISVTX && !(old_mode & S_ISVTX))
00687 mode &= ~0077;
00688 } else {
00689
00690
00691 if ((old_mode & S_ISUID && !(mode & S_ISUID))
00692 || (old_mode & S_ISGID && !(mode & S_ISGID)))
00693 mode &= ~0077;
00694 #endif
00695 }
00696
00697 errfun = "sys_acl_get_entry";
00698 for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry);
00699 rc == 1;
00700 rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {
00701 SMB_ACL_TAG_T tag_type;
00702 if ((rc = sys_acl_get_tag_type(entry, &tag_type))) {
00703 errfun = "sys_acl_get_tag_type";
00704 break;
00705 }
00706 switch (tag_type) {
00707 case SMB_ACL_USER_OBJ:
00708 COE2( store_access_in_entry,((mode >> 6) & 7, entry) );
00709 break;
00710 case SMB_ACL_GROUP_OBJ:
00711
00712 if (racl->group_obj != ACL_NO_ENTRY)
00713 break;
00714 COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
00715 break;
00716 case SMB_ACL_MASK:
00717 #ifndef ACLS_NEED_MASK
00718
00719 if (racl->mask == ACL_NO_ENTRY)
00720 break;
00721 #endif
00722 COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
00723 break;
00724 case SMB_ACL_OTHER:
00725 COE2( store_access_in_entry,(mode & 7, entry) );
00726 break;
00727 }
00728 }
00729 if (rc) {
00730 error_exit:
00731 if (errfun) {
00732 rprintf(FERROR, "change_sacl_perms: %s(): %s\n",
00733 errfun, strerror(errno));
00734 }
00735 return ~0u;
00736 }
00737
00738 #ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS
00739
00740 if (old_mode & (S_ISUID | S_ISGID | S_ISVTX)
00741 && (old_mode & CHMOD_BITS) == (mode & CHMOD_BITS))
00742 old_mode &= ~(S_ISUID | S_ISGID | S_ISVTX);
00743 #endif
00744
00745
00746 return (old_mode & ~ACCESSPERMS) | (mode & ACCESSPERMS);
00747 }
00748
00749 static void receive_rsync_acl(rsync_acl *racl, int f, SMB_ACL_TYPE_T type)
00750 {
00751 uchar computed_mask_bits = 0;
00752 ida_list *idal = NULL;
00753 id_access *ida;
00754 size_t count;
00755
00756 *racl = rsync_acl_initializer;
00757
00758 if (!(count = read_int(f)))
00759 return;
00760
00761 while (count--) {
00762 char tag = read_byte(f);
00763 uchar access = read_byte(f);
00764 if (access & ~ (4 | 2 | 1)) {
00765 rprintf(FERROR, "receive_rsync_acl: bogus permset %o\n",
00766 access);
00767 exit_cleanup(RERR_STREAMIO);
00768 }
00769 switch (tag) {
00770 case 'u':
00771 if (racl->user_obj != ACL_NO_ENTRY) {
00772 rprintf(FERROR, "receive_rsync_acl: error: duplicate USER_OBJ entry\n");
00773 exit_cleanup(RERR_STREAMIO);
00774 }
00775 racl->user_obj = access;
00776 continue;
00777 case 'U':
00778 idal = &racl->users;
00779 break;
00780 case 'g':
00781 if (racl->group_obj != ACL_NO_ENTRY) {
00782 rprintf(FERROR, "receive_rsync_acl: error: duplicate GROUP_OBJ entry\n");
00783 exit_cleanup(RERR_STREAMIO);
00784 }
00785 racl->group_obj = access;
00786 continue;
00787 case 'G':
00788 idal = &racl->groups;
00789 break;
00790 case 'm':
00791 if (racl->mask != ACL_NO_ENTRY) {
00792 rprintf(FERROR, "receive_rsync_acl: error: duplicate MASK entry\n");
00793 exit_cleanup(RERR_STREAMIO);
00794 }
00795 racl->mask = access;
00796 continue;
00797 case 'o':
00798 if (racl->other != ACL_NO_ENTRY) {
00799 rprintf(FERROR, "receive_rsync_acl: error: duplicate OTHER entry\n");
00800 exit_cleanup(RERR_STREAMIO);
00801 }
00802 racl->other = access;
00803 continue;
00804 default:
00805 rprintf(FERROR, "receive_rsync_acl: unknown tag %c\n",
00806 tag);
00807 exit_cleanup(RERR_STREAMIO);
00808 }
00809 expand_ida_list(idal);
00810 ida = &idal->idas[idal->count++];
00811 ida->access = access;
00812 ida->id = read_int(f);
00813 computed_mask_bits |= access;
00814 }
00815
00816 if (type == SMB_ACL_TYPE_DEFAULT) {
00817
00818 if (racl->user_obj == ACL_NO_ENTRY)
00819 racl->user_obj = 7;
00820 if (racl->group_obj == ACL_NO_ENTRY)
00821 racl->group_obj = 0;
00822 if (racl->other == ACL_NO_ENTRY)
00823 racl->other = 0;
00824 }
00825 #ifndef ACLS_NEED_MASK
00826 if (!racl->users.count && !racl->groups.count) {
00827
00828
00829 if (racl->mask != ACL_NO_ENTRY) {
00830
00831 racl->group_obj &= racl->mask | ACL_NO_ENTRY;
00832 racl->mask = ACL_NO_ENTRY;
00833 }
00834 } else
00835 #endif
00836 if (racl->mask == ACL_NO_ENTRY)
00837 racl->mask = computed_mask_bits | (racl->group_obj & 7);
00838 }
00839
00840
00841 void receive_acl(struct file_struct *file, int f)
00842 {
00843 SMB_ACL_TYPE_T type;
00844 char *fname;
00845
00846 if (S_ISLNK(file->mode))
00847 return;
00848
00849 fname = f_name(file, NULL);
00850 type = SMB_ACL_TYPE_ACCESS;
00851 do {
00852 char tag;
00853 file_acl_index_list *flst = file_acl_index_lists(type);
00854
00855 expand_file_acl_index_list(flst);
00856
00857 tag = read_byte(f);
00858 if (tag == 'A' || tag == 'a') {
00859 if (type != SMB_ACL_TYPE_ACCESS) {
00860 rprintf(FERROR, "receive_acl %s: duplicate access ACL\n",
00861 fname);
00862 exit_cleanup(RERR_STREAMIO);
00863 }
00864 } else if (tag == 'D' || tag == 'd') {
00865 if (type == SMB_ACL_TYPE_ACCESS) {
00866 rprintf(FERROR, "receive_acl %s: expecting access ACL; got default\n",
00867 fname);
00868 exit_cleanup(RERR_STREAMIO);
00869 }
00870 } else {
00871 rprintf(FERROR, "receive_acl %s: unknown ACL type tag: %c\n",
00872 fname, tag);
00873 exit_cleanup(RERR_STREAMIO);
00874 }
00875 if (tag == 'A' || tag == 'D') {
00876 rsync_acl racl;
00877 rsync_acl_list *racl_list = rsync_acl_lists(type);
00878 smb_acl_list *sacl_list = smb_acl_lists(type);
00879 flst->fais[flst->count].aclidx = racl_list->count;
00880 flst->fais[flst->count++].file = file;
00881 receive_rsync_acl(&racl, f, type);
00882 expand_rsync_acl_list(racl_list);
00883 racl_list->racls[racl_list->count++] = racl;
00884 expand_smb_acl_list(sacl_list);
00885 sacl_list->sacls[sacl_list->count++] = NULL;
00886 } else {
00887 int index = read_int(f);
00888 rsync_acl_list *racl_list = rsync_acl_lists(type);
00889 if ((size_t) index >= racl_list->count) {
00890 rprintf(FERROR, "receive_acl %s: %s ACL index %d out of range\n",
00891 fname,
00892 str_acl_type(type),
00893 index);
00894 exit_cleanup(RERR_STREAMIO);
00895 }
00896 flst->fais[flst->count].aclidx = index;
00897 flst->fais[flst->count++].file = file;
00898 }
00899 } while (BUMP_TYPE(type) && S_ISDIR(file->mode));
00900 }
00901
00902 static int file_acl_index_list_sorter(const void *f1, const void *f2)
00903 {
00904 const file_acl_index *fileaclidx1 = (const file_acl_index *)f1;
00905 const file_acl_index *fileaclidx2 = (const file_acl_index *)f2;
00906 return fileaclidx1->file == fileaclidx2->file ? 0
00907 : fileaclidx1->file < fileaclidx2->file ? -1 : 1;
00908 }
00909
00910 void sort_file_acl_index_lists()
00911 {
00912 SMB_ACL_TYPE_T type;
00913
00914 type = SMB_ACL_TYPE_ACCESS;
00915 do {
00916 file_acl_index_list *flst = file_acl_index_lists(type);
00917
00918 if (!flst->count)
00919 continue;
00920
00921 qsort(flst->fais, flst->count, sizeof flst->fais[0],
00922 &file_acl_index_list_sorter);
00923 } while (BUMP_TYPE(type));
00924 }
00925
00926 static int find_file_acl_index(const file_acl_index_list *flst,
00927 const struct file_struct *file)
00928 {
00929 int low = 0, high = flst->count;
00930 const struct file_struct *file_mid;
00931
00932 if (!high--)
00933 return -1;
00934 do {
00935 int mid = (high + low) / 2;
00936 file_mid = flst->fais[mid].file;
00937 if (file_mid == file)
00938 return flst->fais[mid].aclidx;
00939 if (file_mid > file)
00940 high = mid - 1;
00941 else
00942 low = mid + 1;
00943 } while (low < high);
00944 if (low == high) {
00945 file_mid = flst->fais[low].file;
00946 if (file_mid == file)
00947 return flst->fais[low].aclidx;
00948 }
00949 rprintf(FERROR,
00950 "find_file_acl_index: can't find entry for file in list\n");
00951 exit_cleanup(RERR_STREAMIO);
00952 return -1;
00953 }
00954
00955
00956 int dup_acl(const char *orig, const char *bak, mode_t mode)
00957 {
00958 SMB_ACL_TYPE_T type;
00959 int ret = 0;
00960
00961 type = SMB_ACL_TYPE_ACCESS;
00962 do {
00963 SMB_ACL_T sacl_orig, sacl_bak;
00964 rsync_acl racl_orig, racl_bak;
00965 if (!(sacl_orig = sys_acl_get_file(orig, type))) {
00966 rprintf(FERROR, "dup_acl: sys_acl_get_file(%s, %s): %s\n",
00967 orig, str_acl_type(type), strerror(errno));
00968 ret = -1;
00969 continue;
00970 }
00971 if (!(sacl_bak = sys_acl_get_file(orig, type))) {
00972 rprintf(FERROR, "dup_acl: sys_acl_get_file(%s, %s): %s. ignoring\n",
00973 bak, str_acl_type(type), strerror(errno));
00974 ret = -1;
00975
00976 }
00977 if (!unpack_smb_acl(&racl_orig, sacl_orig)) {
00978 ret = -1;
00979 goto out_with_sacls;
00980 }
00981 if (sacl_bak) {
00982 if (!unpack_smb_acl(&racl_bak, sacl_bak)) {
00983 ret = -1;
00984 goto out_with_one_racl;
00985 }
00986 if (rsync_acls_equal(&racl_orig, &racl_bak))
00987 goto out_with_all;
00988 } else {
00989 ;
00990 }
00991 if (type == SMB_ACL_TYPE_DEFAULT
00992 && racl_orig.user_obj == ACL_NO_ENTRY) {
00993 if (sys_acl_delete_def_file(bak) < 0) {
00994 rprintf(FERROR, "dup_acl: sys_acl_delete_def_file(%s): %s\n",
00995 bak, strerror(errno));
00996 ret = -1;
00997 }
00998 } else if (sys_acl_set_file(bak, type, sacl_bak) < 0) {
00999 rprintf(FERROR, "dup_acl: sys_acl_set_file(%s, %s): %s\n",
01000 bak, str_acl_type(type), strerror(errno));
01001 ret = -1;
01002 }
01003 out_with_all:
01004 if (sacl_bak)
01005 rsync_acl_free(&racl_bak);
01006 out_with_one_racl:
01007 rsync_acl_free(&racl_orig);
01008 out_with_sacls:
01009 if (sacl_bak)
01010 sys_acl_free_acl(sacl_bak);
01011
01012 if (sacl_orig)
01013 sys_acl_free_acl(sacl_orig);
01014 } while (BUMP_TYPE(type) && S_ISDIR(mode));
01015
01016 return ret;
01017 }
01018
01019
01020
01021 static const struct file_struct *backup_orig_file = NULL;
01022 static const char null_string[] = "";
01023 static const char *backup_orig_fname = null_string;
01024 static const char *backup_dest_fname = null_string;
01025 static SMB_ACL_T _backup_sacl[] = { NULL, NULL };
01026
01027 void push_keep_backup_acl(const struct file_struct *file,
01028 const char *orig, const char *dest)
01029 {
01030 SMB_ACL_TYPE_T type;
01031 SMB_ACL_T *sacl;
01032
01033 backup_orig_file = file;
01034 backup_orig_fname = orig;
01035 backup_dest_fname = dest;
01036
01037 sacl = &_backup_sacl[0];
01038 type = SMB_ACL_TYPE_ACCESS;
01039 do {
01040 if (type == SMB_ACL_TYPE_DEFAULT && !S_ISDIR(file->mode)) {
01041 *sacl = NULL;
01042 break;
01043 }
01044 if (!(*sacl = sys_acl_get_file(orig, type))) {
01045 rprintf(FERROR,
01046 "push_keep_backup_acl: sys_acl_get_file(%s, %s): %s\n",
01047 orig, str_acl_type(type),
01048 strerror(errno));
01049 }
01050 } while (BUMP_TYPE(type));
01051 }
01052
01053 static int set_keep_backup_acl()
01054 {
01055 SMB_ACL_TYPE_T type;
01056 SMB_ACL_T *sacl;
01057 int ret = 0;
01058
01059 sacl = &_backup_sacl[0];
01060 type = SMB_ACL_TYPE_ACCESS;
01061 do {
01062 if (*sacl
01063 && sys_acl_set_file(backup_dest_fname, type, *sacl) < 0) {
01064 rprintf(FERROR,
01065 "push_keep_backup_acl: sys_acl_get_file(%s, %s): %s\n",
01066 backup_dest_fname,
01067 str_acl_type(type),
01068 strerror(errno));
01069 ret = -1;
01070 }
01071 } while (BUMP_TYPE(type));
01072
01073 return ret;
01074 }
01075
01076 void cleanup_keep_backup_acl()
01077 {
01078 SMB_ACL_TYPE_T type;
01079 SMB_ACL_T *sacl;
01080
01081 backup_orig_file = NULL;
01082 backup_orig_fname = null_string;
01083 backup_dest_fname = null_string;
01084
01085 sacl = &_backup_sacl[0];
01086 type = SMB_ACL_TYPE_ACCESS;
01087 do {
01088 if (*sacl) {
01089 sys_acl_free_acl(*sacl);
01090 *sacl = NULL;
01091 }
01092 } while (BUMP_TYPE(type));
01093 }
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103 int set_acl(const char *fname, const struct file_struct *file, mode_t *mode_p)
01104 {
01105 int unchanged = 1;
01106 SMB_ACL_TYPE_T type;
01107
01108 if (S_ISLNK(file->mode))
01109 return 1;
01110
01111 if (file == backup_orig_file) {
01112 if (!strcmp(fname, backup_dest_fname))
01113 return set_keep_backup_acl();
01114 }
01115 type = SMB_ACL_TYPE_ACCESS;
01116 do {
01117 BOOL ok;
01118 SMB_ACL_T sacl_orig, *sacl_new;
01119 rsync_acl racl_orig, *racl_new;
01120 int aclidx = find_file_acl_index(file_acl_index_lists(type), file);
01121
01122 racl_new = &(rsync_acl_lists(type)->racls[aclidx]);
01123 sacl_new = &(smb_acl_lists(type)->sacls[aclidx]);
01124 sacl_orig = sys_acl_get_file(fname, type);
01125 if (!sacl_orig) {
01126 rprintf(FERROR, "set_acl: sys_acl_get_file(%s, %s): %s\n",
01127 fname, str_acl_type(type), strerror(errno));
01128 unchanged = -1;
01129 continue;
01130 }
01131 ok = unpack_smb_acl(&racl_orig, sacl_orig);
01132 sys_acl_free_acl(sacl_orig);
01133 if (!ok) {
01134 unchanged = -1;
01135 continue;
01136 }
01137 if (type == SMB_ACL_TYPE_ACCESS)
01138 ok = rsync_acl_extended_parts_equal(&racl_orig, racl_new);
01139 else
01140 ok = rsync_acls_equal(&racl_orig, racl_new);
01141 rsync_acl_free(&racl_orig);
01142 if (ok)
01143 continue;
01144 if (!dry_run && mode_p) {
01145 if (type == SMB_ACL_TYPE_DEFAULT
01146 && racl_new->user_obj == ACL_NO_ENTRY) {
01147 if (sys_acl_delete_def_file(fname) < 0) {
01148 rprintf(FERROR, "set_acl: sys_acl_delete_def_file(%s): %s\n",
01149 fname, strerror(errno));
01150 unchanged = -1;
01151 continue;
01152 }
01153 } else {
01154 mode_t cur_mode = *mode_p;
01155 if (!*sacl_new
01156 && !pack_smb_acl(sacl_new, racl_new)) {
01157 unchanged = -1;
01158 continue;
01159 }
01160 if (type == SMB_ACL_TYPE_ACCESS) {
01161 cur_mode = change_sacl_perms(*sacl_new, racl_new,
01162 cur_mode, file->mode);
01163 if (cur_mode == ~0u)
01164 continue;
01165 }
01166 if (sys_acl_set_file(fname, type, *sacl_new) < 0) {
01167 rprintf(FERROR, "set_acl: sys_acl_set_file(%s, %s): %s\n",
01168 fname, str_acl_type(type),
01169 strerror(errno));
01170 unchanged = -1;
01171 continue;
01172 }
01173 if (type == SMB_ACL_TYPE_ACCESS)
01174 *mode_p = cur_mode;
01175 }
01176 }
01177 if (unchanged == 1)
01178 unchanged = 0;
01179 } while (BUMP_TYPE(type) && S_ISDIR(file->mode));
01180
01181 return unchanged;
01182 }
01183
01184
01185
01186
01187
01188 static rsync_acl_list *_enum_racl_lists[] = {
01189 &_rsync_acl_lists[0], &_rsync_acl_lists[1], NULL
01190 };
01191
01192 static rsync_acl_list **enum_racl_list = &_enum_racl_lists[0];
01193 static size_t enum_racl_index = 0;
01194 static size_t enum_ida_index = 0;
01195
01196
01197
01198 static id_t *next_ace_id(SMB_ACL_TAG_T tag_type, const rsync_acl *racl)
01199 {
01200 const ida_list *idal = tag_type == SMB_ACL_USER ? &racl->users : &racl->groups;
01201 if (enum_ida_index < idal->count) {
01202 id_access *ida = &idal->idas[enum_ida_index++];
01203 return &ida->id;
01204 }
01205 enum_ida_index = 0;
01206 return NULL;
01207 }
01208
01209 static id_t *next_acl_id(SMB_ACL_TAG_T tag_type, const rsync_acl_list *racl_list)
01210 {
01211 for (; enum_racl_index < racl_list->count; enum_racl_index++) {
01212 rsync_acl *racl = &racl_list->racls[enum_racl_index];
01213 id_t *id = next_ace_id(tag_type, racl);
01214 if (id)
01215 return id;
01216 }
01217 enum_racl_index = 0;
01218 return NULL;
01219 }
01220
01221 static id_t *next_acl_list_id(SMB_ACL_TAG_T tag_type)
01222 {
01223 for (; *enum_racl_list; enum_racl_list++) {
01224 id_t *id = next_acl_id(tag_type, *enum_racl_list);
01225 if (id)
01226 return id;
01227 }
01228 enum_racl_list = &_enum_racl_lists[0];
01229 return NULL;
01230 }
01231
01232 id_t *next_acl_uid()
01233 {
01234 return next_acl_list_id(SMB_ACL_USER);
01235 }
01236
01237 id_t *next_acl_gid()
01238 {
01239 return next_acl_list_id(SMB_ACL_GROUP);
01240 }
01241
01242 int default_perms_for_dir(const char *dir)
01243 {
01244 rsync_acl racl;
01245 SMB_ACL_T sacl;
01246 BOOL ok;
01247 int perms;
01248
01249 if (dir == NULL)
01250 dir = ".";
01251 perms = ACCESSPERMS & ~orig_umask;
01252
01253 sacl = sys_acl_get_file(dir, SMB_ACL_TYPE_DEFAULT);
01254 if (sacl == NULL) {
01255
01256 switch (errno) {
01257 case ENOTSUP:
01258
01259 break;
01260 case ENOENT:
01261 if (dry_run) {
01262
01263
01264 break;
01265 }
01266
01267 default:
01268 rprintf(FERROR, "default_perms_for_dir: sys_acl_get_file(%s, %s): %s, falling back on umask\n",
01269 dir, str_acl_type(SMB_ACL_TYPE_DEFAULT), strerror(errno));
01270 }
01271 return perms;
01272 }
01273
01274
01275 ok = unpack_smb_acl(&racl, sacl);
01276 sys_acl_free_acl(sacl);
01277 if (!ok) {
01278 rprintf(FERROR, "default_perms_for_dir: unpack_smb_acl failed, falling back on umask\n");
01279 return perms;
01280 }
01281
01282
01283 if (racl.user_obj != ACL_NO_ENTRY) {
01284 perms = rsync_acl_get_perms(&racl);
01285 if (verbose > 2)
01286 rprintf(FINFO, "got ACL-based default perms %o for directory %s\n", perms, dir);
01287 }
01288
01289 rsync_acl_free(&racl);
01290 return perms;
01291 }
01292
01293 #endif