- Updated to apply over the new ACL patch.
authorWayne Davison <wayned@samba.org>
Mon, 1 May 2006 19:07:06 +0000 (19:07 +0000)
committerWayne Davison <wayned@samba.org>
Mon, 1 May 2006 19:07:06 +0000 (19:07 +0000)
- Updated with memory-allocation and backup-call improvements
  (similar to what went into the ACL patch).

xattrs.diff

index e3ac333..ad4591f 100644 (file)
@@ -6,6 +6,15 @@ After applying this patch, run these commands for a successful build:
     ./configure --enable-acl-support --enable-xattr-support
     make
 
+TODO:
+
+ - This patch needs to be rewritten to more efficiently handle large xattrs.
+
+ - We need to affect the itemized output to know when xattrs are being updated.
+
+ - We need to affect the --link-dest option to avoid hard-linking two files
+   that differ in their xattrs (when --xattrs was specified).
+
 --- old/Makefile.in
 +++ new/Makefile.in
 @@ -27,13 +27,13 @@ VERSION=@VERSION@
@@ -24,6 +33,49 @@ After applying this patch, run these commands for a successful build:
  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 \
+--- old/acls.c
++++ new/acls.c
+@@ -29,6 +29,7 @@ extern int am_root;
+ extern int dry_run;
+ extern int orig_umask;
+ extern int preserve_acls;
++extern int preserve_xattrs;
+ extern unsigned int file_struct_len;
+ /* === ACL structures === */
+@@ -749,6 +750,10 @@ void receive_acl(struct file_struct *fil
+       type = SMB_ACL_TYPE_ACCESS;
+       racl_list = &access_acl_list;
+       ndx_ptr = (char*)file + file_struct_len;
++#ifdef SUPPORT_XATTRS
++      if (preserve_xattrs)
++              ndx_ptr += 4;
++#endif
+       do {
+               char tag = read_byte(f);
+               int ndx;
+@@ -807,6 +812,10 @@ void cache_acl(struct file_struct *file,
+       racl = sxp->acc_acl;
+       racl_list = &access_acl_list;
+       ndx_ptr = (char*)file + file_struct_len;
++#ifdef SUPPORT_XATTRS
++      if (preserve_xattrs)
++              ndx_ptr += 4;
++#endif
+       do {
+               if (!racl)
+                       ndx = -1;
+@@ -922,6 +931,10 @@ int set_acl(const char *fname, const str
+       type = SMB_ACL_TYPE_ACCESS;
+       ndx_ptr = (char*)file + file_struct_len;
++#ifdef SUPPORT_XATTRS
++      if (preserve_xattrs)
++              ndx_ptr += 4;
++#endif
+       do {
+               acl_duo *duo_item;
+               BOOL eq;
 --- old/backup.c
 +++ new/backup.c
 @@ -30,6 +30,7 @@ extern char *backup_dir;
@@ -34,39 +86,52 @@ After applying this patch, run these commands for a successful build:
  extern int preserve_devices;
  extern int preserve_specials;
  extern int preserve_links;
-@@ -138,6 +139,10 @@ static int make_bak_dir(char *fullpath)
-                               if (preserve_acls)
-                                       dup_acl(end, fullpath, st.st_mode);
+@@ -136,6 +137,9 @@ static int make_bak_dir(char *fullpath)
+ #ifdef SUPPORT_ACLS
+                               sx.acc_acl = sx.def_acl = NULL;
+ #endif
++#ifdef SUPPORT_XATTRS
++                              sx.xattr = NULL;
++#endif
+                               if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
+                                       continue;
+ #ifdef SUPPORT_ACLS
+@@ -144,6 +148,12 @@ static int make_bak_dir(char *fullpath)
+                                       cache_acl(file, &sx);
+                               }
  #endif
 +#ifdef SUPPORT_XATTRS
-+                              if (preserve_xattrs)
-+                                      dup_xattr(end, fullpath );
++                              if (preserve_xattrs) {
++                                      get_xattr(rel, &sx);
++                                      cache_xattr(file, &sx);
++                              }
 +#endif
+                               set_file_attrs(fullpath, file, NULL, 0);
+                               free(file);
                        }
-               }
-               *p = '/';
-@@ -195,6 +200,10 @@ static int keep_backup(char *fname)
-       if (preserve_acls)
-               push_keep_backup_acl(file, fname, buf);
+@@ -195,6 +205,9 @@ static int keep_backup(char *fname)
+ #ifdef SUPPORT_ACLS
+       sx.acc_acl = sx.def_acl = NULL;
  #endif
 +#ifdef SUPPORT_XATTRS
-+      if (preserve_xattrs)
-+              push_keep_backup_xattr(file, fname, buf);
++      sx.xattr = NULL;
 +#endif
  
-       /* Check to see if this is a device file, or link */
-       if ((am_root && preserve_devices && IS_DEVICE(file->mode))
-@@ -275,6 +284,10 @@ static int keep_backup(char *fname)
-       if (preserve_acls)
-               cleanup_keep_backup_acl();
+       if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
+               return 1; /* the file could have disappeared */
+@@ -208,6 +221,12 @@ static int keep_backup(char *fname)
+               cache_acl(file, &sx);
+       }
  #endif
 +#ifdef SUPPORT_XATTRS
-+      if (preserve_xattrs)
-+              cleanup_keep_backup_xattr();
++      if (preserve_xattrs) {
++              get_xattr(fname, &sx);
++              cache_xattr(file, &sx);
++      }
 +#endif
-       free(file);
  
-       if (verbose > 1) {
+       /* Check to see if this is a device file, or link */
+       if ((am_root && preserve_devices && IS_DEVICE(file->mode))
 --- old/configure.in
 +++ new/configure.in
 @@ -823,6 +823,30 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
@@ -110,63 +175,90 @@ After applying this patch, run these commands for a successful build:
  extern int preserve_links;
  extern int preserve_hard_links;
  extern int preserve_devices;
-@@ -966,6 +967,10 @@ static struct file_struct *send_file_nam
-       if (preserve_acls && make_acl(file, fname) < 0)
-               return NULL;
+@@ -502,7 +503,7 @@ static struct file_struct *receive_file_
+       char thisname[MAXPATHLEN];
+       unsigned int l1 = 0, l2 = 0;
+       int alloc_len, basename_len, dirname_len, linkname_len, sum_len;
+-#ifdef SUPPORT_ACLS
++#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
+       int xtra_len;
+ #endif
+       OFF_T file_length;
+@@ -614,10 +615,16 @@ static struct file_struct *receive_file_
+               xtra_len = (S_ISDIR(mode) ? 2 : 1) * 4;
+       else
+               xtra_len = 0;
++#elif defined SUPPORT_XATTRS
++      xtra_len = 0;
++#endif
++#ifdef SUPPORT_XATTRS
++      if (preserve_xattrs)
++              xtra_len += 4;
+ #endif
+       alloc_len = file_struct_len + dirname_len + basename_len
+-#ifdef SUPPORT_ACLS
++#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
+                 + xtra_len
+ #endif
+                 + linkname_len + sum_len;
+@@ -626,7 +633,7 @@ static struct file_struct *receive_file_
+       file = (struct file_struct *)bp;
+       memset(bp, 0, file_struct_len);
+       bp += file_struct_len;
+-#ifdef SUPPORT_ACLS
++#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
+       bp += xtra_len;
+ #endif
+@@ -727,6 +734,10 @@ static struct file_struct *receive_file_
+       if (preserve_acls)
+               receive_acl(file, f);
+ #endif
++#ifdef SUPPORT_XATTRS
++      if (preserve_xattrs)
++              receive_xattr(file, f );
++#endif
+       return file;
+ }
+@@ -997,6 +1008,13 @@ static struct file_struct *send_file_nam
+                       return NULL;
+       }
  #endif
 +#ifdef SUPPORT_XATTRS
-+      if (preserve_xattrs && make_xattr(file, fname) < 0)
-+              return NULL;
++      if (preserve_xattrs) {
++              sx.xattr = NULL;
++              if (get_xattr(fname, &sx) < 0)
++                      return NULL;
++      }
 +#endif
  
        maybe_emit_filelist_progress(flist->count + flist_count_offset);
  
-@@ -978,12 +983,20 @@ static struct file_struct *send_file_nam
+@@ -1009,11 +1027,19 @@ static struct file_struct *send_file_nam
                if (preserve_acls)
-                       send_acl(file, f);
+                       send_acl(&sx, f);
  #endif
 +#ifdef SUPPORT_XATTRS
 +              if (preserve_xattrs)
-+                      send_xattr(file, f);
++                      send_xattr(&sx, f);
 +#endif
        } else {
  #ifdef SUPPORT_ACLS
-               /* Cleanup unsent ACL(s). */
                if (preserve_acls)
-                       send_acl(file, -1);
+                       free_acl(&sx);
  #endif
 +#ifdef SUPPORT_XATTRS
 +              if (preserve_xattrs)
-+                      send_xattr(file, -1);
++                      free_xattr(&sx);
 +#endif
        }
        return file;
  }
-@@ -1376,6 +1389,10 @@ struct file_list *recv_file_list(int f)
-               if (preserve_acls)
-                       receive_acl(file, f);
- #endif
-+#ifdef SUPPORT_XATTRS
-+              if (preserve_xattrs)
-+                      receive_xattr(file, f );
-+#endif
-               if (S_ISREG(file->mode) || S_ISLNK(file->mode))
-                       stats.total_size += file->length;
-@@ -1403,6 +1420,10 @@ struct file_list *recv_file_list(int f)
-       if (preserve_acls)
-               sort_file_acl_index_lists();
- #endif
-+#ifdef SUPPORT_XATTRS
-+      if (preserve_xattrs)
-+              sort_file_xattr_index_lists();
-+#endif
-       if (f >= 0) {
-               recv_uid_list(f, flist);
 --- old/lib/sysxattr.c
 +++ new/lib/sysxattr.c
-@@ -0,0 +1,43 @@
+@@ -0,0 +1,44 @@
 +/*
 + * Extended attribute support for rsync.
 + *
@@ -189,6 +281,7 @@ After applying this patch, run these commands for a successful build:
 + */
 +
 +#include "rsync.h"
++#include "sysxattr.h"
 +
 +#if defined HAVE_LINUX_XATTRS
 +
@@ -212,7 +305,11 @@ After applying this patch, run these commands for a successful build:
 +#endif /* No xattrs */
 --- old/lib/sysxattr.h
 +++ new/lib/sysxattr.h
-@@ -0,0 +1,9 @@
+@@ -0,0 +1,13 @@
++#if defined SUPPORT_XATTRS && defined HAVE_ATTR_XATTR_H
++#include <attr/xattr.h>
++#endif
++
 +#if defined HAVE_LINUX_XATTRS
 +
 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
@@ -283,7 +380,7 @@ After applying this patch, run these commands for a successful build:
    {"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 },
-@@ -1097,6 +1107,17 @@ int parse_arguments(int *argc, const cha
+@@ -1099,6 +1109,17 @@ int parse_arguments(int *argc, const cha
                        return 0;
  #endif
  
@@ -301,7 +398,7 @@ After applying this patch, run these commands for a successful build:
  
                default:
                        /* A large opt value means that set_refuse_options()
-@@ -1544,6 +1565,10 @@ void server_options(char **args,int *arg
+@@ -1538,6 +1559,10 @@ void server_options(char **args,int *arg
        if (preserve_acls)
                argstr[x++] = 'A';
  #endif
@@ -322,34 +419,40 @@ After applying this patch, run these commands for a successful build:
  extern int preserve_perms;
  extern int preserve_executability;
  extern int preserve_times;
-@@ -215,6 +216,10 @@ int set_file_attrs(char *fname, struct f
-       if (preserve_acls && set_acl(fname, file, &st->st_mode) == 0)
+@@ -223,6 +224,10 @@ int set_file_attrs(char *fname, struct f
+       if (preserve_acls && set_acl(fname, file, sxp) == 0)
                updated = 1;
  #endif
 +#ifdef SUPPORT_XATTRS
-+      if (preserve_xattrs && set_xattr(fname, file) == 0)
++      if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
 +              updated = 1;
 +#endif
  
  #ifdef HAVE_CHMOD
-       if ((st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) {
+       if ((sxp->st.st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) {
 --- old/rsync.h
 +++ new/rsync.h
-@@ -674,6 +674,14 @@ struct chmod_mode_struct;
+@@ -494,6 +494,10 @@ struct idev {
+ #define ACLS_NEED_MASK 1
  #endif
- #include "smb_acls.h"
  
 +#ifdef HAVE_LINUX_XATTRS
 +#define SUPPORT_XATTRS 1
 +#endif
 +
-+#if defined SUPPORT_XATTRS && defined HAVE_ATTR_XATTR_H
-+#include <attr/xattr.h>
+ #define GID_NONE ((gid_t)-1)
+ #define HL_CHECK_MASTER       0
+@@ -686,6 +690,9 @@ typedef struct {
+     struct rsync_acl *acc_acl; /* access ACL */
+     struct rsync_acl *def_acl; /* default ACL */
+ #endif
++#ifdef SUPPORT_XATTRS
++    item_list *xattr;
 +#endif
-+
- #include "proto.h"
+ } statx;
  
- /* We have replacement versions of these if they're missing. */
+ #define ACL_READY(sx) ((sx).acc_acl != NULL)
 --- old/rsync.yo
 +++ new/rsync.yo
 @@ -322,6 +322,7 @@ to the detailed description below for a 
@@ -374,12 +477,13 @@ After applying this patch, run these commands for a successful build:
  transfer.  The resulting value is treated as though it was the permissions
 --- old/xattr.c
 +++ new/xattr.c
-@@ -0,0 +1,525 @@
+@@ -0,0 +1,358 @@
 +/*
 + * Extended Attribute support for rsync.
++ * Written by Jay Fenlason, vaguely based on the ACLs patch.
 + *
 + * Copyright (C) 2004 Red Hat, Inc.
-+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
++ * 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
@@ -402,91 +506,56 @@ After applying this patch, run these commands for a successful build:
 +#ifdef SUPPORT_XATTRS
 +
 +extern int dry_run;
++extern unsigned int file_struct_len;
 +
 +#define RSYNC_XAL_INITIAL 5
 +#define RSYNC_XAL_LIST_INITIAL 100
 +
 +typedef struct {
-+      size_t name_len;
 +      char *name;
-+      size_t datum_len;
 +      char *datum;
++      size_t name_len;
++      size_t datum_len;
 +} rsync_xa;
 +
-+typedef struct {
-+      size_t count;
-+      size_t alloc;
-+      rsync_xa *rxas;
-+} rsync_xal;
-+
-+typedef struct {
-+      size_t count;
-+      size_t alloc;
-+      rsync_xal *rxals;
-+} rsync_xal_list;
-+
 +static size_t namebuf_len = 0;
 +static char *namebuf = NULL;
 +
 +static size_t datumbuf_len = 0;
 +static char *datumbuf = NULL;
 +
-+static rsync_xal curr_rsync_xal = { 0, 0, NULL };
-+static rsync_xal_list rsync_xal_l = { 0, 0, NULL };
-+
++static item_list empty_xattr = EMPTY_ITEM_LIST;
++static item_list rsync_xal_l = EMPTY_ITEM_LIST;
 +
 +/* ------------------------------------------------------------------------- */
 +
-+/* the below stuff is only used by the receiver */
-+
-+/* structure to hold index to rsync_xal_l member corresponding to
-+ * flist->files[i] */
-+
-+typedef struct {
-+      const struct file_struct *file;
-+      int xalidx;
-+} file_xal_index;
-+
-+typedef struct {
-+      size_t count;
-+      size_t alloc;
-+      file_xal_index *filexalidxs;
-+} file_xal_index_list;
-+
-+static file_xal_index_list fxil = {0, 0, NULL };
-+
-+/* stuff for redirecting calls to set_acl() from set_perms()
-+ * for keep_backup() */
-+static const struct file_struct *backup_orig_file = NULL;
-+static const char null_string[] = "";
-+static const char *backup_orig_fname = null_string;
-+static const char *backup_dest_fname = null_string;
-+static rsync_xal backup_xal;
-+
-+/* ------------------------------------------------------------------------- */
-+
-+static void rsync_xal_free(rsync_xal *x)
++static void rsync_xal_free(item_list *xalp)
 +{
 +      size_t i;
++      rsync_xa *rxas = xalp->items;
 +
-+      for (i = 0; i < x->count; i++) {
-+              free(x->rxas[i].name);
-+              /* free(x->rxas[i].value); */
++      for (i = 0; i < xalp->count; i++) {
++              free(rxas[i].name);
++              /* free(rxas[i].value); */
 +      }
-+      x->count = 0;
++      xalp->count = 0;
 +}
 +
-+static int rsync_xal_compare_names(const void *x1, const void *x2)
++void free_xattr(statx *sxp)
 +{
-+      const rsync_xa *xa1;
-+      const rsync_xa *xa2;
++      rsync_xal_free(sxp->xattr);
++      free(sxp->xattr);
++      sxp->xattr = NULL;
++}
 +
-+      xa1 = x1;
-+      xa2 = x2;
++static int rsync_xal_compare_names(const void *x1, const void *x2)
++{
++      const rsync_xa *xa1 = x1;
++      const rsync_xa *xa2 = x2;
 +      return strcmp(xa1->name, xa2->name);
 +}
 +
-+static int rsync_xal_get(const char *fname, rsync_xal *x)
++static int rsync_xal_get(const char *fname, item_list *xalp)
 +{
 +      ssize_t name_size;
 +      ssize_t datum_size;
@@ -511,12 +580,12 @@ After applying this patch, run these commands for a successful build:
 +      }
 +      if (name_size < 0) {
 +              if (errno == ENOTSUP)
-+                      return -1;
++                      return 0;
 +              if (errno == ERANGE) {
 +                      name_size = sys_llistxattr(fname, NULL, 0);
 +                      if (name_size < 0) {
-+                              rprintf(FERROR, "%s: rsync_xal_get: llistxattr: %s\n",
-+                                      fname, strerror(errno));
++                              rsyserr(FERROR, errno, "%s: rsync_xal_get: llistxattr",
++                                      fname);
 +                              return -1;
 +                      }
 +                      namebuf = realloc_array(namebuf, char, name_size + 1);
@@ -525,35 +594,24 @@ After applying this patch, run these commands for a successful build:
 +                      namebuf_len = name_size;
 +                      name_size = sys_llistxattr(fname, namebuf, namebuf_len);
 +                      if (name_size < 0) {
-+                              rprintf(FERROR,
-+                                  "%s: rsync_xal_get: re-llistxattr failed: %s\n",
-+                                  fname, strerror(errno));
++                              rsyserr(FERROR, errno,
++                                  "%s: rsync_xal_get: re-llistxattr failed",
++                                  fname);
 +                              return -1;
 +                      }
 +              } else {
-+                      rprintf(FERROR,
-+                          "%s: rsync_xal_get: llistxattr failed: %s\n",
-+                          fname, strerror(errno));
++                      rsyserr(FERROR, errno,
++                          "%s: rsync_xal_get: llistxattr failed:",
++                          fname);
 +                      return -1;
 +              }
 +      }
-+      rsync_xal_free(x);
 +      if (name_size == 0)
 +              return 0;
 +      for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
-+              len = strlen(name) + 1;
-+
-+              if (x->count >= x->alloc) {
-+                      size_t new_alloc;
-+                      rsync_xa *new_rxas;
++              rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
 +
-+                      new_alloc = x->alloc < RSYNC_XAL_INITIAL ? RSYNC_XAL_INITIAL : x->alloc * 2;
-+                      new_rxas = realloc_array(x->rxas, rsync_xa, new_alloc);
-+                      if (!new_rxas)
-+                              out_of_memory("rsync_xal_get");
-+                      x->alloc = new_alloc;
-+                      x->rxas = new_rxas;
-+              }
++              len = strlen(name) + 1;
 +              datum_size = sys_lgetxattr(fname, name, datumbuf, datumbuf_len);
 +              if (datum_size > (ssize_t)datumbuf_len) {
 +                      datum_size = -1;
@@ -565,9 +623,9 @@ After applying this patch, run these commands for a successful build:
 +                      if (errno == ERANGE) {
 +                              datum_size = sys_lgetxattr(fname, name, NULL, 0);
 +                              if (datum_size < 0) {
-+                                      rprintf(FERROR,
-+                                          "%s: rsync_xal_get: lgetxattr %s failed: %s\n",
-+                                          fname, name, strerror(errno));
++                                      rsyserr(FERROR, errno,
++                                          "%s: rsync_xal_get: lgetxattr %s failed",
++                                          fname, name);
 +                                      return -1;
 +                              }
 +                              datumbuf = realloc_array(datumbuf, char, datum_size + 1);
@@ -576,327 +634,205 @@ After applying this patch, run these commands for a successful build:
 +                              datumbuf_len = datum_size;
 +                              datum_size = sys_lgetxattr(fname, name, datumbuf, datumbuf_len);
 +                              if (datum_size < 0) {
-+                                      rprintf(FERROR,
-+                                          "%s: rsync_xal_get: re-lgetxattr of %s failed: %s\n",
-+                                          name, fname, strerror(errno));
++                                      rsyserr(FERROR, errno,
++                                          "%s: rsync_xal_get: re-lgetxattr of %s failed",
++                                          name, fname);
 +                                      return -1;
 +                              }
 +                      } else {
-+                              rprintf(FERROR,
-+                                  "%s: rsync_xal_get: lgetxattr %s failed: %s\n",
-+                                  fname, name, strerror(errno));
++                              rsyserr(FERROR, errno,
++                                  "%s: rsync_xal_get: lgetxattr %s failed",
++                                  fname, name);
 +                              return -1;
 +                      }
 +              }
 +              ptr = new_array(char, len + datum_size);
 +              if (!ptr)
 +                      out_of_memory("rsync_xal_get");
-+              strcpy(ptr, name);
++              memcpy(ptr, name, len);
 +              if (datum_size)
 +                      memcpy(ptr + len, datumbuf, datum_size);
-+              x->rxas[curr_rsync_xal.count].name_len = len;
-+              x->rxas[curr_rsync_xal.count].name = ptr;
-+              x->rxas[curr_rsync_xal.count].datum_len = datum_size;
-+              x->rxas[curr_rsync_xal.count].datum = ptr + len;
-+              x->count++;
-+      }
-+      if (x->count > 1) {
-+              qsort(x->rxas, x->count, sizeof (rsync_xa), rsync_xal_compare_names);
++              rxas->name_len = len;
++              rxas->name = ptr;
++              rxas->datum_len = datum_size;
++              rxas->datum = ptr + len;
 +      }
++      if (xalp->count > 1)
++              qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
 +      return 0;
 +}
 +
-+
-+/* generate the xattr(s) for this flist entry;
-+ * xattr(s) are either sent or cleaned-up by send_xattr() below */
-+
-+int make_xattr(UNUSED(const struct file_struct *file), const char *fname)
++/* Read the xattr(s) for this filename. */
++int get_xattr(const char *fname, statx *sxp)
 +{
-+      rsync_xal_get(fname, &curr_rsync_xal);
-+      return 0; /* TODO:  This needs to return 1 if no xattrs changed! */
++      sxp->xattr = new(item_list);
++      *sxp->xattr = empty_xattr;
++      if (rsync_xal_get(fname, sxp->xattr) < 0) {
++              free_xattr(sxp);
++              return -1;
++      }
++      return 0;
 +}
 +
-+static ssize_t rsync_xal_find_matching(void)
++static int find_matching_xattr(item_list *xalp)
 +{
-+      size_t i;
-+      size_t j;
++      size_t i, j;
++      item_list *lst = rsync_xal_l.items;
 +
 +      for (i = 0; i < rsync_xal_l.count; i++) {
++              rsync_xa *rxas1 = lst[i].items;
++              rsync_xa *rxas2 = xalp->items;
++
 +              /* Wrong number of elements? */
-+              if (rsync_xal_l.rxals[i].count != curr_rsync_xal.count)
++              if (lst[i].count != xalp->count)
 +                      continue;
 +              /* any elements different? */
-+              for (j = 0; j < curr_rsync_xal.count; j++) {
-+                      if (rsync_xal_l.rxals[i].rxas[j].name_len != curr_rsync_xal.rxas[j].name_len
-+                       || rsync_xal_l.rxals[i].rxas[j].datum_len != curr_rsync_xal.rxas[j].datum_len
-+                       || strcmp(rsync_xal_l.rxals[i].rxas[j].name, curr_rsync_xal.rxas[j].name)
-+                       || memcmp(rsync_xal_l.rxals[i].rxas[j].datum, curr_rsync_xal.rxas[j].datum, curr_rsync_xal.rxas[j].datum_len))
++              for (j = 0; j < xalp->count; j++) {
++                      if (rxas1[j].name_len != rxas2[j].name_len
++                       || rxas1[j].datum_len != rxas2[j].datum_len
++                       || strcmp(rxas1[j].name, rxas2[j].name)
++                       || memcmp(rxas1[j].datum, rxas2[j].datum, rxas2[j].datum_len))
 +                              break;
 +              }
 +              /* no differences found.  This is The One! */
-+              if (j == curr_rsync_xal.count)
-+                      break;
++              if (j == xalp->count)
++                      return i;
 +      }
-+      if (i < rsync_xal_l.count)
-+              return i;
-+      return (ssize_t)-1;
++
++      return -1;
 +}
 +
-+/* Store curr_rsync_xal on the end of rsync_xal_l */
-+static void rsync_xal_store(void)
++/* Store *xalp on the end of rsync_xal_l */
++static void rsync_xal_store(item_list *xalp)
 +{
-+      if (rsync_xal_l.count <= rsync_xal_l.alloc) {
-+              size_t new_alloc;
-+              void *new_xal;
-+
-+              new_alloc = rsync_xal_l.count < RSYNC_XAL_LIST_INITIAL ? RSYNC_XAL_LIST_INITIAL : rsync_xal_l.count * 2;
-+              new_xal = realloc_array(rsync_xal_l.rxals, rsync_xal, new_alloc);
-+              if (!new_xal)
-+                      out_of_memory("rsync_xal_store");
-+              rsync_xal_l.alloc = new_alloc;
-+              rsync_xal_l.rxals = new_xal;
-+      }
-+      rsync_xal_l.rxals[rsync_xal_l.count] = curr_rsync_xal;
-+      rsync_xal_l.count++;
-+      curr_rsync_xal.count = 0;
-+      curr_rsync_xal.alloc = 0;
++      item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
++      EXPAND_ITEM_LIST(new_lst, item_list, xalp->count);
++      memcpy(new_lst->items, xalp->items, xalp->count * sizeof (item_list));
++      new_lst->count = xalp->count;
++      xalp->count = 0;
 +}
 +
-+/* send the make_xattr()-generated xattr list for this flist entry,
-+ * or clean up after an flist entry that's not being sent (f == -1) */
-+
-+void send_xattr(UNUSED(const struct file_struct *file), int f)
++/* Send the make_xattr()-generated xattr list for this flist entry. */
++void send_xattr(statx *sxp, int f)
 +{
-+      ssize_t index;
-+
-+      if (f == -1) {
-+              rsync_xal_free(&curr_rsync_xal);
-+              return;
-+      }
-+      index = rsync_xal_find_matching();
-+      if (index != -1) {
++      int ndx = find_matching_xattr(sxp->xattr);
++      if (ndx != -1) {
 +              write_byte(f, 'x');
-+              write_int(f, index);
-+              rsync_xal_free(&curr_rsync_xal);
++              write_int(f, ndx);
++              rsync_xal_free(sxp->xattr);
 +      } else {
 +              rsync_xa *rxa;
-+              size_t count;
-+
-+              count = curr_rsync_xal.count;
++              int count = sxp->xattr->count;
 +              write_byte(f, 'X');
 +              write_int(f, count);
-+              for (rxa = curr_rsync_xal.rxas; count--; rxa++) {
++              for (rxa = sxp->xattr->items; count--; rxa++) {
 +                      write_int(f, rxa->name_len);
 +                      write_int(f, rxa->datum_len);
 +                      write_buf(f, rxa->name, rxa->name_len);
 +                      write_buf(f, rxa->datum, rxa->datum_len);
 +              }
-+              rsync_xal_store();
++              rsync_xal_store(sxp->xattr);
 +      }
++      free_xattr(sxp);
 +}
 +
-+
 +/* ------------------------------------------------------------------------- */
-+/* receive and build the rsync_xattr_lists */
 +
++/* receive and build the rsync_xattr_lists */
 +void receive_xattr(struct file_struct *file, int f)
 +{
-+      char *fname;
-+      int tag;
++      static item_list temp_xattr = EMPTY_ITEM_LIST;
++      int tag = read_byte(f);
++      char *ndx_ptr = (char*)file + file_struct_len;
++      int ndx;
 +
-+      fname = f_name(file, NULL);
-+      tag = read_byte(f);
-+      if (tag != 'X' && tag != 'x') {
-+              rprintf(FERROR,
-+                  "%s: receive_xattr: unknown extended attribute type tag: %c\n",
-+                  fname, tag);
-+              exit_cleanup(RERR_STREAMIO);
-+      }
-+
-+      if (fxil.alloc <= fxil.count) {
-+              void *new_ptr;
-+              size_t new_alloc;
-+
-+              if (fxil.count <  RSYNC_XAL_LIST_INITIAL)
-+                      new_alloc = fxil.alloc + RSYNC_XAL_LIST_INITIAL;
-+              else
-+                      new_alloc = fxil.alloc * 2;
-+              new_ptr = realloc_array(fxil.filexalidxs, file_xal_index, new_alloc);
-+              if (!new_ptr)
-+                      out_of_memory("receive_xattr");
-+              if (verbose >= 3) {
-+                      rprintf(FINFO, "receive_xattr to %lu bytes, %s move\n",
-+                              (unsigned long)(new_alloc * sizeof (file_xal_index)),
-+                              fxil.filexalidxs == new_ptr ? "did not" : "did");
-+              }
-+
-+              fxil.filexalidxs = new_ptr;
-+              fxil.alloc = new_alloc;
-+      }
-+
-+      fxil.filexalidxs[fxil.count].file = file;
 +      if (tag == 'X') {
-+              size_t count;
-+              size_t i;
-+
-+              fxil.filexalidxs[fxil.count].xalidx = rsync_xal_l.count;
-+
-+              count = read_int(f);
-+              curr_rsync_xal.count = count;
-+              curr_rsync_xal.alloc = count;
-+              curr_rsync_xal.rxas = new_array(rsync_xa, count);
-+              if (!curr_rsync_xal.rxas)
-+                      out_of_memory("receive_xattr");
++              int i, count = read_int(f);
 +              for (i = 0; i < count; i++) {
-+                      size_t name_len;
-+                      size_t datum_len;
 +                      char *ptr;
-+
-+                      name_len = read_int(f);
-+                      datum_len = read_int(f);
++                      rsync_xa *rxa;
++                      size_t name_len = read_int(f);
++                      size_t datum_len = read_int(f);
 +                      if (name_len + datum_len < name_len)
 +                              out_of_memory("receive_xattr"); /* overflow */
++                      rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
 +                      ptr = new_array(char, name_len + datum_len);
 +                      if (!ptr)
 +                              out_of_memory("receive_xattr");
 +                      read_buf(f, ptr, name_len);
 +                      read_buf(f, ptr + name_len, datum_len);
-+                      curr_rsync_xal.rxas[i].name_len = name_len;
-+                      curr_rsync_xal.rxas[i].datum_len = datum_len;
-+                      curr_rsync_xal.rxas[i].name = ptr;
-+                      curr_rsync_xal.rxas[i].datum = ptr + name_len;
++                      rxa->name_len = name_len;
++                      rxa->datum_len = datum_len;
++                      rxa->name = ptr;
++                      rxa->datum = ptr + name_len;
 +              }
-+              rsync_xal_store();
-+      } else {
-+              size_t index;
-+
-+              index = read_int(f);
-+              if (index >= rsync_xal_l.count) {
-+                      rprintf(FERROR, "%s: receive_xattr: xa index %lu out of range\n",
-+                              fname, (unsigned long)index);
++              ndx = rsync_xal_l.count;
++              rsync_xal_store(&temp_xattr);
++      } else if (tag == 'x') {
++              ndx = read_int(f);
++              if (ndx < 0 || (size_t)ndx >= rsync_xal_l.count) {
++                      rprintf(FERROR, "%s: receive_xattr: xa index %d out of range\n",
++                              f_name(file, NULL), ndx);
 +                      exit_cleanup(RERR_STREAMIO);
 +              }
-+              fxil.filexalidxs[fxil.count].xalidx = index;
-+      }
-+      fxil.count++;
-+}
-+
-+static int rsync_xal_set(const char *fname, rsync_xal *x)
-+{
-+      size_t i;
-+      int ret = 0;
-+
-+      for (i = 0; i < x->count; i++) {
-+              int status = sys_lsetxattr(fname, x->rxas[i].name, x->rxas[i].datum, x->rxas[i].datum_len, 0);
-+              if (status < 0) {
-+                      rprintf(FERROR, "%s: rsync_xal_set: lsetxattr %s failed: %s\n",
-+                              fname, x->rxas[i].name, strerror(errno));
-+                      ret = -1;
-+              }
++      } else {
++              rprintf(FERROR,
++                  "%s: receive_xattr: unknown extended attribute type tag: %c\n",
++                  f_name(file, NULL), tag);
++              exit_cleanup(RERR_STREAMIO);
++              ndx = 0; /* silence a compiler warning... */
 +      }
-+      return ret;
-+}
-+
-+/* for duplicating xattrs on backups when using backup_dir */
-+
-+int dup_xattr(const char *orig, const char *bak)
-+{
-+      int ret;
-+
-+      if (rsync_xal_get(orig, &backup_xal) < 0)
-+              ret = rsync_xal_set(bak, &backup_xal);
-+      else
-+              ret = 0;
-+      rsync_xal_free(&backup_xal);
-+
-+      return ret;
-+}
-+
-+void push_keep_backup_xattr(const struct file_struct *file, const char *orig, const char *dest)
-+{
-+      backup_orig_file = file;
-+      backup_orig_fname = orig;
-+      backup_dest_fname = dest;
-+      rsync_xal_get(orig, &backup_xal);
-+}
 +
-+static int set_keep_backup_xal(void)
-+{
-+      return rsync_xal_set(backup_dest_fname, &backup_xal);
++      SIVAL(ndx_ptr, 0, ndx);
 +}
 +
-+void cleanup_keep_backup_xattr(void)
++/* Turn the xattr data in statx into cached xattr data, setting the index
++ * values in the file struct. */
++void cache_xattr(struct file_struct *file, statx *sxp)
 +{
-+      backup_orig_file = NULL;
-+      backup_orig_fname = null_string;
-+      backup_dest_fname = null_string;
-+      rsync_xal_free(&backup_xal);
-+}
++      char *ndx_ptr = (char*)file + file_struct_len;
++      int ndx;
 +
-+static int file_xal_index_compare(const void *x1, const void *x2)
-+{
-+      const file_xal_index *xa1;
-+      const file_xal_index *xa2;
++      if (!sxp->xattr)
++              return;
 +
-+      xa1 = x1;
-+      xa2 = x2;
-+      return xa1->file == xa2->file ? 0 : xa1->file < xa2->file ? -1 : 1;
-+}
++      ndx = find_matching_xattr(sxp->xattr);
++      if (ndx == -1)
++              rsync_xal_store(sxp->xattr);
++      free_xattr(sxp);
 +
-+void sort_file_xattr_index_lists(void)
-+{
-+      qsort(fxil.filexalidxs, fxil.count, sizeof (file_xal_index), file_xal_index_compare);
++      SIVAL(ndx_ptr, 0, ndx);
 +}
 +
-+static int find_file_xal_index(const struct file_struct *file)
++static int rsync_xal_set(const char *fname, item_list *xalp)
 +{
-+      int low = 0, high = fxil.count;
-+      const struct file_struct *file_mid;
++      rsync_xa *rxas = xalp->items;
++      size_t i;
++      int ret = 0;
 +
-+      if (!high--) {
-+              rprintf(FERROR, "find_file_xal_index: no entries\n");
-+              exit_cleanup(RERR_STREAMIO);
-+              return -1;
-+      }
-+      do {
-+              int mid = (high + low) / 2;
-+              file_mid = fxil.filexalidxs[mid].file;
-+              if (file_mid == file)
-+                      return fxil.filexalidxs[mid].xalidx;
-+              if (file_mid > file)
-+                      high = mid - 1;
-+              else
-+                      low = mid + 1;
-+      } while (low < high);
-+      if (low == high) {
-+              file_mid = fxil.filexalidxs[low].file;
-+              if (file_mid == file)
-+                      return fxil.filexalidxs[low].xalidx;
++      for (i = 0; i < xalp->count; i++) {
++              int status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len, 0);
++              if (status < 0) {
++                      rsyserr(FERROR, errno, "%s: rsync_xal_set: lsetxattr %s failed",
++                              fname, rxas[i].name);
++                      ret = -1;
++              }
 +      }
-+      rprintf(FERROR,
-+              "find_file_xal_index: can't find entry for file in list\n");
-+      exit_cleanup(RERR_STREAMIO);
-+      return -1;
++      return ret;
 +}
 +
-+/* set extended attributes on rsync-ed or keep_backup-ed file */
-+
-+int set_xattr(const char *fname, const struct file_struct *file)
++/* Set extended attributes on indicated filename. */
++int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
 +{
-+      int xalidx;
-+      rsync_xal *x;
++      int ndx;
++      char *ndx_ptr = (char*)file + file_struct_len;
++      item_list *lst = rsync_xal_l.items;
 +
 +      if (dry_run)
 +              return 1; /* FIXME: --dry-run needs to compute this value */
 +
-+      if (file == backup_orig_file) {
-+              if (!strcmp(fname, backup_dest_fname))
-+                      return set_keep_backup_xal();
-+      }
-+      xalidx = find_file_xal_index(file);
-+      x = &(rsync_xal_l.rxals[xalidx]);
-+
-+      return rsync_xal_set(fname, x);
++      ndx = IVAL(ndx_ptr, 0);
++      return rsync_xal_set(fname, lst + ndx); /* TODO:  This needs to return 1 if no xattrs changed! */
 +}
 +
 +#endif /* SUPPORT_XATTRS */