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.
6 To use this patch, run these commands for a successful build:
8 patch -p1 <patches/acls.diff
9 patch -p1 <patches/xattrs.diff
10 patch -p1 <patches/fake-super.diff
12 ./configure --enable-xattr-support
15 If you want ACL support too, use this configure command instead of the one
18 ./configure --enable-acl-support --enable-xattr-support
22 @@ -128,7 +128,7 @@ static int make_bak_dir(char *fullpath)
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",
31 @@ -199,7 +199,7 @@ static int keep_backup(const char *fname
34 /* return if no file to keep */
35 - if (do_lstat(fname, &sx.st) < 0)
36 + if (x_lstat(fname, &sx.st, NULL) < 0)
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))
46 + if (lp_fake_super(i))
48 + else if (am_root < 0) /* Treat --fake-super from client as --super. */
51 if (filesfrom_fd == 0)
56 @@ -194,12 +194,12 @@ static int readlink_stat(const char *pat
57 rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
60 - return do_stat(path, stp);
61 + return x_stat(path, stp, NULL);
66 - return do_stat(path, stp);
67 + return x_stat(path, stp, NULL);
71 @@ -207,17 +207,17 @@ int link_stat(const char *path, STRUCT_S
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)
80 if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
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))
88 - return do_stat(path, stp);
89 + return x_stat(path, stp, NULL);
93 @@ -252,26 +252,6 @@ static int is_excluded(char *fname, int
97 -static int to_wire_mode(mode_t mode)
100 -#if _S_IFLNK != 0120000
102 - return (mode & ~(_S_IFMT)) | 0120000;
108 -static mode_t from_wire_mode(int mode)
110 -#if _S_IFLNK != 0120000
111 - if ((mode & (_S_IFMT)) == 0120000)
112 - return (mode & ~(_S_IFMT)) | _S_IFLNK;
117 static void send_directory(int f, struct file_list *flist, int ndx,
118 char *fbuf, int len, int flags);
120 @@ -925,7 +905,7 @@ struct file_struct *make_file(const char
121 if (save_errno == ENOENT) {
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;
137 file->mode = st2.st_mode;
140 @@ -150,6 +150,7 @@ typedef struct
146 BOOL ignore_nonreadable;
148 @@ -197,6 +198,7 @@ static service sDefault =
149 /* syslog_facility; */ LOG_DAEMON,
152 + /* fake_super; */ False,
153 /* ignore_errors; */ False,
154 /* ignore_nonreadable; */ False,
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)
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
175 pstrcpy(n2,pszFname);
176 - else if (am_server && !am_root)
177 + else if (am_server && am_root <= 0)
178 pstrcpy(n2,RSYNCD_USERCONF);
180 pstrcpy(n2,RSYNCD_SYSCONF);
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;
188 +int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
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");
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
214 +#ifndef SUPPORT_XATTRS
216 + snprintf(err_buf, sizeof err_buf,
217 + "--fake-super requires an rsync with extended attributes enabled\n");
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");
227 @@ -299,7 +299,9 @@ int set_file_attrs(char *fname, struct f
228 (long)sxp->st.st_gid, (long)F_GID(file));
231 - if (do_lchown(fname,
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",
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)
252 + set_stat_xattr(fname, file);
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
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);
263 rsyserr(FERROR, errno,
264 "failed to set permissions on %s",
267 @@ -813,6 +813,12 @@ typedef struct {
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)
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);
284 +static inline int to_wire_mode(mode_t mode)
286 +#ifdef SUPPORT_LINKS
287 +#if _S_IFLNK != 0120000
289 + return (mode & ~(_S_IFMT)) | 0120000;
295 +static inline mode_t from_wire_mode(int mode)
297 +#if _S_IFLNK != 0120000
298 + if ((mode & (_S_IFMT)) == 0120000)
299 + return (mode & ~(_S_IFMT)) | _S_IFLNK;
305 isDigit(const char *ptr)
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
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).
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).
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
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
354 +quote(tt( rsync -av --rsync-path="rsync --fake-super" /src/ host:/dest/))
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.
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).
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".
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.
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
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_
395 if (dry_run) return 0;
396 RETURN_ERROR_IF_RO_OR_LO;
398 + /* For --fake-super, we create a normal file with mode 0600. */
400 + int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
401 + if (fd < 0 || close(fd) < 0)
406 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
408 return mkfifo(pathname, mode);
415 -int dry_run, read_only, list_only, verbose;
421 int preserve_perms = 0;
428 /* These are to make syscall.o shut up. */
433 int preserve_perms = 0;
438 /* These are to make syscall.o shut up. */
443 int preserve_perms = 0;
446 @@ -42,11 +42,16 @@ extern int list_only;
447 #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
449 #ifdef HAVE_LINUX_XATTRS
451 +#define MIGHT_NEED_RPRE (am_root < 0)
452 +#define RSYNC_PREFIX USER_PREFIX "rsync."
454 +#define MIGHT_NEED_RPRE am_root
455 #define RSYNC_PREFIX "rsync."
456 -#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
458 +#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
460 +#define XSTAT_ATTR RSYNC_PREFIX "%stat"
461 +#define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1)
465 @@ -147,6 +152,10 @@ static int rsync_xal_get(const char *fna
469 + if (am_root < 0 && name_len == XSTAT_LEN + 1
470 + && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
473 datum_len = sys_lgetxattr(fname, name, NULL, 0);
475 if (errno == ENOTSUP)
476 @@ -176,6 +185,13 @@ static int rsync_xal_get(const char *fna
480 +#ifdef HAVE_LINUX_XATTRS
481 + if (am_root < 0 && name_len > RPRE_LEN
482 + && HAS_PREFIX(name, RSYNC_PREFIX)) {
484 + name_len -= RPRE_LEN;
487 rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
488 rxas->name = ptr + datum_len;
490 @@ -296,13 +312,9 @@ void receive_xattr(struct file_struct *f
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;
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 */
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)) {
512 + if (am_root <= 0 && !HAS_PREFIX(name, USER_PREFIX)) {
518 + name_len += RPRE_LEN;
519 + memcpy(name, RSYNC_PREFIX, RPRE_LEN);
522 /* This OS only has a user namespace, so we either
523 @@ -333,6 +350,12 @@ void receive_xattr(struct file_struct *f
527 + if (am_root < 0 && name_len == XSTAT_LEN + 1
528 + && name[RPRE_LEN] == '%'
529 + && strcmp(name, XSTAT_ATTR) == 0) {
533 rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
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! */
540 +int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
542 + int mode, rdev_major, rdev_minor, uid, gid, len;
545 + if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
554 + len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
557 + len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
559 + if (len >= (int)sizeof buf) {
564 + if (errno == ENOTSUP || errno == ENOATTR)
566 + if (errno == EPERM && S_ISLNK(fst->st_mode)) {
571 + rsyserr(FERROR, errno, "failed to read xattr %s for %s",
572 + XSTAT_ATTR, full_fname(fname));
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);
584 + xst->st_mode = from_wire_mode(mode);
585 + xst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
592 +int set_stat_xattr(const char *fname, struct file_struct *file)
594 + STRUCT_STAT fst, xst;
596 + mode_t mode, fmode;
601 + if (read_only || list_only) {
602 + rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
603 + XSTAT_ATTR, full_fname(fname));
607 + if (x_lstat(fname, &fst, &xst) < 0) {
608 + rsyserr(FERROR, errno, "failed to re-stat %s",
609 + full_fname(fname));
613 + fst.st_mode &= (_S_IFMT | CHMOD_BITS);
614 + fmode = file->mode & (_S_IFMT | CHMOD_BITS);
616 + if (IS_DEVICE(fmode) || IS_SPECIAL(fmode)) {
617 + uint32 *devp = F_RDEV_P(file);
618 + rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
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 */
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));
642 + if (xst.st_mode != fmode || xst.st_rdev != rdev
643 + || xst.st_uid != F_UID(file) || xst.st_gid != F_GID(file)) {
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))
652 + rsyserr(FERROR, errno,
653 + "failed to write xattr %s for %s",
654 + XSTAT_ATTR, full_fname(fname));
662 +int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
664 + int ret = do_stat(fname, fst);
665 + if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
670 +int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
672 + int ret = do_lstat(fname, fst);
673 + if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
678 +int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
680 + int ret = do_fstat(fd, fst);
681 + if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
686 #endif /* SUPPORT_XATTRS */