- return -1;
- }
-+ set_file_attrs(fname, file, NULL, cmpbuf, 0);
- if (itemizing)
-- itemize(fname, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
-- set_file_attrs(fname, file, NULL, 0);
-+ itemize(cmpbuf, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
-+#ifdef SUPPORT_XATTRS
-+ if (preserve_xattrs)
-+ xattr_clear_locals(file);
-+#endif
- if (maybe_ATTRS_REPORT
- && ((!itemizing && verbose && match_level == 2)
- || (verbose > 1 && match_level == 3))) {
-@@ -1029,7 +1059,7 @@ static int try_dests_non(struct file_str
- : ITEM_LOCAL_CHANGE
- + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
- char *lp = match_level == 3 ? "" : NULL;
-- itemize(fname, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
-+ itemize(cmpbuf, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
- }
- if (verbose > 1 && maybe_ATTRS_REPORT) {
- rprintf(FCLIENT, "%s%s is uptodate\n",
-@@ -1112,6 +1142,9 @@ static void recv_generator(char *fname,
- #ifdef SUPPORT_ACLS
- sx.acc_acl = sx.def_acl = NULL;
- #endif
-+#ifdef SUPPORT_XATTRS
-+ sx.xattr = NULL;
-+#endif
- if (dry_run > 1) {
- if (fuzzy_dirlist) {
- flist_free(fuzzy_dirlist);
-@@ -1224,7 +1257,7 @@ static void recv_generator(char *fname,
- goto cleanup;
- }
- }
-- if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, 0)
-+ if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
- && verbose && code != FNONE && f_out != -1)
- rprintf(code, "%s/\n", fname);
- if (real_ret != 0 && one_file_system)
-@@ -1278,9 +1311,9 @@ static void recv_generator(char *fname,
- else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0
- && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
- /* The link is pointing to the right place. */
-+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
- if (itemizing)
- itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
-- set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
- #ifdef SUPPORT_HARD_LINKS
- if (preserve_hard_links && F_IS_HLINKED(file))
- finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
-@@ -1317,7 +1350,7 @@ static void recv_generator(char *fname,
- rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
- full_fname(fname), sl);
- } else {
-- set_file_attrs(fname, file, NULL, 0);
-+ set_file_attrs(fname, file, NULL, NULL, 0);
- if (itemizing) {
- itemize(fname, file, ndx, statret, &sx,
- ITEM_LOCAL_CHANGE, 0, NULL);
-@@ -1357,9 +1390,9 @@ static void recv_generator(char *fname,
- && BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
- && sx.st.st_rdev == rdev) {
- /* The device or special file is identical. */
-+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
- if (itemizing)
- itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
-- set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
- #ifdef SUPPORT_HARD_LINKS
- if (preserve_hard_links && F_IS_HLINKED(file))
- finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
-@@ -1399,7 +1432,7 @@ static void recv_generator(char *fname,
- rsyserr(FERROR, errno, "mknod %s failed",
- full_fname(fname));
- } else {
-- set_file_attrs(fname, file, NULL, 0);
-+ set_file_attrs(fname, file, NULL, NULL, 0);
- if (itemizing) {
- itemize(fname, file, ndx, statret, &sx,
- ITEM_LOCAL_CHANGE, 0, NULL);
-@@ -1529,9 +1562,9 @@ static void recv_generator(char *fname,
- do_unlink(partialptr);
- handle_partial_dir(partialptr, PDIR_DELETE);
- }
-+ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
- if (itemizing)
- itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
-- set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
- #ifdef SUPPORT_HARD_LINKS
- if (preserve_hard_links && F_IS_HLINKED(file))
- finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
-@@ -1636,6 +1669,10 @@ static void recv_generator(char *fname,
- if (preserve_acls)
- free_acl(&real_sx);
- #endif
-+#ifdef SUPPORT_XATTRS
-+ if (preserve_xattrs)
-+ free_xattr(&real_sx);
-+#endif
- }
-
- if (!do_xfers) {
-@@ -1657,7 +1694,7 @@ static void recv_generator(char *fname,
-
- if (f_copy >= 0) {
- close(f_copy);
-- set_file_attrs(backupptr, back_file, NULL, 0);
-+ set_file_attrs(backupptr, back_file, NULL, NULL, 0);
- if (verbose > 1) {
- rprintf(FINFO, "backed up %s to %s\n",
- fname, backupptr);
-@@ -1672,6 +1709,10 @@ static void recv_generator(char *fname,
- if (preserve_acls)
- free_acl(&sx);
- #endif
-+#ifdef SUPPORT_XATTRS
-+ if (preserve_xattrs)
-+ free_xattr(&sx);
-+#endif
- return;
- }
-
---- old/lib/sysxattrs.c
-+++ new/lib/sysxattrs.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 "sysxattrs.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/sysxattrs.h
-+++ new/lib/sysxattrs.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/main.c
-+++ new/main.c
-@@ -608,7 +608,7 @@ static void fix_basis_dirs(void)
- }
-
- /* This is only called by the sender. */
--static void read_final_goodbye(int f_in, int f_out)
-+static void read_final_goodbye(int f_in)
- {
- int i, iflags, xlen;
- uchar fnamecmp_type;
-@@ -617,8 +617,8 @@ static void read_final_goodbye(int f_in,
- if (protocol_version < 29)
- i = read_int(f_in);
- else {
-- i = read_ndx_and_attrs(f_in, f_out, &iflags,
-- &fnamecmp_type, xname, &xlen);
-+ i = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
-+ xname, &xlen);
- }
-
- if (i != NDX_DONE) {
-@@ -677,7 +677,7 @@ static void do_server_sender(int f_in, i
- io_flush(FULL_FLUSH);
- handle_stats(f_out);
- if (protocol_version >= 24)
-- read_final_goodbye(f_in, f_out);
-+ read_final_goodbye(f_in);
- io_flush(FULL_FLUSH);
- exit_cleanup(0);
- }
-@@ -740,7 +740,7 @@ static int do_recv(int f_in, int f_out,
- kluge_around_eof = -1;
-
- /* This should only get stopped via a USR2 signal. */
-- read_ndx_and_attrs(f_in, -1, &iflags, &fnamecmp_type,
-+ read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
- xname, &xlen);
-
- rprintf(FERROR, "Invalid packet at end of run [%s]\n",
-@@ -977,7 +977,7 @@ int client_run(int f_in, int f_out, pid_
- io_flush(FULL_FLUSH);
- handle_stats(-1);
- if (protocol_version >= 24)
-- read_final_goodbye(f_in, f_out);
-+ read_final_goodbye(f_in);
- if (pid != -1) {
- if (verbose > 3)
- rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
---- old/options.c
-+++ new/options.c
-@@ -47,6 +47,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
-@@ -239,8 +243,8 @@ static void print_rsync_version(enum log
- (int)(sizeof (int64) * 8));
- rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
- got_socketpair, hardlinks, links, ipv6, have_inplace);
-- rprintf(f, " %sappend, %sACLs\n",
-- have_inplace, acls);
-+ rprintf(f, " %sappend, %sACLs, %sxattrs\n",
-+ have_inplace, acls, xattrs);
-
- #ifdef MAINTAINER_MODE
- rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
-@@ -286,7 +290,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");
-@@ -311,6 +315,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");
-@@ -438,6 +445,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 },
-@@ -1126,6 +1136,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()
-@@ -1590,6 +1611,10 @@ void server_options(char **args,int *arg
- if (preserve_acls)
- argstr[x++] = 'A';