Simplified the populating of the sx struct's ACL info.
[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
fc068916 105@@ -58,6 +58,8 @@ void setup_protocol(int f_out,int f_in)
7e27b6c0 106 preserve_gid = ++file_extra_cnt;
fdf967c7 107 if (preserve_acls && !am_sender)
7e27b6c0 108 preserve_acls = ++file_extra_cnt;
fdf967c7 109+ if (preserve_xattrs && !am_sender)
7e27b6c0 110+ preserve_xattrs = ++file_extra_cnt;
fdf967c7
WD
111
112 if (remote_protocol == 0) {
113 if (!read_batch)
9a7eef96
WD
114--- old/configure.in
115+++ new/configure.in
7e27b6c0 116@@ -883,6 +883,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
fc068916 159@@ -43,6 +43,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;
524e5ed5 167@@ -865,6 +866,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
524e5ed5
WD
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
fc068916 179 int flags, int filter_flags)
a071aea2
WD
180 {
181 struct file_struct *file;
182-#ifdef SUPPORT_ACLS
183+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
184 statx sx;
185 #endif
186
524e5ed5 187@@ -1156,6 +1161,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
524e5ed5 201@@ -1167,6 +1179,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;
fc068916 389@@ -201,6 +202,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;
fc068916 397@@ -220,7 +222,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
fc068916 408@@ -239,8 +243,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());
fc068916 419@@ -286,7 +290,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");
fc068916 428@@ -311,6 +315,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");
fc068916 438@@ -434,6 +441,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 },
fc068916 448@@ -1122,6 +1132,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()
fc068916 466@@ -1590,6 +1611,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
fc068916
WD
474 if (recurse)
475 argstr[x++] = 'r';
476 if (always_checksum)
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;
fc068916 487@@ -321,6 +322,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
fc068916 500@@ -554,6 +554,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 {
fc068916 511@@ -574,6 +578,7 @@ extern int file_extra_cnt;
6eba67ec
WD
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))
fc068916
WD
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
1aa236e1
WD
522 #define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
523+#define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->unum
70891d26 524
8aec0853 525 /* These items are per-entry optional and mutally exclusive: */
fdf967c7 526 #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
fc068916 527@@ -798,6 +804,9 @@ typedef struct {
0f6fb3c8
WD
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;
bbdff76a 533+#endif
0f6fb3c8 534 } statx;
bbdff76a 535
0f6fb3c8 536 #define ACL_READY(sx) ((sx).acc_acl != NULL)
9a7eef96
WD
537--- old/rsync.yo
538+++ new/rsync.yo
4959107f 539@@ -301,7 +301,7 @@ to the detailed description below for a
1b57ecb0 540 -q, --quiet suppress non-error messages
4959107f 541 --no-motd suppress daemon-mode MOTD (see caveat)
1b57ecb0
WD
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
4959107f 548@@ -324,6 +324,7 @@ to the detailed description below for a
4a65fe72 549 -E, --executability preserve executability
063cf77b 550 --chmod=CHMOD affect file and/or directory permissions
4a65fe72
WD
551 -A, --acls preserve ACLs (implies -p) [non-standard]
552+ -X, --xattrs preserve extended attrs (implies -p) [n.s.]
4a65fe72 553 -o, --owner preserve owner (super-user only)
bbdff76a 554 -g, --group preserve group
1ed0b5c9 555 --devices preserve device files (super-user only)
524e5ed5 556@@ -835,6 +836,11 @@ The ACL-sending protocol used by this ve
590329e5
WD
557 the patch that was shipped with 2.6.8. Sending ACLs to an older version
558 of the ACL patch is not supported.
bbdff76a
WD
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+
4a65fe72
WD
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
9a7eef96
WD
568--- old/xattr.c
569+++ new/xattr.c
6eba67ec 570@@ -0,0 +1,413 @@
dc52bc1a
WD
571+/*
572+ * Extended Attribute support for rsync.
0f6fb3c8 573+ * Written by Jay Fenlason, vaguely based on the ACLs patch.
dc52bc1a
WD
574+ *
575+ * Copyright (C) 2004 Red Hat, Inc.
0f6fb3c8 576+ * Copyright (C) 2006 Wayne Davison
dc52bc1a
WD
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+ */
bbdff76a
WD
592+
593+#include "rsync.h"
594+#include "lib/sysxattr.h"
595+
596+#ifdef SUPPORT_XATTRS
597+
bbdff76a 598+extern int dry_run;
3f24a3a8 599+extern int am_root;
8889f85e
WD
600+extern int read_only;
601+extern int list_only;
bbdff76a
WD
602+
603+#define RSYNC_XAL_INITIAL 5
604+#define RSYNC_XAL_LIST_INITIAL 100
605+
922f4409
WD
606+#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
607+ && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
608+
3f24a3a8 609+#define USER_PREFIX "user."
27502d8d
WD
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
3f24a3a8 620+
bbdff76a 621+typedef struct {
27502d8d
WD
622+ char *datum, *name;
623+ size_t datum_len, name_len;
bbdff76a
WD
624+} rsync_xa;
625+
bbdff76a
WD
626+static size_t namebuf_len = 0;
627+static char *namebuf = NULL;
628+
0f6fb3c8
WD
629+static item_list empty_xattr = EMPTY_ITEM_LIST;
630+static item_list rsync_xal_l = EMPTY_ITEM_LIST;
bbdff76a
WD
631+
632+/* ------------------------------------------------------------------------- */
633+
0f6fb3c8 634+static void rsync_xal_free(item_list *xalp)
bbdff76a
WD
635+{
636+ size_t i;
0f6fb3c8 637+ rsync_xa *rxas = xalp->items;
bbdff76a 638+
0f6fb3c8 639+ for (i = 0; i < xalp->count; i++) {
27502d8d
WD
640+ free(rxas[i].datum);
641+ /*free(rxas[i].name);*/
bbdff76a 642+ }
0f6fb3c8 643+ xalp->count = 0;
bbdff76a
WD
644+}
645+
0f6fb3c8 646+void free_xattr(statx *sxp)
bbdff76a 647+{
0f6fb3c8
WD
648+ rsync_xal_free(sxp->xattr);
649+ free(sxp->xattr);
650+ sxp->xattr = NULL;
651+}
bbdff76a 652+
0f6fb3c8
WD
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;
bbdff76a
WD
657+ return strcmp(xa1->name, xa2->name);
658+}
659+
0f6fb3c8 660+static int rsync_xal_get(const char *fname, item_list *xalp)
bbdff76a 661+{
27502d8d
WD
662+ ssize_t list_len, name_len, datum_len;
663+ char *name, *ptr;
bbdff76a
WD
664+
665+ if (!namebuf) {
49de5440 666+ namebuf_len = 1024;
bbdff76a 667+ namebuf = new_array(char, namebuf_len);
49de5440 668+ if (!namebuf)
bbdff76a
WD
669+ out_of_memory("rsync_xal_get");
670+ }
671+
49de5440 672+ /* The length returned includes all the '\0' terminators. */
27502d8d
WD
673+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
674+ if (list_len > (ssize_t)namebuf_len) {
675+ list_len = -1;
bbdff76a
WD
676+ errno = ERANGE;
677+ }
27502d8d 678+ if (list_len < 0) {
bbdff76a 679+ if (errno == ENOTSUP)
0f6fb3c8 680+ return 0;
bbdff76a 681+ if (errno == ERANGE) {
27502d8d
WD
682+ list_len = sys_llistxattr(fname, NULL, 0);
683+ if (list_len < 0) {
892d2287
WD
684+ rsyserr(FERROR, errno,
685+ "rsync_xal_get: llistxattr(\"%s\",0) failed",
0f6fb3c8 686+ fname);
e5754c5f 687+ return -1;
bbdff76a 688+ }
27502d8d 689+ namebuf = realloc_array(namebuf, char, list_len + 1024);
bbdff76a
WD
690+ if (!namebuf)
691+ out_of_memory("rsync_xal_get");
27502d8d
WD
692+ namebuf_len = list_len + 1024;
693+ list_len = sys_llistxattr(fname, namebuf, namebuf_len);
694+ if (list_len < 0) {
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+ } else {
0f6fb3c8 701+ rsyserr(FERROR, errno,
892d2287
WD
702+ "rsync_xal_get: llistxattr(\"%s\",%ld) failed",
703+ fname, (long)namebuf_len);
e5754c5f 704+ return -1;
bbdff76a
WD
705+ }
706+ }
27502d8d 707+
892d2287 708+ for (name = namebuf; list_len > 0; name += name_len) {
3f24a3a8 709+ rsync_xa *rxas;
bbdff76a 710+
27502d8d 711+ name_len = strlen(name) + 1;
892d2287 712+ list_len -= name_len;
3f24a3a8
WD
713+
714+#ifdef HAVE_LINUX_XATTRS
27502d8d 715+ /* We don't send the system namespace. */
922f4409 716+ if (HAS_PREFIX(name, SYSTEM_PREFIX))
3f24a3a8
WD
717+ continue;
718+#endif
719+
27502d8d
WD
720+ datum_len = sys_lgetxattr(fname, name, NULL, 0);
721+ if (datum_len < 0) {
bbdff76a 722+ if (errno == ENOTSUP)
e5754c5f 723+ return -1;
49de5440 724+ rsyserr(FERROR, errno,
892d2287 725+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",0) failed",
49de5440
WD
726+ fname, name);
727+ return -1;
bbdff76a 728+ }
27502d8d 729+ ptr = new_array(char, name_len + datum_len);
bbdff76a
WD
730+ if (!ptr)
731+ out_of_memory("rsync_xal_get");
27502d8d
WD
732+ if (datum_len) {
733+ ssize_t len = sys_lgetxattr(fname, name, ptr, datum_len);
734+ if (len != datum_len) {
735+ if (len < 0) {
49de5440 736+ rsyserr(FERROR, errno,
892d2287
WD
737+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
738+ " failed", fname, name, (long)datum_len);
49de5440
WD
739+ } else {
740+ rprintf(FERROR,
892d2287
WD
741+ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)"
742+ " returned %ld\n", fname, name,
743+ (long)datum_len, (long)len);
49de5440 744+ }
27502d8d 745+ free(ptr);
49de5440
WD
746+ return -1;
747+ }
748+ }
27502d8d
WD
749+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
750+ rxas->name = ptr + datum_len;
b54d5ec2 751+ rxas->datum = ptr;
27502d8d
WD
752+ rxas->name_len = name_len;
753+ rxas->datum_len = datum_len;
b54d5ec2 754+ memcpy(rxas->name, name, name_len);
bbdff76a 755+ }
0f6fb3c8
WD
756+ if (xalp->count > 1)
757+ qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
e5754c5f 758+ return 0;
bbdff76a
WD
759+}
760+
0f6fb3c8
WD
761+/* Read the xattr(s) for this filename. */
762+int get_xattr(const char *fname, statx *sxp)
bbdff76a 763+{
0f6fb3c8
WD
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;
bbdff76a
WD
771+}
772+
0f6fb3c8 773+static int find_matching_xattr(item_list *xalp)
bbdff76a 774+{
0f6fb3c8
WD
775+ size_t i, j;
776+ item_list *lst = rsync_xal_l.items;
bbdff76a
WD
777+
778+ for (i = 0; i < rsync_xal_l.count; i++) {
0f6fb3c8
WD
779+ rsync_xa *rxas1 = lst[i].items;
780+ rsync_xa *rxas2 = xalp->items;
781+
bbdff76a 782+ /* Wrong number of elements? */
0f6fb3c8 783+ if (lst[i].count != xalp->count)
bbdff76a
WD
784+ continue;
785+ /* any elements different? */
0f6fb3c8
WD
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))
bbdff76a
WD
791+ break;
792+ }
793+ /* no differences found. This is The One! */
0f6fb3c8
WD
794+ if (j == xalp->count)
795+ return i;
bbdff76a 796+ }
0f6fb3c8
WD
797+
798+ return -1;
bbdff76a
WD
799+}
800+
0f6fb3c8
WD
801+/* Store *xalp on the end of rsync_xal_l */
802+static void rsync_xal_store(item_list *xalp)
bbdff76a 803+{
0f6fb3c8 804+ item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
de565f59
WD
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. */
49de5440
WD
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));
0f6fb3c8
WD
810+ new_lst->count = xalp->count;
811+ xalp->count = 0;
bbdff76a
WD
812+}
813+
0f6fb3c8
WD
814+/* Send the make_xattr()-generated xattr list for this flist entry. */
815+void send_xattr(statx *sxp, int f)
bbdff76a 816+{
0f6fb3c8
WD
817+ int ndx = find_matching_xattr(sxp->xattr);
818+ if (ndx != -1) {
bbdff76a 819+ write_byte(f, 'x');
0f6fb3c8
WD
820+ write_int(f, ndx);
821+ rsync_xal_free(sxp->xattr);
bbdff76a
WD
822+ } else {
823+ rsync_xa *rxa;
0f6fb3c8 824+ int count = sxp->xattr->count;
bbdff76a
WD
825+ write_byte(f, 'X');
826+ write_int(f, count);
0f6fb3c8 827+ for (rxa = sxp->xattr->items; count--; rxa++) {
27502d8d
WD
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. */
dfe2b257
WD
835+ if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
836+ && rxa->name[RPRE_LEN] != '%') {
27502d8d
WD
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 {
3f24a3a8
WD
841+ write_int(f, rxa->name_len + UPRE_LEN);
842+ write_int(f, rxa->datum_len);
843+ write_buf(f, USER_PREFIX, UPRE_LEN);
27502d8d 844+ write_buf(f, rxa->name, rxa->name_len);
3f24a3a8 845+ }
27502d8d 846+#endif
bbdff76a
WD
847+ write_buf(f, rxa->datum, rxa->datum_len);
848+ }
49de5440 849+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
bbdff76a 850+ }
0f6fb3c8 851+ free_xattr(sxp);
bbdff76a
WD
852+}
853+
bbdff76a 854+/* ------------------------------------------------------------------------- */
bbdff76a 855+
0f6fb3c8 856+/* receive and build the rsync_xattr_lists */
bbdff76a
WD
857+void receive_xattr(struct file_struct *file, int f)
858+{
0f6fb3c8 859+ static item_list temp_xattr = EMPTY_ITEM_LIST;
27502d8d 860+ int ndx, tag = read_byte(f);
bbdff76a 861+
bbdff76a 862+ if (tag == 'X') {
0f6fb3c8 863+ int i, count = read_int(f);
bbdff76a 864+ for (i = 0; i < count; i++) {
27502d8d 865+ char *ptr, *name;
0f6fb3c8
WD
866+ rsync_xa *rxa;
867+ size_t name_len = read_int(f);
868+ size_t datum_len = read_int(f);
bcecb5e3
WD
869+#ifdef HAVE_LINUX_XATTRS
870+ size_t extra_len = 0;
871+#else
872+ size_t extra_len = am_root ? RPRE_LEN : 0;
922f4409 873+ if (datum_len + extra_len < datum_len)
27502d8d
WD
874+ out_of_memory("receive_xattr"); /* overflow */
875+#endif
922f4409
WD
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);
bbdff76a
WD
879+ if (!ptr)
880+ out_of_memory("receive_xattr");
922f4409 881+ name = ptr + datum_len + extra_len;
27502d8d
WD
882+ read_buf(f, name, name_len);
883+ read_buf(f, ptr, datum_len);
3f24a3a8 884+#ifdef HAVE_LINUX_XATTRS
27502d8d 885+ /* Non-root can only save the user namespace. */
922f4409 886+ if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
3f24a3a8
WD
887+ free(ptr);
888+ continue;
889+ }
890+#else
27502d8d
WD
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. */
922f4409 894+ if (HAS_PREFIX(name, USER_PREFIX)) {
27502d8d
WD
895+ name += UPRE_LEN;
896+ name_len -= UPRE_LEN;
922f4409 897+ } else if (am_root) {
27502d8d 898+ name -= RPRE_LEN;
b54d5ec2 899+ name_len += RPRE_LEN;
27502d8d 900+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
922f4409
WD
901+ } else {
902+ free(ptr);
903+ continue;
517cc92f
WD
904+ }
905+#endif
27502d8d
WD
906+ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
907+ rxa->name = name;
27502d8d 908+ rxa->datum = ptr;
b54d5ec2 909+ rxa->name_len = name_len;
27502d8d 910+ rxa->datum_len = datum_len;
bbdff76a 911+ }
49de5440
WD
912+ ndx = rsync_xal_l.count; /* pre-incremented count */
913+ rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
0f6fb3c8
WD
914+ } else if (tag == 'x') {
915+ ndx = read_int(f);
916+ if (ndx < 0 || (size_t)ndx >= rsync_xal_l.count) {
892d2287
WD
917+ rprintf(FERROR, "receive_xattr: xa index %d out of"
918+ " range for %s\n", ndx, f_name(file, NULL));
bbdff76a
WD
919+ exit_cleanup(RERR_STREAMIO);
920+ }
0f6fb3c8 921+ } else {
892d2287
WD
922+ rprintf(FERROR, "receive_xattr: unknown extended attribute"
923+ " type tag (%c) for %s\n", tag, f_name(file, NULL));
0f6fb3c8 924+ exit_cleanup(RERR_STREAMIO);
bbdff76a 925+ }
bbdff76a 926+
70891d26 927+ F_XATTR(file) = ndx;
bbdff76a
WD
928+}
929+
0f6fb3c8
WD
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)
bbdff76a 933+{
0f6fb3c8 934+ int ndx;
bbdff76a 935+
0f6fb3c8
WD
936+ if (!sxp->xattr)
937+ return;
bbdff76a 938+
0f6fb3c8
WD
939+ ndx = find_matching_xattr(sxp->xattr);
940+ if (ndx == -1)
49de5440 941+ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
0f6fb3c8 942+ free_xattr(sxp);
bbdff76a 943+
70891d26 944+ F_XATTR(file) = ndx;
bbdff76a
WD
945+}
946+
0f6fb3c8 947+static int rsync_xal_set(const char *fname, item_list *xalp)
bbdff76a 948+{
0f6fb3c8
WD
949+ rsync_xa *rxas = xalp->items;
950+ size_t i;
951+ int ret = 0;
bbdff76a 952+
0f6fb3c8 953+ for (i = 0; i < xalp->count; i++) {
742a4103 954+ int status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len);
0f6fb3c8 955+ if (status < 0) {
892d2287
WD
956+ rsyserr(FERROR, errno,
957+ "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
0f6fb3c8
WD
958+ fname, rxas[i].name);
959+ ret = -1;
960+ }
bbdff76a 961+ }
0f6fb3c8 962+ return ret;
bbdff76a
WD
963+}
964+
0f6fb3c8
WD
965+/* Set extended attributes on indicated filename. */
966+int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
bbdff76a 967+{
0f6fb3c8 968+ int ndx;
0f6fb3c8 969+ item_list *lst = rsync_xal_l.items;
bbdff76a 970+
93d6ca6d
WD
971+ if (dry_run)
972+ return 1; /* FIXME: --dry-run needs to compute this value */
973+
8889f85e
WD
974+ if (read_only || list_only) {
975+ errno = EROFS;
976+ return -1;
977+ }
978+
70891d26 979+ ndx = F_XATTR(file);
0f6fb3c8 980+ return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
bbdff76a
WD
981+}
982+
983+#endif /* SUPPORT_XATTRS */