The ACL support has arrived! This version has a brand new protocol
authorWayne Davison <wayned@samba.org>
Sun, 11 Mar 2007 00:13:34 +0000 (00:13 +0000)
committerWayne Davison <wayned@samba.org>
Sun, 11 Mar 2007 00:13:34 +0000 (00:13 +0000)
that makes it incompatible with all prior versions.  A patch will be
provided to allow talking with older (patched) rsync versions.

25 files changed:
Makefile.in
acls.c [new file with mode: 0644]
backup.c
byteorder.h
compat.c
configure.in
flist.c
generator.c
hlink.c
io.c
lib/sysacls.c [new file with mode: 0644]
lib/sysacls.h [new file with mode: 0644]
log.c
options.c
receiver.c
rsync.c
rsync.h
rsync.yo
smb_acls.h [new file with mode: 0644]
testsuite/acls.test [new file with mode: 0644]
testsuite/default-acls.test [new file with mode: 0644]
testsuite/devices.test
testsuite/itemize.test
uidlist.c
util.c

index e9bd2c1..c1c5510 100644 (file)
@@ -26,15 +26,15 @@ VERSION=@VERSION@
 .SUFFIXES:
 .SUFFIXES: .c .o
 
 .SUFFIXES:
 .SUFFIXES: .c .o
 
-HEADERS=byteorder.h config.h errcode.h proto.h rsync.h lib/pool_alloc.h
+HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \
 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/inffast.o zlib/inflate.o zlib/inftrees.o \
        zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
 OBJS1=flist.o 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 io.o compat.o hlink.o token.o uidlist.o socket.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=flist.o 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 io.o compat.o hlink.o token.o uidlist.o socket.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 \
 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 \
diff --git a/acls.c b/acls.c
new file mode 100644 (file)
index 0000000..e73eec3
--- /dev/null
+++ b/acls.c
@@ -0,0 +1,1085 @@
+/*
+ * 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"
+
+#ifdef SUPPORT_ACLS
+
+extern int dry_run;
+extern int am_root;
+extern int read_only;
+extern int list_only;
+extern int orig_umask;
+extern int protocol_version;
+extern int numeric_ids;
+extern int inc_recurse;
+
+#define XMIT_USER_OBJ (1<<0)
+#define XMIT_USER_LIST (1<<1)
+#define XMIT_GROUP_OBJ (1<<2)
+#define XMIT_GROUP_LIST (1<<3)
+#define XMIT_MASK_OBJ (1<<4)
+#define XMIT_OTHER_OBJ (1<<5)
+
+/* === ACL structures === */
+
+typedef struct {
+       id_t id;
+       uchar access;
+} id_access;
+
+typedef struct {
+       id_access *idas;
+       int count;
+} ida_entries;
+
+typedef struct {
+       char *name;
+       uchar len;
+} idname;
+
+#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_obj;
+       uchar other_obj;
+} rsync_acl;
+
+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 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";
+}
+
+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_obj != 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_obj != NO_ENTRY ? racl->mask_obj : racl->group_obj) << 3)
+            + racl->other_obj;
+}
+
+/* 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_obj == NO_ENTRY)
+               racl->group_obj = NO_ENTRY;
+       else {
+               if (racl->group_obj == racl->mask_obj)
+                       racl->group_obj = NO_ENTRY;
+               racl->mask_obj = NO_ENTRY;
+       }
+       racl->other_obj = 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_obj = mode & 7;
+}
+
+/* === Rsync ACL functions === */
+
+static rsync_acl *create_racl(void)
+{
+       rsync_acl *racl = new(rsync_acl);
+
+       if (!racl)
+               out_of_memory("create_racl");
+       *racl = empty_rsync_acl;
+
+       return racl;
+}
+
+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;
+}
+
+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_obj == racl2->mask_obj
+           && racl1->other_obj == racl2->other_obj
+           && 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_obj ^ racl2->mask_obj) & 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_obj != 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)
+{
+       if (racl->users.idas)
+               free(racl->users.idas);
+       if (racl->groups.idas)
+               free(racl->groups.idas);
+       *racl = empty_rsync_acl;
+}
+
+void free_acl(statx *sxp)
+{
+       if (sxp->acc_acl) {
+               rsync_acl_free(sxp->acc_acl);
+               free(sxp->acc_acl);
+               sxp->acc_acl = NULL;
+       }
+       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_ida_entries(ida_entries *idal)
+{
+       if (!idal->count)
+               return;
+       qsort(idal->idas, idal->count, sizeof idal->idas[0], id_access_sorter);
+}
+
+/* === System ACLs === */
+
+/* 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(rsync_acl *racl, SMB_ACL_TAG_T type, item_list *temp_ida_list)
+{
+       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;
+}
+
+/* Unpack system ACL -> rsync ACL verbatim.  Return whether we succeeded. */
+static BOOL unpack_smb_acl(SMB_ACL_T sacl, rsync_acl *racl)
+{
+       static item_list temp_ida_list = EMPTY_ITEM_LIST;
+       SMB_ACL_TAG_T prior_list_type = 0;
+       SMB_ACL_ENTRY_T entry;
+       const char *errfun;
+       int rc;
+
+       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;
+               id_access *ida;
+               if ((rc = sys_acl_get_tag_type(entry, &tag_type))) {
+                       errfun = "sys_acl_get_tag_type";
+                       break;
+               }
+               if ((rc = sys_acl_get_permset(entry, &permset))) {
+                       errfun = "sys_acl_get_tag_type";
+                       break;
+               }
+               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_obj == NO_ENTRY)
+                               racl->mask_obj = access;
+                       else
+                               rprintf(FINFO, "unpack_smb_acl: warning: duplicate MASK entry ignored\n");
+                       continue;
+               case SMB_ACL_OTHER:
+                       if (racl->other_obj == NO_ENTRY)
+                               racl->other_obj = 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))) {
+                       errfun = "sys_acl_get_tag_type";
+                       rc = EINVAL;
+                       break;
+               }
+               if (tag_type != prior_list_type) {
+                       if (prior_list_type)
+                               save_idas(racl, prior_list_type, &temp_ida_list);
+                       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) {
+               rsyserr(FERROR, errno, "unpack_smb_acl: %s()", errfun);
+               rsync_acl_free(racl);
+               return False;
+       }
+       if (prior_list_type)
+               save_idas(racl, prior_list_type, &temp_ida_list);
+
+       sort_ida_entries(&racl->users);
+       sort_ida_entries(&racl->groups);
+
+#ifdef ACLS_NEED_MASK
+       if (!racl->users.count && !racl->groups.count && racl->mask_obj != NO_ENTRY) {
+               /* Throw away a superfluous mask, but mask off the
+                * group perms with it first. */
+               racl->group_obj &= racl->mask_obj;
+               racl->mask_obj = NO_ENTRY;
+       }
+#endif
+
+       return True;
+}
+
+/* Synactic sugar for system calls */
+
+#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)
+
+/* Store the permissions in the system ACL entry. */
+static int store_access_in_entry(uchar access, SMB_ACL_ENTRY_T entry)
+{
+       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;
+}
+
+/* Pack rsync ACL -> system ACL verbatim.  Return whether we succeeded. */
+static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl)
+{
+#ifdef ACLS_NEED_MASK
+       uchar mask_bits;
+#endif
+       size_t count;
+       id_access *ida;
+       const char *errfun = NULL;
+       SMB_ACL_ENTRY_T entry;
+
+       if (!(*smb_acl = sys_acl_init(calc_sacl_entries(racl)))) {
+               rsyserr(FERROR, errno, "pack_smb_acl: sys_acl_init()");
+               return False;
+       }
+
+       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) );
+       }
+
+       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) );
+       }
+
+#ifdef ACLS_NEED_MASK
+       mask_bits = racl->mask_obj == NO_ENTRY ? racl->group_obj & 7 : racl->mask_obj;
+       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_obj != 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_obj, 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_obj & 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(const rsync_acl *racl, SMB_ACL_TYPE_T type,
+                                  const item_list *racl_list)
+{
+       static int access_match = -1, default_match = -1;
+       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
+        * time, then start at the end of the list, which should be the
+        * best place to start hunting. */
+       if (*match == -1)
+               *match = racl_list->count - 1;
+       while (count--) {
+               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;
+}
+
+static int get_rsync_acl(const char *fname, rsync_acl *racl,
+                        SMB_ACL_TYPE_T type, mode_t mode)
+{
+       SMB_ACL_T sacl = sys_acl_get_file(fname, type);
+
+       if (sacl) {
+               BOOL ok = unpack_smb_acl(sacl, racl);
+
+               sys_acl_free_acl(sacl);
+               if (!ok) {
+                       return -1;
+               }
+       } else if (errno == ENOTSUP || errno == ENOSYS) {
+               /* ACLs are not supported, so pretend we have a basic ACL. */
+               if (type == SMB_ACL_TYPE_ACCESS)
+                       rsync_acl_fake_perms(racl, mode);
+       } else {
+               rsyserr(FERROR, errno, "get_acl: sys_acl_get_file(%s, %s)",
+                       fname, str_acl_type(type));
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Return the Access Control List for the given filename. */
+int get_acl(const char *fname, statx *sxp)
+{
+       sxp->acc_acl = create_racl();
+       if (get_rsync_acl(fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS,
+                         sxp->st.st_mode) < 0) {
+               free_acl(sxp);
+               return -1;
+       }
+
+       if (S_ISDIR(sxp->st.st_mode)) {
+               sxp->def_acl = create_racl();
+               if (get_rsync_acl(fname, sxp->def_acl, SMB_ACL_TYPE_DEFAULT,
+                                 sxp->st.st_mode) < 0) {
+                       free_acl(sxp);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/* === Send functions === */
+
+/* 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(const ida_entries *idal, int user_names, int f)
+{
+       id_access *ida;
+       size_t count = idal->count;
+
+       write_abbrevint(f, idal->count);
+
+       for (ida = idal->idas; count--; ida++) {
+               char *name = user_names ? add_uid(ida->id) : add_gid(ida->id);
+               write_abbrevint(f, ida->id);
+               if (inc_recurse && name) {
+                       int len = strlen(name);
+                       write_byte(f, ida->access | (uchar)0x80);
+                       write_byte(f, len);
+                       write_buf(f, name, len);
+               } else
+                       write_byte(f, ida->access);
+       }
+}
+
+static void send_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type,
+                          item_list *racl_list, int f)
+{
+       int ndx = find_matching_rsync_acl(racl, type, racl_list);
+
+       /* Send 0 (-1 + 1) to indicate that literal ACL data follows. */
+       write_abbrevint(f, ndx + 1);
+
+       if (ndx < 0) {
+               rsync_acl *new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000);
+               uchar flags = 0;
+
+               if (racl->user_obj != NO_ENTRY)
+                       flags |= XMIT_USER_OBJ;
+               if (racl->users.count)
+                       flags |= XMIT_USER_LIST;
+
+               if (racl->group_obj != NO_ENTRY)
+                       flags |= XMIT_GROUP_OBJ;
+               if (racl->groups.count)
+                       flags |= XMIT_GROUP_LIST;
+
+               if (racl->mask_obj != NO_ENTRY)
+                       flags |= XMIT_MASK_OBJ;
+
+               if (racl->other_obj != NO_ENTRY)
+                       flags |= XMIT_OTHER_OBJ;
+
+               write_byte(f, flags);
+
+               if (flags & XMIT_USER_OBJ)
+                       write_byte(f, racl->user_obj);
+               if (flags & XMIT_USER_LIST)
+                       send_ida_entries(&racl->users, 1, f);
+
+               if (flags & XMIT_GROUP_OBJ)
+                       write_byte(f, racl->group_obj);
+               if (flags & XMIT_GROUP_LIST)
+                       send_ida_entries(&racl->groups, 0, f);
+
+               if (flags & XMIT_MASK_OBJ)
+                       write_byte(f, racl->mask_obj);
+
+               if (flags & XMIT_OTHER_OBJ)
+                       write_byte(f, racl->other_obj);
+
+               /* Give the allocated data to the new list object. */
+               *new_racl = *racl;
+               *racl = empty_rsync_acl;
+       }
+}
+
+/* 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)
+{
+
+       if (!sxp->acc_acl) {
+               sxp->acc_acl = create_racl();
+               rsync_acl_fake_perms(sxp->acc_acl, sxp->st.st_mode);
+       }
+       /* Avoid sending values that can be inferred from other data. */
+       rsync_acl_strip_perms(sxp->acc_acl);
+
+       send_rsync_acl(sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list, f);
+
+       if (S_ISDIR(sxp->st.st_mode)) {
+               if (!sxp->def_acl)
+                       sxp->def_acl = create_racl();
+
+               send_rsync_acl(sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list, f);
+       }
+}
+
+/* === Receive functions === */
+
+static uchar recv_acl_access(uchar *name_follows_val, int f)
+{
+       uchar access = read_byte(f);
+
+       if (name_follows_val) {
+               if (access & (uchar)0x80) {
+                       access &= ~(uchar)0x80;
+                       *name_follows_val = 1;
+               } else
+                       *name_follows_val = 0;
+       }
+
+       if (access & ~(4 | 2 | 1)) {
+               rprintf(FERROR, "recv_acl_access: bogus permset %o\n", access);
+               exit_cleanup(RERR_STREAMIO);
+       }
+
+       return access;
+}
+
+static uchar recv_ida_entries(ida_entries *ent, int user_names, int f)
+{
+       uchar computed_mask_bits = 0;
+       uchar has_name;
+       int i, count = read_abbrevint(f);
+
+       if (count) {
+               if (!(ent->idas = new_array(id_access, ent->count)))
+                       out_of_memory("recv_ida_entries");
+       } else
+               ent->idas = NULL;
+
+       ent->count = count;
+
+       for (i = 0; i < count; i++) {
+               id_t id = read_abbrevint(f);
+               int access = recv_acl_access(&has_name, f);
+
+               if (has_name) {
+                       if (user_names)
+                               id = recv_user_name(f, id);
+                       else
+                               id = recv_group_name(f, id);
+               } else if (user_names) {
+                       if (inc_recurse && am_root && !numeric_ids)
+                               id = match_uid(id);
+               } else {
+                       if (inc_recurse && (!am_root || !numeric_ids))
+                               id = match_gid(id);
+               }
+
+               ent->idas[i].id = id;
+               ent->idas[i].access = access;
+               computed_mask_bits |= access;
+       }
+
+       return computed_mask_bits;
+}
+
+static int recv_rsync_acl(item_list *racl_list, SMB_ACL_TYPE_T type, int f)
+{
+       uchar computed_mask_bits = 0;
+       acl_duo *duo_item;
+       uchar flags;
+       int ndx = read_abbrevint(f);
+
+       if (ndx < 0 || (size_t)ndx > racl_list->count) {
+               rprintf(FERROR, "recv_acl_index: %s ACL index %d > %d\n",
+                       str_acl_type(type), ndx, racl_list->count);
+               exit_cleanup(RERR_STREAMIO);
+       }
+
+       if (ndx != 0)
+               return ndx - 1;
+       
+       ndx = racl_list->count;
+       duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
+       duo_item->racl = empty_rsync_acl;
+
+       flags = read_byte(f);
+
+       if (flags & XMIT_USER_OBJ)
+               duo_item->racl.user_obj = recv_acl_access(NULL, f);
+
+       if (flags & XMIT_USER_LIST)
+               computed_mask_bits |= recv_ida_entries(&duo_item->racl.users, 1, f);
+
+       if (flags & XMIT_GROUP_OBJ)
+               duo_item->racl.group_obj = recv_acl_access(NULL, f);
+
+       if (flags & XMIT_GROUP_LIST)
+               computed_mask_bits |= recv_ida_entries(&duo_item->racl.groups, 0, f);
+
+       if (flags & XMIT_MASK_OBJ)
+               duo_item->racl.mask_obj = recv_acl_access(NULL, f);
+
+       if (flags & XMIT_OTHER_OBJ)
+               duo_item->racl.other_obj = recv_acl_access(NULL, f);
+
+       if (!duo_item->racl.users.count && !duo_item->racl.groups.count) {
+               /* If we received a superfluous mask, throw it away. */
+               if (duo_item->racl.mask_obj != NO_ENTRY) {
+                       /* Mask off the group perms with it first. */
+                       duo_item->racl.group_obj &= duo_item->racl.mask_obj | NO_ENTRY;
+                       duo_item->racl.mask_obj = NO_ENTRY;
+               }
+       } else if (duo_item->racl.mask_obj == NO_ENTRY) /* Must be non-empty with lists. */
+               duo_item->racl.mask_obj = computed_mask_bits | (duo_item->racl.group_obj & 7);
+
+       duo_item->sacl = NULL;
+
+       return ndx;
+}
+
+/* Receive the ACL info the sender has included for this file-list entry. */
+void receive_acl(struct file_struct *file, int f)
+{
+       F_ACL(file) = recv_rsync_acl(&access_acl_list, SMB_ACL_TYPE_ACCESS, f);
+
+       if (S_ISDIR(file->mode))
+               F_DEF_ACL(file) = recv_rsync_acl(&default_acl_list, SMB_ACL_TYPE_DEFAULT, f);
+}
+
+static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type, item_list *racl_list)
+{
+       int ndx;
+
+       if (!racl)
+               ndx = -1;
+       else if ((ndx = find_matching_rsync_acl(racl, type, racl_list)) == -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;
+       }
+
+       return ndx;
+}
+
+/* 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)
+{
+       F_ACL(file) = cache_rsync_acl(sxp->acc_acl,
+                                     SMB_ACL_TYPE_ACCESS, &access_acl_list);
+
+       if (S_ISDIR(sxp->st.st_mode)) {
+               F_DEF_ACL(file) = cache_rsync_acl(sxp->def_acl,
+                                     SMB_ACL_TYPE_DEFAULT, &default_acl_list);
+       }
+}
+
+static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode, mode_t mode)
+{
+       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;
+               }
+               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_obj == 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 (rc) {
+         error_exit:
+               if (errfun) {
+                       rsyserr(FERROR, errno, "change_sacl_perms: %s()",
+                               errfun);
+               }
+               return (mode_t)~0;
+       }
+
+#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)
+        && BITS_EQUAL(old_mode, mode, CHMOD_BITS))
+               old_mode &= ~(S_ISUID | S_ISGID | S_ISVTX);
+#endif
+
+       /* Return the mode of the file on disk, as we will set them. */
+       return (old_mode & ~ACCESSPERMS) | (mode & ACCESSPERMS);
+}
+
+static int set_rsync_acl(const char *fname, acl_duo *duo_item,
+                        SMB_ACL_TYPE_T type, statx *sxp, mode_t mode)
+{
+       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);
+                       return -1;
+               }
+       } else {
+               mode_t cur_mode = sxp->st.st_mode;
+               if (!duo_item->sacl
+                && !pack_smb_acl(&duo_item->sacl, &duo_item->racl))
+                       return -1;
+               if (type == SMB_ACL_TYPE_ACCESS) {
+                       cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl,
+                                                    cur_mode, mode);
+                       if (cur_mode == (mode_t)~0)
+                               return 0;
+               }
+               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));
+                       return -1;
+               }
+               if (type == SMB_ACL_TYPE_ACCESS)
+                       sxp->st.st_mode = cur_mode;
+       }
+
+       return 0;
+}
+
+/* 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)
+{
+       int unchanged = 1;
+       int32 ndx;
+       BOOL eq;
+
+       if (!dry_run && (read_only || list_only)) {
+               errno = EROFS;
+               return -1;
+       }
+
+       ndx = F_ACL(file);
+       if (ndx >= 0 && (size_t)ndx < access_acl_list.count) {
+               acl_duo *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);
+               if (!eq) {
+                       unchanged = 0;
+                       if (!dry_run && fname
+                        && set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_ACCESS,
+                                         sxp, file->mode) < 0)
+                               unchanged = -1;
+               }
+       }
+
+       if (!S_ISDIR(sxp->st.st_mode))
+               return unchanged;
+
+       ndx = F_DEF_ACL(file);
+       if (ndx >= 0 && (size_t)ndx < default_acl_list.count) {
+               acl_duo *duo_item = default_acl_list.items;
+               duo_item += ndx;
+               eq = sxp->def_acl && rsync_acl_equal(sxp->def_acl, &duo_item->racl);
+               if (!eq) {
+                       if (unchanged > 0)
+                               unchanged = 0;
+                       if (!dry_run && fname
+                        && set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_DEFAULT,
+                                         sxp, file->mode) < 0)
+                               unchanged = -1;
+               }
+       }
+
+       return unchanged;
+}
+
+/* === Enumeration functions for uid mapping === */
+
+/* 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 item_list **enum_racl_list = &_enum_racl_lists[0];
+static int enum_ida_index = 0;
+static size_t enum_racl_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)
+{
+       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_ida_index = 0;
+       return NULL;
+}
+
+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++) {
+               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 NULL;
+}
+
+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);
+               if (id)
+                       return id;
+       }
+       enum_racl_list = &_enum_racl_lists[0];
+       return NULL;
+}
+
+id_t *next_acl_uid()
+{
+       return next_acl_list_id(SMB_ACL_USER);
+}
+
+id_t *next_acl_gid()
+{
+       return next_acl_list_id(SMB_ACL_GROUP);
+}
+
+/* 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:
+               case ENOSYS:
+                       /* No ACLs are available. */
+                       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;
+       }
+
+       /* Convert it. */
+       racl = empty_rsync_acl;
+       ok = unpack_smb_acl(sacl, &racl);
+       sys_acl_free_acl(sacl);
+       if (!ok) {
+               rprintf(FERROR, "default_perms_for_dir: unpack_smb_acl failed, falling back on umask\n");
+               return perms;
+       }
+
+       /* 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 */
index c0f924f..8cac4d5 100644 (file)
--- a/backup.c
+++ b/backup.c
@@ -22,6 +22,7 @@
 
 extern int verbose;
 extern int am_root;
 
 extern int verbose;
 extern int am_root;
+extern int preserve_acls;
 extern int preserve_devices;
 extern int preserve_specials;
 extern int preserve_links;
 extern int preserve_devices;
 extern int preserve_specials;
 extern int preserve_links;
@@ -92,7 +93,8 @@ path
 ****************************************************************************/
 static int make_bak_dir(char *fullpath)
 {
 ****************************************************************************/
 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;
        char *rel = fullpath + backup_dir_len;
        char *end = rel + strlen(rel);
        char *p = end;
@@ -124,15 +126,25 @@ 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 (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 {
                                rsyserr(FERROR, errno,
                                        "make_bak_dir stat %s failed",
                                        full_fname(rel));
                        } else {
-                               do_lchown(fullpath, st.st_uid, st.st_gid);
-#ifdef HAVE_CHMOD
-                               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 && !S_ISLNK(file->mode)) {
+                                       get_acl(rel, &sx);
+                                       cache_acl(file, &sx);
+                                       free_acl(&sx);
+                               }
 #endif
 #endif
+                               set_file_attrs(fullpath, file, NULL, 0);
+                               free(file);
                        }
                }
                *p = '/';
                        }
                }
                *p = '/';
@@ -170,15 +182,18 @@ static int robust_move(const char *src, char *dst)
  * We will move the file to be deleted into a parallel directory tree. */
 static int keep_backup(const char *fname)
 {
  * We will move the file to be deleted into a parallel directory tree. */
 static int keep_backup(const 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 */
        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;
                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 */
 
        if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
                return 1; /* the file could have disappeared */
@@ -188,6 +203,14 @@ static int keep_backup(const char *fname)
                return 0;
        }
 
                return 0;
        }
 
