-Depends-On-Patch: acls.diff
-Depends-On-Patch: xattrs.diff
-
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. The items
-affected are:
-
- mode the real mode of the file always has the special-permission bits
- cleared (u-s,g-s,o-t) and full owner access is always enabled
- (u+rw for files and u+rwx for directories). The former makes
- the files safe if the user and/or group info was not really
- preserved, and the latter ensures that our fake-super process
- can always read & write & scan the files and directories.
-
- rdev devices and special files are created as zero-length normal
- files (with all the attributes preserved in the xattr-stat).
-
- uid the real owner will be the executor of the receiving rsync.
-
- gid the real group will be the default group of the executor.
-
-The --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:
-
- rsync -av --rsync-path='rsync --fake-super' /src/ host:/dest/
-
-The --fake-super option affects both sides of a local copy, so if you want
-to affect only one side or the other, you'll need to turn the copy into a
-remote copy to/from localhost. However, it's always safe to copy from some
-non-fake-super files into some fake-super files using a normal local copy
-since the non-fake source files will just have their normal attributes.
-
-A daemon can set "fake super = yes" in the rsync.conf file for any module
-that you'd like to be able to preserve all attributes without having it
-run as root (the client cannot affect this setting on the daemon).
+attribute value instead of as real file-system attributes. See the changes
+to the manpages for details.
-After applying this patch, run these commands for a successful build:
+To use this patch, run these commands for a successful build:
+ patch -p1 <patches/acls.diff
+ patch -p1 <patches/xattrs.diff
+ patch -p1 <patches/fake-super.diff
./prepare-source
./configure --enable-xattr-support
make
-or, if you want ACL support too:
+If you want ACL support too, use this configure command instead of the one
+above:
- ./prepare-source
./configure --enable-acl-support --enable-xattr-support
- make
--- old/backup.c
+++ new/backup.c
if (write_batch && read_batch) {
snprintf(err_buf, sizeof err_buf,
"--write-batch and --read-batch can not be used together\n");
---- old/receiver.c
-+++ new/receiver.c
-@@ -528,7 +528,7 @@ int recv_files(int f_in, struct file_lis
- if (fd1 == -1) {
- st.st_mode = 0;
- st.st_size = 0;
-- } else if (do_fstat(fd1,&st) != 0) {
-+ } else if (x_fstat(fd1, &st, NULL) != 0) {
- rsyserr(FERROR, errno, "fstat %s failed",
- full_fname(fnamecmp));
- discard_receive_data(f_in, file->length);
--- old/rsync.c
+++ new/rsync.c
-@@ -49,7 +49,6 @@ extern int preserve_gid;
- extern int inplace;
- extern int keep_dirlinks;
- extern int make_backups;
--extern mode_t orig_umask;
- extern struct stats stats;
- extern struct chmod_mode_struct *daemon_chmod_modes;
-
-@@ -197,7 +196,9 @@ int set_file_attrs(char *fname, struct f
+@@ -196,7 +196,9 @@ int set_file_attrs(char *fname, struct f
(long)sxp->st.st_gid, (long)file->gid);
}
}
change_uid ? file->uid : sxp->st.st_uid,
change_gid ? file->gid : sxp->st.st_gid) != 0) {
/* shouldn't have attempted to change uid or gid
-@@ -206,7 +207,7 @@ int set_file_attrs(char *fname, struct f
+@@ -205,7 +207,7 @@ int set_file_attrs(char *fname, struct f
change_uid ? "chown" : "chgrp",
full_fname(fname));
goto cleanup;
/* 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 */
-@@ -223,6 +224,8 @@ int set_file_attrs(char *fname, struct f
+@@ -222,6 +224,8 @@ int set_file_attrs(char *fname, struct f
#ifdef SUPPORT_XATTRS
if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
updated = 1;
#endif
#ifdef SUPPORT_ACLS
/* It's OK to call set_acl() now, even for a dir, as the generator
-@@ -237,7 +240,7 @@ int set_file_attrs(char *fname, struct f
+@@ -236,7 +240,7 @@ int set_file_attrs(char *fname, struct f
#ifdef HAVE_CHMOD
if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
/* These are to make syscall.o shut up. */
int dry_run = 0;
-+int am_root = 0; /* TODO: add option to set this to -1. */
++int am_root = 0;
int read_only = 1;
int list_only = 0;
int preserve_perms = 0;
int preserve_perms = 0;
--- old/xattr.c
+++ new/xattr.c
-@@ -28,11 +28,15 @@
- extern int dry_run;
- extern int read_only;
- extern int list_only;
-+extern int am_root;
-+extern mode_t orig_umask;
- extern unsigned int file_struct_len;
-
- #define RSYNC_XAL_INITIAL 5
- #define RSYNC_XAL_LIST_INITIAL 100
+@@ -43,11 +43,16 @@ extern unsigned int file_struct_len;
+ #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
-+#define FAKE_XATTR "user.rsync%stat"
+ #ifdef HAVE_LINUX_XATTRS
+-#define RPRE_LEN 0
++#define RSYNC_PREFIX USER_PREFIX "rsync."
+ #else
+ #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)
+
++#define CENT_POS RPRE_LEN
+
typedef struct {
- char *name;
- char *datum;
-@@ -132,9 +136,15 @@ static int rsync_xal_get(const char *fna
- if (name_size == 0)
- return 0;
- for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
-- rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
-+ rsync_xa *rxas;
+ char *datum, *name;
+@@ -148,6 +153,10 @@ static int rsync_xal_get(const char *fna
+ continue;
+ #endif
- len = strlen(name) + 1;
-+ if (am_root < 0 && len == sizeof FAKE_XATTR
-+ && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
++ if (am_root < 0 && name_len == XSTAT_LEN + 1
++ && name[CENT_POS] == '%' && strcmp(name, XSTAT_ATTR) == 0)
+ continue;
+
-+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
-+
- datum_size = sys_lgetxattr(fname, name, NULL, 0);
- if (datum_size < 0) {
+ datum_len = sys_lgetxattr(fname, name, NULL, 0);
+ if (datum_len < 0) {
if (errno == ENOTSUP)
-@@ -287,10 +297,19 @@ void receive_xattr(struct file_struct *f
- out_of_memory("receive_xattr");
- read_buf(f, ptr, name_len);
- read_buf(f, ptr + name_len, datum_len);
-+
-+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
-+ && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
+@@ -177,6 +186,13 @@ static int rsync_xal_get(const char *fna
+ return -1;
+ }
+ }
++#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 + datum_len;
+ rxas->datum = ptr;
+@@ -263,7 +279,10 @@ void send_xattr(statx *sxp, int f)
+ #else
+ /* We strip the rsync prefix from disguised namespaces
+ * and put everything else in the user namespace. */
+- if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)) {
++ if (HAS_PREFIX(rxa->name, RSYNC_PREFIX)
++ && (am_root < 0 || rxa->name_len != XSTAT_LEN + 1
++ || rxa->name[CENT_POS] != '%'
++ || strcmp(rxa->name, XSTAT_ATTR) != 0)) {
+ write_int(f, rxa->name_len - RPRE_LEN);
+ write_int(f, rxa->datum_len);
+ write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
+@@ -298,10 +317,8 @@ void receive_xattr(struct file_struct *f
+ size_t name_len = read_int(f);
+ size_t datum_len = read_int(f);
+ size_t extra_len = am_root < 0 ? RPRE_LEN : 0;
+-#ifndef HAVE_LINUX_XATTRS
+ if (datum_len + extra_len < datum_len)
+ out_of_memory("receive_xattr"); /* overflow */
+-#endif
+ if (name_len + datum_len + extra_len < name_len)
+ out_of_memory("receive_xattr"); /* overflow */
+ ptr = new_array(char, name_len + datum_len + extra_len);
+@@ -312,6 +329,10 @@ void receive_xattr(struct file_struct *f
+ read_buf(f, ptr, datum_len);
+ #ifdef HAVE_LINUX_XATTRS
+ /* Non-root can only save the user namespace. */
++ if (am_root < 0 && !HAS_PREFIX(name, USER_PREFIX)) {
++ name -= RPRE_LEN;
++ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
++ } else
+ if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
+ free(ptr);
+ continue;
+@@ -332,6 +353,11 @@ void receive_xattr(struct file_struct *f
+ continue;
+ }
+ #endif
++ if (am_root < 0 && name_len == XSTAT_LEN + 1
++ && name[CENT_POS] == '%' && strcmp(name, XSTAT_ATTR) == 0) {
+ free(ptr);
-+ temp_xattr.count--;
+ continue;
+ }
-+
- rxa->name_len = name_len;
- rxa->datum_len = datum_len;
- rxa->name = ptr;
- rxa->datum = ptr + name_len;
-+
- #ifdef HAVE_OSX_XATTRS
- if (strncmp(rxa->name, UNIQUE_PREFIX, UPRE_LEN) == 0) {
- rxa->name_len -= UPRE_LEN;
-@@ -372,4 +391,146 @@ int set_xattr(const char *fname, const s
+ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
+ rxa->name = name;
+ rxa->datum = ptr;
+@@ -411,4 +437,146 @@ int set_xattr(const char *fname, const s
return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
}
+ xst = fst;
+ if (fname) {
+ fd = -1;
-+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
++ len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
+ } else {
+ fname = "fd";
-+ len = sys_fgetxattr(fd, FAKE_XATTR, buf, sizeof buf - 1);
++ len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
+ }
+ if (len >= (int)sizeof buf) {
+ len = -1;
+ return 0;
+ }
+ rsyserr(FERROR, errno, "failed to read xattr %s for %s",
-+ FAKE_XATTR, full_fname(fname));
++ 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",
-+ FAKE_XATTR, full_fname(fname), buf);
++ XSTAT_ATTR, full_fname(fname), buf);
+ exit_cleanup(RERR_FILEIO);
+ }
+
+
+ if (read_only || list_only) {
+ rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
-+ FAKE_XATTR, full_fname(fname));
++ XSTAT_ATTR, full_fname(fname));
+ return -1;
+ }
+
+ if (mode == file->mode && fst.st_rdev == rdev
+ && fst.st_uid == file->uid && fst.st_gid == file->gid) {
+ /* xst.st_mode will be 0 if there's no current stat xattr */
-+ if (xst.st_mode && sys_lremovexattr(fname, FAKE_XATTR) < 0) {
++ if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
+ rsyserr(FERROR, errno,
+ "delete of stat xattr failed for %s",
+ full_fname(fname));
+ to_wire_mode(file->mode) & (_S_IFMT|CHMOD_BITS),
+ (int)major(rdev), (int)minor(rdev),
+ (int)file->uid, (int)file->gid);
-+ if (sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0) < 0) {
++ 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",
-+ FAKE_XATTR, full_fname(fname));
++ XSTAT_ATTR, full_fname(fname));
+ return -1;
+ }
+ }