X-Git-Url: https://mattmccutchen.net/rsync/rsync-patches.git/blobdiff_plain/e3e9343306d6a1a342305df4f766a9bc3570661b..88f7513df3fca996d2669285ddb48a362a9a9825:/acls.diff diff --git a/acls.diff b/acls.diff index e00c4da..d8b5419 100644 --- a/acls.diff +++ b/acls.diff @@ -1,19 +1,15 @@ After applying this patch, run these commands for a successful build: - autoconf - autoheader - ./configure --with-acl-support - make proto + ./prepare-source + ./configure --enable-acl-support make -The program currently complains when the --acls (-A) option is used to copy -from a disk that doesn't support ACLs. This should be changed to silently -notice that no ACLs are available to copy. Of course, trying to write out -ACLs to a non-ACL-supporting disk should complain. +See the --acls (-A) option in the revised man page for a note on using this +latest ACL-enabling patch to send files to an older ACL-enabled rsync. ---- orig/Makefile.in 2004-11-02 16:47:15 -+++ Makefile.in 2004-10-20 06:32:26 -@@ -25,16 +25,16 @@ VERSION=@VERSION@ +--- old/Makefile.in ++++ new/Makefile.in +@@ -25,15 +25,15 @@ VERSION=@VERSION@ .SUFFIXES: .SUFFIXES: .c .o @@ -22,158 +18,294 @@ ACLs to a non-ACL-supporting disk should complain. LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \ - lib/permstring.o lib/pool_alloc.o @LIBOBJS@ + lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@ - ZLIBOBJ=zlib/deflate.o zlib/infblock.o zlib/infcodes.o zlib/inffast.o \ - zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \ - zlib/zutil.o zlib/adler32.o + ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \ + zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \ main.o checksum.o match.o syscall.o log.o backup.o OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \ -- fileio.o batch.o clientname.o -+ fileio.o batch.o clientname.o acls.o +- fileio.o batch.o clientname.o chmod.o ++ fileio.o batch.o clientname.o chmod.o acls.o OBJS3=progress.o pipe.o DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \ ---- orig/acls.c 2004-10-20 06:36:04 -+++ acls.c 2004-10-20 06:36:04 -@@ -0,0 +1,1130 @@ -+/* -*- c-file-style: "linux" -*- -+ Copyright (C) Andrew Tridgell 1996 -+ Copyright (C) Paul Mackerras 1996 -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; if not, write to the Free Software -+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -+*/ -+ -+/* handle passing ACLs between systems */ +--- old/acls.c ++++ new/acls.c +@@ -0,0 +1,1093 @@ ++/* ++ * Handle passing Access Control Lists between systems. ++ * ++ * Copyright (C) 1996 Andrew Tridgell ++ * Copyright (C) 1996 Paul Mackerras ++ * Copyright (C) 2006 Wayne Davison ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. ++ */ + +#include "rsync.h" +#include "lib/sysacls.h" + -+#if SUPPORT_ACLS ++#ifdef SUPPORT_ACLS + -+extern int preserve_acls; +extern int am_root; +extern int dry_run; ++extern int orig_umask; ++extern int preserve_acls; ++extern unsigned int file_struct_len; ++ ++/* === ACL structures === */ + +typedef struct { + id_t id; + uchar access; -+ SMB_ACL_TAG_T tag_type; -+} rsync_ace; ++} id_access; + +typedef struct { -+ size_t count; -+ size_t malloced; -+ rsync_ace *races; ++ id_access *idas; ++ int count; ++} ida_entries; ++ ++#define NO_ENTRY ((uchar)0x80) ++typedef struct rsync_acl { ++ ida_entries users; ++ ida_entries groups; ++ /* These will be NO_ENTRY if there's no such entry. */ ++ uchar user_obj; ++ uchar group_obj; ++ uchar mask; ++ uchar other; +} rsync_acl; + -+static const rsync_acl rsync_acl_initializer = { 0, 0, NULL }; ++typedef struct { ++ rsync_acl racl; ++ SMB_ACL_T sacl; ++} acl_duo; ++ ++static const rsync_acl empty_rsync_acl = { ++ {NULL, 0}, {NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY ++}; ++ ++static item_list access_acl_list = EMPTY_ITEM_LIST; ++static item_list default_acl_list = EMPTY_ITEM_LIST; ++ ++/* === Calculations on ACL types === */ + -+static void expand_rsync_acl(rsync_acl *racl) ++static const char *str_acl_type(SMB_ACL_TYPE_T type) +{ -+ /* first time through, 0 <= 0, so list is expanded: -+ * diabolical, rsync guys! */ -+ if (racl->malloced <= racl->count) { -+ rsync_ace *new_ptr; -+ size_t new_size = racl->malloced + 10; -+ new_ptr = realloc_array(racl->races, rsync_ace, new_size); -+ if (verbose >= 4) { -+ rprintf(FINFO, "expand rsync_acl to %.0f bytes, did%s move\n", -+ (double) new_size * sizeof racl->races[0], -+ racl->races ? "" : " not"); -+ } ++ return type == SMB_ACL_TYPE_ACCESS ? "SMB_ACL_TYPE_ACCESS" ++ : type == SMB_ACL_TYPE_DEFAULT ? "SMB_ACL_TYPE_DEFAULT" ++ : "unknown SMB_ACL_TYPE_T"; ++} ++ ++#define OTHER_TYPE(t) (SMB_ACL_TYPE_ACCESS+SMB_ACL_TYPE_DEFAULT-(t)) ++#define BUMP_TYPE(t) ((t = OTHER_TYPE(t)) == SMB_ACL_TYPE_DEFAULT) ++ ++static int count_racl_entries(const rsync_acl *racl) ++{ ++ return racl->users.count + racl->groups.count ++ + (racl->user_obj != NO_ENTRY) ++ + (racl->group_obj != NO_ENTRY) ++ + (racl->mask != NO_ENTRY) ++ + (racl->other != NO_ENTRY); ++} ++ ++static int calc_sacl_entries(const rsync_acl *racl) ++{ ++ /* A System ACL always gets user/group/other permission entries. */ ++ return racl->users.count + racl->groups.count ++#ifdef ACLS_NEED_MASK ++ + 4; ++#else ++ + (racl->mask != NO_ENTRY) + 3; ++#endif ++} ++ ++/* Extracts and returns the permission bits from the ACL. This cannot be ++ * called on an rsync_acl that has NO_ENTRY in any spot but the mask. */ ++static int rsync_acl_get_perms(const rsync_acl *racl) ++{ ++ return (racl->user_obj << 6) ++ + ((racl->mask != NO_ENTRY ? racl->mask : racl->group_obj) << 3) ++ + racl->other; ++} ++ ++/* Removes the permission-bit entries from the ACL because these ++ * can be reconstructed from the file's mode. */ ++static void rsync_acl_strip_perms(rsync_acl *racl) ++{ ++ racl->user_obj = NO_ENTRY; ++ if (racl->mask == NO_ENTRY) ++ racl->group_obj = NO_ENTRY; ++ else { ++ if (racl->group_obj == racl->mask) ++ racl->group_obj = NO_ENTRY; ++ racl->mask = NO_ENTRY; ++ } ++ racl->other = NO_ENTRY; ++} ++ ++/* Given an empty rsync_acl, fake up the permission bits. */ ++static void rsync_acl_fake_perms(rsync_acl *racl, mode_t mode) ++{ ++ racl->user_obj = (mode >> 6) & 7; ++ racl->group_obj = (mode >> 3) & 7; ++ racl->other = mode & 7; ++} ++ ++/* === Rsync ACL functions === */ + -+ racl->races = new_ptr; -+ racl->malloced = new_size; ++static BOOL ida_entries_equal(const ida_entries *ial1, const ida_entries *ial2) ++{ ++ id_access *ida1, *ida2; ++ int count = ial1->count; ++ if (count != ial2->count) ++ return False; ++ ida1 = ial1->idas; ++ ida2 = ial2->idas; ++ for (; count--; ida1++, ida2++) { ++ if (ida1->access != ida2->access || ida1->id != ida2->id) ++ return False; ++ } ++ return True; ++} + -+ if (!racl->races) -+ out_of_memory("expand_rsync_acl"); ++static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2) ++{ ++ return racl1->user_obj == racl2->user_obj ++ && racl1->group_obj == racl2->group_obj ++ && racl1->mask == racl2->mask ++ && racl1->other == racl2->other ++ && ida_entries_equal(&racl1->users, &racl2->users) ++ && ida_entries_equal(&racl1->groups, &racl2->groups); ++} ++ ++/* Are the extended (non-permission-bit) entries equal? If so, the rest of ++ * the ACL will be handled by the normal mode-preservation code. This is ++ * only meaningful for access ACLs! Note: the 1st arg is a fully-populated ++ * rsync_acl, but the 2nd parameter can be a condensed rsync_acl, which means ++ * that it might have several of its permission objects set to NO_ENTRY. */ ++static BOOL rsync_acl_equal_enough(const rsync_acl *racl1, ++ const rsync_acl *racl2, mode_t m) ++{ ++ if ((racl1->mask ^ racl2->mask) & NO_ENTRY) ++ return False; /* One has a mask and the other doesn't */ ++ ++ /* When there's a mask, the group_obj becomes an extended entry. */ ++ if (racl1->mask != NO_ENTRY) { ++ /* A condensed rsync_acl with a mask can only have no ++ * group_obj when it was identical to the mask. This ++ * means that it was also identical to the group attrs ++ * from the mode. */ ++ if (racl2->group_obj == NO_ENTRY) { ++ if (racl1->group_obj != ((m >> 3) & 7)) ++ return False; ++ } else if (racl1->group_obj != racl2->group_obj) ++ return False; + } ++ return ida_entries_equal(&racl1->users, &racl2->users) ++ && ida_entries_equal(&racl1->groups, &racl2->groups); +} + +static void rsync_acl_free(rsync_acl *racl) +{ -+ free(racl->races); -+ racl->races = NULL; -+ racl->count = 0; -+ racl->malloced = 0; ++ if (racl->users.idas) ++ free(racl->users.idas); ++ if (racl->groups.idas) ++ free(racl->groups.idas); ++ *racl = empty_rsync_acl; +} + -+static int rsync_ace_sorter(const void *r1, const void *r2) ++void free_acl(statx *sxp) +{ -+ rsync_ace *race1 = (rsync_ace *)r1; -+ SMB_ACL_TAG_T rtag1 = race1->tag_type; -+ id_t rid1 = race1->id; -+ rsync_ace *race2 = (rsync_ace *)r2; -+ SMB_ACL_TAG_T rtag2 = race2->tag_type; -+ id_t rid2 = race2->id; -+ /* start at the extrema */ -+ if (rtag1 == SMB_ACL_USER_OBJ || rtag2 == SMB_ACL_MASK) -+ return -1; -+ if (rtag2 == SMB_ACL_USER_OBJ || rtag1 == SMB_ACL_MASK) -+ return 1; -+ /* work inwards */ -+ if (rtag1 == SMB_ACL_OTHER) -+ return 1; -+ if (rtag2 == SMB_ACL_OTHER) -+ return -1; -+ /* only SMB_ACL_USERs and SMB_ACL_GROUP*s left */ -+ if (rtag1 == SMB_ACL_USER) { -+ switch (rtag2) { -+ case SMB_ACL_GROUP: -+ case SMB_ACL_GROUP_OBJ: -+ case SMB_ACL_OTHER: -+ return -1; -+ } -+ /* both USER */ -+ return rid1 == rid2 ? 0 : rid1 < rid2 ? -1 : 1; ++ if (sxp->acc_acl) { ++ rsync_acl_free(sxp->acc_acl); ++ free(sxp->acc_acl); ++ sxp->acc_acl = NULL; + } -+ if (rtag2 == SMB_ACL_USER) -+ return 1; -+ /* only SMB_ACL_GROUP*s to worry about; kick out GROUP_OBJs first */ -+ if (rtag1 == SMB_ACL_GROUP_OBJ) -+ return -1; -+ if (rtag2 == SMB_ACL_GROUP_OBJ) -+ return 1; -+ /* only SMB_ACL_GROUPs left */ ++ if (sxp->def_acl) { ++ rsync_acl_free(sxp->def_acl); ++ free(sxp->def_acl); ++ sxp->def_acl = NULL; ++ } ++} ++ ++static int id_access_sorter(const void *r1, const void *r2) ++{ ++ id_access *ida1 = (id_access *)r1; ++ id_access *ida2 = (id_access *)r2; ++ id_t rid1 = ida1->id, rid2 = ida2->id; + return rid1 == rid2 ? 0 : rid1 < rid2 ? -1 : 1; +} + -+static void sort_rsync_acl(rsync_acl *racl) ++static void sort_ida_entries(ida_entries *idal) +{ -+ if (!racl->count) ++ if (!idal->count) + return; -+ qsort((void **)racl->races, racl->count, sizeof racl->races[0], -+ &rsync_ace_sorter); ++ qsort(idal->idas, idal->count, sizeof idal->idas[0], id_access_sorter); ++} ++ ++/* Transfer the count id_access items out of the temp_ida_list into either ++ * the users or groups ida_entries list in racl. */ ++static void save_idas(item_list *temp_ida_list, rsync_acl *racl, SMB_ACL_TAG_T type) ++{ ++ id_access *idas; ++ ida_entries *ent; ++ ++ if (temp_ida_list->count) { ++ int cnt = temp_ida_list->count; ++ id_access *temp_idas = temp_ida_list->items; ++ if (!(idas = new_array(id_access, cnt))) ++ out_of_memory("save_idas"); ++ memcpy(idas, temp_idas, cnt * sizeof *temp_idas); ++ } else ++ idas = NULL; ++ ++ ent = type == SMB_ACL_USER ? &racl->users : &racl->groups; ++ ++ if (ent->count) { ++ rprintf(FERROR, "save_idas: disjoint list found for type %d\n", type); ++ exit_cleanup(RERR_UNSUPPORTED); ++ } ++ ent->count = temp_ida_list->count; ++ ent->idas = idas; ++ ++ /* Truncate the temporary list now that its idas have been saved. */ ++ temp_ida_list->count = 0; +} + ++/* === System ACLs === */ ++ ++/* Unpack system ACL -> rsync ACL verbatim. Return whether we succeeded. */ +static BOOL unpack_smb_acl(rsync_acl *racl, SMB_ACL_T sacl) +{ ++ static item_list temp_ida_list = EMPTY_ITEM_LIST; ++ SMB_ACL_TAG_T prior_list_type = 0; + SMB_ACL_ENTRY_T entry; -+ int rc; + const char *errfun; -+ *racl = rsync_acl_initializer; ++ int rc; ++ ++ *racl = empty_rsync_acl; + errfun = "sys_acl_get_entry"; + for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry); + rc == 1; + rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) { ++ SMB_ACL_TAG_T tag_type; + SMB_ACL_PERMSET_T permset; ++ uchar access; + void *qualifier; -+ rsync_ace *race; -+ expand_rsync_acl(racl); -+ race = &racl->races[racl->count++]; -+ if ((rc = sys_acl_get_tag_type(entry, &race->tag_type))) { ++ id_access *ida; ++ if ((rc = sys_acl_get_tag_type(entry, &tag_type))) { + errfun = "sys_acl_get_tag_type"; + break; + } @@ -181,14 +313,41 @@ ACLs to a non-ACL-supporting disk should complain. + errfun = "sys_acl_get_tag_type"; + break; + } -+ race->access = (sys_acl_get_perm(permset, SMB_ACL_READ) ? 4 : 0) -+ | (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? 2 : 0) -+ | (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? 1 : 0); -+ switch (race->tag_type) { ++ access = (sys_acl_get_perm(permset, SMB_ACL_READ) ? 4 : 0) ++ | (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? 2 : 0) ++ | (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? 1 : 0); ++ /* continue == done with entry; break == store in temporary ida list */ ++ switch (tag_type) { ++ case SMB_ACL_USER_OBJ: ++ if (racl->user_obj == NO_ENTRY) ++ racl->user_obj = access; ++ else ++ rprintf(FINFO, "unpack_smb_acl: warning: duplicate USER_OBJ entry ignored\n"); ++ continue; + case SMB_ACL_USER: ++ break; ++ case SMB_ACL_GROUP_OBJ: ++ if (racl->group_obj == NO_ENTRY) ++ racl->group_obj = access; ++ else ++ rprintf(FINFO, "unpack_smb_acl: warning: duplicate GROUP_OBJ entry ignored\n"); ++ continue; + case SMB_ACL_GROUP: + break; ++ case SMB_ACL_MASK: ++ if (racl->mask == NO_ENTRY) ++ racl->mask = access; ++ else ++ rprintf(FINFO, "unpack_smb_acl: warning: duplicate MASK entry ignored\n"); ++ continue; ++ case SMB_ACL_OTHER: ++ if (racl->other == NO_ENTRY) ++ racl->other = access; ++ else ++ rprintf(FINFO, "unpack_smb_acl: warning: duplicate OTHER entry ignored\n"); ++ continue; + default: ++ rprintf(FINFO, "unpack_smb_acl: warning: entry with unrecognized tag type ignored\n"); + continue; + } + if (!(qualifier = sys_acl_get_qualifier(entry))) { @@ -196,1006 +355,870 @@ ACLs to a non-ACL-supporting disk should complain. + rc = EINVAL; + break; + } -+ race->id = *((id_t *)qualifier); -+ sys_acl_free_qualifier(qualifier, race->tag_type); ++ if (tag_type != prior_list_type) { ++ if (prior_list_type) ++ save_idas(&temp_ida_list, racl, prior_list_type); ++ prior_list_type = tag_type; ++ } ++ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10); ++ ida->id = *((id_t *)qualifier); ++ ida->access = access; ++ sys_acl_free_qualifier(qualifier, tag_type); + } + if (rc) { -+ rprintf(FERROR, "unpack_smb_acl: %s(): %s\n", -+ errfun, strerror(errno)); ++ rsyserr(FERROR, errno, "unpack_smb_acl: %s()", errfun); + rsync_acl_free(racl); + return False; + } -+ sort_rsync_acl(racl); -+ return True; -+} ++ if (prior_list_type) ++ save_idas(&temp_ida_list, racl, prior_list_type); + -+static BOOL rsync_acls_equal(const rsync_acl *racl1, const rsync_acl *racl2) -+{ -+ rsync_ace *race1, *race2; -+ size_t count = racl1->count; -+ if (count != racl2->count) -+ return False; -+ race1 = racl1->races; -+ race2 = racl2->races; -+ for (; count--; race1++, race2++) { -+ if (race1->tag_type != race2->tag_type -+ || race1->access != race2->access -+ || ((race1->tag_type == SMB_ACL_USER -+ || race1->tag_type == SMB_ACL_GROUP) -+ && race1->id != race2->id)) -+ return False; ++ sort_ida_entries(&racl->users); ++ sort_ida_entries(&racl->groups); ++ ++#ifdef ACLS_NEED_MASK ++ if (!racl->users.count && !racl->groups.count && racl->mask != NO_ENTRY) { ++ /* Throw away a superfluous mask, but mask off the ++ * group perms with it first. */ ++ racl->group_obj &= racl->mask; ++ racl->mask = NO_ENTRY; + } ++#endif ++ + return True; +} + -+typedef struct { -+ size_t count; -+ size_t malloced; -+ rsync_acl *racls; -+} rsync_acl_list; ++/* Synactic sugar for system calls */ + -+static rsync_acl_list _rsync_acl_lists[] = { -+ { 0, 0, NULL }, /* SMB_ACL_TYPE_ACCESS */ -+ { 0, 0, NULL } /* SMB_ACL_TYPE_DEFAULT */ -+}; ++#define CALL_OR_ERROR(func,args,str) \ ++ do { \ ++ if (func args) { \ ++ errfun = str; \ ++ goto error_exit; \ ++ } \ ++ } while (0) ++ ++#define COE(func,args) CALL_OR_ERROR(func,args,#func) ++#define COE2(func,args) CALL_OR_ERROR(func,args,NULL) + -+static inline rsync_acl_list *rsync_acl_lists(SMB_ACL_TYPE_T type) ++/* Store the permissions in the system ACL entry. */ ++static int store_access_in_entry(uchar access, SMB_ACL_ENTRY_T entry) +{ -+ return type == SMB_ACL_TYPE_ACCESS ? &_rsync_acl_lists[0] -+ : &_rsync_acl_lists[1]; ++ const char *errfun = NULL; ++ SMB_ACL_PERMSET_T permset; ++ ++ COE( sys_acl_get_permset,(entry, &permset) ); ++ COE( sys_acl_clear_perms,(permset) ); ++ if (access & 4) ++ COE( sys_acl_add_perm,(permset, SMB_ACL_READ) ); ++ if (access & 2) ++ COE( sys_acl_add_perm,(permset, SMB_ACL_WRITE) ); ++ if (access & 1) ++ COE( sys_acl_add_perm,(permset, SMB_ACL_EXECUTE) ); ++ COE( sys_acl_set_permset,(entry, permset) ); ++ ++ return 0; ++ ++ error_exit: ++ rsyserr(FERROR, errno, "store_access_in_entry %s()", errfun); ++ return -1; +} + -+static void expand_rsync_acl_list(rsync_acl_list *racl_list) ++/* Pack rsync ACL -> system ACL verbatim. Return whether we succeeded. */ ++static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl) +{ -+ /* first time through, 0 <= 0, so list is expanded: -+ * diabolical, rsync guys! */ -+ if (racl_list->malloced <= racl_list->count) { -+ rsync_acl *new_ptr; -+ size_t new_size; -+ if (racl_list->malloced < 1000) -+ new_size = racl_list->malloced + 1000; -+ else -+ new_size = racl_list->malloced * 2; -+ new_ptr = realloc_array(racl_list->racls, rsync_acl, new_size); -+ if (verbose >= 3) { -+ rprintf(FINFO, "expand_rsync_acl_list to %.0f bytes, did%s move\n", -+ (double) new_size * sizeof racl_list->racls[0], -+ racl_list->racls ? "" : " not"); -+ } ++#ifdef ACLS_NEED_MASK ++ uchar mask_bits; ++#endif ++ size_t count; ++ id_access *ida; ++ const char *errfun = NULL; ++ SMB_ACL_ENTRY_T entry; + -+ racl_list->racls = new_ptr; -+ racl_list->malloced = new_size; ++ if (!(*smb_acl = sys_acl_init(calc_sacl_entries(racl)))) { ++ rsyserr(FERROR, errno, "pack_smb_acl: sys_acl_init()"); ++ return False; ++ } + -+ if (!racl_list->racls) -+ out_of_memory("expand_rsync_acl_list"); ++ COE( sys_acl_create_entry,(smb_acl, &entry) ); ++ COE( sys_acl_set_tag_type,(entry, SMB_ACL_USER_OBJ) ); ++ COE2( store_access_in_entry,(racl->user_obj & 7, entry) ); ++ ++ for (ida = racl->users.idas, count = racl->users.count; count--; ida++) { ++ COE( sys_acl_create_entry,(smb_acl, &entry) ); ++ COE( sys_acl_set_tag_type,(entry, SMB_ACL_USER) ); ++ COE( sys_acl_set_qualifier,(entry, (void*)&ida->id) ); ++ COE2( store_access_in_entry,(ida->access, entry) ); + } -+} + -+#if 0 -+static void free_rsync_acl_list(rsync_acl_list *racl_list) -+{ -+ /* run this in reverse, so references are freed before referents, */ -+ /* although not currently necessary */ -+ while (racl_list->count--) { -+ rsync_acl *racl = &racl_list->racls[racl_list->count]; -+ if (racl) -+ rsync_acl_free(racl); ++ COE( sys_acl_create_entry,(smb_acl, &entry) ); ++ COE( sys_acl_set_tag_type,(entry, SMB_ACL_GROUP_OBJ) ); ++ COE2( store_access_in_entry,(racl->group_obj & 7, entry) ); ++ ++ for (ida = racl->groups.idas, count = racl->groups.count; count--; ida++) { ++ COE( sys_acl_create_entry,(smb_acl, &entry) ); ++ COE( sys_acl_set_tag_type,(entry, SMB_ACL_GROUP) ); ++ COE( sys_acl_set_qualifier,(entry, (void*)&ida->id) ); ++ COE2( store_access_in_entry,(ida->access, entry) ); + } -+ free(racl_list->racls); -+ racl_list->racls = NULL; -+ racl_list->malloced = 0; -+} ++ ++#ifdef ACLS_NEED_MASK ++ mask_bits = racl->mask == NO_ENTRY ? racl->group_obj & 7 : racl->mask; ++ COE( sys_acl_create_entry,(smb_acl, &entry) ); ++ COE( sys_acl_set_tag_type,(entry, SMB_ACL_MASK) ); ++ COE2( store_access_in_entry,(mask_bits, entry) ); ++#else ++ if (racl->mask != NO_ENTRY) { ++ COE( sys_acl_create_entry,(smb_acl, &entry) ); ++ COE( sys_acl_set_tag_type,(entry, SMB_ACL_MASK) ); ++ COE2( store_access_in_entry,(racl->mask, entry) ); ++ } ++#endif ++ ++ COE( sys_acl_create_entry,(smb_acl, &entry) ); ++ COE( sys_acl_set_tag_type,(entry, SMB_ACL_OTHER) ); ++ COE2( store_access_in_entry,(racl->other & 7, entry) ); ++ ++#ifdef DEBUG ++ if (sys_acl_valid(*smb_acl) < 0) ++ rprintf(FERROR, "pack_smb_acl: warning: system says the ACL I packed is invalid\n"); +#endif + ++ return True; ++ ++ error_exit: ++ if (errfun) { ++ rsyserr(FERROR, errno, "pack_smb_acl %s()", errfun); ++ } ++ sys_acl_free_acl(*smb_acl); ++ return False; ++} ++ +static int find_matching_rsync_acl(SMB_ACL_TYPE_T type, -+ const rsync_acl_list *racl_list, ++ const item_list *racl_list, + const rsync_acl *racl) +{ + static int access_match = -1, default_match = -1; -+ int *match = (type == SMB_ACL_TYPE_ACCESS) ? -+ &access_match : &default_match; ++ int *match = type == SMB_ACL_TYPE_ACCESS ? &access_match : &default_match; + size_t count = racl_list->count; -+ /* if this is the first time through or we didn't match the last ++ ++ /* If this is the first time through or we didn't match the last + * time, then start at the end of the list, which should be the -+ * best place to start hunting */ ++ * best place to start hunting. */ + if (*match == -1) + *match = racl_list->count - 1; + while (count--) { -+ if (rsync_acls_equal(&racl_list->racls[*match], racl)) ++ rsync_acl *base = racl_list->items; ++ if (rsync_acl_equal(base + *match, racl)) + return *match; + if (!(*match)--) + *match = racl_list->count - 1; + } ++ + *match = -1; + return *match; +} + -+/* the general strategy with the tag_type <-> character mapping is that -+ * lowercase implies that no qualifier follows, where uppercase does. -+ * same sorta thing for the acl type (access or default) itself, but -+ * lowercase in this instance means there's no ACL following, so the -+ * ACL is a repeat, so the receiver should reuse the last of the same -+ * type ACL */ -+ -+static void send_rsync_acl(int f, const rsync_acl *racl) ++/* Return the Access Control List for the given filename. */ ++int get_acl(const char *fname, statx *sxp) +{ -+ rsync_ace *race; -+ size_t count = racl->count; -+ write_int(f, count); -+ for (race = racl->races; count--; race++) { -+ char ch; -+ switch (race->tag_type) { -+ case SMB_ACL_USER_OBJ: -+ ch = 'u'; -+ break; -+ case SMB_ACL_USER: -+ ch = 'U'; -+ break; -+ case SMB_ACL_GROUP_OBJ: -+ ch = 'g'; -+ break; -+ case SMB_ACL_GROUP: -+ ch = 'G'; -+ break; -+ case SMB_ACL_OTHER: -+ ch = 'o'; -+ break; -+ case SMB_ACL_MASK: -+ ch = 'm'; -+ break; -+ default: -+ rprintf(FERROR, -+ "send_rsync_acl: unknown tag_type (%0x) on ACE; disregarding\n", -+ race->tag_type); -+ continue; -+ } -+ write_byte(f, ch); -+ write_byte(f, race->access); -+ if (isupper((int)ch)) { -+ write_int(f, race->id); -+ /* FIXME: sorta wasteful. we should maybe buffer as -+ * many ids as max(ACL_USER + ACL_GROUP) objects to -+ * keep from making so many calls */ -+ if (ch == 'U') -+ add_uid(race->id); -+ else -+ add_gid(race->id); -+ } -+ } -+} -+ -+static rsync_acl _curr_rsync_acls[2]; -+ ++ SMB_ACL_TYPE_T type; + -+static const char *str_acl_type(SMB_ACL_TYPE_T type) -+{ -+ return type == SMB_ACL_TYPE_ACCESS ? "SMB_ACL_TYPE_ACCESS" : -+ type == SMB_ACL_TYPE_DEFAULT ? "SMB_ACL_TYPE_DEFAULT" : -+ "unknown SMB_ACL_TYPE_T"; -+} ++ if (S_ISLNK(sxp->st.st_mode)) ++ return 0; + -+/* generate the ACL(s) for this flist entry; -+ * ACL(s) are either sent or cleaned-up by send_acl() below */ ++ type = SMB_ACL_TYPE_ACCESS; ++ do { ++ SMB_ACL_T sacl = sys_acl_get_file(fname, type); ++ rsync_acl *racl = new(rsync_acl); + -+BOOL make_acl(const struct file_struct *file, const char *fname) -+{ -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ rsync_acl *curr_racl; -+ if (!preserve_acls || S_ISLNK(file->mode)) -+ return True; -+ for (type = &types[0], curr_racl = &_curr_rsync_acls[0]; -+ type < &types[0] + sizeof types / sizeof types[0] -+ && (*type == SMB_ACL_TYPE_ACCESS || S_ISDIR(file->mode)); -+ type++, curr_racl++) { -+ SMB_ACL_T sacl; -+ BOOL ok; -+ *curr_racl = rsync_acl_initializer; -+ if (!(sacl = sys_acl_get_file(fname, *type))) { -+ rprintf(FERROR, "send_acl: sys_acl_get_file(%s, %s): %s\n", -+ fname, str_acl_type(*type), strerror(errno)); -+ return False; -+ } -+ ok = unpack_smb_acl(curr_racl, sacl); -+ sys_acl_free_acl(sacl); -+ if (!ok) -+ return False; -+ } -+ return True; -+} ++ if (!racl) ++ out_of_memory("get_acl"); ++ if (type == SMB_ACL_TYPE_ACCESS) ++ sxp->acc_acl = racl; ++ else ++ sxp->def_acl = racl; + -+/* send the make_acl()-generated ACLs for this flist entry, -+ * or clean up after an flist entry that's not being sent (f == -1) */ ++ if (sacl) { ++ BOOL ok = unpack_smb_acl(racl, sacl); + -+void send_acl(const struct file_struct *file, int f) -+{ -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ rsync_acl *curr_racl; -+ if (!preserve_acls || S_ISLNK(file->mode)) -+ return; -+ for (type = &types[0], curr_racl = &_curr_rsync_acls[0]; -+ type < &types[0] + sizeof types / sizeof types[0] -+ && (*type == SMB_ACL_TYPE_ACCESS || S_ISDIR(file->mode)); -+ type++, curr_racl++) { -+ int index; -+ rsync_acl_list *racl_list = rsync_acl_lists(*type); -+ if (f == -1) { -+ rsync_acl_free(curr_racl); -+ continue; -+ } -+ if ((index = find_matching_rsync_acl(*type, racl_list, curr_racl)) -+ != -1) { -+ write_byte(f, *type == SMB_ACL_TYPE_ACCESS ? 'a' : 'd'); -+ write_int(f, index); -+ rsync_acl_free(curr_racl); ++ sys_acl_free_acl(sacl); ++ if (!ok) { ++ free_acl(sxp); ++ return -1; ++ } ++ } else if (errno == ENOTSUP) { ++ /* ACLs are not supported, so pretend we have a basic ACL. */ ++ *racl = empty_rsync_acl; ++ if (type == SMB_ACL_TYPE_ACCESS) ++ rsync_acl_fake_perms(racl, sxp->st.st_mode); + } else { -+ write_byte(f, *type == SMB_ACL_TYPE_ACCESS ? 'A' : 'D'); -+ send_rsync_acl(f, curr_racl); -+ expand_rsync_acl_list(racl_list); -+ racl_list->racls[racl_list->count++] = *curr_racl; ++ rsyserr(FERROR, errno, "get_acl: sys_acl_get_file(%s, %s)", ++ fname, str_acl_type(type)); ++ free_acl(sxp); ++ return -1; + } -+ } -+} -+ -+/* the below stuff is only used by the receiver */ -+ -+/* structure to hold index to rsync_acl_list member corresponding to -+ * flist->files[i] */ ++ } while (BUMP_TYPE(type) && S_ISDIR(sxp->st.st_mode)); + -+typedef struct { -+ const struct file_struct *file; -+ int aclidx; -+} file_acl_index; -+ -+typedef struct { -+ size_t count; -+ size_t malloced; -+ file_acl_index *fileaclidxs; -+} file_acl_index_list; ++ return 0; ++} + -+static file_acl_index_list _file_acl_index_lists[] = { -+ {0, 0, NULL },/* SMB_ACL_TYPE_ACCESS */ -+ {0, 0, NULL } /* SMB_ACL_TYPE_DEFAULT */ -+}; ++/* === Send functions === */ + -+static inline file_acl_index_list *file_acl_index_lists(SMB_ACL_TYPE_T type) -+{ -+ return type == SMB_ACL_TYPE_ACCESS ? -+ &_file_acl_index_lists[0] : &_file_acl_index_lists[1]; ++/* The general strategy with the tag_type <-> character mapping is that ++ * lowercase implies that no qualifier follows, where uppercase does. ++ * A similar idiom for the ACL type (access or default) itself, but ++ * lowercase in this instance means there's no ACL following, so the ++ * ACL is a repeat, so the receiver should reuse the last of the same ++ * type ACL. */ ++ ++/* Send the ida list over the file descriptor. */ ++static void send_ida_entries(int f, const ida_entries *idal, char tag_char) ++{ ++ id_access *ida; ++ size_t count = idal->count; ++ for (ida = idal->idas; count--; ida++) { ++ write_byte(f, tag_char); ++ write_byte(f, ida->access); ++ write_int(f, ida->id); ++ /* FIXME: sorta wasteful: we should maybe buffer as ++ * many ids as max(ACL_USER + ACL_GROUP) objects to ++ * keep from making so many calls. */ ++ if (tag_char == 'U') ++ add_uid(ida->id); ++ else ++ add_gid(ida->id); ++ } +} + -+static void expand_file_acl_index_list(file_acl_index_list *fileaclidx_list) ++/* Send an rsync ACL over the file descriptor. */ ++static void send_rsync_acl(int f, const rsync_acl *racl) +{ -+ /* first time through, 0 <= 0, so list is expanded: -+ * diabolical, rsync guys! */ -+ if (fileaclidx_list->malloced <= fileaclidx_list->count) { -+ file_acl_index *new_ptr; -+ size_t new_size; -+ if (fileaclidx_list->malloced < 1000) -+ new_size = fileaclidx_list->malloced + 1000; -+ else -+ new_size = fileaclidx_list->malloced * 2; -+ new_ptr = realloc_array(fileaclidx_list->fileaclidxs, file_acl_index, new_size); -+ if (verbose >= 3) { -+ rprintf(FINFO, "expand_file_acl_index_list to %.0f bytes, did%s move\n", -+ (double) new_size * sizeof fileaclidx_list->fileaclidxs[0], -+ fileaclidx_list->fileaclidxs ? "" : " not"); -+ } -+ -+ fileaclidx_list->fileaclidxs = new_ptr; -+ fileaclidx_list->malloced = new_size; -+ -+ if (!fileaclidx_list->fileaclidxs) -+ out_of_memory("expand_file_acl_index_list"); ++ size_t count = count_racl_entries(racl); ++ write_int(f, count); ++ if (racl->user_obj != NO_ENTRY) { ++ write_byte(f, 'u'); ++ write_byte(f, racl->user_obj); ++ } ++ send_ida_entries(f, &racl->users, 'U'); ++ if (racl->group_obj != NO_ENTRY) { ++ write_byte(f, 'g'); ++ write_byte(f, racl->group_obj); ++ } ++ send_ida_entries(f, &racl->groups, 'G'); ++ if (racl->mask != NO_ENTRY) { ++ write_byte(f, 'm'); ++ write_byte(f, racl->mask); ++ } ++ if (racl->other != NO_ENTRY) { ++ write_byte(f, 'o'); ++ write_byte(f, racl->other); + } +} + -+#if 0 -+static void free_file_acl_index_list(file_acl_index_list *fileaclidx_list) ++/* Send the ACL from the statx structure down the indicated file descriptor. ++ * This also frees the ACL data. */ ++void send_acl(statx *sxp, int f) +{ -+ free(fileaclidx_list->fileaclidxs); -+ fileaclidx_list->fileaclidxs = NULL; -+ fileaclidx_list->malloced = 0; -+} -+#endif ++ SMB_ACL_TYPE_T type; ++ rsync_acl *racl, *new_racl; ++ item_list *racl_list; ++ int ndx; + -+/* lists to hold the SMB_ACL_Ts corresponding to the rsync_acl_list entries */ ++ if (S_ISLNK(sxp->st.st_mode)) ++ return; + -+typedef struct { -+ size_t count; -+ size_t malloced; -+ SMB_ACL_T *sacls; -+} smb_acl_list; ++ type = SMB_ACL_TYPE_ACCESS; ++ racl = sxp->acc_acl; ++ racl_list = &access_acl_list; ++ do { ++ if (!racl) { ++ racl = new(rsync_acl); ++ if (!racl) ++ out_of_memory("send_acl"); ++ *racl = empty_rsync_acl; ++ if (type == SMB_ACL_TYPE_ACCESS) { ++ rsync_acl_fake_perms(racl, sxp->st.st_mode); ++ sxp->acc_acl = racl; ++ } else ++ sxp->def_acl = racl; ++ } + -+static smb_acl_list _smb_acl_lists[] = { -+ { 0, 0, NULL }, /* SMB_ACL_TYPE_ACCESS */ -+ { 0, 0, NULL } /* SMB_ACL_TYPE_DEFAULT */ -+}; ++ /* Avoid sending values that can be inferred from other data, ++ * but only when preserve_acls == 1 (it is 2 when we must be ++ * backward compatible with older acls.diff versions). */ ++ if (type == SMB_ACL_TYPE_ACCESS && preserve_acls == 1) ++ rsync_acl_strip_perms(racl); ++ if ((ndx = find_matching_rsync_acl(type, racl_list, racl)) != -1) { ++ write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'a' : 'd'); ++ write_int(f, ndx); ++ } else { ++ new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000); ++ write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'A' : 'D'); ++ send_rsync_acl(f, racl); ++ *new_racl = *racl; ++ *racl = empty_rsync_acl; ++ } ++ racl = sxp->def_acl; ++ racl_list = &default_acl_list; ++ } while (BUMP_TYPE(type) && S_ISDIR(sxp->st.st_mode)); + -+static inline smb_acl_list *smb_acl_lists(SMB_ACL_TYPE_T type) -+{ -+ return type == SMB_ACL_TYPE_ACCESS ? &_smb_acl_lists[0] : -+ &_smb_acl_lists[1]; ++ free_acl(sxp); +} + -+static void expand_smb_acl_list(smb_acl_list *sacl_list) -+{ -+ /* first time through, 0 <= 0, so list is expanded: -+ * diabolical, rsync guys! */ -+ if (sacl_list->malloced <= sacl_list->count) { -+ SMB_ACL_T *new_ptr; -+ size_t new_size; -+ if (sacl_list->malloced < 1000) -+ new_size = sacl_list->malloced + 1000; -+ else -+ new_size = sacl_list->malloced * 2; -+ new_ptr = realloc_array(sacl_list->sacls, SMB_ACL_T, new_size); -+ if (verbose >= 3) { -+ rprintf(FINFO, "expand_smb_acl_list to %.0f bytes, did%s move\n", -+ (double) new_size * sizeof sacl_list->sacls[0], -+ sacl_list->sacls ? "" : " not"); -+ } ++/* === Receive functions === */ + -+ sacl_list->sacls = new_ptr; -+ sacl_list->malloced = new_size; ++static void receive_rsync_acl(rsync_acl *racl, int f, SMB_ACL_TYPE_T type) ++{ ++ static item_list temp_ida_list = EMPTY_ITEM_LIST; ++ SMB_ACL_TAG_T tag_type = 0, prior_list_type = 0; ++ uchar computed_mask_bits = 0; ++ id_access *ida; ++ size_t count; + -+ if (!sacl_list->sacls) -+ out_of_memory("expand_smb_acl_list"); -+ } -+} ++ *racl = empty_rsync_acl; + -+#if 0 -+static void free_smb_acl_list(SMB_ACL_TYPE_T type) -+{ -+ smb_acl_list *sacl_list = smb_acl_lists(type); -+ SMB_ACL_T *sacl = sacl_list->sacls; -+ while (sacl_list->count--) { -+ if (*sacl) -+ sys_acl_free_acl(*sacl++); -+ } -+ free(sacl_list->sacls); -+ sacl_list->sacls = NULL; -+ sacl_list->malloced = 0; -+} -+#endif ++ if (!(count = read_int(f))) ++ return; + -+/* build an SMB_ACL_T corresponding to an rsync_acl */ -+static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl) -+{ -+ size_t count = racl->count; -+ rsync_ace *race = racl->races; -+ const char *errfun = NULL; -+ *smb_acl = sys_acl_init(count); -+ if (!*smb_acl) { -+ rprintf(FERROR, "pack_smb_acl: sys_acl_int(): %s\n", -+ strerror(errno)); -+ return False; -+ } -+ for (; count--; race++) { -+ SMB_ACL_ENTRY_T entry; -+ SMB_ACL_PERMSET_T permset; -+ if (sys_acl_create_entry(smb_acl, &entry)) { -+ errfun = "sys_acl_create)"; -+ break; -+ } -+ if (sys_acl_set_tag_type(entry, race->tag_type)) { -+ errfun = "sys_acl_set_tag"; -+ break; -+ } -+ if (race->tag_type == SMB_ACL_USER || -+ race->tag_type == SMB_ACL_GROUP) -+ if (sys_acl_set_qualifier(entry, (void*)&race->id)) { -+ errfun = "sys_acl_set_qualfier"; -+ break; -+ } -+ if (sys_acl_get_permset(entry, &permset)) { -+ errfun = "sys_acl_get_permset"; -+ break; -+ } -+ if (sys_acl_clear_perms(permset)) { -+ errfun = "sys_acl_clear_perms"; -+ break; -+ } -+ if (race->access & 4) -+ if (sys_acl_add_perm(permset, SMB_ACL_READ)) { -+ errfun = "sys_acl_add_perm"; -+ break; -+ } -+ if (race->access & 2) -+ if (sys_acl_add_perm(permset, SMB_ACL_WRITE)) { -+ errfun = "sys_acl_add_perm"; -+ break; -+ } -+ if (race->access & 1) -+ if (sys_acl_add_perm(permset, SMB_ACL_EXECUTE)) { -+ errfun = "sys_acl_add_perm"; -+ break; -+ } -+ if (sys_acl_set_permset(entry, permset)) { -+ errfun = "sys_acl_set_permset"; -+ break; -+ } -+ } -+ if (errfun) { -+ sys_acl_free_acl(*smb_acl); -+ rprintf(FERROR, "pack_smb_acl %s(): %s\n", errfun, -+ strerror(errno)); -+ return False; -+ } -+ return True; -+} -+ -+static void receive_rsync_acl(rsync_acl *racl, int f) -+{ -+#if ACLS_NEED_MASK -+ uchar required_mask_perm = 0; -+#endif -+ BOOL saw_mask = False; -+ BOOL saw_user_obj = False, saw_group_obj = False, -+ saw_other = False; -+ size_t count = read_int(f); -+ rsync_ace *race; -+ if (!count) -+ return; + while (count--) { -+ uchar tag = read_byte(f); -+ expand_rsync_acl(racl); -+ race = &racl->races[racl->count++]; ++ char tag = read_byte(f); ++ uchar access = read_byte(f); ++ if (access & ~ (4 | 2 | 1)) { ++ rprintf(FERROR, "receive_rsync_acl: bogus permset %o\n", ++ access); ++ exit_cleanup(RERR_STREAMIO); ++ } + switch (tag) { + case 'u': -+ race->tag_type = SMB_ACL_USER_OBJ; -+ saw_user_obj = True; -+ break; ++ if (racl->user_obj != NO_ENTRY) { ++ rprintf(FERROR, "receive_rsync_acl: error: duplicate USER_OBJ entry\n"); ++ exit_cleanup(RERR_STREAMIO); ++ } ++ racl->user_obj = access; ++ continue; + case 'U': -+ race->tag_type = SMB_ACL_USER; ++ tag_type = SMB_ACL_USER; + break; + case 'g': -+ race->tag_type = SMB_ACL_GROUP_OBJ; -+ saw_group_obj = True; -+ break; ++ if (racl->group_obj != NO_ENTRY) { ++ rprintf(FERROR, "receive_rsync_acl: error: duplicate GROUP_OBJ entry\n"); ++ exit_cleanup(RERR_STREAMIO); ++ } ++ racl->group_obj = access; ++ continue; + case 'G': -+ race->tag_type = SMB_ACL_GROUP; -+ break; -+ case 'o': -+ race->tag_type = SMB_ACL_OTHER; -+ saw_other = True; ++ tag_type = SMB_ACL_GROUP; + break; + case 'm': -+ race->tag_type = SMB_ACL_MASK; -+ saw_mask = True; -+ break; ++ if (racl->mask != NO_ENTRY) { ++ rprintf(FERROR, "receive_rsync_acl: error: duplicate MASK entry\n"); ++ exit_cleanup(RERR_STREAMIO); ++ } ++ racl->mask = access; ++ continue; ++ case 'o': ++ if (racl->other != NO_ENTRY) { ++ rprintf(FERROR, "receive_rsync_acl: error: duplicate OTHER entry\n"); ++ exit_cleanup(RERR_STREAMIO); ++ } ++ racl->other = access; ++ continue; + default: + rprintf(FERROR, "receive_rsync_acl: unknown tag %c\n", + tag); + exit_cleanup(RERR_STREAMIO); + } -+ race->access = read_byte(f); -+ if (race->access & ~ (4 | 2 | 1)) { -+ rprintf(FERROR, "receive_rsync_acl: bogus permset %o\n", -+ race->access); -+ exit_cleanup(RERR_STREAMIO); -+ } -+ if (race->tag_type == SMB_ACL_USER || -+ race->tag_type == SMB_ACL_GROUP) { -+ race->id = read_int(f); -+#if ACLS_NEED_MASK -+ required_mask_perm |= race->access; -+#endif ++ if (tag_type != prior_list_type) { ++ if (prior_list_type) ++ save_idas(&temp_ida_list, racl, prior_list_type); ++ prior_list_type = tag_type; + } -+#if ACLS_NEED_MASK -+ else if (race->tag_type == SMB_ACL_GROUP_OBJ) -+ required_mask_perm |= race->access; -+#endif -+ -+ } -+ if (!saw_user_obj) { -+ expand_rsync_acl(racl); -+ race = &racl->races[racl->count++]; -+ race->tag_type = SMB_ACL_USER_OBJ; -+ race->access = 7; -+ } -+ if (!saw_group_obj) { -+ expand_rsync_acl(racl); -+ race = &racl->races[racl->count++]; -+ race->tag_type = SMB_ACL_GROUP_OBJ; -+ race->access = 0; ++ ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10); ++ ida->access = access; ++ ida->id = read_int(f); ++ computed_mask_bits |= access; + } -+ if (!saw_other) { -+ expand_rsync_acl(racl); -+ race = &racl->races[racl->count++]; -+ race->tag_type = SMB_ACL_OTHER; -+ race->access = 0; -+ } -+#if ACLS_NEED_MASK -+ if (!saw_mask) { -+ expand_rsync_acl(racl); -+ race = &racl->races[racl->count++]; -+ race->tag_type = SMB_ACL_MASK; -+ race->access = required_mask_perm; -+ } -+#else -+ /* If we, a system without ACLS_NEED_MASK, received data from a -+ * system that has masks, throw away the extraneous CLASS_OBJs. */ -+ if (saw_mask && racl->count == 4) { -+ rsync_ace *group_obj_race = NULL, *mask_race = NULL; -+ rsync_ace *p; -+ size_t i; -+ for (i = 0, p = racl->races; i < racl->count; i++, p++) { -+ if (p->tag_type == SMB_ACL_MASK) -+ mask_race = p; -+ else if (p->tag_type == SMB_ACL_GROUP_OBJ) -+ group_obj_race = p; -+ } -+ if (mask_race == NULL || group_obj_race == NULL) { -+ rprintf(FERROR, "receive_rsync_acl: have four ACES " -+ "and one's ACL_MASK but missing " -+ "either it or ACL_GROUP_OBJ, " -+ "when pruning ACL\n"); -+ } else { -+ /* mask off group perms with it first */ -+ group_obj_race->access &= mask_race->access; -+ /* dump mask_race; re-slot any followers-on */ -+ racl->count--; -+ if (mask_race != &racl->races[racl->count]) { -+ *mask_race = racl->races[racl->count]; -+ saw_user_obj = False; /* force re-sort */ -+ } ++ if (prior_list_type) ++ save_idas(&temp_ida_list, racl, prior_list_type); ++ ++ if (type == SMB_ACL_TYPE_DEFAULT) { ++ /* Ensure that these are never unset. */ ++ if (racl->user_obj == NO_ENTRY) ++ racl->user_obj = 7; ++ if (racl->group_obj == NO_ENTRY) ++ racl->group_obj = 0; ++ if (racl->other == NO_ENTRY) ++ racl->other = 0; ++ } ++ ++ if (!racl->users.count && !racl->groups.count) { ++ /* If we received a superfluous mask, throw it away. */ ++ if (racl->mask != NO_ENTRY) { ++ /* Mask off the group perms with it first. */ ++ racl->group_obj &= racl->mask | NO_ENTRY; ++ racl->mask = NO_ENTRY; + } -+ } -+#endif -+#if ACLS_NEED_MASK -+ if (!(saw_user_obj && saw_group_obj && saw_other && saw_mask)) -+#else -+ if (!(saw_user_obj && saw_group_obj && saw_other)) -+#endif -+ sort_rsync_acl(racl); ++ } else if (racl->mask == NO_ENTRY) /* Must be non-empty with lists. */ ++ racl->mask = computed_mask_bits | (racl->group_obj & 7); +} + -+/* receive and build the rsync_acl_lists */ -+ ++/* Receive the ACL info the sender has included for this file-list entry. */ +void receive_acl(struct file_struct *file, int f) +{ -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ char *fname; -+ if (!preserve_acls || S_ISLNK(file->mode)) ++ SMB_ACL_TYPE_T type; ++ item_list *racl_list; ++ char *ndx_ptr; ++ ++ if (S_ISLNK(file->mode)) + return; -+ fname = f_name(file); -+ for (type = &types[0]; -+ type < &types[0] + sizeof types / sizeof types[0] -+ && (*type == SMB_ACL_TYPE_ACCESS || S_ISDIR(file->mode)); -+ type++) { -+ file_acl_index_list *fileaclidx_list = -+ file_acl_index_lists(*type); -+ uchar tag; -+ expand_file_acl_index_list(fileaclidx_list); -+ -+ tag = read_byte(f); ++ ++ type = SMB_ACL_TYPE_ACCESS; ++ racl_list = &access_acl_list; ++ ndx_ptr = (char*)file + file_struct_len; ++ do { ++ char tag = read_byte(f); ++ int ndx; ++ + if (tag == 'A' || tag == 'a') { -+ if (*type != SMB_ACL_TYPE_ACCESS) { ++ if (type != SMB_ACL_TYPE_ACCESS) { + rprintf(FERROR, "receive_acl %s: duplicate access ACL\n", -+ fname); ++ f_name(file, NULL)); + exit_cleanup(RERR_STREAMIO); + } + } else if (tag == 'D' || tag == 'd') { -+ if (*type == SMB_ACL_TYPE_ACCESS) { ++ if (type == SMB_ACL_TYPE_ACCESS) { + rprintf(FERROR, "receive_acl %s: expecting access ACL; got default\n", -+ fname); ++ f_name(file, NULL)); + exit_cleanup(RERR_STREAMIO); + } + } else { + rprintf(FERROR, "receive_acl %s: unknown ACL type tag: %c\n", -+ fname, tag); ++ f_name(file, NULL), tag); + exit_cleanup(RERR_STREAMIO); + } + if (tag == 'A' || tag == 'D') { -+ rsync_acl racl = rsync_acl_initializer; -+ rsync_acl_list *racl_list = rsync_acl_lists(*type); -+ smb_acl_list *sacl_list = smb_acl_lists(*type); -+ fileaclidx_list->fileaclidxs[fileaclidx_list->count]. -+ aclidx = racl_list->count; -+ fileaclidx_list->fileaclidxs[fileaclidx_list->count++]. -+ file = file; -+ receive_rsync_acl(&racl, f); -+ expand_rsync_acl_list(racl_list); -+ racl_list->racls[racl_list->count++] = racl; -+ expand_smb_acl_list(sacl_list); -+ sacl_list->sacls[sacl_list->count++] = NULL; ++ acl_duo *duo_item; ++ ndx = racl_list->count; ++ duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000); ++ receive_rsync_acl(&duo_item->racl, f, type); ++ duo_item->sacl = NULL; + } else { -+ int index = read_int(f); -+ rsync_acl_list *racl_list = rsync_acl_lists(*type); -+ if ((size_t) index >= racl_list->count) { ++ ndx = read_int(f); ++ if (ndx < 0 || (size_t)ndx >= racl_list->count) { + rprintf(FERROR, "receive_acl %s: %s ACL index %d out of range\n", -+ fname, -+ str_acl_type(*type), -+ index); ++ f_name(file, NULL), str_acl_type(type), ndx); + exit_cleanup(RERR_STREAMIO); + } -+ fileaclidx_list->fileaclidxs[fileaclidx_list->count]. -+ aclidx = index; -+ fileaclidx_list->fileaclidxs[fileaclidx_list->count++]. -+ file = file; + } -+ } ++ SIVAL(ndx_ptr, 0, ndx); ++ racl_list = &default_acl_list; ++ ndx_ptr += 4; ++ } while (BUMP_TYPE(type) && S_ISDIR(file->mode)); +} + -+static int file_acl_index_list_sorter(const void *f1, const void *f2) ++/* Turn the ACL data in statx into cached ACL data, setting the index ++ * values in the file struct. */ ++void cache_acl(struct file_struct *file, statx *sxp) +{ -+ const file_acl_index *fileaclidx1 = (const file_acl_index *)f1; -+ const file_acl_index *fileaclidx2 = (const file_acl_index *)f2; -+ return fileaclidx1->file == fileaclidx2->file ? 0 : -+ fileaclidx1->file < fileaclidx2->file ? -1 : 1; -+} ++ SMB_ACL_TYPE_T type; ++ rsync_acl *racl; ++ item_list *racl_list; ++ char *ndx_ptr; ++ int ndx; + -+void sort_file_acl_index_lists() -+{ -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ if (!preserve_acls) ++ if (S_ISLNK(file->mode)) + return; -+ for (type = &types[0]; -+ type < &types[0] + sizeof types / sizeof types[0]; -+ type++) -+ { -+ file_acl_index_list *fileaclidx_list = -+ file_acl_index_lists(*type); -+ if (!fileaclidx_list->count) -+ continue; -+ qsort(fileaclidx_list->fileaclidxs, fileaclidx_list->count, -+ sizeof fileaclidx_list->fileaclidxs[0], -+ &file_acl_index_list_sorter); -+ } -+} + -+static int find_file_acl_index(const file_acl_index_list *fileaclidx_list, -+ const struct file_struct *file) { -+ int low = 0, high = fileaclidx_list->count; -+ const struct file_struct *file_mid; -+ if (!high--) -+ return -1; ++ type = SMB_ACL_TYPE_ACCESS; ++ racl = sxp->acc_acl; ++ racl_list = &access_acl_list; ++ ndx_ptr = (char*)file + file_struct_len; + do { -+ int mid = (high + low) / 2; -+ file_mid = fileaclidx_list->fileaclidxs[mid].file; -+ if (file_mid == file) -+ return fileaclidx_list->fileaclidxs[mid].aclidx; -+ if (file_mid > file) -+ high = mid - 1; -+ else -+ low = mid + 1; -+ } while (low < high); -+ if (low == high) { -+ file_mid = fileaclidx_list->fileaclidxs[low].file; -+ if (file_mid == file) -+ return fileaclidx_list->fileaclidxs[low].aclidx; -+ } -+ rprintf(FERROR, -+ "find_file_acl_index: can't find entry for file in list\n"); -+ exit_cleanup(RERR_STREAMIO); -+ return -1; -+} ++ if (!racl) ++ ndx = -1; ++ else if ((ndx = find_matching_rsync_acl(type, racl_list, racl)) == -1) { ++ acl_duo *new_duo; ++ ndx = racl_list->count; ++ new_duo = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000); ++ new_duo->racl = *racl; ++ new_duo->sacl = NULL; ++ *racl = empty_rsync_acl; ++ } ++ SIVAL(ndx_ptr, 0, ndx); ++ racl = sxp->def_acl; ++ racl_list = &default_acl_list; ++ ndx_ptr += 4; ++ } while (BUMP_TYPE(type) && S_ISDIR(sxp->st.st_mode)); + -+/* for duplicating ACLs on backups when using backup_dir */ ++ free_acl(sxp); ++} + -+int dup_acl(const char *orig, const char *bak, mode_t mode) ++static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode, mode_t mode) +{ -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ int ret = 0; -+ if (!preserve_acls) -+ return 0; -+ for (type = &types[0]; -+ type < &types[0] + sizeof types / sizeof types[0] -+ && (*type == SMB_ACL_TYPE_ACCESS || S_ISDIR(mode)); -+ type++) { -+ SMB_ACL_T sacl_orig, sacl_bak; -+ rsync_acl racl_orig, racl_bak; -+ if (!(sacl_orig = sys_acl_get_file(orig, *type))) { -+ rprintf(FERROR, "dup_acl: sys_acl_get_file(%s, %s): %s\n", -+ orig, str_acl_type(*type), strerror(errno)); -+ ret = -1; -+ continue; -+ } -+ if (!(sacl_bak = sys_acl_get_file(orig, *type))) { -+ rprintf(FERROR, "dup_acl: sys_acl_get_file(%s, %s): %s. ignoring\n", -+ bak, str_acl_type(*type), strerror(errno)); -+ ret = -1; -+ /* try to forge on through */ -+ } -+ if (!unpack_smb_acl(&racl_orig, sacl_orig)) { -+ ret = -1; -+ goto out_with_sacls; ++ SMB_ACL_ENTRY_T entry; ++ const char *errfun; ++ int rc; ++ ++ if (S_ISDIR(mode)) { ++ /* If the sticky bit is going on, it's not safe to allow all ++ * the new ACL to go into effect before it gets set. */ ++#ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS ++ if (mode & S_ISVTX) ++ mode &= ~0077; ++#else ++ if (mode & S_ISVTX && !(old_mode & S_ISVTX)) ++ mode &= ~0077; ++ } else { ++ /* If setuid or setgid is going off, it's not safe to allow all ++ * the new ACL to go into effect before they get cleared. */ ++ if ((old_mode & S_ISUID && !(mode & S_ISUID)) ++ || (old_mode & S_ISGID && !(mode & S_ISGID))) ++ mode &= ~0077; ++#endif ++ } ++ ++ errfun = "sys_acl_get_entry"; ++ for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry); ++ rc == 1; ++ rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) { ++ SMB_ACL_TAG_T tag_type; ++ if ((rc = sys_acl_get_tag_type(entry, &tag_type))) { ++ errfun = "sys_acl_get_tag_type"; ++ break; + } -+ if (sacl_bak) { -+ if (!unpack_smb_acl(&racl_bak, sacl_bak)) { -+ ret = -1; -+ goto out_with_one_racl; -+ } -+ if (rsync_acls_equal(&racl_orig, &racl_bak)) -+ goto out_with_all; -+ } else { -+ ; /* presume they're unequal */ ++ switch (tag_type) { ++ case SMB_ACL_USER_OBJ: ++ COE2( store_access_in_entry,((mode >> 6) & 7, entry) ); ++ break; ++ case SMB_ACL_GROUP_OBJ: ++ /* group is only empty when identical to group perms. */ ++ if (racl->group_obj != NO_ENTRY) ++ break; ++ COE2( store_access_in_entry,((mode >> 3) & 7, entry) ); ++ break; ++ case SMB_ACL_MASK: ++#ifndef ACLS_NEED_MASK ++ /* mask is only empty when we don't need it. */ ++ if (racl->mask == NO_ENTRY) ++ break; ++#endif ++ COE2( store_access_in_entry,((mode >> 3) & 7, entry) ); ++ break; ++ case SMB_ACL_OTHER: ++ COE2( store_access_in_entry,(mode & 7, entry) ); ++ break; + } -+ if (*type == SMB_ACL_TYPE_DEFAULT && !racl_orig.count) { -+ if (-1 == sys_acl_delete_def_file(bak)) { -+ rprintf(FERROR, "dup_acl: sys_acl_delete_def_file(%s): %s\n", -+ bak, strerror(errno)); -+ ret = -1; -+ } -+ } else if (-1 == sys_acl_set_file(bak, *type, sacl_bak)) { -+ rprintf(FERROR, "dup_acl: sys_acl_set_file(%s, %s): %s\n", -+ bak, str_acl_type(*type), strerror(errno)); -+ ret = -1; ++ } ++ if (rc) { ++ error_exit: ++ if (errfun) { ++ rsyserr(FERROR, errno, "change_sacl_perms: %s()", ++ errfun); + } -+ out_with_all: -+ if (sacl_bak) -+ rsync_acl_free(&racl_bak); -+ out_with_one_racl: -+ rsync_acl_free(&racl_orig); -+ out_with_sacls: -+ if (sacl_bak) -+ sys_acl_free_acl(sacl_bak); -+ /* out_with_one_sacl: */ -+ if (sacl_orig) -+ sys_acl_free_acl(sacl_orig); ++ return ~0u; + } -+ return ret; -+} + -+/* stuff for redirecting calls to set_acl() from set_perms() -+ * for keep_backup() */ -+static const struct file_struct *backup_orig_file = NULL; -+static const char null_string[] = ""; -+static const char *backup_orig_fname = null_string; -+static const char *backup_dest_fname = null_string; -+static SMB_ACL_T _backup_sacl[] = { NULL, NULL }; ++#ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS ++ /* Ensure that chmod() will be called to restore any lost setid bits. */ ++ if (old_mode & (S_ISUID | S_ISGID | S_ISVTX) ++ && (old_mode & CHMOD_BITS) == (mode & CHMOD_BITS)) ++ old_mode &= ~(S_ISUID | S_ISGID | S_ISVTX); ++#endif + -+void push_keep_backup_acl(const struct file_struct *file, -+ const char *orig, const char *dest) -+{ -+ if (preserve_acls) { -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ SMB_ACL_T *sacl; -+ backup_orig_file = file; -+ backup_orig_fname = orig; -+ backup_dest_fname = dest; -+ for (type = &types[0], sacl = &_backup_sacl[0]; -+ type < &types[0] + sizeof types / sizeof types[0]; -+ type++) { -+ if (*type == SMB_ACL_TYPE_DEFAULT && !S_ISDIR(file->mode)) -+ *sacl = NULL; -+ else { -+ if (!(*sacl = sys_acl_get_file(orig, *type))) { -+ rprintf(FERROR, "push_keep_backup_acl: sys_acl_get_file(%s, %s): %s\n", -+ orig, str_acl_type(*type), -+ strerror(errno)); -+ } -+ } -+ } -+ } ++ /* Return the mode of the file on disk, as we will set them. */ ++ return (old_mode & ~ACCESSPERMS) | (mode & ACCESSPERMS); +} + -+static int set_keep_backup_acl() ++/* Set ACL on indicated filename. ++ * ++ * This sets extended access ACL entries and default ACL. If convenient, ++ * it sets permission bits along with the access ACL and signals having ++ * done so by modifying sxp->st.st_mode. ++ * ++ * Returns 1 for unchanged, 0 for changed, -1 for failed. Call this ++ * with fname set to NULL to just check if the ACL is unchanged. */ ++int set_acl(const char *fname, const struct file_struct *file, statx *sxp) +{ -+ if (preserve_acls) { -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ SMB_ACL_T *sacl; -+ int ret = 0; -+ for (type = &types[0], sacl = &_backup_sacl[0]; -+ type < &types[0] + sizeof types / sizeof types[0]; -+ type++) { -+ if (*sacl) { -+ if (-1 == sys_acl_set_file(backup_dest_fname, -+ *type, *sacl)) -+ { -+ rprintf(FERROR, "push_keep_backup_acl: sys_acl_get_file(%s, %s): %s\n", -+ backup_dest_fname, -+ str_acl_type(*type), -+ strerror(errno)); -+ ret = -1; -+ } -+ } -+ } -+ return ret; -+ } -+ return 0; -+} ++ int unchanged = 1; ++ SMB_ACL_TYPE_T type; ++ char *ndx_ptr; + -+void cleanup_keep_backup_acl() -+{ -+ if (preserve_acls) { -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ SMB_ACL_T *sacl; -+ backup_orig_file = NULL; -+ backup_orig_fname = null_string; -+ backup_dest_fname = null_string; -+ for (type = &types[0], sacl = &_backup_sacl[0]; -+ type < &types[0] + sizeof types / sizeof types[0]; -+ type++) { -+ if (*sacl) -+ sys_acl_free_acl(*sacl); -+ *sacl = NULL; -+ } -+ } -+} ++ if (S_ISLNK(file->mode)) ++ return 1; + -+/* set ACL on rsync-ed or keep_backup-ed file */ ++ type = SMB_ACL_TYPE_ACCESS; ++ ndx_ptr = (char*)file + file_struct_len; ++ do { ++ acl_duo *duo_item; ++ BOOL eq; ++ int32 ndx = IVAL(ndx_ptr, 0); + -+int set_acl(const char *fname, const struct file_struct *file) -+{ -+ int updated = 0; -+ SMB_ACL_TYPE_T *type, -+ types[] = {SMB_ACL_TYPE_ACCESS, SMB_ACL_TYPE_DEFAULT}; -+ if (dry_run || !preserve_acls || S_ISLNK(file->mode)) -+ return 0; -+ if (file == backup_orig_file) { -+ if (!strcmp(fname, backup_dest_fname)) -+ return set_keep_backup_acl(); -+ } -+ for (type = &types[0]; -+ type < &types[0] + sizeof types / sizeof types[0] -+ && (*type == SMB_ACL_TYPE_ACCESS || S_ISDIR(file->mode)); -+ type++) { -+ SMB_ACL_T sacl_orig, *sacl_new; -+ rsync_acl racl_orig, *racl_new; -+ int aclidx = find_file_acl_index(file_acl_index_lists(*type), -+ file); -+ BOOL ok; -+ racl_new = &(rsync_acl_lists(*type)->racls[aclidx]); -+ sacl_new = &(smb_acl_lists(*type)->sacls[aclidx]); -+ sacl_orig = sys_acl_get_file(fname, *type); -+ if (!sacl_orig) { -+ rprintf(FERROR, "set_acl: sys_acl_get_file(%s, %s): %s\n", -+ fname, str_acl_type(*type), strerror(errno)); -+ updated = -1; -+ continue; -+ } -+ ok = unpack_smb_acl(&racl_orig, sacl_orig); -+ sys_acl_free_acl(sacl_orig); -+ if (!ok) { -+ updated = -1; -+ continue; -+ } -+ ok = rsync_acls_equal(&racl_orig, racl_new); -+ rsync_acl_free(&racl_orig); -+ if (ok) -+ continue; -+ if (*type == SMB_ACL_TYPE_DEFAULT && !racl_new->count) { -+ if (-1 == sys_acl_delete_def_file(fname)) { -+ rprintf(FERROR, "set_acl: sys_acl_delete_def_file(%s): %s\n", -+ fname, strerror(errno)); -+ updated = -1; ++ ndx_ptr += 4; ++ ++ if (type == SMB_ACL_TYPE_ACCESS) { ++ if (ndx < 0 || (size_t)ndx >= access_acl_list.count) + continue; -+ } ++ duo_item = access_acl_list.items; ++ duo_item += ndx; ++ eq = sxp->acc_acl ++ && rsync_acl_equal_enough(sxp->acc_acl, &duo_item->racl, file->mode); + } else { -+ if (!*sacl_new) -+ if (!pack_smb_acl(sacl_new, racl_new)) { -+ updated = -1; ++ if (ndx < 0 || (size_t)ndx >= default_acl_list.count) ++ continue; ++ duo_item = default_acl_list.items; ++ duo_item += ndx; ++ eq = sxp->def_acl ++ && rsync_acl_equal(sxp->def_acl, &duo_item->racl); ++ } ++ if (eq) ++ continue; ++ if (!dry_run && fname) { ++ if (type == SMB_ACL_TYPE_DEFAULT ++ && duo_item->racl.user_obj == NO_ENTRY) { ++ if (sys_acl_delete_def_file(fname) < 0) { ++ rsyserr(FERROR, errno, "set_acl: sys_acl_delete_def_file(%s)", ++ fname); ++ unchanged = -1; + continue; + } -+ if (-1 == sys_acl_set_file(fname, *type, *sacl_new)) { -+ rprintf(FERROR, "set_acl: sys_acl_set_file(%s, %s): %s\n", -+ fname, str_acl_type(*type), -+ strerror(errno)); -+ updated = -1; -+ continue; ++ } else { ++ mode_t cur_mode = sxp->st.st_mode; ++ if (!duo_item->sacl ++ && !pack_smb_acl(&duo_item->sacl, &duo_item->racl)) { ++ unchanged = -1; ++ continue; ++ } ++ if (type == SMB_ACL_TYPE_ACCESS) { ++ cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl, ++ cur_mode, file->mode); ++ if (cur_mode == ~0u) ++ continue; ++ } ++ if (sys_acl_set_file(fname, type, duo_item->sacl) < 0) { ++ rsyserr(FERROR, errno, "set_acl: sys_acl_set_file(%s, %s)", ++ fname, str_acl_type(type)); ++ unchanged = -1; ++ continue; ++ } ++ if (type == SMB_ACL_TYPE_ACCESS) ++ sxp->st.st_mode = cur_mode; + } + } -+ if (!updated) -+ updated = 1; -+ } -+ return updated; ++ if (unchanged == 1) ++ unchanged = 0; ++ } while (BUMP_TYPE(type) && S_ISDIR(file->mode)); ++ ++ return unchanged; +} + -+/* enumeration functions for uid mapping */ ++/* === Enumeration functions for uid mapping === */ + -+/* context -- one and only one. should be cycled through once on uid mapping -+ * and once on gid mapping */ -+static rsync_acl_list *_enum_racl_lists[] = { -+ &_rsync_acl_lists[0], &_rsync_acl_lists[1], NULL ++/* Context -- one and only one. Should be cycled through once on uid ++ * mapping and once on gid mapping. */ ++static item_list *_enum_racl_lists[] = { ++ &access_acl_list, &default_acl_list, NULL +}; + -+static rsync_acl_list **enum_racl_list = &_enum_racl_lists[0]; ++static item_list **enum_racl_list = &_enum_racl_lists[0]; ++static int enum_ida_index = 0; +static size_t enum_racl_index = 0; -+static size_t enum_race_index = 0; -+ -+/* this returns the next tag_type id from the given acl for the next entry, -+ * or it returns 0 if there are no more tag_type ids in the acl */ + -+static id_t next_ace_id(SMB_ACL_TAG_T tag_type, const rsync_acl *racl) ++/* This returns the next tag_type id from the given ACL for the next entry, ++ * or it returns 0 if there are no more tag_type ids in the acl. */ ++static id_t *next_ace_id(SMB_ACL_TAG_T tag_type, const rsync_acl *racl) +{ -+ for (; enum_race_index < racl->count; enum_race_index++) { -+ rsync_ace *race = &racl->races[enum_race_index]; -+ if (race->tag_type == tag_type) -+ return race->id; ++ const ida_entries *idal = tag_type == SMB_ACL_USER ? &racl->users : &racl->groups; ++ if (enum_ida_index < idal->count) { ++ id_access *ida = &idal->idas[enum_ida_index++]; ++ return &ida->id; + } -+ enum_race_index = 0; -+ return 0; ++ enum_ida_index = 0; ++ return NULL; +} + -+static id_t next_acl_id(SMB_ACL_TAG_T tag_type, const rsync_acl_list *racl_list) ++static id_t *next_acl_id(SMB_ACL_TAG_T tag_type, const item_list *racl_list) +{ + for (; enum_racl_index < racl_list->count; enum_racl_index++) { -+ rsync_acl *racl = &racl_list->racls[enum_racl_index]; -+ id_t id = next_ace_id(tag_type, racl); -+ if (id) ++ id_t *id; ++ acl_duo *duo_item = racl_list->items; ++ duo_item += enum_racl_index; ++ if ((id = next_ace_id(tag_type, &duo_item->racl)) != NULL) + return id; + } + enum_racl_index = 0; -+ return 0; ++ return NULL; +} + -+static id_t next_acl_list_id(SMB_ACL_TAG_T tag_type) ++static id_t *next_acl_list_id(SMB_ACL_TAG_T tag_type) +{ + for (; *enum_racl_list; enum_racl_list++) { -+ id_t id = next_acl_id(tag_type, *enum_racl_list); ++ id_t *id = next_acl_id(tag_type, *enum_racl_list); + if (id) + return id; + } + enum_racl_list = &_enum_racl_lists[0]; -+ return 0; ++ return NULL; +} + -+id_t next_acl_uid() ++id_t *next_acl_uid() +{ + return next_acl_list_id(SMB_ACL_USER); +} + -+id_t next_acl_gid() ++id_t *next_acl_gid() +{ + return next_acl_list_id(SMB_ACL_GROUP); +} + -+/* referring to the global context enum_entry, sets the entry's id */ -+static void set_acl_id(id_t id) -+{ -+ (*enum_racl_list)->racls[enum_racl_index].races[enum_race_index++].id = id; -+} ++/* This is used by dest_mode(). */ ++int default_perms_for_dir(const char *dir) ++{ ++ rsync_acl racl; ++ SMB_ACL_T sacl; ++ BOOL ok; ++ int perms; ++ ++ if (dir == NULL) ++ dir = "."; ++ perms = ACCESSPERMS & ~orig_umask; ++ /* Read the directory's default ACL. If it has none, this will successfully return an empty ACL. */ ++ sacl = sys_acl_get_file(dir, SMB_ACL_TYPE_DEFAULT); ++ if (sacl == NULL) { ++ /* Couldn't get an ACL. Darn. */ ++ switch (errno) { ++ case ENOTSUP: ++ /* ACLs are disabled. We could yell at the user to turn them on, but... */ ++ break; ++ case ENOENT: ++ if (dry_run) { ++ /* We're doing a dry run, so the containing directory ++ * wasn't actually created. Don't worry about it. */ ++ break; ++ } ++ /* Otherwise fall through. */ ++ default: ++ rprintf(FERROR, "default_perms_for_dir: sys_acl_get_file(%s, %s): %s, falling back on umask\n", ++ dir, str_acl_type(SMB_ACL_TYPE_DEFAULT), strerror(errno)); ++ } ++ return perms; ++ } + -+void acl_uid_map(id_t uid) -+{ -+ set_acl_id(uid); -+} ++ /* Convert it. */ ++ ok = unpack_smb_acl(&racl, sacl); ++ sys_acl_free_acl(sacl); ++ if (!ok) { ++ rprintf(FERROR, "default_perms_for_dir: unpack_smb_acl failed, falling back on umask\n"); ++ return perms; ++ } + -+void acl_gid_map(id_t gid) -+{ -+ set_acl_id(gid); ++ /* Apply the permission-bit entries of the default ACL, if any. */ ++ if (racl.user_obj != NO_ENTRY) { ++ perms = rsync_acl_get_perms(&racl); ++ if (verbose > 2) ++ rprintf(FINFO, "got ACL-based default perms %o for directory %s\n", perms, dir); ++ } ++ ++ rsync_acl_free(&racl); ++ return perms; +} + +#endif /* SUPPORT_ACLS */ ---- orig/backup.c 2005-02-22 01:57:58 -+++ backup.c 2004-10-06 00:13:09 -@@ -135,6 +135,7 @@ static int make_bak_dir(char *fullpath) +--- old/backup.c ++++ new/backup.c +@@ -29,6 +29,7 @@ extern char *backup_suffix; + extern char *backup_dir; + + extern int am_root; ++extern int preserve_acls; + extern int preserve_devices; + extern int preserve_specials; + extern int preserve_links; +@@ -94,7 +95,8 @@ path + ****************************************************************************/ + static int make_bak_dir(char *fullpath) + { +- STRUCT_STAT st; ++ statx sx; ++ struct file_struct *file; + char *rel = fullpath + backup_dir_len; + char *end = rel + strlen(rel); + char *p = end; +@@ -126,13 +128,24 @@ static int make_bak_dir(char *fullpath) + if (p >= rel) { + /* Try to transfer the directory settings of the + * actual dir that the files are coming from. */ +- if (do_stat(rel, &st) < 0) { ++ if (do_stat(rel, &sx.st) < 0) { + rsyserr(FERROR, errno, + "make_bak_dir stat %s failed", + full_fname(rel)); } else { - do_lchown(fullpath, st.st_uid, st.st_gid); - do_chmod(fullpath, st.st_mode); -+ (void)DUP_ACL(end, fullpath, st.st_mode); +- do_lchown(fullpath, st.st_uid, st.st_gid); +- do_chmod(fullpath, st.st_mode); ++#ifdef SUPPORT_ACLS ++ sx.acc_acl = sx.def_acl = NULL; ++#endif ++ if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS))) ++ continue; ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) { ++ get_acl(rel, &sx); ++ cache_acl(file, &sx); ++ } ++#endif ++ set_file_attrs(fullpath, file, NULL, 0); ++ free(file); } } *p = '/'; -@@ -187,6 +188,8 @@ static int keep_backup(char *fname) +@@ -170,15 +183,18 @@ static int robust_move(char *src, char * + * We will move the file to be deleted into a parallel directory tree. */ + static int keep_backup(char *fname) + { +- STRUCT_STAT st; ++ statx sx; + struct file_struct *file; + char *buf; + int kept = 0; + int ret_code; + + /* return if no file to keep */ +- if (do_lstat(fname, &st) < 0) ++ if (do_lstat(fname, &sx.st) < 0) + return 1; ++#ifdef SUPPORT_ACLS ++ sx.acc_acl = sx.def_acl = NULL; ++#endif + + if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) + return 1; /* the file could have disappeared */ +@@ -186,6 +202,13 @@ static int keep_backup(char *fname) if (!(buf = get_backup_name(fname))) return 0; -+ PUSH_KEEP_BACKUP_ACL(file, fname, buf); ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) { ++ get_acl(fname, &sx); ++ cache_acl(file, &sx); ++ } ++#endif + /* Check to see if this is a device file, or link */ - if (IS_DEVICE(file->mode)) { - if (am_root && preserve_devices) { -@@ -260,6 +263,7 @@ static int keep_backup(char *fname) - } - } - set_perms(buf, file, NULL, 0); -+ CLEANUP_KEEP_BACKUP_ACL(); - free(file); - - if (verbose > 1) { ---- orig/configure.in 2005-03-16 02:19:29 -+++ configure.in 2004-08-19 19:53:27 -@@ -467,6 +467,11 @@ if test x"$ac_cv_func_strcasecmp" = x"no + if ((am_root && preserve_devices && IS_DEVICE(file->mode)) + || (preserve_specials && IS_SPECIAL(file->mode))) { +@@ -254,7 +277,7 @@ static int keep_backup(char *fname) + if (robust_move(fname, buf) != 0) { + rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"", + full_fname(fname), buf); +- } else if (st.st_nlink > 1) { ++ } else if (sx.st.st_nlink > 1) { + /* If someone has hard-linked the file into the backup + * dir, rename() might return success but do nothing! */ + robust_unlink(fname); /* Just in case... */ +--- old/configure.in ++++ new/configure.in +@@ -482,6 +482,11 @@ if test x"$ac_cv_func_strcasecmp" = x"no AC_CHECK_LIB(resolv, strcasecmp) fi @@ -1207,140 +1230,1003 @@ ACLs to a non-ACL-supporting disk should complain. dnl At the moment we don't test for a broken memcmp(), because all we dnl need to do is test for equality, not comparison, and it seems that dnl every platform has a memcmp that can do at least that. -@@ -713,6 +718,77 @@ AC_SUBST(OBJ_RESTORE) +@@ -746,6 +751,78 @@ AC_SUBST(OBJ_RESTORE) AC_SUBST(CC_SHOBJ_FLAG) AC_SUBST(BUILD_POPT) -+AC_CHECK_HEADERS(sys/acl.h) -+AC_CHECK_FUNCS(_acl __acl _facl __facl) -+################################################# -+# check for ACL support -+ -+AC_MSG_CHECKING(whether to support ACLs) -+AC_ARG_WITH(acl-support, -+[ --with-acl-support Include ACL support (default=no)], -+[ case "$withval" in -+ yes) ++AC_CHECK_HEADERS(sys/acl.h) ++AC_CHECK_FUNCS(_acl __acl _facl __facl) ++################################################# ++# check for ACL support ++ ++AC_MSG_CHECKING(whether to support ACLs) ++AC_ARG_ENABLE(acl-support, ++AC_HELP_STRING([--enable-acl-support], [Include ACL support (default=no)]), ++[ case "$enableval" in ++ yes) ++ ++ case "$host_os" in ++ *sysv5*) ++ AC_MSG_RESULT(Using UnixWare ACLs) ++ AC_DEFINE(HAVE_UNIXWARE_ACLS, 1, [true if you have UnixWare ACLs]) ++ ;; ++ *solaris*|*cygwin*) ++ AC_MSG_RESULT(Using solaris ACLs) ++ AC_DEFINE(HAVE_SOLARIS_ACLS, 1, [true if you have solaris ACLs]) ++ ;; ++ *hpux*) ++ AC_MSG_RESULT(Using HPUX ACLs) ++ AC_DEFINE(HAVE_HPUX_ACLS, 1, [true if you have HPUX ACLs]) ++ ;; ++ *irix*) ++ AC_MSG_RESULT(Using IRIX ACLs) ++ AC_DEFINE(HAVE_IRIX_ACLS, 1, [true if you have IRIX ACLs]) ++ ;; ++ *aix*) ++ AC_MSG_RESULT(Using AIX ACLs) ++ AC_DEFINE(HAVE_AIX_ACLS, 1, [true if you have AIX ACLs]) ++ ;; ++ *osf*) ++ AC_MSG_RESULT(Using Tru64 ACLs) ++ AC_DEFINE(HAVE_TRU64_ACLS, 1, [true if you have Tru64 ACLs]) ++ LIBS="$LIBS -lpacl" ++ ;; ++ *) ++ AC_MSG_RESULT(ACLs requested -- running tests) ++ AC_CHECK_LIB(acl,acl_get_file) ++ AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[ ++ AC_TRY_LINK([#include ++#include ], ++[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);], ++samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)]) ++ AC_MSG_CHECKING(ACL test results) ++ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then ++ AC_MSG_RESULT(Using posix ACLs) ++ AC_DEFINE(HAVE_POSIX_ACLS, 1, [true if you have posix ACLs]) ++ AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[ ++ AC_TRY_LINK([#include ++#include ], ++[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);], ++samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)]) ++ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then ++ AC_DEFINE(HAVE_ACL_GET_PERM_NP, 1, [true if you have acl_get_perm_np]) ++ fi ++ else ++ AC_MSG_ERROR(Failed to find ACL support) ++ fi ++ ;; ++ esac ++ ;; ++ *) ++ AC_MSG_RESULT(no) ++ AC_DEFINE(HAVE_NO_ACLS, 1, [true if you don't have ACLs]) ++ ;; ++ esac ], ++ AC_DEFINE(HAVE_NO_ACLS, 1, [true if you don't have ACLs]) ++ AC_MSG_RESULT(no) ++) ++ + AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig]) + AC_OUTPUT + +--- old/flist.c ++++ new/flist.c +@@ -40,6 +40,7 @@ extern int filesfrom_fd; + extern int one_file_system; + extern int copy_dirlinks; + extern int keep_dirlinks; ++extern int preserve_acls; + extern int preserve_links; + extern int preserve_hard_links; + extern int preserve_devices; +@@ -133,6 +134,8 @@ static void list_file_entry(struct file_ + + permstring(permbuf, f->mode); + ++ /* TODO: indicate '+' if the entry has an ACL. */ ++ + #ifdef SUPPORT_LINKS + if (preserve_links && S_ISLNK(f->mode)) { + rprintf(FINFO, "%s %11.0f %s %s -> %s\n", +@@ -491,6 +494,9 @@ static struct file_struct *receive_file_ + char thisname[MAXPATHLEN]; + unsigned int l1 = 0, l2 = 0; + int alloc_len, basename_len, dirname_len, linkname_len, sum_len; ++#ifdef SUPPORT_ACLS ++ int xtra_len; ++#endif + OFF_T file_length; + char *basename, *dirname, *bp; + struct file_struct *file; +@@ -594,13 +600,27 @@ static struct file_struct *receive_file_ + + sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0; + ++#ifdef SUPPORT_ACLS ++ /* We need one or two index int32s when we're preserving ACLs. */ ++ if (preserve_acls) ++ xtra_len = (S_ISDIR(mode) ? 2 : 1) * 4; ++ else ++ xtra_len = 0; ++#endif ++ + alloc_len = file_struct_len + dirname_len + basename_len ++#ifdef SUPPORT_ACLS ++ + xtra_len ++#endif + + linkname_len + sum_len; + bp = pool_alloc(flist->file_pool, alloc_len, "receive_file_entry"); + + file = (struct file_struct *)bp; + memset(bp, 0, file_struct_len); + bp += file_struct_len; ++#ifdef SUPPORT_ACLS ++ bp += xtra_len; ++#endif + + file->modtime = modtime; + file->length = file_length; +@@ -693,6 +713,11 @@ static struct file_struct *receive_file_ + read_buf(f, sum, checksum_len); + } + ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ receive_acl(file, f); ++#endif ++ + return file; + } + +@@ -942,6 +967,9 @@ static struct file_struct *send_file_nam + unsigned short flags) + { + struct file_struct *file; ++#ifdef SUPPORT_ACLS ++ statx sx; ++#endif + + file = make_file(fname, flist, stp, flags, + f == -2 ? SERVER_FILTERS : ALL_FILTERS); +@@ -951,6 +979,15 @@ static struct file_struct *send_file_nam + if (chmod_modes && !S_ISLNK(file->mode)) + file->mode = tweak_mode(file->mode, chmod_modes); + ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) { ++ sx.st.st_mode = file->mode; ++ sx.acc_acl = sx.def_acl = NULL; ++ if (get_acl(fname, &sx) < 0) ++ return NULL; ++ } ++#endif ++ + maybe_emit_filelist_progress(flist->count + flist_count_offset); + + flist_expand(flist); +@@ -958,6 +995,15 @@ static struct file_struct *send_file_nam + if (file->basename[0]) { + flist->files[flist->count++] = file; + send_file_entry(file, f); ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ send_acl(&sx, f); ++#endif ++ } else { ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ free_acl(&sx); ++#endif + } + return file; + } +--- old/generator.c ++++ new/generator.c +@@ -36,6 +36,7 @@ extern int recurse; + extern int relative_paths; + extern int implied_dirs; + extern int keep_dirlinks; ++extern int preserve_acls; + extern int preserve_links; + extern int preserve_devices; + extern int preserve_specials; +@@ -85,6 +86,7 @@ extern long block_size; /* "long" becaus + extern int max_delete; + extern int force_delete; + extern int one_file_system; ++extern mode_t orig_umask; + extern struct stats stats; + extern dev_t filesystem_dev; + extern char *backup_dir; +@@ -317,22 +319,27 @@ static void do_delete_pass(struct file_l + rprintf(FINFO, " \r"); + } + +-int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st) ++int unchanged_attrs(struct file_struct *file, statx *sxp) + { + if (preserve_perms +- && (st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) ++ && (sxp->st.st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) + return 0; + +- if (am_root && preserve_uid && st->st_uid != file->uid) ++ if (am_root && preserve_uid && sxp->st.st_uid != file->uid) + return 0; + +- if (preserve_gid && file->gid != GID_NONE && st->st_gid != file->gid) ++ if (preserve_gid && file->gid != GID_NONE && sxp->st.st_gid != file->gid) + return 0; + ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && set_acl(NULL, file, sxp) == 0) ++ return 0; ++#endif ++ + return 1; + } + +-void itemize(struct file_struct *file, int ndx, int statret, STRUCT_STAT *st, ++void itemize(struct file_struct *file, int ndx, int statret, statx *sxp, + int32 iflags, uchar fnamecmp_type, char *xname) + { + if (statret >= 0) { /* A from-dest-dir statret can == 1! */ +@@ -340,19 +347,23 @@ void itemize(struct file_struct *file, i + : S_ISDIR(file->mode) ? !omit_dir_times + : !S_ISLNK(file->mode); + +- if (S_ISREG(file->mode) && file->length != st->st_size) ++ if (S_ISREG(file->mode) && file->length != sxp->st.st_size) + iflags |= ITEM_REPORT_SIZE; + if ((iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !keep_time + && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname)) +- || (keep_time && cmp_time(file->modtime, st->st_mtime) != 0)) ++ || (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0)) + iflags |= ITEM_REPORT_TIME; +- if ((file->mode & CHMOD_BITS) != (st->st_mode & CHMOD_BITS)) ++ if ((file->mode & CHMOD_BITS) != (sxp->st.st_mode & CHMOD_BITS)) + iflags |= ITEM_REPORT_PERMS; +- if (preserve_uid && am_root && file->uid != st->st_uid) ++ if (preserve_uid && am_root && file->uid != sxp->st.st_uid) + iflags |= ITEM_REPORT_OWNER; + if (preserve_gid && file->gid != GID_NONE +- && st->st_gid != file->gid) ++ && sxp->st.st_gid != file->gid) + iflags |= ITEM_REPORT_GROUP; ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && set_acl(NULL, file, sxp) == 0) ++ iflags |= ITEM_REPORT_ACL; ++#endif + } else + iflags |= ITEM_IS_NEW; + +@@ -603,7 +614,7 @@ void check_for_finished_hlinks(int itemi + * handling the file, -1 if no dest-linking occurred, or a non-negative + * value if we found an alternate basis file. */ + static int try_dests_reg(struct file_struct *file, char *fname, int ndx, +- char *cmpbuf, STRUCT_STAT *stp, int itemizing, ++ char *cmpbuf, statx *sxp, int itemizing, + int maybe_ATTRS_REPORT, enum logcode code) + { + int save_ignore_times = ignore_times; +@@ -617,7 +628,7 @@ static int try_dests_reg(struct file_str + + do { + pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); +- if (link_stat(cmpbuf, stp, 0) < 0 || !S_ISREG(stp->st_mode)) ++ if (link_stat(cmpbuf, &sxp->st, 0) < 0 || !S_ISREG(sxp->st.st_mode)) + continue; + switch (match_level) { + case 0: +@@ -625,16 +636,20 @@ static int try_dests_reg(struct file_str + match_level = 1; + /* FALL THROUGH */ + case 1: +- if (!unchanged_file(cmpbuf, file, stp)) ++ if (!unchanged_file(cmpbuf, file, &sxp->st)) + continue; + best_match = j; + match_level = 2; + /* FALL THROUGH */ + case 2: +- if (!unchanged_attrs(file, stp)) ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ get_acl(cmpbuf, sxp); ++#endif ++ if (!unchanged_attrs(file, sxp)) + continue; + if (always_checksum && preserve_times +- && cmp_time(stp->st_mtime, file->modtime)) ++ && cmp_time(sxp->st.st_mtime, file->modtime)) + continue; + best_match = j; + match_level = 3; +@@ -651,14 +666,14 @@ static int try_dests_reg(struct file_str + if (j != best_match) { + j = best_match; + pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); +- if (link_stat(cmpbuf, stp, 0) < 0) ++ if (link_stat(cmpbuf, &sxp->st, 0) < 0) + match_level = 0; + } + + if (match_level == 3 && !copy_dest) { + #ifdef SUPPORT_HARD_LINKS + if (link_dest) { +- if (hard_link_one(file, ndx, fname, 0, stp, ++ if (hard_link_one(file, ndx, fname, 0, sxp, + cmpbuf, 1, + itemizing && verbose > 1, + code) < 0) +@@ -667,8 +682,13 @@ static int try_dests_reg(struct file_str + hard_link_cluster(file, ndx, itemizing, code); + } else + #endif +- if (itemizing) +- itemize(file, ndx, 0, stp, 0, 0, NULL); ++ if (itemizing) { ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && !ACL_READY(*sxp)) ++ get_acl(fname, sxp); ++#endif ++ itemize(file, ndx, 0, sxp, 0, 0, NULL); ++ } + if (verbose > 1 && maybe_ATTRS_REPORT) { + code = logfile_format_has_i || dry_run + ? FCLIENT : FINFO; +@@ -686,8 +706,13 @@ static int try_dests_reg(struct file_str + } + return -1; + } +- if (itemizing) +- itemize(file, ndx, 0, stp, ITEM_LOCAL_CHANGE, 0, NULL); ++ if (itemizing) { ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && !ACL_READY(*sxp)) ++ get_acl(fname, sxp); ++#endif ++ itemize(file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL); ++ } + set_file_attrs(fname, file, NULL, 0); + if (maybe_ATTRS_REPORT + && ((!itemizing && verbose && match_level == 2) +@@ -712,13 +737,18 @@ static int try_dests_non(struct file_str + enum logcode code) + { + char fnamebuf[MAXPATHLEN]; +- STRUCT_STAT st; ++ statx sx; + int i = 0; + + do { + pathjoin(fnamebuf, MAXPATHLEN, basis_dir[i], fname); +- if (link_stat(fnamebuf, &st, 0) < 0 || S_ISDIR(st.st_mode) +- || !unchanged_attrs(file, &st)) ++ if (link_stat(fnamebuf, &sx.st, 0) < 0 || S_ISDIR(sx.st.st_mode)) ++ continue; ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ get_acl(fnamebuf, &sx); ++#endif ++ if (!unchanged_attrs(file, &sx)) + continue; + if (S_ISLNK(file->mode)) { + #ifdef SUPPORT_LINKS +@@ -731,10 +761,10 @@ static int try_dests_non(struct file_str + #endif + continue; + } else if (IS_SPECIAL(file->mode)) { +- if (!IS_SPECIAL(st.st_mode) || st.st_rdev != file->u.rdev) ++ if (!IS_SPECIAL(sx.st.st_mode) || sx.st.st_rdev != file->u.rdev) + continue; + } else if (IS_DEVICE(file->mode)) { +- if (!IS_DEVICE(st.st_mode) || st.st_rdev != file->u.rdev) ++ if (!IS_DEVICE(sx.st.st_mode) || sx.st.st_rdev != file->u.rdev) + continue; + } else { + rprintf(FERROR, +@@ -765,7 +795,15 @@ static int try_dests_non(struct file_str + int changes = compare_dest ? 0 : ITEM_LOCAL_CHANGE + + (link_dest ? ITEM_XNAME_FOLLOWS : 0); + char *lp = link_dest ? "" : NULL; +- itemize(file, ndx, 0, &st, changes, 0, lp); ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ get_acl(fname, &sx); ++#endif ++ itemize(file, ndx, 0, &sx, changes, 0, lp); ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ free_acl(&sx); ++#endif + } + if (verbose > 1 && maybe_ATTRS_REPORT) { + code = logfile_format_has_i || dry_run +@@ -779,6 +817,7 @@ static int try_dests_non(struct file_str + } + + static int phase = 0; ++static int dflt_perms; + + /* Acts on the_file_list->file's ndx'th item, whose name is fname. If a dir, + * make sure it exists, and has the right permissions/timestamp info. For +@@ -800,7 +839,8 @@ static void recv_generator(char *fname, + static int need_fuzzy_dirlist = 0; + struct file_struct *fuzzy_file = NULL; + int fd = -1, f_copy = -1; +- STRUCT_STAT st, real_st, partial_st; ++ statx sx, real_sx; ++ STRUCT_STAT partial_st; + struct file_struct *back_file = NULL; + int statret, real_ret, stat_errno; + char *fnamecmp, *partialptr, *backupptr = NULL; +@@ -851,6 +891,9 @@ static void recv_generator(char *fname, + dry_run--; + missing_below = -1; + } ++#ifdef SUPPORT_ACLS ++ sx.acc_acl = sx.def_acl = NULL; ++#endif + if (dry_run > 1) { + statret = -1; + stat_errno = ENOENT; +@@ -858,7 +901,7 @@ static void recv_generator(char *fname, + char *dn = file->dirname ? file->dirname : "."; + if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { + if (relative_paths && !implied_dirs +- && safe_stat(dn, &st) < 0 ++ && safe_stat(dn, &sx.st) < 0 + && create_directory_path(fname) < 0) { + rsyserr(FERROR, errno, + "recv_generator: mkdir %s failed", +@@ -870,6 +913,10 @@ static void recv_generator(char *fname, + } + if (fuzzy_basis) + need_fuzzy_dirlist = 1; ++#ifdef SUPPORT_ACLS ++ if (!preserve_perms) ++ dflt_perms = default_perms_for_dir(dn); ++#endif + } + parent_dirname = dn; + +@@ -878,7 +925,7 @@ static void recv_generator(char *fname, + need_fuzzy_dirlist = 0; + } + +- statret = link_stat(fname, &st, ++ statret = link_stat(fname, &sx.st, + keep_dirlinks && S_ISDIR(file->mode)); + stat_errno = errno; + } +@@ -896,8 +943,9 @@ static void recv_generator(char *fname, + * mode based on the local permissions and some heuristics. */ + if (!preserve_perms) { + int exists = statret == 0 +- && S_ISDIR(st.st_mode) == S_ISDIR(file->mode); +- file->mode = dest_mode(file->mode, st.st_mode, exists); ++ && S_ISDIR(sx.st.st_mode) == S_ISDIR(file->mode); ++ file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, ++ exists); + } + + if (S_ISDIR(file->mode)) { +@@ -906,8 +954,8 @@ static void recv_generator(char *fname, + * file of that name and it is *not* a directory, then + * we need to delete it. If it doesn't exist, then + * (perhaps recursively) create it. */ +- if (statret == 0 && !S_ISDIR(st.st_mode)) { +- if (delete_item(fname, st.st_mode, del_opts) < 0) ++ if (statret == 0 && !S_ISDIR(sx.st.st_mode)) { ++ if (delete_item(fname, sx.st.st_mode, del_opts) < 0) + return; + statret = -1; + } +@@ -916,7 +964,11 @@ static void recv_generator(char *fname, + dry_run++; + } + if (itemizing && f_out != -1) { +- itemize(file, ndx, statret, &st, ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && statret == 0) ++ get_acl(fname, &sx); ++#endif ++ itemize(file, ndx, statret, &sx, + statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL); + } + if (statret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) { +@@ -928,19 +980,19 @@ static void recv_generator(char *fname, + full_fname(fname)); + } + } +- if (set_file_attrs(fname, file, statret ? NULL : &st, 0) ++ if (set_file_attrs(fname, file, statret ? NULL : &sx, 0) + && verbose && code && f_out != -1) + rprintf(code, "%s/\n", fname); + if (delete_during && f_out != -1 && !phase && dry_run < 2 + && (file->flags & FLAG_DEL_HERE)) +- delete_in_dir(the_file_list, fname, file, &st); +- return; ++ delete_in_dir(the_file_list, fname, file, &sx.st); ++ goto cleanup; + } + + if (preserve_hard_links && file->link_u.links +- && hard_link_check(file, ndx, fname, statret, &st, ++ && hard_link_check(file, ndx, fname, statret, &sx, + itemizing, code, HL_CHECK_MASTER)) +- return; ++ goto cleanup; + + if (preserve_links && S_ISLNK(file->mode)) { + #ifdef SUPPORT_LINKS +@@ -958,7 +1010,7 @@ static void recv_generator(char *fname, + char lnk[MAXPATHLEN]; + int len; + +- if (!S_ISDIR(st.st_mode) ++ if (!S_ISDIR(sx.st.st_mode) + && (len = readlink(fname, lnk, MAXPATHLEN-1)) > 0) { + lnk[len] = 0; + /* A link already pointing to the +@@ -966,10 +1018,10 @@ static void recv_generator(char *fname, + * required. */ + if (strcmp(lnk, file->u.link) == 0) { + if (itemizing) { +- itemize(file, ndx, 0, &st, 0, ++ itemize(file, ndx, 0, &sx, 0, + 0, NULL); + } +- set_file_attrs(fname, file, &st, ++ set_file_attrs(fname, file, &sx, + maybe_ATTRS_REPORT); + if (preserve_hard_links + && file->link_u.links) { +@@ -982,9 +1034,9 @@ static void recv_generator(char *fname, + } + /* Not the right symlink (or not a symlink), so + * delete it. */ +- if (delete_item(fname, st.st_mode, del_opts) < 0) ++ if (delete_item(fname, sx.st.st_mode, del_opts) < 0) + return; +- if (!S_ISLNK(st.st_mode)) ++ if (!S_ISLNK(sx.st.st_mode)) + statret = -1; + } else if (basis_dir[0] != NULL) { + if (try_dests_non(file, fname, ndx, itemizing, +@@ -1000,7 +1052,7 @@ static void recv_generator(char *fname, + } + } + if (preserve_hard_links && file->link_u.links +- && hard_link_check(file, ndx, fname, -1, &st, ++ && hard_link_check(file, ndx, fname, -1, &sx, + itemizing, code, HL_SKIP)) + return; + if (do_symlink(file->u.link,fname) != 0) { +@@ -1009,7 +1061,7 @@ static void recv_generator(char *fname, + } else { + set_file_attrs(fname, file, NULL, 0); + if (itemizing) { +- itemize(file, ndx, statret, &st, ++ itemize(file, ndx, statret, &sx, + ITEM_LOCAL_CHANGE, 0, NULL); + } + if (code && verbose) { +@@ -1043,18 +1095,22 @@ static void recv_generator(char *fname, + itemizing = code = 0; + } + } ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && statret == 0) ++ get_acl(fname, &sx); ++#endif + if (statret != 0 +- || (st.st_mode & ~CHMOD_BITS) != (file->mode & ~CHMOD_BITS) +- || st.st_rdev != file->u.rdev) { ++ || (sx.st.st_mode & ~CHMOD_BITS) != (file->mode & ~CHMOD_BITS) ++ || sx.st.st_rdev != file->u.rdev) { + if (statret == 0 +- && delete_item(fname, st.st_mode, del_opts) < 0) +- return; ++ && delete_item(fname, sx.st.st_mode, del_opts) < 0) ++ goto cleanup; + if (preserve_hard_links && file->link_u.links +- && hard_link_check(file, ndx, fname, -1, &st, ++ && hard_link_check(file, ndx, fname, -1, &sx, + itemizing, code, HL_SKIP)) +- return; +- if ((IS_DEVICE(file->mode) && !IS_DEVICE(st.st_mode)) +- || (IS_SPECIAL(file->mode) && !IS_SPECIAL(st.st_mode))) ++ goto cleanup; ++ if ((IS_DEVICE(file->mode) && !IS_DEVICE(sx.st.st_mode)) ++ || (IS_SPECIAL(file->mode) && !IS_SPECIAL(sx.st.st_mode))) + statret = -1; + if (verbose > 2) { + rprintf(FINFO,"mknod(%s,0%o,0x%x)\n", +@@ -1067,7 +1123,7 @@ static void recv_generator(char *fname, + } else { + set_file_attrs(fname, file, NULL, 0); + if (itemizing) { +- itemize(file, ndx, statret, &st, ++ itemize(file, ndx, statret, &sx, + ITEM_LOCAL_CHANGE, 0, NULL); + } + if (code && verbose) +@@ -1079,12 +1135,12 @@ static void recv_generator(char *fname, + } + } else { + if (itemizing) +- itemize(file, ndx, statret, &st, 0, 0, NULL); +- set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT); ++ itemize(file, ndx, statret, &sx, 0, 0, NULL); ++ set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT); + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); + } +- return; ++ goto cleanup; + } + + if (!S_ISREG(file->mode)) { +@@ -1118,7 +1174,7 @@ static void recv_generator(char *fname, + } + + if (update_only && statret == 0 +- && cmp_time(st.st_mtime, file->modtime) > 0) { ++ && cmp_time(sx.st.st_mtime, file->modtime) > 0) { + if (verbose > 1) + rprintf(FINFO, "%s is newer\n", fname); + return; +@@ -1127,18 +1183,18 @@ static void recv_generator(char *fname, + fnamecmp = fname; + fnamecmp_type = FNAMECMP_FNAME; + +- if (statret == 0 && !S_ISREG(st.st_mode)) { +- if (delete_item(fname, st.st_mode, del_opts) != 0) ++ if (statret == 0 && !S_ISREG(sx.st.st_mode)) { ++ if (delete_item(fname, sx.st.st_mode, del_opts) != 0) + return; + statret = -1; + stat_errno = ENOENT; + } + + if (statret != 0 && basis_dir[0] != NULL) { +- int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &st, ++ int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &sx, + itemizing, maybe_ATTRS_REPORT, code); + if (j == -2) +- return; ++ goto cleanup; + if (j != -1) { + fnamecmp = fnamecmpbuf; + fnamecmp_type = j; +@@ -1147,7 +1203,7 @@ static void recv_generator(char *fname, + } + + real_ret = statret; +- real_st = st; ++ real_sx = sx; + + if (partial_dir && (partialptr = partial_dir_fname(fname)) != NULL + && link_stat(partialptr, &partial_st, 0) == 0 +@@ -1166,7 +1222,7 @@ static void recv_generator(char *fname, + rprintf(FINFO, "fuzzy basis selected for %s: %s\n", + fname, fnamecmpbuf); + } +- st.st_size = fuzzy_file->length; ++ sx.st.st_size = fuzzy_file->length; + statret = 0; + fnamecmp = fnamecmpbuf; + fnamecmp_type = FNAMECMP_FUZZY; +@@ -1175,7 +1231,7 @@ static void recv_generator(char *fname, + + if (statret != 0) { + if (preserve_hard_links && file->link_u.links +- && hard_link_check(file, ndx, fname, statret, &st, ++ && hard_link_check(file, ndx, fname, statret, &sx, + itemizing, code, HL_SKIP)) + return; + if (stat_errno == ENOENT) +@@ -1185,31 +1241,44 @@ static void recv_generator(char *fname, + return; + } + +- if (append_mode && st.st_size > file->length) ++ if (append_mode && sx.st.st_size > file->length) + return; + + if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH) + ; + else if (fnamecmp_type == FNAMECMP_FUZZY) + ; +- else if (unchanged_file(fnamecmp, file, &st)) { ++ else if (unchanged_file(fnamecmp, file, &sx.st)) { + if (partialptr) { + do_unlink(partialptr); + handle_partial_dir(partialptr, PDIR_DELETE); + } + if (itemizing) { +- itemize(file, ndx, real_ret, &real_st, ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && real_ret == 0) ++ get_acl(fname, &real_sx); ++#endif ++ itemize(file, ndx, real_ret, &real_sx, + 0, 0, NULL); ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) { ++ if (fnamecmp_type == FNAMECMP_FNAME) { ++ sx.acc_acl = real_sx.acc_acl; ++ sx.def_acl = real_sx.def_acl; ++ } else ++ free_acl(&real_sx); ++ } ++#endif + } +- set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT); ++ set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT); + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); +- return; ++ goto cleanup; + } + + prepare_to_open: + if (partialptr) { +- st = partial_st; ++ sx.st = partial_st; + fnamecmp = partialptr; + fnamecmp_type = FNAMECMP_PARTIAL_DIR; + statret = 0; +@@ -1233,17 +1302,21 @@ static void recv_generator(char *fname, + pretend_missing: + /* pretend the file didn't exist */ + if (preserve_hard_links && file->link_u.links +- && hard_link_check(file, ndx, fname, statret, &st, ++ && hard_link_check(file, ndx, fname, statret, &sx, + itemizing, code, HL_SKIP)) +- return; ++ goto cleanup; + statret = real_ret = -1; ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && ACL_READY(sx)) ++ free_acl(&sx); ++#endif + goto notify_others; + } + + if (inplace && make_backups && fnamecmp_type == FNAMECMP_FNAME) { + if (!(backupptr = get_backup_name(fname))) { + close(fd); +- return; ++ goto cleanup; + } + if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) { + close(fd); +@@ -1254,7 +1327,7 @@ static void recv_generator(char *fname, + full_fname(backupptr)); + free(back_file); + close(fd); +- return; ++ goto cleanup; + } + if ((f_copy = do_open(backupptr, + O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) { +@@ -1262,14 +1335,14 @@ static void recv_generator(char *fname, + full_fname(backupptr)); + free(back_file); + close(fd); +- return; ++ goto cleanup; + } + fnamecmp_type = FNAMECMP_BACKUP; + } + + if (verbose > 3) { + rprintf(FINFO, "gen mapped %s of size %.0f\n", +- fnamecmp, (double)st.st_size); ++ fnamecmp, (double)sx.st.st_size); + } + + if (verbose > 2) +@@ -1287,24 +1360,32 @@ static void recv_generator(char *fname, + iflags |= ITEM_BASIS_TYPE_FOLLOWS; + if (fnamecmp_type == FNAMECMP_FUZZY) + iflags |= ITEM_XNAME_FOLLOWS; +- itemize(file, -1, real_ret, &real_st, iflags, fnamecmp_type, ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && real_ret == 0) ++ get_acl(fname, &real_sx); ++#endif ++ itemize(file, -1, real_ret, &real_sx, iflags, fnamecmp_type, + fuzzy_file ? fuzzy_file->basename : NULL); ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ free_acl(&real_sx); ++#endif + } + + if (!do_xfers) { + if (preserve_hard_links && file->link_u.links) + hard_link_cluster(file, ndx, itemizing, code); +- return; ++ goto cleanup; + } + if (read_batch) +- return; ++ goto cleanup; + + if (statret != 0 || whole_file) { + write_sum_head(f_out, NULL); +- return; ++ goto cleanup; + } + +- generate_and_send_sums(fd, st.st_size, f_out, f_copy); ++ generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy); + + if (f_copy >= 0) { + close(f_copy); +@@ -1317,6 +1398,13 @@ static void recv_generator(char *fname, + } + + close(fd); + -+ case "$host_os" in -+ *sysv5*) -+ AC_MSG_RESULT(Using UnixWare ACLs) -+ AC_DEFINE(HAVE_UNIXWARE_ACLS, 1, [true if you have UnixWare ACLs]) -+ ;; -+ *solaris*) -+ AC_MSG_RESULT(Using solaris ACLs) -+ AC_DEFINE(HAVE_SOLARIS_ACLS, 1, [true if you have solaris ACLs]) -+ ;; -+ *hpux*) -+ AC_MSG_RESULT(Using HPUX ACLs) -+ AC_DEFINE(HAVE_HPUX_ACLS, 1, [true if you have HPUX ACLs]) -+ ;; -+ *irix*) -+ AC_MSG_RESULT(Using IRIX ACLs) -+ AC_DEFINE(HAVE_IRIX_ACLS, 1, [true if you have IRIX ACLs]) -+ ;; -+ *aix*) -+ AC_MSG_RESULT(Using AIX ACLs) -+ AC_DEFINE(HAVE_AIX_ACLS, 1, [true if you have AIX ACLs]) -+ ;; -+ *osf*) -+ AC_MSG_RESULT(Using Tru64 ACLs) -+ AC_DEFINE(HAVE_TRU64_ACLS, 1, [true if you have Tru64 ACLs]) -+ LIBS="$LIBS -lpacl" -+ ;; -+ *) -+ AC_MSG_RESULT(ACLs requested -- running tests) -+ AC_CHECK_LIB(acl,acl_get_file) -+ AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[ -+ AC_TRY_LINK([#include -+#include ], -+[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);], -+samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)]) -+ if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then -+ AC_MSG_RESULT(Using posix ACLs) -+ AC_DEFINE(HAVE_POSIX_ACLS, 1, [true if you have posix ACLs]) -+ AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[ -+ AC_TRY_LINK([#include -+#include ], -+[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);], -+samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)]) -+ if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then -+ AC_DEFINE(HAVE_ACL_GET_PERM_NP, 1, [true if you have acl_get_perm_np]) -+ fi -+ else -+ AC_MSG_ERROR(Failed to find ACL support) -+ fi -+ ;; -+ esac -+ ;; -+ *) -+ AC_MSG_RESULT(no) -+ AC_DEFINE(HAVE_NO_ACLS, 1, [true if you don't have ACLs]) -+ ;; -+ esac ], -+ AC_DEFINE(HAVE_NO_ACLS, 1, [true if you don't have ACLs]) -+ AC_MSG_RESULT(no) -+) ++ cleanup: ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ free_acl(&sx); ++#endif ++ return; + } + + void generate_files(int f_out, struct file_list *flist, char *local_name) +@@ -1376,6 +1464,8 @@ void generate_files(int f_out, struct fi + * notice that and let us know via the redo pipe (or its closing). */ + ignore_timeout = 1; + ++ dflt_perms = (ACCESSPERMS & ~orig_umask); + - AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig]) - AC_OUTPUT + for (i = 0; i < flist->count; i++) { + struct file_struct *file = flist->files[i]; ---- orig/flist.c 2005-03-16 02:19:29 -+++ flist.c 2005-03-16 02:24:11 -@@ -970,6 +970,8 @@ static struct file_struct *send_file_nam - file = make_file(fname, flist, f == -2 ? SERVER_FILTERS : ALL_FILTERS); - if (!file) - return NULL; -+ if (!MAKE_ACL(file, fname)) -+ return; +--- old/hlink.c ++++ new/hlink.c +@@ -25,6 +25,7 @@ - maybe_emit_filelist_progress(flist); + extern int verbose; + extern int link_dest; ++extern int preserve_acls; + extern int make_backups; + extern int log_format_has_i; + extern char *basis_dir[]; +@@ -143,15 +144,19 @@ void init_hard_links(void) -@@ -978,6 +980,10 @@ static struct file_struct *send_file_nam - if (file->basename[0]) { - flist->files[flist->count++] = file; - send_file_entry(file, f, base_flags); -+ SEND_ACL(file, f); -+ } else { -+ /* Cleanup unsent ACL(s). */ -+ SEND_ACL(file, -1); + #ifdef SUPPORT_HARD_LINKS + static int maybe_hard_link(struct file_struct *file, int ndx, +- char *fname, int statret, STRUCT_STAT *st, ++ char *fname, int statret, statx *sxp, + char *toname, STRUCT_STAT *to_st, + int itemizing, enum logcode code) + { + if (statret == 0) { +- if (st->st_dev == to_st->st_dev +- && st->st_ino == to_st->st_ino) { ++ if (sxp->st.st_dev == to_st->st_dev ++ && sxp->st.st_ino == to_st->st_ino) { + if (itemizing) { +- itemize(file, ndx, statret, st, ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && !ACL_READY(*sxp)) ++ get_acl(fname, sxp); ++#endif ++ itemize(file, ndx, statret, sxp, + ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, + 0, ""); + } +@@ -166,13 +171,13 @@ static int maybe_hard_link(struct file_s + return -1; + } } - return file; +- return hard_link_one(file, ndx, fname, statret, st, toname, ++ return hard_link_one(file, ndx, fname, statret, sxp, toname, + 0, itemizing, code); } -@@ -1304,6 +1310,8 @@ struct file_list *recv_file_list(int f) - flags |= read_byte(f) << 8; - file = receive_file_entry(flist, flags, f); + #endif -+ RECEIVE_ACL(file, f); -+ - if (S_ISREG(file->mode)) - stats.total_size += file->length; + int hard_link_check(struct file_struct *file, int ndx, char *fname, +- int statret, STRUCT_STAT *st, int itemizing, ++ int statret, statx *sxp, int itemizing, + enum logcode code, int skip) + { + #ifdef SUPPORT_HARD_LINKS +@@ -207,7 +212,7 @@ int hard_link_check(struct file_struct * + || st2.st_ino != st3.st_ino) + continue; + statret = 1; +- st = &st3; ++ sxp->st = st3; + if (verbose < 2 || !log_format_has_i) + itemizing = code = 0; + break; +@@ -215,12 +220,16 @@ int hard_link_check(struct file_struct * + if (!unchanged_file(cmpbuf, file, &st3)) + continue; + statret = 1; +- st = &st3; +- if (unchanged_attrs(file, &st3)) ++ sxp->st = st3; ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ get_acl(cmpbuf, sxp); ++#endif ++ if (unchanged_attrs(file, sxp)) + break; + } while (basis_dir[++j] != NULL); + } +- maybe_hard_link(file, ndx, fname, statret, st, ++ maybe_hard_link(file, ndx, fname, statret, sxp, + toname, &st2, itemizing, code); + file->F_HLINDEX = FINISHED_LINK; + } else +@@ -233,7 +242,7 @@ int hard_link_check(struct file_struct * -@@ -1326,6 +1334,8 @@ struct file_list *recv_file_list(int f) + #ifdef SUPPORT_HARD_LINKS + int hard_link_one(struct file_struct *file, int ndx, char *fname, +- int statret, STRUCT_STAT *st, char *toname, int terse, ++ int statret, statx *sxp, char *toname, int terse, + int itemizing, enum logcode code) + { + if (do_link(toname, fname)) { +@@ -249,7 +258,11 @@ int hard_link_one(struct file_struct *fi + } - clean_flist(flist, relative_paths, 1); + if (itemizing) { +- itemize(file, ndx, statret, st, ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && statret == 0 && !ACL_READY(*sxp)) ++ get_acl(fname, sxp); ++#endif ++ itemize(file, ndx, statret, sxp, + ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0, + terse ? "" : toname); + } +@@ -266,11 +279,12 @@ void hard_link_cluster(struct file_struc + #ifdef SUPPORT_HARD_LINKS + char hlink1[MAXPATHLEN]; + char *hlink2; +- STRUCT_STAT st1, st2; ++ statx sx; ++ STRUCT_STAT st; + int statret, ndx = master; -+ SORT_FILE_ACL_INDEX_LISTS(); -+ - if (f >= 0) { - /* Now send the uid/gid list. This was introduced in - * protocol version 15 */ ---- orig/generator.c 2005-03-17 09:05:21 -+++ generator.c 2005-03-11 11:18:22 -@@ -712,6 +712,10 @@ static void recv_generator(char *fname, - if (set_perms(fname, file, statret ? NULL : &st, 0) - && verbose && code && f_out != -1) - rprintf(code, "%s/\n", safe_fname(fname)); -+#if SUPPORT_ACLS -+ if (f_out == -1) -+ SET_ACL(fname, file); + file->F_HLINDEX = FINISHED_LINK; +- if (link_stat(f_name(file, hlink1), &st1, 0) < 0) ++ if (link_stat(f_name(file, hlink1), &st, 0) < 0) + return; + if (!(file->flags & FLAG_HLINK_TOL)) { + while (!(file->flags & FLAG_HLINK_EOL)) { +@@ -284,9 +298,13 @@ void hard_link_cluster(struct file_struc + if (file->F_HLINDEX != SKIPPED_LINK) + continue; + hlink2 = f_name(file, NULL); +- statret = link_stat(hlink2, &st2, 0); +- maybe_hard_link(file, ndx, hlink2, statret, &st2, +- hlink1, &st1, itemizing, code); ++ statret = link_stat(hlink2, &sx.st, 0); ++ maybe_hard_link(file, ndx, hlink2, statret, &sx, ++ hlink1, &st, itemizing, code); ++#ifdef SUPPORT_ACLS ++ if (preserve_acls) ++ free_acl(&sx); +#endif - if (delete_during && f_out != -1 && !phase && dry_run < 2 - && (file->flags & FLAG_DEL_HERE)) - delete_in_dir(the_file_list, fname, file); ---- orig/lib/sysacls.c 2004-10-20 15:35:58 -+++ lib/sysacls.c 2004-10-20 15:35:58 -@@ -0,0 +1,3217 @@ + file->F_HLINDEX = FINISHED_LINK; + } while (!(file->flags & FLAG_HLINK_EOL)); + #endif +--- old/lib/sysacls.c ++++ new/lib/sysacls.c +@@ -0,0 +1,3240 @@ +/* + Unix SMB/CIFS implementation. + Samba system utilities for ACL support. @@ -1362,8 +2248,9 @@ ACLs to a non-ACL-supporting disk should complain. +*/ + +#include "rsync.h" -+#include "sysacls.h" ++#include "sysacls.h" /****** ADDED ******/ + ++/****** EXTRAS -- THESE ITEMS ARE NOT FROM THE SAMBA SOURCE ******/ +void SAFE_FREE(void *mem) +{ + if (mem) @@ -1381,6 +2268,7 @@ ACLs to a non-ACL-supporting disk should complain. + } + return pw->pw_name; +} ++/****** EXTRAS -- END ******/ + +/* + This file wraps all differing system ACL interfaces into a consistent @@ -1545,7 +2433,7 @@ ACLs to a non-ACL-supporting disk should complain. + return acl_free(the_acl); +} + -+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) ++int sys_acl_free_qualifier(void *qual, UNUSED(SMB_ACL_TAG_T tagtype)) +{ + return acl_free(qual); +} @@ -1974,14 +2862,13 @@ ACLs to a non-ACL-supporting disk should complain. + */ + len = 0; + maxlen = 20 * acl_d->count; -+ if ((text = malloc(maxlen)) == NULL) { ++ if ((text = SMB_MALLOC(maxlen)) == NULL) { + errno = ENOMEM; + return NULL; + } + + for (i = 0; i < acl_d->count; i++) { + struct acl *ap = &acl_d->acl[i]; -+ struct passwd *pw; + struct group *gr; + char tagbuf[12]; + char idbuf[12]; @@ -2052,7 +2939,7 @@ ACLs to a non-ACL-supporting disk should complain. + + maxlen += nbytes + 20 * (acl_d->count - i); + -+ if ((text = Realloc(oldtext, maxlen)) == NULL) { ++ if ((text = SMB_REALLOC(oldtext, maxlen)) == NULL) { + SAFE_FREE(oldtext); + errno = ENOMEM; + return NULL; @@ -2084,7 +2971,7 @@ ACLs to a non-ACL-supporting disk should complain. + * acl[] array, this actually allocates an ACL with room + * for (count+1) entries + */ -+ if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) { ++ if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof(struct SMB_ACL_T) + count * sizeof(struct acl))) == NULL) { + errno = ENOMEM; + return NULL; + } @@ -2248,7 +3135,7 @@ ACLs to a non-ACL-supporting disk should complain. + * allocate a temporary buffer for the complete ACL + */ + acl_count = acc_acl->count + def_acl->count; -+ acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0])); ++ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count); + + if (acl_buf == NULL) { + sys_acl_free_acl(tmp_acl); @@ -2327,7 +3214,7 @@ ACLs to a non-ACL-supporting disk should complain. + return 0; +} + -+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) ++int sys_acl_free_qualifier(UNUSED(void *qual), UNUSED(SMB_ACL_TAG_T tagtype)) +{ + return 0; +} @@ -2605,14 +3492,13 @@ ACLs to a non-ACL-supporting disk should complain. + */ + len = 0; + maxlen = 20 * acl_d->count; -+ if ((text = malloc(maxlen)) == NULL) { ++ if ((text = SMB_MALLOC(maxlen)) == NULL) { + errno = ENOMEM; + return NULL; + } + + for (i = 0; i < acl_d->count; i++) { + struct acl *ap = &acl_d->acl[i]; -+ struct passwd *pw; + struct group *gr; + char tagbuf[12]; + char idbuf[12]; @@ -2683,7 +3569,7 @@ ACLs to a non-ACL-supporting disk should complain. + + maxlen += nbytes + 20 * (acl_d->count - i); + -+ if ((text = Realloc(oldtext, maxlen)) == NULL) { ++ if ((text = SMB_REALLOC(oldtext, maxlen)) == NULL) { + free(oldtext); + errno = ENOMEM; + return NULL; @@ -2715,7 +3601,7 @@ ACLs to a non-ACL-supporting disk should complain. + * acl[] array, this actually allocates an ACL with room + * for (count+1) entries + */ -+ if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) { ++ if ((a = SMB_MALLOC(sizeof(struct SMB_ACL_T) + count * sizeof(struct acl))) == NULL) { + errno = ENOMEM; + return NULL; + } @@ -3181,7 +4067,7 @@ ACLs to a non-ACL-supporting disk should complain. + * allocate a temporary buffer for the complete ACL + */ + acl_count = acc_acl->count + def_acl->count; -+ acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0])); ++ acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count); + + if (acl_buf == NULL) { + sys_acl_free_acl(tmp_acl); @@ -3344,7 +4230,7 @@ ACLs to a non-ACL-supporting disk should complain. +{ + SMB_ACL_T a; + -+ if ((a = malloc(sizeof(*a))) == NULL) { ++ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) { + errno = ENOMEM; + return NULL; + } @@ -3361,7 +4247,7 @@ ACLs to a non-ACL-supporting disk should complain. +{ + SMB_ACL_T a; + -+ if ((a = malloc(sizeof(*a))) == NULL) { ++ if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) { + errno = ENOMEM; + return NULL; + } @@ -3418,7 +4304,7 @@ ACLs to a non-ACL-supporting disk should complain. + return NULL; + } + -+ if ((a = malloc(sizeof(*a) + sizeof(struct acl))) == NULL) { ++ if ((a = SMB_MALLOC(sizeof(struct SMB_ACL_T) + sizeof(struct acl))) == NULL) { + errno = ENOMEM; + return NULL; + } @@ -3639,12 +4525,16 @@ ACLs to a non-ACL-supporting disk should complain. + int rc = 0; + uid_t user_id; + ++ /* AIX has no DEFAULT */ ++ if ( type == SMB_ACL_TYPE_DEFAULT ) ++ return NULL; ++ + /* Get the acl using statacl */ + + DEBUG(10,("Entering sys_acl_get_file\n")); + DEBUG(10,("path_p is %s\n",path_p)); + -+ file_acl = (struct acl *)malloc(BUFSIZ); ++ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ); + + if(file_acl == NULL) { + errno=ENOMEM; @@ -3675,7 +4565,7 @@ ACLs to a non-ACL-supporting disk should complain. + if(acl_entry_link_head == NULL) + return(NULL); + -+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); ++ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry); + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; @@ -3710,8 +4600,7 @@ ACLs to a non-ACL-supporting disk should complain. + * and already has entryp allocated. */ + + if(acl_entry_link_head->count != 0) { -+ acl_entry_link->nextp = (struct acl_entry_link *) -+ malloc(sizeof(struct acl_entry_link)); ++ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link); + + if(acl_entry_link->nextp == NULL) { + SAFE_FREE(file_acl); @@ -3722,7 +4611,7 @@ ACLs to a non-ACL-supporting disk should complain. + + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; -+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); ++ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry); + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; @@ -3781,7 +4670,7 @@ ACLs to a non-ACL-supporting disk should complain. + for( i = 1; i < 4; i++) { + DEBUG(10,("i is %d\n",i)); + if(acl_entry_link_head->count != 0) { -+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); ++ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link); + if(acl_entry_link->nextp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; @@ -3791,7 +4680,7 @@ ACLs to a non-ACL-supporting disk should complain. + + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; -+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); ++ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry); + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; @@ -3858,7 +4747,7 @@ ACLs to a non-ACL-supporting disk should complain. + + DEBUG(10,("Entering sys_acl_get_fd\n")); + DEBUG(10,("fd is %d\n",fd)); -+ file_acl = (struct acl *)malloc(BUFSIZ); ++ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ); + + if(file_acl == NULL) { + errno=ENOMEM; @@ -3891,7 +4780,7 @@ ACLs to a non-ACL-supporting disk should complain. + return(NULL); + } + -+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); ++ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry); + + if(acl_entry_link->entryp == NULL) { + errno = ENOMEM; @@ -3928,7 +4817,7 @@ ACLs to a non-ACL-supporting disk should complain. + * and already has entryp allocated. */ + + if(acl_entry_link_head->count != 0) { -+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); ++ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link); + if(acl_entry_link->nextp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); @@ -3937,7 +4826,7 @@ ACLs to a non-ACL-supporting disk should complain. + } + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; -+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); ++ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry); + if(acl_entry_link->entryp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); @@ -3996,7 +4885,7 @@ ACLs to a non-ACL-supporting disk should complain. + for( i = 1; i < 4; i++) { + DEBUG(10,("i is %d\n",i)); + if(acl_entry_link_head->count != 0){ -+ acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); ++ acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link); + if(acl_entry_link->nextp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); @@ -4006,7 +4895,7 @@ ACLs to a non-ACL-supporting disk should complain. + + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; -+ acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); ++ acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry); + + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); @@ -4085,7 +4974,7 @@ ACLs to a non-ACL-supporting disk should complain. + + DEBUG(10,("Entering sys_acl_init\n")); + -+ theacl = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); ++ theacl = SMB_MALLOC_P(struct acl_entry_link); + if(theacl == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_init is %d\n",errno)); @@ -4120,7 +5009,7 @@ ACLs to a non-ACL-supporting disk should complain. + } + + if(theacl->count != 0){ -+ temp_entry->nextp = acl_entryp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); ++ temp_entry->nextp = acl_entryp = SMB_MALLOC_P(struct acl_entry_link); + if(acl_entryp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno)); @@ -4132,7 +5021,7 @@ ACLs to a non-ACL-supporting disk should complain. + DEBUG(10,("The acl_entryp->prevp is %d\n",acl_entryp->prevp)); + } + -+ *pentry = acl_entryp->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); ++ *pentry = acl_entryp->entryp = SMB_MALLOC_P(struct new_acl_entry); + if(*pentry == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno)); @@ -4222,7 +5111,7 @@ ACLs to a non-ACL-supporting disk should complain. + return(0); + + acl_length = BUFSIZ; -+ file_acl = (struct acl *)malloc(BUFSIZ); ++ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ); + + if(file_acl == NULL) { + errno = ENOMEM; @@ -4255,7 +5144,7 @@ ACLs to a non-ACL-supporting disk should complain. + + if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) { + acl_length += sizeof(struct acl_entry); -+ file_acl_temp = (struct acl *)malloc(acl_length); ++ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length); + if(file_acl_temp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; @@ -4310,7 +5199,7 @@ ACLs to a non-ACL-supporting disk should complain. + + DEBUG(10,("Entering sys_acl_set_fd\n")); + acl_length = BUFSIZ; -+ file_acl = (struct acl *)malloc(BUFSIZ); ++ file_acl = (struct acl *)SMB_MALLOC(BUFSIZ); + + if(file_acl == NULL) { + errno = ENOMEM; @@ -4344,7 +5233,7 @@ ACLs to a non-ACL-supporting disk should complain. + + if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) { + acl_length += sizeof(struct acl_entry); -+ file_acl_temp = (struct acl *)malloc(acl_length); ++ file_acl_temp = (struct acl *)SMB_MALLOC(acl_length); + if(file_acl_temp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; @@ -4425,49 +5314,49 @@ ACLs to a non-ACL-supporting disk should complain. + +#else /* No ACLs. */ + -+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p) ++int sys_acl_get_entry(UNUSED(SMB_ACL_T the_acl), UNUSED(int entry_id), UNUSED(SMB_ACL_ENTRY_T *entry_p)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) ++int sys_acl_get_tag_type(UNUSED(SMB_ACL_ENTRY_T entry_d), UNUSED(SMB_ACL_TAG_T *tag_type_p)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) ++int sys_acl_get_permset(UNUSED(SMB_ACL_ENTRY_T entry_d), UNUSED(SMB_ACL_PERMSET_T *permset_p)) +{ + errno = ENOSYS; + return -1; +} + -+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d) ++void *sys_acl_get_qualifier(UNUSED(SMB_ACL_ENTRY_T entry_d)) +{ + errno = ENOSYS; + return NULL; +} + -+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type) ++SMB_ACL_T sys_acl_get_file(UNUSED(const char *path_p), UNUSED(SMB_ACL_TYPE_T type)) +{ + errno = ENOSYS; + return (SMB_ACL_T)NULL; +} + -+SMB_ACL_T sys_acl_get_fd(int fd) ++SMB_ACL_T sys_acl_get_fd(UNUSED(int fd)) +{ + errno = ENOSYS; + return (SMB_ACL_T)NULL; +} + -+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset) ++int sys_acl_clear_perms(UNUSED(SMB_ACL_PERMSET_T permset)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) ++int sys_acl_add_perm( UNUSED(SMB_ACL_PERMSET_T permset), UNUSED(SMB_ACL_PERM_T perm)) +{ + errno = ENOSYS; + return -1; @@ -4479,89 +5368,117 @@ ACLs to a non-ACL-supporting disk should complain. + return (permset & perm) ? 1 : 0; +} + -+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen) ++char *sys_acl_to_text(UNUSED(SMB_ACL_T the_acl), UNUSED(ssize_t *plen)) +{ + errno = ENOSYS; + return NULL; +} + -+int sys_acl_free_text(char *text) ++int sys_acl_free_text(UNUSED(char *text)) +{ + errno = ENOSYS; + return -1; +} + -+SMB_ACL_T sys_acl_init( int count) ++SMB_ACL_T sys_acl_init(UNUSED(int count)) +{ + errno = ENOSYS; + return NULL; +} + -+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) ++int sys_acl_create_entry(UNUSED(SMB_ACL_T *pacl), UNUSED(SMB_ACL_ENTRY_T *pentry)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) ++int sys_acl_set_tag_type(UNUSED(SMB_ACL_ENTRY_T entry), UNUSED(SMB_ACL_TAG_T tagtype)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual) ++int sys_acl_set_qualifier(UNUSED(SMB_ACL_ENTRY_T entry), UNUSED(void *qual)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) ++int sys_acl_set_permset(UNUSED(SMB_ACL_ENTRY_T entry), UNUSED(SMB_ACL_PERMSET_T permset)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_valid( SMB_ACL_T theacl ) ++int sys_acl_valid(UNUSED(SMB_ACL_T theacl)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) ++int sys_acl_set_file(UNUSED(const char *name), UNUSED(SMB_ACL_TYPE_T acltype), UNUSED(SMB_ACL_T theacl)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_set_fd( int fd, SMB_ACL_T theacl) ++int sys_acl_set_fd(UNUSED(int fd), UNUSED(SMB_ACL_T theacl)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_delete_def_file(const char *name) ++int sys_acl_delete_def_file(UNUSED(const char *name)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_free_acl(SMB_ACL_T the_acl) ++int sys_acl_free_acl(UNUSED(SMB_ACL_T the_acl)) +{ + errno = ENOSYS; + return -1; +} + -+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) ++int sys_acl_free_qualifier(UNUSED(void *qual), UNUSED(SMB_ACL_TAG_T tagtype)) +{ + errno = ENOSYS; + return -1; +} + +#endif /* No ACLs. */ ---- orig/lib/sysacls.h 2004-10-20 15:31:22 -+++ lib/sysacls.h 2004-10-20 15:31:22 -@@ -0,0 +1,25 @@ -+#define Realloc(mem, cnt) realloc_array((mem), char, (cnt)) ++ ++/************************************************************************ ++ Deliberately outside the ACL defines. Return 1 if this is a "no acls" ++ errno, 0 if not. ++************************************************************************/ ++ ++int no_acl_syscall_error(int err) ++{ ++#if defined(ENOSYS) ++ if (err == ENOSYS) { ++ return 1; ++ } ++#endif ++#if defined(ENOTSUP) ++ if (err == ENOTSUP) { ++ return 1; ++ } ++#endif ++ return 0; ++} +--- old/lib/sysacls.h ++++ new/lib/sysacls.h +@@ -0,0 +1,33 @@ ++#if defined SUPPORT_ACLS && defined HAVE_SYS_ACL_H ++#include ++#endif ++#include "smb_acls.h" ++ ++#define SMB_MALLOC(cnt) new_array(char, cnt) ++#define SMB_MALLOC_P(obj) new_array(obj, 1) ++#define SMB_MALLOC_ARRAY(obj, cnt) new_array(obj, cnt) ++#define SMB_REALLOC(mem, cnt) realloc_array(mem, char, cnt) +#define slprintf snprintf + +int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p); @@ -4586,28 +5503,43 @@ ACLs to a non-ACL-supporting disk should complain. +int sys_acl_free_text(char *text); +int sys_acl_free_acl(SMB_ACL_T the_acl); +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype); ---- orig/mkproto.awk 2005-02-18 20:16:59 -+++ mkproto.awk 2005-02-18 20:18:17 +--- old/log.c ++++ new/log.c +@@ -592,8 +592,10 @@ static void log_formatted(enum logcode c + n[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p'; + n[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o'; + n[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g'; +- n[8] = '.'; +- n[9] = '\0'; ++ n[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u'; ++ n[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a'; ++ n[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x'; ++ n[11] = '\0'; + + if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) { + char ch = iflags & ITEM_IS_NEW ? '+' : '?'; +--- old/mkproto.awk ++++ new/mkproto.awk @@ -58,7 +58,7 @@ BEGIN { next; } --!/^OFF_T|^size_t|^off_t|^pid_t|^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^uchar|^short|^struct|^BOOL|^void|^time|^const/ { -+!/^OFF_T|^size_t|^off_t|^pid_t|^id_t|^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^uchar|^short|^struct|^BOOL|^void|^time|^const/ { +-!/^OFF_T|^size_t|^off_t|^pid_t|^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^uchar|^short|^struct|^BOOL|^void|^time|^const|^RETSIGTYPE/ { ++!/^OFF_T|^size_t|^off_t|^pid_t|^id_t|^unsigned|^mode_t|^DIR|^user|^int|^char|^uint|^uchar|^short|^struct|^BOOL|^void|^time|^const|^RETSIGTYPE/ { next; } ---- orig/options.c 2005-03-17 09:05:21 -+++ options.c 2005-02-14 02:46:05 -@@ -44,6 +44,7 @@ int keep_dirlinks = 0; +--- old/options.c ++++ new/options.c +@@ -47,6 +47,7 @@ int copy_dirlinks = 0; int copy_links = 0; int preserve_links = 0; int preserve_hard_links = 0; +int preserve_acls = 0; int preserve_perms = 0; + int preserve_executability = 0; int preserve_devices = 0; - int preserve_uid = 0; -@@ -179,6 +180,7 @@ static void print_rsync_version(enum log +@@ -198,6 +199,7 @@ static void print_rsync_version(enum log char const *got_socketpair = "no "; char const *have_inplace = "no "; char const *hardlinks = "no "; @@ -4615,7 +5547,7 @@ ACLs to a non-ACL-supporting disk should complain. char const *links = "no "; char const *ipv6 = "no "; STRUCT_STAT *dumstat; -@@ -195,6 +197,10 @@ static void print_rsync_version(enum log +@@ -214,6 +216,10 @@ static void print_rsync_version(enum log hardlinks = ""; #endif @@ -4626,42 +5558,47 @@ ACLs to a non-ACL-supporting disk should complain. #ifdef SUPPORT_LINKS links = ""; #endif -@@ -209,9 +215,9 @@ static void print_rsync_version(enum log - "Copyright (C) 1996-2005 by Andrew Tridgell and others\n"); +@@ -227,9 +233,9 @@ static void print_rsync_version(enum log + rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n"); rprintf(f, "\n"); rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, " -- "%shard links, %ssymlinks, batchfiles, \n", -+ "%shard links, %sACLs, %ssymlinks, batchfiles, \n", +- "%shard links, %ssymlinks, batchfiles,\n", ++ "%shard links, %sACLs, %ssymlinks, batchfiles,\n", (int) (sizeof (OFF_T) * 8), - got_socketpair, hardlinks, links); + got_socketpair, hardlinks, acls, links); /* Note that this field may not have type ino_t. It depends * on the complicated interaction between largefile feature -@@ -281,6 +287,7 @@ void usage(enum logcode F) +@@ -299,6 +305,9 @@ void usage(enum logcode F) rprintf(F," -H, --hard-links preserve hard links\n"); - rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n"); rprintf(F," -p, --perms preserve permissions\n"); + rprintf(F," -E, --executability preserve the file's executability\n"); ++#ifdef SUPPORT_ACLS + rprintf(F," -A, --acls preserve ACLs (implies --perms)\n"); - rprintf(F," -o, --owner preserve owner (root only)\n"); ++#endif + rprintf(F," --chmod=CHMOD change destination permissions\n"); + rprintf(F," -o, --owner preserve owner (super-user only)\n"); rprintf(F," -g, --group preserve group\n"); - rprintf(F," -D, --devices preserve devices (root only)\n"); -@@ -404,6 +411,7 @@ static struct poptOption long_options[] - {"no-whole-file", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 }, - {"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 }, - {"perms", 'p', POPT_ARG_NONE, &preserve_perms, 0, 0, 0 }, -+ {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 }, - {"owner", 'o', POPT_ARG_NONE, &preserve_uid, 0, 0, 0 }, - {"group", 'g', POPT_ARG_NONE, &preserve_gid, 0, 0, 0 }, - {"devices", 'D', POPT_ARG_NONE, &preserve_devices, 0, 0, 0 }, -@@ -862,6 +870,24 @@ int parse_arguments(int *argc, const cha - basis_dir[basis_dir_cnt++] = (char *)arg; - break; +@@ -415,6 +424,9 @@ static struct poptOption long_options[] + {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 }, + {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 }, + {"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 }, ++ {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 }, ++ {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 }, ++ {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 }, + {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 }, + {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, + {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, +@@ -1080,6 +1092,24 @@ int parse_arguments(int *argc, const cha + usage(FINFO); + exit_cleanup(0); + case 'A': -+#if SUPPORT_ACLS -+ preserve_acls = 1; ++#ifdef SUPPORT_ACLS ++ preserve_acls++; + preserve_perms = 1; ++ break; +#else + /* FIXME: this should probably be ignored with a + * warning and then countermeasures taken to @@ -4672,107 +5609,371 @@ ACLs to a non-ACL-supporting disk should complain. + "ACLs are not supported on this %s\n", + am_server ? "server" : "client"); + return 0; -+#endif /* SUPPORT_ACLS */ -+ break; ++#endif + + default: /* A large opt value means that set_refuse_options() * turned this option off. */ -@@ -1269,6 +1295,8 @@ void server_options(char **args,int *arg +@@ -1526,6 +1556,10 @@ void server_options(char **args,int *arg if (preserve_hard_links) argstr[x++] = 'H'; ++#ifdef SUPPORT_ACLS + if (preserve_acls) + argstr[x++] = 'A'; ++#endif if (preserve_uid) argstr[x++] = 'o'; if (preserve_gid) ---- orig/rsync.c 2005-03-16 02:19:30 -+++ rsync.c 2004-07-03 20:11:58 -@@ -137,6 +137,14 @@ int set_perms(char *fname,struct file_st +--- old/receiver.c ++++ new/receiver.c +@@ -47,6 +47,7 @@ extern int keep_partial; + extern int checksum_seed; + extern int inplace; + extern int delay_updates; ++extern mode_t orig_umask; + extern struct stats stats; + extern char *log_format; + extern char *tmpdir; +@@ -344,6 +345,10 @@ int recv_files(int f_in, struct file_lis + int save_make_backups = make_backups; + int itemizing = am_server ? logfile_format_has_i : log_format_has_i; + int max_phase = protocol_version >= 29 ? 2 : 1; ++ int dflt_perms = (ACCESSPERMS & ~orig_umask); ++#ifdef SUPPORT_ACLS ++ char *parent_dirname = ""; ++#endif + int i, recv_ok; + + if (verbose > 2) +@@ -541,7 +546,16 @@ int recv_files(int f_in, struct file_lis + * mode based on the local permissions and some heuristics. */ + if (!preserve_perms) { + int exists = fd1 != -1; +- file->mode = dest_mode(file->mode, st.st_mode, exists); ++#ifdef SUPPORT_ACLS ++ char *dn = file->dirname ? file->dirname : "."; ++ if (parent_dirname != dn ++ && strcmp(parent_dirname, dn) != 0) { ++ dflt_perms = default_perms_for_dir(dn); ++ parent_dirname = dn; ++ } ++#endif ++ file->mode = dest_mode(file->mode, st.st_mode, ++ dflt_perms, exists); + } + + /* We now check to see if we are writing file "inplace" */ +--- old/rsync.c ++++ new/rsync.c +@@ -33,6 +33,7 @@ + extern int verbose; + extern int dry_run; + extern int logfile_format_has_i; ++extern int preserve_acls; + extern int preserve_perms; + extern int preserve_executability; + extern int preserve_times; +@@ -101,7 +102,8 @@ void free_sums(struct sum_struct *s) + + /* This is only called when we aren't preserving permissions. Figure out what + * the permissions should be and return them merged back into the mode. */ +-mode_t dest_mode(mode_t flist_mode, mode_t cur_mode, int exists) ++mode_t dest_mode(mode_t flist_mode, mode_t cur_mode, int dflt_perms, ++ int exists) + { + /* If the file already exists, we'll return the local permissions, + * possibly tweaked by the --executability option. */ +@@ -116,55 +118,63 @@ mode_t dest_mode(mode_t flist_mode, mode + cur_mode |= (cur_mode & 0444) >> 2; + } + } else +- cur_mode = flist_mode & ACCESSPERMS & ~orig_umask; ++ cur_mode = flist_mode & ACCESSPERMS & dflt_perms; + if (daemon_chmod_modes && !S_ISLNK(flist_mode)) + cur_mode = tweak_mode(cur_mode, daemon_chmod_modes); + return (flist_mode & ~CHMOD_BITS) | (cur_mode & CHMOD_BITS); + } + +-int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st, ++int set_file_attrs(char *fname, struct file_struct *file, statx *sxp, + int flags) + { + int updated = 0; +- STRUCT_STAT st2; ++ statx sx2; + int change_uid, change_gid; + +- if (!st) { ++ if (!sxp) { + if (dry_run) + return 1; +- if (link_stat(fname, &st2, 0) < 0) { ++ if (link_stat(fname, &sx2.st, 0) < 0) { + rsyserr(FERROR, errno, "stat %s failed", + full_fname(fname)); + return 0; + } +- st = &st2; ++#ifdef SUPPORT_ACLS ++ sx2.acc_acl = sx2.def_acl = NULL; ++#endif + if (!preserve_perms && S_ISDIR(file->mode) +- && st->st_mode & S_ISGID) { ++ && sx2.st.st_mode & S_ISGID) { + /* We just created this directory and its setgid + * bit is on, so make sure it stays on. */ + file->mode |= S_ISGID; + } ++ sxp = &sx2; + } + +- if (!preserve_times || (S_ISDIR(st->st_mode) && omit_dir_times)) ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && !ACL_READY(*sxp)) ++ get_acl(fname, sxp); ++#endif ++ ++ if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times)) + flags |= ATTRS_SKIP_MTIME; + if (!(flags & ATTRS_SKIP_MTIME) +- && cmp_time(st->st_mtime, file->modtime) != 0) { +- int ret = set_modtime(fname, file->modtime, st->st_mode); ++ && cmp_time(sxp->st.st_mtime, file->modtime) != 0) { ++ int ret = set_modtime(fname, file->modtime, sxp->st.st_mode); + if (ret < 0) { + rsyserr(FERROR, errno, "failed to set times on %s", + full_fname(fname)); +- return 0; ++ goto cleanup; + } + if (ret == 0) /* ret == 1 if symlink could not be set */ + updated = 1; + } + +- change_uid = am_root && preserve_uid && st->st_uid != file->uid; ++ change_uid = am_root && preserve_uid && sxp->st.st_uid != file->uid; + change_gid = preserve_gid && file->gid != GID_NONE +- && st->st_gid != file->gid; ++ && sxp->st.st_gid != file->gid; + #if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK +- if (S_ISLNK(st->st_mode)) ++ if (S_ISLNK(sxp->st.st_mode)) + ; + else + #endif +@@ -174,43 +184,55 @@ int set_file_attrs(char *fname, struct f + rprintf(FINFO, + "set uid of %s from %ld to %ld\n", + fname, +- (long)st->st_uid, (long)file->uid); ++ (long)sxp->st.st_uid, (long)file->uid); + } + if (change_gid) { + rprintf(FINFO, + "set gid of %s from %ld to %ld\n", + fname, +- (long)st->st_gid, (long)file->gid); ++ (long)sxp->st.st_gid, (long)file->gid); + } + } + if (do_lchown(fname, +- change_uid ? file->uid : st->st_uid, +- change_gid ? file->gid : st->st_gid) != 0) { ++ change_uid ? file->uid : sxp->st.st_uid, ++ change_gid ? file->gid : sxp->st.st_gid) != 0) { + /* shouldn't have attempted to change uid or gid + * unless have the privilege */ + rsyserr(FERROR, errno, "%s %s failed", + change_uid ? "chown" : "chgrp", + full_fname(fname)); +- return 0; ++ goto cleanup; + } + /* a lchown had been done - we have to re-stat if the + * destination had the setuid or setgid bits set due + * to the side effect of the chown call */ +- if (st->st_mode & (S_ISUID | S_ISGID)) { +- link_stat(fname, st, +- keep_dirlinks && S_ISDIR(st->st_mode)); ++ if (sxp->st.st_mode & (S_ISUID | S_ISGID)) { ++ link_stat(fname, &sxp->st, ++ keep_dirlinks && S_ISDIR(sxp->st.st_mode)); + } + updated = 1; + } + ++#ifdef SUPPORT_ACLS ++ /* It's OK to call set_acl() now, even for a dir, as the generator ++ * will enable owner-writability using chmod, if necessary. ++ * ++ * If set_acl() changes permission bits in the process of setting ++ * an access ACL, it changes sxp->st.st_mode so we know whether we ++ * need to chmod(). */ ++ if (preserve_acls && set_acl(fname, file, sxp) == 0) ++ updated = 1; ++#endif ++ + #ifdef HAVE_CHMOD +- if ((st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) { +- int ret = do_chmod(fname, file->mode); ++ if ((sxp->st.st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) { ++ mode_t mode = file->mode; ++ int ret = do_chmod(fname, mode); + if (ret < 0) { + rsyserr(FERROR, errno, + "failed to set permissions on %s", + full_fname(fname)); +- return 0; ++ goto cleanup; + } + if (ret == 0) /* ret == 1 if symlink could not be set */ + updated = 1; +@@ -225,6 +247,11 @@ int set_file_attrs(char *fname, struct f + else + rprintf(code, "%s is uptodate\n", fname); } ++ cleanup: ++#ifdef SUPPORT_ACLS ++ if (preserve_acls && sxp == &sx2) ++ free_acl(&sx2); ++#endif + return updated; + } + +--- old/rsync.h ++++ new/rsync.h +@@ -486,6 +486,15 @@ struct idev { + #define IN_LOOPBACKNET 127 #endif -+ /* If this is a directory, SET_ACL() will be called on the cleanup -+ * receive_generator() pass--if we called it here, we might clobber -+ * writability on the directory. everything else is OK to do now. */ -+ if (!S_ISDIR(st->st_mode)) { -+ if (SET_ACL(fname, file) == 0) -+ updated = 1; -+ } -+ - if (verbose > 1 && flags & PERMS_REPORT) { - enum logcode code = daemon_log_format_has_i || dry_run - ? FCLIENT : FINFO; ---- orig/rsync.h 2005-03-16 02:19:30 -+++ rsync.h 2004-07-03 20:11:58 -@@ -639,6 +639,40 @@ struct stats { - #include "lib/permstring.h" - #include "lib/addrinfo.h" ++#if HAVE_POSIX_ACLS|HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|\ ++ HAVE_HPUX_ACLS|HAVE_IRIX_ACLS|HAVE_AIX_ACLS|HAVE_TRU64_ACLS ++#define SUPPORT_ACLS 1 ++#endif ++ ++#if HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|HAVE_HPUX_ACLS ++#define ACLS_NEED_MASK 1 ++#endif ++ + #define GID_NONE ((gid_t)-1) + + #define HL_CHECK_MASTER 0 +@@ -646,6 +655,17 @@ struct stats { + + struct chmod_mode_struct; -+#define SUPPORT_ACLS HAVE_POSIX_ACLS|HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|\ -+ HAVE_HPUX_ACLS|HAVE_IRIX_ACLS|HAVE_AIX_ACLS|HAVE_TRU64_ACLS ++#define EMPTY_ITEM_LIST {NULL, 0, 0} ++ ++typedef struct { ++ void *items; ++ size_t count; ++ size_t malloced; ++} item_list; + -+#define ACLS_NEED_MASK HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|HAVE_HPUX_ACLS ++#define EXPAND_ITEM_LIST(lp, type, incr) \ ++ (type*)expand_item_list(lp, sizeof (type), #type, incr) + -+#if SUPPORT_ACLS -+#if HAVE_SYS_ACL_H -+#include + #include "byteorder.h" + #include "lib/mdfour.h" + #include "lib/wildmatch.h" +@@ -661,6 +681,16 @@ struct chmod_mode_struct; + + #define UNUSED(x) x __attribute__((__unused__)) + ++typedef struct { ++ STRUCT_STAT st; ++#ifdef SUPPORT_ACLS ++ struct rsync_acl *acc_acl; /* access ACL */ ++ struct rsync_acl *def_acl; /* default ACL */ +#endif -+#define MAKE_ACL(file, fname) make_acl(file, fname) -+#define SEND_ACL(file, f) send_acl(file, f) -+#define RECEIVE_ACL(file, f) receive_acl(file, f) -+#define SORT_FILE_ACL_INDEX_LISTS() sort_file_acl_index_lists() -+#define SET_ACL(fname, file) set_acl(fname, file) -+#define NEXT_ACL_UID() next_acl_uid() -+#define ACL_UID_MAP(uid) acl_uid_map(uid) -+#define PUSH_KEEP_BACKUP_ACL(file, orig, dest) \ -+ push_keep_backup_acl(file, orig, dest) -+#define CLEANUP_KEEP_BACKUP_ACL() cleanup_keep_backup_acl() -+#define DUP_ACL(orig, dest, mode) dup_acl(orig, dest, mode) -+#else /* SUPPORT_ACLS */ -+#define MAKE_ACL(file, fname) 1 /* checked return value */ -+#define SEND_ACL(file, f) -+#define RECEIVE_ACL(file, f) -+#define SORT_FILE_ACL_INDEX_LISTS() -+#define SET_ACL(fname, file) 0 /* checked return value */ -+#define NEXT_ACL_UID() -+#define ACL_UID_MAP(uid) -+#define PUSH_KEEP_BACKUP_ACL(file, orig, dest) -+#define CLEANUP_KEEP_BACKUP_ACL() -+#define DUP_ACL(src, orig, mode) 0 /* checked return value */ -+#endif /* SUPPORT_ACLS */ -+#include "smb_acls.h" ++} statx; ++ ++#define ACL_READY(sx) ((sx).acc_acl != NULL) + #include "proto.h" /* We have replacement versions of these if they're missing. */ ---- orig/rsync.yo 2005-03-16 02:19:30 -+++ rsync.yo 2004-07-03 20:11:58 -@@ -316,6 +316,7 @@ to the detailed description below for a +--- old/rsync.yo ++++ new/rsync.yo +@@ -321,6 +321,7 @@ to the detailed description below for a -H, --hard-links preserve hard links - -K, --keep-dirlinks treat symlinked dir on receiver as dir -p, --perms preserve permissions -+ -A, --acls preserve ACLs (implies -p) [local option] - -o, --owner preserve owner (root only) + -E, --executability preserve executability ++ -A, --acls preserve ACLs (implies -p) [non-standard] + --chmod=CHMOD change destination permissions + -o, --owner preserve owner (super-user only) -g, --group preserve group - -D, --devices preserve devices (root only) -@@ -624,6 +625,11 @@ source file's permissions and the umask - other files (including updated files) retain their existing permissions - (which is the same behavior as other file-copy utilities, such as cp). +@@ -744,7 +745,9 @@ quote(itemize( + permissions, though the bf(--executability) option might change just + the execute permission for the file. + it() New files get their "normal" permission bits set to the source +- file's permissions masked with the receiving end's umask setting, and ++ file's permissions masked with the receiving directory's default ++ permissions (either the receiving process's umask, or the permissions ++ specified via the destination directory's default ACL), and + their special permission bits disabled except in the case where a new + directory inherits a setgid bit from its parent directory. + )) +@@ -775,9 +778,11 @@ The preservation of the destination's se + directories when bf(--perms) is off was added in rsync 2.6.7. Older rsync + versions erroneously preserved the three special permission bits for + newly-created files when bf(--perms) was off, while overriding the +-destination's setgid bit setting on a newly-created directory. (Keep in +-mind that it is the version of the receiving rsync that affects this +-behavior.) ++destination's setgid bit setting on a newly-created directory. Default ACL ++observance was added to the ACL patch for rsync 2.6.7, so older (or ++non-ACL-enabled) rsyncs use the umask even if default ACLs are present. ++(Keep in mind that it is the version of the receiving rsync that affects ++these behaviors.) + + dit(bf(-E, --executability)) This option causes rsync to preserve the + executability (or non-executability) of regular files when bf(--perms) is +@@ -795,6 +800,15 @@ quote(itemize( + + If bf(--perms) is enabled, this option is ignored. -+dit(bf(-A, --acls)) This option causes rsync to update the remote -+ACLs to be the same as the local ACLs. This will work only if the -+remote machine's rsync supports this option also. This is a non-standard -+option. -+ - dit(bf(-o, --owner)) This option causes rsync to set the owner of the - destination file to be the same as the source file. On most systems, - only the super-user can set file ownership. By default, the preservation ---- orig/smb_acls.h 2004-06-30 00:04:07 -+++ smb_acls.h 2004-06-30 00:04:07 -@@ -0,0 +1,277 @@ ++dit(bf(-A, --acls)) This option causes rsync to update the destination ++ACLs to be the same as the source ACLs. This nonstandard option only ++works if the remote rsync also supports it. bf(--acls) implies bf(--perms). ++ ++Note also that an optimization of the ACL-sending protocol used by this ++version makes it incompatible with sending files to an older ACL-enabled ++rsync unless you double the bf(--acls) option (e.g. bf(-AA)). This ++doubling is not needed when pulling files from an older rsync. ++ + dit(bf(--chmod)) This option tells rsync to apply one or more + comma-separated "chmod" strings to the permission of the files in the + transfer. The resulting value is treated as though it was the permissions +@@ -1376,8 +1390,8 @@ if the receiving rsync is at least versi + with older versions of rsync, but that also turns on the output of other + verbose messages). + +-The "%i" escape has a cryptic output that is 9 letters long. The general +-format is like the string bf(YXcstpogz), where bf(Y) is replaced by the ++The "%i" escape has a cryptic output that is 11 letters long. The general ++format is like the string bf(YXcstpoguax), where bf(Y) is replaced by the + type of update being done, bf(X) is replaced by the file-type, and the + other letters represent attributes that may be output if they are being + modified. +@@ -1426,7 +1440,11 @@ quote(itemize( + sender's value (requires bf(--owner) and super-user privileges). + it() A bf(g) means the group is different and is being updated to the + sender's value (requires bf(--group) and the authority to set the group). +- it() The bf(z) slot is reserved for future use. ++ it() The bf(u) slot is reserved for reporting update (access) time changes ++ (a feature that is not yet released). ++ it() The bf(a) means that the ACL information changed. ++ it() The bf(x) slot is reserved for reporting extended attribute changes ++ (a feature that is not yet released). + )) + + One other output is possible: when deleting files, the "%i" will output +--- old/smb_acls.h ++++ new/smb_acls.h +@@ -0,0 +1,281 @@ +/* + Unix SMB/Netbios implementation. + Version 2.2.x @@ -4797,7 +5998,7 @@ ACLs to a non-ACL-supporting disk should complain. +#ifndef _SMB_ACLS_H +#define _SMB_ACLS_H + -+#if defined(HAVE_POSIX_ACLS) ++#if defined HAVE_POSIX_ACLS + +/* This is an identity mapping (just remove the SMB_). */ + @@ -4827,7 +6028,7 @@ ACLs to a non-ACL-supporting disk should complain. +#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS +#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT + -+#elif defined(HAVE_TRU64_ACLS) ++#elif defined HAVE_TRU64_ACLS + +/* This is for DEC/Compaq Tru64 UNIX */ + @@ -4857,7 +6058,7 @@ ACLs to a non-ACL-supporting disk should complain. +#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS +#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT + -+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS) ++#elif defined HAVE_UNIXWARE_ACLS || defined HAVE_SOLARIS_ACLS +/* + * Donated by Michael Davidson for UnixWare / OpenUNIX. + * Modified by Toomas Soome for Solaris. @@ -4895,7 +6096,11 @@ ACLs to a non-ACL-supporting disk should complain. +#define SMB_ACL_TYPE_ACCESS 0 +#define SMB_ACL_TYPE_DEFAULT 1 + -+#elif defined(HAVE_HPUX_ACLS) ++#ifdef __CYGWIN__ ++#define SMB_ACL_LOSES_SPECIAL_MODE_BITS ++#endif ++ ++#elif defined HAVE_HPUX_ACLS + +/* + * Based on the Solaris & UnixWare code. @@ -4936,7 +6141,7 @@ ACLs to a non-ACL-supporting disk should complain. +#define SMB_ACL_TYPE_ACCESS 0 +#define SMB_ACL_TYPE_DEFAULT 1 + -+#elif defined(HAVE_IRIX_ACLS) ++#elif defined HAVE_IRIX_ACLS + +#define SMB_ACL_TAG_T acl_tag_t +#define SMB_ACL_TYPE_T acl_type_t @@ -4968,7 +6173,7 @@ ACLs to a non-ACL-supporting disk should complain. +#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS +#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT + -+#elif defined(HAVE_AIX_ACLS) ++#elif defined HAVE_AIX_ACLS + +/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */ + @@ -5050,9 +6255,390 @@ ACLs to a non-ACL-supporting disk should complain. + +#endif /* No ACLs. */ +#endif /* _SMB_ACLS_H */ ---- orig/uidlist.c 2005-02-14 00:53:44 -+++ uidlist.c 2004-07-03 20:11:58 -@@ -34,6 +34,7 @@ +--- old/t_stub.c ++++ new/t_stub.c +@@ -79,3 +79,7 @@ struct filter_list_struct server_filter_ + return NULL; + } + ++ const char *who_am_i(void) ++{ ++ return "test"; ++} +--- old/testsuite/acls.test ++++ new/testsuite/acls.test +@@ -0,0 +1,34 @@ ++#! /bin/sh ++ ++# This program is distributable under the terms of the GNU GPL (see ++# COPYING). ++ ++# Test that rsync handles basic ACL preservation. ++ ++. $srcdir/testsuite/rsync.fns ++ ++$RSYNC --version | grep ", ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support" ++case "$setfacl_nodef" in ++true) test_skipped "I don't know how to use your setfacl command" ;; ++esac ++ ++makepath "$fromdir/foo" ++echo something >"$fromdir/file1" ++echo else >"$fromdir/file2" ++ ++files='foo file1 file2' ++ ++setfacl -m u:0:7 "$fromdir/foo" || test_skipped "Your filesystem has ACLs disabled" ++setfacl -m u:0:5 "$fromdir/file1" ++setfacl -m u:0:5 "$fromdir/file2" ++ ++$RSYNC -avvA "$fromdir/" "$todir/" ++ ++cd "$fromdir" ++getfacl $files >"$scratchdir/acls.txt" ++ ++cd "$todir" ++getfacl $files | diff $diffopt "$scratchdir/acls.txt" - ++ ++# The script would have aborted on error, so getting here means we've won. ++exit 0 +--- old/testsuite/default-acls.test ++++ new/testsuite/default-acls.test +@@ -0,0 +1,65 @@ ++#! /bin/sh ++ ++# This program is distributable under the terms of the GNU GPL (see ++# COPYING). ++ ++# Test that rsync obeys default ACLs. -- Matt McCutchen ++ ++. $srcdir/testsuite/rsync.fns ++ ++$RSYNC --version | grep ", ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support" ++case "$setfacl_nodef" in ++true) test_skipped "I don't know how to use your setfacl command" ;; ++*-k*) opts='-dm u::7,g::5,o:5' ;; ++*) opts='-m d:u::7,d:g::5,d:o:5' ;; ++esac ++setfacl $opts "$scratchdir" || test_skipped "Your filesystem has ACLs disabled" ++ ++# Call as: testit ++testit() { ++ todir="$scratchdir/$1" ++ mkdir "$todir" ++ $setfacl_nodef "$todir" ++ if [ "$2" ]; then ++ case "$setfacl_nodef" in ++ *-k*) opts="-dm $2" ;; ++ *) opts="-m `echo $2 | sed 's/\([ugom]:\)/d:\1/g'`" ++ esac ++ setfacl $opts "$todir" ++ fi ++ # Make sure we obey ACLs when creating a directory to hold multiple transferred files, ++ # even though the directory itself is outside the transfer ++ $RSYNC -rvv "$scratchdir/dir" "$scratchdir/file" "$scratchdir/program" "$todir/to/" ++ check_perms "$todir/to" $4 "Target $1" ++ check_perms "$todir/to/dir" $4 "Target $1" ++ check_perms "$todir/to/file" $3 "Target $1" ++ check_perms "$todir/to/program" $4 "Target $1" ++ # Make sure get_local_name doesn't mess us up when transferring only one file ++ $RSYNC -rvv "$scratchdir/file" "$todir/to/anotherfile" ++ check_perms "$todir/to/anotherfile" $3 "Target $1" ++ # Make sure we obey default ACLs when not transferring a regular file ++ $RSYNC -rvv "$scratchdir/dir/" "$todir/to/anotherdir/" ++ check_perms "$todir/to/anotherdir" $4 "Target $1" ++} ++ ++mkdir "$scratchdir/dir" ++echo "File!" >"$scratchdir/file" ++echo "#!/bin/sh" >"$scratchdir/program" ++chmod 777 "$scratchdir/dir" ++chmod 666 "$scratchdir/file" ++chmod 777 "$scratchdir/program" ++ ++# Test some target directories ++umask 0077 ++testit da777 u::7,g::7,o:7 rw-rw-rw- rwxrwxrwx ++testit da775 u::7,g::7,o:5 rw-rw-r-- rwxrwxr-x ++testit da750 u::7,g::5,o:0 rw-r----- rwxr-x--- ++testit da770mask u::7,u:0:7,g::0,m:7,o:0 rw-rw---- rwxrwx--- ++testit noda1 '' rw------- rwx------ ++umask 0000 ++testit noda2 '' rw-rw-rw- rwxrwxrwx ++umask 0022 ++testit noda3 '' rw-r--r-- rwxr-xr-x ++ ++# Hooray ++exit 0 +--- old/testsuite/devices.test ++++ new/testsuite/devices.test +@@ -42,14 +42,14 @@ touch -r "$fromdir/block" "$fromdir/bloc + $RSYNC -ai "$fromdir/block" "$todir/block2" \ + | tee "$outfile" + cat <"$chkfile" +-cD+++++++ block ++cD+++++++++ block + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed" + + $RSYNC -ai "$fromdir/block2" "$todir/block" \ + | tee "$outfile" + cat <"$chkfile" +-cD+++++++ block2 ++cD+++++++++ block2 + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 2 failed" + +@@ -58,7 +58,7 @@ sleep 1 + $RSYNC -Di "$fromdir/block3" "$todir/block" \ + | tee "$outfile" + cat <"$chkfile" +-cD..T.... block3 ++cD..T...... block3 + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed" + +@@ -66,15 +66,15 @@ $RSYNC -aiHvv "$fromdir/" "$todir/" \ + | tee "$outfile" + filter_outfile + cat <"$chkfile" +-.d..t.... ./ +-cD..t.... block +-cD....... block2 +-cD+++++++ block3 +-hD+++++++ block2.5 => block3 +-cD+++++++ char +-cD+++++++ char2 +-cD+++++++ char3 +-cS+++++++ fifo ++.d..t...... ./ ++cD..t...... block ++cD......... block2 ++cD+++++++++ block3 ++hD+++++++++ block2.5 => block3 ++cD+++++++++ char ++cD+++++++++ char2 ++cD+++++++++ char3 ++cS+++++++++ fifo + EOT + if test ! -b "$fromdir/block2.5"; then + sed -e '/block2\.5/d' \ +--- old/testsuite/itemize.test ++++ new/testsuite/itemize.test +@@ -29,14 +29,14 @@ ln "$fromdir/foo/config1" "$fromdir/foo/ + $RSYNC -iplr "$fromdir/" "$todir/" \ + | tee "$outfile" + cat <"$chkfile" +-cd+++++++ bar/ +-cd+++++++ bar/baz/ +->f+++++++ bar/baz/rsync +-cd+++++++ foo/ +->f+++++++ foo/config1 +->f+++++++ foo/config2 +->f+++++++ foo/extra +-cL+++++++ foo/sym -> ../bar/baz/rsync ++cd+++++++++ bar/ ++cd+++++++++ bar/baz/ ++>f+++++++++ bar/baz/rsync ++cd+++++++++ foo/ ++>f+++++++++ foo/config1 ++>f+++++++++ foo/config2 ++>f+++++++++ foo/extra ++cL+++++++++ foo/sym -> ../bar/baz/rsync + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed" + +@@ -48,10 +48,10 @@ chmod 601 "$fromdir/foo/config2" + $RSYNC -iplrH "$fromdir/" "$todir/" \ + | tee "$outfile" + cat <"$chkfile" +->f..T.... bar/baz/rsync +->f..T.... foo/config1 +->f.sTp... foo/config2 +-hf..T.... foo/extra => foo/config1 ++>f..T...... bar/baz/rsync ++>f..T...... foo/config1 ++>f.sTp..... foo/config2 ++hf..T...... foo/extra => foo/config1 + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 2 failed" + +@@ -68,11 +68,11 @@ chmod 777 "$todir/bar/baz/rsync" + $RSYNC -iplrtc "$fromdir/" "$todir/" \ + | tee "$outfile" + cat <"$chkfile" +-.f..tp... bar/baz/rsync +-.d..t.... foo/ +-.f..t.... foo/config1 +->fcstp... foo/config2 +-cL..T.... foo/sym -> ../bar/baz/rsync ++.f..tp..... bar/baz/rsync ++.d..t...... foo/ ++.f..t...... foo/config1 ++>fcstp..... foo/config2 ++cL..T...... foo/sym -> ../bar/baz/rsync + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed" + +@@ -97,15 +97,15 @@ $RSYNC -ivvplrtH "$fromdir/" "$todir/" \ + | tee "$outfile" + filter_outfile + cat <"$chkfile" +-.d ./ +-.d bar/ +-.d bar/baz/ +-.f...p... bar/baz/rsync +-.d foo/ +-.f foo/config1 +->f..t.... foo/config2 +-hf foo/extra +-.L foo/sym -> ../bar/baz/rsync ++.d ./ ++.d bar/ ++.d bar/baz/ ++.f...p..... bar/baz/rsync ++.d foo/ ++.f foo/config1 ++>f..t...... foo/config2 ++hf foo/extra ++.L foo/sym -> ../bar/baz/rsync + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 5 failed" + +@@ -124,8 +124,8 @@ touch "$todir/foo/config2" + $RSYNC -iplrtH "$fromdir/" "$todir/" \ + | tee "$outfile" + cat <"$chkfile" +-.f...p... foo/config1 +->f..t.... foo/config2 ++.f...p..... foo/config1 ++>f..t...... foo/config2 + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 7 failed" + +@@ -134,15 +134,15 @@ $RSYNC -ivvplrtH --copy-dest="$lddir" "$ + | tee "$outfile" + filter_outfile + cat <"$chkfile" +-.d..t.... ./ +-cd+++++++ bar/ +-cd+++++++ bar/baz/ +-cf bar/baz/rsync +-cd+++++++ foo/ +-cf foo/config1 +-cf foo/config2 +-hf foo/extra => foo/config1 +-cL..T.... foo/sym -> ../bar/baz/rsync ++.d..t...... ./ ++cd+++++++++ bar/ ++cd+++++++++ bar/baz/ ++cf bar/baz/rsync ++cd+++++++++ foo/ ++cf foo/config1 ++cf foo/config2 ++hf foo/extra => foo/config1 ++cL..T...... foo/sym -> ../bar/baz/rsync + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 8 failed" + +@@ -150,11 +150,11 @@ rm -rf "$todir" + $RSYNC -iplrtH --copy-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" + cat <"$chkfile" +-.d..t.... ./ +-cd+++++++ bar/ +-cd+++++++ bar/baz/ +-cd+++++++ foo/ +-hf foo/extra => foo/config1 ++.d..t...... ./ ++cd+++++++++ bar/ ++cd+++++++++ bar/baz/ ++cd+++++++++ foo/ ++hf foo/extra => foo/config1 + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 9 failed" + +@@ -181,15 +181,15 @@ $RSYNC -ivvplrtH --link-dest="$lddir" "$ + | tee "$outfile" + filter_outfile + cat <"$chkfile" +-.d..t.... ./ +-cd+++++++ bar/ +-cd+++++++ bar/baz/ +-hf bar/baz/rsync +-cd+++++++ foo/ +-hf foo/config1 +-hf foo/config2 +-hf foo/extra => foo/config1 +-hL foo/sym -> ../bar/baz/rsync ++.d..t...... ./ ++cd+++++++++ bar/ ++cd+++++++++ bar/baz/ ++hf bar/baz/rsync ++cd+++++++++ foo/ ++hf foo/config1 ++hf foo/config2 ++hf foo/extra => foo/config1 ++hL foo/sym -> ../bar/baz/rsync + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 11 failed" + +@@ -197,10 +197,10 @@ rm -rf "$todir" + $RSYNC -iplrtH --link-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" + cat <"$chkfile" +-.d..t.... ./ +-cd+++++++ bar/ +-cd+++++++ bar/baz/ +-cd+++++++ foo/ ++.d..t...... ./ ++cd+++++++++ bar/ ++cd+++++++++ bar/baz/ ++cd+++++++++ foo/ + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 12 failed" + +@@ -228,14 +228,14 @@ filter_outfile + # TODO fix really-old problem when combining -H with --compare-dest: + # missing output for foo/extra hard-link (and it might not be updated)! + cat <"$chkfile" +-.d..t.... ./ +-cd+++++++ bar/ +-cd+++++++ bar/baz/ +-.f bar/baz/rsync +-cd+++++++ foo/ +-.f foo/config1 +-.f foo/config2 +-.L foo/sym -> ../bar/baz/rsync ++.d..t...... ./ ++cd+++++++++ bar/ ++cd+++++++++ bar/baz/ ++.f bar/baz/rsync ++cd+++++++++ foo/ ++.f foo/config1 ++.f foo/config2 ++.L foo/sym -> ../bar/baz/rsync + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 14 failed" + +@@ -243,10 +243,10 @@ rm -rf "$todir" + $RSYNC -iplrtH --compare-dest="$lddir" "$fromdir/" "$todir/" \ + | tee "$outfile" + cat <"$chkfile" +-.d..t.... ./ +-cd+++++++ bar/ +-cd+++++++ bar/baz/ +-cd+++++++ foo/ ++.d..t...... ./ ++cd+++++++++ bar/ ++cd+++++++++ bar/baz/ ++cd+++++++++ foo/ + EOT + diff $diffopt "$chkfile" "$outfile" || test_fail "test 15 failed" + +--- old/uidlist.c ++++ new/uidlist.c +@@ -35,6 +35,7 @@ extern int verbose; extern int preserve_uid; extern int preserve_gid; @@ -5060,7 +6646,7 @@ ACLs to a non-ACL-supporting disk should complain. extern int numeric_ids; extern int am_root; -@@ -274,7 +275,7 @@ void send_uid_list(int f) +@@ -275,7 +276,7 @@ void send_uid_list(int f) if (numeric_ids) return; @@ -5069,7 +6655,7 @@ ACLs to a non-ACL-supporting disk should complain. int len; /* we send sequences of uid/byte-length/name */ for (list = uidlist; list; list = list->next) { -@@ -291,7 +292,7 @@ void send_uid_list(int f) +@@ -292,7 +293,7 @@ void send_uid_list(int f) write_int(f, 0); } @@ -5078,7 +6664,7 @@ ACLs to a non-ACL-supporting disk should complain. int len; for (list = gidlist; list; list = list->next) { if (!list->name) -@@ -312,7 +313,7 @@ void recv_uid_list(int f, struct file_li +@@ -313,7 +314,7 @@ void recv_uid_list(int f, struct file_li int id, i; char *name; @@ -5088,30 +6674,62 @@ ACLs to a non-ACL-supporting disk should complain. while ((id = read_int(f)) != 0) { int len = read_byte(f); @@ -325,7 +326,7 @@ void recv_uid_list(int f, struct file_li + } } - - if (preserve_gid && !numeric_ids) { + if ((preserve_gid || preserve_acls) && !numeric_ids) { /* read the gid list */ while ((id = read_int(f)) != 0) { int len = read_byte(f); -@@ -337,6 +338,18 @@ void recv_uid_list(int f, struct file_li +@@ -337,6 +338,16 @@ void recv_uid_list(int f, struct file_li } } -+#if SUPPORT_ACLS ++#ifdef SUPPORT_ACLS + if (preserve_acls && !numeric_ids) { -+ id_t id; -+ /* the enumerations don't return 0 except to flag the last -+ * entry, since uidlist doesn't munge 0 anyway */ -+ while ((id = next_acl_uid(flist))) -+ acl_uid_map(match_uid(id)); -+ while ((id = next_acl_gid(flist))) -+ acl_gid_map(match_gid(id)); ++ id_t *id; ++ while ((id = next_acl_uid(flist)) != NULL) ++ *id = match_uid(*id); ++ while ((id = next_acl_gid(flist)) != NULL) ++ *id = match_gid(*id); + } -+#endif /* SUPPORT_ACLS */ ++#endif + - /* now convert the uid/gid of all files in the list to the mapped - * uid/gid */ + /* Now convert all the uids/gids from sender values to our values. */ if (am_root && preserve_uid && !numeric_ids) { + for (i = 0; i < flist->count; i++) +--- old/util.c ++++ new/util.c +@@ -1555,3 +1555,31 @@ int bitbag_next_bit(struct bitbag *bb, i + + return -1; + } ++ ++void *expand_item_list(item_list *lp, size_t item_size, ++ const char *desc, int incr) ++{ ++ /* First time through, 0 <= 0, so list is expanded. */ ++ if (lp->malloced <= lp->count) { ++ void *new_ptr; ++ size_t new_size = lp->malloced; ++ if (incr < 0) ++ new_size -= incr; /* increase slowly */ ++ else if (new_size < (size_t)incr) ++ new_size += incr; ++ else ++ new_size *= 2; ++ new_ptr = realloc_array(lp->items, char, new_size * item_size); ++ if (verbose >= 4) { ++ rprintf(FINFO, "[%s] expand %s to %.0f bytes, did%s move\n", ++ who_am_i(), desc, (double)new_size * item_size, ++ new_ptr == lp->items ? " not" : ""); ++ } ++ if (!new_ptr) ++ out_of_memory("expand_item_list"); ++ ++ lp->items = new_ptr; ++ lp->malloced = new_size; ++ } ++ return (char*)lp->items + (lp->count++ * item_size); ++}