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:
34 ./configure --enable-xattr-support
37 or, if you want ACL support too:
40 ./configure --enable-acl-support --enable-xattr-support
45 @@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath)
47 /* Try to transfer the directory settings of the
48 * actual dir that the files are coming from. */
49 - if (do_stat(rel, &sx.st) < 0) {
50 + if (x_stat(rel, &sx.st, NULL) < 0) {
51 rsyserr(FERROR, errno,
52 "make_bak_dir stat %s failed",
54 @@ -200,7 +200,7 @@ static int keep_backup(char *fname)
57 /* return if no file to keep */
58 - if (do_lstat(fname, &sx.st) < 0)
59 + if (x_lstat(fname, &sx.st, NULL) < 0)
62 sx.acc_acl = sx.def_acl = NULL;
63 --- old/clientserver.c
64 +++ new/clientserver.c
65 @@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_
66 ret = parse_arguments(&argc, (const char ***) &argv, 0);
67 quiet = 0; /* Don't let someone try to be tricky. */
69 + if (lp_fake_super(i))
71 + else if (am_root < 0) /* Treat --fake-super from client as --super. */
74 if (filesfrom_fd == 0)
79 @@ -181,7 +181,7 @@ static int readlink_stat(const char *pat
83 - return do_stat(path, stp);
84 + return x_stat(path, stp, NULL);
88 @@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S
92 - return do_stat(path, stp);
93 - if (do_lstat(path, stp) < 0)
94 + return x_stat(path, stp, NULL);
95 + if (x_lstat(path, stp, NULL) < 0)
97 if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
99 - if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
100 + if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
105 - return do_stat(path, stp);
106 + return x_stat(path, stp, NULL);
110 @@ -793,7 +793,7 @@ struct file_struct *make_file(char *fnam
111 if (save_errno == ENOENT) {
113 /* Avoid "vanished" error if symlink points nowhere. */
114 - if (copy_links && do_lstat(thisname, &st) == 0
115 + if (copy_links && x_lstat(thisname, &st, NULL) == 0
116 && S_ISLNK(st.st_mode)) {
117 io_error |= IOERR_GENERAL;
118 rprintf(FERROR, "symlink has no referent: %s\n",
119 @@ -963,7 +963,7 @@ struct file_struct *make_file(char *fnam
120 int save_mode = file->mode;
121 file->mode = S_IFDIR; /* Find a directory with our name. */
122 if (flist_find(the_file_list, file) >= 0
123 - && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
124 + && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
125 file->modtime = st2.st_mtime;
126 file->length = st2.st_size;
127 file->mode = st2.st_mode;
130 @@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi
131 recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT,
134 - /* We need to ensure that any dirs we create have writeable
135 + /* We need to ensure that any dirs we create have rwx
136 * permissions during the time we are putting files within
137 * them. This is then fixed after the transfer is done. */
139 - if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR)
140 + if (am_root <= 0 && S_ISDIR(file->mode)
141 + && (file->mode & S_IRWXU) != S_IRWXU
143 - mode_t mode = file->mode | S_IWUSR; /* user write */
144 + mode_t mode = file->mode | S_IRWXU; /* user rwx */
145 char *fname = local_name ? local_name : fbuf;
146 if (do_chmod(fname, mode) < 0) {
147 rsyserr(FERROR, errno,
150 @@ -150,6 +150,7 @@ typedef struct
156 BOOL ignore_nonreadable;
158 @@ -197,6 +198,7 @@ static service sDefault =
159 /* syslog_facility; */ LOG_DAEMON,
162 + /* fake_super; */ False,
163 /* ignore_errors; */ False,
164 /* ignore_nonreadable; */ False,
166 @@ -298,6 +300,7 @@ static struct parm_struct parm_table[] =
167 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
168 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
169 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
170 + {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
171 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
172 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
173 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
174 @@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
175 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
176 FN_LOCAL_INTEGER(lp_timeout, timeout)
178 +FN_LOCAL_BOOL(lp_fake_super, fake_super)
179 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
180 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
181 FN_LOCAL_BOOL(lp_list, list)
182 @@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals
185 pstrcpy(n2,pszFname);
186 - else if (am_server && !am_root)
187 + else if (am_server && am_root <= 0)
188 pstrcpy(n2,RSYNCD_USERCONF);
190 pstrcpy(n2,RSYNCD_SYSCONF);
193 @@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION;
194 int sparse_files = 0;
195 int do_compression = 0;
196 int def_compress_level = Z_DEFAULT_COMPRESSION;
198 +int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */
201 int am_generator = 0;
202 @@ -330,6 +330,7 @@ void usage(enum logcode F)
203 rprintf(F," -t, --times preserve times\n");
204 rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
205 rprintf(F," --super receiver attempts super-user activities\n");
206 + rprintf(F," --fake-super fake root by storing/reading ownership/etc in EAs\n");
207 rprintf(F," -S, --sparse handle sparse files efficiently\n");
208 rprintf(F," -n, --dry-run show what would have been transferred\n");
209 rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
210 @@ -454,6 +455,7 @@ static struct poptOption long_options[]
211 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
212 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
213 {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
214 + {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
215 {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
216 {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
217 {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
220 @@ -528,7 +528,7 @@ int recv_files(int f_in, struct file_lis
224 - } else if (do_fstat(fd1,&st) != 0) {
225 + } else if (x_fstat(fd1, &st, NULL) != 0) {
226 rsyserr(FERROR, errno, "fstat %s failed",
227 full_fname(fnamecmp));
228 discard_receive_data(f_in, file->length);
231 @@ -49,7 +49,6 @@ extern int preserve_gid;
233 extern int keep_dirlinks;
234 extern int make_backups;
235 -extern mode_t orig_umask;
236 extern struct stats stats;
237 extern struct chmod_mode_struct *daemon_chmod_modes;
239 @@ -197,7 +196,9 @@ int set_file_attrs(char *fname, struct f
240 (long)sxp->st.st_gid, (long)file->gid);
243 - if (do_lchown(fname,
246 + else if (do_lchown(fname,
247 change_uid ? file->uid : sxp->st.st_uid,
248 change_gid ? file->gid : sxp->st.st_gid) != 0) {
249 /* shouldn't have attempted to change uid or gid
250 @@ -206,7 +207,7 @@ int set_file_attrs(char *fname, struct f
251 change_uid ? "chown" : "chgrp",
256 /* a lchown had been done - we have to re-stat if the
257 * destination had the setuid or setgid bits set due
258 * to the side effect of the chown call */
259 @@ -224,6 +225,24 @@ int set_file_attrs(char *fname, struct f
260 if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
264 + if (am_root < 0 && !S_ISLNK(file->mode)) {
265 + switch (set_stat_xattr(fname, file)) {
269 + rsyserr(FERROR, errno,
270 + "write of stat xattr failed for %s",
271 + full_fname(fname));
274 + rsyserr(FERROR, errno,
275 + "delete of stat xattr failed for %s",
276 + full_fname(fname));
282 /* It's OK to call set_acl() now, even for a dir, as the generator
283 * will enable owner-writability using chmod, if necessary.
284 @@ -237,7 +256,7 @@ int set_file_attrs(char *fname, struct f
287 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
288 - int ret = do_chmod(fname, new_mode);
289 + int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
291 rsyserr(FERROR, errno,
292 "failed to set permissions on %s",
300 extern int read_only;
301 extern int list_only;
302 extern int preserve_perms;
303 @@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode
305 if (dry_run) return 0;
306 RETURN_ERROR_IF_RO_OR_LO;
308 + /* For --fake-super, we create a normal file with mode 0600. */
310 + int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
311 + if (fd < 0 || close(fd) < 0)
316 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
318 return mkfifo(pathname, mode);
325 -int dry_run, read_only, list_only, verbose;
331 int preserve_perms = 0;
338 /* These are to make syscall.o shut up. */
340 +int am_root = 0; /* TODO: add option to set this to -1. */
343 int preserve_perms = 0;
348 /* These are to make syscall.o shut up. */
353 int preserve_perms = 0;
357 #ifdef SUPPORT_XATTRS
361 +extern mode_t orig_umask;
362 extern unsigned int file_struct_len;
364 #define RSYNC_XAL_INITIAL 5
365 #define RSYNC_XAL_LIST_INITIAL 100
367 +#define FAKE_XATTR "user.rsync%stat"
372 @@ -130,9 +134,15 @@ static int rsync_xal_get(const char *fna
375 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
376 - rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
379 len = strlen(name) + 1;
380 + if (am_root < 0 && len == sizeof FAKE_XATTR
381 + && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
384 + rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
386 datum_size = sys_lgetxattr(fname, name, NULL, 0);
387 if (datum_size < 0) {
388 if (errno == ENOTSUP)
389 @@ -285,10 +295,19 @@ void receive_xattr(struct file_struct *f
390 out_of_memory("receive_xattr");
391 read_buf(f, ptr, name_len);
392 read_buf(f, ptr + name_len, datum_len);
394 + if (am_root < 0 && name_len == sizeof FAKE_XATTR
395 + && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
397 + temp_xattr.count--;
401 rxa->name_len = name_len;
402 rxa->datum_len = datum_len;
404 rxa->datum = ptr + name_len;
406 #ifdef HAVE_OSX_XATTRS
407 if (strncmp(rxa->name, UNIQUE_PREFIX, UPRE_LEN) == 0) {
408 rxa->name_len -= UPRE_LEN;
409 @@ -365,4 +384,103 @@ int set_xattr(const char *fname, const s
410 return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
413 +int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *st)
415 + int mode, rdev_major, rdev_minor, uid, gid, len;
419 + len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
421 + len = sys_fgetxattr(fd, FAKE_XATTR, buf, sizeof buf - 1);
422 + if (len < 0 || len >= (int)sizeof buf) {
423 + if (errno == ENOTSUP || errno == ENOATTR)
429 + if (sscanf(buf, "%o %d,%d %d:%d",
430 + &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
435 + st->st_mode = mode;
436 + st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
443 +int set_stat_xattr(const char *fname, struct file_struct *file)
445 + STRUCT_STAT fst, xst;
452 + if (x_stat(fname, &fst, &xst) < 0)
455 + if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
456 + rdev = file->u.rdev;
460 + /* Force the real file's mode to our liking. */
461 + mode = (fst.st_mode & ~CHMOD_BITS)
462 + | ((S_ISDIR(fst.st_mode) ? 0777 : 0666) & (~orig_umask | S_IRWXU));
463 + if (fst.st_mode != mode)
464 + do_chmod(fname, mode);
465 + if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
466 + fst.st_rdev = 0; /* just in case */
468 + if (mode == file->mode && fst.st_rdev == rdev
469 + && fst.st_uid == file->uid && fst.st_gid == file->gid) {
470 + /* xst.st_mode will be 0 if there's no current stat xattr */
471 + if (xst.st_mode && sys_lremovexattr(fname, FAKE_XATTR) < 0)
476 + if (xst.st_mode != file->mode || xst.st_rdev != rdev
477 + || xst.st_uid != file->uid || xst.st_gid != file->gid) {
479 + int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
481 + (int)major(rdev), (int)minor(rdev),
482 + (int)file->uid, (int)file->gid);
483 + return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
488 +int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
490 + int ret = do_stat(fname, fst);
491 + if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
496 +int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
498 + int ret = do_lstat(fname, fst);
499 + if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
504 +int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
506 + int ret = do_fstat(fd, fst);
507 + if (get_stat_xattr(NULL, fd, xst? xst : fst) != 0 && xst)
512 #endif /* SUPPORT_XATTRS */