xattrs.diff   [plain text]


This patch adds basic support for extended attributes.  It works, but there
are some things that still needs to be improved (see TODO list below).

To use this patch, run these commands for a successful build:

    patch -p1 <patches/acls.diff
    patch -p1 <patches/xattrs.diff
    ./prepare-source
    ./configure --enable-acl-support --enable-xattr-support
    make

Alternately, if you don't want ACL support, configure it this way:

    ./configure --enable-xattr-support

TODO:

 - This patch needs to more efficiently handle large xattrs, especially when
   they're unchanged.

 - Extraneous xattr values need to be removed from files that are not being
   recreated.

 - 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
@@ -28,13 +28,13 @@ VERSION=@VERSION@
 
 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 \
-	lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
+	lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattr.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=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \
 	main.o checksum.o match.o syscall.o log.o backup.o
 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
-	fileio.o batch.o clientname.o chmod.o acls.o
+	fileio.o batch.o clientname.o chmod.o acls.o xattr.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 \
--- old/acls.c
+++ new/acls.c
@@ -30,6 +30,7 @@ extern int read_only;
 extern int list_only;
 extern int orig_umask;
 extern int preserve_acls;
+extern int preserve_xattrs;
 extern unsigned int file_struct_len;
 
 /* === ACL structures === */
@@ -741,6 +742,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;
@@ -800,6 +805,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;
@@ -920,6 +929,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;
 
 extern int am_root;
 extern int preserve_acls;
+extern int preserve_xattrs;
 extern int preserve_devices;
 extern int preserve_specials;
 extern int preserve_links;
@@ -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) {
+					get_xattr(rel, &sx);
+					cache_xattr(file, &sx);
+				}
+#endif
 				set_file_attrs(fullpath, file, NULL, 0);
 				free(file);
 			}
@@ -195,6 +205,9 @@ static int keep_backup(char *fname)
 #ifdef SUPPORT_ACLS
 	sx.acc_acl = sx.def_acl = NULL;
 #endif