+#ifdef SUPPORT_ACLS
+       if (preserve_acls && !S_ISLNK(file->mode)) {
+               get_acl(fname, &sx);
+               cache_acl(file, &sx);
+               free_acl(&sx);
+       }
+#endif
+
        /* Check to see if this is a device file, or link */
        if ((am_root && preserve_devices && IS_DEVICE(file->mode))
         || (preserve_specials && IS_SPECIAL(file->mode))) {
        /* Check to see if this is a device file, or link */
        if ((am_root && preserve_devices && IS_DEVICE(file->mode))
         || (preserve_specials && IS_SPECIAL(file->mode))) {
@@ -259,7 +282,7 @@ static int keep_backup(const char *fname)
                if (robust_move(fname, buf) != 0) {
                        rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
                                full_fname(fname), buf);
                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... */
                        /* If someone has hard-linked the file into the backup
                         * dir, rename() might return success but do nothing! */
                        robust_unlink(fname); /* Just in case... */
index 3bf50c2..2261e47 100644 (file)
@@ -36,7 +36,8 @@
 
 /* Our 64-bit numbers are sent in MSB-first order so that we can use
  * the highest bits to indicate the number of bytes sent. */
 
 /* Our 64-bit numbers are sent in MSB-first order so that we can use
  * the highest bits to indicate the number of bytes sent. */
-#define NVAL2(b,m) ((UVAL(b,0)&~(m))<<8|UVAL(b,1))
+#define NVAL1(b,m) (UVAL(b,0)&~(uint32)(m))
+#define NVAL2(b,m) (NVAL1(b,m)<<8|UVAL(b,1))
 #define NVAL3(b,m) (NVAL2(b,m)<<8|UVAL(b,2))
 #define NVAL4(b,m) (NVAL3(b,m)<<8|UVAL(b,3))
 #define NVAL5(b,m) ((int64)NVAL4(b,m)<<8|UVAL(b,4))
 #define NVAL3(b,m) (NVAL2(b,m)<<8|UVAL(b,2))
 #define NVAL4(b,m) (NVAL3(b,m)<<8|UVAL(b,3))
 #define NVAL5(b,m) ((int64)NVAL4(b,m)<<8|UVAL(b,4))
index 62b2ba0..66438c4 100644 (file)
--- a/compat.c
+++ b/compat.c
@@ -41,6 +41,7 @@ extern int prune_empty_dirs;
 extern int protocol_version;
 extern int preserve_uid;
 extern int preserve_gid;
 extern int protocol_version;
 extern int preserve_uid;
 extern int preserve_gid;
+extern int preserve_acls;
 extern int preserve_hard_links;
 extern int need_messages_from_generator;
 extern int delete_mode, delete_before, delete_during, delete_after;
 extern int preserve_hard_links;
 extern int need_messages_from_generator;
 extern int delete_mode, delete_before, delete_during, delete_after;
@@ -61,6 +62,8 @@ void setup_protocol(int f_out,int f_in)
                preserve_uid = ++file_extra_cnt;
        if (preserve_gid)
                preserve_gid = ++file_extra_cnt;
                preserve_uid = ++file_extra_cnt;
        if (preserve_gid)
                preserve_gid = ++file_extra_cnt;
+       if (preserve_acls && !am_sender)
+               preserve_acls = ++file_extra_cnt;
 
        if (remote_protocol == 0) {
                if (!read_batch)
 
        if (remote_protocol == 0) {
                if (!read_batch)
@@ -108,6 +111,13 @@ void setup_protocol(int f_out,int f_in)
                            protocol_version);
                        exit_cleanup(RERR_PROTOCOL);
                }
                            protocol_version);
                        exit_cleanup(RERR_PROTOCOL);
                }
+               if (preserve_acls) {
+                       rprintf(FERROR,
+                           "--acls requires protocol 30 or higher"
+                           " (negotiated %d).\n",
+                           protocol_version);
+                       exit_cleanup(RERR_PROTOCOL);
+               }
        }
 
        if (delete_mode && !(delete_before+delete_during+delete_after)) {
        }
 
        if (delete_mode && !(delete_before+delete_during+delete_after)) {
index 890a76b..b18a38e 100644 (file)
@@ -542,6 +542,11 @@ if test x"$ac_cv_func_strcasecmp" = x"no"; then
     AC_CHECK_LIB(resolv, strcasecmp)
 fi
 
     AC_CHECK_LIB(resolv, strcasecmp)
 fi
 
+AC_CHECK_FUNCS(aclsort)
+if test x"$ac_cv_func_aclsort" = x"no"; then
+    AC_CHECK_LIB(sec, aclsort)
+fi
+
 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.
 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.
@@ -806,6 +811,76 @@ AC_SUBST(OBJ_RESTORE)
 AC_SUBST(CC_SHOBJ_FLAG)
 AC_SUBST(BUILD_POPT)
 
 AC_SUBST(CC_SHOBJ_FLAG)
 AC_SUBST(BUILD_POPT)
 
+AC_CHECK_HEADERS(sys/acl.h acl/libacl.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([--disable-acl-support],
+                      [Turn off ACL support]))
+
+if test x"$enable_acl_support" = x"no"; then
+    AC_MSG_RESULT(no)
+else
+    AC_DEFINE(SUPPORT_ACLS, 1, [Define to 1 to add support for ACLs])
+    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(running tests:)
+       AC_CHECK_LIB(acl,acl_get_file)
+           AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
+           AC_TRY_LINK([#include <sys/types.h>
+#include <sys/acl.h>],
+[ 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 <sys/types.h>
+#include <sys/acl.h>],
+[ 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)
+           if test x"$enable_acl_support" != x"yes"; then
+               AC_DEFINE(SUPPORT_ACLS, 0)
+           fi
+       fi
+       ;;
+    esac
+fi
+
 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
 AC_OUTPUT
 
 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
 AC_OUTPUT
 
diff --git a/flist.c b/flist.c
index 700d9f7..b61e164 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -41,6 +41,7 @@ extern int filesfrom_fd;
 extern int one_file_system;
 extern int copy_dirlinks;
 extern int keep_dirlinks;
 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;
 extern int preserve_links;
 extern int preserve_hard_links;
 extern int preserve_devices;
@@ -152,6 +153,8 @@ static void list_file_entry(struct file_struct *f)
        permstring(permbuf, f->mode);
        len = F_LENGTH(f);
 
        permstring(permbuf, f->mode);
        len = F_LENGTH(f);
 
+       /* 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",
 #ifdef SUPPORT_LINKS
        if (preserve_links && S_ISLNK(f->mode)) {
                rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
@@ -464,13 +467,13 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
        if (flags & XMIT_SAME_NAME)
                write_byte(f, l1);
        if (flags & XMIT_LONG_NAME)
        if (flags & XMIT_SAME_NAME)
                write_byte(f, l1);
        if (flags & XMIT_LONG_NAME)
-               write_int(f, l2);
+               write_abbrevint30(f, l2);
        else
                write_byte(f, l2);
        write_buf(f, fname + l1, l2);
 
        if (first_hlink_ndx >= 0) {
        else
                write_byte(f, l2);
        write_buf(f, fname + l1, l2);
 
        if (first_hlink_ndx >= 0) {
-               write_int(f, first_hlink_ndx);
+               write_abbrevint30(f, first_hlink_ndx);
                goto the_end;
        }
 
                goto the_end;
        }
 
@@ -480,7 +483,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
        if (!(flags & XMIT_SAME_MODE))
                write_int(f, to_wire_mode(mode));
        if (preserve_uid && !(flags & XMIT_SAME_UID)) {
        if (!(flags & XMIT_SAME_MODE))
                write_int(f, to_wire_mode(mode));
        if (preserve_uid && !(flags & XMIT_SAME_UID)) {
-               write_int(f, uid);
+               write_abbrevint30(f, uid);
                if (flags & XMIT_USER_NAME_FOLLOWS) {
                        int len = strlen(user_name);
                        write_byte(f, len);
                if (flags & XMIT_USER_NAME_FOLLOWS) {
                        int len = strlen(user_name);
                        write_byte(f, len);
@@ -488,7 +491,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
                }
        }
        if (preserve_gid && !(flags & XMIT_SAME_GID)) {
                }
        }
        if (preserve_gid && !(flags & XMIT_SAME_GID)) {
-               write_int(f, gid);
+               write_abbrevint30(f, gid);
                if (flags & XMIT_GROUP_NAME_FOLLOWS) {
                        int len = strlen(group_name);
                        write_byte(f, len);
                if (flags & XMIT_GROUP_NAME_FOLLOWS) {
                        int len = strlen(group_name);
                        write_byte(f, len);
@@ -514,7 +517,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
        if (preserve_links && S_ISLNK(mode)) {
                const char *sl = F_SYMLINK(file);
                int len = strlen(sl);
        if (preserve_links && S_ISLNK(mode)) {
                const char *sl = F_SYMLINK(file);
                int len = strlen(sl);
-               write_int(f, len);
+               write_abbrevint30(f, len);
                write_buf(f, sl, len);
        }
 #endif
                write_buf(f, sl, len);
        }
 #endif
@@ -580,7 +583,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                l1 = read_byte(f);
 
        if (flags & XMIT_LONG_NAME)
                l1 = read_byte(f);
 
        if (flags & XMIT_LONG_NAME)
-               l2 = read_int(f);
+               l2 = read_abbrevint30(f);
        else
                l2 = read_byte(f);
 
        else
                l2 = read_byte(f);
 
@@ -619,7 +622,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
        if (protocol_version >= 30
         && BITS_SETnUNSET(flags, XMIT_HLINKED, XMIT_HLINK_FIRST)) {
                struct file_struct *first;
        if (protocol_version >= 30
         && BITS_SETnUNSET(flags, XMIT_HLINKED, XMIT_HLINK_FIRST)) {
                struct file_struct *first;
-               first_hlink_ndx = read_int(f);
+               first_hlink_ndx = read_abbrevint30(f);
                if (first_hlink_ndx < 0 || first_hlink_ndx >= flist->count) {
                        rprintf(FERROR,
                                "hard-link reference out of range: %d (%d)\n",
                if (first_hlink_ndx < 0 || first_hlink_ndx >= flist->count) {
                        rprintf(FERROR,
                                "hard-link reference out of range: %d (%d)\n",
@@ -657,14 +660,14 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                mode = tweak_mode(mode, chmod_modes);
 
        if (preserve_uid && !(flags & XMIT_SAME_UID)) {
                mode = tweak_mode(mode, chmod_modes);
 
        if (preserve_uid && !(flags & XMIT_SAME_UID)) {
-               uid = (uid_t)read_int(f);
+               uid = (uid_t)read_abbrevint30(f);
                if (flags & XMIT_USER_NAME_FOLLOWS)
                        uid = recv_user_name(f, uid);
                else if (inc_recurse && am_root && !numeric_ids)
                        uid = match_uid(uid);
        }
        if (preserve_gid && !(flags & XMIT_SAME_GID)) {
                if (flags & XMIT_USER_NAME_FOLLOWS)
                        uid = recv_user_name(f, uid);
                else if (inc_recurse && am_root && !numeric_ids)
                        uid = match_uid(uid);
        }
        if (preserve_gid && !(flags & XMIT_SAME_GID)) {
-               gid = (gid_t)read_int(f);
+               gid = (gid_t)read_abbrevint30(f);
                if (flags & XMIT_GROUP_NAME_FOLLOWS)
                        gid = recv_group_name(f, gid);
                else if (inc_recurse && (!am_root || !numeric_ids))
                if (flags & XMIT_GROUP_NAME_FOLLOWS)
                        gid = recv_group_name(f, gid);
                else if (inc_recurse && (!am_root || !numeric_ids))
@@ -693,7 +696,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 
 #ifdef SUPPORT_LINKS
        if (preserve_links && S_ISLNK(mode)) {
 
 #ifdef SUPPORT_LINKS
        if (preserve_links && S_ISLNK(mode)) {
-               linkname_len = read_int(f) + 1; /* count the '\0' */
+               linkname_len = read_abbrevint30(f) + 1; /* count the '\0' */
                if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
                        rprintf(FERROR, "overflow: linkname_len=%d\n",
                                linkname_len - 1);
                if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
                        rprintf(FERROR, "overflow: linkname_len=%d\n",
                                linkname_len - 1);
@@ -714,6 +717,12 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
        }
 #endif
 
        }
 #endif
 
+#ifdef SUPPORT_ACLS
+       /* We need one or two index int32s when we're preserving ACLs. */
+       if (preserve_acls)
+               extra_len += (S_ISDIR(mode) ? 2 : 1) * EXTRA_LEN;
+#endif
+
        if (always_checksum && S_ISREG(mode))
                extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
 
        if (always_checksum && S_ISREG(mode))
                extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
 
@@ -851,6 +860,11 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                        read_buf(f, bp, checksum_len);
        }
 
                        read_buf(f, bp, checksum_len);
        }
 
+#ifdef SUPPORT_ACLS
+       if (preserve_acls && !S_ISLNK(mode))
+               receive_acl(file, f);
+#endif
+
        if (S_ISREG(mode) || S_ISLNK(mode))
                stats.total_size += file_length;
 
        if (S_ISREG(mode) || S_ISLNK(mode))
                stats.total_size += file_length;
 
@@ -1122,6 +1136,9 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
                                          int flags, int filter_flags)
 {
        struct file_struct *file;
                                          int flags, int filter_flags)
 {
        struct file_struct *file;
+#ifdef SUPPORT_ACLS
+       statx sx;
+#endif
 
        file = make_file(fname, flist, stp, flags, filter_flags);
        if (!file)
 
        file = make_file(fname, flist, stp, flags, filter_flags);
        if (!file)
@@ -1130,12 +1147,28 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
        if (chmod_modes && !S_ISLNK(file->mode))
                file->mode = tweak_mode(file->mode, chmod_modes);
 
        if (chmod_modes && !S_ISLNK(file->mode))
                file->mode = tweak_mode(file->mode, chmod_modes);
 
+#ifdef SUPPORT_ACLS
+       if (preserve_acls && !S_ISLNK(file->mode) && f >= 0) {
+               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);
        flist->files[flist->count++] = file;
        maybe_emit_filelist_progress(flist->count + flist_count_offset);
 
        flist_expand(flist);
        flist->files[flist->count++] = file;
-       if (f >= 0)
+       if (f >= 0) {
                send_file_entry(f, file, flist->count - 1);
                send_file_entry(f, file, flist->count - 1);
+#ifdef SUPPORT_ACLS
+               if (preserve_acls && !S_ISLNK(file->mode)) {
+                       send_acl(&sx, f);
+                       free_acl(&sx);
+               }
+#endif
+       }
        return file;
 }
 
        return file;
 }
 
index f0a752b..e80daf2 100644 (file)
@@ -35,6 +35,7 @@ extern int do_progress;
 extern int relative_paths;
 extern int implied_dirs;
 extern int keep_dirlinks;
 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;
 extern int preserve_links;
 extern int preserve_devices;
 extern int preserve_specials;
@@ -89,6 +90,7 @@ extern int force_delete;
 extern int one_file_system;
 extern struct stats stats;
 extern dev_t filesystem_dev;
 extern int one_file_system;
 extern struct stats stats;
 extern dev_t filesystem_dev;
+extern mode_t orig_umask;
 extern char *backup_dir;
 extern char *backup_suffix;
 extern int backup_suffix_len;
 extern char *backup_dir;
 extern char *backup_suffix;
 extern int backup_suffix_len;
@@ -511,22 +513,31 @@ static void do_delete_pass(struct file_list *flist)
                rprintf(FINFO, "                    \r");
 }
 
                rprintf(FINFO, "                    \r");
 }
 
-int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st)
+int unchanged_attrs(const char *fname, struct file_struct *file, statx *sxp)
 {
 {
-       if (preserve_perms && !BITS_EQUAL(st->st_mode, file->mode, CHMOD_BITS))
+       if (preserve_perms && !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
                return 0;
 
                return 0;
 
-       if (am_root && preserve_uid && st->st_uid != F_UID(file))
+       if (am_root && preserve_uid && sxp->st.st_uid != F_UID(file))
                return 0;
 
                return 0;
 
-       if (preserve_gid && F_GID(file) != GID_NONE && st->st_gid != F_GID(file))
+       if (preserve_gid && F_GID(file) != GID_NONE && sxp->st.st_gid != F_GID(file))
                return 0;
 
                return 0;
 
+#ifdef SUPPORT_ACLS
+       if (preserve_acls && !S_ISLNK(file->mode)) {
+               if (!ACL_READY(*sxp))
+                       get_acl(fname, sxp);
+               if (set_acl(NULL, file, sxp) == 0)
+                       return 0;
+       }
+#endif
+
        return 1;
 }
 
        return 1;
 }
 
-void itemize(struct file_struct *file, int ndx, int statret,
-            STRUCT_STAT *st, int32 iflags, uchar fnamecmp_type,
+void itemize(const char *fname, struct file_struct *file, int ndx, int statret,
+            statx *sxp, int32 iflags, uchar fnamecmp_type,
             const char *xname)
 {
        if (statret >= 0) { /* A from-dest-dir statret can == 1! */
             const char *xname)
 {
        if (statret >= 0) { /* A from-dest-dir statret can == 1! */
@@ -534,20 +545,28 @@ void itemize(struct file_struct *file, int ndx, int statret,
                    : S_ISDIR(file->mode) ? !omit_dir_times
                    : !S_ISLNK(file->mode);
 
                    : S_ISDIR(file->mode) ? !omit_dir_times
                    : !S_ISLNK(file->mode);
 
-               if (S_ISREG(file->mode) && F_LENGTH(file) != st->st_size)
+               if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
                        iflags |= ITEM_REPORT_SIZE;
                if ((iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !keep_time
                  && !(iflags & ITEM_MATCHED)
                  && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
                        iflags |= ITEM_REPORT_SIZE;
                if ((iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !keep_time
                  && !(iflags & ITEM_MATCHED)
                  && (!(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;
                        iflags |= ITEM_REPORT_TIME;
-               if (!BITS_EQUAL(st->st_mode, file->mode, CHMOD_BITS))
+               if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
                        iflags |= ITEM_REPORT_PERMS;
                        iflags |= ITEM_REPORT_PERMS;
-               if (preserve_uid && am_root && F_UID(file) != st->st_uid)
+               if (preserve_uid && am_root && F_UID(file) != sxp->st.st_uid)
                        iflags |= ITEM_REPORT_OWNER;
                if (preserve_gid && F_GID(file) != GID_NONE
                        iflags |= ITEM_REPORT_OWNER;
                if (preserve_gid && F_GID(file) != GID_NONE
-                   && st->st_gid != F_GID(file))
+                   && sxp->st.st_gid != F_GID(file))
                        iflags |= ITEM_REPORT_GROUP;
                        iflags |= ITEM_REPORT_GROUP;
+#ifdef SUPPORT_ACLS
+               if (preserve_acls && !S_ISLNK(file->mode)) {
+                       if (!ACL_READY(*sxp))
+                               get_acl(fname, sxp);
+                       if (set_acl(NULL, file, sxp) == 0)
+                               iflags |= ITEM_REPORT_ACL;
+               }
+#endif
        } else
                iflags |= ITEM_IS_NEW;
 
        } else
                iflags |= ITEM_IS_NEW;
 
@@ -783,7 +802,7 @@ static int find_fuzzy(struct file_struct *file, struct file_list *dirlist)
  * 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,
  * 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,
                         enum logcode code)
 {
        int best_match = -1;
                         enum logcode code)
 {
        int best_match = -1;
@@ -792,7 +811,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
 
        do {
                pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
 
        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:
                        continue;
                switch (match_level) {
                case 0:
@@ -800,16 +819,16 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                        match_level = 1;
                        /* FALL THROUGH */
                case 1:
                        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:
                                continue;
                        best_match = j;
                        match_level = 2;
                        /* FALL THROUGH */
                case 2:
-                       if (!unchanged_attrs(file, stp))
+                       if (!unchanged_attrs(cmpbuf, file, sxp))
                                continue;
                        if (always_checksum > 0 && preserve_times
                                continue;
                        if (always_checksum > 0 && preserve_times
-                        && cmp_time(stp->st_mtime, file->modtime))
+                        && cmp_time(sxp->st.st_mtime, file->modtime))
                                continue;
                        best_match = j;
                        match_level = 3;
                                continue;
                        best_match = j;
                        match_level = 3;
@@ -824,7 +843,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
        if (j != best_match) {
                j = best_match;
                pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
        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)
                        return -1;
        }
 
                        return -1;
        }
 
@@ -834,16 +853,16 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                        if (!hard_link_one(file, fname, cmpbuf, 1))
                                goto try_a_copy;
                        if (preserve_hard_links && F_IS_HLINKED(file))
                        if (!hard_link_one(file, fname, cmpbuf, 1))
                                goto try_a_copy;
                        if (preserve_hard_links && F_IS_HLINKED(file))
-                               finish_hard_link(file, fname, stp, itemizing, code, j);
+                               finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
                        if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
                        if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
-                               itemize(file, ndx, 1, stp,
+                               itemize(fname, file, ndx, 1, sxp,
                                        ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
                                        0, "");
                        }
                } else
 #endif
                if (itemizing)
                                        ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
                                        0, "");
                        }
                } else
 #endif
                if (itemizing)
-                       itemize(file, ndx, 0, stp, 0, 0, NULL);
+                       itemize(fname, file, ndx, 0, sxp, 0, 0, NULL);
                if (verbose > 1 && maybe_ATTRS_REPORT)
                        rprintf(FCLIENT, "%s is uptodate\n", fname);
                return -2;
                if (verbose > 1 && maybe_ATTRS_REPORT)
                        rprintf(FCLIENT, "%s is uptodate\n", fname);
                return -2;
@@ -861,7 +880,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                        return -1;
                }
                if (itemizing)
                        return -1;
                }
                if (itemizing)
-                       itemize(file, ndx, 0, stp, ITEM_LOCAL_CHANGE, 0, NULL);
+                       itemize(fname, 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)
                set_file_attrs(fname, file, NULL, 0);
                if (maybe_ATTRS_REPORT
                 && ((!itemizing && verbose && match_level == 2)
@@ -872,7 +891,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                }
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
                }
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
-                       finish_hard_link(file, fname, stp, itemizing, code, -1);
+                       finish_hard_link(file, fname, &sxp->st, itemizing, code, -1);
 #endif
                return -2;
        }
 #endif
                return -2;
        }
@@ -884,7 +903,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
  * handling the file, or -1 if no dest-linking occurred, or a non-negative
  * value if we found an alternate basis file. */
 static int try_dests_non(struct file_struct *file, char *fname, int ndx,
  * handling the file, or -1 if no dest-linking occurred, or a non-negative
  * value if we found an alternate basis file. */
 static int try_dests_non(struct file_struct *file, char *fname, int ndx,
-                        char *cmpbuf, STRUCT_STAT *stp, int itemizing,
+                        char *cmpbuf, statx *sxp, int itemizing,
                         enum logcode code)
 {
        char lnk[MAXPATHLEN];
                         enum logcode code)
 {
        char lnk[MAXPATHLEN];
@@ -917,24 +936,24 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
 
        do {
                pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
 
        do {
                pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
-               if (link_stat(cmpbuf, stp, 0) < 0)
+               if (link_stat(cmpbuf, &sxp->st, 0) < 0)
                        continue;
                switch (type) {
                case TYPE_DIR:
                        continue;
                switch (type) {
                case TYPE_DIR:
-                       if (!S_ISDIR(stp->st_mode))
+                       if (!S_ISDIR(sxp->st.st_mode))
                                continue;
                        break;
                case TYPE_SPECIAL:
                                continue;
                        break;
                case TYPE_SPECIAL:
-                       if (!IS_SPECIAL(stp->st_mode))
+                       if (!IS_SPECIAL(sxp->st.st_mode))
                                continue;
                        break;
                case TYPE_DEVICE:
                                continue;
                        break;
                case TYPE_DEVICE:
-                       if (!IS_DEVICE(stp->st_mode))
+                       if (!IS_DEVICE(sxp->st.st_mode))
                                continue;
                        break;
 #ifdef SUPPORT_LINKS
                case TYPE_SYMLINK:
                                continue;
                        break;
 #ifdef SUPPORT_LINKS
                case TYPE_SYMLINK:
-                       if (!S_ISLNK(stp->st_mode))
+                       if (!S_ISLNK(sxp->st.st_mode))
                                continue;
                        break;
 #endif
                                continue;
                        break;
 #endif
@@ -949,7 +968,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
                case TYPE_SPECIAL:
                case TYPE_DEVICE:
                        devp = F_RDEV_P(file);
                case TYPE_SPECIAL:
                case TYPE_DEVICE:
                        devp = F_RDEV_P(file);
-                       if (stp->st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
+                       if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
                                continue;
                        break;
 #ifdef SUPPORT_LINKS
                                continue;
                        break;
 #ifdef SUPPORT_LINKS
@@ -966,7 +985,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
                        match_level = 2;
                        best_match = j;
                }
                        match_level = 2;
                        best_match = j;
                }
-               if (unchanged_attrs(file, stp)) {
+               if (unchanged_attrs(cmpbuf, file, sxp)) {
                        match_level = 3;
                        best_match = j;
                        break;
                        match_level = 3;
                        best_match = j;
                        break;
@@ -979,7 +998,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
        if (j != best_match) {
                j = best_match;
                pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
        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)
                        return -1;
        }
 
                        return -1;
        }
 
@@ -1010,7 +1029,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
                            : ITEM_LOCAL_CHANGE
                             + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
                        char *lp = match_level == 3 ? "" : NULL;
                            : ITEM_LOCAL_CHANGE
                             + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
                        char *lp = match_level == 3 ? "" : NULL;
-                       itemize(file, ndx, 0, stp, chg + ITEM_MATCHED, 0, lp);
+                       itemize(fname, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
                }
                if (verbose > 1 && maybe_ATTRS_REPORT) {
                        rprintf(FCLIENT, "%s%s is uptodate\n",
                }
                if (verbose > 1 && maybe_ATTRS_REPORT) {
                        rprintf(FCLIENT, "%s%s is uptodate\n",
@@ -1023,6 +1042,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
 }
 
 static int phase = 0;
 }
 
 static int phase = 0;
+static int dflt_perms;
 
 /* Acts on the indicated item in cur_flist whose name is fname.  If a dir,
  * make sure it exists, and has the right permissions/timestamp info.  For
 
 /* Acts on the indicated item in cur_flist whose name is fname.  If a dir,
  * make sure it exists, and has the right permissions/timestamp info.  For
@@ -1043,7 +1063,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        static int need_fuzzy_dirlist = 0;
        struct file_struct *fuzzy_file = NULL;
        int fd = -1, f_copy = -1;
        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;
        struct file_struct *back_file = NULL;
        int statret, real_ret, stat_errno;
        char *fnamecmp, *partialptr, *backupptr = NULL;
@@ -1088,6 +1109,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        return;
                }
        }
                        return;
                }
        }
+#ifdef SUPPORT_ACLS
+       sx.acc_acl = sx.def_acl = NULL;
+#endif
        if (dry_run > 1) {
                if (fuzzy_dirlist) {
                        flist_free(fuzzy_dirlist);
        if (dry_run > 1) {
                if (fuzzy_dirlist) {
                        flist_free(fuzzy_dirlist);
@@ -1100,7 +1124,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                const char *dn = file->dirname ? file->dirname : ".";
                if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
                        if (relative_paths && !implied_dirs
                const char *dn = file->dirname ? file->dirname : ".";
                if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
                        if (relative_paths && !implied_dirs
-                        && do_stat(dn, &st) < 0
+                        && do_stat(dn, &sx.st) < 0
                         && create_directory_path(fname) < 0) {
                                rsyserr(FERROR, errno,
                                        "recv_generator: mkdir %s failed",
                         && create_directory_path(fname) < 0) {
                                rsyserr(FERROR, errno,
                                        "recv_generator: mkdir %s failed",
@@ -1112,6 +1136,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        }
                        if (fuzzy_basis)
                                need_fuzzy_dirlist = 1;
                        }
                        if (fuzzy_basis)
                                need_fuzzy_dirlist = 1;
+#ifdef SUPPORT_ACLS
+                       if (!preserve_perms)
+                               dflt_perms = default_perms_for_dir(dn);
+#endif
                }
                parent_dirname = dn;
 
                }
                parent_dirname = dn;
 
@@ -1121,7 +1149,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        need_fuzzy_dirlist = 0;
                }
 
                        need_fuzzy_dirlist = 0;
                }
 
-               statret = link_stat(fname, &st,
+               statret = link_stat(fname, &sx.st,
                                    keep_dirlinks && S_ISDIR(file->mode));
                stat_errno = errno;
        }
                                    keep_dirlinks && S_ISDIR(file->mode));
                stat_errno = errno;
        }
@@ -1149,8 +1177,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                 * 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. */
                 * 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, "directory", del_opts) != 0)
+               if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
+                       if (delete_item(fname, sx.st.st_mode, "directory", del_opts) != 0)
                                return;
                        statret = -1;
                }
                                return;
                        statret = -1;
                }
@@ -1159,18 +1187,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        dry_run++;
                }
                real_ret = statret;
                        dry_run++;
                }
                real_ret = statret;
-               real_st = st;
+               real_sx = sx;
                if (new_root_dir) {
                        if (*fname == '.' && fname[1] == '\0')
                                statret = -1;
                        new_root_dir = 0;
                }
                if (!preserve_perms) { /* See comment in non-dir code below. */
                if (new_root_dir) {
                        if (*fname == '.' && fname[1] == '\0')
                                statret = -1;
                        new_root_dir = 0;
                }
                if (!preserve_perms) { /* See comment in non-dir code below. */
-                       file->mode = dest_mode(file->mode, st.st_mode,
-                                              statret == 0);
+                       file->mode = dest_mode(file->mode, sx.st.st_mode,
+                                              dflt_perms, statret == 0);
                }
                if (statret != 0 && basis_dir[0] != NULL) {
                }
                if (statret != 0 && basis_dir[0] != NULL) {
-                       int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
+                       int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
                                              itemizing, code);
                        if (j == -2) {
                                itemizing = 0;
                                              itemizing, code);
                        if (j == -2) {
                                itemizing = 0;
@@ -1179,7 +1207,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                statret = 1;
                }
                if (itemizing && f_out != -1) {
                                statret = 1;
                }
                if (itemizing && f_out != -1) {
-                       itemize(file, ndx, statret, &st,
+                       itemize(fname, file, ndx, statret, &sx,
                                statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
                }
                if (real_ret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) {
                                statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
                }
                if (real_ret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) {
@@ -1193,38 +1221,39 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                    "*** Skipping any contents from this failed directory ***\n");
                                missing_below = F_DEPTH(file);
                                file->flags |= FLAG_MISSING_DIR;
                                    "*** Skipping any contents from this failed directory ***\n");
                                missing_below = F_DEPTH(file);
                                file->flags |= FLAG_MISSING_DIR;
-                               return;
+                               goto cleanup;
                        }
                }
                        }
                }
