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