1 This patch adds support for extended attributes.
3 To use this patch, run these commands for a successful build:
5 patch -p1 <patches/xattrs.diff
10 CAUTION: this patch has been recently reworked, and needs more testing!
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.
20 @@ -28,13 +28,13 @@ VERSION=@VERSION@
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 \
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)
48 sx.acc_acl = sx.def_acl = NULL;
50 +#ifdef SUPPORT_XATTRS
53 if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
56 @@ -143,6 +147,13 @@ static int make_bak_dir(char *fullpath)
60 +#ifdef SUPPORT_XATTRS
61 + if (preserve_xattrs) {
62 + get_xattr(rel, &sx);
63 + cache_xattr(file, &sx);
67 set_file_attrs(fullpath, file, NULL, 0);
70 @@ -194,6 +205,9 @@ static int keep_backup(const char *fname
72 sx.acc_acl = sx.def_acl = NULL;
74 +#ifdef SUPPORT_XATTRS
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
84 +#ifdef SUPPORT_XATTRS
85 + if (preserve_xattrs) {
86 + get_xattr(fname, &sx);
87 + cache_xattr(file, &sx);
92 /* Check to see if this is a device file, or link */
93 if ((am_root && preserve_devices && IS_DEVICE(file->mode))
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;
111 if (remote_protocol == 0) {
113 @@ -119,6 +122,13 @@ void setup_protocol(int f_out,int f_in)
115 exit_cleanup(RERR_PROTOCOL);
117 + if (preserve_xattrs) {
119 + "--xattrs requires protocol 30 or higher"
120 + " (negotiated %d).\n",
122 + exit_cleanup(RERR_PROTOCOL);
126 if (delete_mode && !(delete_before+delete_during+delete_after)) {
129 @@ -891,6 +891,46 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
133 +AC_CHECK_HEADERS(attr/xattr.h)
134 +AC_CHECK_HEADERS(sys/xattr.h)
135 +AC_CHECK_HEADERS(sys/extattr.h)
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]))
144 +if test x"$enable_xattr_support" = x"no"; then
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])
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)
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)
164 + if test x"$enable_xattr_support" = x"yes"; then
165 + AC_MSG_ERROR(Failed to find extended attribute support)
167 + AC_MSG_RESULT(No extended attribute support found)
173 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
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);
190 +#ifdef SUPPORT_XATTRS
191 + if (preserve_xattrs)
192 + receive_xattr(file, f );
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)
200 struct file_struct *file;
202 +#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
206 @@ -1179,6 +1184,13 @@ static struct file_struct *send_file_nam
210 +#ifdef SUPPORT_XATTRS
211 + if (preserve_xattrs && f >= 0) {
213 + if (get_xattr(fname, &sx) < 0)
218 maybe_emit_filelist_progress(flist->count + flist_count_offset);
220 @@ -1192,6 +1204,12 @@ static struct file_struct *send_file_nam
224 +#ifdef SUPPORT_XATTRS
225 + if (preserve_xattrs) {
226 + F_XATTR(file) = send_xattr(&sx, f);
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
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))
258 @@ -567,11 +576,19 @@ void itemize(const char *fname, struct f
259 iflags |= ITEM_REPORT_ACL;
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;
271 iflags |= ITEM_IS_NEW;
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) {
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);
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,
292 sx.acc_acl = sx.def_acl = NULL;
294 +#ifdef SUPPORT_XATTRS
299 flist_free(fuzzy_dirlist);
300 @@ -1636,6 +1660,10 @@ static void recv_generator(char *fname,
304 +#ifdef SUPPORT_XATTRS
305 + if (preserve_xattrs)
306 + free_xattr(&real_sx);
311 @@ -1672,6 +1700,10 @@ static void recv_generator(char *fname,
315 +#ifdef SUPPORT_XATTRS
316 + if (preserve_xattrs)
322 --- old/lib/sysxattrs.c
323 +++ new/lib/sysxattrs.c
326 + * Extended attribute support for rsync.
328 + * Copyright (C) 2004 Red Hat, Inc.
329 + * Written by Jay Fenlason.
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.
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.
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.
347 +#include "sysxattrs.h"
349 +#ifdef SUPPORT_XATTRS
351 +#if defined HAVE_LINUX_XATTRS
353 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
355 + return lgetxattr(path, name, value, size);
358 +ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
360 + return fgetxattr(filedes, name, value, size);
363 +int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
365 + return lsetxattr(path, name, value, size, 0);
368 +int sys_lremovexattr(const char *path, const char *name)
370 + return lremovexattr(path, name);
373 +ssize_t sys_llistxattr(const char *path, char *list, size_t size)
375 + return llistxattr(path, list, size);
378 +#elif HAVE_OSX_XATTRS
380 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
382 + return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
385 +ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
387 + return fgetxattr(filedes, name, value, size, 0, 0);
390 +int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
392 + return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
395 +int sys_lremovexattr(const char *path, const char *name)
397 + return removexattr(path, name, XATTR_NOFOLLOW);
400 +ssize_t sys_llistxattr(const char *path, char *list, size_t size)
402 + return listxattr(path, list, size, XATTR_NOFOLLOW);
405 +#elif HAVE_FREEBSD_XATTRS
407 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
409 + return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
412 +ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
414 + return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
417 +int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
419 + return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
422 +int sys_lremovexattr(const char *path, const char *name)
424 + return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
427 +ssize_t sys_llistxattr(const char *path, char *list, size_t size)
429 + unsigned char keylen;
430 + ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
432 + if (len <= 0 || (size_t)len > size)
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! */
446 + memmove(list+off, list+off+1, keylen);
447 + list[off+keylen] = '\0';
455 +#error You need to create xattr compatibility functions.
459 +#endif /* SUPPORT_XATTRS */
460 --- old/lib/sysxattrs.h
461 +++ new/lib/sysxattrs.h
463 +#ifdef SUPPORT_XATTRS
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>
473 +/* Linux 2.4 does not define this as a distinct errno value: */
475 +#define ENOATTR ENODATA
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);
486 +/* No xattrs available */
491 @@ -608,7 +608,7 @@ static void fix_basis_dirs(void)
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)
500 @@ -617,8 +617,8 @@ static void read_final_goodbye(int f_in,
501 if (protocol_version < 29)
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,
511 @@ -677,7 +677,7 @@ static void do_server_sender(int f_in, i
512 io_flush(FULL_FLUSH);
514 if (protocol_version >= 24)
515 - read_final_goodbye(f_in, f_out);
516 + read_final_goodbye(f_in);
517 io_flush(FULL_FLUSH);
520 @@ -740,7 +740,7 @@ static int do_recv(int f_in, int f_out,
521 kluge_around_eof = -1;
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,
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);
532 if (protocol_version >= 24)
533 - read_final_goodbye(f_in, f_out);
534 + read_final_goodbye(f_in);
537 rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
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
561 +#ifdef SUPPORT_XATTRS
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);
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)
589 rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
591 +#ifdef SUPPORT_XATTRS
592 + rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n");
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
612 +#ifdef SUPPORT_XATTRS
613 + preserve_xattrs = 1;
614 + preserve_perms = 1;
617 + snprintf(err_buf,sizeof(err_buf),
618 + "extended attributes are not supported on this %s\n",
619 + am_server ? "server" : "client");
624 /* A large opt value means that set_refuse_options()
625 @@ -1590,6 +1611,10 @@ void server_options(char **args,int *arg
629 +#ifdef SUPPORT_XATTRS
630 + if (preserve_xattrs)
644 extern int am_server;
645 extern int do_progress;
646 @@ -366,8 +367,8 @@ int recv_files(int f_in, char *local_nam
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,
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
659 rprintf(FINFO, "recv_files(%s)\n", fname);
661 +#ifdef SUPPORT_XATTRS
662 + if (iflags & ITEM_REPORT_XATTR && !dry_run)
663 + recv_xattr_request(file, f_in);
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);
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()
689 -/* This is used by sender.c with a valid f_out, and by receive.c with
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)
697 struct file_list *flist;
698 @@ -181,11 +180,6 @@ int read_ndx_and_attrs(int f_in, int f_o
700 exit_cleanup(RERR_PROTOCOL);
702 - } else if (f_out >= 0) {
704 - send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
705 - write_ndx_and_attrs(f_out, ndx, iflags,
706 - fnamecmp_type, buf, len);
710 @@ -247,6 +241,9 @@ int set_file_attrs(char *fname, struct f
712 sx2.acc_acl = sx2.def_acl = NULL;
714 +#ifdef SUPPORT_XATTRS
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);
724 +#ifdef SUPPORT_XATTRS
725 + if (preserve_xattrs && !am_generator)
726 + set_xattr(fname, file, sxp);
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);
737 - if (preserve_acls && sxp == &sx2)
742 +#ifdef SUPPORT_XATTRS
743 + if (preserve_xattrs)
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;
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
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 */
774 +#ifdef SUPPORT_XATTRS
779 #define ACL_READY(sx) ((sx).acc_acl != NULL)
780 +#define XATTR_READY(sx) ((sx).xattr != NULL)
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).
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.
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
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);
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)
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);
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);
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,
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)
864 + send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
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)
871 rprintf(FINFO, "send_files(%d, %s%s%s)\n", ndx, path,slash,fname);
873 + if (iflags & ITEM_REPORT_XATTR)
874 + recv_xattr_request(file, f_in);
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);
883 @@ -251,8 +265,8 @@ void send_files(int f_in, int f_out)
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,
889 + write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
890 + fnamecmp_type, xname, xlen);
894 @@ -305,8 +319,8 @@ void send_files(int f_in, int f_out)
895 path,slash,fname, (double)st.st_size);
898 - write_ndx_and_attrs(f_out, ndx, iflags, fnamecmp_type,
900 + write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
901 + fnamecmp_type, xname, xlen);
902 write_sum_head(f_xfer, s);
905 --- old/testsuite/xattrs.test
906 +++ new/testsuite/xattrs.test
910 +# This program is distributable under the terms of the GNU GPL (see
913 +# Test that rsync handles basic xattr preservation.
915 +. $srcdir/testsuite/rsync.fns
917 +$RSYNC --version | grep ", xattrs" >/dev/null || test_skipped "Rsync is configured without xattr support"
920 +*protocol=29*) test_skipped "xattr support requires protocol 30" ;;
923 +makepath "$fromdir/foo"
924 +echo something >"$fromdir/file1"
925 +echo else >"$fromdir/file2"
926 +echo last >"$fromdir/foo/file3"
928 +makepath "$todir/foo"
929 +echo wow >"$todir/file1"
930 +cp -p "$fromdir/foo/file3" "$todir/foo"
932 +files='foo file1 file2 foo/file3'
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
941 +setfattr -n user.foo -v foo file2
942 +setfattr -n user.bar -v bar file2
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
949 +setfattr -n user.short -v 'old short' "$todir/file1"
950 +setfattr -n user.extra -v 'remove me' "$todir/file1"
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"
955 +$RSYNC -avX . "$todir/"
957 +getfattr -d $files >"$scratchdir/xattrs.txt"
960 +getfattr -d $files | diff $diffopt "$scratchdir/xattrs.txt" -
962 +# The script would have aborted on error, so getting here means we've won.
968 + * Extended Attribute support for rsync.
969 + * Written by Jay Fenlason, vaguely based on the ACLs patch.
971 + * Copyright (C) 2004 Red Hat, Inc.
972 + * Copyright (C) 2006, 2007 Wayne Davison
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.
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.
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.
989 +#include "lib/sysxattrs.h"
991 +#ifdef SUPPORT_XATTRS
995 +extern int am_sender;
996 +extern int read_only;
997 +extern int list_only;
998 +extern int checksum_seed;
1000 +#define RSYNC_XAL_INITIAL 5
1001 +#define RSYNC_XAL_LIST_INITIAL 100
1003 +#define MAX_FULL_DATUM 32
1005 +#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
1006 + && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
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)
1013 +#ifdef HAVE_LINUX_XATTRS
1016 +#define RSYNC_PREFIX "rsync."
1017 +#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
1021 + char *datum, *name;
1022 + size_t datum_len, name_len;
1025 +static size_t namebuf_len = 0;
1026 +static char *namebuf = NULL;
1028 +static item_list empty_xattr = EMPTY_ITEM_LIST;
1029 +static item_list rsync_xal_l = EMPTY_ITEM_LIST;
1031 +/* ------------------------------------------------------------------------- */
1033 +static void rsync_xal_free(item_list *xalp)
1036 + rsync_xa *rxas = xalp->items;
1038 + for (i = 0; i < xalp->count; i++) {
1039 + free(rxas[i].datum);
1040 + /*free(rxas[i].name);*/
1045 +void free_xattr(statx *sxp)
1049 + rsync_xal_free(sxp->xattr);
1051 + sxp->xattr = NULL;
1054 +static int rsync_xal_compare_names(const void *x1, const void *x2)
1056 + const rsync_xa *xa1 = x1;
1057 + const rsync_xa *xa2 = x2;
1058 + return strcmp(xa1->name, xa2->name);
1061 +static ssize_t get_xattr_names(const char *fname)
1066 + namebuf_len = 1024;
1067 + namebuf = new_array(char, namebuf_len);
1069 + out_of_memory("get_xattr_names");
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) {
1078 + if (list_len < 0) {
1079 + if (errno == ENOTSUP)
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",
1091 + namebuf_len = list_len + 1024;
1092 + namebuf = new_array(char, namebuf_len);
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);
1103 + rsyserr(FERROR, errno,
1104 + "get_xattr_names: llistxattr(\"%s\",%ld) failed",
1105 + fname, (long)namebuf_len);
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)
1118 + size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
1121 + if (datum_len == (size_t)-1) {
1122 + if (errno == ENOTSUP)
1124 + rsyserr(FERROR, errno,
1125 + "get_xattr_data: lgetxattr(\"%s\",\"%s\",0) failed",
1130 + *len_ptr = datum_len;
1132 + if (datum_len + *len_ptr < datum_len /* checks for overflow */
1133 + || !(ptr = new_array(char, datum_len + *len_ptr)))
1134 + out_of_memory("get_xattr_data");
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);
1145 + "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
1146 + " returned %ld\n", fname, name,
1147 + (long)datum_len, (long)len);
1157 +static int rsync_xal_get(const char *fname, item_list *xalp)
1159 + ssize_t list_len, name_len;
1160 + size_t datum_len, name_offset;
1162 + int user_only = am_sender ? 0 : !am_root;
1164 + /* This puts the name list into the "namebuf" buffer. */
1165 + if ((list_len = get_xattr_names(fname)) < 0)
1168 + for (name = namebuf; list_len > 0; name += name_len) {
1171 + name_len = strlen(name) + 1;
1172 + list_len -= name_len;
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))
1182 + datum_len = name_len; /* Pass extra size to get_xattr_data() */
1183 + if (!(ptr = get_xattr_data(fname, name, &datum_len)))
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);
1193 + if (!(ptr = new_array(char, name_offset + name_len)))
1194 + out_of_memory("rsync_xal_get");
1198 + name_offset = datum_len;
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;
1207 + if (xalp->count > 1)
1208 + qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
1212 +/* Read the xattr(s) for this filename. */
1213 +int get_xattr(const char *fname, statx *sxp)
1215 + sxp->xattr = new(item_list);
1216 + *sxp->xattr = empty_xattr;
1217 + if (rsync_xal_get(fname, sxp->xattr) < 0) {
1224 +static int find_matching_xattr(item_list *xalp)
1227 + item_list *lst = rsync_xal_l.items;
1229 + for (i = 0; i < rsync_xal_l.count; i++) {
1230 + rsync_xa *rxas1 = lst[i].items;
1231 + rsync_xa *rxas2 = xalp->items;
1233 + /* Wrong number of elements? */
1234 + if (lst[i].count != xalp->count)
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))
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)
1248 + if (memcmp(rxas1[j].datum, rxas2[j].datum,
1249 + rxas2[j].datum_len))
1253 + /* no differences found. This is The One! */
1254 + if (j == xalp->count)
1261 +/* Store *xalp on the end of rsync_xal_l */
1262 +static void rsync_xal_store(item_list *xalp)
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;
1274 +/* Send the make_xattr()-generated xattr list for this flist entry. */
1275 +int send_xattr(statx *sxp, int f)
1277 + int ndx = find_matching_xattr(sxp->xattr);
1279 + /* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
1280 + write_abbrevint(f, ndx + 1);
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);
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);
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);
1306 + if (rxa->datum_len > MAX_FULL_DATUM)
1307 + write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN);
1309 + write_buf(f, rxa->datum, rxa->datum_len);
1311 + ndx = rsync_xal_l.count; /* pre-incremented count */
1312 + rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
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)
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;
1328 + if (!XATTR_READY(*sxp)) {
1332 + rec_rxa = sxp->xattr->items;
1333 + rec_cnt = sxp->xattr->count;
1336 + if (F_XATTR(file) >= 0)
1337 + lst += F_XATTR(file);
1339 + lst = &empty_xattr;
1341 + snd_rxa = lst->items;
1342 + snd_cnt = lst->count;
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) {
1353 + cmp = rec_cnt ? strcmp(snd_rxa->name, rec_rxa->name) : -1;
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;
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;
1387 + return !xattrs_equal;
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)
1395 + item_list *lst = rsync_xal_l.items;
1396 + int j, cnt, prior_req = -1;
1399 + lst += F_XATTR(file);
1401 + for (rxa = lst->items, j = 0; j < cnt; rxa++, j++) {
1402 + if (rxa->datum_len <= MAX_FULL_DATUM
1403 + || rxa->datum[0] != 1)
1406 + /* Flag that we handled this abbreviated item. */
1407 + rxa->datum[0] = 2;
1409 + write_abbrevint(f_out, j - prior_req);
1416 + /* Re-read the long datum. */
1417 + if (!(ptr = get_xattr_data(fname, rxa->name, &len)))
1420 + write_abbrevint(f_out, len); /* length might have changed! */
1421 + write_buf(f_out, ptr, len);
1426 + write_byte(f_out, 0); /* end the list */
1429 +/* Read the request from the generator, and mark any needed xattrs
1430 + * with a flag that lets us know they need to be sent. */
1431 +void recv_xattr_request(struct file_struct *file, int f_in)
1433 + item_list *lst = rsync_xal_l.items;
1434 + char *old_datum, *name;
1438 + if (F_XATTR(file) >= 0)
1439 + lst += F_XATTR(file);
1441 + exit_cleanup(RERR_STREAMIO); /* XXX */
1446 + while ((rel_pos = read_abbrevint(f_in)) != 0) {
1449 + if (cnt < 0 || rxa->datum_len <= MAX_FULL_DATUM
1450 + || rxa->datum[0] != 0)
1451 + exit_cleanup(RERR_STREAMIO); /* XXX */
1454 + rxa->datum[0] = 1;
1458 + old_datum = rxa->datum;
1459 + rxa->datum_len = read_abbrevint(f_in);
1461 + if (rxa->name_len + rxa->datum_len < rxa->name_len)
1462 + out_of_memory("recv_xattr_request"); /* overflow */
1463 + rxa->datum = new_array(char, rxa->datum_len + rxa->name_len);
1465 + out_of_memory("recv_xattr_request");
1466 + name = rxa->datum + rxa->datum_len;
1467 + memcpy(name, rxa->name, rxa->name_len);
1470 + read_buf(f_in, rxa->datum, rxa->datum_len);
1474 +/* ------------------------------------------------------------------------- */
1476 +/* receive and build the rsync_xattr_lists */
1477 +void receive_xattr(struct file_struct *file, int f)
1479 + static item_list temp_xattr = EMPTY_ITEM_LIST;
1481 + int ndx = read_abbrevint(f);
1483 + if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) {
1484 + rprintf(FERROR, "receive_xattr: xa index %d out of"
1485 + " range for %s\n", ndx, f_name(file, NULL));
1486 + exit_cleanup(RERR_STREAMIO);
1490 + F_XATTR(file) = ndx - 1;
1494 + if ((count = read_abbrevint(f)) != 0) {
1495 + (void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
1496 + temp_xattr.count = 0;
1502 + size_t name_len = read_abbrevint(f);
1503 + size_t datum_len = read_abbrevint(f);
1504 + size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
1505 +#ifdef HAVE_LINUX_XATTRS
1506 + size_t extra_len = 0;
1508 + size_t extra_len = am_root ? RPRE_LEN : 0;
1509 + if (dget_len + extra_len < dget_len)
1510 + out_of_memory("receive_xattr"); /* overflow */
1512 + if (dget_len + extra_len + name_len < dget_len)
1513 + out_of_memory("receive_xattr"); /* overflow */
1514 + ptr = new_array(char, dget_len + extra_len + name_len);
1516 + out_of_memory("receive_xattr");
1517 + name = ptr + dget_len + extra_len;
1518 + read_buf(f, name, name_len);
1519 + if (dget_len == datum_len)
1520 + read_buf(f, ptr, dget_len);
1523 + read_buf(f, ptr + 1, MAX_DIGEST_LEN);
1525 +#ifdef HAVE_LINUX_XATTRS
1526 + /* Non-root can only save the user namespace. */
1527 + if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
1532 + /* This OS only has a user namespace, so we either
1533 + * strip the user prefix, or we put a non-user
1534 + * namespace inside our rsync hierarchy. */
1535 + if (HAS_PREFIX(name, USER_PREFIX)) {
1537 + name_len -= UPRE_LEN;
1538 + } else if (am_root) {
1540 + name_len += RPRE_LEN;
1541 + memcpy(name, RSYNC_PREFIX, RPRE_LEN);
1547 + rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1);
1550 + rxa->name_len = name_len;
1551 + rxa->datum_len = datum_len;
1554 + ndx = rsync_xal_l.count; /* pre-incremented count */
1555 + rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
1557 + F_XATTR(file) = ndx;
1560 +/* Turn the xattr data in statx into cached xattr data, setting the index
1561 + * values in the file struct. */
1562 +void cache_xattr(struct file_struct *file, statx *sxp)
1569 + ndx = find_matching_xattr(sxp->xattr);
1571 + rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
1573 + F_XATTR(file) = ndx;
1576 +static int rsync_xal_set(const char *fname, item_list *xalp)
1578 + rsync_xa *rxas = xalp->items;
1582 + int name_len, status, ret = 0;
1584 + /* This puts the current name list into the "namebuf" buffer. */
1585 + if ((list_len = get_xattr_names(fname)) < 0)
1588 + for (i = 0; i < xalp->count; i++) {
1589 + if ((size_t)(rxas[i].name - rxas[i].datum) < rxas[i].datum_len) {
1590 + rprintf(FERROR, "Abbreviated xattr value, %s, not received for %s\n",
1591 + rxas[i].name, full_fname(fname));
1592 + exit_cleanup(RERR_STREAMIO);
1594 + status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len);
1596 + rsyserr(FERROR, errno,
1597 + "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
1598 + fname, rxas[i].name);
1603 + /* Remove any extraneous names. */
1604 + for (name = namebuf; list_len > 0; name += name_len) {
1605 + name_len = strlen(name) + 1;
1606 + list_len -= name_len;
1608 +#ifdef HAVE_LINUX_XATTRS
1609 + /* We always ignore the system namespace, and non-root
1610 + * ignores everything but the user namespace. */
1611 + if (am_root ? HAS_PREFIX(name, SYSTEM_PREFIX)
1612 + : !HAS_PREFIX(name, USER_PREFIX))
1616 + for (i = 0; i < xalp->count; i++) {
1617 + if (strcmp(name, rxas[i].name) == 0)
1620 + if (i == xalp->count) {
1621 + int status = sys_lremovexattr(fname, name);
1623 + rsyserr(FERROR, errno,
1624 + "rsync_xal_clear: lremovexattr(\"%s\",\"%s\") failed",
1634 +/* Set extended attributes on indicated filename. */
1635 +int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
1638 + item_list *lst = rsync_xal_l.items;
1641 + return 1; /* FIXME: --dry-run needs to compute this value */
1643 + if (read_only || list_only) {
1648 + ndx = F_XATTR(file);
1649 + return rsync_xal_set(fname, lst + ndx);
1652 +#endif /* SUPPORT_XATTRS */