-               if (set_file_attrs(fname, file, real_ret ? NULL : &real_st, 0)
+               if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, 0)
                    && verbose && code != FNONE && f_out != -1)
                        rprintf(code, "%s/\n", fname);
                if (real_ret != 0 && one_file_system)
                    && verbose && code != FNONE && f_out != -1)
                        rprintf(code, "%s/\n", fname);
                if (real_ret != 0 && one_file_system)
-                       real_st.st_dev = filesystem_dev;
+                       real_sx.st.st_dev = filesystem_dev;
                if (inc_recurse) {
                        if (one_file_system) {
                                uint32 *devp = F_DIRDEV_P(file);
                if (inc_recurse) {
                        if (one_file_system) {
                                uint32 *devp = F_DIRDEV_P(file);
-                               DEV_MAJOR(devp) = major(real_st.st_dev);
-                               DEV_MINOR(devp) = minor(real_st.st_dev);
+                               DEV_MAJOR(devp) = major(real_sx.st.st_dev);
+                               DEV_MINOR(devp) = minor(real_sx.st.st_dev);
                        }
                }
                else if (delete_during && f_out != -1 && !phase && dry_run < 2
                    && (file->flags & FLAG_XFER_DIR))
                        }
                }
                else if (delete_during && f_out != -1 && !phase && dry_run < 2
                    && (file->flags & FLAG_XFER_DIR))
-                       delete_in_dir(cur_flist, fname, file, &real_st.st_dev);
-               return;
+                       delete_in_dir(cur_flist, fname, file, &real_sx.st.st_dev);
+               goto cleanup;
        }
 
        /* If we're not preserving permissions, change the file-list's
         * mode based on the local permissions and some heuristics. */
        if (!preserve_perms) {
        }
 
        /* If we're not preserving permissions, change the file-list's
         * mode based on the local permissions and some heuristics. */
        if (!preserve_perms) {
-               int exists = statret == 0 && !S_ISDIR(st.st_mode);
-               file->mode = dest_mode(file->mode, st.st_mode, exists);
+               int exists = statret == 0 && !S_ISDIR(sx.st.st_mode);
+               file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms,
+                                      exists);
        }
 
 #ifdef SUPPORT_HARD_LINKS
        if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
        }
 
 #ifdef SUPPORT_HARD_LINKS
        if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
-        && hard_link_check(file, ndx, fname, statret, &st, itemizing, code))
-               return;
+        && hard_link_check(file, ndx, fname, statret, &sx, itemizing, code))
+               goto cleanup;
 #endif
 
        if (preserve_links && S_ISLNK(file->mode)) {
 #endif
 
        if (preserve_links && S_ISLNK(file->mode)) {
@@ -1244,28 +1273,28 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        char lnk[MAXPATHLEN];
                        int len;
 
                        char lnk[MAXPATHLEN];
                        int len;
 
-                       if (!S_ISLNK(st.st_mode))
+                       if (!S_ISLNK(sx.st.st_mode))
                                statret = -1;
                        else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0
                              && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
                                /* The link is pointing to the right place. */
                                if (itemizing)
                                statret = -1;
                        else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0
                              && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
                                /* The link is pointing to the right place. */
                                if (itemizing)
-                                       itemize(file, ndx, 0, &st, 0, 0, NULL);
-                               set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT);
+                                       itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
+                               set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
 #ifdef SUPPORT_HARD_LINKS
                                if (preserve_hard_links && F_IS_HLINKED(file))
 #ifdef SUPPORT_HARD_LINKS
                                if (preserve_hard_links && F_IS_HLINKED(file))
-                                       finish_hard_link(file, fname, &st, itemizing, code, -1);
+                                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
 #endif
                                if (remove_source_files == 1)
                                        goto return_with_success;
 #endif
                                if (remove_source_files == 1)
                                        goto return_with_success;
-                               return;
+                               goto cleanup;
                        }
                        /* Not the right symlink (or not a symlink), so
                         * delete it. */
                        }
                        /* Not the right symlink (or not a symlink), so
                         * delete it. */
-                       if (delete_item(fname, st.st_mode, "symlink", del_opts) != 0)
-                               return;
+                       if (delete_item(fname, sx.st.st_mode, "symlink", del_opts) != 0)
+                               goto cleanup;
                } else if (basis_dir[0] != NULL) {
                } else if (basis_dir[0] != NULL) {
-                       int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
+                       int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
                                              itemizing, code);
                        if (j == -2) {
 #ifndef CAN_HARDLINK_SYMLINK
                                              itemizing, code);
                        if (j == -2) {
 #ifndef CAN_HARDLINK_SYMLINK
@@ -1274,7 +1303,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                } else
 #endif
                                if (!copy_dest)
                                } else
 #endif
                                if (!copy_dest)
-                                       return;
+                                       goto cleanup;
                                itemizing = 0;
                                code = FNONE;
                        } else if (j >= 0)
                                itemizing = 0;
                                code = FNONE;
                        } else if (j >= 0)
@@ -1282,7 +1311,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                }
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
                }
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
-                       return;
+                       goto cleanup;
 #endif
                if (do_symlink(sl, fname) != 0) {
                        rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
 #endif
                if (do_symlink(sl, fname) != 0) {
                        rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
@@ -1290,7 +1319,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                } else {
                        set_file_attrs(fname, file, NULL, 0);
                        if (itemizing) {
                } else {
                        set_file_attrs(fname, file, NULL, 0);
                        if (itemizing) {
-                               itemize(file, ndx, statret, &st,
+                               itemize(fname, file, ndx, statret, &sx,
                                        ITEM_LOCAL_CHANGE, 0, NULL);
                        }
                        if (code != FNONE && verbose)
                                        ITEM_LOCAL_CHANGE, 0, NULL);
                        }
                        if (code != FNONE && verbose)
@@ -1306,7 +1335,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                goto return_with_success;
                }
 #endif
                                goto return_with_success;
                }
 #endif
-               return;
+               goto cleanup;
        }
 
        if ((am_root && preserve_devices && IS_DEVICE(file->mode))
        }
 
        if ((am_root && preserve_devices && IS_DEVICE(file->mode))
@@ -1316,33 +1345,33 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                if (statret == 0) {
                        char *t;
                        if (IS_DEVICE(file->mode)) {
                if (statret == 0) {
                        char *t;
                        if (IS_DEVICE(file->mode)) {
-                               if (!IS_DEVICE(st.st_mode))
+                               if (!IS_DEVICE(sx.st.st_mode))
                                        statret = -1;
                                t = "device file";
                        } else {
                                        statret = -1;
                                t = "device file";
                        } else {
-                               if (!IS_SPECIAL(st.st_mode))
+                               if (!IS_SPECIAL(sx.st.st_mode))
                                        statret = -1;
                                t = "special file";
                        }
                        if (statret == 0
                                        statret = -1;
                                t = "special file";
                        }
                        if (statret == 0
-                        && BITS_EQUAL(st.st_mode, file->mode, _S_IFMT)
-                        && st.st_rdev == rdev) {
+                        && BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
+                        && sx.st.st_rdev == rdev) {
                                /* The device or special file is identical. */
                                if (itemizing)
                                /* The device or special file is identical. */
                                if (itemizing)
-                                       itemize(file, ndx, 0, &st, 0, 0, NULL);
-                               set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT);
+                                       itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
+                               set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
 #ifdef SUPPORT_HARD_LINKS
                                if (preserve_hard_links && F_IS_HLINKED(file))
 #ifdef SUPPORT_HARD_LINKS
                                if (preserve_hard_links && F_IS_HLINKED(file))
-                                       finish_hard_link(file, fname, &st, itemizing, code, -1);
+                                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
 #endif
                                if (remove_source_files == 1)
                                        goto return_with_success;
 #endif
                                if (remove_source_files == 1)
                                        goto return_with_success;
-                               return;
+                               goto cleanup;
                        }
                        }
-                       if (delete_item(fname, st.st_mode, t, del_opts) != 0)
-                               return;
+                       if (delete_item(fname, sx.st.st_mode, t, del_opts) != 0)
+                               goto cleanup;
                } else if (basis_dir[0] != NULL) {
                } else if (basis_dir[0] != NULL) {
-                       int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
+                       int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
                                              itemizing, code);
                        if (j == -2) {
 #ifndef CAN_HARDLINK_SPECIAL
                                              itemizing, code);
                        if (j == -2) {
 #ifndef CAN_HARDLINK_SPECIAL
@@ -1351,7 +1380,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                } else
 #endif
                                if (!copy_dest)
                                } else
 #endif
                                if (!copy_dest)
-                                       return;
+                                       goto cleanup;
                                itemizing = 0;
                                code = FNONE;
                        } else if (j >= 0)
                                itemizing = 0;
                                code = FNONE;
                        } else if (j >= 0)
@@ -1359,7 +1388,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                }
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
                }
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
-                       return;
+                       goto cleanup;
 #endif
                if (verbose > 2) {
                        rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n",
 #endif
                if (verbose > 2) {
                        rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n",
@@ -1372,7 +1401,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                } else {
                        set_file_attrs(fname, file, NULL, 0);
                        if (itemizing) {
                } else {
                        set_file_attrs(fname, file, NULL, 0);
                        if (itemizing) {
-                               itemize(file, ndx, statret, &st,
+                               itemize(fname, file, ndx, statret, &sx,
                                        ITEM_LOCAL_CHANGE, 0, NULL);
                        }
                        if (code != FNONE && verbose)
                                        ITEM_LOCAL_CHANGE, 0, NULL);
                        }
                        if (code != FNONE && verbose)
@@ -1384,14 +1413,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        if (remove_source_files == 1)
                                goto return_with_success;
                }
                        if (remove_source_files == 1)
                                goto return_with_success;
                }
-               return;
+               goto cleanup;
        }
 
        if (!S_ISREG(file->mode)) {
                if (solo_file)
                        fname = f_name(file, NULL);
                rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
        }
 
        if (!S_ISREG(file->mode)) {
                if (solo_file)
                        fname = f_name(file, NULL);
                rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
-               return;
+               goto cleanup;
        }
 
        if (max_size > 0 && F_LENGTH(file) > max_size) {
        }
 
        if (max_size > 0 && F_LENGTH(file) > max_size) {
@@ -1400,7 +1429,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                fname = f_name(file, NULL);
                        rprintf(FINFO, "%s is over max-size\n", fname);
                }
                                fname = f_name(file, NULL);
                        rprintf(FINFO, "%s is over max-size\n", fname);
                }
-               return;
+               goto cleanup;
        }
        if (min_size > 0 && F_LENGTH(file) < min_size) {
                if (verbose > 1) {
        }
        if (min_size > 0 && F_LENGTH(file) < min_size) {
                if (verbose > 1) {
@@ -1408,39 +1437,39 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                fname = f_name(file, NULL);
                        rprintf(FINFO, "%s is under min-size\n", fname);
                }
                                fname = f_name(file, NULL);
                        rprintf(FINFO, "%s is under min-size\n", fname);
                }
-               return;
+               goto cleanup;
        }
 
        if (ignore_existing > 0 && statret == 0) {
                if (verbose > 1)
                        rprintf(FINFO, "%s exists\n", fname);
        }
 
        if (ignore_existing > 0 && statret == 0) {
                if (verbose > 1)
                        rprintf(FINFO, "%s exists\n", fname);
-               return;
+               goto cleanup;
        }
 
        if (update_only > 0 && statret == 0
        }
 
        if (update_only > 0 && 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);
                if (verbose > 1)
                        rprintf(FINFO, "%s is newer\n", fname);
-               return;
+               goto cleanup;
        }
 
        fnamecmp = fname;
        fnamecmp_type = FNAMECMP_FNAME;
 
        }
 
        fnamecmp = fname;
        fnamecmp_type = FNAMECMP_FNAME;
 
-       if (statret == 0 && !S_ISREG(st.st_mode)) {
-               if (delete_item(fname, st.st_mode, "regular file", del_opts) != 0)
-                       return;
+       if (statret == 0 && !S_ISREG(sx.st.st_mode)) {
+               if (delete_item(fname, sx.st.st_mode, "regular file", del_opts) != 0)
+                       goto cleanup;
                statret = -1;
                stat_errno = ENOENT;
        }
 
        if (statret != 0 && basis_dir[0] != NULL) {
                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, code);
                if (j == -2) {
                        if (remove_source_files == 1)
                                goto return_with_success;
                                      itemizing, code);
                if (j == -2) {
                        if (remove_source_files == 1)
                                goto return_with_success;
-                       return;
+                       goto cleanup;
                }
                if (j >= 0) {
                        fnamecmp = fnamecmpbuf;
                }
                if (j >= 0) {
                        fnamecmp = fnamecmpbuf;
@@ -1450,7 +1479,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        }
 
        real_ret = statret;
        }
 
        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
 
        if (partial_dir && (partialptr = partial_dir_fname(fname)) != NULL
            && link_stat(partialptr, &partial_st, 0) == 0
@@ -1469,7 +1498,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                rprintf(FINFO, "fuzzy basis selected for %s: %s\n",
                                        fname, fnamecmpbuf);
                        }
                                rprintf(FINFO, "fuzzy basis selected for %s: %s\n",
                                        fname, fnamecmpbuf);
                        }
-                       st.st_size = F_LENGTH(fuzzy_file);
+                       sx.st.st_size = F_LENGTH(fuzzy_file);
                        statret = 0;
                        fnamecmp = fnamecmpbuf;
                        fnamecmp_type = FNAMECMP_FUZZY;
                        statret = 0;
                        fnamecmp = fnamecmpbuf;
                        fnamecmp_type = FNAMECMP_FUZZY;
@@ -1479,45 +1508,45 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        if (statret != 0) {
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
        if (statret != 0) {
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
-                       return;
+                       goto cleanup;
 #endif
                if (stat_errno == ENOENT)
                        goto notify_others;
                rsyserr(FERROR, stat_errno, "recv_generator: failed to stat %s",
                        full_fname(fname));
 #endif
                if (stat_errno == ENOENT)
                        goto notify_others;
                rsyserr(FERROR, stat_errno, "recv_generator: failed to stat %s",
                        full_fname(fname));
-               return;
+               goto cleanup;
        }
 
        }
 
-       if (append_mode > 0 && st.st_size > F_LENGTH(file))
-               return;
+       if (append_mode > 0 && sx.st.st_size > F_LENGTH(file))
+               goto cleanup;
 
        if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
                ;
        else if (fnamecmp_type == FNAMECMP_FUZZY)
                ;
 
        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)
                if (partialptr) {
                        do_unlink(partialptr);
                        handle_partial_dir(partialptr, PDIR_DELETE);
                }
                if (itemizing)
-                       itemize(file, ndx, statret, &st, 0, 0, NULL);
-               set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT);
+                       itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
+               set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
-                       finish_hard_link(file, fname, &st, itemizing, code, -1);
+                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
 #endif
                if (remove_source_files != 1)
 #endif
                if (remove_source_files != 1)
-                       return;
+                       goto cleanup;
          return_with_success:
                if (!dry_run)
                        send_msg_int(MSG_SUCCESS, ndx);
          return_with_success:
                if (!dry_run)
                        send_msg_int(MSG_SUCCESS, ndx);
-               return;
+               goto cleanup;
        }
 
   prepare_to_open:
        if (partialptr) {
        }
 
   prepare_to_open:
        if (partialptr) {
-               st = partial_st;
+               sx.st = partial_st;
                fnamecmp = partialptr;
                fnamecmp_type = FNAMECMP_PARTIAL_DIR;
                statret = 0;
                fnamecmp = partialptr;
                fnamecmp_type = FNAMECMP_PARTIAL_DIR;
                statret = 0;
@@ -1542,7 +1571,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                /* pretend the file didn't exist */
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
                /* pretend the file didn't exist */
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_HLINK_NOT_LAST(file))
-                       return;
+                       goto cleanup;
 #endif
                statret = real_ret = -1;
                goto notify_others;
 #endif
                statret = real_ret = -1;
                goto notify_others;
@@ -1551,7 +1580,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
                if (!(backupptr = get_backup_name(fname))) {
                        close(fd);
        if (inplace && make_backups > 0 && 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);
                }
                if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
                        close(fd);
@@ -1562,7 +1591,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                full_fname(backupptr));
                        unmake_file(back_file);
                        close(fd);
                                full_fname(backupptr));
                        unmake_file(back_file);
                        close(fd);
-                       return;
+                       goto cleanup;
                }
                if ((f_copy = do_open(backupptr,
                    O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
                }
                if ((f_copy = do_open(backupptr,
                    O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
@@ -1570,14 +1599,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                full_fname(backupptr));
                        unmake_file(back_file);
                        close(fd);
                                full_fname(backupptr));
                        unmake_file(back_file);
                        close(fd);
-                       return;
+                       goto cleanup;
                }
                fnamecmp_type = FNAMECMP_BACKUP;
        }
 
        if (verbose > 3) {
                rprintf(FINFO, "gen mapped %s of size %.0f\n",
                }
                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)
        }
 
        if (verbose > 2)
@@ -1601,26 +1630,30 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        iflags |= ITEM_BASIS_TYPE_FOLLOWS;
                if (fnamecmp_type == FNAMECMP_FUZZY)
                        iflags |= ITEM_XNAME_FOLLOWS;
                        iflags |= ITEM_BASIS_TYPE_FOLLOWS;
                if (fnamecmp_type == FNAMECMP_FUZZY)
                        iflags |= ITEM_XNAME_FOLLOWS;
-               itemize(file, -1, real_ret, &real_st, iflags, fnamecmp_type,
+               itemize(fnamecmp, file, -1, real_ret, &real_sx, iflags, fnamecmp_type,
                        fuzzy_file ? fuzzy_file->basename : NULL);
                        fuzzy_file ? fuzzy_file->basename : NULL);
+#ifdef SUPPORT_ACLS
+               if (preserve_acls)
+                       free_acl(&real_sx);
+#endif
        }
 
        if (!do_xfers) {
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
        }
 
        if (!do_xfers) {
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
-                       finish_hard_link(file, fname, &st, itemizing, code, -1);
+                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
 #endif
 #endif
-               return;
+               goto cleanup;
        }
        if (read_batch)
        }
        if (read_batch)
-               return;
+               goto cleanup;
 
        if (statret != 0 || whole_file) {
                write_sum_head(f_out, NULL);
 
        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);
 
        if (f_copy >= 0) {
                close(f_copy);
@@ -1633,6 +1666,13 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        }
 
        close(fd);
        }
 
        close(fd);
+
+  cleanup:
+#ifdef SUPPORT_ACLS
+       if (preserve_acls)
+               free_acl(&sx);
+#endif
+       return;
 }
 
 static void touch_up_dirs(struct file_list *flist, int ndx)
 }
 
 static void touch_up_dirs(struct file_list *flist, int ndx)
@@ -1803,6 +1843,8 @@ void generate_files(int f_out, const char *local_name)
         * notice that and let us know via the redo pipe (or its closing). */
        ignore_timeout = 1;
 
         * notice that and let us know via the redo pipe (or its closing). */
        ignore_timeout = 1;
 
