Updated to work with latest io.c changes.
[rsync/rsync-patches.git] / xattrs.diff
CommitLineData
bbdff76a
WD
1Depends-On-Patch: acls.diff
2
3After applying this patch, run these commands for a successful build:
4
27e96866 5 ./prepare-source
f787c90c 6 ./configure --enable-acl-support --enable-xattr-support
bbdff76a
WD
7 make
8
9a7eef96
WD
9--- old/Makefile.in
10+++ new/Makefile.in
4bf6f8c7 11@@ -27,13 +27,13 @@ VERSION=@VERSION@
bbdff76a
WD
12
13 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
14 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \
15- lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
16+ lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattr.o @LIBOBJS@
4bf6f8c7
WD
17 ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
18 zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
bbdff76a
WD
19 OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \
20 main.o checksum.o match.o syscall.o log.o backup.o
21 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
610969d1
WD
22- fileio.o batch.o clientname.o chmod.o acls.o
23+ fileio.o batch.o clientname.o chmod.o acls.o xattr.o
bbdff76a
WD
24 OBJS3=progress.o pipe.o
25 DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
26 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
9a7eef96
WD
27--- old/backup.c
28+++ new/backup.c
93d6ca6d
WD
29@@ -29,6 +29,7 @@ extern char *backup_dir;
30
31 extern int am_root;
32 extern int preserve_acls;
33+extern int preserve_xattrs;
34 extern int preserve_devices;
35 extern int preserve_specials;
36 extern int preserve_links;
37@@ -137,6 +138,10 @@ static int make_bak_dir(char *fullpath)
38 if (preserve_acls)
39 dup_acl(end, fullpath, st.st_mode);
09cc6650
WD
40 #endif
41+#ifdef SUPPORT_XATTRS
93d6ca6d
WD
42+ if (preserve_xattrs)
43+ dup_xattr(end, fullpath );
09cc6650 44+#endif
bbdff76a
WD
45 }
46 }
47 *p = '/';
93d6ca6d
WD
48@@ -194,6 +199,10 @@ static int keep_backup(char *fname)
49 if (preserve_acls)
50 push_keep_backup_acl(file, fname, buf);
09cc6650
WD
51 #endif
52+#ifdef SUPPORT_XATTRS
93d6ca6d
WD
53+ if (preserve_xattrs)
54+ push_keep_backup_xattr(file, fname, buf);
09cc6650 55+#endif
bbdff76a
WD
56
57 /* Check to see if this is a device file, or link */
4a65fe72 58 if ((am_root && preserve_devices && IS_DEVICE(file->mode))
93d6ca6d
WD
59@@ -274,6 +283,10 @@ static int keep_backup(char *fname)
60 if (preserve_acls)
61 cleanup_keep_backup_acl();
09cc6650
WD
62 #endif
63+#ifdef SUPPORT_XATTRS
93d6ca6d
WD
64+ if (preserve_xattrs)
65+ cleanup_keep_backup_xattr();
09cc6650 66+#endif
bbdff76a
WD
67 free(file);
68
69 if (verbose > 1) {
9a7eef96
WD
70--- old/configure.in
71+++ new/configure.in
afcb578c 72@@ -814,6 +814,30 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
bbdff76a
WD
73 AC_MSG_RESULT(no)
74 )
75
76+AC_CHECK_HEADERS(attr/xattr.h)
77+AC_MSG_CHECKING(whether to support extended attributes)
f787c90c
WD
78+AC_ARG_ENABLE(xattr-support,
79+AC_HELP_STRING([--enable-xattr-support], [Include extended attribute support (default=no)]),
3b05e91f 80+[ case "$enableval" in
bbdff76a
WD
81+ yes)
82+ case "$host_os" in
83+ *linux*)
84+ AC_MSG_RESULT(Using Linux xattrs)
85+ AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs])
86+ ;;
87+ *)
88+ AC_MSG_RESULT(Xattrs requested but not linux. Good luck)
89+ ;;
90+ esac
91+ ;;
92+ *)
93+ AC_MSG_RESULT(no)
94+ AC_DEFINE(HAVE_NA_XATTRS, 1, [True if you don't have extended attributes])
95+ esac ],
96+ AC_MSG_RESULT(no)
97+ AC_DEFINE(HAVE_NO_XATTRL, 1, [True if you don't have extended attributes])
98+)
99+
100 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
101 AC_OUTPUT
102
9a7eef96
WD
103--- old/flist.c
104+++ new/flist.c
93d6ca6d
WD
105@@ -45,6 +45,7 @@ extern int one_file_system;
106 extern int copy_dirlinks;
107 extern int keep_dirlinks;
108 extern int preserve_acls;
109+extern int preserve_xattrs;
110 extern int preserve_links;
111 extern int preserve_hard_links;
112 extern int preserve_devices;
a2f9a486 113@@ -975,6 +976,10 @@ static struct file_struct *send_file_nam
93d6ca6d 114 if (preserve_acls && make_acl(file, fname) < 0)
bbdff76a 115 return NULL;
09cc6650
WD
116 #endif
117+#ifdef SUPPORT_XATTRS
93d6ca6d 118+ if (preserve_xattrs && make_xattr(file, fname) < 0)
bbdff76a 119+ return NULL;
09cc6650 120+#endif
bbdff76a 121
a2f9a486
WD
122 maybe_emit_filelist_progress(flist->count + flist_count_offset);
123
124@@ -987,12 +992,20 @@ static struct file_struct *send_file_nam
93d6ca6d
WD
125 if (preserve_acls)
126 send_acl(file, f);
127 #endif
09cc6650 128+#ifdef SUPPORT_XATTRS
93d6ca6d
WD
129+ if (preserve_xattrs)
130+ send_xattr(file, f);
09cc6650 131+#endif
bbdff76a 132 } else {
93d6ca6d 133 #ifdef SUPPORT_ACLS
bbdff76a 134 /* Cleanup unsent ACL(s). */
93d6ca6d
WD
135 if (preserve_acls)
136 send_acl(file, -1);
09cc6650
WD
137 #endif
138+#ifdef SUPPORT_XATTRS
93d6ca6d
WD
139+ if (preserve_xattrs)
140+ send_xattr(file, -1);
09cc6650 141+#endif
bbdff76a
WD
142 }
143 return file;
144 }
a2f9a486 145@@ -1385,6 +1398,10 @@ struct file_list *recv_file_list(int f)
93d6ca6d
WD
146 if (preserve_acls)
147 receive_acl(file, f);
09cc6650
WD
148 #endif
149+#ifdef SUPPORT_XATTRS
93d6ca6d
WD
150+ if (preserve_xattrs)
151+ receive_xattr(file, f );
09cc6650 152+#endif
bbdff76a 153
3731f97b 154 if (S_ISREG(file->mode) || S_ISLNK(file->mode))
bbdff76a 155 stats.total_size += file->length;
a2f9a486 156@@ -1412,6 +1429,10 @@ struct file_list *recv_file_list(int f)
93d6ca6d
WD
157 if (preserve_acls)
158 sort_file_acl_index_lists();
bbdff76a
WD
159 #endif
160+#ifdef SUPPORT_XATTRS
93d6ca6d
WD
161+ if (preserve_xattrs)
162+ sort_file_xattr_index_lists();
bbdff76a 163+#endif
09cc6650
WD
164
165 if (f >= 0) {
166 recv_uid_list(f, flist);
9a7eef96
WD
167--- old/lib/sysxattr.c
168+++ new/lib/sysxattr.c
bbdff76a
WD
169@@ -0,0 +1,41 @@
170+/* Extended attribute support for rsync. */
171+/* This file Copyright (C) 2004 Red Hat, Inc. */
172+/* Written by Jay Fenlason */
173+
174+/* This program is free software; you can redistribute it and/or modify
175+ it under the terms of the GNU General Public License as published by
176+ the Free Software Foundation; either version 2 of the License, or
177+ (at your option) any later version.
178+
179+ This program is distributed in the hope that it will be useful,
180+ but WITHOUT ANY WARRANTY; without even the implied warranty of
181+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
182+ GNU General Public License for more details.
183+
184+ You should have received a copy of the GNU General Public License
185+ along with this program; if not, write to the Free Software
186+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
187+*/
188+
189+#include "rsync.h"
190+
09cc6650 191+#if defined HAVE_LINUX_XATTRS
bbdff76a
WD
192+
193+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
194+{
195+ return lgetxattr(path, name, value, size);
196+}
197+
198+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags)
199+{
200+ return lsetxattr(path, name, value, size, flags);
201+}
202+
203+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
204+{
205+ return llistxattr(path, list, size);
206+}
207+
208+#else
209+
210+#endif /* No xattrs */
9a7eef96
WD
211--- old/lib/sysxattr.h
212+++ new/lib/sysxattr.h
bbdff76a 213@@ -0,0 +1,9 @@
09cc6650 214+#if defined HAVE_LINUX_XATTRS
bbdff76a
WD
215+
216+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
217+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags);
218+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
219+
220+#else
221+
222+#endif /* No xattrs */
9a7eef96
WD
223--- old/options.c
224+++ new/options.c
3731f97b 225@@ -46,6 +46,7 @@ int copy_links = 0;
bbdff76a
WD
226 int preserve_links = 0;
227 int preserve_hard_links = 0;
228 int preserve_acls = 0;
229+int preserve_xattrs = 0;
230 int preserve_perms = 0;
4a65fe72 231 int preserve_executability = 0;
bbdff76a 232 int preserve_devices = 0;
afcb578c 233@@ -196,6 +197,7 @@ static void print_rsync_version(enum log
bbdff76a
WD
234 char const *have_inplace = "no ";
235 char const *hardlinks = "no ";
236 char const *acls = "no ";
237+ char const *xattrs = "no ";
238 char const *links = "no ";
239 char const *ipv6 = "no ";
240 STRUCT_STAT *dumstat;
afcb578c 241@@ -215,7 +217,9 @@ static void print_rsync_version(enum log
bbdff76a
WD
242 #ifdef SUPPORT_ACLS
243 acls = "";
244 #endif
245-
246+#ifdef SUPPORT_XATTRS
247+ xattrs = "";
248+#endif
249 #ifdef SUPPORT_LINKS
250 links = "";
251 #endif
afcb578c 252@@ -229,9 +233,9 @@ static void print_rsync_version(enum log
3b05e91f 253 rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n");
bbdff76a
WD
254 rprintf(f, "<http://rsync.samba.org/>\n");
255 rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, "
bc5988ec
WD
256- "%shard links, %sACLs, %ssymlinks, batchfiles,\n",
257+ "%shard links, %sACLs, %sxattrs, %ssymlinks, batchfiles,\n",
bbdff76a
WD
258 (int) (sizeof (OFF_T) * 8),
259- got_socketpair, hardlinks, acls, links);
260+ got_socketpair, hardlinks, acls, xattrs, links);
261
262 /* Note that this field may not have type ino_t. It depends
263 * on the complicated interaction between largefile feature
3731f97b 264@@ -304,6 +308,9 @@ void usage(enum logcode F)
3610c43c 265 #ifdef SUPPORT_ACLS
bbdff76a 266 rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
3610c43c
WD
267 #endif
268+#ifdef SUPPORT_XATTRS
bbdff76a 269+ rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n");
3610c43c 270+#endif
4a65fe72
WD
271 rprintf(F," --chmod=CHMOD change destination permissions\n");
272 rprintf(F," -o, --owner preserve owner (super-user only)\n");
bbdff76a 273 rprintf(F," -g, --group preserve group\n");
3731f97b 274@@ -422,6 +429,9 @@ static struct poptOption long_options[]
489b0a72
WD
275 {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
276 {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
277 {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
278+ {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 },
279+ {"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
280+ {"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
281 {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
282 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
283 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
3731f97b 284@@ -1096,6 +1106,17 @@ int parse_arguments(int *argc, const cha
489b0a72 285 return 0;
3610c43c 286 #endif
bbdff76a
WD
287
288+ case 'X':
289+#ifdef SUPPORT_XATTRS
290+ preserve_xattrs = 1;
291+ preserve_perms = 1;
489b0a72 292+ break;
bbdff76a
WD
293+#else
294+ snprintf(err_buf,sizeof(err_buf),
295+ "extended attributes are not supported on this %s\n",
296+ am_server ? "server" : "client");
297+ return 0;
298+#endif /* SUPPORT_XATTRS */
bbdff76a
WD
299
300 default:
301 /* A large opt value means that set_refuse_options()
3731f97b 302@@ -1544,6 +1565,10 @@ void server_options(char **args,int *arg
bbdff76a
WD
303 if (preserve_acls)
304 argstr[x++] = 'A';
3610c43c
WD
305 #endif
306+#ifdef SUPPORT_XATTRS
bbdff76a
WD
307+ if (preserve_xattrs)
308+ argstr[x++] = 'X';
3610c43c 309+#endif
bbdff76a
WD
310 if (preserve_uid)
311 argstr[x++] = 'o';
312 if (preserve_gid)
9a7eef96
WD
313--- old/rsync.c
314+++ new/rsync.c
93d6ca6d
WD
315@@ -34,6 +34,7 @@ extern int verbose;
316 extern int dry_run;
317 extern int daemon_log_format_has_i;
318 extern int preserve_acls;
319+extern int preserve_xattrs;
320 extern int preserve_perms;
321 extern int preserve_executability;
322 extern int preserve_times;
a2f9a486
WD
323@@ -215,6 +216,10 @@ int set_file_attrs(char *fname, struct f
324 if (preserve_acls && set_acl(fname, file, &st->st_mode) == 0)
09cc6650 325 updated = 1;
e5754c5f 326 #endif
09cc6650 327+#ifdef SUPPORT_XATTRS
93d6ca6d 328+ if (preserve_xattrs && set_xattr(fname, file) == 0)
09cc6650
WD
329+ updated = 1;
330+#endif
bbdff76a 331
493b02f6
WD
332 #ifdef HAVE_CHMOD
333 if ((st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS)) {
9a7eef96
WD
334--- old/rsync.h
335+++ new/rsync.h
09cc6650
WD
336@@ -672,6 +672,14 @@ struct chmod_mode_struct;
337 #endif
bbdff76a
WD
338 #include "smb_acls.h"
339
340+#ifdef HAVE_LINUX_XATTRS
341+#define SUPPORT_XATTRS 1
342+#endif
343+
09cc6650 344+#if defined SUPPORT_XATTRS && defined HAVE_ATTR_XATTR_H
bbdff76a
WD
345+#include <attr/xattr.h>
346+#endif
bbdff76a
WD
347+
348 #include "proto.h"
349
350 /* We have replacement versions of these if they're missing. */
9a7eef96
WD
351--- old/rsync.yo
352+++ new/rsync.yo
3731f97b 353@@ -322,6 +322,7 @@ to the detailed description below for a
bbdff76a 354 -p, --perms preserve permissions
4a65fe72
WD
355 -E, --executability preserve executability
356 -A, --acls preserve ACLs (implies -p) [non-standard]
357+ -X, --xattrs preserve extended attrs (implies -p) [n.s.]
358 --chmod=CHMOD change destination permissions
359 -o, --owner preserve owner (super-user only)
bbdff76a 360 -g, --group preserve group
3731f97b 361@@ -802,6 +803,11 @@ dit(bf(-A, --acls)) This option causes r
4a65fe72
WD
362 ACLs to be the same as the source ACLs. This nonstandard option only
363 works if the remote rsync also supports it. bf(--acls) implies bf(--perms).
bbdff76a
WD
364
365+dit(bf(-X, --xattrs)) This option causes rsync to update the remote
366+extended attributes to be the same as the local ones. This will work
367+only if the remote machine's rsync supports this option also. This is
368+a non-standard option.
369+
4a65fe72
WD
370 dit(bf(--chmod)) This option tells rsync to apply one or more
371 comma-separated "chmod" strings to the permission of the files in the
372 transfer. The resulting value is treated as though it was the permissions
9a7eef96
WD
373--- old/xattr.c
374+++ new/xattr.c
93d6ca6d 375@@ -0,0 +1,521 @@
bbdff76a
WD
376+/* Extended Attribute support for rsync */
377+/* Copyright (C) 2004 Red Hat, Inc */
378+/* Written by Jay Fenlason, vaguely based on the ACLs patch */
379+
380+/* This program is free software; you can redistribute it and/or modify
381+ it under the terms of the GNU General Public License as published by
382+ the Free Software Foundation; either version 2 of the License, or
383+ (at your option) any later version.
384+
385+ This program is distributed in the hope that it will be useful,
386+ but WITHOUT ANY WARRANTY; without even the implied warranty of
387+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
388+ GNU General Public License for more details.
389+
390+ You should have received a copy of the GNU General Public License
391+ along with this program; if not, write to the Free Software
392+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
393+*/
394+
395+#include "rsync.h"
396+#include "lib/sysxattr.h"
397+
398+#ifdef SUPPORT_XATTRS
399+
bbdff76a
WD
400+extern int dry_run;
401+
402+#define RSYNC_XAL_INITIAL 5
403+#define RSYNC_XAL_LIST_INITIAL 100
404+
405+typedef struct {
406+ size_t name_len;
407+ char *name;
408+ size_t datum_len;
409+ char *datum;
410+} rsync_xa;
411+
412+typedef struct {
413+ size_t count;
414+ size_t alloc;
415+ rsync_xa *rxas;
416+} rsync_xal;
417+
418+typedef struct {
419+ size_t count;
420+ size_t alloc;
421+ rsync_xal *rxals;
422+} rsync_xal_list;
423+
424+static size_t namebuf_len = 0;
425+static char *namebuf = NULL;
426+
427+static size_t datumbuf_len = 0;
428+static char *datumbuf = NULL;
429+
430+static rsync_xal curr_rsync_xal = { 0, 0, NULL };
431+static rsync_xal_list rsync_xal_l = { 0, 0, NULL };
432+
433+
434+/* ------------------------------------------------------------------------- */
435+
436+/* the below stuff is only used by the receiver */
437+
438+/* structure to hold index to rsync_xal_l member corresponding to
439+ * flist->files[i] */
440+
441+typedef struct {
442+ const struct file_struct *file;
443+ int xalidx;
444+} file_xal_index;
445+
446+typedef struct {
447+ size_t count;
448+ size_t alloc;
449+ file_xal_index *filexalidxs;
450+} file_xal_index_list;
451+
452+static file_xal_index_list fxil = {0, 0, NULL };
453+
454+/* stuff for redirecting calls to set_acl() from set_perms()
455+ * for keep_backup() */
456+static const struct file_struct *backup_orig_file = NULL;
457+static const char null_string[] = "";
458+static const char *backup_orig_fname = null_string;
459+static const char *backup_dest_fname = null_string;
460+static rsync_xal backup_xal;
461+
462+/* ------------------------------------------------------------------------- */
463+
464+static void rsync_xal_free(rsync_xal *x)
465+{
466+ size_t i;
467+
468+ for (i = 0; i < x->count; i++) {
469+ free(x->rxas[i].name);
470+ /* free(x->rxas[i].value); */
471+ }
472+ x->count = 0;
473+}
474+
475+static int rsync_xal_compare_names(const void *x1, const void *x2)
476+{
477+ const rsync_xa *xa1;
478+ const rsync_xa *xa2;
479+
480+ xa1 = x1;
481+ xa2 = x2;
482+ return strcmp(xa1->name, xa2->name);
483+}
484+
e5754c5f 485+static int rsync_xal_get(const char *fname, rsync_xal *x)
bbdff76a
WD
486+{
487+ ssize_t name_size;
488+ ssize_t datum_size;
489+ ssize_t left;
490+ char *name;
491+ size_t len;
492+ char *ptr;
493+
494+ if (!namebuf) {
495+ namebuf_len = 100;
496+ namebuf = new_array(char, namebuf_len);
497+ datumbuf_len = 100;
498+ datumbuf = new_array(char, datumbuf_len);
499+ if (!namebuf || !datumbuf)
500+ out_of_memory("rsync_xal_get");
501+ }
502+
503+ name_size = sys_llistxattr(fname, namebuf, namebuf_len);
504+ if (name_size > (ssize_t)namebuf_len) {
505+ name_size = -1;
506+ errno = ERANGE;
507+ }
508+ if (name_size < 0) {
509+ if (errno == ENOTSUP)
e5754c5f 510+ return -1;
bbdff76a
WD
511+ if (errno == ERANGE) {
512+ name_size = sys_llistxattr(fname, NULL, 0);
513+ if (name_size < 0) {
514+ rprintf(FERROR, "%s: rsync_xal_get: llistxattr: %s\n",
e5754c5f
WD
515+ fname, strerror(errno));
516+ return -1;
bbdff76a
WD
517+ }
518+ namebuf = realloc_array(namebuf, char, name_size + 1);
519+ if (!namebuf)
520+ out_of_memory("rsync_xal_get");
521+ namebuf_len = name_size;
522+ name_size = sys_llistxattr(fname, namebuf, namebuf_len);
523+ if (name_size < 0) {
524+ rprintf(FERROR,
525+ "%s: rsync_xal_get: re-llistxattr failed: %s\n",
526+ fname, strerror(errno));
e5754c5f 527+ return -1;
bbdff76a
WD
528+ }
529+ } else {
530+ rprintf(FERROR,
531+ "%s: rsync_xal_get: llistxattr failed: %s\n",
532+ fname, strerror(errno));
e5754c5f 533+ return -1;
bbdff76a
WD
534+ }
535+ }
536+ rsync_xal_free(x);
537+ if (name_size == 0)
e5754c5f 538+ return 0;
bbdff76a
WD
539+ for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
540+ len = strlen(name) + 1;
541+
542+ if (x->count >= x->alloc) {
543+ size_t new_alloc;
544+ rsync_xa *new_rxas;
545+
546+ new_alloc = x->alloc < RSYNC_XAL_INITIAL ? RSYNC_XAL_INITIAL : x->alloc * 2;
547+ new_rxas = realloc_array(x->rxas, rsync_xa, new_alloc);
548+ if (!new_rxas)
549+ out_of_memory("rsync_xal_get");
550+ x->alloc = new_alloc;
551+ x->rxas = new_rxas;
552+ }
553+ datum_size = sys_lgetxattr(fname, name, datumbuf, datumbuf_len);
554+ if (datum_size > (ssize_t)datumbuf_len) {
555+ datum_size = -1;
556+ errno = ERANGE;
557+ }
558+ if (datum_size < 0) {
559+ if (errno == ENOTSUP)
e5754c5f 560+ return -1;
bbdff76a
WD
561+ if (errno == ERANGE) {
562+ datum_size = sys_lgetxattr(fname, name, NULL, 0);
563+ if (datum_size < 0) {
564+ rprintf(FERROR,
565+ "%s: rsync_xal_get: lgetxattr %s failed: %s\n",
566+ fname, name, strerror(errno));
e5754c5f 567+ return -1;
bbdff76a
WD
568+ }
569+ datumbuf = realloc_array(datumbuf, char, datum_size + 1);
570+ if (!datumbuf)
571+ out_of_memory("rsync_xal_get");
572+ datumbuf_len = datum_size;
573+ datum_size = sys_lgetxattr(fname, name, datumbuf, datumbuf_len);
574+ if (datum_size < 0) {
575+ rprintf(FERROR,
576+ "%s: rsync_xal_get: re-lgetxattr of %s failed: %s\n",
577+ name, fname, strerror(errno));
e5754c5f 578+ return -1;
bbdff76a
WD
579+ }
580+ } else {
581+ rprintf(FERROR,
582+ "%s: rsync_xal_get: lgetxattr %s failed: %s\n",
583+ fname, name, strerror(errno));
e5754c5f 584+ return -1;
bbdff76a
WD
585+ }
586+ }
587+ ptr = new_array(char, len + datum_size);
588+ if (!ptr)
589+ out_of_memory("rsync_xal_get");
590+ strcpy(ptr, name);
591+ if (datum_size)
592+ memcpy(ptr + len, datumbuf, datum_size);
593+ x->rxas[curr_rsync_xal.count].name_len = len;
594+ x->rxas[curr_rsync_xal.count].name = ptr;
595+ x->rxas[curr_rsync_xal.count].datum_len = datum_size;
596+ x->rxas[curr_rsync_xal.count].datum = ptr + len;
597+ x->count++;
598+ }
599+ if (x->count > 1) {
600+ qsort(x->rxas, x->count, sizeof (rsync_xa), rsync_xal_compare_names);
601+ }
e5754c5f 602+ return 0;
bbdff76a
WD
603+}
604+
605+
606+/* generate the xattr(s) for this flist entry;
607+ * xattr(s) are either sent or cleaned-up by send_xattr() below */
608+
93d6ca6d 609+int make_xattr(UNUSED(const struct file_struct *file), const char *fname)
bbdff76a 610+{
bbdff76a 611+ rsync_xal_get(fname, &curr_rsync_xal);
e5754c5f 612+ return 0; /* TODO: This needs to return 1 if no xattrs changed! */
bbdff76a
WD
613+}
614+
615+static ssize_t rsync_xal_find_matching(void)
616+{
617+ size_t i;
618+ size_t j;
619+
620+ for (i = 0; i < rsync_xal_l.count; i++) {
621+ /* Wrong number of elements? */
622+ if (rsync_xal_l.rxals[i].count != curr_rsync_xal.count)
623+ continue;
624+ /* any elements different? */
625+ for (j = 0; j < curr_rsync_xal.count; j++) {
626+ if (rsync_xal_l.rxals[i].rxas[j].name_len != curr_rsync_xal.rxas[j].name_len
627+ || rsync_xal_l.rxals[i].rxas[j].datum_len != curr_rsync_xal.rxas[j].datum_len
628+ || strcmp(rsync_xal_l.rxals[i].rxas[j].name, curr_rsync_xal.rxas[j].name)
629+ || memcmp(rsync_xal_l.rxals[i].rxas[j].datum, curr_rsync_xal.rxas[j].datum, curr_rsync_xal.rxas[j].datum_len))
630+ break;
631+ }
632+ /* no differences found. This is The One! */
633+ if (j == curr_rsync_xal.count)
634+ break;
635+ }
636+ if (i < rsync_xal_l.count)
637+ return i;
638+ return (ssize_t)-1;
639+}
640+
641+/* Store curr_rsync_xal on the end of rsync_xal_l */
642+static void rsync_xal_store(void)
643+{
644+ if (rsync_xal_l.count <= rsync_xal_l.alloc) {
645+ size_t new_alloc;
646+ void *new_xal;
647+
648+ new_alloc = rsync_xal_l.count < RSYNC_XAL_LIST_INITIAL ? RSYNC_XAL_LIST_INITIAL : rsync_xal_l.count * 2;
649+ new_xal = realloc_array(rsync_xal_l.rxals, rsync_xal, new_alloc);
650+ if (!new_xal)
651+ out_of_memory("rsync_xal_store");
652+ rsync_xal_l.alloc = new_alloc;
653+ rsync_xal_l.rxals = new_xal;
654+ }
655+ rsync_xal_l.rxals[rsync_xal_l.count] = curr_rsync_xal;
656+ rsync_xal_l.count++;
657+ curr_rsync_xal.count = 0;
658+ curr_rsync_xal.alloc = 0;
659+}
660+
661+/* send the make_xattr()-generated xattr list for this flist entry,
662+ * or clean up after an flist entry that's not being sent (f == -1) */
663+
93d6ca6d 664+void send_xattr(UNUSED(const struct file_struct *file), int f)
bbdff76a
WD
665+{
666+ ssize_t index;
667+
bbdff76a
WD
668+ if (f == -1) {
669+ rsync_xal_free(&curr_rsync_xal);
670+ return;
671+ }
672+ index = rsync_xal_find_matching();
673+ if (index != -1) {
674+ write_byte(f, 'x');
675+ write_int(f, index);
676+ rsync_xal_free(&curr_rsync_xal);
677+ } else {
678+ rsync_xa *rxa;
679+ size_t count;
680+
681+ count = curr_rsync_xal.count;
682+ write_byte(f, 'X');
683+ write_int(f, count);
684+ for (rxa = curr_rsync_xal.rxas; count--; rxa++) {
685+ write_int(f, rxa->name_len);
686+ write_int(f, rxa->datum_len);
687+ write_buf(f, rxa->name, rxa->name_len);
688+ write_buf(f, rxa->datum, rxa->datum_len);
689+ }
690+ rsync_xal_store();
691+ }
692+}
693+
694+
695+/* ------------------------------------------------------------------------- */
696+/* receive and build the rsync_xattr_lists */
697+
698+void receive_xattr(struct file_struct *file, int f)
699+{
700+ char *fname;
701+ int tag;
702+
bd68c3c2 703+ fname = f_name(file, NULL);
bbdff76a
WD
704+ tag = read_byte(f);
705+ if (tag != 'X' && tag != 'x') {
706+ rprintf(FERROR,
707+ "%s: receive_xattr: unknown extended attribute type tag: %c\n",
708+ fname, tag);
709+ exit_cleanup(RERR_STREAMIO);
710+ }
711+
712+ if (fxil.alloc <= fxil.count) {
713+ void *new_ptr;
714+ size_t new_alloc;
715+
716+ if (fxil.count < RSYNC_XAL_LIST_INITIAL)
717+ new_alloc = fxil.alloc + RSYNC_XAL_LIST_INITIAL;
718+ else
719+ new_alloc = fxil.alloc * 2;
720+ new_ptr = realloc_array(fxil.filexalidxs, file_xal_index, new_alloc);
721+ if (!new_ptr)
722+ out_of_memory("receive_xattr");
723+ if (verbose >= 3) {
724+ rprintf(FINFO, "receive_xattr to %lu bytes, %s move\n",
725+ (unsigned long)(new_alloc * sizeof (file_xal_index)),
726+ fxil.filexalidxs == new_ptr ? "did not" : "did");
727+ }
728+
729+ fxil.filexalidxs = new_ptr;
730+ fxil.alloc = new_alloc;
731+ }
732+
733+ fxil.filexalidxs[fxil.count].file = file;
734+ if (tag == 'X') {
735+ size_t count;
736+ size_t i;
737+
738+ fxil.filexalidxs[fxil.count].xalidx = rsync_xal_l.count;
739+
740+ count = read_int(f);
741+ curr_rsync_xal.count = count;
742+ curr_rsync_xal.alloc = count;
743+ curr_rsync_xal.rxas = new_array(rsync_xa, count);
744+ if (!curr_rsync_xal.rxas)
745+ out_of_memory("receive_xattr");
746+ for (i = 0; i < count; i++) {
747+ size_t name_len;
748+ size_t datum_len;
749+ char *ptr;
750+
751+ name_len = read_int(f);
752+ datum_len = read_int(f);
753+ ptr = new_array(char, name_len + datum_len);
754+ if (!ptr)
755+ out_of_memory("receive_xattr");
756+ read_buf(f, ptr, name_len);
757+ read_buf(f, ptr + name_len, datum_len);
758+ curr_rsync_xal.rxas[i].name_len = name_len;
759+ curr_rsync_xal.rxas[i].datum_len = datum_len;
760+ curr_rsync_xal.rxas[i].name = ptr;
761+ curr_rsync_xal.rxas[i].datum = ptr + name_len;
762+ }
763+ rsync_xal_store();
764+ } else {
765+ size_t index;
766+
767+ index = read_int(f);
768+ if (index >= rsync_xal_l.count) {
769+ rprintf(FERROR, "%s: receive_xattr: xa index %lu out of range\n",
e5754c5f 770+ fname, (unsigned long)index);
bbdff76a
WD
771+ exit_cleanup(RERR_STREAMIO);
772+ }
773+ fxil.filexalidxs[fxil.count].xalidx = index;
774+ }
775+ fxil.count++;
776+}
777+
e5754c5f 778+static int rsync_xal_set(const char *fname, rsync_xal *x)
bbdff76a
WD
779+{
780+ size_t i;
e5754c5f 781+ int ret = 0;
bbdff76a 782+
bbdff76a 783+ for (i = 0; i < x->count; i++) {
e5754c5f 784+ int status = sys_lsetxattr(fname, x->rxas[i].name, x->rxas[i].datum, x->rxas[i].datum_len, 0);
bbdff76a
WD
785+ if (status < 0) {
786+ rprintf(FERROR, "%s: rsync_xal_set: lsetxattr %s failed: %s\n",
787+ fname, x->rxas[i].name, strerror(errno));
e5754c5f 788+ ret = -1;
bbdff76a
WD
789+ }
790+ }
791+ return ret;
792+}
793+
794+/* for duplicating xattrs on backups when using backup_dir */
795+
796+int dup_xattr(const char *orig, const char *bak)
797+{
798+ int ret;
799+
e5754c5f 800+ if (rsync_xal_get(orig, &backup_xal) < 0)
bbdff76a 801+ ret = rsync_xal_set(bak, &backup_xal);
e5754c5f
WD
802+ else
803+ ret = 0;
bbdff76a 804+ rsync_xal_free(&backup_xal);
93d6ca6d 805+
bbdff76a
WD
806+ return ret;
807+}
808+
809+void push_keep_backup_xattr(const struct file_struct *file, const char *orig, const char *dest)
810+{
bbdff76a
WD
811+ backup_orig_file = file;
812+ backup_orig_fname = orig;
813+ backup_dest_fname = dest;
814+ rsync_xal_get(orig, &backup_xal);
815+}
816+
817+static int set_keep_backup_xal(void)
818+{
bbdff76a
WD
819+ return rsync_xal_set(backup_dest_fname, &backup_xal);
820+}
821+
822+void cleanup_keep_backup_xattr(void)
823+{
bbdff76a
WD
824+ backup_orig_file = NULL;
825+ backup_orig_fname = null_string;
826+ backup_dest_fname = null_string;
827+ rsync_xal_free(&backup_xal);
828+}
829+
830+static int file_xal_index_compare(const void *x1, const void *x2)
831+{
832+ const file_xal_index *xa1;
833+ const file_xal_index *xa2;
834+
835+ xa1 = x1;
836+ xa2 = x2;
837+ return xa1->file == xa2->file ? 0 : xa1->file < xa2->file ? -1 : 1;
838+}
839+
840+void sort_file_xattr_index_lists(void)
841+{
bbdff76a
WD
842+ qsort(fxil.filexalidxs, fxil.count, sizeof (file_xal_index), file_xal_index_compare);
843+}
844+
845+static int find_file_xal_index(const struct file_struct *file)
846+{
847+ int low = 0, high = fxil.count;
848+ const struct file_struct *file_mid;
849+
850+ if (!high--) {
851+ rprintf(FERROR, "find_file_xal_index: no entries\n");
852+ exit_cleanup(RERR_STREAMIO);
853+ return -1;
854+ }
855+ do {
856+ int mid = (high + low) / 2;
857+ file_mid = fxil.filexalidxs[mid].file;
858+ if (file_mid == file)
859+ return fxil.filexalidxs[mid].xalidx;
860+ if (file_mid > file)
861+ high = mid - 1;
862+ else
863+ low = mid + 1;
864+ } while (low < high);
865+ if (low == high) {
866+ file_mid = fxil.filexalidxs[low].file;
867+ if (file_mid == file)
868+ return fxil.filexalidxs[low].xalidx;
869+ }
870+ rprintf(FERROR,
871+ "find_file_xal_index: can't find entry for file in list\n");
872+ exit_cleanup(RERR_STREAMIO);
873+ return -1;
874+}
875+
bbdff76a
WD
876+/* set extended attributes on rsync-ed or keep_backup-ed file */
877+
878+int set_xattr(const char *fname, const struct file_struct *file)
879+{
bbdff76a
WD
880+ int xalidx;
881+ rsync_xal *x;
882+
93d6ca6d
WD
883+ if (dry_run)
884+ return 1; /* FIXME: --dry-run needs to compute this value */
885+
bbdff76a
WD
886+ if (file == backup_orig_file) {
887+ if (!strcmp(fname, backup_dest_fname))
888+ return set_keep_backup_xal();
889+ }
890+ xalidx = find_file_xal_index(file);
891+ x = &(rsync_xal_l.rxals[xalidx]);
93d6ca6d 892+
e5754c5f 893+ return rsync_xal_set(fname, x);
bbdff76a
WD
894+}
895+
896+#endif /* SUPPORT_XATTRS */