+#ifdef SUPPORT_XATTRS
+	sx.xattr = NULL;
+#endif
 
 	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) {
+		get_xattr(fname, &sx);
+		cache_xattr(file, &sx);
+	}
+#endif
 
 	/* 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
@@ -856,6 +856,40 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
   AC_MSG_RESULT(no)
 )
 
+AC_CHECK_HEADERS(attr/xattr.h)
+AC_CHECK_HEADERS(sys/xattr.h)
+AC_CHECK_HEADERS(sys/extattr.h)
+AC_MSG_CHECKING(whether to support extended attributes)
+AC_ARG_ENABLE(xattr-support,
+AC_HELP_STRING([--enable-xattr-support], [Include extended attribute support (default=no)]),
+[ case "$enableval" in
+  yes)
+      case "$host_os" in
+      *linux*)
+            AC_MSG_RESULT(Using Linux xattrs)
+            AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs])
+            ;;
+      darwin*)
+            AC_MSG_RESULT(Using OS X xattrs)
+            AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs])
+            ;;
+      freebsd*)
+            AC_MSG_RESULT(Using FreeBSD extattrs)
+            AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
+            ;;
+      *)
+            AC_MSG_RESULT(Xattrs requested but not Linux or OS X.  Good luck...)
+            ;;
+      esac
+      ;;
+  *)
+      AC_MSG_RESULT(no)
+      AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes])
+  esac ],
+  AC_MSG_RESULT(no)
+  AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes])
+)
+
 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
 AC_OUTPUT
 
--- old/flist.c
+++ new/flist.c
@@ -41,6 +41,7 @@ extern int one_file_system;
 extern int copy_dirlinks;
 extern int keep_dirlinks;
 extern int preserve_acls;
+extern int preserve_xattrs;
 extern int preserve_links;
 extern int preserve_hard_links;
 extern int preserve_devices;
@@ -498,7 +499,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;
@@ -610,10 +611,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;
@@ -622,7 +629,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
 
@@ -723,6 +730,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;
 }
@@ -974,7 +985,7 @@ static struct file_struct *send_file_nam
 					  unsigned short flags)
 {
 	struct file_struct *file;
-#ifdef SUPPORT_ACLS
+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
 	statx sx;
 #endif
 
@@ -994,6 +1005,13 @@ static struct file_struct *send_file_nam
 			return NULL;
 	}
 #endif
+#ifdef SUPPORT_XATTRS
+	if (preserve_xattrs) {
+		sx.xattr = NULL;
+		if (get_xattr(fname, &sx) < 0)
+			return NULL;
+	}
+#endif
 
 	maybe_emit_filelist_progress(flist->count + flist_count_offset);
 
@@ -1006,11 +1024,19 @@ static struct file_struct *send_file_nam
 		if (preserve_acls)
 			send_acl(&sx, f);
 #endif
+#ifdef SUPPORT_XATTRS
+		if (preserve_xattrs)
+			send_xattr(&sx, f);
+#endif
 	} else {
 #ifdef SUPPORT_ACLS
 		if (preserve_acls)
 			free_acl(&sx);
 #endif
+#ifdef SUPPORT_XATTRS
+		if (preserve_xattrs)
+			free_xattr(&sx);
+#endif
 	}
 	return file;
 }
--- old/lib/sysxattr.c
+++ new/lib/sysxattr.c
@@ -0,0 +1,135 @@
+/*
+ * Extended attribute support for rsync.
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Written by Jay Fenlason.
+ *
+ * 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 "sysxattr.h"
+
+#ifdef SUPPORT_XATTRS
+
+#if defined HAVE_LINUX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+	return lgetxattr(path, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+	return fgetxattr(filedes, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+	return lsetxattr(path, name, value, size, 0);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+	return lremovexattr(path, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+	return llistxattr(path, list, size);
+}
+
+#elif HAVE_OSX_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+	return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+	return fgetxattr(filedes, name, value, size, 0, 0);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+	return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+	return removexattr(path, name, XATTR_NOFOLLOW);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+	return listxattr(path, list, size, XATTR_NOFOLLOW);
+}
+
+#elif HAVE_FREEBSD_XATTRS
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
+{
+	return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
+{
+	return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
+{
+	return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
+}
+
+int sys_lremovexattr(const char *path, const char *name)
+{
+	return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
+}
+
+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
+{
+	unsigned char keylen;
+	ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
+
+	if (len <= 0 || (size_t)len > size)
+		return len;
+
+	/* FreeBSD puts a single-byte length before each string, with no '\0'
+	 * terminator.  We need to change this into a series of null-terminted
+	 * strings.  Since the size is the same, we can simply transform the
+	 * output in place. */
+	for (off = 0; off < len; off += keylen + 1) {
+		keylen = ((unsigned char*)list)[off];
+		if (off + keylen >= len) {
+			/* Should be impossible, but kernel bugs happen! */
+			errno = EINVAL;
+			return -1;
+		}
+		memmove(list+off, list+off+1, keylen);
+		list[off+keylen] = '\0';
+	}
+
+	return len;
+}
+
+#else
+
+#error You need to create xattr compatibility functions.
+
+#endif
+
+#endif /* SUPPORT_XATTRS */
--- old/lib/sysxattr.h
+++ new/lib/sysxattr.h
@@ -0,0 +1,26 @@
+#ifdef SUPPORT_XATTRS
+
+#if defined HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif defined HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#elif defined HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+
+/* Linux 2.4 does not define this as a distinct errno value: */
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
+
+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
+int sys_lremovexattr(const char *path, const char *name);
+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
+
+#else
+
+/* No xattrs available */
+
+#endif
--- old/options.c
+++ new/options.c
@@ -48,6 +48,7 @@ int copy_links = 0;
 int preserve_links = 0;
 int preserve_hard_links = 0;
 int preserve_acls = 0;
+int preserve_xattrs = 0;
 int preserve_perms = 0;
 int preserve_executability = 0;
 int preserve_devices = 0;