+       dflt_perms = (ACCESSPERMS & ~orig_umask);
+
        do {
                if (inc_recurse && delete_during && cur_flist->ndx_start) {
                        struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
        do {
                if (inc_recurse && delete_during && cur_flist->ndx_start) {
                        struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
diff --git a/hlink.c b/hlink.c
index 010389d..f52c0ed 100644 (file)
--- a/hlink.c
+++ b/hlink.c
@@ -26,6 +26,7 @@ extern int verbose;
 extern int dry_run;
 extern int do_xfers;
 extern int link_dest;
 extern int dry_run;
 extern int do_xfers;
 extern int link_dest;
+extern int preserve_acls;
 extern int make_backups;
 extern int protocol_version;
 extern int remove_source_files;
 extern int make_backups;
 extern int protocol_version;
 extern int remove_source_files;
@@ -267,15 +268,15 @@ void match_hard_links(void)
 }
 
 static int maybe_hard_link(struct file_struct *file, int ndx,
 }
 
 static int maybe_hard_link(struct file_struct *file, int ndx,
-                          const char *fname, int statret, STRUCT_STAT *stp,
+                          const char *fname, int statret, statx *sxp,
                           const char *oldname, STRUCT_STAT *old_stp,
                           const char *realname, int itemizing, enum logcode code)
 {
        if (statret == 0) {
                           const char *oldname, STRUCT_STAT *old_stp,
                           const char *realname, int itemizing, enum logcode code)
 {
        if (statret == 0) {
-               if (stp->st_dev == old_stp->st_dev
-                && stp->st_ino == old_stp->st_ino) {
+               if (sxp->st.st_dev == old_stp->st_dev
+                && sxp->st.st_ino == old_stp->st_ino) {
                        if (itemizing) {
                        if (itemizing) {
-                               itemize(file, ndx, statret, stp,
+                               itemize(fname, file, ndx, statret, sxp,
                                        ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
                                        0, "");
                        }
                                        ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
                                        0, "");
                        }
@@ -296,7 +297,7 @@ static int maybe_hard_link(struct file_struct *file, int ndx,
 
        if (hard_link_one(file, fname, oldname, 0)) {
                if (itemizing) {
 
        if (hard_link_one(file, fname, oldname, 0)) {
                if (itemizing) {
-                       itemize(file, ndx, statret, stp,
+                       itemize(fname, file, ndx, statret, sxp,
                                ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
                                realname);
                }
                                ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
                                realname);
                }
@@ -310,7 +311,7 @@ static int maybe_hard_link(struct file_struct *file, int ndx,
 /* Only called if FLAG_HLINKED is set and FLAG_HLINK_FIRST is not.  Returns:
  * 0 = process the file, 1 = skip the file, -1 = error occurred. */
 int hard_link_check(struct file_struct *file, int ndx, const char *fname,
 /* Only called if FLAG_HLINKED is set and FLAG_HLINK_FIRST is not.  Returns:
  * 0 = process the file, 1 = skip the file, -1 = error occurred. */
 int hard_link_check(struct file_struct *file, int ndx, const char *fname,
-                   int statret, STRUCT_STAT *stp, int itemizing,
+                   int statret, statx *sxp, int itemizing,
                    enum logcode code)
 {
        STRUCT_STAT prev_st;
                    enum logcode code)
 {
        STRUCT_STAT prev_st;
@@ -361,18 +362,20 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
        if (statret < 0 && basis_dir[0] != NULL) {
                /* If we match an alt-dest item, we don't output this as a change. */
                char cmpbuf[MAXPATHLEN];
        if (statret < 0 && basis_dir[0] != NULL) {
                /* If we match an alt-dest item, we don't output this as a change. */
                char cmpbuf[MAXPATHLEN];
-               STRUCT_STAT alt_st;
+               statx alt_sx;
                int j = 0;
                int j = 0;
+#ifdef SUPPORT_ACLS
+               alt_sx.acc_acl = alt_sx.def_acl = NULL;
+#endif
                do {
                        pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
                do {
                        pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
-                       if (link_stat(cmpbuf, &alt_st, 0) < 0)
+                       if (link_stat(cmpbuf, &alt_sx.st, 0) < 0)
                                continue;
                        if (link_dest) {
                                continue;
                        if (link_dest) {
-                               if (prev_st.st_dev != alt_st.st_dev
-                                || prev_st.st_ino != alt_st.st_ino)
+                               if (prev_st.st_dev != alt_sx.st.st_dev
+                                || prev_st.st_ino != alt_sx.st.st_ino)
                                        continue;
                                statret = 1;
                                        continue;
                                statret = 1;
-                               *stp = alt_st;
                                if (verbose < 2 || !stdout_format_has_i) {
                                        itemizing = 0;
                                        code = FNONE;
                                if (verbose < 2 || !stdout_format_has_i) {
                                        itemizing = 0;
                                        code = FNONE;
@@ -381,16 +384,32 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
                                }
                                break;
                        }
                                }
                                break;
                        }
-                       if (!unchanged_file(cmpbuf, file, &alt_st))
+                       if (!unchanged_file(cmpbuf, file, &alt_sx.st))
                                continue;
                        statret = 1;
                                continue;
                        statret = 1;
-                       *stp = alt_st;
-                       if (unchanged_attrs(file, &alt_st))
+                       if (unchanged_attrs(cmpbuf, file, &alt_sx))
                                break;
                } while (basis_dir[++j] != NULL);
                                break;
                } while (basis_dir[++j] != NULL);
+               if (statret == 1) {
+                       sxp->st = alt_sx.st;
+#ifdef SUPPORT_ACLS
+                       if (preserve_acls && !S_ISLNK(file->mode)) {
+                               if (!ACL_READY(*sxp))
+                                       get_acl(cmpbuf, sxp);
+                               else {
+                                       sxp->acc_acl = alt_sx.acc_acl;
+                                       sxp->def_acl = alt_sx.def_acl;
+                               }
+                       }
+#endif
+               }
+#ifdef SUPPORT_ACLS
+               else if (preserve_acls)
+                       free_acl(&alt_sx);
+#endif
        }
 
        }
 
-       if (maybe_hard_link(file, ndx, fname, statret, stp, prev_name, &prev_st,
+       if (maybe_hard_link(file, ndx, fname, statret, sxp, prev_name, &prev_st,
                            realname, itemizing, code) < 0)
                return -1;
 
                            realname, itemizing, code) < 0)
                return -1;
 
@@ -425,7 +444,8 @@ void finish_hard_link(struct file_struct *file, const char *fname,
                      STRUCT_STAT *stp, int itemizing, enum logcode code,
                      int alt_dest)
 {
                      STRUCT_STAT *stp, int itemizing, enum logcode code,
                      int alt_dest)
 {
-       STRUCT_STAT st, prev_st;
+       statx prev_sx;
+       STRUCT_STAT st;
        char alt_name[MAXPATHLEN], *prev_name;
        const char *our_name;
        int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
        char alt_name[MAXPATHLEN], *prev_name;
        const char *our_name;
        int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
@@ -449,14 +469,24 @@ void finish_hard_link(struct file_struct *file, const char *fname,
        } else
                our_name = fname;
 
        } else
                our_name = fname;
 
+#ifdef SUPPORT_ACLS
+       prev_sx.acc_acl = prev_sx.def_acl = NULL;
+#endif
+
        while ((ndx = prev_ndx) >= 0) {
        while ((ndx = prev_ndx) >= 0) {
+               int val;
                file = FPTR(ndx);
                file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
                prev_ndx = F_HL_PREV(file);
                prev_name = f_name(file, NULL);
                file = FPTR(ndx);
                file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
                prev_ndx = F_HL_PREV(file);
                prev_name = f_name(file, NULL);
-               prev_statret = link_stat(prev_name, &prev_st, 0);
-               if (maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_st,
-                                   our_name, stp, fname, itemizing, code) < 0)
+               prev_statret = link_stat(prev_name, &prev_sx.st, 0);
+               val = maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_sx,
+                                     our_name, stp, fname, itemizing, code);
+#ifdef SUPPORT_ACLS
+               if (preserve_acls)
+                       free_acl(&prev_sx);
+#endif
+               if (val < 0)
                        continue;
                if (remove_source_files == 1 && do_xfers)
                        send_msg_int(MSG_SUCCESS, ndx);
                        continue;
                if (remove_source_files == 1 && do_xfers)
                        send_msg_int(MSG_SUCCESS, ndx);
diff --git a/io.c b/io.c
index b69816a..6abaab2 100644 (file)
--- a/io.c
+++ b/io.c
@@ -100,10 +100,10 @@ static int active_filecnt = 0;
 static OFF_T active_bytecnt = 0;
 
 static char int_byte_cnt[64] = {
 static OFF_T active_bytecnt = 0;
 
 static char int_byte_cnt[64] = {
-       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* (00 - 3F)/4 */
-       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* (40 - 7F)/4 */
-       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* (80 - BF)/4 */
-       5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 8, 9, /* (C0 - FF)/4 */
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (00 - 3F)/4 */
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (40 - 7F)/4 */
+       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* (80 - BF)/4 */
+       3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 7, /* (C0 - FF)/4 */
 };
 
 static void readfd(int fd, char *buffer, size_t N);
 };
 
 static void readfd(int fd, char *buffer, size_t N);
@@ -998,6 +998,37 @@ unsigned short read_shortint(int f)
        return (UVAL(b, 1) << 8) + UVAL(b, 0);
 }
 
        return (UVAL(b, 1) << 8) + UVAL(b, 0);
 }
 
+int32 read_abbrevint(int f)
+{
+       int32 num;
+       char b[5];
+       int cnt;
+       readfd(f, b, 1);
+       cnt = int_byte_cnt[CVAL(b, 0) / 4];
+       if (cnt > 1)
+               readfd(f, b + 1, cnt - 1);
+       switch (cnt) {
+       case 1:
+               num = NVAL1(b, 0);
+               break;
+       case 2:
+               num = NVAL2(b, 0x80);
+               break;
+       case 3:
+               num = NVAL3(b, 0xC0);
+               break;
+       case 4:
+               num = NVAL4(b, 0xE0);
+               break;
+       case 5:
+               num = NVAL4(b+1, 0);
+               break;
+       default:
+               exit_cleanup(RERR_PROTOCOL); /* impossible... */
+       }
+       return num;
+}
+
 int32 read_int(int f)
 {
        char b[4];
 int32 read_int(int f)
 {
        char b[4];
@@ -1033,7 +1064,7 @@ int64 read_longint(int f)
        } else {
                int cnt;
                readfd(f, b, 3);
        } else {
                int cnt;
                readfd(f, b, 3);
-               cnt = int_byte_cnt[CVAL(b, 0) / 4];
+               cnt = int_byte_cnt[CVAL(b, 0) / 4] + 2;
 #if SIZEOF_INT64 < 8
                if (cnt > 5 || (cnt == 5 && (CVAL(b,0)&0x3F || CVAL(b,1)&0x80))) {
                        rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
 #if SIZEOF_INT64 < 8
                if (cnt > 5 || (cnt == 5 && (CVAL(b,0)&0x3F || CVAL(b,1)&0x80))) {
                        rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
@@ -1373,6 +1404,37 @@ void write_shortint(int f, unsigned short x)
        writefd(f, b, 2);
 }
 
        writefd(f, b, 2);
 }
 
+void write_abbrevint(int f, int32 x)
+{
+       char b[5];
+       if ((uint32)x < ((uint32)1<<(1*8-1))) {
+               b[0] = (char)x;
+               writefd(f, b, 1);
+       } else if ((uint32)x < ((uint32)1<<(2*8-2))) {
+               b[0] = (char)(x >> 8) | 0x80;
+               b[1] = (char)x;
+               writefd(f, b, 2);
+       } else if ((uint32)x < ((uint32)1<<(3*8-3))) {
+               b[0] = (char)(x >> 16) | 0xC0;
+               b[1] = (char)(x >> 8);
+               b[2] = (char)x;
+               writefd(f, b, 3);
+       } else if ((uint32)x < ((uint32)1<<(4*8-4))) {
+               b[0] = (char)(x >> 24) | 0xE0;
+               b[1] = (char)(x >> 16);
+               b[2] = (char)(x >> 8);
+               b[3] = (char)x;
+               writefd(f, b, 4);
+       } else {
+               b[0] = 0xF0;
+               b[1] = (char)(x >> 24);
+               b[2] = (char)(x >> 16);
+               b[3] = (char)(x >> 8);
+               b[4] = (char)x;
+               writefd(f, b, 5);
+       }
+}
+
 void write_int(int f, int32 x)
 {
        char b[4];
 void write_int(int f, int32 x)
 {
        char b[4];
diff --git a/lib/sysacls.c b/lib/sysacls.c
new file mode 100644 (file)
index 0000000..0ad8075
--- /dev/null
@@ -0,0 +1,3251 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba system utilities for ACL support.
+   Copyright (C) Jeremy Allison 2000.
+   
+   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.
+*/
+
+#include "rsync.h"
+#include "sysacls.h" /****** ADDED ******/
+
+#ifdef SUPPORT_ACLS
+
+/****** EXTRAS -- THESE ITEMS ARE NOT FROM THE SAMBA SOURCE ******/
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG(x,y)
+
+void SAFE_FREE(void *mem)
+{
+       if (mem)
+               free(mem);
+}
+
+char *uidtoname(uid_t uid)
+{
+       static char idbuf[12];
+       struct passwd *pw;
+
+       if ((pw = getpwuid(uid)) == NULL) {
+               slprintf(idbuf, sizeof(idbuf)-1, "%ld", (long)uid);
+               return idbuf;
+       }
+       return pw->pw_name;
+}
+/****** EXTRAS -- END ******/
+
+/*
+ This file wraps all differing system ACL interfaces into a consistent
+ one based on the POSIX interface. It also returns the correct errors
+ for older UNIX systems that don't support ACLs.
+
+ The interfaces that each ACL implementation must support are as follows :
+
+ int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+ int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+ int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p
+ void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+ SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+ SMB_ACL_T sys_acl_get_fd(int fd)
+ int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset);
+ int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+ char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+ SMB_ACL_T sys_acl_init( int count)
+ int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+ int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+ int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+ int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+ int sys_acl_valid( SMB_ACL_T theacl )
+ int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+ int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+ int sys_acl_delete_def_file(const char *path)
+
+ This next one is not POSIX complient - but we *have* to have it !
+ More POSIX braindamage.
+
+ int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+
+ The generic POSIX free is the following call. We split this into
+ several different free functions as we may need to add tag info
+ to structures when emulating the POSIX interface.
+
+ int sys_acl_free( void *obj_p)
+
+ The calls we actually use are :
+
+ int sys_acl_free_text(char *text) - free acl_to_text
+ int sys_acl_free_acl(SMB_ACL_T posix_acl)
+ int sys_acl_free_qualifier(void *qualifier, SMB_ACL_TAG_T tagtype)
+
+*/
+
+#if defined(HAVE_POSIX_ACLS)
+
+/* Identity mapping - easy. */
+
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       return acl_get_entry( the_acl, entry_id, entry_p);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+       return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+       return acl_get_file( path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       return acl_get_fd(fd);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+       return acl_clear_perms(permset);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return acl_add_perm(permset, perm);
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+#if defined(HAVE_ACL_GET_PERM_NP)
+       /*
+        * Required for TrustedBSD-based ACL implementations where
+        * non-POSIX.1e functions are denoted by a _np (non-portable)
+        * suffix.
+        */
+       return acl_get_perm_np(permset, perm);
+#else
+       return acl_get_perm(permset, perm);
+#endif
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+       return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+       return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       return acl_create_entry(pacl, pentry);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+       return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+       return acl_valid(theacl);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       return acl_set_file(name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+       return acl_set_fd(fd, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+       return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl) 
+{
+       return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, UNUSED(SMB_ACL_TAG_T tagtype))
+{
+       return acl_free(qual);
+}
+
+#elif defined(HAVE_TRU64_ACLS)
+/*
+ * The interface to DEC/Compaq Tru64 UNIX ACLs
+ * is based on Draft 13 of the POSIX spec which is
+ * slightly different from the Draft 16 interface.
+ * 
+ * Also, some of the permset manipulation functions
+ * such as acl_clear_perm() and acl_add_perm() appear
+ * to be broken on Tru64 so we have to manipulate
+ * the permission bits in the permset directly.
+ */
+int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_ENTRY_T entry;
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) {
+               return -1;
+       }
+
+       errno = 0;
+       if ((entry = acl_get_entry(the_acl)) != NULL) {
+               *entry_p = entry;
+               return 1;
+       }
+
+       return errno ? -1 : 0;
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       return acl_get_tag_type( entry_d, tag_type_p);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       return acl_get_permset( entry_d, permset_p);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+       return acl_get_qualifier( entry_d);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+       return acl_get_file((char *)path_p, type);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       return acl_get_fd(fd, ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+       *permset = 0;           /* acl_clear_perm() is broken on Tru64  */
+
+       return 0;
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       if (perm & ~(SMB_ACL_READ | SMB_ACL_WRITE | SMB_ACL_EXECUTE)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *permset |= perm;       /* acl_add_perm() is broken on Tru64    */
+
+       return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return *permset & perm; /* Tru64 doesn't have acl_get_perm() */
+}
+
+char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen)
+{
+       return acl_to_text( the_acl, plen);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+       return acl_init(count);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       SMB_ACL_ENTRY_T entry;
+
+       if ((entry = acl_create_entry(pacl)) == NULL) {
+               return -1;
+       }
+
+       *pentry = entry;
+       return 0;
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       return acl_set_tag_type(entry, tagtype);
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+       return acl_set_qualifier(entry, qual);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       return acl_set_permset(entry, permset);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+       acl_entry_t     entry;
+
+       return acl_valid(theacl, &entry);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       return acl_set_file((char *)name, acltype, theacl);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+       return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       return acl_delete_def_file((char *)name);
+}
+
+int sys_acl_free_text(char *text)
+{
+       /*
+        * (void) cast and explicit return 0 are for DEC UNIX
+        *  which just #defines acl_free_text() to be free()
+        */
+       (void) acl_free_text(text);
+       return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T the_acl) 
+{
+       return acl_free(the_acl);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return acl_free_qualifier(qual, tagtype);
+}
+
+#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS)
+
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ *     sys_acl_valid()
+ *     sys_acl_set_file()
+ *     sys_acl_set_fd()
+ */
+
+/*
+ * The only difference between Solaris and UnixWare / OpenUNIX is
+ * that the #defines for the ACL operations have different names
+ */
+#if defined(HAVE_UNIXWARE_ACLS)
+
+#define        SETACL          ACL_SET
+#define        GETACL          ACL_GET
+#define        GETACLCNT       ACL_CNT
+
+#endif
+
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_p == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY) {
+               acl_d->next = 0;
+       }
+
+       if (acl_d->next < 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->next >= acl_d->count) {
+               return 0;
+       }
+
+       *entry_p = &acl_d->acl[acl_d->next++];
+
+       return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+       *type_p = entry_d->a_type;
+
+       return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       *permset_p = &entry_d->a_perm;
+
+       return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+       if (entry_d->a_type != SMB_ACL_USER
+           && entry_d->a_type != SMB_ACL_GROUP) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * GETACL will be unless you first call GETACLCNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use GETACLCNT to find out the actual
+ * size, reallocate the ACL buffer, and then call GETACL again.
+ */
+
+#define        INITIAL_ACL_SIZE        16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+       SMB_ACL_T       acl_d;
+       int             count;          /* # of ACL entries allocated   */
+       int             naccess;        /* # of access ACL entries      */
+       int             ndefault;       /* # of default ACL entries     */
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       count = INITIAL_ACL_SIZE;
+       if ((acl_d = sys_acl_init(count)) == NULL) {
+               return NULL;
+       }
+
+       /*
+        * If there isn't enough space for the ACL entries we use
+        * GETACLCNT to determine the actual number of ACL entries
+        * reallocate and try again. This is in a loop because it
+        * is possible that someone else could modify the ACL and
+        * increase the number of entries between the call to
+        * GETACLCNT and the call to GETACL.
+        */
+       while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0
+           && errno == ENOSPC) {
+
+               sys_acl_free_acl(acl_d);
+
+               if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) {
+                       return NULL;
+               }
+
+               if ((acl_d = sys_acl_init(count)) == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (count < 0) {
+               sys_acl_free_acl(acl_d);
+               return NULL;
+       }
+
+       /*
+        * calculate the number of access and default ACL entries
+        *
+        * Note: we assume that the acl() system call returned a
+        * well formed ACL which is sorted so that all of the
+        * access ACL entries preceed any default ACL entries
+        */
+       for (naccess = 0; naccess < count; naccess++) {
+               if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+                       break;
+       }
+       ndefault = count - naccess;
+       
+       /*
+        * if the caller wants the default ACL we have to copy
+        * the entries down to the start of the acl[] buffer
+        * and mask out the ACL_DEFAULT flag from the type field
+        */
+       if (type == SMB_ACL_TYPE_DEFAULT) {
+               int     i, j;
+
+               for (i = 0, j = naccess; i < ndefault; i++, j++) {
+                       acl_d->acl[i] = acl_d->acl[j];
+                       acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+               }
+
+               acl_d->count = ndefault;
+       } else {
+               acl_d->count = naccess;
+       }
+
+       return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       SMB_ACL_T       acl_d;
+       int             count;          /* # of ACL entries allocated   */
+       int             naccess;        /* # of access ACL entries      */
+
+       count = INITIAL_ACL_SIZE;
+       if ((acl_d = sys_acl_init(count)) == NULL) {
+               return NULL;
+       }
+
+       while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0
+           && errno == ENOSPC) {
+
+               sys_acl_free_acl(acl_d);
+
+               if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) {
+                       return NULL;
+               }
+
+               if ((acl_d = sys_acl_init(count)) == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (count < 0) {
+               sys_acl_free_acl(acl_d);
+               return NULL;
+       }
+
+       /*
+        * calculate the number of access ACL entries
+        */
+       for (naccess = 0; naccess < count; naccess++) {
+               if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+                       break;
+       }
+       
+       acl_d->count = naccess;
+
+       return acl_d;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+       *permset_d = 0;
+
+       return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+           && perm != SMB_ACL_EXECUTE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (permset_d == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *permset_d |= perm;
+
+       return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+       int     i;
+       int     len, maxlen;
+       char    *text;
+
+       /*
+        * use an initial estimate of 20 bytes per ACL entry
+        * when allocating memory for the text representation
+        * of the ACL
+        */
+       len     = 0;
+       maxlen  = 20 * acl_d->count;
+       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 group    *gr;
+               char            tagbuf[12];
+               char            idbuf[12];
+               char            *tag;
+               char            *id     = "";
+               char            perms[4];
+               int             nbytes;
+
+               switch (ap->a_type) {
+                       /*
+                        * for debugging purposes it's probably more
+                        * useful to dump unknown tag types rather
+                        * than just returning an error
+                        */
+                       default:
+                               slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+                                       ap->a_type);
+                               tag = tagbuf;
+                               slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                       (long)ap->a_id);
+                               id = idbuf;
+                               break;
+
+                       case SMB_ACL_USER:
+                               id = uidtoname(ap->a_id);
+                       case SMB_ACL_USER_OBJ:
+                               tag = "user";
+                               break;
+
+                       case SMB_ACL_GROUP:
+                               if ((gr = getgrgid(ap->a_id)) == NULL) {
+                                       slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                               (long)ap->a_id);
+                                       id = idbuf;
+                               } else {
+                                       id = gr->gr_name;
+                               }
+                       case SMB_ACL_GROUP_OBJ:
+                               tag = "group";
+                               break;
+
+                       case SMB_ACL_OTHER:
+                               tag = "other";
+                               break;
+
+                       case SMB_ACL_MASK:
+                               tag = "mask";
+                               break;
+
+               }
+
+               perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+               perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+               perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+               perms[3] = '\0';
+
+               /*          <tag>      :  <qualifier>   :  rwx \n  \0 */
+               nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+               /*
+                * If this entry would overflow the buffer
+                * allocate enough additional memory for this
+                * entry and an estimate of another 20 bytes
+                * for each entry still to be processed
+                */
+               if ((len + nbytes) > maxlen) {
+                       char *oldtext = text;
+
+                       maxlen += nbytes + 20 * (acl_d->count - i);
+
+                       if ((text = SMB_REALLOC(oldtext, maxlen)) == NULL) {
+                               SAFE_FREE(oldtext);
+                               errno = ENOMEM;
+                               return NULL;
+                       }
+               }
+
+               slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+               len += nbytes - 1;
+       }
+
+       if (len_p)
+               *len_p = len;
+
+       return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+       SMB_ACL_T       a;
+
+       if (count < 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /*
+        * note that since the definition of the structure pointed
+        * to by the SMB_ACL_T includes the first element of the
+        * acl[] array, this actually allocates an ACL with room
+        * for (count+1) entries
+        */
+       if ((a = (SMB_ACL_T)SMB_MALLOC(sizeof(struct SMB_ACL_T) + count * sizeof(struct acl))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       a->size = count + 1;
+       a->count = 0;
+       a->next = -1;
+
+       return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_T       acl_d;
+       SMB_ACL_ENTRY_T entry_d;
+
+       if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->count >= acl_d->size) {
+               errno = ENOSPC;
+               return -1;
+       }
+
+       entry_d         = &acl_d->acl[acl_d->count++];
+       entry_d->a_type = 0;
+       entry_d->a_id   = -1;
+       entry_d->a_perm = 0;
+       *entry_p        = entry_d;
+
+       return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+       switch (tag_type) {
+               case SMB_ACL_USER:
+               case SMB_ACL_USER_OBJ:
+               case SMB_ACL_GROUP:
+               case SMB_ACL_GROUP_OBJ:
+               case SMB_ACL_OTHER:
+               case SMB_ACL_MASK:
+                       entry_d->a_type = tag_type;
+                       break;
+               default:
+                       errno = EINVAL;
+                       return -1;
+       }
+
+       return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+       if (entry_d->a_type != SMB_ACL_GROUP
+           && entry_d->a_type != SMB_ACL_USER) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       entry_d->a_id = *((id_t *)qual_p);
+
+       return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+       if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+               return EINVAL;
+       }
+
+       entry_d->a_perm = *permset_d;
+
+       return 0;
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+       int     fixmask = (acl_d->count <= 4);
+
+       if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+       return 0;
+}
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+       return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+       struct stat     s;
+       struct acl      *acl_p;
+       int             acl_count;
+       struct acl      *acl_buf        = NULL;
+       int             ret;
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       acl_p           = &acl_d->acl[0];
+       acl_count       = acl_d->count;
+
+       /*
+        * if it's a directory there is extra work to do
+        * since the acl() system call will replace both
+        * the access ACLs and the default ACLs (if any)
+        */
+       if (stat(name, &s) != 0) {
+               return -1;
+       }
+       if (S_ISDIR(s.st_mode)) {
+               SMB_ACL_T       acc_acl;
+               SMB_ACL_T       def_acl;
+               SMB_ACL_T       tmp_acl;
+               int             i;
+
+               if (type == SMB_ACL_TYPE_ACCESS) {
+                       acc_acl = acl_d;
+                       def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+               } else {
+                       def_acl = acl_d;
+                       acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+               }
+
+               if (tmp_acl == NULL) {
+                       return -1;
+               }
+
+               /*
+                * allocate a temporary buffer for the complete ACL
+                */
+               acl_count = acc_acl->count + def_acl->count;
+               acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+               if (acl_buf == NULL) {
+                       sys_acl_free_acl(tmp_acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               /*
+                * copy the access control and default entries into the buffer
+                */
+               memcpy(&acl_buf[0], &acc_acl->acl[0],
+                       acc_acl->count * sizeof(acl_buf[0]));
+
+               memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+                       def_acl->count * sizeof(acl_buf[0]));
+
+               /*
+                * set the ACL_DEFAULT flag on the default entries
+                */
+               for (i = acc_acl->count; i < acl_count; i++) {
+                       acl_buf[i].a_type |= ACL_DEFAULT;
+               }
+
+               sys_acl_free_acl(tmp_acl);
+
+       } else if (type != SMB_ACL_TYPE_ACCESS) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       ret = acl(name, SETACL, acl_count, acl_p);
+
+       SAFE_FREE(acl_buf);
+
+       return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+       SMB_ACL_T       acl_d;
+       int             ret;
+
+       /*
+        * fetching the access ACL and rewriting it has
+        * the effect of deleting the default ACL
+        */
+       if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+               return -1;
+       }
+
+       ret = acl(path, SETACL, acl_d->count, acl_d->acl);
+
+       sys_acl_free_acl(acl_d);
+       
+       return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+       SAFE_FREE(text);
+       return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d) 
+{
+       SAFE_FREE(acl_d);
+       return 0;
+}
+
+int sys_acl_free_qualifier(UNUSED(void *qual), UNUSED(SMB_ACL_TAG_T tagtype))
+{
+       return 0;
+}
+
+#elif defined(HAVE_HPUX_ACLS)
+#include <dl.h>
+
+/*
+ * Based on the Solaris/SCO code - with modifications.
+ */
+
+/*
+ * Note that while this code implements sufficient functionality
+ * to support the sys_acl_* interfaces it does not provide all
+ * of the semantics of the POSIX ACL interfaces.
+ *
+ * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned
+ * from a call to sys_acl_get_entry() should not be assumed to be
+ * valid after calling any of the following functions, which may
+ * reorder the entries in the ACL.
+ *
+ *     sys_acl_valid()
+ *     sys_acl_set_file()
+ *     sys_acl_set_fd()
+ */
+
+/* This checks if the POSIX ACL system call is defined */
+/* which basically corresponds to whether JFS 3.3 or   */
+/* higher is installed. If acl() was called when it    */
+/* isn't defined, it causes the process to core dump   */
+/* so it is important to check this and avoid acl()    */
+/* calls if it isn't there.                            */
+
+static BOOL hpux_acl_call_presence(void)
+{
+
+       shl_t handle = NULL;
+       void *value;
+       int ret_val=0;
+       static BOOL already_checked=0;
+
+       if(already_checked)
+               return True;
+
+
+       ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value);
+
+       if(ret_val != 0) {
+               DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n",
+                       ret_val, errno, strerror(errno)));
+               DEBUG(5,("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n"));
+               return False;
+       }
+
+       DEBUG(10,("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n"));
+
+       already_checked = True;
+       return True;
+}
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_p == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY) {
+               acl_d->next = 0;
+       }
+
+       if (acl_d->next < 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->next >= acl_d->count) {
+               return 0;
+       }
+
+       *entry_p = &acl_d->acl[acl_d->next++];
+
+       return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+       *type_p = entry_d->a_type;
+
+       return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       *permset_p = &entry_d->a_perm;
+
+       return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+       if (entry_d->a_type != SMB_ACL_USER
+           && entry_d->a_type != SMB_ACL_GROUP) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       return &entry_d->a_id;
+}
+
+/*
+ * There is no way of knowing what size the ACL returned by
+ * ACL_GET will be unless you first call ACL_CNT which means
+ * making an additional system call.
+ *
+ * In the hope of avoiding the cost of the additional system
+ * call in most cases, we initially allocate enough space for
+ * an ACL with INITIAL_ACL_SIZE entries. If this turns out to
+ * be too small then we use ACL_CNT to find out the actual
+ * size, reallocate the ACL buffer, and then call ACL_GET again.
+ */
+
+#define        INITIAL_ACL_SIZE        16
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+       SMB_ACL_T       acl_d;
+       int             count;          /* # of ACL entries allocated   */
+       int             naccess;        /* # of access ACL entries      */
+       int             ndefault;       /* # of default ACL entries     */
+
+       if(hpux_acl_call_presence() == False) {
+               /* Looks like we don't have the acl() system call on HPUX. 
+                * May be the system doesn't have the latest version of JFS.
+                */
+               return NULL; 
+       }
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       count = INITIAL_ACL_SIZE;
+       if ((acl_d = sys_acl_init(count)) == NULL) {
+               return NULL;
+       }
+
+       /*
+        * If there isn't enough space for the ACL entries we use
+        * ACL_CNT to determine the actual number of ACL entries
+        * reallocate and try again. This is in a loop because it
+        * is possible that someone else could modify the ACL and
+        * increase the number of entries between the call to
+        * ACL_CNT and the call to ACL_GET.
+        */
+       while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) {
+
+               sys_acl_free_acl(acl_d);
+
+               if ((count = acl(path_p, ACL_CNT, 0, NULL)) < 0) {
+                       return NULL;
+               }
+
+               if ((acl_d = sys_acl_init(count)) == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (count < 0) {
+               sys_acl_free_acl(acl_d);
+               return NULL;
+       }
+
+       /*
+        * calculate the number of access and default ACL entries
+        *
+        * Note: we assume that the acl() system call returned a
+        * well formed ACL which is sorted so that all of the
+        * access ACL entries preceed any default ACL entries
+        */
+       for (naccess = 0; naccess < count; naccess++) {
+               if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
+                       break;
+       }
+       ndefault = count - naccess;
+       
+       /*
+        * if the caller wants the default ACL we have to copy
+        * the entries down to the start of the acl[] buffer
+        * and mask out the ACL_DEFAULT flag from the type field
+        */
+       if (type == SMB_ACL_TYPE_DEFAULT) {
+               int     i, j;
+
+               for (i = 0, j = naccess; i < ndefault; i++, j++) {
+                       acl_d->acl[i] = acl_d->acl[j];
+                       acl_d->acl[i].a_type &= ~ACL_DEFAULT;
+               }
+
+               acl_d->count = ndefault;
+       } else {
+               acl_d->count = naccess;
+       }
+
+       return acl_d;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       /*
+        * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+        */
+
+       files_struct *fsp = file_find_fd(fd);
+
+       if (fsp == NULL) {
+               errno = EBADF;
+               return NULL;
+       }
+
+       /*
+        * We know we're in the same conn context. So we
+        * can use the relative path.
+        */
+
+       return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+       *permset_d = 0;
+
+       return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+           && perm != SMB_ACL_EXECUTE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (permset_d == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *permset_d |= perm;
+
+       return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       return *permset_d & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+       int     i;
+       int     len, maxlen;
+       char    *text;
+
+       /*
+        * use an initial estimate of 20 bytes per ACL entry
+        * when allocating memory for the text representation
+        * of the ACL
+        */
+       len     = 0;
+       maxlen  = 20 * acl_d->count;
+       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 group    *gr;
+               char            tagbuf[12];
+               char            idbuf[12];
+               char            *tag;
+               char            *id     = "";
+               char            perms[4];
+               int             nbytes;
+
+               switch (ap->a_type) {
+                       /*
+                        * for debugging purposes it's probably more
+                        * useful to dump unknown tag types rather
+                        * than just returning an error
+                        */
+                       default:
+                               slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x",
+                                       ap->a_type);
+                               tag = tagbuf;
+                               slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                       (long)ap->a_id);
+                               id = idbuf;
+                               break;
+
+                       case SMB_ACL_USER:
+                               id = uidtoname(ap->a_id);
+                       case SMB_ACL_USER_OBJ:
+                               tag = "user";
+                               break;
+
+                       case SMB_ACL_GROUP:
+                               if ((gr = getgrgid(ap->a_id)) == NULL) {
+                                       slprintf(idbuf, sizeof(idbuf)-1, "%ld",
+                                               (long)ap->a_id);
+                                       id = idbuf;
+                               } else {
+                                       id = gr->gr_name;
+                               }
+                       case SMB_ACL_GROUP_OBJ:
+                               tag = "group";
+                               break;
+
+                       case SMB_ACL_OTHER:
+                               tag = "other";
+                               break;
+
+                       case SMB_ACL_MASK:
+                               tag = "mask";
+                               break;
+
+               }
+
+               perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-';
+               perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-';
+               perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-';
+               perms[3] = '\0';
+
+               /*          <tag>      :  <qualifier>   :  rwx \n  \0 */
+               nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1;
+
+               /*
+                * If this entry would overflow the buffer
+                * allocate enough additional memory for this
+                * entry and an estimate of another 20 bytes
+                * for each entry still to be processed
+                */
+               if ((len + nbytes) > maxlen) {
+                       char *oldtext = text;
+
+                       maxlen += nbytes + 20 * (acl_d->count - i);
+
+                       if ((text = SMB_REALLOC(oldtext, maxlen)) == NULL) {
+                               free(oldtext);
+                               errno = ENOMEM;
+                               return NULL;
+                       }
+               }
+
+               slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms);
+               len += nbytes - 1;
+       }
+
+       if (len_p)
+               *len_p = len;
+
+       return text;
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+       SMB_ACL_T       a;
+
+       if (count < 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /*
+        * note that since the definition of the structure pointed
+        * to by the SMB_ACL_T includes the first element of the
+        * acl[] array, this actually allocates an ACL with room
+        * for (count+1) entries
+        */
+       if ((a = SMB_MALLOC(sizeof(struct SMB_ACL_T) + count * sizeof(struct acl))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       a->size = count + 1;
+       a->count = 0;
+       a->next = -1;
+
+       return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_T       acl_d;
+       SMB_ACL_ENTRY_T entry_d;
+
+       if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->count >= acl_d->size) {
+               errno = ENOSPC;
+               return -1;
+       }
+
+       entry_d         = &acl_d->acl[acl_d->count++];
+       entry_d->a_type = 0;
+       entry_d->a_id   = -1;
+       entry_d->a_perm = 0;
+       *entry_p        = entry_d;
+
+       return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+       switch (tag_type) {
+               case SMB_ACL_USER:
+               case SMB_ACL_USER_OBJ:
+               case SMB_ACL_GROUP:
+               case SMB_ACL_GROUP_OBJ:
+               case SMB_ACL_OTHER:
+               case SMB_ACL_MASK:
+                       entry_d->a_type = tag_type;
+                       break;
+               default:
+                       errno = EINVAL;
+                       return -1;
+       }
+
+       return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+       if (entry_d->a_type != SMB_ACL_GROUP
+           && entry_d->a_type != SMB_ACL_USER) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       entry_d->a_id = *((id_t *)qual_p);
+
+       return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+       if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+               return EINVAL;
+       }
+
+       entry_d->a_perm = *permset_d;
+
+       return 0;
+}
+
+/* Structure to capture the count for each type of ACE. */
+
+struct hpux_acl_types {
+       int n_user;
+       int n_def_user;
+       int n_user_obj;
+       int n_def_user_obj;
+
+       int n_group;
+       int n_def_group;
+       int n_group_obj;
+       int n_def_group_obj;
+
+       int n_other;
+       int n_other_obj;
+       int n_def_other_obj;
+
+       int n_class_obj;
+       int n_def_class_obj;
+
+       int n_illegal_obj;
+};
+
+/* count_obj:
+ * Counts the different number of objects in a given array of ACL
+ * structures.
+ * Inputs:
+ *
+ * acl_count      - Count of ACLs in the array of ACL strucutres.
+ * aclp           - Array of ACL structures.
+ * acl_type_count - Pointer to acl_types structure. Should already be
+ *                  allocated.
+ * Output: 
+ *
+ * acl_type_count - This structure is filled up with counts of various 
+ *                  acl types.
+ */
+
+static int hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count)
+{
+       int i;
+
+       memset(acl_type_count, 0, sizeof(struct hpux_acl_types));
+
+       for(i=0;i<acl_count;i++) {
+               switch(aclp[i].a_type) {
+               case USER: 
+                       acl_type_count->n_user++;
+                       break;
+               case USER_OBJ: 
+                       acl_type_count->n_user_obj++;
+                       break;
+               case DEF_USER_OBJ: 
+                       acl_type_count->n_def_user_obj++;
+                       break;
+               case GROUP: 
+                       acl_type_count->n_group++;
+                       break;
+               case GROUP_OBJ: 
+                       acl_type_count->n_group_obj++;
+                       break;
+               case DEF_GROUP_OBJ: 
+                       acl_type_count->n_def_group_obj++;
+                       break;
+               case OTHER_OBJ: 
+                       acl_type_count->n_other_obj++;
+                       break;
+               case DEF_OTHER_OBJ: 
+                       acl_type_count->n_def_other_obj++;
+                       break;
+               case CLASS_OBJ:
+                       acl_type_count->n_class_obj++;
+                       break;
+               case DEF_CLASS_OBJ:
+                       acl_type_count->n_def_class_obj++;
+                       break;
+               case DEF_USER:
+                       acl_type_count->n_def_user++;
+                       break;
+               case DEF_GROUP:
+                       acl_type_count->n_def_group++;
+                       break;
+               default: 
+                       acl_type_count->n_illegal_obj++;
+                       break;
+               }
+       }
+}
+
+/* swap_acl_entries:  Swaps two ACL entries. 
+ *
+ * Inputs: aclp0, aclp1 - ACL entries to be swapped.
+ */
+
+static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
+{
+       struct acl temp_acl;
+
+       temp_acl.a_type = aclp0->a_type;
+       temp_acl.a_id = aclp0->a_id;
+       temp_acl.a_perm = aclp0->a_perm;
+
+       aclp0->a_type = aclp1->a_type;
+       aclp0->a_id = aclp1->a_id;
+       aclp0->a_perm = aclp1->a_perm;
+
+       aclp1->a_type = temp_acl.a_type;
+       aclp1->a_id = temp_acl.a_id;
+       aclp1->a_perm = temp_acl.a_perm;
+}
+
+/* prohibited_duplicate_type
+ * Identifies if given ACL type can have duplicate entries or 
+ * not.
+ *
+ * Inputs: acl_type - ACL Type.
+ *
+ * Outputs: 
+ *
+ * Return.. 
+ *
+ * True - If the ACL type matches any of the prohibited types.
+ * False - If the ACL type doesn't match any of the prohibited types.
+ */ 
+
+static BOOL hpux_prohibited_duplicate_type(int acl_type)
+{
+       switch(acl_type) {
+               case USER:
+               case GROUP:
+               case DEF_USER: 
+               case DEF_GROUP:
+                       return True;
+               default:
+                       return False;
+       }
+}
+
+/* get_needed_class_perm
+ * Returns the permissions of a ACL structure only if the ACL
+ * type matches one of the pre-determined types for computing 
+ * CLASS_OBJ permissions.
+ *
+ * Inputs: aclp - Pointer to ACL structure.
+ */
+
+static int hpux_get_needed_class_perm(struct acl *aclp)
+{
+       switch(aclp->a_type) {
+               case USER: 
+               case GROUP_OBJ: 
+               case GROUP: 
+               case DEF_USER_OBJ: 
+               case DEF_USER:
+               case DEF_GROUP_OBJ: 
+               case DEF_GROUP:
+               case DEF_CLASS_OBJ:
+               case DEF_OTHER_OBJ: 
+                       return aclp->a_perm;
+               default: 
+                       return 0;
+       }
+}
+
+/* acl_sort for HPUX.
+ * Sorts the array of ACL structures as per the description in
+ * aclsort man page. Refer to aclsort man page for more details
+ *
+ * Inputs:
+ *
+ * acl_count - Count of ACLs in the array of ACL structures.
+ * calclass  - If this is not zero, then we compute the CLASS_OBJ
+ *             permissions.
+ * aclp      - Array of ACL structures.
+ *
+ * Outputs:
+ *
+ * aclp     - Sorted array of ACL structures.
+ *
+ * Outputs:
+ *
+ * Returns 0 for success -1 for failure. Prints a message to the Samba
+ * debug log in case of failure.
+ */
+
+static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
+{
+#if !defined(HAVE_HPUX_ACLSORT)
+       /*
+        * The aclsort() system call is availabe on the latest HPUX General
+        * Patch Bundles. So for HPUX, we developed our version of acl_sort 
+        * function. Because, we don't want to update to a new 
+        * HPUX GR bundle just for aclsort() call.
+        */
+
+       struct hpux_acl_types acl_obj_count;
+       int n_class_obj_perm = 0;
+       int i, j;
+       if(!acl_count) {
+               DEBUG(10,("Zero acl count passed. Returning Success\n"));
+               return 0;
+       }
+
+       if(aclp == NULL) {
+               DEBUG(0,("Null ACL pointer in hpux_acl_sort. Returning Failure. \n"));
+               return -1;
+       }
+
+       /* Count different types of ACLs in the ACLs array */
+
+       hpux_count_obj(acl_count, aclp, &acl_obj_count);
+
+       /* There should be only one entry each of type USER_OBJ, GROUP_OBJ, 
+        * CLASS_OBJ and OTHER_OBJ 
+        */
+
+       if( (acl_obj_count.n_user_obj  != 1) || 
+               (acl_obj_count.n_group_obj != 1) || 
+               (acl_obj_count.n_class_obj != 1) ||
+               (acl_obj_count.n_other_obj != 1) 
+       ) {
+               DEBUG(0,("hpux_acl_sort: More than one entry or no entries for \
+USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n"));
+               return -1;
+       }
+
+       /* If any of the default objects are present, there should be only
+        * one of them each.
+        */
+
+       if( (acl_obj_count.n_def_user_obj  > 1) || (acl_obj_count.n_def_group_obj > 1) || 
+                       (acl_obj_count.n_def_other_obj > 1) || (acl_obj_count.n_def_class_obj > 1) ) {
+               DEBUG(0,("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \
+or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
+               return -1;
+       }
+
+       /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl 
+        * structures.  
+        *
+        * Sorting crieteria - First sort by ACL type. If there are multiple entries of
+        * same ACL type, sort by ACL id.
+        *
+        * I am using the trival kind of sorting method here because, performance isn't 
+        * really effected by the ACLs feature. More over there aren't going to be more
+        * than 17 entries on HPUX. 
+        */
+
+       for(i=0; i<acl_count;i++) {
+               for (j=i+1; j<acl_count; j++) {
+                       if( aclp[i].a_type > aclp[j].a_type ) {
+                               /* ACL entries out of order, swap them */
+
+                               hpux_swap_acl_entries((aclp+i), (aclp+j));
+
+                       } else if ( aclp[i].a_type == aclp[j].a_type ) {
+
+                               /* ACL entries of same type, sort by id */
+
+                               if(aclp[i].a_id > aclp[j].a_id) {
+                                       hpux_swap_acl_entries((aclp+i), (aclp+j));
+                               } else if (aclp[i].a_id == aclp[j].a_id) {
+                                       /* We have a duplicate entry. */
+                                       if(hpux_prohibited_duplicate_type(aclp[i].a_type)) {
+                                               DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n",
+                                                       aclp[i].a_type, aclp[i].a_id));
+                                               return -1;
+                                       }
+                               }
+
+                       }
+               }
+       }
+
+       /* set the class obj permissions to the computed one. */
+       if(calclass) {
+               int n_class_obj_index = -1;
+
+               for(i=0;i<acl_count;i++) {
+                       n_class_obj_perm |= hpux_get_needed_class_perm((aclp+i));
+
+                       if(aclp[i].a_type == CLASS_OBJ)
+                               n_class_obj_index = i;
+               }
+               aclp[n_class_obj_index].a_perm = n_class_obj_perm;
+       }
+
+       return 0;
+#else
+       return aclsort(acl_count, calclass, aclp);
+#endif
+}
+
+/*
+ * sort the ACL and check it for validity
+ *
+ * if it's a minimal ACL with only 4 entries then we
+ * need to recalculate the mask permissions to make
+ * sure that they are the same as the GROUP_OBJ
+ * permissions as required by the UnixWare acl() system call.
+ *
+ * (note: since POSIX allows minimal ACLs which only contain
+ * 3 entries - ie there is no mask entry - we should, in theory,
+ * check for this and add a mask entry if necessary - however
+ * we "know" that the caller of this interface always specifies
+ * a mask so, in practice "this never happens" (tm) - if it *does*
+ * happen aclsort() will fail and return an error and someone will
+ * have to fix it ...)
+ */
+
+static int acl_sort(SMB_ACL_T acl_d)
+{
+       int fixmask = (acl_d->count <= 4);
+
+       if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+       return 0;
+}
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+       return acl_sort(acl_d);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+       struct stat     s;
+       struct acl      *acl_p;
+       int             acl_count;
+       struct acl      *acl_buf        = NULL;
+       int             ret;
+
+       if(hpux_acl_call_presence() == False) {
+               /* Looks like we don't have the acl() system call on HPUX. 
+                * May be the system doesn't have the latest version of JFS.
+                */
+               errno=ENOSYS;
+               return -1; 
+       }
+
+       if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       acl_p           = &acl_d->acl[0];
+       acl_count       = acl_d->count;
+
+       /*
+        * if it's a directory there is extra work to do
+        * since the acl() system call will replace both
+        * the access ACLs and the default ACLs (if any)
+        */
+       if (stat(name, &s) != 0) {
+               return -1;
+       }
+       if (S_ISDIR(s.st_mode)) {
+               SMB_ACL_T       acc_acl;
+               SMB_ACL_T       def_acl;
+               SMB_ACL_T       tmp_acl;
+               int             i;
+
+               if (type == SMB_ACL_TYPE_ACCESS) {
+                       acc_acl = acl_d;
+                       def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT);
+
+               } else {
+                       def_acl = acl_d;
+                       acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS);
+               }
+
+               if (tmp_acl == NULL) {
+                       return -1;
+               }
+
+               /*
+                * allocate a temporary buffer for the complete ACL
+                */
+               acl_count = acc_acl->count + def_acl->count;
+               acl_p = acl_buf = SMB_MALLOC_ARRAY(struct acl, acl_count);
+
+               if (acl_buf == NULL) {
+                       sys_acl_free_acl(tmp_acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               /*
+                * copy the access control and default entries into the buffer
+                */
+               memcpy(&acl_buf[0], &acc_acl->acl[0],
+                       acc_acl->count * sizeof(acl_buf[0]));
+
+               memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0],
+                       def_acl->count * sizeof(acl_buf[0]));
+
+               /*
+                * set the ACL_DEFAULT flag on the default entries
+                */
+               for (i = acc_acl->count; i < acl_count; i++) {
+                       acl_buf[i].a_type |= ACL_DEFAULT;
+               }
+
+               sys_acl_free_acl(tmp_acl);
+
+       } else if (type != SMB_ACL_TYPE_ACCESS) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       ret = acl(name, ACL_SET, acl_count, acl_p);
+
+       if (acl_buf) {
+               free(acl_buf);
+       }
+
+       return ret;
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+       /*
+        * HPUX doesn't have the facl call. Fake it using the path.... JRA.
+        */
+
+       files_struct *fsp = file_find_fd(fd);
+
+       if (fsp == NULL) {
+               errno = EBADF;
+               return NULL;
+       }
+
+       if (acl_sort(acl_d) != 0) {
+               return -1;
+       }
+
+       /*
+        * We know we're in the same conn context. So we
+        * can use the relative path.
+        */
+
+       return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d);
+}
+
+int sys_acl_delete_def_file(const char *path)
+{
+       SMB_ACL_T       acl_d;
+       int             ret;
+
+       /*
+        * fetching the access ACL and rewriting it has
+        * the effect of deleting the default ACL
+        */
+       if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) {
+               return -1;
+       }
+
+       ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
+
+       sys_acl_free_acl(acl_d);
+       
+       return ret;
+}
+
+int sys_acl_free_text(char *text)
+{
+       free(text);
+       return 0;
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d) 
+{
+       free(acl_d);
+       return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return 0;
+}
+
+#elif defined(HAVE_IRIX_ACLS)
+
+int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_p == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (entry_id == SMB_ACL_FIRST_ENTRY) {
+               acl_d->next = 0;
+       }
+
+       if (acl_d->next < 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->next >= acl_d->aclp->acl_cnt) {
+               return 0;
+       }
+
+       *entry_p = &acl_d->aclp->acl_entry[acl_d->next++];
+
+       return 1;
+}
+
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p)
+{
+       *type_p = entry_d->ae_tag;
+
+       return 0;
+}
+
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       *permset_p = entry_d;
+
+       return 0;
+}
+
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d)
+{
+       if (entry_d->ae_tag != SMB_ACL_USER
+           && entry_d->ae_tag != SMB_ACL_GROUP) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       return &entry_d->ae_id;
+}
+
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
+{
+       SMB_ACL_T       a;
+
+       if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       if ((a->aclp = acl_get_file(path_p, type)) == NULL) {
+               SAFE_FREE(a);
+               return NULL;
+       }
+       a->next = -1;
+       a->freeaclp = True;
+       return a;
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       SMB_ACL_T       a;
+
+       if ((a = SMB_MALLOC_P(struct SMB_ACL_T)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       if ((a->aclp = acl_get_fd(fd)) == NULL) {
+               SAFE_FREE(a);
+               return NULL;
+       }
+       a->next = -1;
+       a->freeaclp = True;
+       return a;
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d)
+{
+       permset_d->ae_perm = 0;
+
+       return 0;
+}
+
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE
+           && perm != SMB_ACL_EXECUTE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (permset_d == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       permset_d->ae_perm |= perm;
+
+       return 0;
+}
+
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm)
+{
+       return permset_d->ae_perm & perm;
+}
+
+char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p)
+{
+       return acl_to_text(acl_d->aclp, len_p);
+}
+
+SMB_ACL_T sys_acl_init(int count)
+{
+       SMB_ACL_T       a;
+
+       if (count < 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       if ((a = SMB_MALLOC(sizeof(struct SMB_ACL_T) + sizeof(struct acl))) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       a->next = -1;
+       a->freeaclp = False;
+       a->aclp = (struct acl *)(&a->aclp + sizeof(struct acl *));
+       a->aclp->acl_cnt = 0;
+
+       return a;
+}
+
+
+int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p)
+{
+       SMB_ACL_T       acl_d;
+       SMB_ACL_ENTRY_T entry_d;
+
+       if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) {
+               errno = ENOSPC;
+               return -1;
+       }
+
+       entry_d         = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++];
+       entry_d->ae_tag = 0;
+       entry_d->ae_id  = 0;
+       entry_d->ae_perm        = 0;
+       *entry_p        = entry_d;
+
+       return 0;
+}
+
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type)
+{
+       switch (tag_type) {
+               case SMB_ACL_USER:
+               case SMB_ACL_USER_OBJ:
+               case SMB_ACL_GROUP:
+               case SMB_ACL_GROUP_OBJ:
+               case SMB_ACL_OTHER:
+               case SMB_ACL_MASK:
+                       entry_d->ae_tag = tag_type;
+                       break;
+               default:
+                       errno = EINVAL;
+                       return -1;
+       }
+
+       return 0;
+}
+
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p)
+{
+       if (entry_d->ae_tag != SMB_ACL_GROUP
+           && entry_d->ae_tag != SMB_ACL_USER) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       entry_d->ae_id = *((id_t *)qual_p);
+
+       return 0;
+}
+
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d)
+{
+       if (permset_d->ae_perm & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
+               return EINVAL;
+       }
+
+       entry_d->ae_perm = permset_d->ae_perm;
+
+       return 0;
+}
+
+int sys_acl_valid(SMB_ACL_T acl_d)
+{
+       return acl_valid(acl_d->aclp);
+}
+
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
+{
+       return acl_set_file(name, type, acl_d->aclp);
+}
+
+int sys_acl_set_fd(int fd, SMB_ACL_T acl_d)
+{
+       return acl_set_fd(fd, acl_d->aclp);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       return acl_delete_def_file(name);
+}
+
+int sys_acl_free_text(char *text)
+{
+       return acl_free(text);
+}
+
+int sys_acl_free_acl(SMB_ACL_T acl_d) 
+{
+       if (acl_d->freeaclp) {
+               acl_free(acl_d->aclp);
+       }
+       acl_free(acl_d);
+       return 0;
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return 0;
+}
+
+#elif defined(HAVE_AIX_ACLS)
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p)
+{
+       struct acl_entry_link *link;
+       struct new_acl_entry *entry;
+       int keep_going;
+
+       DEBUG(10,("This is the count: %d\n",theacl->count));
+
+       /* Check if count was previously set to -1. *
+        * If it was, that means we reached the end *
+        * of the acl last time.                    */
+       if(theacl->count == -1)
+               return(0);
+
+       link = theacl;
+       /* To get to the next acl, traverse linked list until index *
+        * of acl matches the count we are keeping.  This count is  *
+        * incremented each time we return an acl entry.            */
+
+       for(keep_going = 0; keep_going < theacl->count; keep_going++)
+               link = link->nextp;
+
+       entry = *entry_p =  link->entryp;
+
+       DEBUG(10,("*entry_p is %d\n",entry_p));
+       DEBUG(10,("*entry_p->ace_access is %d\n",entry->ace_access));
+
+       /* Increment count */
+       theacl->count++;
+       if(link->nextp == NULL)
+               theacl->count = -1;
+
+       return(1);
+}
+
+int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p)
+{
+       /* Initialize tag type */
+
+       *tag_type_p = -1;
+       DEBUG(10,("the tagtype is %d\n",entry_d->ace_id->id_type));
+
+       /* Depending on what type of entry we have, *
+        * return tag type.                         */
+       switch(entry_d->ace_id->id_type) {
+       case ACEID_USER:
+               *tag_type_p = SMB_ACL_USER;
+               break;
+       case ACEID_GROUP:
+               *tag_type_p = SMB_ACL_GROUP;
+               break;
+
+       case SMB_ACL_USER_OBJ:
+       case SMB_ACL_GROUP_OBJ:
+       case SMB_ACL_OTHER:
+               *tag_type_p = entry_d->ace_id->id_type;
+               break;
+       default:
+               return(-1);
+       }
+
+       return(0);
+}
+
+int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p)
+{
+       DEBUG(10,("Starting AIX sys_acl_get_permset\n"));
+       *permset_p = &entry_d->ace_access;
+       DEBUG(10,("**permset_p is %d\n",**permset_p));
+       if(!(**permset_p & S_IXUSR) &&
+               !(**permset_p & S_IWUSR) &&
+               !(**permset_p & S_IRUSR) &&
+               (**permset_p != 0))
+                       return(-1);
+
+       DEBUG(10,("Ending AIX sys_acl_get_permset\n"));
+       return(0);
+}
+
+void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d)
+{
+       return(entry_d->ace_id->id_data);
+}
+
+SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type)
+{
+       struct acl *file_acl = (struct acl *)NULL;
+       struct acl_entry *acl_entry;
+       struct new_acl_entry *new_acl_entry;
+       struct ace_id *idp;
+       struct acl_entry_link *acl_entry_link;
+       struct acl_entry_link *acl_entry_link_head;
+       int i;
+       int rc = 0;
+       uid_t user_id;
+
+       /* AIX has no DEFAULT */
+       if  ( type == SMB_ACL_TYPE_DEFAULT ) {
+               errno = ENOTSUP;
+               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 *)SMB_MALLOC(BUFSIZ);
+       if(file_acl == NULL) {
+               errno=ENOMEM;
+               DEBUG(0,("Error in AIX sys_acl_get_file: %d\n",errno));
+               return(NULL);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+
+       rc = statacl((char *)path_p,0,file_acl,BUFSIZ);
+       if(rc == -1) {
+               DEBUG(0,("statacl returned %d with errno %d\n",rc,errno));
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       DEBUG(10,("Got facl and returned it\n"));
+
+       /* Point to the first acl entry in the acl */
+       acl_entry =  file_acl->acl_ext;
+
+       /* Begin setting up the head of the linked list *
+        * that will be used for the storing the acl    *
+        * in a way that is useful for the posix_acls.c *
+        * code.                                          */
+
+       acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+       if(acl_entry_link_head == NULL)
+               return(NULL);
+
+       acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+       if(acl_entry_link->entryp == NULL) {
+               SAFE_FREE(file_acl);
+               errno = ENOMEM;
+               DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+               return(NULL);
+       }
+
+       DEBUG(10,("acl_entry is %d\n",acl_entry));
+       DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+
+       /* Check if the extended acl bit is on.   *
+        * If it isn't, do not show the           *
+        * contents of the acl since AIX intends *
+        * the extended info to remain unused     */
+
+       if(file_acl->acl_mode & S_IXACL){
+               /* while we are not pointing to the very end */
+               while(acl_entry < acl_last(file_acl)) {
+                       /* before we malloc anything, make sure this is  */
+                       /* a valid acl entry and one that we want to map */
+                       idp = id_nxt(acl_entry->ace_id);
+                       if((acl_entry->ace_type == ACC_SPECIFY ||
+                               (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+                                       acl_entry = acl_nxt(acl_entry);
+                                       continue;
+                       }
+
+                       idp = acl_entry->ace_id;
+
+                       /* Check if this is the first entry in the linked list. *
+                        * The first entry needs to keep prevp pointing to NULL *
+                        * and already has entryp allocated.                  */
+
+                       if(acl_entry_link_head->count != 0) {
+                               acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+
+                               if(acl_entry_link->nextp == NULL) {
+                                       SAFE_FREE(file_acl);
+                                       errno = ENOMEM;
+                                       DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                                       return(NULL);
+                               }
+
+                               acl_entry_link->nextp->prevp = acl_entry_link;
+                               acl_entry_link = acl_entry_link->nextp;
+                               acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+                               if(acl_entry_link->entryp == NULL) {
+                                       SAFE_FREE(file_acl);
+                                       errno = ENOMEM;
+                                       DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                                       return(NULL);
+                               }
+                               acl_entry_link->nextp = NULL;
+                       }
+
+                       acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+                       /* Don't really need this since all types are going *
+                        * to be specified but, it's better than leaving it 0 */
+
+                       acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+                       acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+                       memcpy(acl_entry_link->entryp->ace_id,idp,sizeof(struct ace_id));
+
+                       /* The access in the acl entries must be left shifted by *
+                        * three bites, because they will ultimately be compared *
+                        * to S_IRUSR, S_IWUSR, and S_IXUSR.                  */
+
+                       switch(acl_entry->ace_type){
+                       case ACC_PERMIT:
+                       case ACC_SPECIFY:
+                               acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       case ACC_DENY:
+                               /* Since there is no way to return a DENY acl entry *
+                                * change to PERMIT and then shift.                 */
+                               DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+                               acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+                               DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       default:
+                               return(0);
+                       }
+
+                       DEBUG(10,("acl_entry = %d\n",acl_entry));
+                       DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+                       acl_entry = acl_nxt(acl_entry);
+               }
+       } /* end of if enabled */
+
+       /* Since owner, group, other acl entries are not *
+        * part of the acl entries in an acl, they must  *
+        * be dummied up to become part of the list.     */
+
+       for( i = 1; i < 4; i++) {
+               DEBUG(10,("i is %d\n",i));
+               if(acl_entry_link_head->count != 0) {
+                       acl_entry_link->nextp = SMB_MALLOC_P(struct acl_entry_link);
+                       if(acl_entry_link->nextp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                               return(NULL);
+                       }
+
+                       acl_entry_link->nextp->prevp = acl_entry_link;
+                       acl_entry_link = acl_entry_link->nextp;
+                       acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+                       if(acl_entry_link->entryp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno));
+                               return(NULL);
+                       }
+               }
+
+               acl_entry_link->nextp = NULL;
+
+               new_acl_entry = acl_entry_link->entryp;
+               idp = new_acl_entry->ace_id;
+
+               new_acl_entry->ace_len = sizeof(struct acl_entry);
+               new_acl_entry->ace_type = ACC_PERMIT;
+               idp->id_len = sizeof(struct ace_id);
+               DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+               memset(idp->id_data,0,sizeof(uid_t));
+
+               switch(i) {
+               case 2:
+                       new_acl_entry->ace_access = file_acl->g_access << 6;
+                       idp->id_type = SMB_ACL_GROUP_OBJ;
+                       break;
+
+               case 3:
+                       new_acl_entry->ace_access = file_acl->o_access << 6;
+                       idp->id_type = SMB_ACL_OTHER;
+                       break;
+               case 1:
+                       new_acl_entry->ace_access = file_acl->u_access << 6;
+                       idp->id_type = SMB_ACL_USER_OBJ;
+                       break;
+               default:
+                       return(NULL);
+
+               }
+
+               acl_entry_link_head->count++;
+               DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+       }
+
+       acl_entry_link_head->count = 0;
+       SAFE_FREE(file_acl);
+
+       return(acl_entry_link_head);
+}
+
+SMB_ACL_T sys_acl_get_fd(int fd)
+{
+       struct acl *file_acl = (struct acl *)NULL;
+       struct acl_entry *acl_entry;
+       struct new_acl_entry *new_acl_entry;
+       struct ace_id *idp;
+       struct acl_entry_link *acl_entry_link;
+       struct acl_entry_link *acl_entry_link_head;
+       int i;
+       int rc = 0;
+       uid_t user_id;
+
+       /* Get the acl using fstatacl */
+   
+       DEBUG(10,("Entering sys_acl_get_fd\n"));
+       DEBUG(10,("fd is %d\n",fd));
+       file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+       if(file_acl == NULL) {
+               errno=ENOMEM;
+               DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+               return(NULL);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+
+       rc = fstatacl(fd,0,file_acl,BUFSIZ);
+       if(rc == -1) {
+               DEBUG(0,("The fstatacl call returned %d with errno %d\n",rc,errno));
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       DEBUG(10,("Got facl and returned it\n"));
+
+       /* Point to the first acl entry in the acl */
+
+       acl_entry =  file_acl->acl_ext;
+       /* Begin setting up the head of the linked list *
+        * that will be used for the storing the acl    *
+        * in a way that is useful for the posix_acls.c *
+        * code.                                        */
+
+       acl_entry_link_head = acl_entry_link = sys_acl_init(0);
+       if(acl_entry_link_head == NULL){
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       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));
+               SAFE_FREE(file_acl);
+               return(NULL);
+       }
+
+       DEBUG(10,("acl_entry is %d\n",acl_entry));
+       DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl)));
+       /* Check if the extended acl bit is on.   *
+        * If it isn't, do not show the           *
+        * contents of the acl since AIX intends  *
+        * the extended info to remain unused     */
+       if(file_acl->acl_mode & S_IXACL){
+               /* while we are not pointing to the very end */
+               while(acl_entry < acl_last(file_acl)) {
+                       /* before we malloc anything, make sure this is  */
+                       /* a valid acl entry and one that we want to map */
+
+                       idp = id_nxt(acl_entry->ace_id);
+                       if((acl_entry->ace_type == ACC_SPECIFY ||
+                               (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) {
+                                       acl_entry = acl_nxt(acl_entry);
+                                       continue;
+                       }
+
+                       idp = acl_entry->ace_id;
+                       /* Check if this is the first entry in the linked list. *
+                        * The first entry needs to keep prevp pointing to NULL *
+                        * and already has entryp allocated.                 */
+
+                       if(acl_entry_link_head->count != 0) {
+                               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));
+                                       SAFE_FREE(file_acl);
+                                       return(NULL);
+                               }
+                               acl_entry_link->nextp->prevp = acl_entry_link;
+                               acl_entry_link = acl_entry_link->nextp;
+                               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));
+                                       SAFE_FREE(file_acl);
+                                       return(NULL);
+                               }
+
+                               acl_entry_link->nextp = NULL;
+                       }
+
+                       acl_entry_link->entryp->ace_len = acl_entry->ace_len;
+
+                       /* Don't really need this since all types are going *
+                        * to be specified but, it's better than leaving it 0 */
+
+                       acl_entry_link->entryp->ace_type = acl_entry->ace_type;
+                       acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+
+                       memcpy(acl_entry_link->entryp->ace_id, idp, sizeof(struct ace_id));
+
+                       /* The access in the acl entries must be left shifted by *
+                        * three bites, because they will ultimately be compared *
+                        * to S_IRUSR, S_IWUSR, and S_IXUSR.                  */
+
+                       switch(acl_entry->ace_type){
+                       case ACC_PERMIT:
+                       case ACC_SPECIFY:
+                               acl_entry_link->entryp->ace_access = acl_entry->ace_access;
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       case ACC_DENY:
+                               /* Since there is no way to return a DENY acl entry *
+                                * change to PERMIT and then shift.                 */
+                               DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access));
+                               acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7;
+                               DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access));
+                               acl_entry_link->entryp->ace_access <<= 6;
+                               acl_entry_link_head->count++;
+                               break;
+                       default:
+                               return(0);
+                       }
+
+                       DEBUG(10,("acl_entry = %d\n",acl_entry));
+                       DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type));
+                       acl_entry = acl_nxt(acl_entry);
+               }
+       } /* end of if enabled */
+
+       /* Since owner, group, other acl entries are not *
+        * part of the acl entries in an acl, they must  *
+        * be dummied up to become part of the list.     */
+
+       for( i = 1; i < 4; i++) {
+               DEBUG(10,("i is %d\n",i));
+               if(acl_entry_link_head->count != 0){
+                       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));
+                               SAFE_FREE(file_acl);
+                               return(NULL);
+                       }
+
+                       acl_entry_link->nextp->prevp = acl_entry_link;
+                       acl_entry_link = acl_entry_link->nextp;
+                       acl_entry_link->entryp = SMB_MALLOC_P(struct new_acl_entry);
+
+                       if(acl_entry_link->entryp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno));
+                               return(NULL);
+                       }
+               }
+
+               acl_entry_link->nextp = NULL;
+               new_acl_entry = acl_entry_link->entryp;
+               idp = new_acl_entry->ace_id;
+               new_acl_entry->ace_len = sizeof(struct acl_entry);
+               new_acl_entry->ace_type = ACC_PERMIT;
+               idp->id_len = sizeof(struct ace_id);
+               DEBUG(10,("idp->id_len = %d\n",idp->id_len));
+               memset(idp->id_data,0,sizeof(uid_t));
+               switch(i) {
+               case 2:
+                       new_acl_entry->ace_access = file_acl->g_access << 6;
+                       idp->id_type = SMB_ACL_GROUP_OBJ;
+                       break;
+               case 3:
+                       new_acl_entry->ace_access = file_acl->o_access << 6;
+                       idp->id_type = SMB_ACL_OTHER;
+                       break;
+               case 1:
+                       new_acl_entry->ace_access = file_acl->u_access << 6;
+                       idp->id_type = SMB_ACL_USER_OBJ;
+                       break;
+               default:
+                       return(NULL);
+               }
+               acl_entry_link_head->count++;
+               DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access));
+       }
+
+       acl_entry_link_head->count = 0;
+       SAFE_FREE(file_acl);
+       return(acl_entry_link_head);
+}
+
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset)
+{
+       *permset = *permset & ~0777;
+       return(0);
+}
+
+int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       if((perm != 0) &&
+                       (perm & (S_IXUSR | S_IWUSR | S_IRUSR)) == 0)
+               return(-1);
+
+       *permset |= perm;
+       DEBUG(10,("This is the permset now: %d\n",*permset));
+       return(0);
+}
+
+char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen)
+{
+       return(NULL);
+}
+
+SMB_ACL_T sys_acl_init( int count)
+{
+       struct acl_entry_link *theacl = NULL;
+       DEBUG(10,("Entering sys_acl_init\n"));
+
+       theacl = SMB_MALLOC_P(struct acl_entry_link);
+       if(theacl == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_init is %d\n",errno));
+               return(NULL);
+       }
+
+       theacl->count = 0;
+       theacl->nextp = NULL;
+       theacl->prevp = NULL;
+       theacl->entryp = NULL;
+       DEBUG(10,("Exiting sys_acl_init\n"));
+       return(theacl);
+}
+
+int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry)
+{
+       struct acl_entry_link *theacl;
+       struct acl_entry_link *acl_entryp;
+       struct acl_entry_link *temp_entry;
+       int counting;
+
+       DEBUG(10,("Entering the sys_acl_create_entry\n"));
+
+       theacl = acl_entryp = *pacl;
+
+       /* Get to the end of the acl before adding entry */
+
+       for(counting=0; counting < theacl->count; counting++){
+               DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+               temp_entry = acl_entryp;
+               acl_entryp = acl_entryp->nextp;
+       }
+
+       if(theacl->count != 0){
+               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));
+                       return(-1);
+               }
+
+               DEBUG(10,("The acl_entryp is %d\n",acl_entryp));
+               acl_entryp->prevp = temp_entry;
+               DEBUG(10,("The acl_entryp->prevp is %d\n",acl_entryp->prevp));
+       }
+
+       *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));
+               return(-1);
+       }
+
+       memset(*pentry,0,sizeof(struct new_acl_entry));
+       acl_entryp->entryp->ace_len = sizeof(struct acl_entry);
+       acl_entryp->entryp->ace_type = ACC_PERMIT;
+       acl_entryp->entryp->ace_id->id_len = sizeof(struct ace_id);
+       acl_entryp->nextp = NULL;
+       theacl->count++;
+       DEBUG(10,("Exiting sys_acl_create_entry\n"));
+       return(0);
+}
+
+int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype)
+{
+       DEBUG(10,("Starting AIX sys_acl_set_tag_type\n"));
+       entry->ace_id->id_type = tagtype;
+       DEBUG(10,("The tag type is %d\n",entry->ace_id->id_type));
+       DEBUG(10,("Ending AIX sys_acl_set_tag_type\n"));
+}
+
+int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual)
+{
+       DEBUG(10,("Starting AIX sys_acl_set_qualifier\n"));
+       memcpy(entry->ace_id->id_data,qual,sizeof(uid_t));
+       DEBUG(10,("Ending AIX sys_acl_set_qualifier\n"));
+       return(0);
+}
+
+int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset)
+{
+       DEBUG(10,("Starting AIX sys_acl_set_permset\n"));
+       if(!(*permset & S_IXUSR) &&
+               !(*permset & S_IWUSR) &&
+               !(*permset & S_IRUSR) &&
+               (*permset != 0))
+                       return(-1);
+
+       entry->ace_access = *permset;
+       DEBUG(10,("entry->ace_access = %d\n",entry->ace_access));
+       DEBUG(10,("Ending AIX sys_acl_set_permset\n"));
+       return(0);
+}
+
+int sys_acl_valid( SMB_ACL_T theacl )
+{
+       int user_obj = 0;
+       int group_obj = 0;
+       int other_obj = 0;
+       struct acl_entry_link *acl_entry;
+
+       for(acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) {
+               user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ);
+               group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ);
+               other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER);
+       }
+
+       DEBUG(10,("user_obj=%d, group_obj=%d, other_obj=%d\n",user_obj,group_obj,other_obj));
+       if(user_obj != 1 || group_obj != 1 || other_obj != 1)
+               return(-1); 
+
+       return(0);
+}
+
+int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
+{
+       struct acl_entry_link *acl_entry_link = NULL;
+       struct acl *file_acl = NULL;
+       struct acl *file_acl_temp = NULL;
+       struct acl_entry *acl_entry = NULL;
+       struct ace_id *ace_id = NULL;
+       uint id_type;
+       uint ace_access;
+       uint user_id;
+       uint acl_length;
+       uint rc;
+
+       DEBUG(10,("Entering sys_acl_set_file\n"));
+       DEBUG(10,("File name is %s\n",name));
+       /* AIX has no default ACL */
+       if(acltype == SMB_ACL_TYPE_DEFAULT)
+               return(0);
+
+       acl_length = BUFSIZ;
+       file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+       if(file_acl == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+               return(-1);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+
+       file_acl->acl_len = ACL_SIZ;
+       file_acl->acl_mode = S_IXACL;
+
+       for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+               acl_entry_link->entryp->ace_access >>= 6;
+               id_type = acl_entry_link->entryp->ace_id->id_type;
+
+               switch(id_type) {
+               case SMB_ACL_USER_OBJ:
+                       file_acl->u_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_GROUP_OBJ:
+                       file_acl->g_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_OTHER:
+                       file_acl->o_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_MASK:
+                       continue;
+               }
+
+               if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+                       acl_length += sizeof(struct acl_entry);
+                       file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+                       if(file_acl_temp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in sys_acl_set_file is %d\n",errno));
+                               return(-1);
+                       }  
+
+                       memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+                       SAFE_FREE(file_acl);
+                       file_acl = file_acl_temp;
+               }
+
+               acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+               file_acl->acl_len += sizeof(struct acl_entry);
+               acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+               acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+               /* In order to use this, we'll need to wait until we can get denies */
+               /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+               acl_entry->ace_type = ACC_SPECIFY; */
+
+               acl_entry->ace_type = ACC_SPECIFY;
+               ace_id = acl_entry->ace_id;
+               ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+               DEBUG(10,("The id type is %d\n",ace_id->id_type));
+               ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+               memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+               memcpy(acl_entry->ace_id->id_data, &user_id, sizeof(uid_t));
+       }
+
+       rc = chacl(name,file_acl,file_acl->acl_len);
+       DEBUG(10,("errno is %d\n",errno));
+       DEBUG(10,("return code is %d\n",rc));
+       SAFE_FREE(file_acl);
+       DEBUG(10,("Exiting the sys_acl_set_file\n"));
+       return(rc);
+}
+
+int sys_acl_set_fd( int fd, SMB_ACL_T theacl)
+{
+       struct acl_entry_link *acl_entry_link = NULL;
+       struct acl *file_acl = NULL;
+       struct acl *file_acl_temp = NULL;
+       struct acl_entry *acl_entry = NULL;
+       struct ace_id *ace_id = NULL;
+       uint id_type;
+       uint user_id;
+       uint acl_length;
+       uint rc;
+       DEBUG(10,("Entering sys_acl_set_fd\n"));
+       acl_length = BUFSIZ;
+       file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
+
+       if(file_acl == NULL) {
+               errno = ENOMEM;
+               DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+               return(-1);
+       }
+
+       memset(file_acl,0,BUFSIZ);
+       file_acl->acl_len = ACL_SIZ;
+       file_acl->acl_mode = S_IXACL;
+
+       for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) {
+               acl_entry_link->entryp->ace_access >>= 6;
+               id_type = acl_entry_link->entryp->ace_id->id_type;
+               DEBUG(10,("The id_type is %d\n",id_type));
+
+               switch(id_type) {
+               case SMB_ACL_USER_OBJ:
+                       file_acl->u_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_GROUP_OBJ:
+                       file_acl->g_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_OTHER:
+                       file_acl->o_access = acl_entry_link->entryp->ace_access;
+                       continue;
+               case SMB_ACL_MASK:
+                       continue;
+               }
+
+               if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) {
+                       acl_length += sizeof(struct acl_entry);
+                       file_acl_temp = (struct acl *)SMB_MALLOC(acl_length);
+                       if(file_acl_temp == NULL) {
+                               SAFE_FREE(file_acl);
+                               errno = ENOMEM;
+                               DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno));
+                               return(-1);
+                       }
+
+                       memcpy(file_acl_temp,file_acl,file_acl->acl_len);
+                       SAFE_FREE(file_acl);
+                       file_acl = file_acl_temp;
+               }
+
+               acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len);
+               file_acl->acl_len += sizeof(struct acl_entry);
+               acl_entry->ace_len = acl_entry_link->entryp->ace_len;
+               acl_entry->ace_access = acl_entry_link->entryp->ace_access;
+               /* In order to use this, we'll need to wait until we can get denies */
+               /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
+                       acl_entry->ace_type = ACC_SPECIFY; */
+               acl_entry->ace_type = ACC_SPECIFY;
+               ace_id = acl_entry->ace_id;
+               ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
+               DEBUG(10,("The id type is %d\n",ace_id->id_type));
+               ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
+               memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t));
+               memcpy(ace_id->id_data, &user_id, sizeof(uid_t));
+       }
+       rc = fchacl(fd,file_acl,file_acl->acl_len);
+       DEBUG(10,("errno is %d\n",errno));
+       DEBUG(10,("return code is %d\n",rc));
+       SAFE_FREE(file_acl);
+       DEBUG(10,("Exiting sys_acl_set_fd\n"));
+       return(rc);
+}
+
+int sys_acl_delete_def_file(const char *name)
+{
+       /* AIX has no default ACL */
+       return 0;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       return(*permset & perm);
+}
+
+int sys_acl_free_text(char *text)
+{
+       return(0);
+}
+
+int sys_acl_free_acl(SMB_ACL_T posix_acl)
+{
+       struct acl_entry_link *acl_entry_link;
+
+       for(acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) {
+               SAFE_FREE(acl_entry_link->prevp->entryp);
+               SAFE_FREE(acl_entry_link->prevp);
+       }
+
+       SAFE_FREE(acl_entry_link->prevp->entryp);
+       SAFE_FREE(acl_entry_link->prevp);
+       SAFE_FREE(acl_entry_link->entryp);
+       SAFE_FREE(acl_entry_link);
+       return(0);
+}
+
+int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype)
+{
+       return(0);
+}
+
+#else /* No ACLs. */
+
+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(UNUSED(SMB_ACL_ENTRY_T entry_d), UNUSED(SMB_ACL_TAG_T *tag_type_p))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+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(UNUSED(SMB_ACL_ENTRY_T entry_d))
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+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(UNUSED(int fd))
+{
+       errno = ENOSYS;
+       return (SMB_ACL_T)NULL;
+}
+
+int sys_acl_clear_perms(UNUSED(SMB_ACL_PERMSET_T permset))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_add_perm( UNUSED(SMB_ACL_PERMSET_T permset), UNUSED(SMB_ACL_PERM_T perm))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm)
+{
+       errno = ENOSYS;
+       return (permset & perm) ? 1 : 0;
+}
+
+char *sys_acl_to_text(UNUSED(SMB_ACL_T the_acl), UNUSED(ssize_t *plen))
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+int sys_acl_free_text(UNUSED(char *text))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+SMB_ACL_T sys_acl_init(UNUSED(int count))
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+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(UNUSED(SMB_ACL_ENTRY_T entry), UNUSED(SMB_ACL_TAG_T tagtype))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_set_qualifier(UNUSED(SMB_ACL_ENTRY_T entry), UNUSED(void *qual))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+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(UNUSED(SMB_ACL_T theacl))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+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(UNUSED(int fd), UNUSED(SMB_ACL_T theacl))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_delete_def_file(UNUSED(const char *name))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_free_acl(UNUSED(SMB_ACL_T the_acl))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+int sys_acl_free_qualifier(UNUSED(void *qual), UNUSED(SMB_ACL_TAG_T tagtype))
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+#endif /* No ACLs. */
+
+/************************************************************************
+ 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;
+}
+
+#endif /* SUPPORT_ACLS */
diff --git a/lib/sysacls.h b/lib/sysacls.h
new file mode 100644 (file)
index 0000000..ce643ad
--- /dev/null
@@ -0,0 +1,40 @@
+#ifdef SUPPORT_ACLS
+
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#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);
+int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p);
+int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p);
+void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d);
+SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type);
+SMB_ACL_T sys_acl_get_fd(int fd);
+int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset);
+int sys_acl_add_perm(SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+int sys_acl_get_perm(SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
+char *sys_acl_to_text(SMB_ACL_T the_acl, ssize_t *plen);
+SMB_ACL_T sys_acl_init(int count);
+int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry);
+int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype);
+int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry, void *qual);
+int sys_acl_set_permset(SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset);
+int sys_acl_valid(SMB_ACL_T theacl);
+int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
+int sys_acl_set_fd(int fd, SMB_ACL_T theacl);
+int sys_acl_delete_def_file(const char *name);
+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);
+
+#endif /* SUPPORT_ACLS */
diff --git a/log.c b/log.c
index 83375a7..a2da787 100644 (file)
--- a/log.c
+++ b/log.c
@@ -624,8 +624,10 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
                        c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
                        c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
                        c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
                        c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
                        c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
                        c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
