statx *sxp, int32 iflags, uchar fnamecmp_type,
const char *xname)
{
-@@ -562,16 +571,24 @@ void itemize(const char *fname, struct f
+@@ -562,16 +571,29 @@ void itemize(const char *fname, struct f
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
if (!ACL_READY(*sxp))
iflags |= ITEM_REPORT_ACL;
}
#endif
+- } else
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ if (!XATTR_READY(*sxp))
+ iflags |= ITEM_REPORT_XATTR;
+ }
+#endif
- } else
++ } else {
++#ifdef SUPPORT_XATTRS
++ if (preserve_xattrs && xattr_diff(file, NULL, 1))
++ iflags |= ITEM_REPORT_XATTR;
++#endif
iflags |= ITEM_IS_NEW;
++ }
iflags &= 0xffff;
- if ((iflags & SIGNIFICANT_ITEM_FLAGS || verbose > 1
|| stdout_format_has_i > 1 || (xname && *xname)) && !read_batch) {
if (protocol_version >= 29) {
if (ndx >= 0)
-@@ -581,6 +598,10 @@ void itemize(const char *fname, struct f
+@@ -581,6 +603,10 @@ void itemize(const char *fname, struct f
write_byte(sock_f_out, fnamecmp_type);
if (iflags & ITEM_XNAME_FOLLOWS)
write_vstring(sock_f_out, xname, strlen(xname));
} else if (ndx >= 0) {
enum logcode code = logfile_format_has_i ? FINFO : FCLIENT;
log_item(code, file, &stats, iflags, xname);
-@@ -855,14 +876,14 @@ static int try_dests_reg(struct file_str
+@@ -855,14 +881,14 @@ static int try_dests_reg(struct file_str
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
if (verbose > 1 && maybe_ATTRS_REPORT)
rprintf(FCLIENT, "%s is uptodate\n", fname);
return -2;
-@@ -879,9 +900,13 @@ static int try_dests_reg(struct file_str
+@@ -879,9 +905,13 @@ static int try_dests_reg(struct file_str
}
return -1;
}
if (maybe_ATTRS_REPORT
&& ((!itemizing && verbose && match_level == 2)
|| (verbose > 1 && match_level == 3))) {
-@@ -1029,7 +1054,7 @@ static int try_dests_non(struct file_str
+@@ -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;
}
if (verbose > 1 && maybe_ATTRS_REPORT) {
rprintf(FCLIENT, "%s%s is uptodate\n",
-@@ -1112,6 +1137,9 @@ static void recv_generator(char *fname,
+@@ -1112,6 +1142,9 @@ static void recv_generator(char *fname,
#ifdef SUPPORT_ACLS
sx.acc_acl = sx.def_acl = NULL;
#endif
if (dry_run > 1) {
if (fuzzy_dirlist) {
flist_free(fuzzy_dirlist);
-@@ -1224,7 +1252,7 @@ static void recv_generator(char *fname,
+@@ -1224,7 +1257,7 @@ static void recv_generator(char *fname,
goto cleanup;
}
}
&& verbose && code != FNONE && f_out != -1)
rprintf(code, "%s/\n", fname);
if (real_ret != 0 && one_file_system)
-@@ -1278,9 +1306,9 @@ static void recv_generator(char *fname,
+@@ -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. */
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
-@@ -1317,7 +1345,7 @@ static void recv_generator(char *fname,
+@@ -1317,7 +1350,7 @@ static void recv_generator(char *fname,
rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
full_fname(fname), sl);
} else {
if (itemizing) {
itemize(fname, file, ndx, statret, &sx,
ITEM_LOCAL_CHANGE, 0, NULL);
-@@ -1357,9 +1385,9 @@ static void recv_generator(char *fname,
+@@ -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. */
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
-@@ -1399,7 +1427,7 @@ static void recv_generator(char *fname,
+@@ -1399,7 +1432,7 @@ static void recv_generator(char *fname,
rsyserr(FERROR, errno, "mknod %s failed",
full_fname(fname));
} else {
if (itemizing) {
itemize(fname, file, ndx, statret, &sx,
ITEM_LOCAL_CHANGE, 0, NULL);
-@@ -1529,9 +1557,9 @@ static void recv_generator(char *fname,
+@@ -1529,9 +1562,9 @@ static void recv_generator(char *fname,
do_unlink(partialptr);
handle_partial_dir(partialptr, PDIR_DELETE);
}
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
-@@ -1636,6 +1664,10 @@ static void recv_generator(char *fname,
+@@ -1636,6 +1669,10 @@ static void recv_generator(char *fname,
if (preserve_acls)
free_acl(&real_sx);
#endif
}
if (!do_xfers) {
-@@ -1657,7 +1689,7 @@ static void recv_generator(char *fname,
+@@ -1657,7 +1694,7 @@ static void recv_generator(char *fname,
if (f_copy >= 0) {
close(f_copy);
if (verbose > 1) {
rprintf(FINFO, "backed up %s to %s\n",
fname, backupptr);
-@@ -1672,6 +1704,10 @@ static void recv_generator(char *fname,
+@@ -1672,6 +1709,10 @@ static void recv_generator(char *fname,
if (preserve_acls)
free_acl(&sx);
#endif
if (verbose > 2)
--- old/testsuite/xattrs.test
+++ new/testsuite/xattrs.test
-@@ -0,0 +1,76 @@
+@@ -0,0 +1,90 @@
+#! /bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+*protocol=29*) test_skipped "xattr support requires protocol 30" ;;
+esac
+
++xset() {
++ xnam="$1"
++ xval="$2"
++ shift 2
++ setfattr -n "$xnam" -v "$xval" "${@}"
++ #xattr -s "$xnam" "$xval" "${@}"
++}
++
++xls() {
++ getfattr -d "${@}"
++ #xattr -l "${@}"
++}
++
+makepath "$fromdir/foo"
+echo something >"$fromdir/file1"
+echo else >"$fromdir/file2"
+
+cd "$fromdir"
+
-+setfattr -n user.short -v 'this is short' file1 2>/dev/null || test_skipped "Unable to set an xattr"
-+setfattr -n user.long -v 'this is a long attribute that will be truncated in the initial data send' file1
-+setfattr -n user.good -v 'this is good' file1
-+setfattr -n user.nice -v 'this is nice' file1
++xset user.short 'this is short' file1 2>/dev/null || test_skipped "Unable to set an xattr"
++xset user.long 'this is a long attribute that will be truncated in the initial data send' file1
++xset user.good 'this is good' file1
++xset user.nice 'this is nice' file1
+
-+setfattr -n user.foo -v foo file2
-+setfattr -n user.bar -v bar file2
++xset user.foo foo file2
++xset user.bar bar file2
++xset user.long 'a long attribute for our new file that tests to ensure that this works' file2
+
-+setfattr -n user.foo -v 'new foo' foo/file3
-+setfattr -n user.bar -v 'new bar' foo/file3
-+setfattr -n user.long -v 'this is also a long attribute that will be truncated in the initial data send' foo/file3
-+setfattr -n user.equal -v 'this long attribute should remain the same and not need to be transferred' foo/file3
++xset user.foo 'new foo' foo/file3
++xset user.bar 'new bar' foo/file3
++xset user.long 'this is also a long attribute that will be truncated in the initial data send' foo/file3
++xset user.equal 'this long attribute should remain the same and not need to be transferred' foo/file3
+
-+setfattr -n user.short -v 'old short' "$chkdir/file1"
-+setfattr -n user.extra -v 'remove me' "$chkdir/file1"
++xset user.short 'old short' "$chkdir/file1"
++xset user.extra 'remove me' "$chkdir/file1"
+
-+setfattr -n user.foo -v 'old foo' "$chkdir/foo/file3"
-+setfattr -n user.equal -v 'this long attribute should remain the same and not need to be transferred' "$chkdir/foo/file3"
++xset user.foo 'old foo' "$chkdir/foo/file3"
++xset user.equal 'this long attribute should remain the same and not need to be transferred' "$chkdir/foo/file3"
+
-+getfattr -d $files >"$scratchdir/xattrs.txt"
++xls $files >"$scratchdir/xattrs.txt"
+
+# OK, let's try a simple xattr copy.
+checkit "$RSYNC -avX . \"$chkdir/\"" "$fromdir" "$chkdir"
+
+cd "$chkdir"
-+getfattr -d $files | diff $diffopt "$scratchdir/xattrs.txt" -
++xls $files | diff $diffopt "$scratchdir/xattrs.txt" -
+
+cd "$fromdir"
+
+checkit "$RSYNC -aiX --copy-dest=../chk . ../to" "$fromdir" "$todir"
+
+cd "$todir"
-+getfattr -d $files | diff $diffopt "$scratchdir/xattrs.txt" -
++xls $files | diff $diffopt "$scratchdir/xattrs.txt" -
+
+cd "$fromdir"
-+setfattr -n user.nice -v 'this is nice, but different' file1
++xset user.nice 'this is nice, but different' file1
+
-+getfattr -d $files >"$scratchdir/xattrs.txt"
++xls $files >"$scratchdir/xattrs.txt"
+
+rm -rf "$todir"
+
+checkit "$RSYNC -aiX --link-dest=../chk . ../to" "$chkdir" "$todir"
+
+cd "$todir"
-+getfattr -d $files | diff $diffopt "$scratchdir/xattrs.txt" -
++xls $files | diff $diffopt "$scratchdir/xattrs.txt" -
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
--- old/xattrs.c
+++ new/xattrs.c
-@@ -0,0 +1,775 @@
+@@ -0,0 +1,771 @@
+/*
+ * Extended Attribute support for rsync.
+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
+ 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,
-+ "get_xattr_names: llistxattr(\"%s\",0) failed",
-+ fname);
-+ return -1;
-+ }
-+ if (namebuf_len)
-+ free(namebuf);
-+ namebuf_len = list_len + 1024;
-+ namebuf = new_array(char, namebuf_len);
-+ if (!namebuf)
-+ out_of_memory("get_xattr_names");
-+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
-+ if (list_len < 0) {
-+ rsyserr(FERROR, errno,
-+ "get_xattr_names: llistxattr(\"%s\",%ld) failed",
-+ fname, (long)namebuf_len);
-+ return -1;
-+ }
-+ } else {
++ if (list_len >= 0)
++ return list_len;
++ if (errno == ENOTSUP)
++ return 0;
++ if (errno == ERANGE) {
++ list_len = sys_llistxattr(fname, NULL, 0);
++ if (list_len < 0) {
+ rsyserr(FERROR, errno,
-+ "get_xattr_names: llistxattr(\"%s\",%ld) failed",
-+ fname, (long)namebuf_len);
++ "get_xattr_names: llistxattr(\"%s\",0) failed",
++ fname);
+ return -1;
+ }
++ if (namebuf_len)
++ free(namebuf);
++ namebuf_len = list_len + 1024;
++ namebuf = new_array(char, namebuf_len);
++ if (!namebuf)
++ out_of_memory("get_xattr_names");
++ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
++ if (list_len >= 0)
++ return list_len;
+ }
+
-+ return list_len;
++ rsyserr(FERROR, errno,
++ "get_xattr_names: llistxattr(\"%s\",%ld) failed",
++ fname, (long)namebuf_len);
++ return -1;
+}
+
+/* On entry, the *len_ptr parameter contains the size of the extra space we
+ ssize_t list_len, name_len;
+ size_t datum_len, name_offset;
+ char *name, *ptr;
++#ifdef HAVE_LINUX_XATTRS
+ int user_only = am_sender ? 0 : !am_root;
++#endif
+
+ /* This puts the name list into the "namebuf" buffer. */
+ if ((list_len = get_xattr_names(fname)) < 0)
+ int snd_cnt, rec_cnt;
+ int cmp, same, xattrs_equal = 1;
+
-+ if (!XATTR_READY(*sxp)) {
-+ rec_rxa = NULL;
-+ rec_cnt = 0;
-+ } else {
++ if (sxp && XATTR_READY(*sxp)) {
+ rec_rxa = sxp->xattr->items;
+ rec_cnt = sxp->xattr->count;
++ } else {
++ rec_rxa = NULL;
++ rec_cnt = 0;
+ }
+
+ if (F_XATTR(file) >= 0)