Fixed failing hunks.
[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 20
a74926e4
WD
21 - Extraneous xattr values need to be removed from files that are not being
22 recreated.
23
0f6fb3c8
WD
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
9a7eef96
WD
29--- old/Makefile.in
30+++ new/Makefile.in
1ed0b5c9 31@@ -28,13 +28,13 @@ VERSION=@VERSION@
bbdff76a
WD
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@
4bf6f8c7
WD
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
590329e5
WD
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 \
610969d1
WD
42- fileio.o batch.o clientname.o chmod.o acls.o
43+ fileio.o batch.o clientname.o chmod.o acls.o xattr.o
bbdff76a
WD
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 \
9a7eef96
WD
47--- old/backup.c
48+++ new/backup.c
70891d26
WD
49@@ -24,6 +24,7 @@
50 extern int verbose;
93d6ca6d
WD
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;
1aa236e1 57@@ -135,6 +136,9 @@ static int make_bak_dir(char *fullpath)
0f6fb3c8
WD
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
1aa236e1 67@@ -143,6 +147,12 @@ static int make_bak_dir(char *fullpath)
0f6fb3c8
WD
68 cache_acl(file, &sx);
69 }
09cc6650
WD
70 #endif
71+#ifdef SUPPORT_XATTRS
0f6fb3c8
WD
72+ if (preserve_xattrs) {
73+ get_xattr(rel, &sx);
74+ cache_xattr(file, &sx);
75+ }
09cc6650 76+#endif
0f6fb3c8
WD
77 set_file_attrs(fullpath, file, NULL, 0);
78 free(file);
bbdff76a 79 }
1aa236e1 80@@ -194,6 +204,9 @@ static int keep_backup(const char *fname
0f6fb3c8
WD
81 #ifdef SUPPORT_ACLS
82 sx.acc_acl = sx.def_acl = NULL;
09cc6650
WD
83 #endif
84+#ifdef SUPPORT_XATTRS
0f6fb3c8 85+ sx.xattr = NULL;
09cc6650 86+#endif
bbdff76a 87
0f6fb3c8
WD
88 if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
89 return 1; /* the file could have disappeared */
1aa236e1 90@@ -209,6 +222,12 @@ static int keep_backup(const char *fname
0f6fb3c8
WD
91 cache_acl(file, &sx);
92 }
09cc6650
WD
93 #endif
94+#ifdef SUPPORT_XATTRS
0f6fb3c8
WD
95+ if (preserve_xattrs) {
96+ get_xattr(fname, &sx);
97+ cache_xattr(file, &sx);
98+ }
09cc6650 99+#endif
bbdff76a 100
0f6fb3c8
WD
101 /* Check to see if this is a device file, or link */
102 if ((am_root && preserve_devices && IS_DEVICE(file->mode))
fdf967c7
WD
103--- old/compat.c
104+++ new/compat.c
105@@ -50,6 +50,8 @@ void setup_protocol(int f_out,int f_in)
106 preserve_gid = ++flist_extra_cnt;
107 if (preserve_acls && !am_sender)
108 preserve_acls = ++flist_extra_cnt;
109+ if (preserve_xattrs && !am_sender)
110+ preserve_xattrs = ++flist_extra_cnt;
111
112 if (remote_protocol == 0) {
113 if (!read_batch)
9a7eef96
WD
114--- old/configure.in
115+++ new/configure.in
5e3c6c93 116@@ -878,6 +878,40 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
bbdff76a
WD
117 AC_MSG_RESULT(no)
118 )
119
120+AC_CHECK_HEADERS(attr/xattr.h)
49de5440 121+AC_CHECK_HEADERS(sys/xattr.h)
742a4103 122+AC_CHECK_HEADERS(sys/extattr.h)
bbdff76a 123+AC_MSG_CHECKING(whether to support extended attributes)
f787c90c
WD
124+AC_ARG_ENABLE(xattr-support,
125+AC_HELP_STRING([--enable-xattr-support], [Include extended attribute support (default=no)]),
3b05e91f 126+[ case "$enableval" in
bbdff76a
WD
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+ ;;
49de5440
WD
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+ ;;
742a4103
WD
137+ freebsd*)
138+ AC_MSG_RESULT(Using FreeBSD extattrs)
139+ AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
140+ ;;
bbdff76a 141+ *)
49de5440 142+ AC_MSG_RESULT(Xattrs requested but not Linux or OS X. Good luck...)
bbdff76a
WD
143+ ;;
144+ esac
145+ ;;
146+ *)
147+ AC_MSG_RESULT(no)
742378cf 148+ AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes])
bbdff76a
WD
149+ esac ],
150+ AC_MSG_RESULT(no)
56473cb9 151+ AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes])
bbdff76a
WD
152+)
153+
154 AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
155 AC_OUTPUT
156
9a7eef96
WD
157--- old/flist.c
158+++ new/flist.c
590329e5 159@@ -42,6 +42,7 @@ extern int one_file_system;
93d6ca6d
WD
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;
fdf967c7 167@@ -812,6 +813,10 @@ static struct file_struct *recv_file_ent
0f6fb3c8
WD
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 return file;
177 }
fdf967c7 178@@ -1082,7 +1087,7 @@ static struct file_struct *send_file_nam
a071aea2
WD
179 unsigned short 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
fdf967c7 187@@ -1102,6 +1107,13 @@ static struct file_struct *send_file_nam
0f6fb3c8
WD
188 return NULL;
189 }
09cc6650
WD
190 #endif
191+#ifdef SUPPORT_XATTRS
e70fd6a8 192+ if (preserve_xattrs && f >= 0) {
0f6fb3c8
WD
193+ sx.xattr = NULL;
194+ if (get_xattr(fname, &sx) < 0)
195+ return NULL;
196+ }
09cc6650 197+#endif
bbdff76a 198
a2f9a486
WD
199 maybe_emit_filelist_progress(flist->count + flist_count_offset);
200
fdf967c7 201@@ -1113,6 +1125,10 @@ static struct file_struct *send_file_nam
7c15d5aa
WD
202 if (preserve_acls)
203 send_acl(&sx, f);
09cc6650
WD
204 #endif
205+#ifdef SUPPORT_XATTRS
7c15d5aa
WD
206+ if (preserve_xattrs)
207+ send_xattr(&sx, f);
09cc6650 208+#endif
7c15d5aa 209 }
bbdff76a
WD
210 return file;
211 }
9a7eef96
WD
212--- old/lib/sysxattr.c
213+++ new/lib/sysxattr.c
bdeab877 214@@ -0,0 +1,135 @@
dc52bc1a
WD
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+ */
bbdff76a
WD
235+
236+#include "rsync.h"
0f6fb3c8 237+#include "sysxattr.h"
bbdff76a 238+
56473cb9
WD
239+#ifdef SUPPORT_XATTRS
240+
09cc6650 241+#if defined HAVE_LINUX_XATTRS
bbdff76a
WD
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+
8627d8a2
WD
248+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
249+{
0bac098f 250+ return fgetxattr(filedes, name, value, size);
8627d8a2
WD
251+}
252+
742a4103 253+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
bbdff76a 254+{
742a4103 255+ return lsetxattr(path, name, value, size, 0);
bbdff76a
WD
256+}
257+
a74926e4
WD
258+int sys_lremovexattr(const char *path, const char *name)
259+{
260+ return lremovexattr(path, name);
261+}
262+
bbdff76a
WD
263+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
264+{
265+ return llistxattr(path, list, size);
266+}
267+
49de5440
WD
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+
8627d8a2
WD
275+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
276+{
0bac098f 277+ return fgetxattr(filedes, name, value, size, 0, 0);
8627d8a2
WD
278+}
279+
742a4103 280+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
49de5440 281+{
742a4103 282+ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
49de5440
WD
283+}
284+
a74926e4
WD
285+int sys_lremovexattr(const char *path, const char *name)
286+{
287+ return removexattr(path, name, XATTR_NOFOLLOW);
288+}
289+
49de5440
WD
290+ssize_t sys_llistxattr(const char *path, char *list, size_t size)
291+{
292+ return listxattr(path, list, size, XATTR_NOFOLLOW);
293+}
294+
742a4103
WD
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. */
bdeab877 329+ for (off = 0; off < len; off += keylen + 1) {
742a4103
WD
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);
bdeab877 337+ list[off+keylen] = '\0';
742a4103
WD
338+ }
339+
340+ return len;
341+}
342+
bbdff76a
WD
343+#else
344+
56473cb9
WD
345+#error You need to create xattr compatibility functions.
346+
347+#endif
348+
349+#endif /* SUPPORT_XATTRS */
9a7eef96
WD
350--- old/lib/sysxattr.h
351+++ new/lib/sysxattr.h
742a4103 352@@ -0,0 +1,26 @@
49de5440 353+#ifdef SUPPORT_XATTRS
8889f85e 354+
49de5440 355+#if defined HAVE_ATTR_XATTR_H
0f6fb3c8 356+#include <attr/xattr.h>
49de5440
WD
357+#elif defined HAVE_SYS_XATTR_H
358+#include <sys/xattr.h>
742a4103
WD
359+#elif defined HAVE_SYS_EXTATTR_H
360+#include <sys/extattr.h>
49de5440 361+#endif
bbdff76a 362+
a74926e4
WD
363+/* Linux 2.4 does not define this as a distinct errno value: */
364+#ifndef ENOATTR
365+#define ENOATTR ENODATA
366+#endif
367+
bbdff76a 368+ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
8627d8a2 369+ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
742a4103 370+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
a74926e4 371+int sys_lremovexattr(const char *path, const char *name);
bbdff76a
WD
372+ssize_t sys_llistxattr(const char *path, char *list, size_t size);
373+
374+#else
375+
8889f85e 376+/* No xattrs available */
49de5440
WD
377+
378+#endif
9a7eef96
WD
379--- old/options.c
380+++ new/options.c
dc52bc1a 381@@ -48,6 +48,7 @@ int copy_links = 0;
bbdff76a
WD
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;
4a65fe72 387 int preserve_executability = 0;
bbdff76a 388 int preserve_devices = 0;
fdf967c7 389@@ -200,6 +201,7 @@ static void print_rsync_version(enum log
bbdff76a
WD
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;
fdf967c7 397@@ -219,7 +221,9 @@ static void print_rsync_version(enum log
bbdff76a
WD
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
fdf967c7 408@@ -238,8 +242,8 @@ static void print_rsync_version(enum log
5e3c6c93
WD
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);
56473cb9 416
5e3c6c93
WD
417 #ifdef MAINTAINER_MODE
418 rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
fdf967c7 419@@ -285,7 +289,7 @@ void usage(enum logcode F)
1b57ecb0 420 rprintf(F," -q, --quiet suppress non-error messages\n");
4959107f 421 rprintf(F," --no-motd suppress daemon-mode MOTD (see manpage caveat)\n");
1b57ecb0
WD
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");
fdf967c7 428@@ -310,6 +314,9 @@ void usage(enum logcode F)
3610c43c 429 #ifdef SUPPORT_ACLS
bbdff76a 430 rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
3610c43c
WD
431 #endif
432+#ifdef SUPPORT_XATTRS
bbdff76a 433+ rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n");
3610c43c 434+#endif
4a65fe72 435 rprintf(F," -o, --owner preserve owner (super-user only)\n");
bbdff76a 436 rprintf(F," -g, --group preserve group\n");
1ed0b5c9 437 rprintf(F," --devices preserve device files (super-user only)\n");
fdf967c7 438@@ -433,6 +440,9 @@ static struct poptOption long_options[]
489b0a72
WD
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 },
fdf967c7 448@@ -1115,6 +1125,17 @@ int parse_arguments(int *argc, const cha
489b0a72 449 return 0;
3610c43c 450 #endif
bbdff76a
WD
451
452+ case 'X':
453+#ifdef SUPPORT_XATTRS
454+ preserve_xattrs = 1;
455+ preserve_perms = 1;
489b0a72 456+ break;
bbdff76a
WD
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;
d2dc8a18 462+#endif
bbdff76a
WD
463
464 default:
465 /* A large opt value means that set_refuse_options()
fdf967c7 466@@ -1562,6 +1583,10 @@ void server_options(char **args,int *arg
bbdff76a
WD
467 if (preserve_acls)
468 argstr[x++] = 'A';
3610c43c
WD
469 #endif
470+#ifdef SUPPORT_XATTRS
bbdff76a
WD
471+ if (preserve_xattrs)
472+ argstr[x++] = 'X';
3610c43c 473+#endif
bbdff76a
WD
474 if (preserve_uid)
475 argstr[x++] = 'o';
476 if (preserve_gid)
9a7eef96
WD
477--- old/rsync.c
478+++ new/rsync.c
ff318e90
WD
479@@ -33,6 +33,7 @@
480 extern int verbose;
93d6ca6d 481 extern int dry_run;
93d6ca6d
WD
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;
70891d26 487@@ -271,6 +272,10 @@ int set_file_attrs(char *fname, struct f
071c5b19
WD
488 if (daemon_chmod_modes && !S_ISLNK(new_mode))
489 new_mode = tweak_mode(new_mode, daemon_chmod_modes);
490
09cc6650 491+#ifdef SUPPORT_XATTRS
0f6fb3c8 492+ if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
09cc6650
WD
493+ updated = 1;
494+#endif
071c5b19
WD
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.
9a7eef96
WD
498--- old/rsync.h
499+++ new/rsync.h
fdf967c7 500@@ -511,6 +511,10 @@ struct idev_node {
0f6fb3c8 501 #define ACLS_NEED_MASK 1
09cc6650 502 #endif
bbdff76a 503
56473cb9 504+#ifndef HAVE_NO_XATTRS
bbdff76a
WD
505+#define SUPPORT_XATTRS 1
506+#endif
507+
0f6fb3c8
WD
508 #define GID_NONE ((gid_t)-1)
509
590329e5 510 union file_extras {
fdf967c7
WD
511@@ -563,6 +567,7 @@ extern int preserve_gid;
512 #define F_UID(f) REQ_EXTRA(f, preserve_uid)->unum
513 #define F_GID(f) REQ_EXTRA(f, preserve_gid)->unum
1aa236e1
WD
514 #define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
515+#define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->unum
70891d26 516
8aec0853 517 /* These items are per-entry optional and mutally exclusive: */
fdf967c7
WD
518 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
519@@ -738,6 +743,9 @@ typedef struct {
0f6fb3c8
WD
520 struct rsync_acl *acc_acl; /* access ACL */
521 struct rsync_acl *def_acl; /* default ACL */
522 #endif
523+#ifdef SUPPORT_XATTRS
524+ item_list *xattr;
bbdff76a 525+#endif
0f6fb3c8 526 } statx;
bbdff76a 527
0f6fb3c8 528 #define ACL_READY(sx) ((sx).acc_acl != NULL)
9a7eef96
WD
529--- old/rsync.yo
530+++ new/rsync.yo
4959107f 531@@ -301,7 +301,7 @@ to the detailed description below for a
1b57ecb0 532 -q, --quiet suppress non-error messages
4959107f 533 --no-motd suppress daemon-mode MOTD (see caveat)
1b57ecb0
WD
534 -c, --checksum skip based on checksum, not mod-time & size
535- -a, --archive archive mode; same as -rlptgoD (no -H, -A)
536+ -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)
537 --no-OPTION turn off an implied OPTION (e.g. --no-D)
538 -r, --recursive recurse into directories
539 -R, --relative use relative path names
4959107f 540@@ -324,6 +324,7 @@ to the detailed description below for a
4a65fe72 541 -E, --executability preserve executability
063cf77b 542 --chmod=CHMOD affect file and/or directory permissions
4a65fe72
WD
543 -A, --acls preserve ACLs (implies -p) [non-standard]
544+ -X, --xattrs preserve extended attrs (implies -p) [n.s.]
4a65fe72 545 -o, --owner preserve owner (super-user only)
bbdff76a 546 -g, --group preserve group
1ed0b5c9 547 --devices preserve device files (super-user only)
590329e5
WD
548@@ -818,6 +819,11 @@ The ACL-sending protocol used by this ve
549 the patch that was shipped with 2.6.8. Sending ACLs to an older version
550 of the ACL patch is not supported.
bbdff76a
WD
551
552+dit(bf(-X, --xattrs)) This option causes rsync to update the remote
553+extended attributes to be the same as the local ones. This will work
554+only if the remote machine's rsync supports this option also. This is
555+a non-standard option.
556+
4a65fe72
WD
557 dit(bf(--chmod)) This option tells rsync to apply one or more
558 comma-separated "chmod" strings to the permission of the files in the
559 transfer. The resulting value is treated as though it was the permissions
9a7eef96
WD
560--- old/xattr.c
561+++ new/xattr.c
70891d26 562@@ -0,0 +1,415 @@
dc52bc1a
WD
563+/*
564+ * Extended Attribute support for rsync.
0f6fb3c8 565+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
dc52bc1a
WD
566+ *
567+ * Copyright (C) 2004 Red Hat, Inc.
0f6fb3c8 568+ * Copyright (C) 2006 Wayne Davison
dc52bc1a
WD
569+ *
570+ * This program is free software; you can redistribute it and/or modify
571+ * it under the terms of the GNU General Public License as published by
572+ * the Free Software Foundation; either version 2 of the License, or
573+ * (at your option) any later version.
574+ *
575+ * This program is distributed in the hope that it will be useful,
576+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
577+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
578+ * GNU General Public License for more details.
579+ *
580+ * You should have received a copy of the GNU General Public License along
581+ * with this program; if not, write to the Free Software Foundation, Inc.,
582+ * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
583+ */
bbdff76a
WD
584+
585+#include "rsync.h"
586+#include "lib/sysxattr.h"
587+
588+#ifdef SUPPORT_XATTRS
589+
bbdff76a 590+extern int dry_run;
3f24a3a8 591+extern int am_root;
8889f85e
WD
592+extern int read_only;
593+extern int list_only;
70891d26 594+extern int preserve_xattrs;
0f6fb3c8 595+extern unsigned int file_struct_len;
bbdff76a
WD
596+
597+#define RSYNC_XAL_INITIAL 5
598+#define RSYNC_XAL_LIST_INITIAL 100
599+
922f4409
WD
600+#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
601+ && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
602+
3f24a3a8 603+#define USER_PREFIX "user."
27502d8d
WD
604+#define UPRE_LEN ((int)sizeof USER_PREFIX - 1)
605+#define SYSTEM_PREFIX "system."
606+#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
607+
608+#ifdef HAVE_LINUX_XATTRS
609+#define RPRE_LEN 0
610+#else
611+#define RSYNC_PREFIX "rsync."
612+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
613+#endif
3f24a3a8 614+
bbdff76a 615+typedef struct {
27502d8d
WD
616+ char *datum, *name;
617+ size_t datum_len, name_len;
bbdff76a
WD
618+} rsync_xa;
619+
bbdff76a
WD
620+static size_t namebuf_len = 0;
621+static char *namebuf = NULL;
622+
0f6fb3c8
WD
623+static item_list empty_xattr = EMPTY_ITEM_LIST;
624+static item_list rsync_xal_l = EMPTY_ITEM_LIST;
bbdff76a
WD
625+
626+/* ------------------------------------------------------------------------- */
627+
0f6fb3c8 628+static void rsync_xal_free(item_list *xalp)
bbdff76a
WD
629+{
630+ size_t i;
0f6fb3c8 631+ rsync_xa *rxas = xalp->items;
bbdff76a 632+
0f6fb3c8 633+ for (i = 0; i < xalp->count; i++) {
27502d8d
WD
634+ free(rxas[i].datum);
635+ /*free(rxas[i].name);*/
bbdff76a 636+ }
0f6fb3c8 637+ xalp->count = 0;
bbdff76a
WD
638+}
639+
0f6fb3c8 640+void free_xattr(statx *sxp)
bbdff76a 641+{
0f6fb3c8
WD
642+ rsync_xal_free(sxp->xattr);
643+ free(sxp->xattr);
644+ sxp->xattr = NULL;
645+}
bbdff76a 646+
0f6fb3c8
WD
647+static int rsync_xal_compare_names(const void *x1, const void *x2)
648+{
649+ const rsync_xa *xa1 = x1;
650+ const rsync_xa *xa2 = x2;
bbdff76a
WD
651+ return strcmp(xa1->name, xa2->name);
652+}
653+
0f6fb3c8 654+static int rsync_xal_get(const char *fname, item_list *xalp)
bbdff76a 655+{
27502d8d
WD
656+ ssize_t list_len, name_len, datum_len;
657+ char *name, *ptr;
bbdff76a
WD
658+
659+ if (!namebuf) {
49de5440 660+ namebuf_len = 1024;
bbdff76a 661+ namebuf = new_array(char, namebuf_len);
49de5440 662+ if (!namebuf)
bbdff76a
WD
663+ out_of_memory("rsync_xal_get");
664+ }
665+
49de5440 666+ /* The length returned includes all the '\0' terminators. */
27502d8d
WD
667+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
668+ if (list_len > (ssize_t)namebuf_len) {
669+ list_len = -1;
bbdff76a
WD
670+ errno = ERANGE;
671+ }
27502d8d 672+ if (list_len < 0) {
bbdff76a 673+ if (errno == ENOTSUP)
0f6fb3c8 674+ return 0;
bbdff76a 675+ if (errno == ERANGE) {
27502d8d
WD
676+ list_len = sys_llistxattr(fname, NULL, 0);
677+ if (list_len < 0) {
892d2287
WD
678+ rsyserr(FERROR, errno,
679+ "rsync_xal_get: llistxattr(\"%s\",0) failed",
0f6fb3c8 680+ fname);
e5754c5f 681+ return -1;
bbdff76a 682+ }
27502d8d 683+ namebuf = realloc_array(namebuf, char, list_len + 1024);
bbdff76a
WD
684+ if (!namebuf)
685+ out_of_memory("rsync_xal_get");
27502d8d
WD
686+ namebuf_len = list_len + 1024;
687+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
688+ if (list_len < 0) {
0f6fb3c8 689+ rsyserr(FERROR, errno,
892d2287
WD
690+ "rsync_xal_get: llistxattr(\"%s\",%ld) failed",
691+ fname, (long)namebuf_len);
e5754c5f 692+ return -1;
bbdff76a
WD
693+ }
694+ } else {
0f6fb3c8 695+ rsyserr(FERROR, errno,
892d2287
WD
696+ "rsync_xal_get: llistxattr(\"%s\",%ld) failed",
697+ fname, (long)namebuf_len);
e5754c5f 698+ return -1;
bbdff76a
WD
699+ }
700+ }
27502d8d 701+
892d2287 702+ for (name = namebuf; list_len > 0; name += name_len) {
3f24a3a8 703+ rsync_xa *rxas;
bbdff76a 704+
27502d8d 705+ name_len = strlen(name) + 1;
892d2287 706+ list_len -= name_len;
3f24a3a8
WD
707+
708+#ifdef HAVE_LINUX_XATTRS
27502d8d 709+ /* We don't send the system namespace. */
922f4409 710+ if (HAS_PREFIX(name, SYSTEM_PREFIX))
3f24a3a8
WD
711+ continue;
712+#endif
713+
27502d8d
WD
714+ datum_len = sys_lgetxattr(fname, name, NULL, 0);
715+ if (datum_len < 0) {
bbdff76a 716+ if (errno == ENOTSUP)
e5754c5f 717+ return -1;
49de5440 718+ rsyserr(FERROR, errno,
892d2287 719+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",0) failed",
49de5440
WD
720+ fname, name);
721+ return -1;
bbdff76a 722+ }
27502d8d 723+ ptr = new_array(char, name_len + datum_len);
bbdff76a
WD
724+ if (!ptr)
725+ out_of_memory("rsync_xal_get");
27502d8d
WD
726+ if (datum_len) {
727+ ssize_t len = sys_lgetxattr(fname, name, ptr, datum_len);
728+ if (len != datum_len) {
729+ if (len < 0) {
49de5440 730+ rsyserr(FERROR, errno,
892d2287
WD
731+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
732+ " failed", fname, name, (long)datum_len);
49de5440
WD
733+ } else {
734+ rprintf(FERROR,
892d2287
WD
735+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
736+ " returned %ld\n", fname, name,
737+ (long)datum_len, (long)len);
49de5440 738+ }
27502d8d 739+ free(ptr);
49de5440
WD
740+ return -1;
741+ }
742+ }
27502d8d
WD
743+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
744+ rxas->name = ptr + datum_len;
b54d5ec2 745+ rxas->datum = ptr;
27502d8d
WD
746+ rxas->name_len = name_len;
747+ rxas->datum_len = datum_len;
b54d5ec2 748+ memcpy(rxas->name, name, name_len);
bbdff76a 749+ }
0f6fb3c8
WD
750+ if (xalp->count > 1)
751+ qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
e5754c5f 752+ return 0;
bbdff76a
WD
753+}
754+
0f6fb3c8
WD
755+/* Read the xattr(s) for this filename. */
756+int get_xattr(const char *fname, statx *sxp)
bbdff76a 757+{
0f6fb3c8
WD
758+ sxp->xattr = new(item_list);
759+ *sxp->xattr = empty_xattr;
760+ if (rsync_xal_get(fname, sxp->xattr) < 0) {
761+ free_xattr(sxp);
762+ return -1;
763+ }
764+ return 0;
bbdff76a
WD
765+}
766+
0f6fb3c8 767+static int find_matching_xattr(item_list *xalp)
bbdff76a 768+{
0f6fb3c8
WD
769+ size_t i, j;
770+ item_list *lst = rsync_xal_l.items;
bbdff76a
WD
771+
772+ for (i = 0; i < rsync_xal_l.count; i++) {
0f6fb3c8
WD
773+ rsync_xa *rxas1 = lst[i].items;
774+ rsync_xa *rxas2 = xalp->items;
775+
bbdff76a 776+ /* Wrong number of elements? */
0f6fb3c8 777+ if (lst[i].count != xalp->count)
bbdff76a
WD
778+ continue;
779+ /* any elements different? */
0f6fb3c8
WD
780+ for (j = 0; j < xalp->count; j++) {
781+ if (rxas1[j].name_len != rxas2[j].name_len
782+ || rxas1[j].datum_len != rxas2[j].datum_len
783+ || strcmp(rxas1[j].name, rxas2[j].name)
784+ || memcmp(rxas1[j].datum, rxas2[j].datum, rxas2[j].datum_len))
bbdff76a
WD
785+ break;
786+ }
787+ /* no differences found. This is The One! */
0f6fb3c8
WD
788+ if (j == xalp->count)
789+ return i;
bbdff76a 790+ }
0f6fb3c8
WD
791+
792+ return -1;
bbdff76a
WD
793+}
794+
0f6fb3c8
WD
795+/* Store *xalp on the end of rsync_xal_l */
796+static void rsync_xal_store(item_list *xalp)
bbdff76a 797+{
0f6fb3c8 798+ item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
de565f59
WD
799+ /* Since the following call starts a new list, we know it will hold the
800+ * entire initial-count, not just enough space for one new item. */
49de5440
WD
801+ *new_lst = empty_xattr;
802+ (void)EXPAND_ITEM_LIST(new_lst, rsync_xa, xalp->count);
803+ memcpy(new_lst->items, xalp->items, xalp->count * sizeof (rsync_xa));
0f6fb3c8
WD
804+ new_lst->count = xalp->count;
805+ xalp->count = 0;
bbdff76a
WD
806+}
807+
0f6fb3c8
WD
808+/* Send the make_xattr()-generated xattr list for this flist entry. */
809+void send_xattr(statx *sxp, int f)
bbdff76a 810+{
0f6fb3c8
WD
811+ int ndx = find_matching_xattr(sxp->xattr);
812+ if (ndx != -1) {
bbdff76a 813+ write_byte(f, 'x');
0f6fb3c8
WD
814+ write_int(f, ndx);
815+ rsync_xal_free(sxp->xattr);
bbdff76a
WD
816+ } else {
817+ rsync_xa *rxa;
0f6fb3c8 818+ int count = sxp->xattr->count;
bbdff76a
WD
819+ write_byte(f, 'X');
820+ write_int(f, count);
0f6fb3c8 821+ for (rxa = sxp->xattr->items; count--; rxa++) {
27502d8d
WD
822+#ifdef HAVE_LINUX_XATTRS
823+ write_int(f, rxa->name_len);
824+ write_int(f, rxa->datum_len);
825+ write_buf(f, rxa->name, rxa->name_len);
826+#else
827+ /* We strip the rsync prefix from disguised namespaces
828+ * and put everything else in the user namespace. */
dfe2b257
WD
829+ if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
830+ && rxa->name[RPRE_LEN] != '%') {
27502d8d
WD
831+ write_int(f, rxa->name_len - RPRE_LEN);
832+ write_int(f, rxa->datum_len);
833+ write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
834+ } else {
3f24a3a8
WD
835+ write_int(f, rxa->name_len + UPRE_LEN);
836+ write_int(f, rxa->datum_len);
837+ write_buf(f, USER_PREFIX, UPRE_LEN);
27502d8d 838+ write_buf(f, rxa->name, rxa->name_len);
3f24a3a8 839+ }
27502d8d 840+#endif
bbdff76a
WD
841+ write_buf(f, rxa->datum, rxa->datum_len);
842+ }
49de5440 843+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
bbdff76a 844+ }
0f6fb3c8 845+ free_xattr(sxp);
bbdff76a
WD
846+}
847+
bbdff76a 848+/* ------------------------------------------------------------------------- */
bbdff76a 849+
0f6fb3c8 850+/* receive and build the rsync_xattr_lists */
bbdff76a
WD
851+void receive_xattr(struct file_struct *file, int f)
852+{
0f6fb3c8 853+ static item_list temp_xattr = EMPTY_ITEM_LIST;
27502d8d 854+ int ndx, tag = read_byte(f);
bbdff76a 855+
bbdff76a 856+ if (tag == 'X') {
0f6fb3c8 857+ int i, count = read_int(f);
bbdff76a 858+ for (i = 0; i < count; i++) {
27502d8d 859+ char *ptr, *name;
0f6fb3c8
WD
860+ rsync_xa *rxa;
861+ size_t name_len = read_int(f);
862+ size_t datum_len = read_int(f);
bcecb5e3
WD
863+#ifdef HAVE_LINUX_XATTRS
864+ size_t extra_len = 0;
865+#else
866+ size_t extra_len = am_root ? RPRE_LEN : 0;
922f4409 867+ if (datum_len + extra_len < datum_len)
27502d8d
WD
868+ out_of_memory("receive_xattr"); /* overflow */
869+#endif
922f4409
WD
870+ if (name_len + datum_len + extra_len < name_len)
871+ out_of_memory("receive_xattr"); /* overflow */
872+ ptr = new_array(char, name_len + datum_len + extra_len);
bbdff76a
WD
873+ if (!ptr)
874+ out_of_memory("receive_xattr");
922f4409 875+ name = ptr + datum_len + extra_len;
27502d8d
WD
876+ read_buf(f, name, name_len);
877+ read_buf(f, ptr, datum_len);
3f24a3a8 878+#ifdef HAVE_LINUX_XATTRS
27502d8d 879+ /* Non-root can only save the user namespace. */
922f4409 880+ if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
3f24a3a8
WD
881+ free(ptr);
882+ continue;
883+ }
884+#else
27502d8d
WD
885+ /* This OS only has a user namespace, so we either
886+ * strip the user prefix, or we put a non-user
887+ * namespace inside our rsync hierarchy. */
922f4409 888+ if (HAS_PREFIX(name, USER_PREFIX)) {
27502d8d
WD
889+ name += UPRE_LEN;
890+ name_len -= UPRE_LEN;
922f4409 891+ } else if (am_root) {
27502d8d 892+ name -= RPRE_LEN;
b54d5ec2 893+ name_len += RPRE_LEN;
27502d8d 894+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
922f4409
WD
895+ } else {
896+ free(ptr);
897+ continue;
517cc92f
WD
898+ }
899+#endif
27502d8d
WD
900+ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
901+ rxa->name = name;
27502d8d 902+ rxa->datum = ptr;
b54d5ec2 903+ rxa->name_len = name_len;
27502d8d 904+ rxa->datum_len = datum_len;
bbdff76a 905+ }
49de5440
WD
906+ ndx = rsync_xal_l.count; /* pre-incremented count */
907+ rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
0f6fb3c8
WD
908+ } else if (tag == 'x') {
909+ ndx = read_int(f);
910+ if (ndx < 0 || (size_t)ndx >= rsync_xal_l.count) {
892d2287
WD
911+ rprintf(FERROR, "receive_xattr: xa index %d out of"
912+ " range for %s\n", ndx, f_name(file, NULL));
bbdff76a
WD
913+ exit_cleanup(RERR_STREAMIO);
914+ }
0f6fb3c8 915+ } else {
892d2287
WD
916+ rprintf(FERROR, "receive_xattr: unknown extended attribute"
917+ " type tag (%c) for %s\n", tag, f_name(file, NULL));
0f6fb3c8 918+ exit_cleanup(RERR_STREAMIO);
bbdff76a 919+ }
bbdff76a 920+
70891d26 921+ F_XATTR(file) = ndx;
bbdff76a
WD
922+}
923+
0f6fb3c8
WD
924+/* Turn the xattr data in statx into cached xattr data, setting the index
925+ * values in the file struct. */
926+void cache_xattr(struct file_struct *file, statx *sxp)
bbdff76a 927+{
0f6fb3c8 928+ int ndx;
bbdff76a 929+
0f6fb3c8
WD
930+ if (!sxp->xattr)
931+ return;
bbdff76a 932+
0f6fb3c8
WD
933+ ndx = find_matching_xattr(sxp->xattr);
934+ if (ndx == -1)
49de5440 935+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
0f6fb3c8 936+ free_xattr(sxp);
bbdff76a 937+
70891d26 938+ F_XATTR(file) = ndx;
bbdff76a
WD
939+}
940+
0f6fb3c8 941+static int rsync_xal_set(const char *fname, item_list *xalp)
bbdff76a 942+{
0f6fb3c8
WD
943+ rsync_xa *rxas = xalp->items;
944+ size_t i;
945+ int ret = 0;
bbdff76a 946+
0f6fb3c8 947+ for (i = 0; i < xalp->count; i++) {
742a4103 948+ int status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len);
0f6fb3c8 949+ if (status < 0) {
892d2287
WD
950+ rsyserr(FERROR, errno,
951+ "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
0f6fb3c8
WD
952+ fname, rxas[i].name);
953+ ret = -1;
954+ }
bbdff76a 955+ }
0f6fb3c8 956+ return ret;
bbdff76a
WD
957+}
958+
0f6fb3c8
WD
959+/* Set extended attributes on indicated filename. */
960+int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
bbdff76a 961+{
0f6fb3c8 962+ int ndx;
0f6fb3c8 963+ item_list *lst = rsync_xal_l.items;
bbdff76a 964+
93d6ca6d
WD
965+ if (dry_run)
966+ return 1; /* FIXME: --dry-run needs to compute this value */
967+
8889f85e
WD
968+ if (read_only || list_only) {
969+ errno = EROFS;
970+ return -1;
971+ }
972+
70891d26 973+ ndx = F_XATTR(file);
0f6fb3c8 974+ return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
bbdff76a
WD
975+}
976+
977+#endif /* SUPPORT_XATTRS */