-                       c[8] = '.';
-                       c[9] = '\0';
+                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
+                       c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
+                       c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
+                       c[11] = '\0';
 
                        if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
                                char ch = iflags & ITEM_IS_NEW ? '+' : '?';
 
                        if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
                                char ch = iflags & ITEM_IS_NEW ? '+' : '?';
index 97e50b8..83f80c4 100644 (file)
--- a/options.c
+++ b/options.c
@@ -46,6 +46,7 @@ int copy_dirlinks = 0;
 int copy_links = 0;
 int preserve_links = 0;
 int preserve_hard_links = 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_perms = 0;
 int preserve_executability = 0;
 int preserve_devices = 0;
@@ -198,6 +199,7 @@ static void print_rsync_version(enum logcode f)
        char const *got_socketpair = "no ";
        char const *have_inplace = "no ";
        char const *hardlinks = "no ";
        char const *got_socketpair = "no ";
        char const *have_inplace = "no ";
        char const *hardlinks = "no ";
+       char const *acls = "no ";
        char const *links = "no ";
        char const *ipv6 = "no ";
        STRUCT_STAT *dumstat;
        char const *links = "no ";
        char const *ipv6 = "no ";
        STRUCT_STAT *dumstat;
@@ -214,6 +216,10 @@ static void print_rsync_version(enum logcode f)
        hardlinks = "";
 #endif
 
        hardlinks = "";
 #endif
 
