This is now on the trunk.
authorWayne Davison <wayned@samba.org>
Tue, 24 Apr 2007 07:33:10 +0000 (07:33 +0000)
committerWayne Davison <wayned@samba.org>
Tue, 24 Apr 2007 07:33:10 +0000 (07:33 +0000)
fake-super.diff [deleted file]

diff --git a/fake-super.diff b/fake-super.diff
deleted file mode 100644 (file)
index b29824f..0000000
+++ /dev/null
@@ -1,679 +0,0 @@
-This patch adds a new option:  --fake-super, which tells rsync to copy in a
-fake super-user mode that stores various file attributes in an extended-
-attribute value instead of as real file-system attributes.  See the changes
-to the manpages for details.
-
-To use this patch, run these commands for a successful build:
-
-    patch -p1 <patches/fake-super.diff
-    ./configure                         (optional if already run)
-    make
-
---- old/backup.c
-+++ new/backup.c
-@@ -127,7 +127,7 @@ static int make_bak_dir(char *fullpath)
-               if (p >= rel) {
-                       /* Try to transfer the directory settings of the
-                        * actual dir that the files are coming from. */
--                      if (do_stat(rel, &sx.st) < 0) {
-+                      if (x_stat(rel, &sx.st, NULL) < 0) {
-                               rsyserr(FERROR, errno,
-                                       "make_bak_dir stat %s failed",
-                                       full_fname(rel));
-@@ -200,7 +200,7 @@ static int keep_backup(const char *fname
-       int ret_code;
-       /* return if no file to keep */
--      if (do_lstat(fname, &sx.st) < 0)
-+      if (x_lstat(fname, &sx.st, NULL) < 0)
-               return 1;
- #ifdef SUPPORT_ACLS
-       sx.acc_acl = sx.def_acl = NULL;
---- old/clientserver.c
-+++ new/clientserver.c
-@@ -630,6 +630,11 @@ static int rsync_module(int f_in, int f_
-       if (lp_ignore_errors(module_id))
-               ignore_errors = 1;
-+      if (lp_fake_super(i))
-+              am_root = -1;
-+      else if (am_root < 0) /* Treat --fake-super from client as --super. */
-+              am_root = 2;
-+
-       if (filesfrom_fd == 0)
-               filesfrom_fd = f_in;
---- old/flist.c
-+++ new/flist.c
-@@ -196,12 +196,12 @@ static int readlink_stat(const char *pat
-                               rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
-                                       path, linkbuf);
-                       }
--                      return do_stat(path, stp);
-+                      return x_stat(path, stp, NULL);
-               }
-       }
-       return 0;
- #else
--      return do_stat(path, stp);
-+      return x_stat(path, stp, NULL);
- #endif
- }
-@@ -209,17 +209,17 @@ int link_stat(const char *path, STRUCT_S
- {
- #ifdef SUPPORT_LINKS
-       if (copy_links)
--              return do_stat(path, stp);
--      if (do_lstat(path, stp) < 0)
-+              return x_stat(path, stp, NULL);
-+      if (x_lstat(path, stp, NULL) < 0)
-               return -1;
-       if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
-               STRUCT_STAT st;
--              if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
-+              if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
-                       *stp = st;
-       }
-       return 0;
- #else
--      return do_stat(path, stp);
-+      return x_stat(path, stp, NULL);
- #endif
- }
-@@ -254,26 +254,6 @@ static int is_excluded(char *fname, int 
-       return 0;
- }
--static int to_wire_mode(mode_t mode)
--{
--#ifdef SUPPORT_LINKS
--#if _S_IFLNK != 0120000
--      if (S_ISLNK(mode))
--              return (mode & ~(_S_IFMT)) | 0120000;
--#endif
--#endif
--      return mode;
--}
--
--static mode_t from_wire_mode(int mode)
--{
--#if _S_IFLNK != 0120000
--      if ((mode & (_S_IFMT)) == 0120000)
--              return (mode & ~(_S_IFMT)) | _S_IFLNK;
--#endif
--      return mode;
--}
--
- static void send_directory(int f, struct file_list *flist, int ndx,
-                          char *fbuf, int len, int flags);
-@@ -954,7 +934,7 @@ struct file_struct *make_file(const char
-               if (save_errno == ENOENT) {
- #ifdef SUPPORT_LINKS
-                       /* Avoid "vanished" error if symlink points nowhere. */
--                      if (copy_links && do_lstat(thisname, &st) == 0
-+                      if (copy_links && x_lstat(thisname, &st, NULL) == 0
-                           && S_ISLNK(st.st_mode)) {
-                               io_error |= IOERR_GENERAL;
-                               rprintf(FERROR, "symlink has no referent: %s\n",
-@@ -1126,7 +1106,7 @@ struct file_struct *make_file(const char
-               int save_mode = file->mode;
-               file->mode = S_IFDIR; /* Find a directory with our name. */
-               if (flist_find(dir_flist, file) >= 0
--                  && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
-+                  && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
-                       file->modtime = st2.st_mtime;
-                       file->len32 = 0;
-                       file->mode = st2.st_mode;
---- old/loadparm.c
-+++ new/loadparm.c
-@@ -149,6 +149,7 @@ typedef struct
-       int syslog_facility;
-       int timeout;
-+      BOOL fake_super;
-       BOOL ignore_errors;
-       BOOL ignore_nonreadable;
-       BOOL list;
-@@ -196,6 +197,7 @@ static service sDefault =
-  /* syslog_facility; */               LOG_DAEMON,
-  /* timeout; */                       0,
-+ /* fake_super; */            False,
-  /* ignore_errors; */         False,
-  /* ignore_nonreadable; */    False,
-  /* list; */                  True,
-@@ -297,6 +299,7 @@ static struct parm_struct parm_table[] =
-  {"dont compress",     P_STRING, P_LOCAL, &sDefault.dont_compress,     NULL,0},
-  {"exclude from",      P_STRING, P_LOCAL, &sDefault.exclude_from,      NULL,0},
-  {"exclude",           P_STRING, P_LOCAL, &sDefault.exclude,           NULL,0},
-+ {"fake super",        P_BOOL,   P_LOCAL, &sDefault.fake_super,        NULL,0},
-  {"filter",            P_STRING, P_LOCAL, &sDefault.filter,            NULL,0},
-  {"gid",               P_STRING, P_LOCAL, &sDefault.gid,               NULL,0},
-  {"hosts allow",       P_STRING, P_LOCAL, &sDefault.hosts_allow,       NULL,0},
-@@ -411,6 +414,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
- FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
- FN_LOCAL_INTEGER(lp_timeout, timeout)
-+FN_LOCAL_BOOL(lp_fake_super, fake_super)
- FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
- FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
- FN_LOCAL_BOOL(lp_list, list)
-@@ -814,7 +818,7 @@ BOOL lp_load(char *pszFname, int globals
-       if (pszFname)
-           pstrcpy(n2,pszFname);
--      else if (am_server && !am_root)
-+      else if (am_server && am_root <= 0)
-           pstrcpy(n2,RSYNCD_USERCONF);
-       else
-           pstrcpy(n2,RSYNCD_SYSCONF);
---- old/options.c
-+++ new/options.c
-@@ -72,7 +72,7 @@ int protocol_version = PROTOCOL_VERSION;
- int sparse_files = 0;
- int do_compression = 0;
- int def_compress_level = Z_DEFAULT_COMPRESSION;
--int am_root = 0;
-+int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
- int am_server = 0;
- int am_sender = 0;
- int am_generator = 0;
-@@ -326,6 +326,9 @@ void usage(enum logcode F)
-   rprintf(F," -t, --times                 preserve times\n");
-   rprintf(F," -O, --omit-dir-times        omit directories when preserving times\n");
-   rprintf(F,"     --super                 receiver attempts super-user activities\n");
-+#ifdef SUPPORT_XATTRS
-+  rprintf(F,"     --fake-super            store/recover privileged attrs using xattrs\n");
-+#endif
-   rprintf(F," -S, --sparse                handle sparse files efficiently\n");
-   rprintf(F," -n, --dry-run               show what would have been transferred\n");
-   rprintf(F," -W, --whole-file            copy files whole (without rsync algorithm)\n");
-@@ -455,6 +458,7 @@ static struct poptOption long_options[] 
-   {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
-   {"super",            0,  POPT_ARG_VAL,    &am_root, 2, 0, 0 },
-   {"no-super",         0,  POPT_ARG_VAL,    &am_root, 0, 0, 0 },
-+  {"fake-super",       0,  POPT_ARG_VAL,    &am_root, -1, 0, 0 },
-   {"owner",           'o', POPT_ARG_VAL,    &preserve_uid, 1, 0, 0 },
-   {"no-owner",         0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
-   {"no-o",             0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
-@@ -1187,6 +1191,14 @@ int parse_arguments(int *argc, const cha
-       }
- #endif
-+#ifndef SUPPORT_XATTRS
-+      if (am_root < 0) {
-+              snprintf(err_buf, sizeof err_buf,
-+                       "--fake-super requires an rsync with extended attributes enabled\n");
-+              return 0;
-+      }
-+#endif
-+
-       if (write_batch && read_batch) {
-               snprintf(err_buf, sizeof err_buf,
-                       "--write-batch and --read-batch can not be used together\n");
---- old/rsync.c
-+++ new/rsync.c
-@@ -261,6 +261,8 @@ int set_file_attrs(const char *fname, st
- #ifdef SUPPORT_XATTRS
-       if (preserve_xattrs && fnamecmp)
-               set_xattr(fname, file, fnamecmp, sxp);
-+      if (am_root < 0)
-+              set_stat_xattr(fname, file);
- #endif
-       if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
-@@ -300,7 +302,9 @@ int set_file_attrs(const char *fname, st
-                                       (long)sxp->st.st_gid, (long)F_GID(file));
-                       }
-               }
--              if (do_lchown(fname,
-+              if (am_root < 0) {
-+                      ;
-+              } else if (do_lchown(fname,
-                   change_uid ? F_UID(file) : sxp->st.st_uid,
-                   change_gid ? F_GID(file) : sxp->st.st_gid) != 0) {
-                       /* shouldn't have attempted to change uid or gid
-@@ -309,7 +313,7 @@ int set_file_attrs(const char *fname, st
-                           change_uid ? "chown" : "chgrp",
-                           full_fname(fname));
-                       goto cleanup;
--              }
-+              } else
-               /* a lchown had been done - we have to re-stat if the
-                * destination had the setuid or setgid bits set due
-                * to the side effect of the chown call */
-@@ -336,7 +340,7 @@ int set_file_attrs(const char *fname, st
- #ifdef HAVE_CHMOD
-       if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
--              int ret = do_chmod(fname, new_mode);
-+              int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
-               if (ret < 0) {
-                       rsyserr(FERROR, errno,
-                               "failed to set permissions on %s",
---- old/rsync.h
-+++ new/rsync.h
-@@ -805,6 +805,12 @@ typedef struct {
- #include "proto.h"
-+#ifndef SUPPORT_XATTRS
-+#define x_stat(fn,fst,xst) do_stat(fn,fst)
-+#define x_lstat(fn,fst,xst) do_lstat(fn,fst)
-+#define x_fstat(fd,fst,xst) do_fstat(fd,fst)
-+#endif
-+
- /* We have replacement versions of these if they're missing. */
- #ifndef HAVE_ASPRINTF
- int asprintf(char **ptr, const char *format, ...);
-@@ -1023,6 +1029,26 @@ int inet_pton(int af, const char *src, v
- const char *get_panic_action(void);
- #endif
-+static inline int to_wire_mode(mode_t mode)
-+{
-+#ifdef SUPPORT_LINKS
-+#if _S_IFLNK != 0120000
-+      if (S_ISLNK(mode))
-+              return (mode & ~(_S_IFMT)) | 0120000;
-+#endif
-+#endif
-+      return mode;
-+}
-+
-+static inline mode_t from_wire_mode(int mode)
-+{
-+#if _S_IFLNK != 0120000
-+      if ((mode & (_S_IFMT)) == 0120000)
-+              return (mode & ~(_S_IFMT)) | _S_IFLNK;
-+#endif
-+      return mode;
-+}
-+
- static inline int
- isDigit(const char *ptr)
- {
---- old/rsync.yo
-+++ new/rsync.yo
-@@ -333,6 +333,7 @@ to the detailed description below for a 
-  -t, --times                 preserve times
-  -O, --omit-dir-times        omit directories when preserving times
-      --super                 receiver attempts super-user activities
-+     --fake-super            store/recover privileged attrs using xattrs
-  -S, --sparse                handle sparse files efficiently
-  -n, --dry-run               show what would have been transferred
-  -W, --whole-file            copy files whole (without rsync algorithm)
-@@ -859,7 +860,7 @@ permission value can be applied to the f
- dit(bf(-o, --owner)) This option causes rsync to set the owner of the
- destination file to be the same as the source file, but only if the
- receiving rsync is being run as the super-user (see also the bf(--super)
--option to force rsync to attempt super-user activities).
-+and bf(--fake-super) options).
- Without this option, the owner is set to the invoking user on the
- receiving side.
-@@ -882,7 +883,7 @@ default, but may fall back to using the 
- dit(bf(--devices)) This option causes rsync to transfer character and
- block device files to the remote system to recreate these devices.
- This option has no effect if the receiving rsync is not run as the
--super-user and bf(--super) is not specified.
-+super-user (see also the bf(--super) and bf(--fake-super) options).
- dit(bf(--specials)) This option causes rsync to transfer special files
- such as named sockets and fifos.
-@@ -912,6 +913,34 @@ also for ensuring that you will get erro
- being running as the super-user.  To turn off super-user activities, the
- super-user can use bf(--no-super).
-+dit(bf(--fake-super)) When this option is enabled, rsync simulates
-+super-user activities by saving/restoring the privileged attributes via a
-+special extended attribute that is attached to each file (as needed).  This
-+includes the file's owner and group (if it is not the default), the file's
-+device info (device & special files are created as empty text files), and
-+any permission bits that we won't allow to be set on the real file (e.g.
-+the real file gets u-s,g-s,o-t for safety) or that would limit the owner's
-+access (since the real super-user can always access/change a file or
-+directory, the files we create can always be accessed/changed by the
-+creating user).
-+
-+The bf(--fake-super) option only affects the side where the option is used.
-+To affect the remote side of a remote-shell connection, specify an rsync
-+path:
-+
-+quote(tt(  rsync -av --rsync-path="rsync --fake-super" /src/ host:/dest/))
-+
-+Since there is only one "side" in a local copy, this option affects both
-+the sending and recieving of files.  You'll need to specify a copy using
-+"localhost" if you need to avoid this.  Note, however, that it is always
-+safe to copy from some non-fake-super files into some fake-super files
-+using a local bf(--fake-super) command because the non-fake source files
-+will just have their normal attributes.
-+
-+This option is overridden by both bf(--super) and bf(--no-super).
-+
-+See also the "fake super" setting in the daemon's rsyncd.conf file.
-+
- dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take
- up less space on the destination.  Conflicts with bf(--inplace) because it's
- not possible to overwrite data in a sparse fashion.
---- old/rsyncd.conf.yo
-+++ new/rsyncd.conf.yo
-@@ -226,6 +226,11 @@ file transfers to and from that module s
- was run as root. This complements the "uid" option. The default is gid -2,
- which is normally the group "nobody".
-+dit(bf(fake super)) Setting "fake super = yes" for a module causes the
-+daemon side to behave as if the bf(--fake-user) command-line option had
-+been specified.  This allows the full attributes of a file to be stored
-+without having to have the daemon actually running as root.
-+
- dit(bf(filter)) The "filter" option allows you to specify a space-separated
- list of filter rules that the daemon will not allow to be read or written.
- This is only superficially equivalent to the client specifying these
---- old/syscall.c
-+++ new/syscall.c
-@@ -27,6 +27,7 @@
- #endif
- extern int dry_run;
-+extern int am_root;
- extern int read_only;
- extern int list_only;
- extern int preserve_perms;
-@@ -78,6 +79,15 @@ int do_mknod(const char *pathname, mode_
- {
-       if (dry_run) return 0;
-       RETURN_ERROR_IF_RO_OR_LO;
-+
-+      /* For --fake-super, we create a normal file with mode 0600. */
-+      if (am_root < 0) {
-+              int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
-+              if (fd < 0 || close(fd) < 0)
-+                      return -1;
-+              return 0;
-+      }
-+
- #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
-       if (S_ISFIFO(mode))
-               return mkfifo(pathname, mode);
---- old/t_unsafe.c
-+++ new/t_unsafe.c
-@@ -23,7 +23,11 @@
- #include "rsync.h"
--int dry_run, read_only, list_only, verbose;
-+int dry_run = 0;
-+int am_root = 0;
-+int read_only = 0;
-+int list_only = 0;
-+int verbose = 0;
- int preserve_perms = 0;
- int
---- old/tls.c
-+++ new/tls.c
-@@ -39,6 +39,7 @@
- /* These are to make syscall.o shut up. */
- int dry_run = 0;
-+int am_root = 0;
- int read_only = 1;
- int list_only = 0;
- int preserve_perms = 0;
---- old/trimslash.c
-+++ new/trimslash.c
-@@ -22,6 +22,7 @@
- /* These are to make syscall.o shut up. */
- int dry_run = 0;
-+int am_root = 0;
- int read_only = 1;
- int list_only = 0;
- int preserve_perms = 0;
---- old/xattrs.c
-+++ new/xattrs.c
-@@ -53,11 +53,16 @@ extern int checksum_seed;
- #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
- #ifdef HAVE_LINUX_XATTRS
--#define RPRE_LEN 0
-+#define MIGHT_NEED_RPRE (am_root < 0)
-+#define RSYNC_PREFIX USER_PREFIX "rsync."
- #else
-+#define MIGHT_NEED_RPRE am_root
- #define RSYNC_PREFIX "rsync."
--#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
- #endif
-+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
-+
-+#define XSTAT_ATTR RSYNC_PREFIX "%stat"
-+#define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1)
- typedef struct {
-       char *datum, *name;
-@@ -218,6 +223,10 @@ static int rsync_xal_get(const char *fna
-                       continue;
- #endif
-+              if (am_root < 0 && name_len == XSTAT_LEN + 1
-+               && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
-+                      continue;
-+
-               datum_len = name_len; /* Pass extra size to get_xattr_data() */
-               if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
-                       return -1;
-@@ -236,6 +245,14 @@ static int rsync_xal_get(const char *fna
-               } else
-                       name_offset = datum_len;
-+#ifdef HAVE_LINUX_XATTRS
-+              if (am_root < 0 && name_len > RPRE_LEN
-+               && HAS_PREFIX(name, RSYNC_PREFIX)) {
-+                      name += RPRE_LEN;
-+                      name_len -= RPRE_LEN;
-+              }
-+#endif
-+
-               rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
-               rxas->name = ptr + name_offset;
-               memcpy(rxas->name, name, name_len);
-@@ -576,13 +593,9 @@ void receive_xattr(struct file_struct *f
-               size_t name_len = read_abbrevint(f);
-               size_t datum_len = read_abbrevint(f);
-               size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
--#ifdef HAVE_LINUX_XATTRS
--              size_t extra_len = 0;
--#else
--              size_t extra_len = am_root ? RPRE_LEN : 0;
-+              size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
-               if (dget_len + extra_len < dget_len)
-                       out_of_memory("receive_xattr"); /* overflow */
--#endif
-               if (dget_len + extra_len + name_len < dget_len)
-                       out_of_memory("receive_xattr"); /* overflow */
-               ptr = new_array(char, dget_len + extra_len + name_len);
-@@ -598,9 +611,14 @@ void receive_xattr(struct file_struct *f
-               }
- #ifdef HAVE_LINUX_XATTRS
-               /* Non-root can only save the user namespace. */
--              if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
--                      free(ptr);
--                      continue;
-+                      if (am_root <= 0 && !HAS_PREFIX(name, USER_PREFIX)) {
-+                              if (!am_root) {
-+                                      free(ptr);
-+                                      continue;
-+                              }
-+                              name -= RPRE_LEN;
-+                              name_len += RPRE_LEN;
-+                              memcpy(name, RSYNC_PREFIX, RPRE_LEN);
-               }
- #else
-               /* This OS only has a user namespace, so we either
-@@ -618,6 +636,11 @@ void receive_xattr(struct file_struct *f
-                       continue;
-               }
- #endif
-+              if (am_root < 0 && name_len == XSTAT_LEN + 1
-+               && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0) {
-+                      free(ptr);
-+                      continue;
-+              }
-               rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1);
-               rxa->name = name;
-               rxa->datum = ptr;
-@@ -772,4 +795,150 @@ int set_xattr(const char *fname, const s
-       return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp);
- }
-+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
-+{
-+      int mode, rdev_major, rdev_minor, uid, gid, len;
-+      char buf[256];
-+
-+      if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
-+              return -1;
-+
-+      if (xst)
-+              *xst = *fst;
-+      else
-+              xst = fst;
-+      if (fname) {
-+              fd = -1;
-+              len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
-+      } else {
-+              fname = "fd";
-+              len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
-+      }
-+      if (len >= (int)sizeof buf) {
-+              len = -1;
-+              errno = ERANGE;
-+      }
-+      if (len < 0) {
-+              if (errno == ENOTSUP || errno == ENOATTR)
-+                      return -1;
-+              if (errno == EPERM && S_ISLNK(fst->st_mode)) {
-+                      xst->st_uid = 0;
-+                      xst->st_gid = 0;
-+                      return 0;
-+              }
-+              rsyserr(FERROR, errno, "failed to read xattr %s for %s",
-+                      XSTAT_ATTR, full_fname(fname));
-+              return -1;
-+      }
-+      buf[len] = '\0';
-+
-+      if (sscanf(buf, "%o %d,%d %d:%d",
-+                 &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
-+              rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
-+                      XSTAT_ATTR, full_fname(fname), buf);
-+              exit_cleanup(RERR_FILEIO);
-+      }
-+
-+      xst->st_mode = from_wire_mode(mode);
-+      xst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
-+      xst->st_uid = uid;
-+      xst->st_gid = gid;
-+
-+      return 0;
-+}
-+
-+int set_stat_xattr(const char *fname, struct file_struct *file)
-+{
-+      STRUCT_STAT fst, xst;
-+      dev_t rdev;
-+      mode_t mode, fmode;
-+
-+      if (dry_run)
-+              return 0;
-+
-+      if (read_only || list_only) {
-+              rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
-+                      XSTAT_ATTR, full_fname(fname));
-+              return -1;
-+      }
-+
-+      if (x_lstat(fname, &fst, &xst) < 0) {
-+              rsyserr(FERROR, errno, "failed to re-stat %s",
-+                      full_fname(fname));
-+              return -1;
-+      }
-+
-+      fst.st_mode &= (_S_IFMT | CHMOD_BITS);
-+      fmode = file->mode & (_S_IFMT | CHMOD_BITS);
-+
-+      if (IS_DEVICE(fmode) || IS_SPECIAL(fmode)) {
-+              uint32 *devp = F_RDEV_P(file);
-+              rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
-+      } else
-+              rdev = 0;
-+
-+      /* Dump the special permissions and enable full owner access. */
-+      mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
-+           | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
-+      if (fst.st_mode != mode)
-+              do_chmod(fname, mode);
-+      if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
-+              fst.st_rdev = 0; /* just in case */
-+
-+      if (mode == fmode && fst.st_rdev == rdev
-+       && fst.st_uid == F_UID(file) && fst.st_gid == F_GID(file)) {
-+              /* xst.st_mode will be 0 if there's no current stat xattr */
-+              if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
-+                      rsyserr(FERROR, errno,
-+                              "delete of stat xattr failed for %s",
-+                              full_fname(fname));
-+                      return -1;
-+              }
-+              return 0;
-+      }
-+
-+      if (xst.st_mode != fmode || xst.st_rdev != rdev
-+       || xst.st_uid != F_UID(file) || xst.st_gid != F_GID(file)) {
-+              char buf[256];
-+              int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
-+                      to_wire_mode(fmode),
-+                      (int)major(rdev), (int)minor(rdev),
-+                      (int)F_UID(file), (int)F_GID(file));
-+              if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) {
-+                      if (errno == EPERM && S_ISLNK(fst.st_mode))
-+                              return 0;
-+                      rsyserr(FERROR, errno,
-+                              "failed to write xattr %s for %s",
-+                              XSTAT_ATTR, full_fname(fname));
-+                      return -1;
-+              }
-+      }
-+
-+      return 0;
-+}
-+
-+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
-+{
-+      int ret = do_stat(fname, fst);
-+      if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
-+              xst->st_mode = 0;
-+      return ret;
-+}
-+
-+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
-+{
-+      int ret = do_lstat(fname, fst);
-+      if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
-+              xst->st_mode = 0;
-+      return ret;
-+}
-+
-+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
-+{
-+      int ret = do_fstat(fd, fst);
-+      if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
-+              xst->st_mode = 0;
-+      return ret;
-+}
-+
- #endif /* SUPPORT_XATTRS */