The first version of my reworked xattr patch. We now use a checksum
[rsync-patches.git] / xattrs.diff
1 This patch adds support for extended attributes.
2
3 To use this patch, run these commands for a successful build:
4
5     patch -p1 <patches/xattrs.diff
6     ./prepare-source
7     ./configure
8     make
9
10 CAUTION:  this patch has been recently reworked, and needs more testing!
11
12 TODO:
13
14  - When an xattr value is long enough to be checksummed and is not changed,
15    we need to have the receiver get the unchanged value from the receiving
16    side instead of requesting that it be sent from the sender.
17
18 --- old/Makefile.in
19 +++ new/Makefile.in
20 @@ -28,13 +28,13 @@ VERSION=@VERSION@
21  
22  HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
23  LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
24 -       lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
25 +       lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
26  ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
27         zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
28  OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
29         util.o main.o checksum.o match.o syscall.o log.o backup.o
30  OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o \
31 -       fileio.o batch.o clientname.o chmod.o acls.o
32 +       fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
33  OBJS3=progress.o pipe.o
34  DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
35  popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
36 --- old/backup.c
37 +++ new/backup.c
38 @@ -23,6 +23,7 @@
39  extern int verbose;
40  extern int am_root;
41  extern int preserve_acls;
42 +extern int preserve_xattrs;
43  extern int preserve_devices;
44  extern int preserve_specials;
45  extern int preserve_links;
46 @@ -134,6 +135,9 @@ static int make_bak_dir(char *fullpath)
47  #ifdef SUPPORT_ACLS
48                                 sx.acc_acl = sx.def_acl = NULL;
49  #endif
50 +#ifdef SUPPORT_XATTRS
51 +                               sx.xattr = NULL;
52 +#endif
53                                 if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
54                                         continue;
55  #ifdef SUPPORT_ACLS
56 @@ -143,6 +147,13 @@ static int make_bak_dir(char *fullpath)
57                                         free_acl(&sx);
58                                 }
59  #endif
60 +#ifdef SUPPORT_XATTRS
61 +                               if (preserve_xattrs) {
62 +                                       get_xattr(rel, &sx);
63 +                                       cache_xattr(file, &sx);
64 +                                       free_xattr(&sx);
65 +                               }
66 +#endif
67                                 set_file_attrs(fullpath, file, NULL, 0);
68                                 free(file);
69                         }
70 @@ -194,6 +205,9 @@ static int keep_backup(const char *fname
71  #ifdef SUPPORT_ACLS
72         sx.acc_acl = sx.def_acl = NULL;
73  #endif
74 +#ifdef SUPPORT_XATTRS
75 +       sx.xattr = NULL;
76 +#endif
77  
78         if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
79                 return 1; /* the file could have disappeared */
80 @@ -210,6 +224,13 @@ static int keep_backup(const char *fname
81                 free_acl(&sx);
82         }
83  #endif
84 +#ifdef SUPPORT_XATTRS
85 +       if (preserve_xattrs) {
86 +               get_xattr(fname, &sx);
87 +               cache_xattr(file, &sx);
88 +               free_xattr(&sx);
89 +       }
90 +#endif
91  
92         /* Check to see if this is a device file, or link */
93         if ((am_root && preserve_devices && IS_DEVICE(file->mode))
94 --- old/compat.c
95 +++ new/compat.c
96 @@ -43,6 +43,7 @@ extern int protocol_version;
97  extern int preserve_uid;
98  extern int preserve_gid;
99  extern int preserve_acls;
100 +extern int preserve_xattrs;
101  extern int preserve_hard_links;
102  extern int need_messages_from_generator;
103  extern int delete_mode, delete_before, delete_during, delete_after;
104 @@ -65,6 +66,8 @@ void setup_protocol(int f_out,int f_in)
105                 preserve_gid = ++file_extra_cnt;
106         if (preserve_acls && !am_sender)
107                 preserve_acls = ++file_extra_cnt;
108 +       if (preserve_xattrs)
109 +               preserve_xattrs = ++file_extra_cnt;
110  
111         if (remote_protocol == 0) {
112                 if (!read_batch)
113 @@ -119,6 +122,13 @@ void setup_protocol(int f_out,int f_in)
114                             protocol_version);
115                         exit_cleanup(RERR_PROTOCOL);
116                 }
117 +               if (preserve_xattrs) {
118 +                       rprintf(FERROR,
119 +                           "--xattrs requires protocol 30 or higher"
120 +                           " (negotiated %d).\n",
121 +                           protocol_version);
122 +                       exit_cleanup(RERR_PROTOCOL);
123 +               }
124         }
125  
126         if (delete_mode && !(delete_before+delete_during+delete_after)) {
127 --- old/configure.in
128 +++ new/configure.in
129 @@ -891,6 +891,46 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_
130      esac
131  fi
132  
133 +AC_CHECK_HEADERS(attr/xattr.h)
134 +AC_CHECK_HEADERS(sys/xattr.h)
135 +AC_CHECK_HEADERS(sys/extattr.h)
136 +
137 +#################################################
138 +# check for extended attribute support
139 +AC_MSG_CHECKING(whether to support extended attributes)
140 +AC_ARG_ENABLE(xattr-support,
141 +       AC_HELP_STRING([--disable-xattr-support],
142 +                      [Turn off extended attribute support]))
143 +
144 +if test x"$enable_xattr_support" = x"no"; then
145 +    AC_MSG_RESULT(no)
146 +else
147 +    case "$host_os" in
148 +    *linux*)
149 +       AC_MSG_RESULT(Using Linux xattrs)
150 +       AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs])
151 +       AC_DEFINE(SUPPORT_XATTRS, 1, [Define to 1 to add support for extended attributes])
152 +       ;;
153 +    darwin*)
154 +       AC_MSG_RESULT(Using OS X xattrs)
155 +       AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs])
156 +       AC_DEFINE(SUPPORT_XATTRS, 1)
157 +       ;;
158 +    freebsd*)
159 +       AC_MSG_RESULT(Using FreeBSD extattrs)
160 +       AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
161 +       AC_DEFINE(SUPPORT_XATTRS, 1)
162 +       ;;
163 +    *)
164 +       if test x"$enable_xattr_support" = x"yes"; then
165 +           AC_MSG_ERROR(Failed to find extended attribute support)
166 +       else
167 +           AC_MSG_RESULT(No extended attribute support found)
168 +       fi
169 +       ;;
170 +    esac
171 +fi
172 +
173  AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
174  AC_OUTPUT
175  
176 --- old/flist.c
177 +++ new/flist.c
178 @@ -43,6 +43,7 @@ extern int one_file_system;
179  extern int copy_dirlinks;
180  extern int keep_dirlinks;
181  extern int preserve_acls;
182 +extern int preserve_xattrs;
183  extern int preserve_links;
184  extern int preserve_hard_links;
185  extern int preserve_devices;
186 @@ -888,6 +889,10 @@ static struct file_struct *recv_file_ent
187         if (preserve_acls && !S_ISLNK(mode))
188                 receive_acl(file, f);
189  #endif
190 +#ifdef SUPPORT_XATTRS
191 +       if (preserve_xattrs)
192 +               receive_xattr(file, f );
193 +#endif
194  
195         if (S_ISREG(mode) || S_ISLNK(mode))
196                 stats.total_size += file_length;
197 @@ -1160,7 +1165,7 @@ static struct file_struct *send_file_nam
198                                           int flags, int filter_flags)
199  {
200         struct file_struct *file;
201 -#ifdef SUPPORT_ACLS
202 +#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
203         statx sx;
204  #endif
205  
206 @@ -1179,6 +1184,13 @@ static struct file_struct *send_file_nam
207                         return NULL;
208         }
209  #endif
210 +#ifdef SUPPORT_XATTRS
211 +       if (preserve_xattrs && f >= 0) {
212 +               sx.xattr = NULL;
213 +               if (get_xattr(fname, &sx) < 0)
214 +                       return NULL;
215 +       }
216 +#endif
217  
218         maybe_emit_filelist_progress(flist->count + flist_count_offset);
219  
220 @@ -1192,6 +1204,12 @@ static struct file_struct *send_file_nam
221                         free_acl(&sx);
222                 }
223  #endif
224 +#ifdef SUPPORT_XATTRS
225 +               if (preserve_xattrs) {
226 +                       F_XATTR(file) = send_xattr(&sx, f);
227 +                       free_xattr(&sx);
228 +               }
229 +#endif
230         }
231         return file;
232  }
233 --- old/generator.c
234 +++ new/generator.c
235 @@ -36,6 +36,7 @@ extern int relative_paths;
236  extern int implied_dirs;
237  extern int keep_dirlinks;
238  extern int preserve_acls;
239 +extern int preserve_xattrs;
240  extern int preserve_links;
241  extern int preserve_devices;
242  extern int preserve_specials;
243 @@ -532,6 +533,14 @@ int unchanged_attrs(const char *fname, s
244                         return 0;
245         }
246  #endif
247 +#ifdef SUPPORT_XATTRS
248 +       if (preserve_xattrs) {
249 +               if (!XATTR_READY(*sxp))
250 +                       get_xattr(fname, sxp);
251 +               if (xattr_diff(file, sxp, 0))
252 +                       return 0;
253 +       }
254 +#endif
255  
256         return 1;
257  }
258 @@ -567,11 +576,19 @@ void itemize(const char *fname, struct f
259                                 iflags |= ITEM_REPORT_ACL;
260                 }
261  #endif
262 +#ifdef SUPPORT_XATTRS
263 +               if (preserve_xattrs) {
264 +                       if (!XATTR_READY(*sxp))
265 +                               get_xattr(fname, sxp);
266 +                       if (xattr_diff(file, sxp, 1))
267 +                               iflags |= ITEM_REPORT_XATTR;
268 +               }
269 +#endif
270         } else
271                 iflags |= ITEM_IS_NEW;
272  
273         iflags &= 0xffff;
274 -       if ((iflags & SIGNIFICANT_ITEM_FLAGS || verbose > 1
275 +       if ((iflags & (SIGNIFICANT_ITEM_FLAGS|ITEM_REPORT_XATTR) || verbose > 1
276           || stdout_format_has_i > 1 || (xname && *xname)) && !read_batch) {
277                 if (protocol_version >= 29) {
278                         if (ndx >= 0)
279 @@ -581,6 +598,10 @@ void itemize(const char *fname, struct f
280                                 write_byte(sock_f_out, fnamecmp_type);
281                         if (iflags & ITEM_XNAME_FOLLOWS)
282                                 write_vstring(sock_f_out, xname, strlen(xname));
283 +#ifdef SUPPORT_XATTRS
284 +                       if (iflags & ITEM_REPORT_XATTR && !dry_run)
285 +                               send_xattr_request(NULL, file, sock_f_out);
286 +#endif
287                 } else if (ndx >= 0) {
288                         enum logcode code = logfile_format_has_i ? FINFO : FCLIENT;
289                         log_item(code, file, &stats, iflags, xname);
290 @@ -1112,6 +1133,9 @@ static void recv_generator(char *fname, 
291  #ifdef SUPPORT_ACLS
292         sx.acc_acl = sx.def_acl = NULL;
293  #endif
294 +#ifdef SUPPORT_XATTRS
295 +       sx.xattr = NULL;
296 +#endif
297         if (dry_run > 1) {
298                 if (fuzzy_dirlist) {
299                         flist_free(fuzzy_dirlist);
300 @@ -1636,6 +1660,10 @@ static void recv_generator(char *fname, 
301                 if (preserve_acls)
302                         free_acl(&real_sx);
303  #endif
304 +#ifdef SUPPORT_XATTRS
305 +               if (preserve_xattrs)
306 +                       free_xattr(&real_sx);
307 +#endif
308         }
309  
310         if (!do_xfers) {
311 @@ -1672,6 +1700,10 @@ static void recv_generator(char *fname, 
312         if (preserve_acls)
313                 free_acl(&sx);
314  #endif
315 +#ifdef SUPPORT_XATTRS
316 +       if (preserve_xattrs)
317 +               free_xattr(&sx);
318 +#endif
319         return;
320  }
321  
322 --- old/lib/sysxattrs.c
323 +++ new/lib/sysxattrs.c
324 @@ -0,0 +1,135 @@
325 +/*
326 + * Extended attribute support for rsync.
327 + *
328 + * Copyright (C) 2004 Red Hat, Inc.
329 + * Written by Jay Fenlason.
330 + *
331 + * This program is free software; you can redistribute it and/or modify
332 + * it under the terms of the GNU General Public License as published by
333 + * the Free Software Foundation; either version 2 of the License, or
334 + * (at your option) any later version.
335 + *
336 + * This program is distributed in the hope that it will be useful,
337 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
338 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
339 + * GNU General Public License for more details.
340 + *
341 + * You should have received a copy of the GNU General Public License along
342 + * with this program; if not, write to the Free Software Foundation, Inc.,
343 + * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
344 + */
345 +
346 +#include "rsync.h"
347 +#include "sysxattrs.h"
348 +
349 +#ifdef SUPPORT_XATTRS
350 +
351 +#if defined HAVE_LINUX_XATTRS
352 +
353 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
354 +{
355 +       return lgetxattr(path, name, value, size);
356 +}
357 +
358 +ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
359 +{
360 +       return fgetxattr(filedes, name, value, size);
361 +}
362 +
363 +int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
364 +{
365 +       return lsetxattr(path, name, value, size, 0);
366 +}
367 +
368 +int sys_lremovexattr(const char *path, const char *name)
369 +{
370 +       return lremovexattr(path, name);
371 +}
372 +
373 +ssize_t sys_llistxattr(const char *path, char *list, size_t size)
374 +{
375 +       return llistxattr(path, list, size);
376 +}
377 +
378 +#elif HAVE_OSX_XATTRS
379 +
380 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
381 +{
382 +       return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
383 +}
384 +
385 +ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
386 +{
387 +       return fgetxattr(filedes, name, value, size, 0, 0);
388 +}
389 +
390 +int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
391 +{
392 +       return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
393 +}
394 +
395 +int sys_lremovexattr(const char *path, const char *name)
396 +{
397 +       return removexattr(path, name, XATTR_NOFOLLOW);
398 +}
399 +
400 +ssize_t sys_llistxattr(const char *path, char *list, size_t size)
401 +{
402 +       return listxattr(path, list, size, XATTR_NOFOLLOW);
403 +}
404 +
405 +#elif HAVE_FREEBSD_XATTRS
406 +
407 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
408 +{
409 +       return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
410 +}
411 +
412 +ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
413 +{
414 +       return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
415 +}
416 +
417 +int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
418 +{
419 +       return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
420 +}
421 +
422 +int sys_lremovexattr(const char *path, const char *name)
423 +{
424 +       return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
425 +}
426 +
427 +ssize_t sys_llistxattr(const char *path, char *list, size_t size)
428 +{
429 +       unsigned char keylen;
430 +       ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
431 +
432 +       if (len <= 0 || (size_t)len > size)
433 +               return len;
434 +
435 +       /* FreeBSD puts a single-byte length before each string, with no '\0'
436 +        * terminator.  We need to change this into a series of null-terminted
437 +        * strings.  Since the size is the same, we can simply transform the
438 +        * output in place. */
439 +       for (off = 0; off < len; off += keylen + 1) {
440 +               keylen = ((unsigned char*)list)[off];
441 +               if (off + keylen >= len) {
442 +                       /* Should be impossible, but kernel bugs happen! */
443 +                       errno = EINVAL;
444 +                       return -1;
445 +               }
446 +               memmove(list+off, list+off+1, keylen);
447 +               list[off+keylen] = '\0';
448 +       }
449 +
450 +       return len;
451 +}
452 +
453 +#else
454 +
455 +#error You need to create xattr compatibility functions.
456 +
457 +#endif
458 +
459 +#endif /* SUPPORT_XATTRS */
460 --- old/lib/sysxattrs.h
461 +++ new/lib/sysxattrs.h
462 @@ -0,0 +1,26 @@
463 +#ifdef SUPPORT_XATTRS
464 +
465 +#if defined HAVE_ATTR_XATTR_H
466 +#include <attr/xattr.h>
467 +#elif defined HAVE_SYS_XATTR_H
468 +#include <sys/xattr.h>
469 +#elif defined HAVE_SYS_EXTATTR_H
470 +#include <sys/extattr.h>
471 +#endif
472 +
473 +/* Linux 2.4 does not define this as a distinct errno value: */
474 +#ifndef ENOATTR
475 +#define ENOATTR ENODATA
476 +#endif
477 +
478 +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
479 +ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
480 +int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
481 +int sys_lremovexattr(const char *path, const char *name);
482 +ssize_t sys_llistxattr(const char *path, char *list, size_t size);
483 +
484 +#else
485 +
486 +/* No xattrs available */
487 +
488 +#endif
489 --- old/main.c
490 +++ new/main.c
491 @@ -608,7 +608,7 @@ static void fix_basis_dirs(void)
492  }
493  
494  /* This is only called by the sender. */
495 -static void read_final_goodbye(int f_in, int f_out)
496 +static void read_final_goodbye(int f_in)
497  {
498         int i, iflags, xlen;
499         uchar fnamecmp_type;
500 @@ -617,8 +617,8 @@ static void read_final_goodbye(int f_in,
501         if (protocol_version < 29)
502                 i = read_int(f_in);
503         else {
504 -               i = read_ndx_and_attrs(f_in, f_out, &iflags,
505 -                                      &fnamecmp_type, xname, &xlen);
506 +               i = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
507 +                                      xname, &xlen);
508         }
509  
510         if (i != NDX_DONE) {
511 @@ -677,7 +677,7 @@ static void do_server_sender(int f_in, i
512         io_flush(FULL_FLUSH);
513         handle_stats(f_out);
514         if (protocol_version >= 24)
515 -               read_final_goodbye(f_in, f_out);
516 +               read_final_goodbye(f_in);
517         io_flush(FULL_FLUSH);
518         exit_cleanup(0);
519  }
520 @@ -740,7 +740,7 @@ static int do_recv(int f_in, int f_out, 
521                         kluge_around_eof = -1;
522  
523                         /* This should only get stopped via a USR2 signal. */
524 -                       read_ndx_and_attrs(f_in, -1, &iflags, &fnamecmp_type,
525 +                       read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
526                                            xname, &xlen);
527  
528                         rprintf(FERROR, "Invalid packet at end of run [%s]\n",
529 @@ -977,7 +977,7 @@ int client_run(int f_in, int f_out, pid_
530                 io_flush(FULL_FLUSH);
531                 handle_stats(-1);
532                 if (protocol_version >= 24)
533 -                       read_final_goodbye(f_in, f_out);
534 +                       read_final_goodbye(f_in);
535                 if (pid != -1) {
536                         if (verbose > 3)
537                                 rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
538 --- old/options.c
539 +++ new/options.c
540 @@ -47,6 +47,7 @@ int copy_links = 0;
541  int preserve_links = 0;
542  int preserve_hard_links = 0;
543  int preserve_acls = 0;
544 +int preserve_xattrs = 0;
545  int preserve_perms = 0;
546  int preserve_executability = 0;
547  int preserve_devices = 0;
548 @@ -201,6 +202,7 @@ static void print_rsync_version(enum log
549         char const *have_inplace = "no ";
550         char const *hardlinks = "no ";
551         char const *acls = "no ";
552 +       char const *xattrs = "no ";
553         char const *links = "no ";
554         char const *ipv6 = "no ";
555         STRUCT_STAT *dumstat;
556 @@ -220,7 +222,9 @@ static void print_rsync_version(enum log
557  #ifdef SUPPORT_ACLS
558         acls = "";
559  #endif
560 -
561 +#ifdef SUPPORT_XATTRS
562 +       xattrs = "";
563 +#endif
564  #ifdef SUPPORT_LINKS
565         links = "";
566  #endif
567 @@ -239,8 +243,8 @@ static void print_rsync_version(enum log
568                 (int)(sizeof (int64) * 8));
569         rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
570                 got_socketpair, hardlinks, links, ipv6, have_inplace);
571 -       rprintf(f, "    %sappend, %sACLs\n",
572 -               have_inplace, acls);
573 +       rprintf(f, "    %sappend, %sACLs, %sxattrs\n",
574 +               have_inplace, acls, xattrs);
575  
576  #ifdef MAINTAINER_MODE
577         rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
578 @@ -286,7 +290,7 @@ void usage(enum logcode F)
579    rprintf(F," -q, --quiet                 suppress non-error messages\n");
580    rprintf(F,"     --no-motd               suppress daemon-mode MOTD (see manpage caveat)\n");
581    rprintf(F," -c, --checksum              skip based on checksum, not mod-time & size\n");
582 -  rprintf(F," -a, --archive               archive mode; same as -rlptgoD (no -H, -A)\n");
583 +  rprintf(F," -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)\n");
584    rprintf(F,"     --no-OPTION             turn off an implied OPTION (e.g. --no-D)\n");
585    rprintf(F," -r, --recursive             recurse into directories\n");
586    rprintf(F," -R, --relative              use relative path names\n");
587 @@ -311,6 +315,9 @@ void usage(enum logcode F)
588  #ifdef SUPPORT_ACLS
589    rprintf(F," -A, --acls                  preserve ACLs (implies --perms)\n");
590  #endif
591 +#ifdef SUPPORT_XATTRS
592 +  rprintf(F," -X, --xattrs                preserve extended attributes (implies --perms)\n");
593 +#endif
594    rprintf(F," -o, --owner                 preserve owner (super-user only)\n");
595    rprintf(F," -g, --group                 preserve group\n");
596    rprintf(F,"     --devices               preserve device files (super-user only)\n");
597 @@ -438,6 +445,9 @@ static struct poptOption long_options[] 
598    {"acls",            'A', POPT_ARG_NONE,   0, 'A', 0, 0 },
599    {"no-acls",          0,  POPT_ARG_VAL,    &preserve_acls, 0, 0, 0 },
600    {"no-A",             0,  POPT_ARG_VAL,    &preserve_acls, 0, 0, 0 },
601 +  {"xattrs",          'X', POPT_ARG_NONE,   0, 'X', 0, 0 },
602 +  {"no-xattrs",        0,  POPT_ARG_VAL,    &preserve_xattrs, 0, 0, 0 },
603 +  {"no-X",             0,  POPT_ARG_VAL,    &preserve_xattrs, 0, 0, 0 },
604    {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
605    {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
606    {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
607 @@ -1126,6 +1136,17 @@ int parse_arguments(int *argc, const cha
608                         return 0;
609  #endif
610  
611 +               case 'X':
612 +#ifdef SUPPORT_XATTRS
613 +                       preserve_xattrs = 1;
614 +                       preserve_perms = 1;
615 +                       break;
616 +#else
617 +                       snprintf(err_buf,sizeof(err_buf),
618 +                                "extended attributes are not supported on this %s\n",
619 +                                am_server ? "server" : "client");
620 +                       return 0;
621 +#endif
622  
623                 default:
624                         /* A large opt value means that set_refuse_options()
625 @@ -1590,6 +1611,10 @@ void server_options(char **args,int *arg
626         if (preserve_acls)
627                 argstr[x++] = 'A';
628  #endif
629 +#ifdef SUPPORT_XATTRS
630 +       if (preserve_xattrs)
631 +               argstr[x++] = 'X';
632 +#endif
633         if (recurse)
634                 argstr[x++] = 'r';
635         if (always_checksum)
636 --- old/receiver.c
637 +++ new/receiver.c
638 @@ -22,6 +22,7 @@
639  #include "rsync.h"
640  
641  extern int verbose;
642 +extern int dry_run;
643  extern int do_xfers;
644  extern int am_server;
645  extern int do_progress;
646 @@ -366,8 +367,8 @@ int recv_files(int f_in, char *local_nam
647                 cleanup_disable();
648  
649                 /* This call also sets cur_flist. */
650 -               ndx = read_ndx_and_attrs(f_in, -1, &iflags,
651 -                                        &fnamecmp_type, xname, &xlen);
652 +               ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
653 +                                        xname, &xlen);
654                 if (ndx == NDX_DONE) {
655                         if (inc_recurse && first_flist) {
656                                 flist_free(first_flist);
657 @@ -397,8 +398,17 @@ int recv_files(int f_in, char *local_nam
658                 if (verbose > 2)
659                         rprintf(FINFO, "recv_files(%s)\n", fname);
660  
661 +#ifdef SUPPORT_XATTRS
662 +               if (iflags & ITEM_REPORT_XATTR && !dry_run)
663 +                       recv_xattr_request(file, f_in);
664 +#endif
665 +
666                 if (!(iflags & ITEM_TRANSFER)) {
667                         maybe_log_item(file, iflags, itemizing, xname);
668 +#ifdef SUPPORT_XATTRS
669 +                       if (iflags & ITEM_REPORT_XATTR && !dry_run)
670 +                               set_file_attrs(fname, file, NULL, 0);
671 +#endif
672                         continue;
673                 }
674                 if (phase == 2) {
675 --- old/rsync.c
676 +++ new/rsync.c
677 @@ -32,6 +32,7 @@
678  extern int verbose;
679  extern int dry_run;
680  extern int preserve_acls;
681 +extern int preserve_xattrs;
682  extern int preserve_perms;
683  extern int preserve_executability;
684  extern int preserve_times;
685 @@ -91,10 +92,8 @@ void setup_iconv()
686  }
687  #endif
688  
689 -/* This is used by sender.c with a valid f_out, and by receive.c with
690 - * f_out = -1. */
691 -int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr,
692 -                      uchar *type_ptr, char *buf, int *len_ptr)
693 +int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr,
694 +                      char *buf, int *len_ptr)
695  {
696         int len, iflags = 0;
697         struct file_list *flist;
698 @@ -181,11 +180,6 @@ int read_ndx_and_attrs(int f_in, int f_o
699                                 ndx, who_am_i());
700                         exit_cleanup(RERR_PROTOCOL);
701                 }
702 -       } else if (f_out >= 0) {
703 -               if (inc_recurse)
704 -                       send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
705 -               write_ndx_and_attrs(f_out, ndx, iflags,
706 -                                   fnamecmp_type, buf, len);
707         }
708  
709         *iflag_ptr = iflags;
710 @@ -247,6 +241,9 @@ int set_file_attrs(char *fname, struct f
711  #ifdef SUPPORT_ACLS
712                 sx2.acc_acl = sx2.def_acl = NULL;
713  #endif
714 +#ifdef SUPPORT_XATTRS
715 +               sx2.xattr = NULL;
716 +#endif
717                 if (!preserve_perms && S_ISDIR(new_mode)
718                  && sx2.st.st_mode & S_ISGID) {
719                         /* We just created this directory and its setgid
720 @@ -321,6 +318,10 @@ int set_file_attrs(char *fname, struct f
721         if (daemon_chmod_modes && !S_ISLNK(new_mode))
722                 new_mode = tweak_mode(new_mode, daemon_chmod_modes);
723  
724 +#ifdef SUPPORT_XATTRS
725 +       if (preserve_xattrs && !am_generator)
726 +               set_xattr(fname, file, sxp);
727 +#endif
728  #ifdef SUPPORT_ACLS
729         /* It's OK to call set_acl() now, even for a dir, as the generator
730          * will enable owner-writability using chmod, if necessary.
731 @@ -353,10 +354,16 @@ int set_file_attrs(char *fname, struct f
732                         rprintf(FCLIENT, "%s is uptodate\n", fname);
733         }
734    cleanup:
735 +       if (sxp == &sx2) {
736  #ifdef SUPPORT_ACLS
737 -       if (preserve_acls && sxp == &sx2)
738 -               free_acl(&sx2);
739 +               if (preserve_acls)
740 +                       free_acl(&sx2);
741  #endif
742 +#ifdef SUPPORT_XATTRS
743 +               if (preserve_xattrs)
744 +                       free_xattr(&sx2);
745 +#endif
746 +       }
747         return updated;
748  }
749  
750 --- old/rsync.h
751 +++ new/rsync.h
752 @@ -569,6 +569,7 @@ extern int file_extra_cnt;
753  extern int preserve_uid;
754  extern int preserve_gid;
755  extern int preserve_acls;
756 +extern int preserve_xattrs;
757  
758  #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
759  #define EXTRA_LEN (sizeof (union file_extras))
760 @@ -601,7 +602,8 @@ extern int preserve_acls;
761  /* When the associated option is on, all entries will have these present: */
762  #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
763  #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
764 -#define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
765 +#define F_ACL(f) REQ_EXTRA(f, preserve_acls)->num
766 +#define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->num
767  
768  /* These items are per-entry optional and mutally exclusive: */
769  #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
770 @@ -793,9 +795,13 @@ typedef struct {
771      struct rsync_acl *acc_acl; /* access ACL */
772      struct rsync_acl *def_acl; /* default ACL */
773  #endif
774 +#ifdef SUPPORT_XATTRS
775 +    item_list *xattr;
776 +#endif
777  } statx;
778  
779  #define ACL_READY(sx) ((sx).acc_acl != NULL)
780 +#define XATTR_READY(sx) ((sx).xattr != NULL)
781  
782  #include "proto.h"
783  
784 --- old/rsync.yo
785 +++ new/rsync.yo
786 @@ -301,7 +301,7 @@ to the detailed description below for a 
787   -q, --quiet                 suppress non-error messages
788       --no-motd               suppress daemon-mode MOTD (see caveat)
789   -c, --checksum              skip based on checksum, not mod-time & size
790 - -a, --archive               archive mode; same as -rlptgoD (no -H, -A)
791 + -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
792       --no-OPTION             turn off an implied OPTION (e.g. --no-D)
793   -r, --recursive             recurse into directories
794   -R, --relative              use relative path names
795 @@ -324,6 +324,7 @@ to the detailed description below for a 
796   -E, --executability         preserve executability
797       --chmod=CHMOD           affect file and/or directory permissions
798   -A, --acls                  preserve ACLs (implies -p)
799 + -X, --xattrs                preserve extended attrs (implies -p)
800   -o, --owner                 preserve owner (super-user only)
801   -g, --group                 preserve group
802       --devices               preserve device files (super-user only)
803 @@ -831,6 +832,11 @@ dit(bf(-A, --acls)) This option causes r
804  ACLs to be the same as the source ACLs.  This nonstandard option only
805  works if the remote rsync also supports it.  bf(--acls) implies bf(--perms).
806  
807 +dit(bf(-X, --xattrs)) This option causes rsync to update the remote
808 +extended attributes to be the same as the local ones.  This will work
809 +only if the remote machine's rsync supports this option also. This is
810 +a non-standard option.
811 +
812  dit(bf(--chmod)) This option tells rsync to apply one or more
813  comma-separated "chmod" strings to the permission of the files in the
814  transfer.  The resulting value is treated as though it was the permissions
815 --- old/sender.c
816 +++ new/sender.c
817 @@ -22,6 +22,7 @@
818  #include "rsync.h"
819  
820  extern int verbose;
821 +extern int dry_run;
822  extern int do_xfers;
823  extern int am_server;
824  extern int am_daemon;
825 @@ -144,8 +145,9 @@ void successful_send(int ndx)
826                 rsyserr(FERROR, errno, "sender failed to remove %s", fname);
827  }
828  
829 -void write_ndx_and_attrs(int f_out, int ndx, int iflags,
830 -                        uchar fnamecmp_type, char *buf, int len)
831 +static void write_ndx_and_attrs(int f_out, int ndx, int iflags,
832 +                               const char *fname, struct file_struct *file,
833 +                               uchar fnamecmp_type, char *buf, int len)
834  {
835         write_ndx(f_out, ndx);
836         if (protocol_version < 29)
837 @@ -155,6 +157,10 @@ void write_ndx_and_attrs(int f_out, int 
838                 write_byte(f_out, fnamecmp_type);
839         if (iflags & ITEM_XNAME_FOLLOWS)
840                 write_vstring(f_out, buf, len);
841 +#ifdef SUPPORT_XATTRS
842 +       if (iflags & ITEM_REPORT_XATTR && !dry_run)
843 +               send_xattr_request(fname, file, f_out);
844 +#endif
845  }
846  
847  void send_files(int f_in, int f_out)
848 @@ -183,8 +189,8 @@ void send_files(int f_in, int f_out)
849                         send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
850  
851                 /* This call also sets cur_flist. */
852 -               ndx = read_ndx_and_attrs(f_in, f_out, &iflags,
853 -                                        &fnamecmp_type, xname, &xlen);
854 +               ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
855 +                                        xname, &xlen);
856                 if (ndx == NDX_DONE) {
857                         if (inc_recurse && first_flist) {
858                                 flist_free(first_flist);
859 @@ -201,6 +207,9 @@ void send_files(int f_in, int f_out)
860                         continue;
861                 }
862  
863 +               if (inc_recurse)
864 +                       send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
865 +
866                 file = cur_flist->files[ndx - cur_flist->ndx_start];
867                 if (F_ROOTDIR(file)) {
868                         path = F_ROOTDIR(file);
869 @@ -215,8 +224,13 @@ void send_files(int f_in, int f_out)
870                 if (verbose > 2)
871                         rprintf(FINFO, "send_files(%d, %s%s%s)\n", ndx, path,slash,fname);
872  
873 +               if (iflags & ITEM_REPORT_XATTR)
874 +                       recv_xattr_request(file, f_in);
875 +
876                 if (!(iflags & ITEM_TRANSFER)) {
877                         maybe_log_item(file, iflags, itemizing, xname);
878 +                       write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
879 +                                           fnamecmp_type, xname, xlen);
880                         continue;
881                 }
882                 if (phase == 2) {
883 @@ -251,8 +265,8 @@ void send_files(int f_in, int f_out)
884  
885                 if (!do_xfers) { /* log the transfer */
886                         log_item(FCLIENT, file, &stats, iflags, NULL);
887 -                       write_ndx_and_attrs(f_out, ndx, iflags, fnamecmp_type,
888 -                                           xname, xlen);
889 +                       write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
890 +                                           fnamecmp_type, xname, xlen);
891                         continue;
892                 }
893  
894 @@ -305,8 +319,8 @@ void send_files(int f_in, int f_out)
895                                 path,slash,fname, (double)st.st_size);
896                 }
897  
898 -               write_ndx_and_attrs(f_out, ndx, iflags, fnamecmp_type,
899 -                                   xname, xlen);
900 +               write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
901 +                                   fnamecmp_type, xname, xlen);
902                 write_sum_head(f_xfer, s);
903  
904                 if (verbose > 2)
905 --- old/testsuite/xattrs.test
906 +++ new/testsuite/xattrs.test
907 @@ -0,0 +1,59 @@
908 +#! /bin/sh
909 +
910 +# This program is distributable under the terms of the GNU GPL (see
911 +# COPYING).
912 +
913 +# Test that rsync handles basic xattr preservation.
914 +
915 +. $srcdir/testsuite/rsync.fns
916 +
917 +$RSYNC --version | grep ", xattrs" >/dev/null || test_skipped "Rsync is configured without xattr support"
918 +
919 +case "$RSYNC" in
920 +*protocol=29*) test_skipped "xattr support requires protocol 30" ;;
921 +esac
922 +
923 +makepath "$fromdir/foo"
924 +echo something >"$fromdir/file1"
925 +echo else >"$fromdir/file2"
926 +echo last >"$fromdir/foo/file3"
927 +
928 +makepath "$todir/foo"
929 +echo wow >"$todir/file1"
930 +cp -p "$fromdir/foo/file3" "$todir/foo"
931 +
932 +files='foo file1 file2 foo/file3'
933 +
934 +cd "$fromdir"
935 +
936 +setfattr -n user.foo -v foo file2
937 +setfattr -n user.bar -v bar file2
938 +
939 +setfattr -n user.short -v 'this is short' file1 2>/dev/null || test_skipped "Unable to set an xattr"
940 +setfattr -n user.long -v 'this is a long attribute that will be truncated in the initial data send' file1
941 +setfattr -n user.good -v 'this is good' file1
942 +setfattr -n user.nice -v 'this is nice' file1
943 +
944 +setfattr -n user.foo -v foo file2
945 +setfattr -n user.bar -v bar file2
946 +
947 +setfattr -n user.foo -v 'new foo' foo/file3
948 +setfattr -n user.bar -v 'new bar' foo/file3
949 +setfattr -n user.long -v 'this is also a long attribute that will be truncated in the initial data send' foo/file3
950 +setfattr -n user.equal -v 'this long attribute should remain the same and not need to be transferred' foo/file3
951 +
952 +setfattr -n user.short -v 'old short' "$todir/file1"
953 +setfattr -n user.extra -v 'remove me' "$todir/file1"
954 +
955 +setfattr -n user.foo -v 'old foo' "$todir/foo/file3"
956 +setfattr -n user.equal -v 'this long attribute should remain the same and not need to be transferred' "$todir/foo/file3"
957 +
958 +$RSYNC -avX . "$todir/"
959 +
960 +getfattr -d $files >"$scratchdir/xattrs.txt"
961 +
962 +cd "$todir"
963 +getfattr -d $files | diff $diffopt "$scratchdir/xattrs.txt" -
964 +
965 +# The script would have aborted on error, so getting here means we've won.
966 +exit 0
967 --- old/xattrs.c
968 +++ new/xattrs.c
969 @@ -0,0 +1,686 @@
970 +/*
971 + * Extended Attribute support for rsync.
972 + * Written by Jay Fenlason, vaguely based on the ACLs patch.
973 + *
974 + * Copyright (C) 2004 Red Hat, Inc.
975 + * Copyright (C) 2006, 2007 Wayne Davison
976 + *
977 + * This program is free software; you can redistribute it and/or modify
978 + * it under the terms of the GNU General Public License version 2 as
979 + * published by the Free Software Foundation.
980 + *
981 + * This program is distributed in the hope that it will be useful,
982 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
983 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
984 + * GNU General Public License for more details.
985 + *
986 + * You should have received a copy of the GNU General Public License along
987 + * with this program; if not, write to the Free Software Foundation, Inc.,
988 + * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
989 + */
990 +
991 +#include "rsync.h"
992 +#include "lib/sysxattrs.h"
993 +
994 +#ifdef SUPPORT_XATTRS
995 +
996 +extern int dry_run;
997 +extern int am_root;
998 +extern int am_sender;
999 +extern int read_only;
1000 +extern int list_only;
1001 +extern int checksum_seed;
1002 +
1003 +#define RSYNC_XAL_INITIAL 5
1004 +#define RSYNC_XAL_LIST_INITIAL 100
1005 +
1006 +#define MAX_FULL_DATUM 32
1007 +
1008 +#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
1009 +                           && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
1010 +
1011 +#define USER_PREFIX "user."
1012 +#define UPRE_LEN ((int)sizeof USER_PREFIX - 1)
1013 +#define SYSTEM_PREFIX "system."
1014 +#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
1015 +
1016 +#ifdef HAVE_LINUX_XATTRS
1017 +#define RPRE_LEN 0
1018 +#else
1019 +#define RSYNC_PREFIX "rsync."
1020 +#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
1021 +#endif
1022 +
1023 +typedef struct {
1024 +       char *datum, *name;
1025 +       size_t datum_len, name_len;
1026 +} rsync_xa;
1027 +
1028 +static size_t namebuf_len = 0;
1029 +static char *namebuf = NULL;
1030 +
1031 +static item_list empty_xattr = EMPTY_ITEM_LIST;
1032 +static item_list rsync_xal_l = EMPTY_ITEM_LIST;
1033 +
1034 +/* ------------------------------------------------------------------------- */
1035 +
1036 +static void rsync_xal_free(item_list *xalp)
1037 +{
1038 +       size_t i;
1039 +       rsync_xa *rxas = xalp->items;
1040 +
1041 +       for (i = 0; i < xalp->count; i++) {
1042 +               free(rxas[i].datum);
1043 +               /*free(rxas[i].name);*/
1044 +       }
1045 +       xalp->count = 0;
1046 +}
1047 +
1048 +void free_xattr(statx *sxp)
1049 +{
1050 +       if (!sxp->xattr)
1051 +               return;
1052 +       rsync_xal_free(sxp->xattr);
1053 +       free(sxp->xattr);
1054 +       sxp->xattr = NULL;
1055 +}
1056 +
1057 +static int rsync_xal_compare_names(const void *x1, const void *x2)
1058 +{
1059 +       const rsync_xa *xa1 = x1;
1060 +       const rsync_xa *xa2 = x2;
1061 +       return strcmp(xa1->name, xa2->name);
1062 +}
1063 +
1064 +static ssize_t get_xattr_names(const char *fname)
1065 +{
1066 +       ssize_t list_len;
1067 +
1068 +       if (!namebuf) {
1069 +               namebuf_len = 1024;
1070 +               namebuf = new_array(char, namebuf_len);
1071 +               if (!namebuf)
1072 +                       out_of_memory("get_xattr_names");
1073 +       }
1074 +
1075 +       /* The length returned includes all the '\0' terminators. */
1076 +       list_len = sys_llistxattr(fname, namebuf, namebuf_len);
1077 +       if (list_len > (ssize_t)namebuf_len) {
1078 +               list_len = -1;
1079 +               errno = ERANGE;
1080 +       }
1081 +       if (list_len < 0) {
1082 +               if (errno == ENOTSUP)
1083 +                       return 0;
1084 +               if (errno == ERANGE) {
1085 +                       list_len = sys_llistxattr(fname, NULL, 0);
1086 +                       if (list_len < 0) {
1087 +                               rsyserr(FERROR, errno,
1088 +                                       "get_xattr_names: llistxattr(\"%s\",0) failed",
1089 +                                       fname);
1090 +                               return -1;
1091 +                       }
1092 +                       if (namebuf_len)
1093 +                               free(namebuf);
1094 +                       namebuf_len = list_len + 1024;
1095 +                       namebuf = new_array(char, namebuf_len);
1096 +                       if (!namebuf)
1097 +                               out_of_memory("get_xattr_names");
1098 +                       list_len = sys_llistxattr(fname, namebuf, namebuf_len);
1099 +                       if (list_len < 0) {
1100 +                               rsyserr(FERROR, errno,
1101 +                                       "get_xattr_names: llistxattr(\"%s\",%ld) failed",
1102 +                                       fname, (long)namebuf_len);
1103 +                               return -1;
1104 +                       }
1105 +               } else {
1106 +                       rsyserr(FERROR, errno,
1107 +                               "get_xattr_names: llistxattr(\"%s\",%ld) failed",
1108 +                               fname, (long)namebuf_len);
1109 +                       return -1;
1110 +               }
1111 +       }
1112 +
1113 +       return list_len;
1114 +}
1115 +
1116 +/* On entry, the *len_ptr parameter contains the size of the extra space we
1117 + * should allocate when we create a buffer for the data.  On exit, it contains
1118 + * the length of the datum. */
1119 +static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr)
1120 +{
1121 +       size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
1122 +       char *ptr;
1123 +
1124 +       if (datum_len == (size_t)-1) {
1125 +               if (errno == ENOTSUP)
1126 +                       return NULL;
1127 +               rsyserr(FERROR, errno,
1128 +                       "get_xattr_data: lgetxattr(\"%s\",\"%s\",0) failed",
1129 +                       fname, name);
1130 +               return NULL;
1131 +       }
1132 +
1133 +       *len_ptr = datum_len;
1134 +
1135 +       if (datum_len + *len_ptr < datum_len /* checks for overflow */
1136 +        || !(ptr = new_array(char, datum_len + *len_ptr)))
1137 +               out_of_memory("get_xattr_data");
1138 +
1139 +       if (datum_len) {
1140 +               size_t len = sys_lgetxattr(fname, name, ptr, datum_len);
1141 +               if (len != datum_len) {
1142 +                       if (len == (size_t)-1) {
1143 +                               rsyserr(FERROR, errno,
1144 +                                   "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
1145 +                                   " failed", fname, name, (long)datum_len);
1146 +                       } else {
1147 +                               rprintf(FERROR,
1148 +                                   "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
1149 +                                   " returned %ld\n", fname, name,
1150 +                                   (long)datum_len, (long)len);
1151 +                       }
1152 +                       free(ptr);
1153 +                       return NULL;
1154 +               }
1155 +       }
1156 +
1157 +       return ptr;
1158 +}
1159 +
1160 +static int rsync_xal_get(const char *fname, item_list *xalp)
1161 +{
1162 +       ssize_t list_len, name_len;
1163 +       size_t datum_len, name_offset;
1164 +       char *name, *ptr;
1165 +       int user_only = am_sender ? 0 : !am_root;
1166 +
1167 +       /* This puts the name list into the "namebuf" buffer. */
1168 +       if ((list_len = get_xattr_names(fname)) < 0)
1169 +               return -1;
1170 +
1171 +       for (name = namebuf; list_len > 0; name += name_len) {
1172 +               rsync_xa *rxas;
1173 +
1174 +               name_len = strlen(name) + 1;
1175 +               list_len -= name_len;
1176 +
1177 +#ifdef HAVE_LINUX_XATTRS
1178 +               /* We always ignore the system namespace, and non-root
1179 +                * ignores everything but the user namespace. */
1180 +               if (user_only ? !HAS_PREFIX(name, USER_PREFIX)
1181 +                             : HAS_PREFIX(name, SYSTEM_PREFIX))
1182 +                       continue;
1183 +#endif
1184 +
1185 +               datum_len = name_len; /* Pass extra size to get_xattr_data() */
1186 +               if (!(ptr = get_xattr_data(fname, name, &datum_len)))
1187 +                       return -1;
1188 +
1189 +               if (datum_len > MAX_FULL_DATUM) {
1190 +                       /* For large datums, we store a flag and a checksum. */
1191 +                       name_offset = 1 + MAX_DIGEST_LEN;
1192 +                       sum_init(checksum_seed);
1193 +                       sum_update(ptr, datum_len);
1194 +                       free(ptr);
1195 +
1196 +                       if (!(ptr = new_array(char, name_offset + name_len)))
1197 +                               out_of_memory("rsync_xal_get");
1198 +                       *ptr = 0;
1199 +                       sum_end(ptr + 1);
1200 +               } else
1201 +                       name_offset = datum_len;
1202 +
1203 +               rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
1204 +               rxas->name = ptr + name_offset;
1205 +               memcpy(rxas->name, name, name_len);
1206 +               rxas->datum = ptr;
1207 +               rxas->name_len = name_len;
1208 +               rxas->datum_len = datum_len;
1209 +       }
1210 +       if (xalp->count > 1)
1211 +               qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
1212 +       return 0;
1213 +}
1214 +
1215 +/* Read the xattr(s) for this filename. */
1216 +int get_xattr(const char *fname, statx *sxp)
1217 +{
1218 +       sxp->xattr = new(item_list);
1219 +       *sxp->xattr = empty_xattr;
1220 +       if (rsync_xal_get(fname, sxp->xattr) < 0) {
1221 +               free_xattr(sxp);
1222 +               return -1;
1223 +       }
1224 +       return 0;
1225 +}
1226 +
1227 +static int find_matching_xattr(item_list *xalp)
1228 +{
1229 +       size_t i, j;
1230 +       item_list *lst = rsync_xal_l.items;
1231 +
1232 +       for (i = 0; i < rsync_xal_l.count; i++) {
1233 +               rsync_xa *rxas1 = lst[i].items;
1234 +               rsync_xa *rxas2 = xalp->items;
1235 +
1236 +               /* Wrong number of elements? */
1237 +               if (lst[i].count != xalp->count)
1238 +                       continue;
1239 +               /* any elements different? */
1240 +               for (j = 0; j < xalp->count; j++) {
1241 +                       if (rxas1[j].name_len != rxas2[j].name_len
1242 +                        || rxas1[j].datum_len != rxas2[j].datum_len
1243 +                        || strcmp(rxas1[j].name, rxas2[j].name))
1244 +                               break;
1245 +                       if (rxas1[j].datum_len > MAX_FULL_DATUM) {
1246 +                               if (memcmp(rxas1[j].datum + 1,
1247 +                                          rxas2[j].datum + 1,
1248 +                                          MAX_DIGEST_LEN) != 0)
1249 +                                       break;
1250 +                       } else {
1251 +                               if (memcmp(rxas1[j].datum, rxas2[j].datum,
1252 +                                          rxas2[j].datum_len))
1253 +                                       break;
1254 +                       }
1255 +               }
1256 +               /* no differences found.  This is The One! */
1257 +               if (j == xalp->count)
1258 +                       return i;
1259 +       }
1260 +
1261 +       return -1;
1262 +}
1263 +
1264 +/* Store *xalp on the end of rsync_xal_l */
1265 +static void rsync_xal_store(item_list *xalp)
1266 +{
1267 +       item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL);
1268 +       /* Since the following call starts a new list, we know it will hold the
1269 +        * entire initial-count, not just enough space for one new item. */
1270 +       *new_lst = empty_xattr;
1271 +       (void)EXPAND_ITEM_LIST(new_lst, rsync_xa, xalp->count);
1272 +       memcpy(new_lst->items, xalp->items, xalp->count * sizeof (rsync_xa));
1273 +       new_lst->count = xalp->count;
1274 +       xalp->count = 0;
1275 +}
1276 +
1277 +/* Send the make_xattr()-generated xattr list for this flist entry. */
1278 +int send_xattr(statx *sxp, int f)
1279 +{
1280 +       int ndx = find_matching_xattr(sxp->xattr);
1281 +
1282 +       /* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
1283 +       write_abbrevint(f, ndx + 1);
1284 +
1285 +       if (ndx < 0) {
1286 +               rsync_xa *rxa;
1287 +               int count = sxp->xattr->count;
1288 +               write_abbrevint(f, count);
1289 +               for (rxa = sxp->xattr->items; count--; rxa++) {
1290 +#ifdef HAVE_LINUX_XATTRS
1291 +                       write_abbrevint(f, rxa->name_len);
1292 +                       write_abbrevint(f, rxa->datum_len);
1293 +                       write_buf(f, rxa->name, rxa->name_len);
1294 +#else
1295 +                       /* We strip the rsync prefix from disguised namespaces
1296 +                        * and put everything else in the user namespace. */
1297 +                       if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
1298 +                        && rxa->name[RPRE_LEN] != '%') {
1299 +                               write_abbrevint(f, rxa->name_len - RPRE_LEN);
1300 +                               write_abbrevint(f, rxa->datum_len);
1301 +                               write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
1302 +                       } else {
1303 +                               write_abbrevint(f, rxa->name_len + UPRE_LEN);
1304 +                               write_abbrevint(f, rxa->datum_len);
1305 +                               write_buf(f, USER_PREFIX, UPRE_LEN);
1306 +                               write_buf(f, rxa->name, rxa->name_len);
1307 +                       }
1308 +#endif
1309 +                       if (rxa->datum_len > MAX_FULL_DATUM)
1310 +                               write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN);
1311 +                       else
1312 +                               write_buf(f, rxa->datum, rxa->datum_len);
1313 +               }
1314 +               ndx = rsync_xal_l.count; /* pre-incremented count */
1315 +               rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
1316 +       }
1317 +
1318 +       return ndx;
1319 +}
1320 +
1321 +/* Return a flag indicating if we need to change a file's xattrs.  If
1322 + * "find_all" is specified, also mark any abbreviated xattrs that we
1323 + * need so that send_xattr_request() can tell the sender about them. */
1324 +int xattr_diff(struct file_struct *file, statx *sxp, int find_all)
1325 +{
1326 +       item_list *lst = rsync_xal_l.items;
1327 +       rsync_xa *snd_rxa, *rec_rxa;
1328 +       int snd_cnt, rec_cnt;
1329 +       int cmp, same, xattrs_equal = 1;
1330 +
1331 +       if (!XATTR_READY(*sxp)) {
1332 +               rec_rxa = NULL;
1333 +               rec_cnt = 0;
1334 +       } else {
1335 +               rec_rxa = sxp->xattr->items;
1336 +               rec_cnt = sxp->xattr->count;
1337 +       }
1338 +
1339 +       if (F_XATTR(file) >= 0)
1340 +               lst += F_XATTR(file);
1341 +       else
1342 +               lst = &empty_xattr;
1343 +
1344 +       snd_rxa = lst->items;
1345 +       snd_cnt = lst->count;
1346 +
1347 +       /* If the count of the sender's xattrs is different from our
1348 +        * (receiver's) xattrs, the lists are not the same. */
1349 +       if (snd_cnt != rec_cnt) {
1350 +               if (!find_all)
1351 +                       return 1;
1352 +               xattrs_equal = 0;
1353 +       }
1354 +
1355 +       while (snd_cnt) {
1356 +               cmp = rec_cnt ? strcmp(snd_rxa->name, rec_rxa->name) : -1;
1357 +               if (cmp > 0)
1358 +                       same = 0;
1359 +               else if (snd_rxa->datum_len > MAX_FULL_DATUM) {
1360 +                       same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
1361 +                           && memcmp(snd_rxa->datum + 1, rec_rxa->datum + 1,
1362 +                                     MAX_DIGEST_LEN) == 0;
1363 +                       /* Flag unrequested items that we need. */
1364 +                       if (/*!same &&*/ find_all && snd_rxa->datum[0] == 0)
1365 +                               snd_rxa->datum[0] = 1;
1366 +               } else {
1367 +                       same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
1368 +                           && memcmp(snd_rxa->datum, rec_rxa->datum,
1369 +                                     snd_rxa->datum_len) == 0;
1370 +               }
1371 +               if (!same) {
1372 +                       if (!find_all)
1373 +                               return 1;
1374 +                       xattrs_equal = 0;
1375 +               }
1376 +
1377 +               if (cmp <= 0) {
1378 +                       snd_rxa++;
1379 +                       snd_cnt--;
1380 +               }
1381 +               if (cmp >= 0) {
1382 +                       rec_rxa++;
1383 +                       rec_cnt--;
1384 +               }
1385 +       }
1386 +
1387 +       if (rec_cnt)
1388 +               xattrs_equal = 0;
1389 +
1390 +       return !xattrs_equal;
1391 +}
1392 +
1393 +/* When called by the generator with a NULL fname, this tells the sender
1394 + * which abbreviated xattr values we need.  When called by the sender
1395 + * with a non-NULL fname, we send all the extra xattr data it needs. */
1396 +void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
1397 +{
1398 +       item_list *lst = rsync_xal_l.items;
1399 +       int j, cnt, prior_req = -1;
1400 +       rsync_xa *rxa;
1401 +
1402 +       lst += F_XATTR(file);
1403 +       cnt = lst->count;
1404 +       for (rxa = lst->items, j = 0; j < cnt; rxa++, j++) {
1405 +               if (rxa->datum_len <= MAX_FULL_DATUM
1406 +                || rxa->datum[0] != 1)
1407 +                       continue;
1408 +
1409 +               /* Flag that we handled this abbreviated item. */
1410 +               rxa->datum[0] = 2;
1411 +
1412 +               write_abbrevint(f_out, j - prior_req);
1413 +               prior_req = j;
1414 +
1415 +               if (fname) {
1416 +                       size_t len = 0;
1417 +                       char *ptr;
1418 +
1419 +                       /* Re-read the long datum. */
1420 +                       if (!(ptr = get_xattr_data(fname, rxa->name, &len)))
1421 +                               continue;
1422 +
1423 +                       write_abbrevint(f_out, len); /* length might have changed! */
1424 +                       write_buf(f_out, ptr, len);
1425 +                       free(ptr);
1426 +               }
1427 +       }
1428 +
1429 +       write_byte(f_out, 0); /* end the list */
1430 +}
1431 +
1432 +/* Read the request from the generator, and mark any needed xattrs
1433 + * with a flag that lets us know they need to be sent. */
1434 +void recv_xattr_request(struct file_struct *file, int f_in)
1435 +{
1436 +       item_list *lst = rsync_xal_l.items;
1437 +       char *old_datum, *name;
1438 +       rsync_xa *rxa;
1439 +       int rel_pos, cnt;
1440 +
1441 +       if (F_XATTR(file) >= 0)
1442 +               lst += F_XATTR(file);
1443 +       else
1444 +               exit_cleanup(RERR_STREAMIO); /* XXX */
1445 +
1446 +       cnt = lst->count;
1447 +       rxa = lst->items;
1448 +       rxa -= 1;
1449 +       while ((rel_pos = read_abbrevint(f_in)) != 0) {
1450 +               rxa += rel_pos;
1451 +               cnt -= rel_pos;
1452 +               if (cnt < 0 || rxa->datum_len <= MAX_FULL_DATUM
1453 +                || rxa->datum[0] != 0)
1454 +                       exit_cleanup(RERR_STREAMIO); /* XXX */
1455 +
1456 +               if (am_sender) {
1457 +                       rxa->datum[0] = 1;
1458 +                       continue;
1459 +               }
1460 +
1461 +               old_datum = rxa->datum;
1462 +               rxa->datum_len = read_abbrevint(f_in);
1463 +
1464 +               if (rxa->name_len + rxa->datum_len < rxa->name_len)
1465 +                       out_of_memory("recv_xattr_request"); /* overflow */
1466 +               rxa->datum = new_array(char, rxa->datum_len + rxa->name_len);
1467 +               if (!rxa->datum)
1468 +                       out_of_memory("recv_xattr_request");
1469 +               name = rxa->datum + rxa->datum_len;
1470 +               memcpy(name, rxa->name, rxa->name_len);
1471 +               rxa->name = name;
1472 +               free(old_datum);
1473 +               read_buf(f_in, rxa->datum, rxa->datum_len);
1474 +       }
1475 +}
1476 +
1477 +/* ------------------------------------------------------------------------- */
1478 +
1479 +/* receive and build the rsync_xattr_lists */
1480 +void receive_xattr(struct file_struct *file, int f)
1481 +{
1482 +       static item_list temp_xattr = EMPTY_ITEM_LIST;
1483 +       int count;
1484 +       int ndx = read_abbrevint(f);
1485 +
1486 +       if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) {
1487 +               rprintf(FERROR, "receive_xattr: xa index %d out of"
1488 +                       " range for %s\n", ndx, f_name(file, NULL));
1489 +               exit_cleanup(RERR_STREAMIO);
1490 +       }
1491 +
1492 +       if (ndx != 0) {
1493 +               F_XATTR(file) = ndx - 1;
1494 +               return;
1495 +       }
1496 +       
1497 +       if ((count = read_abbrevint(f)) != 0) {
1498 +               (void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
1499 +               temp_xattr.count = 0;
1500 +       }
1501 +
1502 +       while (count--) {
1503 +               char *ptr, *name;
1504 +               rsync_xa *rxa;
1505 +               size_t name_len = read_abbrevint(f);
1506 +               size_t datum_len = read_abbrevint(f);
1507 +               size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
1508 +#ifdef HAVE_LINUX_XATTRS
1509 +               size_t extra_len = 0;
1510 +#else
1511 +               size_t extra_len = am_root ? RPRE_LEN : 0;
1512 +               if (dget_len + extra_len < dget_len)
1513 +                       out_of_memory("receive_xattr"); /* overflow */
1514 +#endif
1515 +               if (dget_len + extra_len + name_len < dget_len)
1516 +                       out_of_memory("receive_xattr"); /* overflow */
1517 +               ptr = new_array(char, dget_len + extra_len + name_len);
1518 +               if (!ptr)
1519 +                       out_of_memory("receive_xattr");
1520 +               name = ptr + dget_len + extra_len;
1521 +               read_buf(f, name, name_len);
1522 +               if (dget_len == datum_len)
1523 +                       read_buf(f, ptr, dget_len);
1524 +               else {
1525 +                       *ptr = 0;
1526 +                       read_buf(f, ptr + 1, MAX_DIGEST_LEN);
1527 +               }
1528 +#ifdef HAVE_LINUX_XATTRS
1529 +               /* Non-root can only save the user namespace. */
1530 +               if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
1531 +                       free(ptr);
1532 +                       continue;
1533 +               }
1534 +#else
1535 +               /* This OS only has a user namespace, so we either
1536 +                * strip the user prefix, or we put a non-user
1537 +                * namespace inside our rsync hierarchy. */
1538 +               if (HAS_PREFIX(name, USER_PREFIX)) {
1539 +                       name += UPRE_LEN;
1540 +                       name_len -= UPRE_LEN;
1541 +               } else if (am_root) {
1542 +                       name -= RPRE_LEN;
1543 +                       name_len += RPRE_LEN;
1544 +                       memcpy(name, RSYNC_PREFIX, RPRE_LEN);
1545 +               } else {
1546 +                       free(ptr);
1547 +                       continue;
1548 +               }
1549 +#endif
1550 +               rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1);
1551 +               rxa->name = name;
1552 +               rxa->datum = ptr;
1553 +               rxa->name_len = name_len;
1554 +               rxa->datum_len = datum_len;
1555 +       }
1556 +
1557 +       ndx = rsync_xal_l.count; /* pre-incremented count */
1558 +       rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
1559 +
1560 +       F_XATTR(file) = ndx;
1561 +}
1562 +
1563 +/* Turn the xattr data in statx into cached xattr data, setting the index
1564 + * values in the file struct. */
1565 +void cache_xattr(struct file_struct *file, statx *sxp)
1566 +{
1567 +       int ndx;
1568 +
1569 +       if (!sxp->xattr)
1570 +               return;
1571 +
1572 +       ndx = find_matching_xattr(sxp->xattr);
1573 +       if (ndx < 0)
1574 +               rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
1575 +
1576 +       F_XATTR(file) = ndx;
1577 +}
1578 +
1579 +static int rsync_xal_set(const char *fname, item_list *xalp)
1580 +{
1581 +       rsync_xa *rxas = xalp->items;
1582 +       ssize_t list_len;
1583 +       size_t i;
1584 +       char *name;
1585 +       int name_len, status, ret = 0;
1586 +
1587 +       /* This puts the current name list into the "namebuf" buffer. */
1588 +       if ((list_len = get_xattr_names(fname)) < 0)
1589 +               return -1;
1590 +
1591 +       for (i = 0; i < xalp->count; i++) {
1592 +               if ((size_t)(rxas[i].name - rxas[i].datum) < rxas[i].datum_len) {
1593 +                       rprintf(FERROR, "Abbreviated xattr value, %s, not received for %s\n",
1594 +                               rxas[i].name, full_fname(fname));
1595 +                       exit_cleanup(RERR_STREAMIO);
1596 +               }
1597 +               status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len);
1598 +               if (status < 0) {
1599 +                       rsyserr(FERROR, errno,
1600 +                               "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
1601 +                               fname, rxas[i].name);
1602 +                       ret = -1;
1603 +               }
1604 +       }
1605 +
1606 +       /* Remove any extraneous names. */
1607 +       for (name = namebuf; list_len > 0; name += name_len) {
1608 +               name_len = strlen(name) + 1;
1609 +               list_len -= name_len;
1610 +
1611 +#ifdef HAVE_LINUX_XATTRS
1612 +               /* We always ignore the system namespace, and non-root
1613 +                * ignores everything but the user namespace. */
1614 +               if (am_root ? HAS_PREFIX(name, SYSTEM_PREFIX)
1615 +                           : !HAS_PREFIX(name, USER_PREFIX))
1616 +                       continue;
1617 +#endif
1618 +
1619 +               for (i = 0; i < xalp->count; i++) {
1620 +                       if (strcmp(name, rxas[i].name) == 0)
1621 +                               break;
1622 +               }
1623 +               if (i == xalp->count) {
1624 +                       int status = sys_lremovexattr(fname, name);
1625 +                       if (status < 0) {
1626 +                               rsyserr(FERROR, errno,
1627 +                                       "rsync_xal_clear: lremovexattr(\"%s\",\"%s\") failed",
1628 +                                       fname, name);
1629 +                               ret = -1;
1630 +                       }
1631 +               }
1632 +       }
1633 +
1634 +       return ret;
1635 +}
1636 +
1637 +/* Set extended attributes on indicated filename. */
1638 +int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp))
1639 +{
1640 +       int ndx;
1641 +       item_list *lst = rsync_xal_l.items;
1642 +
1643 +       if (dry_run)
1644 +               return 1; /* FIXME: --dry-run needs to compute this value */
1645 +
1646 +       if (read_only || list_only) {
1647 +               errno = EROFS;
1648 +               return -1;
1649 +       }
1650 +
1651 +       ndx = F_XATTR(file);
1652 +       return rsync_xal_set(fname, lst + ndx);
1653 +}
1654 +
1655 +#endif /* SUPPORT_XATTRS */