A small tweak to the manpage.
[rsync/rsync-patches.git] / fake-super.diff
1 This patch adds a new option:  --fake-super, which tells rsync to copy in a
2 fake super-user mode that stores various file attributes in an extended-
3 attribute value instead of as real file-system attributes.  See the changes
4 to the manpages for details.
5
6 To use this patch, run these commands for a successful build:
7
8     patch -p1 <patches/fake-super.diff
9     ./configure                         (optional if already run)
10     make
11
12 --- old/backup.c
13 +++ new/backup.c
14 @@ -127,7 +127,7 @@ static int make_bak_dir(char *fullpath)
15                 if (p >= rel) {
16                         /* Try to transfer the directory settings of the
17                          * actual dir that the files are coming from. */
18 -                       if (do_stat(rel, &sx.st) < 0) {
19 +                       if (x_stat(rel, &sx.st, NULL) < 0) {
20                                 rsyserr(FERROR, errno,
21                                         "make_bak_dir stat %s failed",
22                                         full_fname(rel));
23 @@ -200,7 +200,7 @@ static int keep_backup(const char *fname
24         int ret_code;
25  
26         /* return if no file to keep */
27 -       if (do_lstat(fname, &sx.st) < 0)
28 +       if (x_lstat(fname, &sx.st, NULL) < 0)
29                 return 1;
30  #ifdef SUPPORT_ACLS
31         sx.acc_acl = sx.def_acl = NULL;
32 --- old/clientserver.c
33 +++ new/clientserver.c
34 @@ -630,6 +630,11 @@ static int rsync_module(int f_in, int f_
35         if (lp_ignore_errors(module_id))
36                 ignore_errors = 1;
37  
38 +       if (lp_fake_super(i))
39 +               am_root = -1;
40 +       else if (am_root < 0) /* Treat --fake-super from client as --super. */
41 +               am_root = 2;
42 +
43         if (filesfrom_fd == 0)
44                 filesfrom_fd = f_in;
45  
46 --- old/flist.c
47 +++ new/flist.c
48 @@ -196,12 +196,12 @@ static int readlink_stat(const char *pat
49                                 rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
50                                         path, linkbuf);
51                         }
52 -                       return do_stat(path, stp);
53 +                       return x_stat(path, stp, NULL);
54                 }
55         }
56         return 0;
57  #else
58 -       return do_stat(path, stp);
59 +       return x_stat(path, stp, NULL);
60  #endif
61  }
62  
63 @@ -209,17 +209,17 @@ int link_stat(const char *path, STRUCT_S
64  {
65  #ifdef SUPPORT_LINKS
66         if (copy_links)
67 -               return do_stat(path, stp);
68 -       if (do_lstat(path, stp) < 0)
69 +               return x_stat(path, stp, NULL);
70 +       if (x_lstat(path, stp, NULL) < 0)
71                 return -1;
72         if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
73                 STRUCT_STAT st;
74 -               if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
75 +               if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
76                         *stp = st;
77         }
78         return 0;
79  #else
80 -       return do_stat(path, stp);
81 +       return x_stat(path, stp, NULL);
82  #endif
83  }
84  
85 @@ -254,26 +254,6 @@ static int is_excluded(char *fname, int 
86         return 0;
87  }
88  
89 -static int to_wire_mode(mode_t mode)
90 -{
91 -#ifdef SUPPORT_LINKS
92 -#if _S_IFLNK != 0120000
93 -       if (S_ISLNK(mode))
94 -               return (mode & ~(_S_IFMT)) | 0120000;
95 -#endif
96 -#endif
97 -       return mode;
98 -}
99 -
100 -static mode_t from_wire_mode(int mode)
101 -{
102 -#if _S_IFLNK != 0120000
103 -       if ((mode & (_S_IFMT)) == 0120000)
104 -               return (mode & ~(_S_IFMT)) | _S_IFLNK;
105 -#endif
106 -       return mode;
107 -}
108 -
109  static void send_directory(int f, struct file_list *flist, int ndx,
110                            char *fbuf, int len, int flags);
111  
112 @@ -954,7 +934,7 @@ struct file_struct *make_file(const char
113                 if (save_errno == ENOENT) {
114  #ifdef SUPPORT_LINKS
115                         /* Avoid "vanished" error if symlink points nowhere. */
116 -                       if (copy_links && do_lstat(thisname, &st) == 0
117 +                       if (copy_links && x_lstat(thisname, &st, NULL) == 0
118                             && S_ISLNK(st.st_mode)) {
119                                 io_error |= IOERR_GENERAL;
120                                 rprintf(FERROR, "symlink has no referent: %s\n",
121 @@ -1126,7 +1106,7 @@ struct file_struct *make_file(const char
122                 int save_mode = file->mode;
123                 file->mode = S_IFDIR; /* Find a directory with our name. */
124                 if (flist_find(dir_flist, file) >= 0
125 -                   && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
126 +                   && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
127                         file->modtime = st2.st_mtime;
128                         file->len32 = 0;
129                         file->mode = st2.st_mode;
130 --- old/loadparm.c
131 +++ new/loadparm.c
132 @@ -149,6 +149,7 @@ typedef struct
133         int syslog_facility;
134         int timeout;
135  
136 +       BOOL fake_super;
137         BOOL ignore_errors;
138         BOOL ignore_nonreadable;
139         BOOL list;
140 @@ -196,6 +197,7 @@ static service sDefault =
141   /* syslog_facility; */                LOG_DAEMON,
142   /* timeout; */                        0,
143  
144 + /* fake_super; */             False,
145   /* ignore_errors; */          False,
146   /* ignore_nonreadable; */     False,
147   /* list; */                   True,
148 @@ -297,6 +299,7 @@ static struct parm_struct parm_table[] =
149   {"dont compress",     P_STRING, P_LOCAL, &sDefault.dont_compress,     NULL,0},
150   {"exclude from",      P_STRING, P_LOCAL, &sDefault.exclude_from,      NULL,0},
151   {"exclude",           P_STRING, P_LOCAL, &sDefault.exclude,           NULL,0},
152 + {"fake super",        P_BOOL,   P_LOCAL, &sDefault.fake_super,        NULL,0},
153   {"filter",            P_STRING, P_LOCAL, &sDefault.filter,            NULL,0},
154   {"gid",               P_STRING, P_LOCAL, &sDefault.gid,               NULL,0},
155   {"hosts allow",       P_STRING, P_LOCAL, &sDefault.hosts_allow,       NULL,0},
156 @@ -411,6 +414,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
157  FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
158  FN_LOCAL_INTEGER(lp_timeout, timeout)
159  
160 +FN_LOCAL_BOOL(lp_fake_super, fake_super)
161  FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
162  FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
163  FN_LOCAL_BOOL(lp_list, list)
164 @@ -814,7 +818,7 @@ BOOL lp_load(char *pszFname, int globals
165  
166         if (pszFname)
167             pstrcpy(n2,pszFname);
168 -       else if (am_server && !am_root)
169 +       else if (am_server && am_root <= 0)
170             pstrcpy(n2,RSYNCD_USERCONF);
171         else
172             pstrcpy(n2,RSYNCD_SYSCONF);
173 --- old/options.c
174 +++ new/options.c
175 @@ -72,7 +72,7 @@ int protocol_version = PROTOCOL_VERSION;
176  int sparse_files = 0;
177  int do_compression = 0;
178  int def_compress_level = Z_DEFAULT_COMPRESSION;
179 -int am_root = 0;
180 +int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
181  int am_server = 0;
182  int am_sender = 0;
183  int am_generator = 0;
184 @@ -326,6 +326,9 @@ void usage(enum logcode F)
185    rprintf(F," -t, --times                 preserve times\n");
186    rprintf(F," -O, --omit-dir-times        omit directories when preserving times\n");
187    rprintf(F,"     --super                 receiver attempts super-user activities\n");
188 +#ifdef SUPPORT_XATTRS
189 +  rprintf(F,"     --fake-super            store/recover privileged attrs using xattrs\n");
190 +#endif
191    rprintf(F," -S, --sparse                handle sparse files efficiently\n");
192    rprintf(F," -n, --dry-run               show what would have been transferred\n");
193    rprintf(F," -W, --whole-file            copy files whole (without rsync algorithm)\n");
194 @@ -455,6 +458,7 @@ static struct poptOption long_options[] 
195    {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
196    {"super",            0,  POPT_ARG_VAL,    &am_root, 2, 0, 0 },
197    {"no-super",         0,  POPT_ARG_VAL,    &am_root, 0, 0, 0 },
198 +  {"fake-super",       0,  POPT_ARG_VAL,    &am_root, -1, 0, 0 },
199    {"owner",           'o', POPT_ARG_VAL,    &preserve_uid, 1, 0, 0 },
200    {"no-owner",         0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
201    {"no-o",             0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
202 @@ -1187,6 +1191,14 @@ int parse_arguments(int *argc, const cha
203         }
204  #endif
205  
206 +#ifndef SUPPORT_XATTRS
207 +       if (am_root < 0) {
208 +               snprintf(err_buf, sizeof err_buf,
209 +                        "--fake-super requires an rsync with extended attributes enabled\n");
210 +               return 0;
211 +       }
212 +#endif
213 +
214         if (write_batch && read_batch) {
215                 snprintf(err_buf, sizeof err_buf,
216                         "--write-batch and --read-batch can not be used together\n");
217 --- old/rsync.c
218 +++ new/rsync.c
219 @@ -261,6 +261,8 @@ int set_file_attrs(const char *fname, st
220  #ifdef SUPPORT_XATTRS
221         if (preserve_xattrs && fnamecmp)
222                 set_xattr(fname, file, fnamecmp, sxp);
223 +       if (am_root < 0)
224 +               set_stat_xattr(fname, file);
225  #endif
226  
227         if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
228 @@ -300,7 +302,9 @@ int set_file_attrs(const char *fname, st
229                                         (long)sxp->st.st_gid, (long)F_GID(file));
230                         }
231                 }
232 -               if (do_lchown(fname,
233 +               if (am_root < 0) {
234 +                       ;
235 +               } else if (do_lchown(fname,
236                     change_uid ? F_UID(file) : sxp->st.st_uid,
237                     change_gid ? F_GID(file) : sxp->st.st_gid) != 0) {
238                         /* shouldn't have attempted to change uid or gid
239 @@ -309,7 +313,7 @@ int set_file_attrs(const char *fname, st
240                             change_uid ? "chown" : "chgrp",
241                             full_fname(fname));
242                         goto cleanup;
243 -               }
244 +               } else
245                 /* a lchown had been done - we have to re-stat if the
246                  * destination had the setuid or setgid bits set due
247                  * to the side effect of the chown call */
248 @@ -336,7 +340,7 @@ int set_file_attrs(const char *fname, st
249  
250  #ifdef HAVE_CHMOD
251         if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
252 -               int ret = do_chmod(fname, new_mode);
253 +               int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
254                 if (ret < 0) {
255                         rsyserr(FERROR, errno,
256                                 "failed to set permissions on %s",
257 --- old/rsync.h
258 +++ new/rsync.h
259 @@ -805,6 +805,12 @@ typedef struct {
260  
261  #include "proto.h"
262  
263 +#ifndef SUPPORT_XATTRS
264 +#define x_stat(fn,fst,xst) do_stat(fn,fst)
265 +#define x_lstat(fn,fst,xst) do_lstat(fn,fst)
266 +#define x_fstat(fd,fst,xst) do_fstat(fd,fst)
267 +#endif
268 +
269  /* We have replacement versions of these if they're missing. */
270  #ifndef HAVE_ASPRINTF
271  int asprintf(char **ptr, const char *format, ...);
272 @@ -1023,6 +1029,26 @@ int inet_pton(int af, const char *src, v
273  const char *get_panic_action(void);
274  #endif
275  
276 +static inline int to_wire_mode(mode_t mode)
277 +{
278 +#ifdef SUPPORT_LINKS
279 +#if _S_IFLNK != 0120000
280 +       if (S_ISLNK(mode))
281 +               return (mode & ~(_S_IFMT)) | 0120000;
282 +#endif
283 +#endif
284 +       return mode;
285 +}
286 +
287 +static inline mode_t from_wire_mode(int mode)
288 +{
289 +#if _S_IFLNK != 0120000
290 +       if ((mode & (_S_IFMT)) == 0120000)
291 +               return (mode & ~(_S_IFMT)) | _S_IFLNK;
292 +#endif
293 +       return mode;
294 +}
295 +
296  static inline int
297  isDigit(const char *ptr)
298  {
299 --- old/rsync.yo
300 +++ new/rsync.yo
301 @@ -333,6 +333,7 @@ to the detailed description below for a 
302   -t, --times                 preserve times
303   -O, --omit-dir-times        omit directories when preserving times
304       --super                 receiver attempts super-user activities
305 +     --fake-super            store/recover privileged attrs using xattrs
306   -S, --sparse                handle sparse files efficiently
307   -n, --dry-run               show what would have been transferred
308   -W, --whole-file            copy files whole (without rsync algorithm)
309 @@ -859,7 +860,7 @@ permission value can be applied to the f
310  dit(bf(-o, --owner)) This option causes rsync to set the owner of the
311  destination file to be the same as the source file, but only if the
312  receiving rsync is being run as the super-user (see also the bf(--super)
313 -option to force rsync to attempt super-user activities).
314 +and bf(--fake-super) options).
315  Without this option, the owner is set to the invoking user on the
316  receiving side.
317  
318 @@ -882,7 +883,7 @@ default, but may fall back to using the 
319  dit(bf(--devices)) This option causes rsync to transfer character and
320  block device files to the remote system to recreate these devices.
321  This option has no effect if the receiving rsync is not run as the
322 -super-user and bf(--super) is not specified.
323 +super-user (see also the bf(--super) and bf(--fake-super) options).
324  
325  dit(bf(--specials)) This option causes rsync to transfer special files
326  such as named sockets and fifos.
327 @@ -912,6 +913,34 @@ also for ensuring that you will get erro
328  being running as the super-user.  To turn off super-user activities, the
329  super-user can use bf(--no-super).
330  
331 +dit(bf(--fake-super)) When this option is enabled, rsync simulates
332 +super-user activities by saving/restoring the privileged attributes via a
333 +special extended attribute that is attached to each file (as needed).  This
334 +includes the file's owner and group (if it is not the default), the file's
335 +device info (device & special files are created as empty text files), and
336 +any permission bits that we won't allow to be set on the real file (e.g.
337 +the real file gets u-s,g-s,o-t for safety) or that would limit the owner's
338 +access (since the real super-user can always access/change a file or
339 +directory, the files we create can always be accessed/changed by the
340 +creating user).
341 +
342 +The bf(--fake-super) option only affects the side where the option is used.
343 +To affect the remote side of a remote-shell connection, specify an rsync
344 +path:
345 +
346 +quote(tt(  rsync -av --rsync-path="rsync --fake-super" /src/ host:/dest/))
347 +
348 +Since there is only one "side" in a local copy, this option affects both
349 +the sending and recieving of files.  You'll need to specify a copy using
350 +"localhost" if you need to avoid this.  Note, however, that it is always
351 +safe to copy from some non-fake-super files into some fake-super files
352 +using a local bf(--fake-super) command because the non-fake source files
353 +will just have their normal attributes.
354 +
355 +This option is overridden by both bf(--super) and bf(--no-super).
356 +
357 +See also the "fake super" setting in the daemon's rsyncd.conf file.
358 +
359  dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take
360  up less space on the destination.  Conflicts with bf(--inplace) because it's
361  not possible to overwrite data in a sparse fashion.
362 --- old/rsyncd.conf.yo
363 +++ new/rsyncd.conf.yo
364 @@ -226,6 +226,11 @@ file transfers to and from that module s
365  was run as root. This complements the "uid" option. The default is gid -2,
366  which is normally the group "nobody".
367  
368 +dit(bf(fake super)) Setting "fake super = yes" for a module causes the
369 +daemon side to behave as if the bf(--fake-user) command-line option had
370 +been specified.  This allows the full attributes of a file to be stored
371 +without having to have the daemon actually running as root.
372 +
373  dit(bf(filter)) The "filter" option allows you to specify a space-separated
374  list of filter rules that the daemon will not allow to be read or written.
375  This is only superficially equivalent to the client specifying these
376 --- old/syscall.c
377 +++ new/syscall.c
378 @@ -27,6 +27,7 @@
379  #endif
380  
381  extern int dry_run;
382 +extern int am_root;
383  extern int read_only;
384  extern int list_only;
385  extern int preserve_perms;
386 @@ -78,6 +79,15 @@ int do_mknod(const char *pathname, mode_
387  {
388         if (dry_run) return 0;
389         RETURN_ERROR_IF_RO_OR_LO;
390 +
391 +       /* For --fake-super, we create a normal file with mode 0600. */
392 +       if (am_root < 0) {
393 +               int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
394 +               if (fd < 0 || close(fd) < 0)
395 +                       return -1;
396 +               return 0;
397 +       }
398 +
399  #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
400         if (S_ISFIFO(mode))
401                 return mkfifo(pathname, mode);
402 --- old/t_unsafe.c
403 +++ new/t_unsafe.c
404 @@ -23,7 +23,11 @@
405  
406  #include "rsync.h"
407  
408 -int dry_run, read_only, list_only, verbose;
409 +int dry_run = 0;
410 +int am_root = 0;
411 +int read_only = 0;
412 +int list_only = 0;
413 +int verbose = 0;
414  int preserve_perms = 0;
415  
416  int
417 --- old/tls.c
418 +++ new/tls.c
419 @@ -39,6 +39,7 @@
420  
421  /* These are to make syscall.o shut up. */
422  int dry_run = 0;
423 +int am_root = 0;
424  int read_only = 1;
425  int list_only = 0;
426  int preserve_perms = 0;
427 --- old/trimslash.c
428 +++ new/trimslash.c
429 @@ -22,6 +22,7 @@
430  
431  /* These are to make syscall.o shut up. */
432  int dry_run = 0;
433 +int am_root = 0;
434  int read_only = 1;
435  int list_only = 0;
436  int preserve_perms = 0;
437 --- old/xattrs.c
438 +++ new/xattrs.c
439 @@ -53,11 +53,16 @@ extern int checksum_seed;
440  #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
441  
442  #ifdef HAVE_LINUX_XATTRS
443 -#define RPRE_LEN 0
444 +#define MIGHT_NEED_RPRE (am_root < 0)
445 +#define RSYNC_PREFIX USER_PREFIX "rsync."
446  #else
447 +#define MIGHT_NEED_RPRE am_root
448  #define RSYNC_PREFIX "rsync."
449 -#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
450  #endif
451 +#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
452 +
453 +#define XSTAT_ATTR RSYNC_PREFIX "%stat"
454 +#define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1)
455  
456  typedef struct {
457         char *datum, *name;
458 @@ -218,6 +223,10 @@ static int rsync_xal_get(const char *fna
459                         continue;
460  #endif
461  
462 +               if (am_root < 0 && name_len == XSTAT_LEN + 1
463 +                && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
464 +                       continue;
465 +
466                 datum_len = name_len; /* Pass extra size to get_xattr_data() */
467                 if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
468                         return -1;
469 @@ -236,6 +245,14 @@ static int rsync_xal_get(const char *fna
470                 } else
471                         name_offset = datum_len;
472  
473 +#ifdef HAVE_LINUX_XATTRS
474 +               if (am_root < 0 && name_len > RPRE_LEN
475 +                && HAS_PREFIX(name, RSYNC_PREFIX)) {
476 +                       name += RPRE_LEN;
477 +                       name_len -= RPRE_LEN;
478 +               }
479 +#endif
480 +
481                 rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
482                 rxas->name = ptr + name_offset;
483                 memcpy(rxas->name, name, name_len);
484 @@ -576,13 +593,9 @@ void receive_xattr(struct file_struct *f
485                 size_t name_len = read_abbrevint(f);
486                 size_t datum_len = read_abbrevint(f);
487                 size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
488 -#ifdef HAVE_LINUX_XATTRS
489 -               size_t extra_len = 0;
490 -#else
491 -               size_t extra_len = am_root ? RPRE_LEN : 0;
492 +               size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
493                 if (dget_len + extra_len < dget_len)
494                         out_of_memory("receive_xattr"); /* overflow */
495 -#endif
496                 if (dget_len + extra_len + name_len < dget_len)
497                         out_of_memory("receive_xattr"); /* overflow */
498                 ptr = new_array(char, dget_len + extra_len + name_len);
499 @@ -598,9 +611,14 @@ void receive_xattr(struct file_struct *f
500                 }
501  #ifdef HAVE_LINUX_XATTRS
502                 /* Non-root can only save the user namespace. */
503 -               if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
504 -                       free(ptr);
505 -                       continue;
506 +                       if (am_root <= 0 && !HAS_PREFIX(name, USER_PREFIX)) {
507 +                               if (!am_root) {
508 +                                       free(ptr);
509 +                                       continue;
510 +                               }
511 +                               name -= RPRE_LEN;
512 +                               name_len += RPRE_LEN;
513 +                               memcpy(name, RSYNC_PREFIX, RPRE_LEN);
514                 }
515  #else
516                 /* This OS only has a user namespace, so we either
517 @@ -618,6 +636,11 @@ void receive_xattr(struct file_struct *f
518                         continue;
519                 }
520  #endif
521 +               if (am_root < 0 && name_len == XSTAT_LEN + 1
522 +                && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0) {
523 +                       free(ptr);
524 +                       continue;
525 +               }
526                 rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1);
527                 rxa->name = name;
528                 rxa->datum = ptr;
529 @@ -772,4 +795,150 @@ int set_xattr(const char *fname, const s
530         return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp);
531  }
532  
533 +int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
534 +{
535 +       int mode, rdev_major, rdev_minor, uid, gid, len;
536 +       char buf[256];
537 +
538 +       if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
539 +               return -1;
540 +
541 +       if (xst)
542 +               *xst = *fst;
543 +       else
544 +               xst = fst;
545 +       if (fname) {
546 +               fd = -1;
547 +               len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
548 +       } else {
549 +               fname = "fd";
550 +               len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
551 +       }
552 +       if (len >= (int)sizeof buf) {
553 +               len = -1;
554 +               errno = ERANGE;
555 +       }
556 +       if (len < 0) {
557 +               if (errno == ENOTSUP || errno == ENOATTR)
558 +                       return -1;
559 +               if (errno == EPERM && S_ISLNK(fst->st_mode)) {
560 +                       xst->st_uid = 0;
561 +                       xst->st_gid = 0;
562 +                       return 0;
563 +               }
564 +               rsyserr(FERROR, errno, "failed to read xattr %s for %s",
565 +                       XSTAT_ATTR, full_fname(fname));
566 +               return -1;
567 +       }
568 +       buf[len] = '\0';
569 +
570 +       if (sscanf(buf, "%o %d,%d %d:%d",
571 +                  &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
572 +               rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
573 +                       XSTAT_ATTR, full_fname(fname), buf);
574 +               exit_cleanup(RERR_FILEIO);
575 +       }
576 +
577 +       xst->st_mode = from_wire_mode(mode);
578 +       xst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
579 +       xst->st_uid = uid;
580 +       xst->st_gid = gid;
581 +
582 +       return 0;
583 +}
584 +
585 +int set_stat_xattr(const char *fname, struct file_struct *file)
586 +{
587 +       STRUCT_STAT fst, xst;
588 +       dev_t rdev;
589 +       mode_t mode, fmode;
590 +
591 +       if (dry_run)
592 +               return 0;
593 +
594 +       if (read_only || list_only) {
595 +               rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
596 +                       XSTAT_ATTR, full_fname(fname));
597 +               return -1;
598 +       }
599 +
600 +       if (x_lstat(fname, &fst, &xst) < 0) {
601 +               rsyserr(FERROR, errno, "failed to re-stat %s",
602 +                       full_fname(fname));
603 +               return -1;
604 +       }
605 +
606 +       fst.st_mode &= (_S_IFMT | CHMOD_BITS);
607 +       fmode = file->mode & (_S_IFMT | CHMOD_BITS);
608 +
609 +       if (IS_DEVICE(fmode) || IS_SPECIAL(fmode)) {
610 +               uint32 *devp = F_RDEV_P(file);
611 +               rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
612 +       } else
613 +               rdev = 0;
614 +
615 +       /* Dump the special permissions and enable full owner access. */
616 +       mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
617 +            | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
618 +       if (fst.st_mode != mode)
619 +               do_chmod(fname, mode);
620 +       if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
621 +               fst.st_rdev = 0; /* just in case */
622 +
623 +       if (mode == fmode && fst.st_rdev == rdev
624 +        && fst.st_uid == F_UID(file) && fst.st_gid == F_GID(file)) {
625 +               /* xst.st_mode will be 0 if there's no current stat xattr */
626 +               if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
627 +                       rsyserr(FERROR, errno,
628 +                               "delete of stat xattr failed for %s",
629 +                               full_fname(fname));
630 +                       return -1;
631 +               }
632 +               return 0;
633 +       }
634 +
635 +       if (xst.st_mode != fmode || xst.st_rdev != rdev
636 +        || xst.st_uid != F_UID(file) || xst.st_gid != F_GID(file)) {
637 +               char buf[256];
638 +               int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
639 +                       to_wire_mode(fmode),
640 +                       (int)major(rdev), (int)minor(rdev),
641 +                       (int)F_UID(file), (int)F_GID(file));
642 +               if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) {
643 +                       if (errno == EPERM && S_ISLNK(fst.st_mode))
644 +                               return 0;
645 +                       rsyserr(FERROR, errno,
646 +                               "failed to write xattr %s for %s",
647 +                               XSTAT_ATTR, full_fname(fname));
648 +                       return -1;
649 +               }
650 +       }
651 +
652 +       return 0;
653 +}
654 +
655 +int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
656 +{
657 +       int ret = do_stat(fname, fst);
658 +       if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
659 +               xst->st_mode = 0;
660 +       return ret;
661 +}
662 +
663 +int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
664 +{
665 +       int ret = do_lstat(fname, fst);
666 +       if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
667 +               xst->st_mode = 0;
668 +       return ret;
669 +}
670 +
671 +int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
672 +{
673 +       int ret = do_fstat(fd, fst);
674 +       if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
675 +               xst->st_mode = 0;
676 +       return ret;
677 +}
678 +
679  #endif /* SUPPORT_XATTRS */