+#ifdef SUPPORT_ACLS
+       acls = "";
+#endif
+
 #ifdef SUPPORT_LINKS
        links = "";
 #endif
 #ifdef SUPPORT_LINKS
        links = "";
 #endif
@@ -232,8 +238,8 @@ static void print_rsync_version(enum logcode f)
                (int)(sizeof (int64) * 8));
        rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
                got_socketpair, hardlinks, links, ipv6, have_inplace);
                (int)(sizeof (int64) * 8));
        rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
                got_socketpair, hardlinks, links, ipv6, have_inplace);
-       rprintf(f, "    %sappend\n",
-               have_inplace);
+       rprintf(f, "    %sappend, %sACLs\n",
+               have_inplace, acls);
 
 #ifdef MAINTAINER_MODE
        rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
 
 #ifdef MAINTAINER_MODE
        rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
@@ -279,7 +285,7 @@ void usage(enum logcode F)
   rprintf(F," -q, --quiet                 suppress non-error messages\n");
   rprintf(F,"     --no-motd               suppress daemon-mode MOTD (see manpage caveat)\n");
   rprintf(F," -c, --checksum              skip based on checksum, not mod-time & size\n");
   rprintf(F," -q, --quiet                 suppress non-error messages\n");
   rprintf(F,"     --no-motd               suppress daemon-mode MOTD (see manpage caveat)\n");
   rprintf(F," -c, --checksum              skip based on checksum, not mod-time & size\n");
