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. The items
9 mode the real mode of a file is always (666 & umask) while
10 the real mode of a directory is always (777 & umask).
12 rdev devices and special files are created as zero-length
15 uid the real owner is always left unchanged.
17 gid the real group is always left unchanged.
19 A daemon can set "fake super = yes" in the rsync.conf file for any module
20 that you'd like to run without root perms while pretending it has them (the
21 client cannot affect this).
23 The --fake-super option only affects the side where the option is used. To
24 affect the remote side of a remote-shell connection, specify an rsync path:
26 rsync -av --rsync-path='rsync --fake-super' /src/ host:/dest/
28 For a local copy where you want to affect only one side or the other,
29 you'll need to turn the copy into a remote copy to localhost.
31 After applying this patch, run these commands for a successful build:
39 @@ -41,7 +41,7 @@ popt_OBJS=popt/findme.o popt/popt.o po
40 popt/popthelp.o popt/poptparse.o
41 OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
43 -TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o
44 +TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattr.o
46 # Programs we must have to run the test cases
47 CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
48 @@ -83,11 +83,11 @@ getgroups$(EXEEXT): getgroups.o
49 getfsdev$(EXEEXT): getfsdev.o
50 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
52 -TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o
53 +TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o lib/sysxattr.o
54 trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
55 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
57 -T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o lib/snprintf.o
58 +T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o lib/snprintf.o lib/sysxattr.o
59 t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
60 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
62 --- old/clientserver.c
63 +++ new/clientserver.c
64 @@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_
65 ret = parse_arguments(&argc, (const char ***) &argv, 0);
66 quiet = 0; /* Don't let someone try to be tricky. */
68 + if (lp_fake_super(i))
70 + else if (am_root < 0) /* Treat --fake-super from client as --super. */
73 if (filesfrom_fd == 0)
78 @@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi
79 recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT,
82 - /* We need to ensure that any dirs we create have writeable
83 + /* We need to ensure that any dirs we create have rwx
84 * permissions during the time we are putting files within
85 * them. This is then fixed after the transfer is done. */
87 - if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR)
88 + if (am_root <= 0 && S_ISDIR(file->mode)
89 + && (file->mode & S_IRWXU) != S_IRWXU
91 - mode_t mode = file->mode | S_IWUSR; /* user write */
92 + mode_t mode = file->mode | S_IRWXU; /* user rwx */
93 char *fname = local_name ? local_name : fbuf;
94 if (do_chmod(fname, mode) < 0) {
95 rsyserr(FERROR, errno,
98 @@ -150,6 +150,7 @@ typedef struct
104 BOOL ignore_nonreadable;
106 @@ -197,6 +198,7 @@ static service sDefault =
107 /* syslog_facility; */ LOG_DAEMON,
110 + /* fake_super; */ False,
111 /* ignore_errors; */ False,
112 /* ignore_nonreadable; */ False,
114 @@ -298,6 +300,7 @@ static struct parm_struct parm_table[] =
115 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
116 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
117 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
118 + {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
119 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
120 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
121 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
122 @@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
123 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
124 FN_LOCAL_INTEGER(lp_timeout, timeout)
126 +FN_LOCAL_BOOL(lp_fake_super, fake_super)
127 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
128 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
129 FN_LOCAL_BOOL(lp_list, list)
130 @@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals
133 pstrcpy(n2,pszFname);
134 - else if (am_server && !am_root)
135 + else if (am_server && am_root <= 0)
136 pstrcpy(n2,RSYNCD_USERCONF);
138 pstrcpy(n2,RSYNCD_SYSCONF);
141 @@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION;
142 int sparse_files = 0;
143 int do_compression = 0;
144 int def_compress_level = Z_DEFAULT_COMPRESSION;
146 +int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */
149 int am_generator = 0;
150 @@ -330,6 +330,7 @@ void usage(enum logcode F)
151 rprintf(F," -t, --times preserve times\n");
152 rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
153 rprintf(F," --super receiver attempts super-user activities\n");
154 + rprintf(F," --fake-super fake root by storing/reading ownership/etc in EAs\n");
155 rprintf(F," -S, --sparse handle sparse files efficiently\n");
156 rprintf(F," -n, --dry-run show what would have been transferred\n");
157 rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
158 @@ -454,6 +455,7 @@ static struct poptOption long_options[]
159 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
160 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
161 {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
162 + {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
163 {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
164 {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
165 {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
168 @@ -197,7 +197,9 @@ int set_file_attrs(char *fname, struct f
169 (long)sxp->st.st_gid, (long)file->gid);
172 - if (do_lchown(fname,
175 + else if (do_lchown(fname,
176 change_uid ? file->uid : sxp->st.st_uid,
177 change_gid ? file->gid : sxp->st.st_gid) != 0) {
178 /* shouldn't have attempted to change uid or gid
179 @@ -206,7 +208,7 @@ int set_file_attrs(char *fname, struct f
180 change_uid ? "chown" : "chgrp",
185 /* a lchown had been done - we have to re-stat if the
186 * destination had the setuid or setgid bits set due
187 * to the side effect of the chown call */
188 @@ -237,7 +239,15 @@ int set_file_attrs(char *fname, struct f
191 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
192 - int ret = do_chmod(fname, new_mode);
195 + mode_t mode = 0666 & ~orig_umask;
196 + if ((sxp->st.st_mode & CHMOD_BITS) != mode)
197 + ret = do_chmod(fname, mode);
201 + ret = do_chmod(fname, new_mode);
203 rsyserr(FERROR, errno,
204 "failed to set permissions on %s",
205 @@ -249,6 +259,23 @@ int set_file_attrs(char *fname, struct f
210 + switch (set_stat_xattr(fname, file)) {
214 + rsyserr(FERROR, errno,
215 + "write of stat xattr failed for %s",
216 + full_fname(fname));
219 + rsyserr(FERROR, errno,
220 + "delete of stat xattr failed for %s",
221 + full_fname(fname));
226 if (verbose > 1 && flags & ATTRS_REPORT) {
228 rprintf(FCLIENT, "%s\n", fname);
233 #define BACKUP_SUFFIX "~"
235 +#define FAKE_XATTR "user.rsync%stat"
237 /* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
238 incompatible with older versions :-( */
239 #define CHAR_OFFSET 0
246 +#include "lib/sysxattr.h"
248 #if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H
254 extern int read_only;
255 extern int list_only;
256 extern int preserve_perms;
257 @@ -79,6 +81,15 @@ int do_mknod(char *pathname, mode_t mode
259 if (dry_run) return 0;
260 RETURN_ERROR_IF_RO_OR_LO;
262 + /* For --fake-super, we create a normal file with mode 0600. */
264 + int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
265 + if (fd < 0 || close(fd) < 0)
270 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
272 return mkfifo(pathname, mode);
273 @@ -215,23 +226,98 @@ int do_mkstemp(char *template, mode_t pe
277 +int get_stat_xattr(const char *fname, STRUCT_STAT *st)
279 + int mode, rdev_major, rdev_minor, uid, gid, len;
282 + len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
283 + if (len < 0 || len >= (int)sizeof buf) {
284 + if (errno == ENOTSUP || errno == ENOATTR)
290 + if (sscanf(buf, "%o %d,%d %d:%d",
291 + &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
296 + st->st_mode = mode;
297 + st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
304 +int set_stat_xattr(const char *fname, struct file_struct *file)
306 + STRUCT_STAT fst, xst;
309 + if (dry_run) return 0;
310 + RETURN_ERROR_IF_RO_OR_LO;
312 + am_root = 2; /* get real stat() w/o xattr overlay */
313 + do_stat(fname, &fst);
315 + have_xattr = get_stat_xattr(fname, &xst) == 0;
317 + if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
318 + rdev = file->u.rdev;
321 + if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
322 + fst.st_rdev = 0; /* just in case */
324 + if (fst.st_mode == file->mode && fst.st_rdev == rdev
325 + && fst.st_uid == file->uid && fst.st_gid == file->gid) {
326 + if (have_xattr && sys_lremovexattr(fname, FAKE_XATTR) < 0)
332 + || xst.st_mode != file->mode || xst.st_rdev != rdev
333 + || xst.st_uid != file->uid || xst.st_gid != file->gid) {
335 + int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
337 + (int)major(rdev), (int)minor(rdev),
338 + (int)file->uid, (int)file->gid);
339 + return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
344 int do_stat(const char *fname, STRUCT_STAT *st)
347 #ifdef USE_STAT64_FUNCS
348 - return stat64(fname, st);
349 + ret = stat64(fname, st);
351 - return stat(fname, st);
352 + ret = stat(fname, st);
354 + if (am_root < 0 && ret == 0)
355 + get_stat_xattr(fname, st);
359 int do_lstat(const char *fname, STRUCT_STAT *st)
363 # ifdef USE_STAT64_FUNCS
364 - return lstat64(fname, st);
365 + ret = lstat64(fname, st);
367 - return lstat(fname, st);
368 + ret = lstat(fname, st);
370 + if (am_root < 0 && ret == 0)
371 + get_stat_xattr(fname, st);
374 return do_stat(fname, st);
382 -int dry_run, read_only, list_only, verbose;
388 int preserve_perms = 0;
395 /* These are to make syscall.o shut up. */
397 +int am_root = 0; /* TODO: add option to set this to -1. */
400 int preserve_perms = 0;
405 /* These are to make syscall.o shut up. */
410 int preserve_perms = 0;
414 #ifdef SUPPORT_XATTRS
418 extern unsigned int file_struct_len;
420 #define RSYNC_XAL_INITIAL 5
421 @@ -130,9 +131,15 @@ static int rsync_xal_get(const char *fna
424 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
425 - rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
428 len = strlen(name) + 1;
429 + if (am_root < 0 && len == sizeof FAKE_XATTR
430 + && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
433 + rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
435 datum_size = sys_lgetxattr(fname, name, NULL, 0);
436 if (datum_size < 0) {
437 if (errno == ENOTSUP)
438 @@ -285,10 +292,19 @@ void receive_xattr(struct file_struct *f
439 out_of_memory("receive_xattr");
440 read_buf(f, ptr, name_len);
441 read_buf(f, ptr + name_len, datum_len);
443 + if (am_root < 0 && name_len == sizeof FAKE_XATTR
444 + && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
446 + temp_xattr.count--;
450 rxa->name_len = name_len;
451 rxa->datum_len = datum_len;
453 rxa->datum = ptr + name_len;
455 #ifdef HAVE_OSX_XATTRS
456 if (strncmp(rxa->name, unique_prefix, upre_len) == 0) {
457 rxa->name_len -= upre_len;