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