This patch will make rsync 3.0.x able to exchange ACLs with an older
[rsync/rsync-patches.git] / xattrs.diff
CommitLineData
03019e41
WD
1This patch adds basic support for extended attributes. It works, but there
2are some things that still needs to be improved (see TODO list below).
bbdff76a 3
03019e41 4To use this patch, run these commands for a successful build:
bbdff76a 5
03019e41
WD
6 patch -p1 <patches/acls.diff
7 patch -p1 <patches/xattrs.diff
27e96866 8 ./prepare-source
f787c90c 9 ./configure --enable-acl-support --enable-xattr-support
bbdff76a
WD
10 make
11
03019e41
WD
12Alternately, if you don't want ACL support, configure it this way:
13
14 ./configure --enable-xattr-support
15
0f6fb3c8
WD
16TODO:
17
d2dc8a18
WD
18 - This patch needs to more efficiently handle large xattrs, especially when
19 they're unchanged.
0f6fb3c8
WD
20
21 - We need to affect the itemized output to know when xattrs are being updated.
22
23 - We need to affect the --link-dest option to avoid hard-linking two files
24 that differ in their xattrs (when --xattrs was specified).
25
9a7eef96
WD
26--- old/Makefile.in
27+++ new/Makefile.in
1ed0b5c9 28@@ -28,13 +28,13 @@ VERSION=@VERSION@
bbdff76a
WD
29
30 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
31 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \
32- lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
33+ lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattr.o @LIBOBJS@
4bf6f8c7
WD
34 ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
35 zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
590329e5
WD
36 OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
37 util.o main.o checksum.o match.o syscall.o log.o backup.o
38 OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o \
610969d1
WD
39- fileio.o batch.o clientname.o chmod.o acls.o
40+ fileio.o batch.o clientname.o chmod.o acls.o xattr.o
bbdff76a
WD
41 OBJS3=progress.o pipe.o
42 DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
43 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
9a7eef96
WD
44--- old/backup.c
45+++ new/backup.c
526184b9 46@@ -23,6 +23,7 @@
70891d26 47 extern int verbose;
93d6ca6d
WD
48 extern int am_root;
49 extern int preserve_acls;
50+extern int preserve_xattrs;
51 extern int preserve_devices;
52 extern int preserve_specials;
53 extern int preserve_links;
526184b9 54@@ -134,6 +135,9 @@ static int make_bak_dir(char *fullpath)
0f6fb3c8
WD
55 #ifdef SUPPORT_ACLS
56 sx.acc_acl = sx.def_acl = NULL;
57 #endif
58+#ifdef SUPPORT_XATTRS
59+ sx.xattr = NULL;
60+#endif
61 if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
62 continue;
63 #ifdef SUPPORT_ACLS
526184b9 64@@ -142,6 +146,12 @@ static int make_bak_dir(char *fullpath)
0f6fb3c8
WD
65 cache_acl(file, &sx);
66 }
09cc6650
WD
67 #endif
68+#ifdef SUPPORT_XATTRS
0f6fb3c8
WD
69+ if (preserve_xattrs) {
70+ get_xattr(rel, &sx);
71+ cache_xattr(file, &sx);
72+ }
09cc6650 73+#endif
0f6fb3c8
WD
74 set_file_attrs(fullpath, file, NULL, 0);
75 free(file);
bbdff76a 76 }
526184b9 77@@ -193,6 +203,9 @@ static int keep_backup(const char *fname
0f6fb3c8
WD
78 #ifdef SUPPORT_ACLS
79 sx.acc_acl = sx.def_acl = NULL;
09cc6650
WD
80 #endif
81+#ifdef SUPPORT_XATTRS
0f6fb3c8 82+ sx.xattr = NULL;
09cc6650 83+#endif
bbdff76a 84
0f6fb3c8
WD
85 if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
86 return 1; /* the file could have disappeared */
526184b9 87@@ -208,6 +221,12 @@ static int keep_backup(const char *fname
0f6fb3c8
WD
88 cache_acl(file, &sx);
89 }
09cc6650
WD
90 #endif
91+#ifdef SUPPORT_XATTRS
0f6fb3c8
WD
92+ if (preserve_xattrs) {
93+ get_xattr(fname, &sx);
94+ cache_xattr(file, &sx);
95+ }
09cc6650 96+#endif
bbdff76a 97
0f6fb3c8
WD
98 /* Check to see if this is a device file, or link */
99 if ((am_root && preserve_devices && IS_DEVICE(file->mode))
fdf967c7
WD
100--- old/compat.c
101+++ new/compat.c
526184b9 102@@ -64,6 +64,8 @@ void setup_protocol(int f_out,int f_in)
7e27b6c0 103 preserve_gid = ++file_extra_cnt;
fdf967c7 104 if (preserve_acls && !am_sender)
7e27b6c0 105 preserve_acls = ++file_extra_cnt;
fdf967c7 106+ if (preserve_xattrs && !am_sender)
7e27b6c0 107+ preserve_xattrs = ++file_extra_cnt;
fdf967c7
WD
108
109 if (remote_protocol == 0) {
110 if (!read_batch)
9a7eef96
WD
111--- old/configure.in
112+++ new/configure.in
7e27b6c0 113@@ -883,6 +883,40 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
bbdff76a
WD
114 AC_MSG_RESULT(no)
115 )
116
117+AC_CHECK_HEADERS(attr/xattr.h)
49de5440 118+AC_CHECK_HEADERS(sys/xattr.h)
742a4103 119+AC_CHECK_HEADERS(sys/extattr.h)
bbdff76a 120+AC_MSG_CHECKING(whether to support extended attributes)
f787c90c
WD
121+AC_ARG_ENABLE(xattr-support,
122+AC_HELP_STRING([--enable-xattr-support], [Include extended attribute support (default=no)]),
3b05e91f 123+[ case "$enableval" in
bbdff76a
WD
124+ yes)
125+ case "$host_os" in
126+ *linux*)
127+ AC_MSG_RESULT(Using Linux xattrs)
128+ AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs])
129+ ;;
49de5440
WD
130+ darwin*)
131+ AC_MSG_RESULT(Using OS X xattrs)
132+ AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs])
133+ ;;
742a4103
WD
134+ freebsd*)
135+ AC_MSG_RESULT(Using FreeBSD extattrs)
136+ AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
137+ ;;
bbdff76a 138+ *)
49de5440 139+ AC_MSG_RESULT(Xattrs requested but not Linux or OS X. Good luck...)
bbdff76a
WD
140+ ;;
141+ esac
142+ ;;
143+ *)
144+ AC_MSG_RESULT(no)
742378cf 145+ AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes])
bbdff76a
WD
146+ esac ],
147+ AC_MSG_RESULT(no)
56473cb9 148+ AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes])
bbdff76a
WD
149+)
150+
151 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
152 AC_OUTPUT
153
9a7eef96
WD
154--- old/flist.c
155+++ new/flist.c
526184b9 156@@ -42,6 +42,7 @@ extern int one_file_system;
93d6ca6d
WD
157 extern int copy_dirlinks;
158 extern int keep_dirlinks;
159 extern int preserve_acls;
160+extern int preserve_xattrs;
161 extern int preserve_links;
162 extern int preserve_hard_links;
163 extern int preserve_devices;
526184b9 164@@ -864,6 +865,10 @@ static struct file_struct *recv_file_ent
0f6fb3c8
WD
165 if (preserve_acls)
166 receive_acl(file, f);
167 #endif
168+#ifdef SUPPORT_XATTRS
169+ if (preserve_xattrs)
170+ receive_xattr(file, f );
171+#endif
172
524e5ed5
WD
173 if (S_ISREG(mode) || S_ISLNK(mode))
174 stats.total_size += file_length;
526184b9 175@@ -1136,7 +1141,7 @@ static struct file_struct *send_file_nam
fc068916 176 int flags, int filter_flags)
a071aea2
WD
177 {
178 struct file_struct *file;
179-#ifdef SUPPORT_ACLS
180+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
181 statx sx;
182 #endif
183
526184b9 184@@ -1155,6 +1160,13 @@ static struct file_struct *send_file_nam
0f6fb3c8
WD
185 return NULL;
186 }
09cc6650
WD
187 #endif
188+#ifdef SUPPORT_XATTRS
e70fd6a8 189+ if (preserve_xattrs && f >= 0) {
0f6fb3c8
WD
190+ sx.xattr = NULL;
191+ if (get_xattr(fname, &sx) < 0)
192+ return NULL;
193+ }
09cc6650 194+#endif
bbdff76a 195
a2f9a486
WD
196 maybe_emit_filelist_progress(flist->count + flist_count_offset);
197
526184b9 198@@ -1166,6 +1178,10 @@ static struct file_struct *send_file_nam
7c15d5aa
WD
199 if (preserve_acls)
200 send_acl(&sx, f);
09cc6650
WD
201 #endif
202+#ifdef SUPPORT_XATTRS
7c15d5aa
WD
203+ if (preserve_xattrs)
204+ send_xattr(&sx, f);
09cc6650 205+#endif
7c15d5aa 206 }
bbdff76a
WD
207 return file;
208 }
9a7eef96
WD
209--- old/lib/sysxattr.c
210+++ new/lib/sysxattr.c
bdeab877 211@@ -0,0 +1,135 @@
dc52bc1a
WD
212+/*
213+ * Extended attribute support for rsync.
214+ *
215+ * Copyright (C) 2004 Red Hat, Inc.
216+ * Written by Jay Fenlason.
217+ *
218+ * This program is free software; you can redistribute it and/or modify
219+ * it under the terms of the GNU General Public License as published by
220+ * the Free Software Foundation; either version 2 of the License, or
221+ * (at your option) any later version.
222+ *
223+ * This program is distributed in the hope that it will be useful,
224+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
225+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
226+ * GNU General Public License for more details.
227+ *
228+ * You should have received a copy of the GNU General Public License along
229+ * with this program; if not, write to the Free Software Foundation, Inc.,
230+ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
231+ */
bbdff76a
WD
232+
233+#include "rsync.h"
0f6fb3c8 234+#include "sysxattr.h"
bbdff76a 235+
56473cb9
WD
236+#ifdef SUPPORT_XATTRS
237+
09cc6650 238+#if defined HAVE_LINUX_XATTRS
bbdff76a
WD
239+
240+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
241+{
242+ return lgetxattr(path, name, value, size);
243+}
244+
8627d8a2
WD
245+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
246+{
0bac098f 247+ return fgetxattr(filedes, name, value, size);
8627d8a2
WD
248+}
249+
742a4103 250+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
bbdff76a 251+{
742a4103 252+ return lsetxattr(path, name, value, size, 0);
bbdff76a
WD
253+}
254+
a74926e4
WD
255+int sys_lremovexattr(const char *path, const char *name)
256+{
257+ return lremovexattr(path, name);
258+}
259+
bbdff76a
WD
260+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
261+{
262+ return llistxattr(path, list, size);
263+}
264+
49de5440
WD
265+#elif HAVE_OSX_XATTRS
266+
267+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
268+{
269+ return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
270+}
271+
8627d8a2
WD
272+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
273+{
0bac098f 274+ return fgetxattr(filedes, name, value, size, 0, 0);
8627d8a2
WD
275+}
276+
742a4103 277+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
49de5440 278+{
742a4103 279+ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
49de5440
WD
280+}
281+
a74926e4
WD
282+int sys_lremovexattr(const char *path, const char *name)
283+{
284+ return removexattr(path, name, XATTR_NOFOLLOW);
285+}
286+
49de5440
WD
287+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
288+{
289+ return listxattr(path, list, size, XATTR_NOFOLLOW);
290+}
291+
742a4103
WD
292+#elif HAVE_FREEBSD_XATTRS
293+
294+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
295+{
296+ return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
297+}
298+
299+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
300+{
301+ return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
302+}
303+
304+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
305+{
306+ return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
307+}
308+
309+int sys_lremovexattr(const char *path, const char *name)
310+{
311+ return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
312+}
313+
314+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
315+{
316+ unsigned char keylen;
317+ ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
318+
319+ if (len <= 0 || (size_t)len > size)
320+ return len;
321+
322+ /* FreeBSD puts a single-byte length before each string, with no '\0'
323+ * terminator. We need to change this into a series of null-terminted
324+ * strings. Since the size is the same, we can simply transform the
325+ * output in place. */
bdeab877 326+ for (off = 0; off < len; off += keylen + 1) {
742a4103
WD
327+ keylen = ((unsigned char*)list)[off];
328+ if (off + keylen >= len) {
329+ /* Should be impossible, but kernel bugs happen! */
330+ errno = EINVAL;
331+ return -1;
332+ }
333+ memmove(list+off, list+off+1, keylen);
bdeab877 334+ list[off+keylen] = '\0';
742a4103
WD
335+ }
336+
337+ return len;
338+}
339+
bbdff76a
WD
340+#else
341+
56473cb9
WD
342+#error You need to create xattr compatibility functions.
343+
344+#endif
345+
346+#endif /* SUPPORT_XATTRS */
9a7eef96
WD
347--- old/lib/sysxattr.h
348+++ new/lib/sysxattr.h
742a4103 349@@ -0,0 +1,26 @@
49de5440 350+#ifdef SUPPORT_XATTRS
8889f85e 351+
49de5440 352+#if defined HAVE_ATTR_XATTR_H
0f6fb3c8 353+#include <attr/xattr.h>
49de5440
WD
354+#elif defined HAVE_SYS_XATTR_H
355+#include <sys/xattr.h>
742a4103
WD
356+#elif defined HAVE_SYS_EXTATTR_H
357+#include <sys/extattr.h>
49de5440 358+#endif
bbdff76a 359+
a74926e4
WD
360+/* Linux 2.4 does not define this as a distinct errno value: */
361+#ifndef ENOATTR
362+#define ENOATTR ENODATA
363+#endif
364+
bbdff76a 365+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
8627d8a2 366+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
742a4103 367+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
a74926e4 368+int sys_lremovexattr(const char *path, const char *name);
bbdff76a
WD
369+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
370+
371+#else
372+
8889f85e 373+/* No xattrs available */
49de5440
WD
374+
375+#endif
9a7eef96
WD
376--- old/options.c
377+++ new/options.c
526184b9 378@@ -47,6 +47,7 @@ int copy_links = 0;
bbdff76a
WD
379 int preserve_links = 0;
380 int preserve_hard_links = 0;
381 int preserve_acls = 0;
382+int preserve_xattrs = 0;
383 int preserve_perms = 0;
4a65fe72 384 int preserve_executability = 0;
bbdff76a 385 int preserve_devices = 0;
526184b9 386@@ -200,6 +201,7 @@ static void print_rsync_version(enum log
bbdff76a
WD
387 char const *have_inplace = "no ";
388 char const *hardlinks = "no ";
389 char const *acls = "no ";
390+ char const *xattrs = "no ";
391 char const *links = "no ";
392 char const *ipv6 = "no ";
393 STRUCT_STAT *dumstat;
526184b9 394@@ -219,7 +221,9 @@ static void print_rsync_version(enum log
bbdff76a
WD
395 #ifdef SUPPORT_ACLS
396 acls = "";
397 #endif
398-
399+#ifdef SUPPORT_XATTRS
400+ xattrs = "";
401+#endif
402 #ifdef SUPPORT_LINKS
403 links = "";
404 #endif
526184b9 405@@ -238,8 +242,8 @@ static void print_rsync_version(enum log
5e3c6c93
WD
406 (int)(sizeof (int64) * 8));
407 rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
408 got_socketpair, hardlinks, links, ipv6, have_inplace);
409- rprintf(f, " %sappend, %sACLs\n",
410- have_inplace, acls);
411+ rprintf(f, " %sappend, %sACLs, %sxattrs\n",
412+ have_inplace, acls, xattrs);
56473cb9 413
5e3c6c93
WD
414 #ifdef MAINTAINER_MODE
415 rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
526184b9 416@@ -285,7 +289,7 @@ void usage(enum logcode F)
1b57ecb0 417 rprintf(F," -q, --quiet suppress non-error messages\n");
4959107f 418 rprintf(F," --no-motd suppress daemon-mode MOTD (see manpage caveat)\n");
1b57ecb0
WD
419 rprintf(F," -c, --checksum skip based on checksum, not mod-time & size\n");
420- rprintf(F," -a, --archive archive mode; same as -rlptgoD (no -H, -A)\n");
421+ rprintf(F," -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)\n");
422 rprintf(F," --no-OPTION turn off an implied OPTION (e.g. --no-D)\n");
423 rprintf(F," -r, --recursive recurse into directories\n");
424 rprintf(F," -R, --relative use relative path names\n");
526184b9 425@@ -310,6 +314,9 @@ void usage(enum logcode F)
3610c43c 426 #ifdef SUPPORT_ACLS
bbdff76a 427 rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
3610c43c
WD
428 #endif
429+#ifdef SUPPORT_XATTRS
bbdff76a 430+ rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n");
3610c43c 431+#endif
4a65fe72 432 rprintf(F," -o, --owner preserve owner (super-user only)\n");
bbdff76a 433 rprintf(F," -g, --group preserve group\n");
1ed0b5c9 434 rprintf(F," --devices preserve device files (super-user only)\n");
526184b9 435@@ -433,6 +440,9 @@ static struct poptOption long_options[]
489b0a72
WD
436 {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
437 {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
438 {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
439+ {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 },
440+ {"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
441+ {"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
442 {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
443 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
444 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
526184b9 445@@ -1121,6 +1131,17 @@ int parse_arguments(int *argc, const cha
489b0a72 446 return 0;
3610c43c 447 #endif
bbdff76a
WD
448
449+ case 'X':
450+#ifdef SUPPORT_XATTRS
451+ preserve_xattrs = 1;
452+ preserve_perms = 1;
489b0a72 453+ break;
bbdff76a
WD
454+#else
455+ snprintf(err_buf,sizeof(err_buf),
456+ "extended attributes are not supported on this %s\n",
457+ am_server ? "server" : "client");
458+ return 0;
d2dc8a18 459+#endif
bbdff76a
WD
460
461 default:
462 /* A large opt value means that set_refuse_options()
526184b9 463@@ -1585,6 +1606,10 @@ void server_options(char **args,int *arg
bbdff76a
WD
464 if (preserve_acls)
465 argstr[x++] = 'A';
3610c43c
WD
466 #endif
467+#ifdef SUPPORT_XATTRS
bbdff76a
WD
468+ if (preserve_xattrs)
469+ argstr[x++] = 'X';
3610c43c 470+#endif
fc068916
WD
471 if (recurse)
472 argstr[x++] = 'r';
473 if (always_checksum)
9a7eef96
WD
474--- old/rsync.c
475+++ new/rsync.c
526184b9 476@@ -32,6 +32,7 @@
ff318e90 477 extern int verbose;
93d6ca6d 478 extern int dry_run;
93d6ca6d
WD
479 extern int preserve_acls;
480+extern int preserve_xattrs;
481 extern int preserve_perms;
482 extern int preserve_executability;
483 extern int preserve_times;
fc068916 484@@ -321,6 +322,10 @@ int set_file_attrs(char *fname, struct f
071c5b19
WD
485 if (daemon_chmod_modes && !S_ISLNK(new_mode))
486 new_mode = tweak_mode(new_mode, daemon_chmod_modes);
487
09cc6650 488+#ifdef SUPPORT_XATTRS
0f6fb3c8 489+ if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
09cc6650
WD
490+ updated = 1;
491+#endif
071c5b19
WD
492 #ifdef SUPPORT_ACLS
493 /* It's OK to call set_acl() now, even for a dir, as the generator
494 * will enable owner-writability using chmod, if necessary.
9a7eef96
WD
495--- old/rsync.h
496+++ new/rsync.h
526184b9 497@@ -555,6 +555,10 @@ struct idev_node {
0f6fb3c8 498 #define ACLS_NEED_MASK 1
09cc6650 499 #endif
bbdff76a 500
56473cb9 501+#ifndef HAVE_NO_XATTRS
bbdff76a
WD
502+#define SUPPORT_XATTRS 1
503+#endif
504+
0f6fb3c8
WD
505 #define GID_NONE ((gid_t)-1)
506
590329e5 507 union file_extras {
526184b9 508@@ -575,6 +579,7 @@ extern int file_extra_cnt;
6eba67ec
WD
509 extern int preserve_uid;
510 extern int preserve_gid;
511 extern int preserve_acls;
512+extern int preserve_xattrs;
513
514 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
515 #define EXTRA_LEN (sizeof (union file_extras))
526184b9 516@@ -608,6 +613,7 @@ extern int preserve_acls;
fc068916
WD
517 #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
518 #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
1aa236e1
WD
519 #define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
520+#define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->unum
70891d26 521
8aec0853 522 /* These items are per-entry optional and mutally exclusive: */
fdf967c7 523 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
526184b9 524@@ -799,6 +805,9 @@ typedef struct {
0f6fb3c8
WD
525 struct rsync_acl *acc_acl; /* access ACL */
526 struct rsync_acl *def_acl; /* default ACL */
527 #endif
528+#ifdef SUPPORT_XATTRS
529+ item_list *xattr;
bbdff76a 530+#endif
0f6fb3c8 531 } statx;
bbdff76a 532
0f6fb3c8 533 #define ACL_READY(sx) ((sx).acc_acl != NULL)
9a7eef96
WD
534--- old/rsync.yo
535+++ new/rsync.yo
4959107f 536@@ -301,7 +301,7 @@ to the detailed description below for a
1b57ecb0 537 -q, --quiet suppress non-error messages
4959107f 538 --no-motd suppress daemon-mode MOTD (see caveat)
1b57ecb0
WD
539 -c, --checksum skip based on checksum, not mod-time & size
540- -a, --archive archive mode; same as -rlptgoD (no -H, -A)
541+ -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)
542 --no-OPTION turn off an implied OPTION (e.g. --no-D)
543 -r, --recursive recurse into directories
544 -R, --relative use relative path names
4959107f 545@@ -324,6 +324,7 @@ to the detailed description below for a
4a65fe72 546 -E, --executability preserve executability
063cf77b 547 --chmod=CHMOD affect file and/or directory permissions
4a65fe72
WD
548 -A, --acls preserve ACLs (implies -p) [non-standard]
549+ -X, --xattrs preserve extended attrs (implies -p) [n.s.]
4a65fe72 550 -o, --owner preserve owner (super-user only)
bbdff76a 551 -g, --group preserve group
1ed0b5c9 552 --devices preserve device files (super-user only)
524e5ed5 553@@ -835,6 +836,11 @@ The ACL-sending protocol used by this ve
590329e5
WD
554 the patch that was shipped with 2.6.8. Sending ACLs to an older version
555 of the ACL patch is not supported.
bbdff76a
WD
556
557+dit(bf(-X, --xattrs)) This option causes rsync to update the remote
558+extended attributes to be the same as the local ones. This will work
559+only if the remote machine's rsync supports this option also. This is
560+a non-standard option.
561+
4a65fe72
WD
562 dit(bf(--chmod)) This option tells rsync to apply one or more
563 comma-separated "chmod" strings to the permission of the files in the
564 transfer. The resulting value is treated as though it was the permissions
9a7eef96
WD
565--- old/xattr.c
566+++ new/xattr.c
e3cd70c8 567@@ -0,0 +1,459 @@
dc52bc1a
WD
568+/*
569+ * Extended Attribute support for rsync.
0f6fb3c8 570+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
dc52bc1a
WD
571+ *
572+ * Copyright (C) 2004 Red Hat, Inc.
0f6fb3c8 573+ * Copyright (C) 2006 Wayne Davison
dc52bc1a
WD
574+ *
575+ * This program is free software; you can redistribute it and/or modify
576+ * it under the terms of the GNU General Public License as published by
577+ * the Free Software Foundation; either version 2 of the License, or
578+ * (at your option) any later version.
579+ *
580+ * This program is distributed in the hope that it will be useful,
581+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
582+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
583+ * GNU General Public License for more details.
584+ *
585+ * You should have received a copy of the GNU General Public License along
586+ * with this program; if not, write to the Free Software Foundation, Inc.,
587+ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
588+ */
bbdff76a
WD
589+
590+#include "rsync.h"
591+#include "lib/sysxattr.h"
592+
593+#ifdef SUPPORT_XATTRS
594+
bbdff76a 595+extern int dry_run;
3f24a3a8 596+extern int am_root;
8889f85e
WD
597+extern int read_only;
598+extern int list_only;
bbdff76a
WD
599+
600+#define RSYNC_XAL_INITIAL 5
601+#define RSYNC_XAL_LIST_INITIAL 100
602+
922f4409
WD
603+#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
604+ && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
605+
3f24a3a8 606+#define USER_PREFIX "user."
27502d8d
WD
607+#define UPRE_LEN ((int)sizeof USER_PREFIX - 1)
608+#define SYSTEM_PREFIX "system."
609+#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
610+
611+#ifdef HAVE_LINUX_XATTRS
612+#define RPRE_LEN 0
613+#else
614+#define RSYNC_PREFIX "rsync."
615+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
616+#endif
3f24a3a8 617+
bbdff76a 618+typedef struct {
27502d8d
WD
619+ char *datum, *name;
620+ size_t datum_len, name_len;
bbdff76a
WD
621+} rsync_xa;
622+
bbdff76a
WD
623+static size_t namebuf_len = 0;
624+static char *namebuf = NULL;
625+
0f6fb3c8
WD
626+static item_list empty_xattr = EMPTY_ITEM_LIST;
627+static item_list rsync_xal_l = EMPTY_ITEM_LIST;
bbdff76a
WD
628+
629+/* ------------------------------------------------------------------------- */
630+
0f6fb3c8 631+static void rsync_xal_free(item_list *xalp)
bbdff76a
WD
632+{
633+ size_t i;
0f6fb3c8 634+ rsync_xa *rxas = xalp->items;
bbdff76a 635+
0f6fb3c8 636+ for (i = 0; i < xalp->count; i++) {
27502d8d
WD
637+ free(rxas[i].datum);
638+ /*free(rxas[i].name);*/
bbdff76a 639+ }
0f6fb3c8 640+ xalp->count = 0;
bbdff76a
WD
641+}
642+
0f6fb3c8 643+void free_xattr(statx *sxp)
bbdff76a 644+{
0f6fb3c8
WD
645+ rsync_xal_free(sxp->xattr);
646+ free(sxp->xattr);
647+ sxp->xattr = NULL;
648+}
bbdff76a 649+
0f6fb3c8
WD
650+static int rsync_xal_compare_names(const void *x1, const void *x2)
651+{
652+ const rsync_xa *xa1 = x1;
653+ const rsync_xa *xa2 = x2;
bbdff76a
WD
654+ return strcmp(xa1->name, xa2->name);
655+}
656+
526184b9 657+static ssize_t rsync_xal_list(const char *fname)
bbdff76a 658+{
526184b9 659+ ssize_t list_len;
bbdff76a
WD
660+
661+ if (!namebuf) {
49de5440 662+ namebuf_len = 1024;
bbdff76a 663+ namebuf = new_array(char, namebuf_len);
49de5440 664+ if (!namebuf)
526184b9 665+ out_of_memory("rsync_xal_list");
bbdff76a
WD
666+ }
667+
49de5440 668+ /* The length returned includes all the '\0' terminators. */
27502d8d
WD
669+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
670+ if (list_len > (ssize_t)namebuf_len) {
671+ list_len = -1;
bbdff76a
WD
672+ errno = ERANGE;
673+ }
27502d8d 674+ if (list_len < 0) {
bbdff76a 675+ if (errno == ENOTSUP)
0f6fb3c8 676+ return 0;
bbdff76a 677+ if (errno == ERANGE) {
27502d8d
WD
678+ list_len = sys_llistxattr(fname, NULL, 0);
679+ if (list_len < 0) {
892d2287 680+ rsyserr(FERROR, errno,
526184b9 681+ "rsync_xal_list: llistxattr(\"%s\",0) failed",
0f6fb3c8 682+ fname);
e5754c5f 683+ return -1;
bbdff76a 684+ }
27502d8d 685+ namebuf = realloc_array(namebuf, char, list_len + 1024);
bbdff76a 686+ if (!namebuf)
526184b9 687+ out_of_memory("rsync_xal_list");
27502d8d
WD
688+ namebuf_len = list_len + 1024;
689+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
690+ if (list_len < 0) {
0f6fb3c8 691+ rsyserr(FERROR, errno,
526184b9 692+ "rsync_xal_list: llistxattr(\"%s\",%ld) failed",
892d2287 693+ fname, (long)namebuf_len);
e5754c5f 694+ return -1;
bbdff76a
WD
695+ }
696+ } else {
0f6fb3c8 697+ rsyserr(FERROR, errno,
526184b9 698+ "rsync_xal_list: llistxattr(\"%s\",%ld) failed",
892d2287 699+ fname, (long)namebuf_len);
e5754c5f 700+ return -1;
bbdff76a
WD
701+ }
702+ }
27502d8d 703+
526184b9
WD
704+ return list_len;
705+}
706+
707+static int rsync_xal_get(const char *fname, item_list *xalp)
708+{
709+ ssize_t list_len, name_len, datum_len;
710+ char *name, *ptr;
711+
712+ /* This puts the name list into the "namebuf" buffer. */
713+ if ((list_len = rsync_xal_list(fname)) < 0)
714+ return -1;
715+
892d2287 716+ for (name = namebuf; list_len > 0; name += name_len) {
3f24a3a8 717+ rsync_xa *rxas;
bbdff76a 718+
27502d8d 719+ name_len = strlen(name) + 1;
892d2287 720+ list_len -= name_len;
3f24a3a8
WD
721+
722+#ifdef HAVE_LINUX_XATTRS
27502d8d 723+ /* We don't send the system namespace. */
922f4409 724+ if (HAS_PREFIX(name, SYSTEM_PREFIX))
3f24a3a8
WD
725+ continue;
726+#endif
727+
27502d8d
WD
728+ datum_len = sys_lgetxattr(fname, name, NULL, 0);
729+ if (datum_len < 0) {
bbdff76a 730+ if (errno == ENOTSUP)
e5754c5f 731+ return -1;
49de5440 732+ rsyserr(FERROR, errno,
892d2287 733+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",0) failed",
49de5440
WD
734+ fname, name);
735+ return -1;
bbdff76a 736+ }
27502d8d 737+ ptr = new_array(char, name_len + datum_len);
bbdff76a
WD
738+ if (!ptr)
739+ out_of_memory("rsync_xal_get");
27502d8d
WD
740+ if (datum_len) {
741+ ssize_t len = sys_lgetxattr(fname, name, ptr, datum_len);
742+ if (len != datum_len) {
743+ if (len < 0) {
49de5440 744+ rsyserr(FERROR, errno,
892d2287
WD
745+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
746+ " failed", fname, name, (long)datum_len);
49de5440
WD
747+ } else {
748+ rprintf(FERROR,
892d2287
WD
749+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
750+ " returned %ld\n", fname, name,
751+ (long)datum_len, (long)len);
49de5440 752+ }
27502d8d 753+ free(ptr);
49de5440
WD
754+ return -1;
755+ }
756+ }
27502d8d
WD
757+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
758+ rxas->name = ptr + datum_len;
b54d5ec2 759+ rxas->datum = ptr;
27502d8d
WD
760+ rxas->name_len = name_len;
761+ rxas->datum_len = datum_len;
b54d5ec2 762+ memcpy(rxas->name, name, name_len);
bbdff76a 763+ }
0f6fb3c8
WD
764+ if (xalp->count > 1)
765+ qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
e5754c5f 766+ return 0;
bbdff76a
WD
767+}
768+
0f6fb3c8
WD
769+/* Read the xattr(s) for this filename. */
770+int get_xattr(const char *fname, statx *sxp)
bbdff76a 771+{
0f6fb3c8
WD
772+ sxp->xattr = new(item_list);
773+ *sxp->xattr = empty_xattr;
774+ if (rsync_xal_get(fname, sxp->xattr) < 0) {
775+ free_xattr(sxp);
776+ return -1;
777+ }
778+ return 0;
bbdff76a
WD
779+}
780+
0f6fb3c8 781+static int find_matching_xattr(item_list *xalp)
bbdff76a 782+{
0f6fb3c8
WD
783+ size_t i, j;
784+ item_list *lst = rsync_xal_l.items;
bbdff76a
WD
785+
786+ for (i = 0; i < rsync_xal_l.count; i++) {
0f6fb3c8
WD
787+ rsync_xa *rxas1 = lst[i].items;
788+ rsync_xa *rxas2 = xalp->items;
789+
bbdff76a 790+ /* Wrong number of elements? */
0f6fb3c8 791+ if (lst[i].count != xalp->count)
bbdff76a
WD
792+ continue;
793+ /* any elements different? */
0f6fb3c8
WD
794+ for (j = 0; j < xalp->count; j++) {
795+ if (rxas1[j].name_len != rxas2[j].name_len
796+ || rxas1[j].datum_len != rxas2[j].datum_len
797+ || strcmp(rxas1[j].name, rxas2[j].name)
798+ || memcmp(rxas1[j].datum, rxas2[j].datum, rxas2[j].datum_len))
bbdff76a
WD
799+ break;
800+ }
801+ /* no differences found. This is The One! */
0f6fb3c8
WD
802+ if (j == xalp->count)
803+ return i;
bbdff76a 804+ }
0f6fb3c8
WD
805+
806+ return -1;
bbdff76a
WD
807+}
808+
0f6fb3c8
WD
809+/* Store *xalp on the end of rsync_xal_l */
810+static void rsync_xal_store(item_list *xalp)
bbdff76a 811+{
0f6fb3c8 812+ item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
de565f59
WD
813+ /* Since the following call starts a new list, we know it will hold the
814+ * entire initial-count, not just enough space for one new item. */
49de5440
WD
815+ *new_lst = empty_xattr;
816+ (void)EXPAND_ITEM_LIST(new_lst, rsync_xa, xalp->count);
817+ memcpy(new_lst->items, xalp->items, xalp->count * sizeof (rsync_xa));
0f6fb3c8
WD
818+ new_lst->count = xalp->count;
819+ xalp->count = 0;
bbdff76a
WD
820+}
821+
0f6fb3c8
WD
822+/* Send the make_xattr()-generated xattr list for this flist entry. */
823+void send_xattr(statx *sxp, int f)
bbdff76a 824+{
0f6fb3c8
WD
825+ int ndx = find_matching_xattr(sxp->xattr);
826+ if (ndx != -1) {
bbdff76a 827+ write_byte(f, 'x');
0f6fb3c8
WD
828+ write_int(f, ndx);
829+ rsync_xal_free(sxp->xattr);
bbdff76a
WD
830+ } else {
831+ rsync_xa *rxa;
0f6fb3c8 832+ int count = sxp->xattr->count;
bbdff76a
WD
833+ write_byte(f, 'X');
834+ write_int(f, count);
0f6fb3c8 835+ for (rxa = sxp->xattr->items; count--; rxa++) {
27502d8d
WD
836+#ifdef HAVE_LINUX_XATTRS
837+ write_int(f, rxa->name_len);
838+ write_int(f, rxa->datum_len);
839+ write_buf(f, rxa->name, rxa->name_len);
840+#else
841+ /* We strip the rsync prefix from disguised namespaces
842+ * and put everything else in the user namespace. */
dfe2b257
WD
843+ if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
844+ && rxa->name[RPRE_LEN] != '%') {
27502d8d
WD
845+ write_int(f, rxa->name_len - RPRE_LEN);
846+ write_int(f, rxa->datum_len);
847+ write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
848+ } else {
3f24a3a8
WD
849+ write_int(f, rxa->name_len + UPRE_LEN);
850+ write_int(f, rxa->datum_len);
851+ write_buf(f, USER_PREFIX, UPRE_LEN);
27502d8d 852+ write_buf(f, rxa->name, rxa->name_len);
3f24a3a8 853+ }
27502d8d 854+#endif
bbdff76a
WD
855+ write_buf(f, rxa->datum, rxa->datum_len);
856+ }
49de5440 857+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
bbdff76a 858+ }
0f6fb3c8 859+ free_xattr(sxp);
bbdff76a
WD
860+}
861+
bbdff76a 862+/* ------------------------------------------------------------------------- */
bbdff76a 863+
0f6fb3c8 864+/* receive and build the rsync_xattr_lists */
bbdff76a
WD
865+void receive_xattr(struct file_struct *file, int f)
866+{
0f6fb3c8 867+ static item_list temp_xattr = EMPTY_ITEM_LIST;
27502d8d 868+ int ndx, tag = read_byte(f);
bbdff76a 869+
bbdff76a 870+ if (tag == 'X') {
0f6fb3c8 871+ int i, count = read_int(f);
bbdff76a 872+ for (i = 0; i < count; i++) {
27502d8d 873+ char *ptr, *name;
0f6fb3c8
WD
874+ rsync_xa *rxa;
875+ size_t name_len = read_int(f);
876+ size_t datum_len = read_int(f);
bcecb5e3
WD
877+#ifdef HAVE_LINUX_XATTRS
878+ size_t extra_len = 0;
879+#else
880+ size_t extra_len = am_root ? RPRE_LEN : 0;
922f4409 881+ if (datum_len + extra_len < datum_len)
27502d8d
WD
882+ out_of_memory("receive_xattr"); /* overflow */
883+#endif
922f4409
WD
884+ if (name_len + datum_len + extra_len < name_len)
885+ out_of_memory("receive_xattr"); /* overflow */
886+ ptr = new_array(char, name_len + datum_len + extra_len);
bbdff76a
WD
887+ if (!ptr)
888+ out_of_memory("receive_xattr");
922f4409 889+ name = ptr + datum_len + extra_len;
27502d8d
WD
890+ read_buf(f, name, name_len);
891+ read_buf(f, ptr, datum_len);
3f24a3a8 892+#ifdef HAVE_LINUX_XATTRS
27502d8d 893+ /* Non-root can only save the user namespace. */
922f4409 894+ if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
3f24a3a8
WD
895+ free(ptr);
896+ continue;
897+ }
898+#else
27502d8d
WD
899+ /* This OS only has a user namespace, so we either
900+ * strip the user prefix, or we put a non-user
901+ * namespace inside our rsync hierarchy. */
922f4409 902+ if (HAS_PREFIX(name, USER_PREFIX)) {
27502d8d
WD
903+ name += UPRE_LEN;
904+ name_len -= UPRE_LEN;
922f4409 905+ } else if (am_root) {
27502d8d 906+ name -= RPRE_LEN;
b54d5ec2 907+ name_len += RPRE_LEN;
27502d8d 908+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
922f4409
WD
909+ } else {
910+ free(ptr);
911+ continue;
517cc92f
WD
912+ }
913+#endif
27502d8d
WD
914+ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
915+ rxa->name = name;
27502d8d 916+ rxa->datum = ptr;
b54d5ec2 917+ rxa->name_len = name_len;
27502d8d 918+ rxa->datum_len = datum_len;
bbdff76a 919+ }
49de5440
WD
920+ ndx = rsync_xal_l.count; /* pre-incremented count */
921+ rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
0f6fb3c8
WD
922+ } else if (tag == 'x') {
923+ ndx = read_int(f);
924+ if (ndx < 0 || (size_t)ndx >= rsync_xal_l.count) {
892d2287
WD
925+ rprintf(FERROR, "receive_xattr: xa index %d out of"
926+ " range for %s\n", ndx, f_name(file, NULL));
bbdff76a
WD
927+ exit_cleanup(RERR_STREAMIO);
928+ }
0f6fb3c8 929+ } else {
892d2287
WD
930+ rprintf(FERROR, "receive_xattr: unknown extended attribute"
931+ " type tag (%c) for %s\n", tag, f_name(file, NULL));
0f6fb3c8 932+ exit_cleanup(RERR_STREAMIO);
bbdff76a 933+ }
bbdff76a 934+
70891d26 935+ F_XATTR(file) = ndx;
bbdff76a
WD
936+}
937+
0f6fb3c8
WD
938+/* Turn the xattr data in statx into cached xattr data, setting the index
939+ * values in the file struct. */
940+void cache_xattr(struct file_struct *file, statx *sxp)
bbdff76a 941+{
0f6fb3c8 942+ int ndx;
bbdff76a 943+
0f6fb3c8
WD
944+ if (!sxp->xattr)
945+ return;
bbdff76a 946+
0f6fb3c8
WD
947+ ndx = find_matching_xattr(sxp->xattr);
948+ if (ndx == -1)
49de5440 949+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
0f6fb3c8 950+ free_xattr(sxp);
bbdff76a 951+
70891d26 952+ F_XATTR(file) = ndx;
bbdff76a
WD
953+}
954+
0f6fb3c8 955+static int rsync_xal_set(const char *fname, item_list *xalp)
bbdff76a 956+{
0f6fb3c8 957+ rsync_xa *rxas = xalp->items;
526184b9 958+ ssize_t list_len;
0f6fb3c8 959+ size_t i;
526184b9
WD
960+ char *name;
961+ int name_len, ret = 0;
962+
963+ /* This puts the current name list into the "namebuf" buffer. */
964+ if ((list_len = rsync_xal_list(fname)) < 0)
965+ return -1;
bbdff76a 966+
0f6fb3c8 967+ for (i = 0; i < xalp->count; i++) {
742a4103 968+ int status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len);
0f6fb3c8 969+ if (status < 0) {
892d2287
WD
970+ rsyserr(FERROR, errno,
971+ "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
0f6fb3c8
WD
972+ fname, rxas[i].name);
973+ ret = -1;
974+ }
bbdff76a 975+ }
526184b9
WD
976+
977+ /* Remove any extraneous names. */
978+ for (name = namebuf; list_len > 0; name += name_len) {
979+ name_len = strlen(name) + 1;
980+ list_len -= name_len;
981+
e3cd70c8
WD
982+#ifdef HAVE_LINUX_XATTRS
983+ /* We always ignore the system namespace, and non-root
984+ * ignores everything but the user namespace. */
985+ if (am_root ? HAS_PREFIX(name, SYSTEM_PREFIX)
986+ : !HAS_PREFIX(name, USER_PREFIX))
987+ continue;
988+#endif
989+
526184b9
WD
990+ for (i = 0; i < xalp->count; i++) {
991+ if (strcmp(name, rxas[i].name) == 0)
992+ break;
993+ }
994+ if (i == xalp->count) {
995+ int status = sys_lremovexattr(fname, name);
996+ if (status < 0) {
997+ rsyserr(FERROR, errno,
998+ "rsync_xal_clear: lremovexattr(\"%s\",\"%s\") failed",
999+ fname, name);
1000+ ret = -1;
1001+ }
1002+ }
1003+ }
1004+
0f6fb3c8 1005+ return ret;
bbdff76a
WD
1006+}
1007+
0f6fb3c8
WD
1008+/* Set extended attributes on indicated filename. */
1009+int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
bbdff76a 1010+{
0f6fb3c8 1011+ int ndx;
0f6fb3c8 1012+ item_list *lst = rsync_xal_l.items;
bbdff76a 1013+
93d6ca6d
WD
1014+ if (dry_run)
1015+ return 1; /* FIXME: --dry-run needs to compute this value */
1016+
8889f85e
WD
1017+ if (read_only || list_only) {
1018+ errno = EROFS;
1019+ return -1;
1020+ }
1021+
70891d26 1022+ ndx = F_XATTR(file);
0f6fb3c8 1023+ return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
bbdff76a
WD
1024+}
1025+
1026+#endif /* SUPPORT_XATTRS */