In get_xattr_data(): replace the input value of *len_ptr with its
[rsync/rsync-patches.git] / xattrs.diff
CommitLineData
64e98de1 1This patch adds support for extended attributes.
bbdff76a 2
03019e41 3To use this patch, run these commands for a successful build:
bbdff76a 4
03019e41 5 patch -p1 <patches/xattrs.diff
27e96866 6 ./prepare-source
64e98de1 7 ./configure
bbdff76a
WD
8 make
9
64e98de1 10CAUTION: this patch has been recently reworked, and needs more testing!
03019e41 11
0f6fb3c8
WD
12TODO:
13
64e98de1
WD
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.
0f6fb3c8 17
9a7eef96
WD
18--- old/Makefile.in
19+++ new/Makefile.in
1ed0b5c9 20@@ -28,13 +28,13 @@ VERSION=@VERSION@
bbdff76a
WD
21
22 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
87a38eea 23 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
bbdff76a 24- lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
64e98de1 25+ lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
4bf6f8c7
WD
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
590329e5
WD
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 \
610969d1 31- fileio.o batch.o clientname.o chmod.o acls.o
64e98de1 32+ fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
bbdff76a
WD
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 \
9a7eef96
WD
36--- old/backup.c
37+++ new/backup.c
526184b9 38@@ -23,6 +23,7 @@
70891d26 39 extern int verbose;
93d6ca6d
WD
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;
526184b9 46@@ -134,6 +135,9 @@ static int make_bak_dir(char *fullpath)
0f6fb3c8
WD
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
64e98de1 56@@ -143,6 +147,13 @@ static int make_bak_dir(char *fullpath)
4306c620 57 free_acl(&sx);
0f6fb3c8 58 }
09cc6650
WD
59 #endif
60+#ifdef SUPPORT_XATTRS
0f6fb3c8
WD
61+ if (preserve_xattrs) {
62+ get_xattr(rel, &sx);
63+ cache_xattr(file, &sx);
64e98de1 64+ free_xattr(&sx);
0f6fb3c8 65+ }
09cc6650 66+#endif
0f6fb3c8
WD
67 set_file_attrs(fullpath, file, NULL, 0);
68 free(file);
bbdff76a 69 }
64e98de1 70@@ -194,6 +205,9 @@ static int keep_backup(const char *fname
0f6fb3c8
WD
71 #ifdef SUPPORT_ACLS
72 sx.acc_acl = sx.def_acl = NULL;
09cc6650
WD
73 #endif
74+#ifdef SUPPORT_XATTRS
0f6fb3c8 75+ sx.xattr = NULL;
09cc6650 76+#endif
bbdff76a 77
0f6fb3c8
WD
78 if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
79 return 1; /* the file could have disappeared */
64e98de1 80@@ -210,6 +224,13 @@ static int keep_backup(const char *fname
4306c620 81 free_acl(&sx);
0f6fb3c8 82 }
09cc6650
WD
83 #endif
84+#ifdef SUPPORT_XATTRS
0f6fb3c8
WD
85+ if (preserve_xattrs) {
86+ get_xattr(fname, &sx);
87+ cache_xattr(file, &sx);
64e98de1 88+ free_xattr(&sx);
0f6fb3c8 89+ }
09cc6650 90+#endif
bbdff76a 91
0f6fb3c8
WD
92 /* Check to see if this is a device file, or link */
93 if ((am_root && preserve_devices && IS_DEVICE(file->mode))
fdf967c7
WD
94--- old/compat.c
95+++ new/compat.c
64e98de1
WD
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)
7e27b6c0 105 preserve_gid = ++file_extra_cnt;
fdf967c7 106 if (preserve_acls && !am_sender)
7e27b6c0 107 preserve_acls = ++file_extra_cnt;
64e98de1 108+ if (preserve_xattrs)
7e27b6c0 109+ preserve_xattrs = ++file_extra_cnt;
fdf967c7
WD
110
111 if (remote_protocol == 0) {
112 if (!read_batch)
64e98de1
WD
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)) {
9a7eef96
WD
127--- old/configure.in
128+++ new/configure.in
64e98de1 129@@ -891,6 +891,46 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
4306c620
WD
130 esac
131 fi
bbdff76a
WD
132
133+AC_CHECK_HEADERS(attr/xattr.h)
49de5440 134+AC_CHECK_HEADERS(sys/xattr.h)
742a4103 135+AC_CHECK_HEADERS(sys/extattr.h)
64e98de1
WD
136+
137+#################################################
138+# check for extended attribute support
bbdff76a 139+AC_MSG_CHECKING(whether to support extended attributes)
f787c90c 140+AC_ARG_ENABLE(xattr-support,
64e98de1
WD
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
bbdff76a
WD
172+
173 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
174 AC_OUTPUT
175
9a7eef96
WD
176--- old/flist.c
177+++ new/flist.c
87a38eea 178@@ -43,6 +43,7 @@ extern int one_file_system;
93d6ca6d
WD
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;
87a38eea 186@@ -888,6 +889,10 @@ static struct file_struct *recv_file_ent
4306c620 187 if (preserve_acls && !S_ISLNK(mode))
0f6fb3c8
WD
188 receive_acl(file, f);
189 #endif
190+#ifdef SUPPORT_XATTRS
191+ if (preserve_xattrs)
192+ receive_xattr(file, f );
193+#endif
194
524e5ed5
WD
195 if (S_ISREG(mode) || S_ISLNK(mode))
196 stats.total_size += file_length;
87a38eea 197@@ -1160,7 +1165,7 @@ static struct file_struct *send_file_nam
fc068916 198 int flags, int filter_flags)
a071aea2
WD
199 {
200 struct file_struct *file;
201-#ifdef SUPPORT_ACLS
202+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
203 statx sx;
204 #endif
205
87a38eea 206@@ -1179,6 +1184,13 @@ static struct file_struct *send_file_nam
0f6fb3c8
WD
207 return NULL;
208 }
09cc6650
WD
209 #endif
210+#ifdef SUPPORT_XATTRS
e70fd6a8 211+ if (preserve_xattrs && f >= 0) {
0f6fb3c8
WD
212+ sx.xattr = NULL;
213+ if (get_xattr(fname, &sx) < 0)
214+ return NULL;
215+ }
09cc6650 216+#endif
bbdff76a 217
a2f9a486
WD
218 maybe_emit_filelist_progress(flist->count + flist_count_offset);
219
64e98de1 220@@ -1192,6 +1204,12 @@ static struct file_struct *send_file_nam
4306c620
WD
221 free_acl(&sx);
222 }
09cc6650
WD
223 #endif
224+#ifdef SUPPORT_XATTRS
64e98de1
WD
225+ if (preserve_xattrs) {
226+ F_XATTR(file) = send_xattr(&sx, f);
227+ free_xattr(&sx);
228+ }
09cc6650 229+#endif
7c15d5aa 230 }
bbdff76a
WD
231 return file;
232 }
64e98de1
WD
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
bdeab877 324@@ -0,0 +1,135 @@
dc52bc1a
WD
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+ */
bbdff76a
WD
345+
346+#include "rsync.h"
64e98de1 347+#include "sysxattrs.h"
bbdff76a 348+
56473cb9
WD
349+#ifdef SUPPORT_XATTRS
350+
09cc6650 351+#if defined HAVE_LINUX_XATTRS
bbdff76a
WD
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+
8627d8a2
WD
358+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
359+{
0bac098f 360+ return fgetxattr(filedes, name, value, size);
8627d8a2
WD
361+}
362+
742a4103 363+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
bbdff76a 364+{
742a4103 365+ return lsetxattr(path, name, value, size, 0);
bbdff76a
WD
366+}
367+
a74926e4
WD
368+int sys_lremovexattr(const char *path, const char *name)
369+{
370+ return lremovexattr(path, name);
371+}
372+
bbdff76a
WD
373+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
374+{
375+ return llistxattr(path, list, size);
376+}
377+
49de5440
WD
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+
8627d8a2
WD
385+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
386+{
0bac098f 387+ return fgetxattr(filedes, name, value, size, 0, 0);
8627d8a2
WD
388+}
389+
742a4103 390+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
49de5440 391+{
742a4103 392+ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
49de5440
WD
393+}
394+
a74926e4
WD
395+int sys_lremovexattr(const char *path, const char *name)
396+{
397+ return removexattr(path, name, XATTR_NOFOLLOW);
398+}
399+
49de5440
WD
400+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
401+{
402+ return listxattr(path, list, size, XATTR_NOFOLLOW);
403+}
404+
742a4103
WD
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. */
bdeab877 439+ for (off = 0; off < len; off += keylen + 1) {
742a4103
WD
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);
bdeab877 447+ list[off+keylen] = '\0';
742a4103
WD
448+ }
449+
450+ return len;
451+}
452+
bbdff76a
WD
453+#else
454+
56473cb9
WD
455+#error You need to create xattr compatibility functions.
456+
457+#endif
458+
459+#endif /* SUPPORT_XATTRS */
64e98de1
WD
460--- old/lib/sysxattrs.h
461+++ new/lib/sysxattrs.h
742a4103 462@@ -0,0 +1,26 @@
49de5440 463+#ifdef SUPPORT_XATTRS
8889f85e 464+
49de5440 465+#if defined HAVE_ATTR_XATTR_H
0f6fb3c8 466+#include <attr/xattr.h>
49de5440
WD
467+#elif defined HAVE_SYS_XATTR_H
468+#include <sys/xattr.h>
742a4103
WD
469+#elif defined HAVE_SYS_EXTATTR_H
470+#include <sys/extattr.h>
49de5440 471+#endif
bbdff76a 472+
a74926e4
WD
473+/* Linux 2.4 does not define this as a distinct errno value: */
474+#ifndef ENOATTR
475+#define ENOATTR ENODATA
476+#endif
477+
bbdff76a 478+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
8627d8a2 479+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
742a4103 480+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
a74926e4 481+int sys_lremovexattr(const char *path, const char *name);
bbdff76a
WD
482+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
483+
484+#else
485+
8889f85e 486+/* No xattrs available */
49de5440
WD
487+
488+#endif
64e98de1
WD
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);
9a7eef96
WD
538--- old/options.c
539+++ new/options.c
526184b9 540@@ -47,6 +47,7 @@ int copy_links = 0;
bbdff76a
WD
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;
4a65fe72 546 int preserve_executability = 0;
bbdff76a 547 int preserve_devices = 0;
87a38eea 548@@ -201,6 +202,7 @@ static void print_rsync_version(enum log
bbdff76a
WD
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;
87a38eea 556@@ -220,7 +222,9 @@ static void print_rsync_version(enum log
bbdff76a
WD
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
87a38eea 567@@ -239,8 +243,8 @@ static void print_rsync_version(enum log
5e3c6c93
WD
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);
56473cb9 575
5e3c6c93
WD
576 #ifdef MAINTAINER_MODE
577 rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
87a38eea 578@@ -286,7 +290,7 @@ void usage(enum logcode F)
1b57ecb0 579 rprintf(F," -q, --quiet suppress non-error messages\n");
4959107f 580 rprintf(F," --no-motd suppress daemon-mode MOTD (see manpage caveat)\n");
1b57ecb0
WD
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");
87a38eea 587@@ -311,6 +315,9 @@ void usage(enum logcode F)
3610c43c 588 #ifdef SUPPORT_ACLS
bbdff76a 589 rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
3610c43c
WD
590 #endif
591+#ifdef SUPPORT_XATTRS
bbdff76a 592+ rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n");
3610c43c 593+#endif
4a65fe72 594 rprintf(F," -o, --owner preserve owner (super-user only)\n");
bbdff76a 595 rprintf(F," -g, --group preserve group\n");
1ed0b5c9 596 rprintf(F," --devices preserve device files (super-user only)\n");
87a38eea 597@@ -438,6 +445,9 @@ static struct poptOption long_options[]
489b0a72
WD
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 },
87a38eea 607@@ -1126,6 +1136,17 @@ int parse_arguments(int *argc, const cha
489b0a72 608 return 0;
3610c43c 609 #endif
bbdff76a
WD
610
611+ case 'X':
612+#ifdef SUPPORT_XATTRS
613+ preserve_xattrs = 1;
614+ preserve_perms = 1;
489b0a72 615+ break;
bbdff76a
WD
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;
d2dc8a18 621+#endif
bbdff76a
WD
622
623 default:
624 /* A large opt value means that set_refuse_options()
87a38eea 625@@ -1590,6 +1611,10 @@ void server_options(char **args,int *arg
bbdff76a
WD
626 if (preserve_acls)
627 argstr[x++] = 'A';
3610c43c
WD
628 #endif
629+#ifdef SUPPORT_XATTRS
bbdff76a
WD
630+ if (preserve_xattrs)
631+ argstr[x++] = 'X';
3610c43c 632+#endif
fc068916
WD
633 if (recurse)
634 argstr[x++] = 'r';
635 if (always_checksum)
64e98de1
WD
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) {
9a7eef96
WD
675--- old/rsync.c
676+++ new/rsync.c
526184b9 677@@ -32,6 +32,7 @@
ff318e90 678 extern int verbose;
93d6ca6d 679 extern int dry_run;
93d6ca6d
WD
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;
64e98de1
WD
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
071c5b19
WD
721 if (daemon_chmod_modes && !S_ISLNK(new_mode))
722 new_mode = tweak_mode(new_mode, daemon_chmod_modes);
723
09cc6650 724+#ifdef SUPPORT_XATTRS
64e98de1
WD
725+ if (preserve_xattrs && !am_generator)
726+ set_xattr(fname, file, sxp);
09cc6650 727+#endif
071c5b19
WD
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.
64e98de1
WD
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);
09cc6650 741 #endif
64e98de1
WD
742+#ifdef SUPPORT_XATTRS
743+ if (preserve_xattrs)
744+ free_xattr(&sx2);
bbdff76a 745+#endif
64e98de1
WD
746+ }
747 return updated;
748 }
0f6fb3c8 749
64e98de1
WD
750--- old/rsync.h
751+++ new/rsync.h
752@@ -569,6 +569,7 @@ extern int file_extra_cnt;
6eba67ec
WD
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))
64e98de1
WD
760@@ -601,7 +602,8 @@ extern int preserve_acls;
761 /* When the associated option is on, all entries will have these present: */
fc068916
WD
762 #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
763 #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
64e98de1
WD
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
70891d26 767
8aec0853 768 /* These items are per-entry optional and mutally exclusive: */
fdf967c7 769 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
64e98de1 770@@ -793,9 +795,13 @@ typedef struct {
0f6fb3c8
WD
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;
bbdff76a 776+#endif
0f6fb3c8 777 } statx;
bbdff76a 778
0f6fb3c8 779 #define ACL_READY(sx) ((sx).acc_acl != NULL)
64e98de1
WD
780+#define XATTR_READY(sx) ((sx).xattr != NULL)
781
782 #include "proto.h"
783
9a7eef96
WD
784--- old/rsync.yo
785+++ new/rsync.yo
4959107f 786@@ -301,7 +301,7 @@ to the detailed description below for a
1b57ecb0 787 -q, --quiet suppress non-error messages
4959107f 788 --no-motd suppress daemon-mode MOTD (see caveat)
1b57ecb0
WD
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
4959107f 795@@ -324,6 +324,7 @@ to the detailed description below for a
4a65fe72 796 -E, --executability preserve executability
063cf77b 797 --chmod=CHMOD affect file and/or directory permissions
4306c620
WD
798 -A, --acls preserve ACLs (implies -p)
799+ -X, --xattrs preserve extended attrs (implies -p)
4a65fe72 800 -o, --owner preserve owner (super-user only)
bbdff76a 801 -g, --group preserve group
1ed0b5c9 802 --devices preserve device files (super-user only)
4306c620
WD
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).
bbdff76a
WD
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+
4a65fe72
WD
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
64e98de1
WD
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
5b0f8e16 907@@ -0,0 +1,56 @@
64e98de1
WD
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+
64e98de1
WD
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
9c18601b 966@@ -0,0 +1,688 @@
dc52bc1a
WD
967+/*
968+ * Extended Attribute support for rsync.
0f6fb3c8 969+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
dc52bc1a
WD
970+ *
971+ * Copyright (C) 2004 Red Hat, Inc.
64e98de1 972+ * Copyright (C) 2006, 2007 Wayne Davison
dc52bc1a
WD
973+ *
974+ * This program is free software; you can redistribute it and/or modify
64e98de1
WD
975+ * it under the terms of the GNU General Public License version 2 as
976+ * published by the Free Software Foundation.
dc52bc1a
WD
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+ */
bbdff76a
WD
987+
988+#include "rsync.h"
64e98de1 989+#include "lib/sysxattrs.h"
bbdff76a
WD
990+
991+#ifdef SUPPORT_XATTRS
992+
bbdff76a 993+extern int dry_run;
3f24a3a8 994+extern int am_root;
64e98de1 995+extern int am_sender;
8889f85e
WD
996+extern int read_only;
997+extern int list_only;
64e98de1 998+extern int checksum_seed;
bbdff76a
WD
999+
1000+#define RSYNC_XAL_INITIAL 5
1001+#define RSYNC_XAL_LIST_INITIAL 100
1002+
64e98de1
WD
1003+#define MAX_FULL_DATUM 32
1004+
922f4409
WD
1005+#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
1006+ && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
1007+
3f24a3a8 1008+#define USER_PREFIX "user."
27502d8d
WD
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
3f24a3a8 1019+
bbdff76a 1020+typedef struct {
27502d8d
WD
1021+ char *datum, *name;
1022+ size_t datum_len, name_len;
bbdff76a
WD
1023+} rsync_xa;
1024+
bbdff76a
WD
1025+static size_t namebuf_len = 0;
1026+static char *namebuf = NULL;
1027+
0f6fb3c8
WD
1028+static item_list empty_xattr = EMPTY_ITEM_LIST;
1029+static item_list rsync_xal_l = EMPTY_ITEM_LIST;
bbdff76a
WD
1030+
1031+/* ------------------------------------------------------------------------- */
1032+
0f6fb3c8 1033+static void rsync_xal_free(item_list *xalp)
bbdff76a
WD
1034+{
1035+ size_t i;
0f6fb3c8 1036+ rsync_xa *rxas = xalp->items;
bbdff76a 1037+
0f6fb3c8 1038+ for (i = 0; i < xalp->count; i++) {
27502d8d
WD
1039+ free(rxas[i].datum);
1040+ /*free(rxas[i].name);*/
bbdff76a 1041+ }
0f6fb3c8 1042+ xalp->count = 0;
bbdff76a
WD
1043+}
1044+
0f6fb3c8 1045+void free_xattr(statx *sxp)
bbdff76a 1046+{
64e98de1
WD
1047+ if (!sxp->xattr)
1048+ return;
0f6fb3c8
WD
1049+ rsync_xal_free(sxp->xattr);
1050+ free(sxp->xattr);
1051+ sxp->xattr = NULL;
1052+}
bbdff76a 1053+
0f6fb3c8
WD
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;
bbdff76a
WD
1058+ return strcmp(xa1->name, xa2->name);
1059+}
1060+
64e98de1 1061+static ssize_t get_xattr_names(const char *fname)
bbdff76a 1062+{
526184b9 1063+ ssize_t list_len;
bbdff76a
WD
1064+
1065+ if (!namebuf) {
49de5440 1066+ namebuf_len = 1024;
bbdff76a 1067+ namebuf = new_array(char, namebuf_len);
49de5440 1068+ if (!namebuf)
64e98de1 1069+ out_of_memory("get_xattr_names");
bbdff76a
WD
1070+ }
1071+
49de5440 1072+ /* The length returned includes all the '\0' terminators. */
27502d8d
WD
1073+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
1074+ if (list_len > (ssize_t)namebuf_len) {
1075+ list_len = -1;
bbdff76a
WD
1076+ errno = ERANGE;
1077+ }
27502d8d 1078+ if (list_len < 0) {
bbdff76a 1079+ if (errno == ENOTSUP)
0f6fb3c8 1080+ return 0;
bbdff76a 1081+ if (errno == ERANGE) {
27502d8d
WD
1082+ list_len = sys_llistxattr(fname, NULL, 0);
1083+ if (list_len < 0) {
892d2287 1084+ rsyserr(FERROR, errno,
64e98de1 1085+ "get_xattr_names: llistxattr(\"%s\",0) failed",
0f6fb3c8 1086+ fname);
e5754c5f 1087+ return -1;
bbdff76a 1088+ }
64e98de1
WD
1089+ if (namebuf_len)
1090+ free(namebuf);
27502d8d 1091+ namebuf_len = list_len + 1024;
64e98de1
WD
1092+ namebuf = new_array(char, namebuf_len);
1093+ if (!namebuf)
1094+ out_of_memory("get_xattr_names");
27502d8d
WD
1095+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
1096+ if (list_len < 0) {
0f6fb3c8 1097+ rsyserr(FERROR, errno,
64e98de1 1098+ "get_xattr_names: llistxattr(\"%s\",%ld) failed",
892d2287 1099+ fname, (long)namebuf_len);
e5754c5f 1100+ return -1;
bbdff76a
WD
1101+ }
1102+ } else {
0f6fb3c8 1103+ rsyserr(FERROR, errno,
64e98de1 1104+ "get_xattr_names: llistxattr(\"%s\",%ld) failed",
892d2287 1105+ fname, (long)namebuf_len);
e5754c5f 1106+ return -1;
bbdff76a
WD
1107+ }
1108+ }
27502d8d 1109+
526184b9
WD
1110+ return list_len;
1111+}
1112+
64e98de1
WD
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+
64e98de1
WD
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+
9c18601b
WD
1134+ *len_ptr = datum_len;
1135+
64e98de1
WD
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+
526184b9
WD
1157+static int rsync_xal_get(const char *fname, item_list *xalp)
1158+{
64e98de1
WD
1159+ ssize_t list_len, name_len;
1160+ size_t datum_len, name_offset;
526184b9 1161+ char *name, *ptr;
64e98de1 1162+ int user_only = am_sender ? 0 : !am_root;
526184b9
WD
1163+
1164+ /* This puts the name list into the "namebuf" buffer. */
64e98de1 1165+ if ((list_len = get_xattr_names(fname)) < 0)
526184b9
WD
1166+ return -1;
1167+
892d2287 1168+ for (name = namebuf; list_len > 0; name += name_len) {
3f24a3a8 1169+ rsync_xa *rxas;
bbdff76a 1170+
27502d8d 1171+ name_len = strlen(name) + 1;
892d2287 1172+ list_len -= name_len;
3f24a3a8
WD
1173+
1174+#ifdef HAVE_LINUX_XATTRS
64e98de1
WD
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))
3f24a3a8
WD
1179+ continue;
1180+#endif
1181+
64e98de1
WD
1182+ datum_len = name_len; /* Pass extra size to get_xattr_data() */
1183+ if (!(ptr = get_xattr_data(fname, name, &datum_len)))
49de5440 1184+ return -1;
64e98de1
WD
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+
27502d8d 1200+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
64e98de1
WD
1201+ rxas->name = ptr + name_offset;
1202+ memcpy(rxas->name, name, name_len);
b54d5ec2 1203+ rxas->datum = ptr;
27502d8d
WD
1204+ rxas->name_len = name_len;
1205+ rxas->datum_len = datum_len;
bbdff76a 1206+ }
0f6fb3c8
WD
1207+ if (xalp->count > 1)
1208+ qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
e5754c5f 1209+ return 0;
bbdff76a
WD
1210+}
1211+
0f6fb3c8
WD
1212+/* Read the xattr(s) for this filename. */
1213+int get_xattr(const char *fname, statx *sxp)
bbdff76a 1214+{
0f6fb3c8
WD
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;
bbdff76a
WD
1222+}
1223+
0f6fb3c8 1224+static int find_matching_xattr(item_list *xalp)
bbdff76a 1225+{
0f6fb3c8
WD
1226+ size_t i, j;
1227+ item_list *lst = rsync_xal_l.items;
bbdff76a
WD
1228+
1229+ for (i = 0; i < rsync_xal_l.count; i++) {
0f6fb3c8
WD
1230+ rsync_xa *rxas1 = lst[i].items;
1231+ rsync_xa *rxas2 = xalp->items;
1232+
bbdff76a 1233+ /* Wrong number of elements? */
0f6fb3c8 1234+ if (lst[i].count != xalp->count)
bbdff76a
WD
1235+ continue;
1236+ /* any elements different? */
0f6fb3c8
WD
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
64e98de1 1240+ || strcmp(rxas1[j].name, rxas2[j].name))
bbdff76a 1241+ break;
64e98de1
WD
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+ }
bbdff76a
WD
1252+ }
1253+ /* no differences found. This is The One! */
0f6fb3c8
WD
1254+ if (j == xalp->count)
1255+ return i;
bbdff76a 1256+ }
0f6fb3c8
WD
1257+
1258+ return -1;
bbdff76a
WD
1259+}
1260+
0f6fb3c8
WD
1261+/* Store *xalp on the end of rsync_xal_l */
1262+static void rsync_xal_store(item_list *xalp)
bbdff76a 1263+{
0f6fb3c8 1264+ item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
de565f59
WD
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. */
49de5440
WD
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));
0f6fb3c8
WD
1270+ new_lst->count = xalp->count;
1271+ xalp->count = 0;
bbdff76a
WD
1272+}
1273+
0f6fb3c8 1274+/* Send the make_xattr()-generated xattr list for this flist entry. */
64e98de1 1275+int send_xattr(statx *sxp, int f)
bbdff76a 1276+{
0f6fb3c8 1277+ int ndx = find_matching_xattr(sxp->xattr);
64e98de1
WD
1278+
1279+ /* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
1280+ write_abbrevint(f, ndx + 1);
1281+
1282+ if (ndx < 0) {
bbdff76a 1283+ rsync_xa *rxa;
0f6fb3c8 1284+ int count = sxp->xattr->count;
64e98de1 1285+ write_abbrevint(f, count);
0f6fb3c8 1286+ for (rxa = sxp->xattr->items; count--; rxa++) {
27502d8d 1287+#ifdef HAVE_LINUX_XATTRS
64e98de1
WD
1288+ write_abbrevint(f, rxa->name_len);
1289+ write_abbrevint(f, rxa->datum_len);
27502d8d
WD
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. */
dfe2b257
WD
1294+ if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
1295+ && rxa->name[RPRE_LEN] != '%') {
64e98de1
WD
1296+ write_abbrevint(f, rxa->name_len - RPRE_LEN);
1297+ write_abbrevint(f, rxa->datum_len);
27502d8d
WD
1298+ write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
1299+ } else {
64e98de1
WD
1300+ write_abbrevint(f, rxa->name_len + UPRE_LEN);
1301+ write_abbrevint(f, rxa->datum_len);
3f24a3a8 1302+ write_buf(f, USER_PREFIX, UPRE_LEN);
27502d8d 1303+ write_buf(f, rxa->name, rxa->name_len);
3f24a3a8 1304+ }
27502d8d 1305+#endif
64e98de1
WD
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);
bbdff76a 1310+ }
64e98de1 1311+ ndx = rsync_xal_l.count; /* pre-incremented count */
49de5440 1312+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
bbdff76a 1313+ }
64e98de1
WD
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
9c18601b 1392+ * (with a non-NULL fname), we send all the extra xattr data it needs. */
64e98de1
WD
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+
9c18601b
WD
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. */
64e98de1
WD
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+ }
bbdff76a
WD
1474+}
1475+
bbdff76a 1476+/* ------------------------------------------------------------------------- */
bbdff76a 1477+
0f6fb3c8 1478+/* receive and build the rsync_xattr_lists */
bbdff76a
WD
1479+void receive_xattr(struct file_struct *file, int f)
1480+{
0f6fb3c8 1481+ static item_list temp_xattr = EMPTY_ITEM_LIST;
64e98de1
WD
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;
bcecb5e3 1507+#ifdef HAVE_LINUX_XATTRS
64e98de1 1508+ size_t extra_len = 0;
bcecb5e3 1509+#else
64e98de1
WD
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 */
27502d8d 1513+#endif
64e98de1
WD
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+ }
3f24a3a8 1527+#ifdef HAVE_LINUX_XATTRS
64e98de1
WD
1528+ /* Non-root can only save the user namespace. */
1529+ if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
1530+ free(ptr);
1531+ continue;
bbdff76a 1532+ }
64e98de1
WD
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;
bbdff76a 1547+ }
64e98de1
WD
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;
bbdff76a 1554+ }
bbdff76a 1555+
64e98de1
WD
1556+ ndx = rsync_xal_l.count; /* pre-incremented count */
1557+ rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
1558+
70891d26 1559+ F_XATTR(file) = ndx;
bbdff76a
WD
1560+}
1561+
0f6fb3c8
WD
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)
bbdff76a 1565+{
0f6fb3c8 1566+ int ndx;
bbdff76a 1567+
0f6fb3c8
WD
1568+ if (!sxp->xattr)
1569+ return;
bbdff76a 1570+
0f6fb3c8 1571+ ndx = find_matching_xattr(sxp->xattr);
64e98de1 1572+ if (ndx < 0)
49de5440 1573+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
bbdff76a 1574+
70891d26 1575+ F_XATTR(file) = ndx;
bbdff76a
WD
1576+}
1577+
0f6fb3c8 1578+static int rsync_xal_set(const char *fname, item_list *xalp)
bbdff76a 1579+{
0f6fb3c8 1580+ rsync_xa *rxas = xalp->items;
526184b9 1581+ ssize_t list_len;
0f6fb3c8 1582+ size_t i;
526184b9 1583+ char *name;
64e98de1 1584+ int name_len, status, ret = 0;
526184b9
WD
1585+
1586+ /* This puts the current name list into the "namebuf" buffer. */
64e98de1 1587+ if ((list_len = get_xattr_names(fname)) < 0)
526184b9 1588+ return -1;
bbdff76a 1589+
0f6fb3c8 1590+ for (i = 0; i < xalp->count; i++) {
64e98de1
WD
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);
0f6fb3c8 1597+ if (status < 0) {
892d2287
WD
1598+ rsyserr(FERROR, errno,
1599+ "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
0f6fb3c8
WD
1600+ fname, rxas[i].name);
1601+ ret = -1;
1602+ }
bbdff76a 1603+ }
526184b9
WD
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+
e3cd70c8
WD
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+
526184b9
WD
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+
0f6fb3c8 1633+ return ret;
bbdff76a
WD
1634+}
1635+
0f6fb3c8
WD
1636+/* Set extended attributes on indicated filename. */
1637+int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
bbdff76a 1638+{
0f6fb3c8 1639+ int ndx;
0f6fb3c8 1640+ item_list *lst = rsync_xal_l.items;
bbdff76a 1641+
93d6ca6d
WD
1642+ if (dry_run)
1643+ return 1; /* FIXME: --dry-run needs to compute this value */
1644+
8889f85e
WD
1645+ if (read_only || list_only) {
1646+ errno = EROFS;
1647+ return -1;
1648+ }
1649+
70891d26 1650+ ndx = F_XATTR(file);
64e98de1 1651+ return rsync_xal_set(fname, lst + ndx);
bbdff76a
WD
1652+}
1653+
1654+#endif /* SUPPORT_XATTRS */