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