In get_xattr_data(): replace the input value of *len_ptr with its
[rsync/rsync-patches.git] / xattrs.diff
... / ...
CommitLineData
1This patch adds support for extended attributes.
2
3To use this patch, run these commands for a successful build:
4
5 patch -p1 <patches/xattrs.diff
6 ./prepare-source
7 ./configure
8 make
9
10CAUTION: this patch has been recently reworked, and needs more testing!
11
12TODO:
13
14 - When an xattr value is long enough to be checksummed and is not changed,
15 we need to have the receiver get the unchanged value from the receiving
16 side instead of requesting that it be sent from the sender.
17
18--- old/Makefile.in
19+++ new/Makefile.in
20@@ -28,13 +28,13 @@ VERSION=@VERSION@
21
22 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
23 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
24- lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
25+ lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
26 ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
27 zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
28 OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
29 util.o main.o checksum.o match.o syscall.o log.o backup.o
30 OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o \
31- fileio.o batch.o clientname.o chmod.o acls.o
32+ fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
33 OBJS3=progress.o pipe.o
34 DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
35 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
36--- old/backup.c
37+++ new/backup.c
38@@ -23,6 +23,7 @@
39 extern int verbose;
40 extern int am_root;
41 extern int preserve_acls;
42+extern int preserve_xattrs;
43 extern int preserve_devices;
44 extern int preserve_specials;
45 extern int preserve_links;
46@@ -134,6 +135,9 @@ static int make_bak_dir(char *fullpath)
47 #ifdef SUPPORT_ACLS
48 sx.acc_acl = sx.def_acl = NULL;
49 #endif
50+#ifdef SUPPORT_XATTRS
51+ sx.xattr = NULL;
52+#endif
53 if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
54 continue;
55 #ifdef SUPPORT_ACLS
56@@ -143,6 +147,13 @@ static int make_bak_dir(char *fullpath)
57 free_acl(&sx);
58 }
59 #endif
60+#ifdef SUPPORT_XATTRS
61+ if (preserve_xattrs) {
62+ get_xattr(rel, &sx);
63+ cache_xattr(file, &sx);
64+ free_xattr(&sx);
65+ }
66+#endif
67 set_file_attrs(fullpath, file, NULL, 0);
68 free(file);
69 }
70@@ -194,6 +205,9 @@ static int keep_backup(const char *fname
71 #ifdef SUPPORT_ACLS
72 sx.acc_acl = sx.def_acl = NULL;
73 #endif
74+#ifdef SUPPORT_XATTRS
75+ sx.xattr = NULL;
76+#endif
77
78 if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
79 return 1; /* the file could have disappeared */
80@@ -210,6 +224,13 @@ static int keep_backup(const char *fname
81 free_acl(&sx);
82 }
83 #endif
84+#ifdef SUPPORT_XATTRS
85+ if (preserve_xattrs) {
86+ get_xattr(fname, &sx);
87+ cache_xattr(file, &sx);
88+ free_xattr(&sx);
89+ }
90+#endif
91
92 /* Check to see if this is a device file, or link */
93 if ((am_root && preserve_devices && IS_DEVICE(file->mode))
94--- old/compat.c
95+++ new/compat.c
96@@ -43,6 +43,7 @@ extern int protocol_version;
97 extern int preserve_uid;
98 extern int preserve_gid;
99 extern int preserve_acls;
100+extern int preserve_xattrs;
101 extern int preserve_hard_links;
102 extern int need_messages_from_generator;
103 extern int delete_mode, delete_before, delete_during, delete_after;
104@@ -65,6 +66,8 @@ void setup_protocol(int f_out,int f_in)
105 preserve_gid = ++file_extra_cnt;
106 if (preserve_acls && !am_sender)
107 preserve_acls = ++file_extra_cnt;
108+ if (preserve_xattrs)
109+ preserve_xattrs = ++file_extra_cnt;
110
111 if (remote_protocol == 0) {
112 if (!read_batch)
113@@ -119,6 +122,13 @@ void setup_protocol(int f_out,int f_in)
114 protocol_version);
115 exit_cleanup(RERR_PROTOCOL);
116 }
117+ if (preserve_xattrs) {
118+ rprintf(FERROR,
119+ "--xattrs requires protocol 30 or higher"
120+ " (negotiated %d).\n",
121+ protocol_version);
122+ exit_cleanup(RERR_PROTOCOL);
123+ }
124 }
125
126 if (delete_mode && !(delete_before+delete_during+delete_after)) {
127--- old/configure.in
128+++ new/configure.in
129@@ -891,6 +891,46 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
130 esac
131 fi
132
133+AC_CHECK_HEADERS(attr/xattr.h)
134+AC_CHECK_HEADERS(sys/xattr.h)
135+AC_CHECK_HEADERS(sys/extattr.h)
136+
137+#################################################
138+# check for extended attribute support
139+AC_MSG_CHECKING(whether to support extended attributes)
140+AC_ARG_ENABLE(xattr-support,
141+ AC_HELP_STRING([--disable-xattr-support],
142+ [Turn off extended attribute support]))
143+
144+if test x"$enable_xattr_support" = x"no"; then
145+ AC_MSG_RESULT(no)
146+else
147+ case "$host_os" in
148+ *linux*)
149+ AC_MSG_RESULT(Using Linux xattrs)
150+ AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs])
151+ AC_DEFINE(SUPPORT_XATTRS, 1, [Define to 1 to add support for extended attributes])
152+ ;;
153+ darwin*)
154+ AC_MSG_RESULT(Using OS X xattrs)
155+ AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs])
156+ AC_DEFINE(SUPPORT_XATTRS, 1)
157+ ;;
158+ freebsd*)
159+ AC_MSG_RESULT(Using FreeBSD extattrs)
160+ AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
161+ AC_DEFINE(SUPPORT_XATTRS, 1)
162+ ;;
163+ *)
164+ if test x"$enable_xattr_support" = x"yes"; then
165+ AC_MSG_ERROR(Failed to find extended attribute support)
166+ else
167+ AC_MSG_RESULT(No extended attribute support found)
168+ fi
169+ ;;
170+ esac
171+fi
172+
173 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
174 AC_OUTPUT
175
176--- old/flist.c
177+++ new/flist.c
178@@ -43,6 +43,7 @@ extern int one_file_system;
179 extern int copy_dirlinks;
180 extern int keep_dirlinks;
181 extern int preserve_acls;
182+extern int preserve_xattrs;
183 extern int preserve_links;
184 extern int preserve_hard_links;
185 extern int preserve_devices;
186@@ -888,6 +889,10 @@ static struct file_struct *recv_file_ent
187 if (preserve_acls && !S_ISLNK(mode))
188 receive_acl(file, f);
189 #endif
190+#ifdef SUPPORT_XATTRS
191+ if (preserve_xattrs)
192+ receive_xattr(file, f );
193+#endif
194
195 if (S_ISREG(mode) || S_ISLNK(mode))
196 stats.total_size += file_length;
197@@ -1160,7 +1165,7 @@ static struct file_struct *send_file_nam
198 int flags, int filter_flags)
199 {
200 struct file_struct *file;
201-#ifdef SUPPORT_ACLS
202+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
203 statx sx;
204 #endif
205
206@@ -1179,6 +1184,13 @@ static struct file_struct *send_file_nam
207 return NULL;
208 }
209 #endif
210+#ifdef SUPPORT_XATTRS
211+ if (preserve_xattrs && f >= 0) {
212+ sx.xattr = NULL;
213+ if (get_xattr(fname, &sx) < 0)
214+ return NULL;
215+ }
216+#endif
217
218 maybe_emit_filelist_progress(flist->count + flist_count_offset);
219
220@@ -1192,6 +1204,12 @@ static struct file_struct *send_file_nam
221 free_acl(&sx);
222 }
223 #endif
224+#ifdef SUPPORT_XATTRS
225+ if (preserve_xattrs) {
226+ F_XATTR(file) = send_xattr(&sx, f);
227+ free_xattr(&sx);
228+ }
229+#endif
230 }
231 return file;
232 }
233--- old/generator.c
234+++ new/generator.c
235@@ -36,6 +36,7 @@ extern int relative_paths;
236 extern int implied_dirs;
237 extern int keep_dirlinks;
238 extern int preserve_acls;
239+extern int preserve_xattrs;
240 extern int preserve_links;
241 extern int preserve_devices;
242 extern int preserve_specials;
243@@ -532,6 +533,14 @@ int unchanged_attrs(const char *fname, s
244 return 0;
245 }
246 #endif
247+#ifdef SUPPORT_XATTRS
248+ if (preserve_xattrs) {
249+ if (!XATTR_READY(*sxp))
250+ get_xattr(fname, sxp);
251+ if (xattr_diff(file, sxp, 0))
252+ return 0;
253+ }
254+#endif
255
256 return 1;
257 }
258@@ -567,11 +576,19 @@ void itemize(const char *fname, struct f
259 iflags |= ITEM_REPORT_ACL;
260 }
261 #endif
262+#ifdef SUPPORT_XATTRS
263+ if (preserve_xattrs) {
264+ if (!XATTR_READY(*sxp))
265+ get_xattr(fname, sxp);
266+ if (xattr_diff(file, sxp, 1))
267+ iflags |= ITEM_REPORT_XATTR;
268+ }
269+#endif
270 } else
271 iflags |= ITEM_IS_NEW;
272
273 iflags &= 0xffff;
274- if ((iflags & SIGNIFICANT_ITEM_FLAGS || verbose > 1
275+ if ((iflags & (SIGNIFICANT_ITEM_FLAGS|ITEM_REPORT_XATTR) || verbose > 1
276 || stdout_format_has_i > 1 || (xname && *xname)) && !read_batch) {
277 if (protocol_version >= 29) {
278 if (ndx >= 0)
279@@ -581,6 +598,10 @@ void itemize(const char *fname, struct f
280 write_byte(sock_f_out, fnamecmp_type);
281 if (iflags & ITEM_XNAME_FOLLOWS)
282 write_vstring(sock_f_out, xname, strlen(xname));
283+#ifdef SUPPORT_XATTRS
284+ if (iflags & ITEM_REPORT_XATTR && !dry_run)
285+ send_xattr_request(NULL, file, sock_f_out);
286+#endif
287 } else if (ndx >= 0) {
288 enum logcode code = logfile_format_has_i ? FINFO : FCLIENT;
289 log_item(code, file, &stats, iflags, xname);
290@@ -1112,6 +1133,9 @@ static void recv_generator(char *fname,
291 #ifdef SUPPORT_ACLS
292 sx.acc_acl = sx.def_acl = NULL;
293 #endif
294+#ifdef SUPPORT_XATTRS
295+ sx.xattr = NULL;
296+#endif
297 if (dry_run > 1) {
298 if (fuzzy_dirlist) {
299 flist_free(fuzzy_dirlist);
300@@ -1636,6 +1660,10 @@ static void recv_generator(char *fname,
301 if (preserve_acls)
302 free_acl(&real_sx);
303 #endif
304+#ifdef SUPPORT_XATTRS
305+ if (preserve_xattrs)
306+ free_xattr(&real_sx);
307+#endif
308 }
309
310 if (!do_xfers) {
311@@ -1672,6 +1700,10 @@ static void recv_generator(char *fname,
312 if (preserve_acls)
313 free_acl(&sx);
314 #endif
315+#ifdef SUPPORT_XATTRS
316+ if (preserve_xattrs)
317+ free_xattr(&sx);
318+#endif
319 return;
320 }
321
322--- old/lib/sysxattrs.c
323+++ new/lib/sysxattrs.c
324@@ -0,0 +1,135 @@
325+/*
326+ * Extended attribute support for rsync.
327+ *
328+ * Copyright (C) 2004 Red Hat, Inc.
329+ * Written by Jay Fenlason.
330+ *
331+ * This program is free software; you can redistribute it and/or modify
332+ * it under the terms of the GNU General Public License as published by
333+ * the Free Software Foundation; either version 2 of the License, or
334+ * (at your option) any later version.
335+ *
336+ * This program is distributed in the hope that it will be useful,
337+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
338+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
339+ * GNU General Public License for more details.
340+ *
341+ * You should have received a copy of the GNU General Public License along
342+ * with this program; if not, write to the Free Software Foundation, Inc.,
343+ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
344+ */
345+
346+#include "rsync.h"
347+#include "sysxattrs.h"
348+
349+#ifdef SUPPORT_XATTRS
350+
351+#if defined HAVE_LINUX_XATTRS
352+
353+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
354+{
355+ return lgetxattr(path, name, value, size);
356+}
357+
358+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
359+{
360+ return fgetxattr(filedes, name, value, size);
361+}
362+
363+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
364+{
365+ return lsetxattr(path, name, value, size, 0);
366+}
367+
368+int sys_lremovexattr(const char *path, const char *name)
369+{
370+ return lremovexattr(path, name);
371+}
372+
373+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
374+{
375+ return llistxattr(path, list, size);
376+}
377+
378+#elif HAVE_OSX_XATTRS
379+
380+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
381+{
382+ return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
383+}
384+
385+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
386+{
387+ return fgetxattr(filedes, name, value, size, 0, 0);
388+}
389+
390+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
391+{
392+ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
393+}
394+
395+int sys_lremovexattr(const char *path, const char *name)
396+{
397+ return removexattr(path, name, XATTR_NOFOLLOW);
398+}
399+
400+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
401+{
402+ return listxattr(path, list, size, XATTR_NOFOLLOW);
403+}
404+
405+#elif HAVE_FREEBSD_XATTRS
406+
407+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
408+{
409+ return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
410+}
411+
412+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
413+{
414+ return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
415+}
416+
417+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
418+{
419+ return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
420+}
421+
422+int sys_lremovexattr(const char *path, const char *name)
423+{
424+ return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
425+}
426+
427+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
428+{
429+ unsigned char keylen;
430+ ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
431+
432+ if (len <= 0 || (size_t)len > size)
433+ return len;
434+
435+ /* FreeBSD puts a single-byte length before each string, with no '\0'
436+ * terminator. We need to change this into a series of null-terminted
437+ * strings. Since the size is the same, we can simply transform the
438+ * output in place. */
439+ for (off = 0; off < len; off += keylen + 1) {
440+ keylen = ((unsigned char*)list)[off];
441+ if (off + keylen >= len) {
442+ /* Should be impossible, but kernel bugs happen! */
443+ errno = EINVAL;
444+ return -1;
445+ }
446+ memmove(list+off, list+off+1, keylen);
447+ list[off+keylen] = '\0';
448+ }
449+
450+ return len;
451+}
452+
453+#else
454+
455+#error You need to create xattr compatibility functions.
456+
457+#endif
458+
459+#endif /* SUPPORT_XATTRS */
460--- old/lib/sysxattrs.h
461+++ new/lib/sysxattrs.h
462@@ -0,0 +1,26 @@
463+#ifdef SUPPORT_XATTRS
464+
465+#if defined HAVE_ATTR_XATTR_H
466+#include <attr/xattr.h>
467+#elif defined HAVE_SYS_XATTR_H
468+#include <sys/xattr.h>
469+#elif defined HAVE_SYS_EXTATTR_H
470+#include <sys/extattr.h>
471+#endif
472+
473+/* Linux 2.4 does not define this as a distinct errno value: */
474+#ifndef ENOATTR
475+#define ENOATTR ENODATA
476+#endif
477+
478+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
479+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
480+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
481+int sys_lremovexattr(const char *path, const char *name);
482+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
483+
484+#else
485+
486+/* No xattrs available */
487+
488+#endif
489--- old/main.c
490+++ new/main.c
491@@ -608,7 +608,7 @@ static void fix_basis_dirs(void)
492 }
493
494 /* This is only called by the sender. */
495-static void read_final_goodbye(int f_in, int f_out)
496+static void read_final_goodbye(int f_in)
497 {
498 int i, iflags, xlen;
499 uchar fnamecmp_type;
500@@ -617,8 +617,8 @@ static void read_final_goodbye(int f_in,
501 if (protocol_version < 29)
502 i = read_int(f_in);
503 else {
504- i = read_ndx_and_attrs(f_in, f_out, &iflags,
505- &fnamecmp_type, xname, &xlen);
506+ i = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
507+ xname, &xlen);
508 }
509
510 if (i != NDX_DONE) {
511@@ -677,7 +677,7 @@ static void do_server_sender(int f_in, i
512 io_flush(FULL_FLUSH);
513 handle_stats(f_out);
514 if (protocol_version >= 24)
515- read_final_goodbye(f_in, f_out);
516+ read_final_goodbye(f_in);
517 io_flush(FULL_FLUSH);
518 exit_cleanup(0);
519 }
520@@ -740,7 +740,7 @@ static int do_recv(int f_in, int f_out,
521 kluge_around_eof = -1;
522
523 /* This should only get stopped via a USR2 signal. */
524- read_ndx_and_attrs(f_in, -1, &iflags, &fnamecmp_type,
525+ read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
526 xname, &xlen);
527
528 rprintf(FERROR, "Invalid packet at end of run [%s]\n",
529@@ -977,7 +977,7 @@ int client_run(int f_in, int f_out, pid_
530 io_flush(FULL_FLUSH);
531 handle_stats(-1);
532 if (protocol_version >= 24)
533- read_final_goodbye(f_in, f_out);
534+ read_final_goodbye(f_in);
535 if (pid != -1) {
536 if (verbose > 3)
537 rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
538--- old/options.c
539+++ new/options.c
540@@ -47,6 +47,7 @@ int copy_links = 0;
541 int preserve_links = 0;
542 int preserve_hard_links = 0;
543 int preserve_acls = 0;
544+int preserve_xattrs = 0;
545 int preserve_perms = 0;
546 int preserve_executability = 0;
547 int preserve_devices = 0;
548@@ -201,6 +202,7 @@ static void print_rsync_version(enum log
549 char const *have_inplace = "no ";
550 char const *hardlinks = "no ";
551 char const *acls = "no ";
552+ char const *xattrs = "no ";
553 char const *links = "no ";
554 char const *ipv6 = "no ";
555 STRUCT_STAT *dumstat;
556@@ -220,7 +222,9 @@ static void print_rsync_version(enum log
557 #ifdef SUPPORT_ACLS
558 acls = "";
559 #endif
560-
561+#ifdef SUPPORT_XATTRS
562+ xattrs = "";
563+#endif
564 #ifdef SUPPORT_LINKS
565 links = "";
566 #endif
567@@ -239,8 +243,8 @@ static void print_rsync_version(enum log
568 (int)(sizeof (int64) * 8));
569 rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
570 got_socketpair, hardlinks, links, ipv6, have_inplace);
571- rprintf(f, " %sappend, %sACLs\n",
572- have_inplace, acls);
573+ rprintf(f, " %sappend, %sACLs, %sxattrs\n",
574+ have_inplace, acls, xattrs);
575
576 #ifdef MAINTAINER_MODE
577 rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
578@@ -286,7 +290,7 @@ void usage(enum logcode F)
579 rprintf(F," -q, --quiet suppress non-error messages\n");
580 rprintf(F," --no-motd suppress daemon-mode MOTD (see manpage caveat)\n");
581 rprintf(F," -c, --checksum skip based on checksum, not mod-time & size\n");
582- rprintf(F," -a, --archive archive mode; same as -rlptgoD (no -H, -A)\n");
583+ rprintf(F," -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)\n");
584 rprintf(F," --no-OPTION turn off an implied OPTION (e.g. --no-D)\n");
585 rprintf(F," -r, --recursive recurse into directories\n");
586 rprintf(F," -R, --relative use relative path names\n");
587@@ -311,6 +315,9 @@ void usage(enum logcode F)
588 #ifdef SUPPORT_ACLS
589 rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
590 #endif
591+#ifdef SUPPORT_XATTRS
592+ rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n");
593+#endif
594 rprintf(F," -o, --owner preserve owner (super-user only)\n");
595 rprintf(F," -g, --group preserve group\n");
596 rprintf(F," --devices preserve device files (super-user only)\n");
597@@ -438,6 +445,9 @@ static struct poptOption long_options[]
598 {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
599 {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
600 {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
601+ {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 },
602+ {"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
603+ {"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
604 {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
605 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
606 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
607@@ -1126,6 +1136,17 @@ int parse_arguments(int *argc, const cha
608 return 0;
609 #endif
610
611+ case 'X':
612+#ifdef SUPPORT_XATTRS
613+ preserve_xattrs = 1;
614+ preserve_perms = 1;
615+ break;
616+#else
617+ snprintf(err_buf,sizeof(err_buf),
618+ "extended attributes are not supported on this %s\n",
619+ am_server ? "server" : "client");
620+ return 0;
621+#endif
622
623 default:
624 /* A large opt value means that set_refuse_options()
625@@ -1590,6 +1611,10 @@ void server_options(char **args,int *arg
626 if (preserve_acls)
627 argstr[x++] = 'A';
628 #endif
629+#ifdef SUPPORT_XATTRS
630+ if (preserve_xattrs)
631+ argstr[x++] = 'X';
632+#endif
633 if (recurse)
634 argstr[x++] = 'r';
635 if (always_checksum)
636--- old/receiver.c
637+++ new/receiver.c
638@@ -22,6 +22,7 @@
639 #include "rsync.h"
640
641 extern int verbose;
642+extern int dry_run;
643 extern int do_xfers;
644 extern int am_server;
645 extern int do_progress;
646@@ -366,8 +367,8 @@ int recv_files(int f_in, char *local_nam
647 cleanup_disable();
648
649 /* This call also sets cur_flist. */
650- ndx = read_ndx_and_attrs(f_in, -1, &iflags,
651- &fnamecmp_type, xname, &xlen);
652+ ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
653+ xname, &xlen);
654 if (ndx == NDX_DONE) {
655 if (inc_recurse && first_flist) {
656 flist_free(first_flist);
657@@ -397,8 +398,17 @@ int recv_files(int f_in, char *local_nam
658 if (verbose > 2)
659 rprintf(FINFO, "recv_files(%s)\n", fname);
660
661+#ifdef SUPPORT_XATTRS
662+ if (iflags & ITEM_REPORT_XATTR && !dry_run)
663+ recv_xattr_request(file, f_in);
664+#endif
665+
666 if (!(iflags & ITEM_TRANSFER)) {
667 maybe_log_item(file, iflags, itemizing, xname);
668+#ifdef SUPPORT_XATTRS
669+ if (iflags & ITEM_REPORT_XATTR && !dry_run)
670+ set_file_attrs(fname, file, NULL, 0);
671+#endif
672 continue;
673 }
674 if (phase == 2) {
675--- old/rsync.c
676+++ new/rsync.c
677@@ -32,6 +32,7 @@
678 extern int verbose;
679 extern int dry_run;
680 extern int preserve_acls;
681+extern int preserve_xattrs;
682 extern int preserve_perms;
683 extern int preserve_executability;
684 extern int preserve_times;
685@@ -91,10 +92,8 @@ void setup_iconv()
686 }
687 #endif
688
689-/* This is used by sender.c with a valid f_out, and by receive.c with
690- * f_out = -1. */
691-int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr,
692- uchar *type_ptr, char *buf, int *len_ptr)
693+int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr,
694+ char *buf, int *len_ptr)
695 {
696 int len, iflags = 0;
697 struct file_list *flist;
698@@ -181,11 +180,6 @@ int read_ndx_and_attrs(int f_in, int f_o
699 ndx, who_am_i());
700 exit_cleanup(RERR_PROTOCOL);
701 }
702- } else if (f_out >= 0) {
703- if (inc_recurse)
704- send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
705- write_ndx_and_attrs(f_out, ndx, iflags,
706- fnamecmp_type, buf, len);
707 }
708
709 *iflag_ptr = iflags;
710@@ -247,6 +241,9 @@ int set_file_attrs(char *fname, struct f
711 #ifdef SUPPORT_ACLS
712 sx2.acc_acl = sx2.def_acl = NULL;
713 #endif
714+#ifdef SUPPORT_XATTRS
715+ sx2.xattr = NULL;
716+#endif
717 if (!preserve_perms && S_ISDIR(new_mode)
718 && sx2.st.st_mode & S_ISGID) {
719 /* We just created this directory and its setgid
720@@ -321,6 +318,10 @@ int set_file_attrs(char *fname, struct f
721 if (daemon_chmod_modes && !S_ISLNK(new_mode))
722 new_mode = tweak_mode(new_mode, daemon_chmod_modes);
723
724+#ifdef SUPPORT_XATTRS
725+ if (preserve_xattrs && !am_generator)
726+ set_xattr(fname, file, sxp);
727+#endif
728 #ifdef SUPPORT_ACLS
729 /* It's OK to call set_acl() now, even for a dir, as the generator
730 * will enable owner-writability using chmod, if necessary.
731@@ -353,10 +354,16 @@ int set_file_attrs(char *fname, struct f
732 rprintf(FCLIENT, "%s is uptodate\n", fname);
733 }
734 cleanup:
735+ if (sxp == &sx2) {
736 #ifdef SUPPORT_ACLS
737- if (preserve_acls && sxp == &sx2)
738- free_acl(&sx2);
739+ if (preserve_acls)
740+ free_acl(&sx2);
741 #endif
742+#ifdef SUPPORT_XATTRS
743+ if (preserve_xattrs)
744+ free_xattr(&sx2);
745+#endif
746+ }
747 return updated;
748 }
749
750--- old/rsync.h
751+++ new/rsync.h
752@@ -569,6 +569,7 @@ extern int file_extra_cnt;
753 extern int preserve_uid;
754 extern int preserve_gid;
755 extern int preserve_acls;
756+extern int preserve_xattrs;
757
758 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
759 #define EXTRA_LEN (sizeof (union file_extras))
760@@ -601,7 +602,8 @@ extern int preserve_acls;
761 /* When the associated option is on, all entries will have these present: */
762 #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
763 #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
764-#define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
765+#define F_ACL(f) REQ_EXTRA(f, preserve_acls)->num
766+#define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->num
767
768 /* These items are per-entry optional and mutally exclusive: */
769 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
770@@ -793,9 +795,13 @@ typedef struct {
771 struct rsync_acl *acc_acl; /* access ACL */
772 struct rsync_acl *def_acl; /* default ACL */
773 #endif
774+#ifdef SUPPORT_XATTRS
775+ item_list *xattr;
776+#endif
777 } statx;
778
779 #define ACL_READY(sx) ((sx).acc_acl != NULL)
780+#define XATTR_READY(sx) ((sx).xattr != NULL)
781
782 #include "proto.h"
783
784--- old/rsync.yo
785+++ new/rsync.yo
786@@ -301,7 +301,7 @@ to the detailed description below for a
787 -q, --quiet suppress non-error messages
788 --no-motd suppress daemon-mode MOTD (see caveat)
789 -c, --checksum skip based on checksum, not mod-time & size
790- -a, --archive archive mode; same as -rlptgoD (no -H, -A)
791+ -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)
792 --no-OPTION turn off an implied OPTION (e.g. --no-D)
793 -r, --recursive recurse into directories
794 -R, --relative use relative path names
795@@ -324,6 +324,7 @@ to the detailed description below for a
796 -E, --executability preserve executability
797 --chmod=CHMOD affect file and/or directory permissions
798 -A, --acls preserve ACLs (implies -p)
799+ -X, --xattrs preserve extended attrs (implies -p)
800 -o, --owner preserve owner (super-user only)
801 -g, --group preserve group
802 --devices preserve device files (super-user only)
803@@ -831,6 +832,11 @@ dit(bf(-A, --acls)) This option causes r
804 ACLs to be the same as the source ACLs. This nonstandard option only
805 works if the remote rsync also supports it. bf(--acls) implies bf(--perms).
806
807+dit(bf(-X, --xattrs)) This option causes rsync to update the remote
808+extended attributes to be the same as the local ones. This will work
809+only if the remote machine's rsync supports this option also. This is
810+a non-standard option.
811+
812 dit(bf(--chmod)) This option tells rsync to apply one or more
813 comma-separated "chmod" strings to the permission of the files in the
814 transfer. The resulting value is treated as though it was the permissions
815--- old/sender.c
816+++ new/sender.c
817@@ -22,6 +22,7 @@
818 #include "rsync.h"
819
820 extern int verbose;
821+extern int dry_run;
822 extern int do_xfers;
823 extern int am_server;
824 extern int am_daemon;
825@@ -144,8 +145,9 @@ void successful_send(int ndx)
826 rsyserr(FERROR, errno, "sender failed to remove %s", fname);
827 }
828
829-void write_ndx_and_attrs(int f_out, int ndx, int iflags,
830- uchar fnamecmp_type, char *buf, int len)
831+static void write_ndx_and_attrs(int f_out, int ndx, int iflags,
832+ const char *fname, struct file_struct *file,
833+ uchar fnamecmp_type, char *buf, int len)
834 {
835 write_ndx(f_out, ndx);
836 if (protocol_version < 29)
837@@ -155,6 +157,10 @@ void write_ndx_and_attrs(int f_out, int
838 write_byte(f_out, fnamecmp_type);
839 if (iflags & ITEM_XNAME_FOLLOWS)
840 write_vstring(f_out, buf, len);
841+#ifdef SUPPORT_XATTRS
842+ if (iflags & ITEM_REPORT_XATTR && !dry_run)
843+ send_xattr_request(fname, file, f_out);
844+#endif
845 }
846
847 void send_files(int f_in, int f_out)
848@@ -183,8 +189,8 @@ void send_files(int f_in, int f_out)
849 send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
850
851 /* This call also sets cur_flist. */
852- ndx = read_ndx_and_attrs(f_in, f_out, &iflags,
853- &fnamecmp_type, xname, &xlen);
854+ ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
855+ xname, &xlen);
856 if (ndx == NDX_DONE) {
857 if (inc_recurse && first_flist) {
858 flist_free(first_flist);
859@@ -201,6 +207,9 @@ void send_files(int f_in, int f_out)
860 continue;
861 }
862
863+ if (inc_recurse)
864+ send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
865+
866 file = cur_flist->files[ndx - cur_flist->ndx_start];
867 if (F_ROOTDIR(file)) {
868 path = F_ROOTDIR(file);
869@@ -215,8 +224,13 @@ void send_files(int f_in, int f_out)
870 if (verbose > 2)
871 rprintf(FINFO, "send_files(%d, %s%s%s)\n", ndx, path,slash,fname);
872
873+ if (iflags & ITEM_REPORT_XATTR)
874+ recv_xattr_request(file, f_in);
875+
876 if (!(iflags & ITEM_TRANSFER)) {
877 maybe_log_item(file, iflags, itemizing, xname);
878+ write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
879+ fnamecmp_type, xname, xlen);
880 continue;
881 }
882 if (phase == 2) {
883@@ -251,8 +265,8 @@ void send_files(int f_in, int f_out)
884
885 if (!do_xfers) { /* log the transfer */
886 log_item(FCLIENT, file, &stats, iflags, NULL);
887- write_ndx_and_attrs(f_out, ndx, iflags, fnamecmp_type,
888- xname, xlen);
889+ write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
890+ fnamecmp_type, xname, xlen);
891 continue;
892 }
893
894@@ -305,8 +319,8 @@ void send_files(int f_in, int f_out)
895 path,slash,fname, (double)st.st_size);
896 }
897
898- write_ndx_and_attrs(f_out, ndx, iflags, fnamecmp_type,
899- xname, xlen);
900+ write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
901+ fnamecmp_type, xname, xlen);
902 write_sum_head(f_xfer, s);
903
904 if (verbose > 2)
905--- old/testsuite/xattrs.test
906+++ new/testsuite/xattrs.test
907@@ -0,0 +1,56 @@
908+#! /bin/sh
909+
910+# This program is distributable under the terms of the GNU GPL (see
911+# COPYING).
912+
913+# Test that rsync handles basic xattr preservation.
914+
915+. $srcdir/testsuite/rsync.fns
916+
917+$RSYNC --version | grep ", xattrs" >/dev/null || test_skipped "Rsync is configured without xattr support"
918+
919+case "$RSYNC" in
920+*protocol=29*) test_skipped "xattr support requires protocol 30" ;;
921+esac
922+
923+makepath "$fromdir/foo"
924+echo something >"$fromdir/file1"
925+echo else >"$fromdir/file2"
926+echo last >"$fromdir/foo/file3"
927+
928+makepath "$todir/foo"
929+echo wow >"$todir/file1"
930+cp -p "$fromdir/foo/file3" "$todir/foo"
931+
932+files='foo file1 file2 foo/file3'
933+
934+cd "$fromdir"
935+
936+setfattr -n user.short -v 'this is short' file1 2>/dev/null || test_skipped "Unable to set an xattr"
937+setfattr -n user.long -v 'this is a long attribute that will be truncated in the initial data send' file1
938+setfattr -n user.good -v 'this is good' file1
939+setfattr -n user.nice -v 'this is nice' file1
940+
941+setfattr -n user.foo -v foo file2
942+setfattr -n user.bar -v bar file2
943+
944+setfattr -n user.foo -v 'new foo' foo/file3
945+setfattr -n user.bar -v 'new bar' foo/file3
946+setfattr -n user.long -v 'this is also a long attribute that will be truncated in the initial data send' foo/file3
947+setfattr -n user.equal -v 'this long attribute should remain the same and not need to be transferred' foo/file3
948+
949+setfattr -n user.short -v 'old short' "$todir/file1"
950+setfattr -n user.extra -v 'remove me' "$todir/file1"
951+
952+setfattr -n user.foo -v 'old foo' "$todir/foo/file3"
953+setfattr -n user.equal -v 'this long attribute should remain the same and not need to be transferred' "$todir/foo/file3"
954+
955+$RSYNC -avX . "$todir/"
956+
957+getfattr -d $files >"$scratchdir/xattrs.txt"
958+
959+cd "$todir"
960+getfattr -d $files | diff $diffopt "$scratchdir/xattrs.txt" -
961+
962+# The script would have aborted on error, so getting here means we've won.
963+exit 0
964--- old/xattrs.c
965+++ new/xattrs.c
966@@ -0,0 +1,688 @@
967+/*
968+ * Extended Attribute support for rsync.
969+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
970+ *
971+ * Copyright (C) 2004 Red Hat, Inc.
972+ * Copyright (C) 2006, 2007 Wayne Davison
973+ *
974+ * This program is free software; you can redistribute it and/or modify
975+ * it under the terms of the GNU General Public License version 2 as
976+ * published by the Free Software Foundation.
977+ *
978+ * This program is distributed in the hope that it will be useful,
979+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
980+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
981+ * GNU General Public License for more details.
982+ *
983+ * You should have received a copy of the GNU General Public License along
984+ * with this program; if not, write to the Free Software Foundation, Inc.,
985+ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
986+ */
987+
988+#include "rsync.h"
989+#include "lib/sysxattrs.h"
990+
991+#ifdef SUPPORT_XATTRS
992+
993+extern int dry_run;
994+extern int am_root;
995+extern int am_sender;
996+extern int read_only;
997+extern int list_only;
998+extern int checksum_seed;
999+
1000+#define RSYNC_XAL_INITIAL 5
1001+#define RSYNC_XAL_LIST_INITIAL 100
1002+
1003+#define MAX_FULL_DATUM 32
1004+
1005+#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
1006+ && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
1007+
1008+#define USER_PREFIX "user."
1009+#define UPRE_LEN ((int)sizeof USER_PREFIX - 1)
1010+#define SYSTEM_PREFIX "system."
1011+#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
1012+
1013+#ifdef HAVE_LINUX_XATTRS
1014+#define RPRE_LEN 0
1015+#else
1016+#define RSYNC_PREFIX "rsync."
1017+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
1018+#endif
1019+
1020+typedef struct {
1021+ char *datum, *name;
1022+ size_t datum_len, name_len;
1023+} rsync_xa;
1024+
1025+static size_t namebuf_len = 0;
1026+static char *namebuf = NULL;
1027+
1028+static item_list empty_xattr = EMPTY_ITEM_LIST;
1029+static item_list rsync_xal_l = EMPTY_ITEM_LIST;
1030+
1031+/* ------------------------------------------------------------------------- */
1032+
1033+static void rsync_xal_free(item_list *xalp)
1034+{
1035+ size_t i;
1036+ rsync_xa *rxas = xalp->items;
1037+
1038+ for (i = 0; i < xalp->count; i++) {
1039+ free(rxas[i].datum);
1040+ /*free(rxas[i].name);*/
1041+ }
1042+ xalp->count = 0;
1043+}
1044+
1045+void free_xattr(statx *sxp)
1046+{
1047+ if (!sxp->xattr)
1048+ return;
1049+ rsync_xal_free(sxp->xattr);
1050+ free(sxp->xattr);
1051+ sxp->xattr = NULL;
1052+}
1053+
1054+static int rsync_xal_compare_names(const void *x1, const void *x2)
1055+{
1056+ const rsync_xa *xa1 = x1;
1057+ const rsync_xa *xa2 = x2;
1058+ return strcmp(xa1->name, xa2->name);
1059+}
1060+
1061+static ssize_t get_xattr_names(const char *fname)
1062+{
1063+ ssize_t list_len;
1064+
1065+ if (!namebuf) {
1066+ namebuf_len = 1024;
1067+ namebuf = new_array(char, namebuf_len);
1068+ if (!namebuf)
1069+ out_of_memory("get_xattr_names");
1070+ }
1071+
1072+ /* The length returned includes all the '\0' terminators. */
1073+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
1074+ if (list_len > (ssize_t)namebuf_len) {
1075+ list_len = -1;
1076+ errno = ERANGE;
1077+ }
1078+ if (list_len < 0) {
1079+ if (errno == ENOTSUP)
1080+ return 0;
1081+ if (errno == ERANGE) {
1082+ list_len = sys_llistxattr(fname, NULL, 0);
1083+ if (list_len < 0) {
1084+ rsyserr(FERROR, errno,
1085+ "get_xattr_names: llistxattr(\"%s\",0) failed",
1086+ fname);
1087+ return -1;
1088+ }
1089+ if (namebuf_len)
1090+ free(namebuf);
1091+ namebuf_len = list_len + 1024;
1092+ namebuf = new_array(char, namebuf_len);
1093+ if (!namebuf)
1094+ out_of_memory("get_xattr_names");
1095+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
1096+ if (list_len < 0) {
1097+ rsyserr(FERROR, errno,
1098+ "get_xattr_names: llistxattr(\"%s\",%ld) failed",
1099+ fname, (long)namebuf_len);
1100+ return -1;
1101+ }
1102+ } else {
1103+ rsyserr(FERROR, errno,
1104+ "get_xattr_names: llistxattr(\"%s\",%ld) failed",
1105+ fname, (long)namebuf_len);
1106+ return -1;
1107+ }
1108+ }
1109+
1110+ return list_len;
1111+}
1112+
1113+/* On entry, the *len_ptr parameter contains the size of the extra space we
1114+ * should allocate when we create a buffer for the data. On exit, it contains
1115+ * the length of the datum. */
1116+static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr)
1117+{
1118+ size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
1119+ char *ptr;
1120+
1121+ if (datum_len == (size_t)-1) {
1122+ if (errno == ENOTSUP)
1123+ return NULL;
1124+ rsyserr(FERROR, errno,
1125+ "get_xattr_data: lgetxattr(\"%s\",\"%s\",0) failed",
1126+ fname, name);
1127+ return NULL;
1128+ }
1129+
1130+ if (datum_len + *len_ptr < datum_len /* checks for overflow */
1131+ || !(ptr = new_array(char, datum_len + *len_ptr)))
1132+ out_of_memory("get_xattr_data");
1133+
1134+ *len_ptr = datum_len;
1135+
1136+ if (datum_len) {
1137+ size_t len = sys_lgetxattr(fname, name, ptr, datum_len);
1138+ if (len != datum_len) {
1139+ if (len == (size_t)-1) {
1140+ rsyserr(FERROR, errno,
1141+ "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
1142+ " failed", fname, name, (long)datum_len);
1143+ } else {
1144+ rprintf(FERROR,
1145+ "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
1146+ " returned %ld\n", fname, name,
1147+ (long)datum_len, (long)len);
1148+ }
1149+ free(ptr);
1150+ return NULL;
1151+ }
1152+ }
1153+
1154+ return ptr;
1155+}
1156+
1157+static int rsync_xal_get(const char *fname, item_list *xalp)
1158+{
1159+ ssize_t list_len, name_len;
1160+ size_t datum_len, name_offset;
1161+ char *name, *ptr;
1162+ int user_only = am_sender ? 0 : !am_root;
1163+
1164+ /* This puts the name list into the "namebuf" buffer. */
1165+ if ((list_len = get_xattr_names(fname)) < 0)
1166+ return -1;
1167+
1168+ for (name = namebuf; list_len > 0; name += name_len) {
1169+ rsync_xa *rxas;
1170+
1171+ name_len = strlen(name) + 1;
1172+ list_len -= name_len;
1173+
1174+#ifdef HAVE_LINUX_XATTRS
1175+ /* We always ignore the system namespace, and non-root
1176+ * ignores everything but the user namespace. */
1177+ if (user_only ? !HAS_PREFIX(name, USER_PREFIX)
1178+ : HAS_PREFIX(name, SYSTEM_PREFIX))
1179+ continue;
1180+#endif
1181+
1182+ datum_len = name_len; /* Pass extra size to get_xattr_data() */
1183+ if (!(ptr = get_xattr_data(fname, name, &datum_len)))
1184+ return -1;
1185+
1186+ if (datum_len > MAX_FULL_DATUM) {
1187+ /* For large datums, we store a flag and a checksum. */
1188+ name_offset = 1 + MAX_DIGEST_LEN;
1189+ sum_init(checksum_seed);
1190+ sum_update(ptr, datum_len);
1191+ free(ptr);
1192+
1193+ if (!(ptr = new_array(char, name_offset + name_len)))
1194+ out_of_memory("rsync_xal_get");
1195+ *ptr = 0;
1196+ sum_end(ptr + 1);
1197+ } else
1198+ name_offset = datum_len;
1199+
1200+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
1201+ rxas->name = ptr + name_offset;
1202+ memcpy(rxas->name, name, name_len);
1203+ rxas->datum = ptr;
1204+ rxas->name_len = name_len;
1205+ rxas->datum_len = datum_len;
1206+ }
1207+ if (xalp->count > 1)
1208+ qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
1209+ return 0;
1210+}
1211+
1212+/* Read the xattr(s) for this filename. */
1213+int get_xattr(const char *fname, statx *sxp)
1214+{
1215+ sxp->xattr = new(item_list);
1216+ *sxp->xattr = empty_xattr;
1217+ if (rsync_xal_get(fname, sxp->xattr) < 0) {
1218+ free_xattr(sxp);
1219+ return -1;
1220+ }
1221+ return 0;
1222+}
1223+
1224+static int find_matching_xattr(item_list *xalp)
1225+{
1226+ size_t i, j;
1227+ item_list *lst = rsync_xal_l.items;
1228+
1229+ for (i = 0; i < rsync_xal_l.count; i++) {
1230+ rsync_xa *rxas1 = lst[i].items;
1231+ rsync_xa *rxas2 = xalp->items;
1232+
1233+ /* Wrong number of elements? */
1234+ if (lst[i].count != xalp->count)
1235+ continue;
1236+ /* any elements different? */
1237+ for (j = 0; j < xalp->count; j++) {
1238+ if (rxas1[j].name_len != rxas2[j].name_len
1239+ || rxas1[j].datum_len != rxas2[j].datum_len
1240+ || strcmp(rxas1[j].name, rxas2[j].name))
1241+ break;
1242+ if (rxas1[j].datum_len > MAX_FULL_DATUM) {
1243+ if (memcmp(rxas1[j].datum + 1,
1244+ rxas2[j].datum + 1,
1245+ MAX_DIGEST_LEN) != 0)
1246+ break;
1247+ } else {
1248+ if (memcmp(rxas1[j].datum, rxas2[j].datum,
1249+ rxas2[j].datum_len))
1250+ break;
1251+ }
1252+ }
1253+ /* no differences found. This is The One! */
1254+ if (j == xalp->count)
1255+ return i;
1256+ }
1257+
1258+ return -1;
1259+}
1260+
1261+/* Store *xalp on the end of rsync_xal_l */
1262+static void rsync_xal_store(item_list *xalp)
1263+{
1264+ item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
1265+ /* Since the following call starts a new list, we know it will hold the
1266+ * entire initial-count, not just enough space for one new item. */
1267+ *new_lst = empty_xattr;
1268+ (void)EXPAND_ITEM_LIST(new_lst, rsync_xa, xalp->count);
1269+ memcpy(new_lst->items, xalp->items, xalp->count * sizeof (rsync_xa));
1270+ new_lst->count = xalp->count;
1271+ xalp->count = 0;
1272+}
1273+
1274+/* Send the make_xattr()-generated xattr list for this flist entry. */
1275+int send_xattr(statx *sxp, int f)
1276+{
1277+ int ndx = find_matching_xattr(sxp->xattr);
1278+
1279+ /* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
1280+ write_abbrevint(f, ndx + 1);
1281+
1282+ if (ndx < 0) {
1283+ rsync_xa *rxa;
1284+ int count = sxp->xattr->count;
1285+ write_abbrevint(f, count);
1286+ for (rxa = sxp->xattr->items; count--; rxa++) {
1287+#ifdef HAVE_LINUX_XATTRS
1288+ write_abbrevint(f, rxa->name_len);
1289+ write_abbrevint(f, rxa->datum_len);
1290+ write_buf(f, rxa->name, rxa->name_len);
1291+#else
1292+ /* We strip the rsync prefix from disguised namespaces
1293+ * and put everything else in the user namespace. */
1294+ if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
1295+ && rxa->name[RPRE_LEN] != '%') {
1296+ write_abbrevint(f, rxa->name_len - RPRE_LEN);
1297+ write_abbrevint(f, rxa->datum_len);
1298+ write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
1299+ } else {
1300+ write_abbrevint(f, rxa->name_len + UPRE_LEN);
1301+ write_abbrevint(f, rxa->datum_len);
1302+ write_buf(f, USER_PREFIX, UPRE_LEN);
1303+ write_buf(f, rxa->name, rxa->name_len);
1304+ }
1305+#endif
1306+ if (rxa->datum_len > MAX_FULL_DATUM)
1307+ write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN);
1308+ else
1309+ write_buf(f, rxa->datum, rxa->datum_len);
1310+ }
1311+ ndx = rsync_xal_l.count; /* pre-incremented count */
1312+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
1313+ }
1314+
1315+ return ndx;
1316+}
1317+
1318+/* Return a flag indicating if we need to change a file's xattrs. If
1319+ * "find_all" is specified, also mark any abbreviated xattrs that we
1320+ * need so that send_xattr_request() can tell the sender about them. */
1321+int xattr_diff(struct file_struct *file, statx *sxp, int find_all)
1322+{
1323+ item_list *lst = rsync_xal_l.items;
1324+ rsync_xa *snd_rxa, *rec_rxa;
1325+ int snd_cnt, rec_cnt;
1326+ int cmp, same, xattrs_equal = 1;
1327+
1328+ if (!XATTR_READY(*sxp)) {
1329+ rec_rxa = NULL;
1330+ rec_cnt = 0;
1331+ } else {
1332+ rec_rxa = sxp->xattr->items;
1333+ rec_cnt = sxp->xattr->count;
1334+ }
1335+
1336+ if (F_XATTR(file) >= 0)
1337+ lst += F_XATTR(file);
1338+ else
1339+ lst = &empty_xattr;
1340+
1341+ snd_rxa = lst->items;
1342+ snd_cnt = lst->count;
1343+
1344+ /* If the count of the sender's xattrs is different from our
1345+ * (receiver's) xattrs, the lists are not the same. */
1346+ if (snd_cnt != rec_cnt) {
1347+ if (!find_all)
1348+ return 1;
1349+ xattrs_equal = 0;
1350+ }
1351+
1352+ while (snd_cnt) {
1353+ cmp = rec_cnt ? strcmp(snd_rxa->name, rec_rxa->name) : -1;
1354+ if (cmp > 0)
1355+ same = 0;
1356+ else if (snd_rxa->datum_len > MAX_FULL_DATUM) {
1357+ same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
1358+ && memcmp(snd_rxa->datum + 1, rec_rxa->datum + 1,
1359+ MAX_DIGEST_LEN) == 0;
1360+ /* Flag unrequested items that we need. */
1361+ if (/*!same &&*/ find_all && snd_rxa->datum[0] == 0)
1362+ snd_rxa->datum[0] = 1;
1363+ } else {
1364+ same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
1365+ && memcmp(snd_rxa->datum, rec_rxa->datum,
1366+ snd_rxa->datum_len) == 0;
1367+ }
1368+ if (!same) {
1369+ if (!find_all)
1370+ return 1;
1371+ xattrs_equal = 0;
1372+ }
1373+
1374+ if (cmp <= 0) {
1375+ snd_rxa++;
1376+ snd_cnt--;
1377+ }
1378+ if (cmp >= 0) {
1379+ rec_rxa++;
1380+ rec_cnt--;
1381+ }
1382+ }
1383+
1384+ if (rec_cnt)
1385+ xattrs_equal = 0;
1386+
1387+ return !xattrs_equal;
1388+}
1389+
1390+/* When called by the generator with a NULL fname, this tells the sender
1391+ * which abbreviated xattr values we need. When called by the sender
1392+ * (with a non-NULL fname), we send all the extra xattr data it needs. */
1393+void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
1394+{
1395+ item_list *lst = rsync_xal_l.items;
1396+ int j, cnt, prior_req = -1;
1397+ rsync_xa *rxa;
1398+
1399+ lst += F_XATTR(file);
1400+ cnt = lst->count;
1401+ for (rxa = lst->items, j = 0; j < cnt; rxa++, j++) {
1402+ if (rxa->datum_len <= MAX_FULL_DATUM
1403+ || rxa->datum[0] != 1)
1404+ continue;
1405+
1406+ /* Flag that we handled this abbreviated item. */
1407+ rxa->datum[0] = 2;
1408+
1409+ write_abbrevint(f_out, j - prior_req);
1410+ prior_req = j;
1411+
1412+ if (fname) {
1413+ size_t len = 0;
1414+ char *ptr;
1415+
1416+ /* Re-read the long datum. */
1417+ if (!(ptr = get_xattr_data(fname, rxa->name, &len)))
1418+ continue;
1419+
1420+ write_abbrevint(f_out, len); /* length might have changed! */
1421+ write_buf(f_out, ptr, len);
1422+ free(ptr);
1423+ }
1424+ }
1425+
1426+ write_byte(f_out, 0); /* end the list */
1427+}
1428+
1429+/* When called by the sender, read the request from the generator and mark
1430+ * any needed xattrs with a flag that lets us know they need to be sent to
1431+ * the receiver. When called by the receiver, reads the sent data and
1432+ * stores it in place of its checksum. */
1433+void recv_xattr_request(struct file_struct *file, int f_in)
1434+{
1435+ item_list *lst = rsync_xal_l.items;
1436+ char *old_datum, *name;
1437+ rsync_xa *rxa;
1438+ int rel_pos, cnt;
1439+
1440+ if (F_XATTR(file) >= 0)
1441+ lst += F_XATTR(file);
1442+ else
1443+ exit_cleanup(RERR_STREAMIO); /* XXX */
1444+
1445+ cnt = lst->count;
1446+ rxa = lst->items;
1447+ rxa -= 1;
1448+ while ((rel_pos = read_abbrevint(f_in)) != 0) {
1449+ rxa += rel_pos;
1450+ cnt -= rel_pos;
1451+ if (cnt < 0 || rxa->datum_len <= MAX_FULL_DATUM
1452+ || rxa->datum[0] != 0)
1453+ exit_cleanup(RERR_STREAMIO); /* XXX */
1454+
1455+ if (am_sender) {
1456+ rxa->datum[0] = 1;
1457+ continue;
1458+ }
1459+
1460+ old_datum = rxa->datum;
1461+ rxa->datum_len = read_abbrevint(f_in);
1462+
1463+ if (rxa->name_len + rxa->datum_len < rxa->name_len)
1464+ out_of_memory("recv_xattr_request"); /* overflow */
1465+ rxa->datum = new_array(char, rxa->datum_len + rxa->name_len);
1466+ if (!rxa->datum)
1467+ out_of_memory("recv_xattr_request");
1468+ name = rxa->datum + rxa->datum_len;
1469+ memcpy(name, rxa->name, rxa->name_len);
1470+ rxa->name = name;
1471+ free(old_datum);
1472+ read_buf(f_in, rxa->datum, rxa->datum_len);
1473+ }
1474+}
1475+
1476+/* ------------------------------------------------------------------------- */
1477+
1478+/* receive and build the rsync_xattr_lists */
1479+void receive_xattr(struct file_struct *file, int f)
1480+{
1481+ static item_list temp_xattr = EMPTY_ITEM_LIST;
1482+ int count;
1483+ int ndx = read_abbrevint(f);
1484+
1485+ if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) {
1486+ rprintf(FERROR, "receive_xattr: xa index %d out of"
1487+ " range for %s\n", ndx, f_name(file, NULL));
1488+ exit_cleanup(RERR_STREAMIO);
1489+ }
1490+
1491+ if (ndx != 0) {
1492+ F_XATTR(file) = ndx - 1;
1493+ return;
1494+ }
1495+
1496+ if ((count = read_abbrevint(f)) != 0) {
1497+ (void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
1498+ temp_xattr.count = 0;
1499+ }
1500+
1501+ while (count--) {
1502+ char *ptr, *name;
1503+ rsync_xa *rxa;
1504+ size_t name_len = read_abbrevint(f);
1505+ size_t datum_len = read_abbrevint(f);
1506+ size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
1507+#ifdef HAVE_LINUX_XATTRS
1508+ size_t extra_len = 0;
1509+#else
1510+ size_t extra_len = am_root ? RPRE_LEN : 0;
1511+ if (dget_len + extra_len < dget_len)
1512+ out_of_memory("receive_xattr"); /* overflow */
1513+#endif
1514+ if (dget_len + extra_len + name_len < dget_len)
1515+ out_of_memory("receive_xattr"); /* overflow */
1516+ ptr = new_array(char, dget_len + extra_len + name_len);
1517+ if (!ptr)
1518+ out_of_memory("receive_xattr");
1519+ name = ptr + dget_len + extra_len;
1520+ read_buf(f, name, name_len);
1521+ if (dget_len == datum_len)
1522+ read_buf(f, ptr, dget_len);
1523+ else {
1524+ *ptr = 0;
1525+ read_buf(f, ptr + 1, MAX_DIGEST_LEN);
1526+ }
1527+#ifdef HAVE_LINUX_XATTRS
1528+ /* Non-root can only save the user namespace. */
1529+ if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
1530+ free(ptr);
1531+ continue;
1532+ }
1533+#else
1534+ /* This OS only has a user namespace, so we either
1535+ * strip the user prefix, or we put a non-user
1536+ * namespace inside our rsync hierarchy. */
1537+ if (HAS_PREFIX(name, USER_PREFIX)) {
1538+ name += UPRE_LEN;
1539+ name_len -= UPRE_LEN;
1540+ } else if (am_root) {
1541+ name -= RPRE_LEN;
1542+ name_len += RPRE_LEN;
1543+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
1544+ } else {
1545+ free(ptr);
1546+ continue;
1547+ }
1548+#endif
1549+ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1);
1550+ rxa->name = name;
1551+ rxa->datum = ptr;
1552+ rxa->name_len = name_len;
1553+ rxa->datum_len = datum_len;
1554+ }
1555+
1556+ ndx = rsync_xal_l.count; /* pre-incremented count */
1557+ rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
1558+
1559+ F_XATTR(file) = ndx;
1560+}
1561+
1562+/* Turn the xattr data in statx into cached xattr data, setting the index
1563+ * values in the file struct. */
1564+void cache_xattr(struct file_struct *file, statx *sxp)
1565+{
1566+ int ndx;
1567+
1568+ if (!sxp->xattr)
1569+ return;
1570+
1571+ ndx = find_matching_xattr(sxp->xattr);
1572+ if (ndx < 0)
1573+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
1574+
1575+ F_XATTR(file) = ndx;
1576+}
1577+
1578+static int rsync_xal_set(const char *fname, item_list *xalp)
1579+{
1580+ rsync_xa *rxas = xalp->items;
1581+ ssize_t list_len;
1582+ size_t i;
1583+ char *name;
1584+ int name_len, status, ret = 0;
1585+
1586+ /* This puts the current name list into the "namebuf" buffer. */
1587+ if ((list_len = get_xattr_names(fname)) < 0)
1588+ return -1;
1589+
1590+ for (i = 0; i < xalp->count; i++) {
1591+ if ((size_t)(rxas[i].name - rxas[i].datum) < rxas[i].datum_len) {
1592+ rprintf(FERROR, "Abbreviated xattr value, %s, not received for %s\n",
1593+ rxas[i].name, full_fname(fname));
1594+ exit_cleanup(RERR_STREAMIO);
1595+ }
1596+ status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len);
1597+ if (status < 0) {
1598+ rsyserr(FERROR, errno,
1599+ "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
1600+ fname, rxas[i].name);
1601+ ret = -1;
1602+ }
1603+ }
1604+
1605+ /* Remove any extraneous names. */
1606+ for (name = namebuf; list_len > 0; name += name_len) {
1607+ name_len = strlen(name) + 1;
1608+ list_len -= name_len;
1609+
1610+#ifdef HAVE_LINUX_XATTRS
1611+ /* We always ignore the system namespace, and non-root
1612+ * ignores everything but the user namespace. */
1613+ if (am_root ? HAS_PREFIX(name, SYSTEM_PREFIX)
1614+ : !HAS_PREFIX(name, USER_PREFIX))
1615+ continue;
1616+#endif
1617+
1618+ for (i = 0; i < xalp->count; i++) {
1619+ if (strcmp(name, rxas[i].name) == 0)
1620+ break;
1621+ }
1622+ if (i == xalp->count) {
1623+ int status = sys_lremovexattr(fname, name);
1624+ if (status < 0) {
1625+ rsyserr(FERROR, errno,
1626+ "rsync_xal_clear: lremovexattr(\"%s\",\"%s\") failed",
1627+ fname, name);
1628+ ret = -1;
1629+ }
1630+ }
1631+ }
1632+
1633+ return ret;
1634+}
1635+
1636+/* Set extended attributes on indicated filename. */
1637+int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
1638+{
1639+ int ndx;
1640+ item_list *lst = rsync_xal_l.items;
1641+
1642+ if (dry_run)
1643+ return 1; /* FIXME: --dry-run needs to compute this value */
1644+
1645+ if (read_only || list_only) {
1646+ errno = EROFS;
1647+ return -1;
1648+ }
1649+
1650+ ndx = F_XATTR(file);
1651+ return rsync_xal_set(fname, lst + ndx);
1652+}
1653+
1654+#endif /* SUPPORT_XATTRS */