-  rprintf(F," -a, --archive               archive mode; same as -rlptgoD (no -H)\n");
+  rprintf(F," -a, --archive               archive mode; same as -rlptgoD (no -H, -A)\n");
   rprintf(F,"     --no-OPTION             turn off an implied OPTION (e.g. --no-D)\n");
   rprintf(F," -r, --recursive             recurse into directories\n");
   rprintf(F," -R, --relative              use relative path names\n");
   rprintf(F,"     --no-OPTION             turn off an implied OPTION (e.g. --no-D)\n");
   rprintf(F," -r, --recursive             recurse into directories\n");
   rprintf(F," -R, --relative              use relative path names\n");
@@ -301,6 +307,9 @@ void usage(enum logcode F)
   rprintf(F," -p, --perms                 preserve permissions\n");
   rprintf(F," -E, --executability         preserve the file's executability\n");
   rprintf(F,"     --chmod=CHMOD           affect file and/or directory permissions\n");
   rprintf(F," -p, --perms                 preserve permissions\n");
   rprintf(F," -E, --executability         preserve the file's executability\n");
   rprintf(F,"     --chmod=CHMOD           affect file and/or directory permissions\n");
+#ifdef SUPPORT_ACLS
+  rprintf(F," -A, --acls                  preserve ACLs (implies --perms)\n");
+#endif
   rprintf(F," -o, --owner                 preserve owner (super-user only)\n");
   rprintf(F," -g, --group                 preserve group\n");
   rprintf(F,"     --devices               preserve device files (super-user only)\n");
   rprintf(F," -o, --owner                 preserve owner (super-user only)\n");
   rprintf(F," -g, --group                 preserve group\n");
   rprintf(F,"     --devices               preserve device files (super-user only)\n");
@@ -421,6 +430,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 },
   {"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 },
   {"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 },
@@ -1092,6 +1104,24 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        usage(FINFO);
                        exit_cleanup(0);
 
                        usage(FINFO);
                        exit_cleanup(0);
 
+               case 'A':
+#ifdef SUPPORT_ACLS
+                       preserve_acls = 1;
+                       preserve_perms = 1;
+                       break;
+#else
+                       /* FIXME: this should probably be ignored with a
+                        * warning and then countermeasures taken to
+                        * restrict group and other access in the presence
+                        * of any more restrictive ACLs, but this is safe
+                        * for now */
+                       snprintf(err_buf,sizeof(err_buf),
+                                 "ACLs are not supported on this %s\n",
+                                am_server ? "server" : "client");
+                       return 0;
+#endif
+
+
                default:
                        /* A large opt value means that set_refuse_options()
                         * turned this option off. */
                default:
                        /* A large opt value means that set_refuse_options()
                         * turned this option off. */
@@ -1551,6 +1581,10 @@ void server_options(char **args,int *argc)
                argstr[x++] = 'p';
        else if (preserve_executability && am_sender)
                argstr[x++] = 'E';
                argstr[x++] = 'p';
        else if (preserve_executability && am_sender)
                argstr[x++] = 'E';
+#ifdef SUPPORT_ACLS
+       if (preserve_acls)
+               argstr[x++] = 'A';
+#endif
        if (recurse)
                argstr[x++] = 'r';
        if (always_checksum)
        if (recurse)
                argstr[x++] = 'r';
        if (always_checksum)
index 3615e03..3b3c51e 100644 (file)
@@ -47,6 +47,7 @@ extern int keep_partial;
 extern int checksum_seed;
 extern int inplace;
 extern int delay_updates;
 extern int checksum_seed;
 extern int inplace;
 extern int delay_updates;
+extern mode_t orig_umask;
 extern struct stats stats;
 extern char *tmpdir;
 extern char *partial_dir;
 extern struct stats stats;
 extern char *tmpdir;
 extern char *partial_dir;
@@ -347,6 +348,10 @@ int recv_files(int f_in, char *local_name)
        int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
        enum logcode log_code = log_before_transfer ? FLOG : FINFO;
        int max_phase = protocol_version >= 29 ? 2 : 1;
        int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
        enum logcode log_code = log_before_transfer ? FLOG : FINFO;
        int max_phase = protocol_version >= 29 ? 2 : 1;
+       int dflt_perms = (ACCESSPERMS & ~orig_umask);
+#ifdef SUPPORT_ACLS
+       const char *parent_dirname = "";
+#endif
        int ndx, recv_ok;
 
        if (verbose > 2)
        int ndx, recv_ok;
 
        if (verbose > 2)
@@ -562,7 +567,16 @@ int recv_files(int f_in, char *local_name)
                 * mode based on the local permissions and some heuristics. */
                if (!preserve_perms) {
                        int exists = fd1 != -1;
                 * 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
+                       const 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 the file "inplace" */
                }
 
                /* We now check to see if we are writing the file "inplace" */
diff --git a/rsync.c b/rsync.c
index 789aa61..13c8643 100644 (file)
--- a/rsync.c
+++ b/rsync.c
@@ -31,6 +31,7 @@
 
 extern int verbose;
 extern int dry_run;
 
 extern int verbose;
 extern int dry_run;
+extern int preserve_acls;
 extern int preserve_perms;
 extern int preserve_executability;
 extern int preserve_times;
 extern int preserve_perms;
 extern int preserve_executability;
 extern int preserve_times;
@@ -49,7 +50,6 @@ extern int inplace;
 extern int flist_eof;
 extern int keep_dirlinks;
 extern int make_backups;
 extern int flist_eof;
 extern int keep_dirlinks;
 extern int make_backups;
-extern mode_t orig_umask;
 extern struct file_list *cur_flist, *first_flist, *dir_flist;
 extern struct chmod_mode_struct *daemon_chmod_modes;
 
 extern struct file_list *cur_flist, *first_flist, *dir_flist;
 extern struct chmod_mode_struct *daemon_chmod_modes;
 
@@ -203,7 +203,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. */
 
 /* 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 stat_mode, int exists)
+mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
+                int exists)
 {
        int new_mode;
        /* If the file already exists, we'll return the local permissions,
 {
        int new_mode;
        /* If the file already exists, we'll return the local permissions,
@@ -220,56 +221,65 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int exists)
                                new_mode |= (new_mode & 0444) >> 2;
                }
        } else {
                                new_mode |= (new_mode & 0444) >> 2;
                }
        } else {
-               /* Apply the umask and turn off special permissions. */
-               new_mode = flist_mode & (~CHMOD_BITS | (ACCESSPERMS & ~orig_umask));
+               /* Apply destination default permissions and turn
+                * off special permissions. */
+               new_mode = flist_mode & (~CHMOD_BITS | dflt_perms);
        }
        return new_mode;
 }
 
        }
        return new_mode;
 }
 
-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;
                   int flags)
 {
        int updated = 0;
-       STRUCT_STAT st2;
+       statx sx2;
        int change_uid, change_gid;
        mode_t new_mode = file->mode;
 
        int change_uid, change_gid;
        mode_t new_mode = file->mode;
 
-       if (!st) {
+       if (!sxp) {
                if (dry_run)
                        return 1;
                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;
                }
                        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(new_mode)
                if (!preserve_perms && S_ISDIR(new_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. */
                        new_mode |= S_ISGID;
                }
                        /* We just created this directory and its setgid
                         * bit is on, so make sure it stays on. */
                        new_mode |= S_ISGID;
                }
+               sxp = &sx2;
        }
 
        }
 
-       if (!preserve_times || (S_ISDIR(st->st_mode) && omit_dir_times))
+#ifdef SUPPORT_ACLS
+       if (preserve_acls && !S_ISLNK(file->mode) && !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)
                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));
                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;
        }
 
                }
                if (ret == 0) /* ret == 1 if symlink could not be set */
                        updated = 1;
        }
 
-       change_uid = am_root && preserve_uid && st->st_uid != F_UID(file);
+       change_uid = am_root && preserve_uid && sxp->st.st_uid != F_UID(file);
        change_gid = preserve_gid && F_GID(file) != GID_NONE
        change_gid = preserve_gid && F_GID(file) != GID_NONE
-               && st->st_gid != F_GID(file);
+               && sxp->st.st_gid != F_GID(file);
 #if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK
 #if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK
-       if (S_ISLNK(st->st_mode))
+       if (S_ISLNK(sxp->st.st_mode))
                ;
        else
 #endif
                ;
        else
 #endif
@@ -279,45 +289,57 @@ int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st,
                                rprintf(FINFO,
                                        "set uid of %s from %ld to %ld\n",
                                        fname,
                                rprintf(FINFO,
                                        "set uid of %s from %ld to %ld\n",
                                        fname,
-                                       (long)st->st_uid, (long)F_UID(file));
+                                       (long)sxp->st.st_uid, (long)F_UID(file));
                        }
                        if (change_gid) {
                                rprintf(FINFO,
                                        "set gid of %s from %ld to %ld\n",
                                        fname,
                        }
                        if (change_gid) {
                                rprintf(FINFO,
                                        "set gid of %s from %ld to %ld\n",
                                        fname,
-                                       (long)st->st_gid, (long)F_GID(file));
+                                       (long)sxp->st.st_gid, (long)F_GID(file));
                        }
                }
                if (do_lchown(fname,
                        }
                }
                if (do_lchown(fname,
-                   change_uid ? F_UID(file) : st->st_uid,
-                   change_gid ? F_GID(file) : st->st_gid) != 0) {
+                   change_uid ? F_UID(file) : sxp->st.st_uid,
+                   change_gid ? F_GID(file) : 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));
                        /* 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 */
                }
                /* 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;
        }
 
        if (daemon_chmod_modes && !S_ISLNK(new_mode))
                new_mode = tweak_mode(new_mode, daemon_chmod_modes);
                }
                updated = 1;
        }
 
        if (daemon_chmod_modes && !S_ISLNK(new_mode))
                new_mode = tweak_mode(new_mode, daemon_chmod_modes);
+
+#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 && !S_ISLNK(new_mode) && set_acl(fname, file, sxp) == 0)
+               updated = 1;
+#endif
+
 #ifdef HAVE_CHMOD
 #ifdef HAVE_CHMOD
-       if (!BITS_EQUAL(st->st_mode, new_mode, CHMOD_BITS)) {
+       if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
                int ret = do_chmod(fname, new_mode);
                if (ret < 0) {
                        rsyserr(FERROR, errno,
                                "failed to set permissions on %s",
                                full_fname(fname));
                int ret = do_chmod(fname, new_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;
                }
                if (ret == 0) /* ret == 1 if symlink could not be set */
                        updated = 1;
@@ -330,6 +352,11 @@ int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st,
                else
                        rprintf(FCLIENT, "%s is uptodate\n", fname);
        }
                else
                        rprintf(FCLIENT, "%s is uptodate\n", fname);
        }
+  cleanup:
+#ifdef SUPPORT_ACLS
+       if (preserve_acls && sxp == &sx2)
+               free_acl(&sx2);
+#endif
        return updated;
 }
 
        return updated;
 }
 