@@ -201,6 +202,7 @@ static void print_rsync_version(enum log
 	char const *have_inplace = "no ";
 	char const *hardlinks = "no ";
 	char const *acls = "no ";
+	char const *xattrs = "no ";
 	char const *links = "no ";
 	char const *ipv6 = "no ";
 	STRUCT_STAT *dumstat;
@@ -220,7 +222,9 @@ static void print_rsync_version(enum log
 #ifdef SUPPORT_ACLS
 	acls = "";
 #endif
-
+#ifdef SUPPORT_XATTRS
+	xattrs = "";
+#endif
 #ifdef SUPPORT_LINKS
 	links = "";
 #endif
@@ -236,8 +240,8 @@ static void print_rsync_version(enum log
 	rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, %shard links, %ssymlinks,\n",
 		(int) (sizeof (OFF_T) * 8), got_socketpair, hardlinks, links);
 
-	rprintf(f, "              batchfiles, %sinplace, %sIPv6, %sACLs,\n",
-		have_inplace, ipv6, acls);
+	rprintf(f, "              batchfiles, %sinplace, %sIPv6, %sACLs, %sxattrs,\n",
+		have_inplace, ipv6, acls, xattrs);
 
 	/* Note that this field may not have type ino_t.  It depends
 	 * on the complicated interaction between largefile feature
@@ -289,7 +293,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," -a, --archive               archive mode; same as -rlptgoD (no -H, -A)\n");
+  rprintf(F," -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)\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");
@@ -314,6 +318,9 @@ void usage(enum logcode F)
 #ifdef SUPPORT_ACLS
   rprintf(F," -A, --acls                  preserve ACLs (implies --perms)\n");
 #endif
+#ifdef SUPPORT_XATTRS
+  rprintf(F," -X, --xattrs                preserve extended attributes (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");
@@ -436,6 +443,9 @@ static struct poptOption long_options[] 
   {"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 },
+  {"xattrs",          'X', POPT_ARG_NONE,   0, 'X', 0, 0 },
+  {"no-xattrs",        0,  POPT_ARG_VAL,    &preserve_xattrs, 0, 0, 0 },
+  {"no-X",             0,  POPT_ARG_VAL,    &preserve_xattrs, 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 },
@@ -1117,6 +1127,17 @@ int parse_arguments(int *argc, const cha
 			return 0;
 #endif
 
+		case 'X':
+#ifdef SUPPORT_XATTRS
+			preserve_xattrs = 1;
+			preserve_perms = 1;
+			break;
+#else
+			snprintf(err_buf,sizeof(err_buf),
+				 "extended attributes are not supported on this %s\n",
+				 am_server ? "server" : "client");
+			return 0;
+#endif
 
 		default:
 			/* A large opt value means that set_refuse_options()
@@ -1563,6 +1584,10 @@ void server_options(char **args,int *arg
 	if (preserve_acls)
 		argstr[x++] = 'A';
 #endif
+#ifdef SUPPORT_XATTRS
+	if (preserve_xattrs)
+		argstr[x++] = 'X';
+#endif
 	if (preserve_uid)
 		argstr[x++] = 'o';
 	if (preserve_gid)
--- old/rsync.c
+++ new/rsync.c
@@ -33,6 +33,7 @@
 extern int verbose;
 extern int dry_run;
 extern int preserve_acls;
+extern int preserve_xattrs;
 extern int preserve_perms;
 extern int preserve_executability;
 extern int preserve_times;
@@ -218,6 +219,10 @@ int set_file_attrs(char *fname, struct f
 	if (daemon_chmod_modes && !S_ISLNK(new_mode))
 		new_mode = tweak_mode(new_mode, daemon_chmod_modes);
 
+#ifdef SUPPORT_XATTRS
+	if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
+		updated = 1;
+#endif
 #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.
--- old/rsync.h
+++ new/rsync.h
@@ -500,6 +500,10 @@ struct idev {
 #define ACLS_NEED_MASK 1
 #endif
 
+#ifndef HAVE_NO_XATTRS
+#define SUPPORT_XATTRS 1
+#endif
+
 #define GID_NONE ((gid_t)-1)
 
 #define HL_CHECK_MASTER	0
@@ -694,6 +698,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
 } statx;
 
 #define ACL_READY(sx) ((sx).acc_acl != NULL)
--- old/rsync.yo
+++ new/rsync.yo
@@ -301,7 +301,7 @@ to the detailed description below for a 
  -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)
+ -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
      --no-OPTION             turn off an implied OPTION (e.g. --no-D)
  -r, --recursive             recurse into directories
  -R, --relative              use relative path names
@@ -324,6 +324,7 @@ to the detailed description below for a 
  -E, --executability         preserve executability
      --chmod=CHMOD           affect file and/or directory permissions
  -A, --acls                  preserve ACLs (implies -p) [non-standard]
+ -X, --xattrs                preserve extended attrs (implies -p) [n.s.]
  -o, --owner                 preserve owner (super-user only)
  -g, --group                 preserve group
      --devices               preserve device files (super-user only)
@@ -818,6 +819,11 @@ version makes it incompatible with sendi
 rsync unless you double the bf(--acls) option (e.g. bf(-AA)).  This
 doubling is not needed when pulling files from an older rsync.
 
+dit(bf(-X, --xattrs)) This option causes rsync to update the remote
+extended attributes to be the same as the local ones.  This will work
+only if the remote machine's rsync supports this option also. This is
+a non-standard option.
+
 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
--- old/xattr.c
+++ new/xattr.c
@@ -0,0 +1,417 @@
+/*
+ * Extended Attribute support for rsync.
+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * 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/sysxattr.h"
+
+#ifdef SUPPORT_XATTRS
+
+extern int dry_run;
+extern int am_root;
+extern int read_only;
+extern int list_only;
+extern unsigned int file_struct_len;
+
+#define RSYNC_XAL_INITIAL 5
+#define RSYNC_XAL_LIST_INITIAL 100
+
+#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
+			    && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
+
+#define USER_PREFIX "user."
+#define UPRE_LEN ((int)sizeof USER_PREFIX - 1)
+#define SYSTEM_PREFIX "system."
+#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
+
+#ifdef HAVE_LINUX_XATTRS
+#define RPRE_LEN 0
+#else
+#define RSYNC_PREFIX "rsync."
+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
+#endif
+
+typedef struct {
+	char *datum, *name;
+	size_t datum_len, name_len;
+} rsync_xa;
+
+static size_t namebuf_len = 0;
+static char *namebuf = NULL;
+
+static item_list empty_xattr = EMPTY_ITEM_LIST;
+static item_list rsync_xal_l = EMPTY_ITEM_LIST;
+
+/* ------------------------------------------------------------------------- */
+
+static void rsync_xal_free(item_list *xalp)
+{
+	size_t i;
+	rsync_xa *rxas = xalp->items;
+
+	for (i = 0; i < xalp->count; i++) {
+		free(rxas[i].datum);
+		/*free(rxas[i].name);*/
+	}
+	xalp->count = 0;
+}
+
+void free_xattr(statx *sxp)
+{
+	rsync_xal_free(sxp->xattr);
+	free(sxp->xattr);
+	sxp->xattr = NULL;
+}
+
+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, item_list *xalp)
+{
+	ssize_t list_len, name_len, datum_len;
+	char *name, *ptr;
+
+	if (!namebuf) {
+		namebuf_len = 1024;
+		namebuf = new_array(char, namebuf_len);
+		if (!namebuf)
+			out_of_memory("rsync_xal_get");
+	}
+
+	/* The length returned includes all the '\0' terminators. */
+	list_len = sys_llistxattr(fname, namebuf, namebuf_len);
+	if (list_len > (ssize_t)namebuf_len) {
+		list_len = -1;
+		errno = ERANGE;
+	}
+	if (list_len < 0) {
+		if (errno == ENOTSUP)
+			return 0;
+		if (errno == ERANGE) {
+			list_len = sys_llistxattr(fname, NULL, 0);
+			if (list_len < 0) {
+				rsyserr(FERROR, errno,
+					"rsync_xal_get: llistxattr(\"%s\",0) failed",
+					fname);
+				return -1;
+			}
+			namebuf = realloc_array(namebuf, char, list_len + 1024);
+			if (!namebuf)
+				out_of_memory("rsync_xal_get");
+			namebuf_len = list_len + 1024;
+			list_len = sys_llistxattr(fname, namebuf, namebuf_len);
+			if (list_len < 0) {
+				rsyserr(FERROR, errno,
+					"rsync_xal_get: llistxattr(\"%s\",%ld) failed",
+					fname, (long)namebuf_len);
+				return -1;
+			}
+		} else {
+			rsyserr(FERROR, errno,
+				"rsync_xal_get: llistxattr(\"%s\",%ld) failed",
+				fname, (long)namebuf_len);
+			return -1;
+		}
+	}
+
+	for (name = namebuf; list_len > 0; name += name_len) {
+		rsync_xa *rxas;
+
+		name_len = strlen(name) + 1;
+		list_len -= name_len;
+
+#ifdef HAVE_LINUX_XATTRS
+		/* We don't send the system namespace. */
+		if (HAS_PREFIX(name, SYSTEM_PREFIX))
+			continue;
+#endif
+
+		datum_len = sys_lgetxattr(fname, name, NULL, 0);
+		if (datum_len < 0) {
+			if (errno == ENOTSUP)
+				return -1;
+			rsyserr(FERROR, errno,
+			    "rsync_xal_get: lgetxattr(\"%s\",\"%s\",0) failed",
+			    fname, name);
+			return -1;
+		}
+		ptr = new_array(char, name_len + datum_len);
+		if (!ptr)
+			out_of_memory("rsync_xal_get");
+		if (datum_len) {
+			ssize_t len = sys_lgetxattr(fname, name, ptr, datum_len);
+			if (len != datum_len) {
+				if (len < 0) {
+					rsyserr(FERROR, errno,
+					    "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
+					    " failed", fname, name, (long)datum_len);
+				} else {
+					rprintf(FERROR,
+					    "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
+					    " returned %ld\n", fname, name,
+					    (long)datum_len, (long)len);
+				}
+				free(ptr);
+				return -1;
+			}
+		}
+		rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
+		rxas->name = ptr + datum_len;
+		rxas->datum = ptr;
+		rxas->name_len = name_len;
+		rxas->datum_len = datum_len;
+		memcpy(rxas->name, name, name_len);
+	}
+	if (xalp->count > 1)
+		qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
+	return 0;
+}
+
+/* Read the xattr(s) for this filename. */
+int get_xattr(const char *fname, statx *sxp)
+{
+	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 int find_matching_xattr(item_list *xalp)
+{
+	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 (lst[i].count != xalp->count)
+			continue;
+		/* any elements different? */
+		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 == xalp->count)
+			return i;
+	}
+
+	return -1;
+}
+
+/* Store *xalp on the end of rsync_xal_l */
+static void rsync_xal_store(item_list *xalp)
+{
+	item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
+	/* Since the following call starts a new list, we know it will hold the
+	 * entire initial-count, not just enough space for one new item. */
+	*new_lst = empty_xattr;
+	(void)EXPAND_ITEM_LIST(new_lst, rsync_xa, xalp->count);
+	memcpy(new_lst->items, xalp->items, xalp->count * sizeof (rsync_xa));
+	new_lst->count = xalp->count;
+	xalp->count = 0;
+}
+
+/* Send the make_xattr()-generated xattr list for this flist entry. */
+void send_xattr(statx *sxp, int f)
+{
+	int ndx = find_matching_xattr(sxp->xattr);
+	if (ndx != -1) {
+		write_byte(f, 'x');
+		write_int(f, ndx);
+		rsync_xal_free(sxp->xattr);
+	} else {
+		rsync_xa *rxa;
+		int count = sxp->xattr->count;
+		write_byte(f, 'X');
+		write_int(f, count);
+		for (rxa = sxp->xattr->items; count--; rxa++) {
+#ifdef HAVE_LINUX_XATTRS
+			write_int(f, rxa->name_len);
+			write_int(f, rxa->datum_len);
+			write_buf(f, rxa->name, rxa->name_len);
+#else
+			/* We strip the rsync prefix from disguised namespaces
+			 * and put everything else in the user namespace. */
+			if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
+			 && rxa->name[RPRE_LEN] != '%') {
+				write_int(f, rxa->name_len - RPRE_LEN);
+				write_int(f, rxa->datum_len);
+				write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
+			} else {
+				write_int(f, rxa->name_len + UPRE_LEN);
+				write_int(f, rxa->datum_len);
+				write_buf(f, USER_PREFIX, UPRE_LEN);
+				write_buf(f, rxa->name, rxa->name_len);
+			}
+#endif
+			write_buf(f, rxa->datum, rxa->datum_len);
+		}
+		rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
+	}
+	free_xattr(sxp);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* receive and build the rsync_xattr_lists */
+void receive_xattr(struct file_struct *file, int f)
+{
+	static item_list temp_xattr = EMPTY_ITEM_LIST;
+	char *ndx_ptr = (char*)file + file_struct_len;
+	int ndx, tag = read_byte(f);
+
+	if (tag == 'X') {
+		int i, count = read_int(f);
+		for (i = 0; i < count; i++) {
+			char *ptr, *name;
+			rsync_xa *rxa;
+			size_t name_len = read_int(f);
+			size_t datum_len = read_int(f);
+#ifdef HAVE_LINUX_XATTRS
+			size_t extra_len = 0;
+#else
+			size_t extra_len = am_root ? RPRE_LEN : 0;
+			if (datum_len + extra_len < datum_len)
+				out_of_memory("receive_xattr"); /* overflow */
+#endif
+			if (name_len + datum_len + extra_len < name_len)
+				out_of_memory("receive_xattr"); /* overflow */
+			ptr = new_array(char, name_len + datum_len + extra_len);
+			if (!ptr)
+				out_of_memory("receive_xattr");
+			name = ptr + datum_len + extra_len;
+			read_buf(f, name, name_len);
+			read_buf(f, ptr, datum_len);
+#ifdef HAVE_LINUX_XATTRS
+			/* Non-root can only save the user namespace. */
+			if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
+				free(ptr);
+				continue;
+			}
+#else
+			/* This OS only has a user namespace, so we either
+			 * strip the user prefix, or we put a non-user
+			 * namespace inside our rsync hierarchy. */
+			if (HAS_PREFIX(name, USER_PREFIX)) {
+				name += UPRE_LEN;
+				name_len -= UPRE_LEN;
+			} else if (am_root) {
+				name -= RPRE_LEN;
+				name_len += RPRE_LEN;
+				memcpy(name, RSYNC_PREFIX, RPRE_LEN);
+			} else {
+				free(ptr);
+				continue;
+			}
+#endif
+			rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
+			rxa->name = name;
+			rxa->datum = ptr;
+			rxa->name_len = name_len;
+			rxa->datum_len = datum_len;
+		}
+		ndx = rsync_xal_l.count; /* pre-incremented count */
+		rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
+	} else if (tag == 'x') {
+		ndx = read_int(f);
+		if (ndx < 0 || (size_t)ndx >= rsync_xal_l.count) {
+			rprintf(FERROR, "receive_xattr: xa index %d out of"
+				" range for %s\n", ndx, f_name(file, NULL));
+			exit_cleanup(RERR_STREAMIO);
+		}
+	} else {
+		rprintf(FERROR, "receive_xattr: unknown extended attribute"
+			" type tag (%c) for %s\n", tag, f_name(file, NULL));
+		exit_cleanup(RERR_STREAMIO);
+	}
+
+	SIVAL(ndx_ptr, 0, ndx);
+}
+
+/* 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)
+{
+	char *ndx_ptr = (char*)file + file_struct_len;
+	int ndx;
+
+	if (!sxp->xattr)
+		return;
+
+	ndx = find_matching_xattr(sxp->xattr);
+	if (ndx == -1)
+		rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
+	free_xattr(sxp);
+
+	SIVAL(ndx_ptr, 0, ndx);
+}
+
+static int rsync_xal_set(const char *fname, item_list *xalp)
+{
+	rsync_xa *rxas = xalp->items;
+	size_t i;
+	int ret = 0;
+
+	for (i = 0; i < xalp->count; i++) {
+		int status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len);
+		if (status < 0) {
+			rsyserr(FERROR, errno,
+				"rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
+				fname, rxas[i].name);
+			ret = -1;
+		}
+	}
+	return ret;
+}
+
+/* Set extended attributes on indicated filename. */
+int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
+{
+	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 (read_only || list_only) {
+		errno = EROFS;
+		return -1;
+	}
+
+	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 */
--- old/proto.h
+++ new/proto.h
@@ -332,4 +332,10 @@ int bitbag_check_bit(struct bitbag *bb, 
 int bitbag_next_bit(struct bitbag *bb, int after);
 void *expand_item_list(item_list *lp, size_t item_size,
 		       const char *desc, int incr);
+void free_xattr(statx *sxp);
+int get_xattr(const char *fname, statx *sxp);
+void send_xattr(statx *sxp, int f);
+void receive_xattr(struct file_struct *file, int f);
+void cache_xattr(struct file_struct *file, statx *sxp);
+int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp));
 int sys_gettimeofday(struct timeval *tv);
