1 Depends-On-Patch: acls.diff
2 Depends-On-Patch: xattrs.diff
4 This patch adds a new option: --fake-super, which tells rsync to copy in a
5 fake super-user mode that stores various file attributes in an extended-
6 attribute value instead of as real file-system attributes. See the changes
7 to the manpages for details.
9 After applying this patch, run these commands for a successful build:
12 ./configure --enable-xattr-support
15 or, if you want ACL support too:
18 ./configure --enable-acl-support --enable-xattr-support
23 @@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath)
25 /* Try to transfer the directory settings of the
26 * actual dir that the files are coming from. */
27 - if (do_stat(rel, &sx.st) < 0) {
28 + if (x_stat(rel, &sx.st, NULL) < 0) {
29 rsyserr(FERROR, errno,
30 "make_bak_dir stat %s failed",
32 @@ -200,7 +200,7 @@ static int keep_backup(char *fname)
35 /* return if no file to keep */
36 - if (do_lstat(fname, &sx.st) < 0)
37 + if (x_lstat(fname, &sx.st, NULL) < 0)
40 sx.acc_acl = sx.def_acl = NULL;
41 --- old/clientserver.c
42 +++ new/clientserver.c
43 @@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_
44 ret = parse_arguments(&argc, (const char ***) &argv, 0);
45 quiet = 0; /* Don't let someone try to be tricky. */
47 + if (lp_fake_super(i))
49 + else if (am_root < 0) /* Treat --fake-super from client as --super. */
52 if (filesfrom_fd == 0)
57 @@ -181,7 +181,7 @@ static int readlink_stat(const char *pat
61 - return do_stat(path, stp);
62 + return x_stat(path, stp, NULL);
66 @@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S
70 - return do_stat(path, stp);
71 - if (do_lstat(path, stp) < 0)
72 + return x_stat(path, stp, NULL);
73 + if (x_lstat(path, stp, NULL) < 0)
75 if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
77 - if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
78 + if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
83 - return do_stat(path, stp);
84 + return x_stat(path, stp, NULL);
88 @@ -234,26 +234,6 @@ static int is_excluded(char *fname, int
92 -static int to_wire_mode(mode_t mode)
95 -#if _S_IFLNK != 0120000
97 - return (mode & ~(_S_IFMT)) | 0120000;
103 -static mode_t from_wire_mode(int mode)
105 -#if _S_IFLNK != 0120000
106 - if ((mode & (_S_IFMT)) == 0120000)
107 - return (mode & ~(_S_IFMT)) | _S_IFLNK;
112 static void send_directory(int f, struct file_list *flist,
113 char *fbuf, int len);
115 @@ -793,7 +773,7 @@ struct file_struct *make_file(char *fnam
116 if (save_errno == ENOENT) {
118 /* Avoid "vanished" error if symlink points nowhere. */
119 - if (copy_links && do_lstat(thisname, &st) == 0
120 + if (copy_links && x_lstat(thisname, &st, NULL) == 0
121 && S_ISLNK(st.st_mode)) {
122 io_error |= IOERR_GENERAL;
123 rprintf(FERROR, "symlink has no referent: %s\n",
124 @@ -963,7 +943,7 @@ struct file_struct *make_file(char *fnam
125 int save_mode = file->mode;
126 file->mode = S_IFDIR; /* Find a directory with our name. */
127 if (flist_find(the_file_list, file) >= 0
128 - && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
129 + && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
130 file->modtime = st2.st_mtime;
131 file->length = st2.st_size;
132 file->mode = st2.st_mode;
135 @@ -150,6 +150,7 @@ typedef struct
141 BOOL ignore_nonreadable;
143 @@ -197,6 +198,7 @@ static service sDefault =
144 /* syslog_facility; */ LOG_DAEMON,
147 + /* fake_super; */ False,
148 /* ignore_errors; */ False,
149 /* ignore_nonreadable; */ False,
151 @@ -298,6 +300,7 @@ static struct parm_struct parm_table[] =
152 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
153 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
154 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
155 + {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
156 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
157 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
158 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
159 @@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
160 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
161 FN_LOCAL_INTEGER(lp_timeout, timeout)
163 +FN_LOCAL_BOOL(lp_fake_super, fake_super)
164 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
165 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
166 FN_LOCAL_BOOL(lp_list, list)
167 @@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals
170 pstrcpy(n2,pszFname);
171 - else if (am_server && !am_root)
172 + else if (am_server && am_root <= 0)
173 pstrcpy(n2,RSYNCD_USERCONF);
175 pstrcpy(n2,RSYNCD_SYSCONF);
178 @@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION;
179 int sparse_files = 0;
180 int do_compression = 0;
181 int def_compress_level = Z_DEFAULT_COMPRESSION;
183 +int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
186 int am_generator = 0;
187 @@ -329,6 +329,9 @@ void usage(enum logcode F)
188 rprintf(F," -t, --times preserve times\n");
189 rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
190 rprintf(F," --super receiver attempts super-user activities\n");
191 +#ifdef SUPPORT_XATTRS
192 + rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
194 rprintf(F," -S, --sparse handle sparse files efficiently\n");
195 rprintf(F," -n, --dry-run show what would have been transferred\n");
196 rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
197 @@ -453,6 +456,7 @@ static struct poptOption long_options[]
198 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
199 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
200 {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
201 + {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
202 {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
203 {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
204 {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
205 @@ -1178,6 +1182,14 @@ int parse_arguments(int *argc, const cha
209 +#ifndef SUPPORT_XATTRS
211 + snprintf(err_buf, sizeof err_buf,
212 + "--fake-super requires an rsync with extended attributes enabled\n");
217 if (write_batch && read_batch) {
218 snprintf(err_buf, sizeof err_buf,
219 "--write-batch and --read-batch can not be used together\n");
222 @@ -196,7 +196,9 @@ int set_file_attrs(char *fname, struct f
223 (long)sxp->st.st_gid, (long)file->gid);
226 - if (do_lchown(fname,
229 + } else if (do_lchown(fname,
230 change_uid ? file->uid : sxp->st.st_uid,
231 change_gid ? file->gid : sxp->st.st_gid) != 0) {
232 /* shouldn't have attempted to change uid or gid
233 @@ -205,7 +207,7 @@ int set_file_attrs(char *fname, struct f
234 change_uid ? "chown" : "chgrp",
239 /* a lchown had been done - we have to re-stat if the
240 * destination had the setuid or setgid bits set due
241 * to the side effect of the chown call */
242 @@ -222,6 +224,8 @@ int set_file_attrs(char *fname, struct f
243 #ifdef SUPPORT_XATTRS
244 if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
247 + set_stat_xattr(fname, file);
250 /* It's OK to call set_acl() now, even for a dir, as the generator
251 @@ -236,7 +240,7 @@ int set_file_attrs(char *fname, struct f
254 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
255 - int ret = do_chmod(fname, new_mode);
256 + int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
258 rsyserr(FERROR, errno,
259 "failed to set permissions on %s",
262 @@ -707,6 +707,12 @@ typedef struct {
266 +#ifndef SUPPORT_XATTRS
267 +#define x_stat(fn,fst,xst) do_stat(fn,fst)
268 +#define x_lstat(fn,fst,xst) do_lstat(fn,fst)
269 +#define x_fstat(fd,fst,xst) do_fstat(fd,fst)
272 /* We have replacement versions of these if they're missing. */
273 #ifndef HAVE_ASPRINTF
274 int asprintf(char **ptr, const char *format, ...);
275 @@ -924,3 +930,23 @@ int inet_pton(int af, const char *src, v
276 #ifdef MAINTAINER_MODE
277 const char *get_panic_action(void);
280 +static inline int to_wire_mode(mode_t mode)
282 +#ifdef SUPPORT_LINKS
283 +#if _S_IFLNK != 0120000
285 + return (mode & ~(_S_IFMT)) | 0120000;
291 +static inline mode_t from_wire_mode(int mode)
293 +#if _S_IFLNK != 0120000
294 + if ((mode & (_S_IFMT)) == 0120000)
295 + return (mode & ~(_S_IFMT)) | _S_IFLNK;
301 @@ -333,6 +333,7 @@ to the detailed description below for a
302 -t, --times preserve times
303 -O, --omit-dir-times omit directories when preserving times
304 --super receiver attempts super-user activities
305 + --fake-super store/recover privileged attrs using xattrs
306 -S, --sparse handle sparse files efficiently
307 -n, --dry-run show what would have been transferred
308 -W, --whole-file copy files whole (without rsync algorithm)
309 @@ -846,7 +847,7 @@ permission value can be applied to the f
310 dit(bf(-o, --owner)) This option causes rsync to set the owner of the
311 destination file to be the same as the source file, but only if the
312 receiving rsync is being run as the super-user (see also the bf(--super)
313 -option to force rsync to attempt super-user activities).
314 +and bf(--fake-super) options).
315 Without this option, the owner is set to the invoking user on the
318 @@ -869,7 +870,7 @@ default, but may fall back to using the
319 dit(bf(--devices)) This option causes rsync to transfer character and
320 block device files to the remote system to recreate these devices.
321 This option has no effect if the receiving rsync is not run as the
322 -super-user and bf(--super) is not specified.
323 +super-user (see also the bf(--super) and bf(--fake-super) options).
325 dit(bf(--specials)) This option causes rsync to transfer special files
326 such as named sockets and fifos.
327 @@ -899,6 +900,33 @@ also for ensuring that you will get erro
328 being running as the super-user. To turn off super-user activities, the
329 super-user can use bf(--no-super).
331 +dit(bf(--fake-super)) When this option is enabled, rsync simulates
332 +super-user activities by saving/restoring the privileged attributes via a
333 +special extended attribute that is attached to each file (as needed). This
334 +includes the file's owner and group (if it is not the default), the file's
335 +device info (device & special files are created as empty text files), and
336 +any permission bits that we won't allow to be set on the real file (e.g.
337 +the real file gets u-s,g-s,o-t for safety) or that would limit the owner's
338 +access (since the real super-user can always access/change a file or
339 +directory, the files we create can always be accessed/changed by the
342 +The bf(--fake-super) option only affects the side where the option is used.
343 +To affect the remote side of a remote-shell connection, specify an rsync
346 +quote(tt( rsync -av --rsync-path="rsync --fake-super" /src/ host:/dest/))
348 +Since there is only one "side" in a local copy, this option affects both
349 +the sending and recieving of files. You'll need to specify a copy using
350 +"localhost" if you need to avoid this. Note, however, that it is always
351 +safe to copy from some non-fake-super files into some fake-super files
352 +using a local bf(--fake-super) command because the non-fake source files
353 +will just have their normal attributes.
355 +See also the "fake super" setting in the daemon's rsyncd.conf file.
356 +This option is overridden by both bf(--super) and bf(--no-super).
358 dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take
359 up less space on the destination. Conflicts with bf(--inplace) because it's
360 not possible to overwrite data in a sparse fashion.
361 --- old/rsyncd.conf.yo
362 +++ new/rsyncd.conf.yo
363 @@ -226,6 +226,11 @@ file transfers to and from that module s
364 was run as root. This complements the "uid" option. The default is gid -2,
365 which is normally the group "nobody".
367 +dit(bf(fake super)) Setting "fake super = yes" for a module causes the
368 +daemon side to behave as if the bf(--fake-user) command-line option had
369 +been specified. This allows the full attributes of a file to be stored
370 +without having to have the daemon actually running as root.
372 dit(bf(filter)) The "filter" option allows you to specify a space-separated
373 list of filter rules that the daemon will not allow to be read or written.
374 This is only superficially equivalent to the client specifying these
382 extern int read_only;
383 extern int list_only;
384 extern int preserve_perms;
385 @@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode
387 if (dry_run) return 0;
388 RETURN_ERROR_IF_RO_OR_LO;
390 + /* For --fake-super, we create a normal file with mode 0600. */
392 + int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
393 + if (fd < 0 || close(fd) < 0)
398 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
400 return mkfifo(pathname, mode);
407 -int dry_run, read_only, list_only, verbose;
413 int preserve_perms = 0;
420 /* These are to make syscall.o shut up. */
422 +int am_root = 0; /* TODO: add option to set this to -1. */
425 int preserve_perms = 0;
430 /* These are to make syscall.o shut up. */
435 int preserve_perms = 0;
440 extern int read_only;
441 extern int list_only;
443 extern unsigned int file_struct_len;
445 #define RSYNC_XAL_INITIAL 5
446 #define RSYNC_XAL_LIST_INITIAL 100
448 +#define FAKE_XATTR "user.rsync%stat"
453 @@ -132,9 +135,15 @@ static int rsync_xal_get(const char *fna
456 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
457 - rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
460 len = strlen(name) + 1;
461 + if (am_root < 0 && len == sizeof FAKE_XATTR
462 + && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
465 + rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
467 datum_size = sys_lgetxattr(fname, name, NULL, 0);
468 if (datum_size < 0) {
469 if (errno == ENOTSUP)
470 @@ -287,10 +296,19 @@ void receive_xattr(struct file_struct *f
471 out_of_memory("receive_xattr");
472 read_buf(f, ptr, name_len);
473 read_buf(f, ptr + name_len, datum_len);
475 + if (am_root < 0 && name_len == sizeof FAKE_XATTR
476 + && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
478 + temp_xattr.count--;
482 rxa->name_len = name_len;
483 rxa->datum_len = datum_len;
485 rxa->datum = ptr + name_len;
487 #ifdef HAVE_OSX_XATTRS
488 if (strncmp(rxa->name, UNIQUE_PREFIX, UPRE_LEN) == 0) {
489 rxa->name_len -= UPRE_LEN;
490 @@ -372,4 +390,146 @@ int set_xattr(const char *fname, const s
491 return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
494 +int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
496 + int mode, rdev_major, rdev_minor, uid, gid, len;
499 + if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
508 + len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
511 + len = sys_fgetxattr(fd, FAKE_XATTR, buf, sizeof buf - 1);
513 + if (len >= (int)sizeof buf) {
518 + if (errno == ENOTSUP || errno == ENOATTR)
520 + if (errno == EPERM && S_ISLNK(fst->st_mode)) {
525 + rsyserr(FERROR, errno, "failed to read xattr %s for %s",
526 + FAKE_XATTR, full_fname(fname));
531 + if (sscanf(buf, "%o %d,%d %d:%d",
532 + &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
533 + rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
534 + FAKE_XATTR, full_fname(fname), buf);
535 + exit_cleanup(RERR_FILEIO);
538 + xst->st_mode = from_wire_mode(mode);
539 + xst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
546 +int set_stat_xattr(const char *fname, struct file_struct *file)
548 + STRUCT_STAT fst, xst;
555 + if (read_only || list_only) {
556 + rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
557 + FAKE_XATTR, full_fname(fname));
561 + if (x_lstat(fname, &fst, &xst) < 0) {
562 + rsyserr(FERROR, errno, "failed to re-stat %s",
563 + full_fname(fname));
567 + if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
568 + rdev = file->u.rdev;
572 + /* Dump the special permissions and enable full owner access. */
573 + mode = (fst.st_mode & ~CHMOD_BITS) | (file->mode & ACCESSPERMS)
574 + | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
575 + if (fst.st_mode != mode)
576 + do_chmod(fname, mode);
577 + if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
578 + fst.st_rdev = 0; /* just in case */
580 + if (mode == file->mode && fst.st_rdev == rdev
581 + && fst.st_uid == file->uid && fst.st_gid == file->gid) {
582 + /* xst.st_mode will be 0 if there's no current stat xattr */
583 + if (xst.st_mode && sys_lremovexattr(fname, FAKE_XATTR) < 0) {
584 + rsyserr(FERROR, errno,
585 + "delete of stat xattr failed for %s",
586 + full_fname(fname));
592 + if (xst.st_mode != file->mode || xst.st_rdev != rdev
593 + || xst.st_uid != file->uid || xst.st_gid != file->gid) {
595 + int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
596 + to_wire_mode(file->mode) & (_S_IFMT|CHMOD_BITS),
597 + (int)major(rdev), (int)minor(rdev),
598 + (int)file->uid, (int)file->gid);
599 + if (sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0) < 0) {
600 + if (errno == EPERM && S_ISLNK(fst.st_mode))
602 + rsyserr(FERROR, errno,
603 + "failed to write xattr %s for %s",
604 + FAKE_XATTR, full_fname(fname));
612 +int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
614 + int ret = do_stat(fname, fst);
615 + if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
620 +int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
622 + int ret = do_lstat(fname, fst);
623 + if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
628 +int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
630 + int ret = do_fstat(fd, fst);
631 + if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
636 #endif /* SUPPORT_XATTRS */