diff --git a/rsync.h b/rsync.h
index a6d5137..1d2441a 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -547,6 +547,10 @@ struct idev_node {
 #define IN_LOOPBACKNET 127
 #endif
 
 #define IN_LOOPBACKNET 127
 #endif
 
+#if HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|HAVE_HPUX_ACLS
+#define ACLS_NEED_MASK 1
+#endif
+
 #define GID_NONE ((gid_t)-1)
 
 union file_extras {
 #define GID_NONE ((gid_t)-1)
 
 union file_extras {
@@ -566,6 +570,7 @@ struct file_struct {
 extern int file_extra_cnt;
 extern int preserve_uid;
 extern int preserve_gid;
 extern int file_extra_cnt;
 extern int preserve_uid;
 extern int preserve_gid;
+extern int preserve_acls;
 
 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
 #define EXTRA_LEN (sizeof (union file_extras))
 
 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
 #define EXTRA_LEN (sizeof (union file_extras))
@@ -598,10 +603,12 @@ extern int preserve_gid;
 /* When the associated option is on, all entries will have these present: */
 #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
 #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
 /* When the associated option is on, all entries will have these present: */
 #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
 #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
+#define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
 
 /* These items are per-entry optional and mutally exclusive: */
 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
 #define F_HL_PREV(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
 
 /* These items are per-entry optional and mutally exclusive: */
 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
 #define F_HL_PREV(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
+#define F_DEF_ACL(f) OPT_EXTRA(f, LEN64_BUMP(f))->unum
 #define F_DIRDEV_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + 2 - 1)->unum)
 #define F_DIRNODE_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + 3 - 1)->num)
 
 #define F_DIRDEV_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + 2 - 1)->unum)
 #define F_DIRNODE_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + 3 - 1)->num)
 
@@ -753,6 +760,17 @@ struct stats {
 
 struct chmod_mode_struct;
 
 
 struct chmod_mode_struct;
 
+#define EMPTY_ITEM_LIST {NULL, 0, 0}
+
+typedef struct {
+       void *items;
+       size_t count;
+       size_t malloced;
+} item_list;
+
+#define EXPAND_ITEM_LIST(lp, type, incr) \
+       (type*)expand_item_list(lp, sizeof (type), #type, incr)
+
 #include "byteorder.h"
 #include "lib/mdfour.h"
 #include "lib/wildmatch.h"
 #include "byteorder.h"
 #include "lib/mdfour.h"
 #include "lib/wildmatch.h"
@@ -771,6 +789,16 @@ struct chmod_mode_struct;
 #define NORETURN __attribute__((__noreturn__))
 #endif
 
 #define NORETURN __attribute__((__noreturn__))
 #endif
 
+typedef struct {
+    STRUCT_STAT st;
+#ifdef SUPPORT_ACLS
+    struct rsync_acl *acc_acl; /* access ACL */
+    struct rsync_acl *def_acl; /* default ACL */
+#endif
+} statx;
+
+#define ACL_READY(sx) ((sx).acc_acl != NULL)
+
 #include "proto.h"
 
 /* We have replacement versions of these if they're missing. */
 #include "proto.h"
 
 /* We have replacement versions of these if they're missing. */
@@ -977,7 +1005,7 @@ size_t strlcat(char *d, const char *s, size_t bufsize);
 #define MY_GID() getgid()
 #endif
 
 #define MY_GID() getgid()
 #endif
 
-extern int verbose;
+extern int verbose, protocol_version;
 
 #ifndef HAVE_INET_NTOP
 const char *inet_ntop(int af, const void *src, char *dst, size_t size);
 
 #ifndef HAVE_INET_NTOP
 const char *inet_ntop(int af, const void *src, char *dst, size_t size);
@@ -991,6 +1019,23 @@ int inet_pton(int af, const char *src, void *dst);
 const char *get_panic_action(void);
 #endif
 
 const char *get_panic_action(void);
 #endif
 
+static inline int32
+read_abbrevint30(int f)
+{
+       if (protocol_version < 30)
+               return read_int(f);
+       return read_abbrevint(f);
+}
+
+static inline void
+write_abbrevint30(int f, int32 x)
+{
+       if (protocol_version < 30)
+               write_int(f, x);
+       else
+               write_abbrevint(f, x);
+}
+
 static inline int
 isDigit(const char *ptr)
 {
 static inline int
 isDigit(const char *ptr)
 {
index 5680670..b1f362d 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -301,7 +301,7 @@ to the detailed description below for a complete description.  verb(
  -q, --quiet                 suppress non-error messages
      --no-motd               suppress daemon-mode MOTD (see caveat)
  -c, --checksum              skip based on checksum, not mod-time & size
  -q, --quiet                 suppress non-error messages
      --no-motd               suppress daemon-mode MOTD (see caveat)
  -c, --checksum              skip based on checksum, not mod-time & size
- -a, --archive               archive mode; same as -rlptgoD (no -H)
+ -a, --archive               archive mode; same as -rlptgoD (no -H, -A)
      --no-OPTION             turn off an implied OPTION (e.g. --no-D)
  -r, --recursive             recurse into directories
  -R, --relative              use relative path names
      --no-OPTION             turn off an implied OPTION (e.g. --no-D)
  -r, --recursive             recurse into directories
  -R, --relative              use relative path names
@@ -323,6 +323,7 @@ to the detailed description below for a complete description.  verb(
  -p, --perms                 preserve permissions
  -E, --executability         preserve executability
      --chmod=CHMOD           affect file and/or directory permissions
  -p, --perms                 preserve permissions
  -E, --executability         preserve executability
      --chmod=CHMOD           affect file and/or directory permissions
+ -A, --acls                  preserve ACLs (implies -p)
  -o, --owner                 preserve owner (super-user only)
  -g, --group                 preserve group
      --devices               preserve device files (super-user only)
  -o, --owner                 preserve owner (super-user only)
  -g, --group                 preserve group
      --devices               preserve device files (super-user only)
@@ -771,7 +772,9 @@ quote(itemization(
   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
   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.
 ))
   their special permission bits disabled except in the case where a new
   directory inherits a setgid bit from its parent directory.
 ))
@@ -802,9 +805,11 @@ The preservation of the destination's setgid bit on newly-created
 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
 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
 
 dit(bf(-E, --executability)) This option causes rsync to preserve the
 executability (or non-executability) of regular files when bf(--perms) is
@@ -822,6 +827,10 @@ quote(itemization(
 
 If bf(--perms) is enabled, this option is ignored.
 
 
 If bf(--perms) is enabled, this option is ignored.
 
+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).
+
 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
 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
@@ -1432,8 +1441,8 @@ if the receiving rsync is at least version 2.6.7 (you can use bf(-vv)
 with older versions of rsync, but that also turns on the output of other
 verbose messages).
 
 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.
 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.
@@ -1482,7 +1491,11 @@ quote(itemization(
   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).
   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
 ))
 
 One other output is possible:  when deleting files, the "%i" will output
diff --git a/smb_acls.h b/smb_acls.h
new file mode 100644 (file)
index 0000000..e645ccc
--- /dev/null
@@ -0,0 +1,281 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 2.2.x
+   Portable SMB ACL interface
+   Copyright (C) Jeremy Allison 2000
+   
+   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.
+*/
+
+#ifndef _SMB_ACLS_H
+#define _SMB_ACLS_H
+
+#if defined HAVE_POSIX_ACLS
+
+/* This is an identity mapping (just remove the SMB_). */
+
+#define SMB_ACL_TAG_T          acl_tag_t
+#define SMB_ACL_TYPE_T         acl_type_t
+#define SMB_ACL_PERMSET_T      acl_permset_t
+#define SMB_ACL_PERM_T         acl_perm_t
+#define SMB_ACL_READ           ACL_READ
+#define SMB_ACL_WRITE          ACL_WRITE
+#define SMB_ACL_EXECUTE                ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER           ACL_USER
+#define SMB_ACL_USER_OBJ       ACL_USER_OBJ
+#define SMB_ACL_GROUP          ACL_GROUP
+#define SMB_ACL_GROUP_OBJ      ACL_GROUP_OBJ
+#define SMB_ACL_OTHER          ACL_OTHER
+#define SMB_ACL_MASK           ACL_MASK
+
+#define SMB_ACL_T              acl_t
+
+#define SMB_ACL_ENTRY_T                acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY    ACL_FIRST_ENTRY
+#define SMB_ACL_NEXT_ENTRY     ACL_NEXT_ENTRY
+
+#define SMB_ACL_TYPE_ACCESS    ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT   ACL_TYPE_DEFAULT
+
+#elif defined HAVE_TRU64_ACLS
+
+/* This is for DEC/Compaq Tru64 UNIX */
+
+#define SMB_ACL_TAG_T          acl_tag_t
+#define SMB_ACL_TYPE_T         acl_type_t
+#define SMB_ACL_PERMSET_T      acl_permset_t
+#define SMB_ACL_PERM_T         acl_perm_t
+#define SMB_ACL_READ           ACL_READ
+#define SMB_ACL_WRITE          ACL_WRITE
+#define SMB_ACL_EXECUTE                ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER           ACL_USER
+#define SMB_ACL_USER_OBJ       ACL_USER_OBJ
+#define SMB_ACL_GROUP          ACL_GROUP
+#define SMB_ACL_GROUP_OBJ      ACL_GROUP_OBJ
+#define SMB_ACL_OTHER          ACL_OTHER
+#define SMB_ACL_MASK           ACL_MASK
+
+#define SMB_ACL_T              acl_t
+
+#define SMB_ACL_ENTRY_T                acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY    0
+#define SMB_ACL_NEXT_ENTRY     1
+
+#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
+/*
+ * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
+ * Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
+ */
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+typedef ushort *SMB_ACL_PERMSET_T;
+typedef ushort SMB_ACL_PERM_T;
+#define SMB_ACL_READ           4
+#define SMB_ACL_WRITE          2
+#define SMB_ACL_EXECUTE                1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER           USER
+#define SMB_ACL_USER_OBJ       USER_OBJ
+#define SMB_ACL_GROUP          GROUP
+#define SMB_ACL_GROUP_OBJ      GROUP_OBJ
+#define SMB_ACL_OTHER          OTHER_OBJ
+#define SMB_ACL_MASK           CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+       int size;
+       int count;
+       int next;
+       struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY    0
+#define SMB_ACL_NEXT_ENTRY     1
+
+#define SMB_ACL_TYPE_ACCESS    0
+#define SMB_ACL_TYPE_DEFAULT   1
+
+#ifdef __CYGWIN__
+#define SMB_ACL_LOSES_SPECIAL_MODE_BITS
+#endif
+
+#elif defined HAVE_HPUX_ACLS
+
+/*
+ * Based on the Solaris & UnixWare code.
+ */
+
+#undef GROUP
+#include <sys/aclv.h>
+
+/* SVR4.2 ES/MP ACLs */
+typedef int SMB_ACL_TAG_T;
+typedef int SMB_ACL_TYPE_T;
+typedef ushort *SMB_ACL_PERMSET_T;
+typedef ushort SMB_ACL_PERM_T;
+#define SMB_ACL_READ           4
+#define SMB_ACL_WRITE          2
+#define SMB_ACL_EXECUTE                1
+
+/* Types of ACLs. */
+#define SMB_ACL_USER           USER
+#define SMB_ACL_USER_OBJ       USER_OBJ
+#define SMB_ACL_GROUP          GROUP
+#define SMB_ACL_GROUP_OBJ      GROUP_OBJ
+#define SMB_ACL_OTHER          OTHER_OBJ
+#define SMB_ACL_MASK           CLASS_OBJ
+
+typedef struct SMB_ACL_T {
+       int size;
+       int count;
+       int next;
+       struct acl acl[1];
+} *SMB_ACL_T;
+
+typedef struct acl *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY    0
+#define SMB_ACL_NEXT_ENTRY     1
+
+#define SMB_ACL_TYPE_ACCESS    0
+#define SMB_ACL_TYPE_DEFAULT   1
+
+#elif defined HAVE_IRIX_ACLS
+
+#define SMB_ACL_TAG_T          acl_tag_t
+#define SMB_ACL_TYPE_T         acl_type_t
+#define SMB_ACL_PERMSET_T      acl_permset_t
+#define SMB_ACL_PERM_T         acl_perm_t
+#define SMB_ACL_READ           ACL_READ
+#define SMB_ACL_WRITE          ACL_WRITE
+#define SMB_ACL_EXECUTE                ACL_EXECUTE
+
+/* Types of ACLs. */
+#define SMB_ACL_USER           ACL_USER
+#define SMB_ACL_USER_OBJ       ACL_USER_OBJ
+#define SMB_ACL_GROUP          ACL_GROUP
+#define SMB_ACL_GROUP_OBJ      ACL_GROUP_OBJ
+#define SMB_ACL_OTHER          ACL_OTHER_OBJ
+#define SMB_ACL_MASK           ACL_MASK
+
+typedef struct SMB_ACL_T {
+       int next;
+       BOOL freeaclp;
+       struct acl *aclp;
+} *SMB_ACL_T;
+
+#define SMB_ACL_ENTRY_T                acl_entry_t
+
+#define SMB_ACL_FIRST_ENTRY    0
+#define SMB_ACL_NEXT_ENTRY     1
+
+#define SMB_ACL_TYPE_ACCESS    ACL_TYPE_ACCESS
+#define SMB_ACL_TYPE_DEFAULT   ACL_TYPE_DEFAULT
+
+#elif defined HAVE_AIX_ACLS
+
+/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
+
+#include "/usr/include/acl.h"
+
+typedef uint *SMB_ACL_PERMSET_T;
+struct acl_entry_link{
+       struct acl_entry_link *prevp;
+       struct new_acl_entry *entryp;
+       struct acl_entry_link *nextp;
+       int count;
+};
+
+struct new_acl_entry{
+       unsigned short ace_len;
+       unsigned short ace_type;
+       unsigned int ace_access;
+       struct ace_id ace_id[1];
+};
+
+#define SMB_ACL_ENTRY_T                struct new_acl_entry*
+#define SMB_ACL_T              struct acl_entry_link*
+#define SMB_ACL_TAG_T          unsigned short
+#define SMB_ACL_TYPE_T         int
+#define SMB_ACL_PERM_T         uint
+#define SMB_ACL_READ           S_IRUSR
+#define SMB_ACL_WRITE          S_IWUSR
+#define SMB_ACL_EXECUTE                S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER           ACEID_USER
+#define SMB_ACL_USER_OBJ       3
+#define SMB_ACL_GROUP          ACEID_GROUP
+#define SMB_ACL_GROUP_OBJ      4
+#define SMB_ACL_OTHER          5
+#define SMB_ACL_MASK           6
+
+
+#define SMB_ACL_FIRST_ENTRY    1
+#define SMB_ACL_NEXT_ENTRY     2
+
+#define SMB_ACL_TYPE_ACCESS    0
+#define SMB_ACL_TYPE_DEFAULT   1
+
+#else /* No ACLs. */
+
+/* No ACLS - fake it. */
+#define SMB_ACL_TAG_T          int
+#define SMB_ACL_TYPE_T         int
+#define SMB_ACL_PERMSET_T      mode_t
+#define SMB_ACL_PERM_T         mode_t
+#define SMB_ACL_READ           S_IRUSR
+#define SMB_ACL_WRITE          S_IWUSR
+#define SMB_ACL_EXECUTE                S_IXUSR
+
+/* Types of ACLs. */
+#define SMB_ACL_USER           0
+#define SMB_ACL_USER_OBJ       1
+#define SMB_ACL_GROUP          2
+#define SMB_ACL_GROUP_OBJ      3
+#define SMB_ACL_OTHER          4
+#define SMB_ACL_MASK           5
+
+typedef struct SMB_ACL_T {
+       int dummy;
+} *SMB_ACL_T;
+
+typedef struct SMB_ACL_ENTRY_T {
+       int dummy;
+} *SMB_ACL_ENTRY_T;
+
+#define SMB_ACL_FIRST_ENTRY    0
+#define SMB_ACL_NEXT_ENTRY     1
+
+#define SMB_ACL_TYPE_ACCESS    0
+#define SMB_ACL_TYPE_DEFAULT   1
+
+#endif /* No ACLs. */
+#endif /* _SMB_ACLS_H */
diff --git a/testsuite/acls.test b/testsuite/acls.test
new file mode 100644 (file)
index 0000000..da9bea0
--- /dev/null
@@ -0,0 +1,39 @@
+#! /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 "$RSYNC" in
+*protocol=29*) test_skipped "ACLs require protocol 30" ;;
+esac
+
+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
diff --git a/testsuite/default-acls.test b/testsuite/default-acls.test
new file mode 100644 (file)
index 0000000..1aa8d49
--- /dev/null
@@ -0,0 +1,70 @@
+#! /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 "$RSYNC" in
+*protocol=29*) test_skipped "ACLs require protocol 30" ;;
+esac
+
+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 <dirname> <default-acl> <file-expected> <program-expected>
+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
index aebb86d..1c34477 100644 (file)
@@ -42,14 +42,14 @@ touch -r "$fromdir/block" "$fromdir/block2"
 $RSYNC -ai "$fromdir/block" "$todir/block2" \
     | tee "$outfile"
 cat <<EOT >"$chkfile"
 $RSYNC -ai "$fromdir/block" "$todir/block2" \
     | tee "$outfile"
 cat <<EOT >"$chkfile"
-cD+++++++ block
+cD+++++++++ block
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed"
 
 $RSYNC -ai "$fromdir/block2" "$todir/block" \
     | tee "$outfile"
 cat <<EOT >"$chkfile"
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed"
 
 $RSYNC -ai "$fromdir/block2" "$todir/block" \
     | tee "$outfile"
 cat <<EOT >"$chkfile"
-cD+++++++ block2
+cD+++++++++ block2
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 2 failed"
 
 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 <<EOT >"$chkfile"
 $RSYNC -Di "$fromdir/block3" "$todir/block" \
     | tee "$outfile"
 cat <<EOT >"$chkfile"
-cD..T.... block3
+cD..T...... block3
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
 
@@ -66,15 +66,15 @@ $RSYNC -aiHvv "$fromdir/" "$todir/" \
     | tee "$outfile"
 filter_outfile
 cat <<EOT >"$chkfile"
     | tee "$outfile"
 filter_outfile
 cat <<EOT >"$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' \
 EOT
 if test ! -b "$fromdir/block2.5"; then
     sed -e '/block2\.5/d' \
@@ -94,15 +94,15 @@ if test -b "$fromdir/block2.5"; then
     $RSYNC -aii --link-dest="$todir" "$fromdir/" "$chkdir/" \
        | tee "$outfile"
     cat <<EOT >"$chkfile"
     $RSYNC -aii --link-dest="$todir" "$fromdir/" "$chkdir/" \
        | tee "$outfile"
     cat <<EOT >"$chkfile"
-cd        ./
-hD        block
-hD        block2
-hD        block2.5
-hD        block3
-hD        char
-hD        char2
-hD        char3
-hS        fifo
+cd          ./
+hD          block
+hD          block2
+hD          block2.5
+hD          block3
+hD          char
+hD          char2
+hD          char3
+hS          fifo
 EOT
     diff $diffopt "$chkfile" "$outfile" || test_fail "test 4 failed"
 fi
 EOT
     diff $diffopt "$chkfile" "$outfile" || test_fail "test 4 failed"
 fi
index a165fa1..c041fbb 100644 (file)
@@ -47,16 +47,16 @@ rm -f "$to2dir" "$to2dir.test"
 $RSYNC -iplr "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
 $RSYNC -iplr "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
-cd+++++++ ./
-cd+++++++ bar/
-cd+++++++ foo/_P30_
-cd+++++++ bar/baz/
->f+++++++ bar/baz/rsync
-cd+++++++ foo/_P29_
->f+++++++ foo/config1
->f+++++++ foo/config2
->f+++++++ foo/extra
-cL+++++++ foo/sym -> ../bar/baz/rsync
+cd+++++++++ ./
+cd+++++++++ bar/
+cd+++++++++ foo/_P30_
+cd+++++++++ bar/baz/
+>f+++++++++ bar/baz/rsync
+cd+++++++++ foo/_P29_
+>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"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed"
 
@@ -68,10 +68,10 @@ chmod 601 "$fromdir/foo/config2"
 $RSYNC -iplrH "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
 $RSYNC -iplrH "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$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"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 2 failed"
 
@@ -88,12 +88,12 @@ chmod 777 "$todir/bar/baz/rsync"
 $RSYNC -iplrtc "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
 $RSYNC -iplrtc "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
-.d..t.... foo/_P30_
-.f..tp... bar/baz/rsync
-.d..t.... foo/_P29_
-.f..t.... foo/config1
->fcstp... foo/config2
-cL..T.... foo/sym -> ../bar/baz/rsync
+.d..t...... foo/_P30_
+.f..tp..... bar/baz/rsync
+.d..t...... foo/_P29_
+.f..t...... foo/config1
+>fcstp..... foo/config2
+cL..T...... foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
 
@@ -118,15 +118,15 @@ $RSYNC -ivvplrtH "$fromdir/" "$todir/" \
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$chkfile"
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$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"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 5 failed"
 
@@ -145,8 +145,8 @@ touch "$todir/foo/config2"
 $RSYNC -iplrtH "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
 $RSYNC -iplrtH "$fromdir/" "$todir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$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"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 7 failed"
 
@@ -154,15 +154,15 @@ $RSYNC -ivvplrtH --copy-dest=../to "$fromdir/" "$to2dir/" \
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$chkfile"
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$chkfile"
-cd        ./
-cd        bar/
-cd        bar/baz/
-cf        bar/baz/rsync
-cd        foo/
-cf        foo/config1
-cf        foo/config2
-hf        foo/extra => foo/config1
-cL        foo/sym -> ../bar/baz/rsync
+cd          ./
+cd          bar/
+cd          bar/baz/
+cf          bar/baz/rsync
+cd          foo/
+cf          foo/config1
+cf          foo/config2
+hf          foo/extra => foo/config1
+cL          foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 8 failed"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 8 failed"
 
@@ -170,7 +170,7 @@ rm -rf "$to2dir"
 $RSYNC -iplrtH --copy-dest=../to "$fromdir/" "$to2dir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
 $RSYNC -iplrtH --copy-dest=../to "$fromdir/" "$to2dir/" \
     | tee "$outfile"
 sed -e "$sed_cmd" <<EOT >"$chkfile"
-hf        foo/extra => foo/config1
+hf          foo/extra => foo/config1
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 9 failed"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 9 failed"
 
@@ -196,15 +196,15 @@ $RSYNC -ivvplrtH --link-dest="$todir" "$fromdir/" "$to2dir/" \
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$chkfile"
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$chkfile"
-cd        ./
-cd        bar/
-cd        bar/baz/
-hf        bar/baz/rsync
-cd        foo/
-hf        foo/config1
-hf        foo/config2
-hf        foo/extra => foo/config1
-$L        foo/sym -> ../bar/baz/rsync
+cd          ./
+cd          bar/
+cd          bar/baz/
+hf          bar/baz/rsync
+cd          foo/
+hf          foo/config1
+hf          foo/config2
+hf          foo/extra => foo/config1
+$L          foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 11 failed"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 11 failed"
 
@@ -244,15 +244,15 @@ $RSYNC -ivvplrtH --compare-dest="$todir" "$fromdir/" "$to2dir/" \
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$chkfile"
     | tee "$outfile"
 filter_outfile
 sed -e "$sed_cmd" <<EOT >"$chkfile"
-cd        ./
-cd        bar/
-cd        bar/baz/
-.f        bar/baz/rsync
-cd        foo/
-.f        foo/config1
-.f        foo/config2
-.f        foo/extra
-.L        foo/sym -> ../bar/baz/rsync
+cd          ./
+cd          bar/
+cd          bar/baz/
+.f          bar/baz/rsync
+cd          foo/
+.f          foo/config1
+.f          foo/config2
+.f          foo/extra
+.L          foo/sym -> ../bar/baz/rsync
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 15 failed"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 15 failed"
 
index 9c74bfe..2503d8a 100644 (file)
--- a/uidlist.c
+++ b/uidlist.c
@@ -35,6 +35,7 @@ extern int verbose;
 extern int am_root;
 extern int preserve_uid;
 extern int preserve_gid;
 extern int am_root;
 extern int preserve_uid;
 extern int preserve_gid;
+extern int preserve_acls;
 extern int numeric_ids;
 
 struct idlist {
 extern int numeric_ids;
 
 struct idlist {
@@ -271,7 +272,7 @@ void send_uid_list(int f)
 {
        struct idlist *list;
 
 {
        struct idlist *list;
 
-       if (preserve_uid) {
+       if (preserve_uid || preserve_acls) {
                int len;
                /* we send sequences of uid/byte-length/name */
                for (list = uidlist; list; list = list->next) {
                int len;
                /* we send sequences of uid/byte-length/name */
                for (list = uidlist; list; list = list->next) {
@@ -288,7 +289,7 @@ void send_uid_list(int f)
                write_int(f, 0);
        }
 
                write_int(f, 0);
        }
 
-       if (preserve_gid) {
+       if (preserve_gid || preserve_acls) {
                int len;
                for (list = gidlist; list; list = list->next) {
                        if (!list->name)
                int len;
                for (list = gidlist; list; list = list->next) {
                        if (!list->name)
@@ -328,18 +329,28 @@ void recv_uid_list(int f, struct file_list *flist)
 {
        int id, i;
 
 {
        int id, i;
 
-       if (preserve_uid && !numeric_ids) {
+       if ((preserve_uid || preserve_acls) && !numeric_ids) {
                /* read the uid list */
                while ((id = read_int(f)) != 0)
                        recv_user_name(f, (uid_t)id);
        }
 
                /* read the uid list */
                while ((id = read_int(f)) != 0)
                        recv_user_name(f, (uid_t)id);
        }
 
-       if (preserve_gid && !numeric_ids) {
+       if ((preserve_gid || preserve_acls) && !numeric_ids) {
                /* read the gid list */
                while ((id = read_int(f)) != 0)
                        recv_group_name(f, (gid_t)id);
        }
 
                /* read the gid list */
                while ((id = read_int(f)) != 0)
                        recv_group_name(f, (gid_t)id);
        }
 
+#ifdef SUPPORT_ACLS
+       if (preserve_acls && !numeric_ids) {
+               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
+
        /* 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++)
        /* 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++)
diff --git a/util.c b/util.c
index 64a5b38..4e03a19 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1466,3 +1466,31 @@ int bitbag_next_bit(struct bitbag *bb, int after)
 
        return -1;
 }
 
        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);
+}