--- old/configure
+++ new/configure
@@ -1259,6 +1259,7 @@ Optional Features:
   --disable-ipv6          don't even try to use IPv6
   --disable-locale        turn off locale features
   --enable-acl-support    Include ACL support (default=no)
+  --enable-xattr-support  Include extended attribute support (default=no)
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -15628,6 +15629,559 @@ echo "${ECHO_T}no" >&6; }
 fi
 
 
+
+for ac_header in attr/xattr.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/xattr.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/extattr.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+    ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+	       { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ echo "$as_me:$LINENO: checking whether to support extended attributes" >&5
+echo $ECHO_N "checking whether to support extended attributes... $ECHO_C" >&6; }
+# Check whether --enable-xattr-support was given.
+if test "${enable_xattr_support+set}" = set; then
+  enableval=$enable_xattr_support;  case "$enableval" in
+  yes)
+      case "$host_os" in
+      *linux*)
+            { echo "$as_me:$LINENO: result: Using Linux xattrs" >&5
+echo "${ECHO_T}Using Linux xattrs" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LINUX_XATTRS 1
+_ACEOF
+
+            ;;
+      darwin*)
+            { echo "$as_me:$LINENO: result: Using OS X xattrs" >&5
+echo "${ECHO_T}Using OS X xattrs" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_OSX_XATTRS 1
+_ACEOF
+
+            ;;
+      freebsd*)
+            { echo "$as_me:$LINENO: result: Using FreeBSD extattrs" >&5
+echo "${ECHO_T}Using FreeBSD extattrs" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_FREEBSD_XATTRS 1
+_ACEOF
+
+            ;;
+      *)
+            { echo "$as_me:$LINENO: result: Xattrs requested but not Linux or OS X.  Good luck..." >&5
+echo "${ECHO_T}Xattrs requested but not Linux or OS X.  Good luck..." >&6; }
+            ;;
+      esac
+      ;;
+  *)
+      { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_NO_XATTRS 1
+_ACEOF
+
+  esac
+else
+  { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_NO_XATTRS 1
+_ACEOF
+
+
+fi
+
+
 ac_config_files="$ac_config_files Makefile lib/dummy zlib/dummy popt/dummy shconfig"
 
 cat >confcache <<\_ACEOF
--- old/config.h.in
+++ new/config.h.in
@@ -55,6 +55,9 @@
 /* Define to 1 if you have the `asprintf' function. */
 #undef HAVE_ASPRINTF
 
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+#undef HAVE_ATTR_XATTR_H
+
 /* Define to 1 if readdir() is broken */
 #undef HAVE_BROKEN_READDIR
 
@@ -92,6 +95,9 @@
 /* Define to 1 if you have the <float.h> header file. */
 #undef HAVE_FLOAT_H
 
+/* True if you have FreeBSD xattrs */
+#undef HAVE_FREEBSD_XATTRS
+
 /* Define to 1 if you have the `fstat' function. */
 #undef HAVE_FSTAT
 
@@ -194,6 +200,9 @@
 /* Define to 1 if you have the `link' function. */
 #undef HAVE_LINK
 
+/* True if you have Linux xattrs */
+#undef HAVE_LINUX_XATTRS
+
 /* Define to 1 if you have the `locale_charset' function. */
 #undef HAVE_LOCALE_CHARSET
 
@@ -253,9 +262,15 @@
 /* true if you don't have ACLs */
 #undef HAVE_NO_ACLS
 
+/* True if you don't have extended attributes */
+#undef HAVE_NO_XATTRS
+
 /* Define to 1 if you have the `open64' function. */
 #undef HAVE_OPEN64
 
+/* True if you have Mac OS X xattrs */
+#undef HAVE_OSX_XATTRS
+
 /* true if you have posix ACLs */
 #undef HAVE_POSIX_ACLS
 
@@ -365,6 +380,9 @@
    */
 #undef HAVE_SYS_DIR_H
 
+/* Define to 1 if you have the <sys/extattr.h> header file. */
+#undef HAVE_SYS_EXTATTR_H
+
 /* Define to 1 if you have the <sys/fcntl.h> header file. */
 #undef HAVE_SYS_FCNTL_H
 
@@ -408,6 +426,9 @@
 /* Define to 1 if you have the <sys/wait.h> header file. */
 #undef HAVE_SYS_WAIT_H
 
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#undef HAVE_SYS_XATTR_H
+
 /* Define to 1 if you have the `tcgetpgrp' function. */
 #undef HAVE_TCGETPGRP
 
--- old/rsync.1
+++ new/rsync.1
@@ -367,7 +367,7 @@ to the detailed description below for a 
  \-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)
+ \-a, \-\-archive               archive mode; equals \-rlptgoD (no \-H,\-A,\-X)
      \-\-no\-OPTION             turn off an implied OPTION (e\&.g\&. \-\-no\-D)
  \-r, \-\-recursive             recurse into directories
  \-R, \-\-relative              use relative path names
@@ -390,6 +390,7 @@ to the detailed description below for a 
  \-E, \-\-executability         preserve executability
      \-\-chmod=CHMOD           affect file and/or directory permissions
  \-A, \-\-acls                  preserve ACLs (implies \-p) [non-standard]
+ \-X, \-\-xattrs                preserve extended attrs (implies \-p) [n\&.s\&.]
  \-o, \-\-owner                 preserve owner (super-user only)
  \-g, \-\-group                 preserve group
      \-\-devices               preserve device files (super-user only)
@@ -947,6 +948,12 @@ version makes it incompatible with sendi
 rsync unless you double the \fB\-\-acls\fP option (e\&.g\&. \fB\-AA\fP)\&.  This
 doubling is not needed when pulling files from an older rsync\&.
 .IP 
+.IP "\fB\-X, \-\-xattrs\fP"
+This option causes rsync to update the remote
+extended attributes to be the same as the local ones\&.  This will work
+only if the remote machine\&'s rsync supports this option also\&. This is
+a non-standard option\&.
+.IP 
 .IP "\fB\-\-chmod\fP"
 This option tells rsync to apply one or more
 comma-separated "chmod" strings to the permission of the files in the