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