+++ /dev/null
-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 */