Changed the unique_prefix and upre_len vars into defines.
[rsync/rsync-patches.git] / fake-super.diff
CommitLineData
c44efccb
WD
1Depends-On-Patch: acls.diff
2Depends-On-Patch: xattrs.diff
3
4This patch adds a new option: --fake-super, which tells rsync to copy in a
5fake super-user mode that stores various file attributes in an extended-
6attribute value instead of as real file-system attributes. The items
7affected are:
8
9 mode the real mode of a file is always (666 & umask) while
10 the real mode of a directory is always (777 & umask).
11
12 rdev devices and special files are created as zero-length
13 normal files.
14
15 uid the real owner is always left unchanged.
16
17 gid the real group is always left unchanged.
18
19A daemon can set "fake super = yes" in the rsync.conf file for any module
20that you'd like to run without root perms while pretending it has them (the
21client cannot affect this).
22
23The --fake-super option only affects the side where the option is used. To
24affect the remote side of a remote-shell connection, specify an rsync path:
25
26 rsync -av --rsync-path='rsync --fake-super' /src/ host:/dest/
27
28For a local copy where you want to affect only one side or the other,
29you'll need to turn the copy into a remote copy to localhost.
30
31After applying this patch, run these commands for a successful build:
32
33 ./prepare-source
1e883fdf
WD
34 ./configure --enable-xattr-support
35 make
36
37or, if you want ACL support too:
38
39 ./prepare-source
40 ./configure --enable-acl-support --enable-xattr-support
c44efccb
WD
41 make
42
e66b9b1f
WD
43--- old/backup.c
44+++ new/backup.c
45@@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath)
46 if (p >= rel) {
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",
53 full_fname(rel));
54@@ -200,7 +200,7 @@ static int keep_backup(char *fname)
55 int ret_code;
c44efccb 56
e66b9b1f
WD
57 /* return if no file to keep */
58- if (do_lstat(fname, &sx.st) < 0)
59+ if (x_lstat(fname, &sx.st, NULL) < 0)
60 return 1;
61 #ifdef SUPPORT_ACLS
62 sx.acc_acl = sx.def_acl = NULL;
c44efccb
WD
63--- old/clientserver.c
64+++ new/clientserver.c
611f9f3c
WD
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. */
c44efccb 68
611f9f3c 69+ if (lp_fake_super(i))
c44efccb 70+ am_root = -1;
611f9f3c
WD
71+ else if (am_root < 0) /* Treat --fake-super from client as --super. */
72+ am_root = 2;
c44efccb 73+
611f9f3c
WD
74 if (filesfrom_fd == 0)
75 filesfrom_fd = f_in;
76
e66b9b1f
WD
77--- old/flist.c
78+++ new/flist.c
79@@ -181,7 +181,7 @@ static int readlink_stat(const char *pat
80 }
81 return 0;
82 #else
83- return do_stat(path, stp);
84+ return x_stat(path, stp, NULL);
85 #endif
86 }
87
88@@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S
89 {
90 #ifdef SUPPORT_LINKS
91 if (copy_links)
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)
96 return -1;
97 if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
98 STRUCT_STAT st;
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))
101 *stp = st;
102 }
103 return 0;
104 #else
105- return do_stat(path, stp);
106+ return x_stat(path, stp, NULL);
107 #endif
108 }
109
110@@ -793,7 +793,7 @@ struct file_struct *make_file(char *fnam
111 if (save_errno == ENOENT) {
112 #ifdef SUPPORT_LINKS
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;
c44efccb
WD
128--- old/generator.c
129+++ new/generator.c
130@@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi
131 recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT,
132 code, f_out);
133
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. */
138 #ifdef HAVE_CHMOD
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
142 && dir_tweaking) {
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,
148--- old/loadparm.c
149+++ new/loadparm.c
150@@ -150,6 +150,7 @@ typedef struct
151 int syslog_facility;
152 int timeout;
153
154+ BOOL fake_super;
155 BOOL ignore_errors;
156 BOOL ignore_nonreadable;
157 BOOL list;
158@@ -197,6 +198,7 @@ static service sDefault =
159 /* syslog_facility; */ LOG_DAEMON,
160 /* timeout; */ 0,
161
162+ /* fake_super; */ False,
163 /* ignore_errors; */ False,
164 /* ignore_nonreadable; */ False,
165 /* list; */ True,
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)
177
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
183
184 if (pszFname)
185 pstrcpy(n2,pszFname);
186- else if (am_server && !am_root)
187+ else if (am_server && am_root <= 0)
188 pstrcpy(n2,RSYNCD_USERCONF);
189 else
190 pstrcpy(n2,RSYNCD_SYSCONF);
191--- old/options.c
192+++ new/options.c
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;
197-int am_root = 0;
198+int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */
199 int am_server = 0;
200 int am_sender = 0;
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 },
e66b9b1f
WD
218--- old/receiver.c
219+++ new/receiver.c
220@@ -528,7 +528,7 @@ int recv_files(int f_in, struct file_lis
221 if (fd1 == -1) {
222 st.st_mode = 0;
223 st.st_size = 0;
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);
c44efccb
WD
229--- old/rsync.c
230+++ new/rsync.c
35d8f76e
WD
231@@ -49,7 +49,6 @@ extern int preserve_gid;
232 extern int inplace;
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;
238
239@@ -197,7 +196,9 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
240 (long)sxp->st.st_gid, (long)file->gid);
241 }
242 }
3ae9c1ee
WD
243- if (do_lchown(fname,
244+ if (am_root < 0)
245+ ;
246+ else if (do_lchown(fname,
c44efccb
WD
247 change_uid ? file->uid : sxp->st.st_uid,
248 change_gid ? file->gid : sxp->st.st_gid) != 0) {
3ae9c1ee 249 /* shouldn't have attempted to change uid or gid
35d8f76e 250@@ -206,7 +207,7 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
251 change_uid ? "chown" : "chgrp",
252 full_fname(fname));
253 goto cleanup;
254- }
255+ } else
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 */
35d8f76e 259@@ -224,6 +225,24 @@ int set_file_attrs(char *fname, struct f
e66b9b1f
WD
260 if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
261 updated = 1;
c44efccb 262 #endif
e66b9b1f
WD
263+
264+ if (am_root < 0 && !S_ISLNK(file->mode)) {
3ae9c1ee
WD
265+ switch (set_stat_xattr(fname, file)) {
266+ case 0:
267+ break;
268+ case -1:
c44efccb
WD
269+ rsyserr(FERROR, errno,
270+ "write of stat xattr failed for %s",
271+ full_fname(fname));
3ae9c1ee
WD
272+ break;
273+ case -2:
274+ rsyserr(FERROR, errno,
275+ "delete of stat xattr failed for %s",
276+ full_fname(fname));
277+ break;
c44efccb
WD
278+ }
279+ }
280+
e66b9b1f
WD
281 #ifdef SUPPORT_ACLS
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.
35d8f76e 284@@ -237,7 +256,7 @@ int set_file_attrs(char *fname, struct f
c44efccb 285
e66b9b1f
WD
286 #ifdef HAVE_CHMOD
287 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
288- int ret = do_chmod(fname, new_mode);
9b91764c 289+ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
e66b9b1f
WD
290 if (ret < 0) {
291 rsyserr(FERROR, errno,
292 "failed to set permissions on %s",
c44efccb
WD
293--- old/syscall.c
294+++ new/syscall.c
e66b9b1f 295@@ -28,6 +28,7 @@
c44efccb
WD
296 #endif
297
298 extern int dry_run;
299+extern int am_root;
300 extern int read_only;
301 extern int list_only;
302 extern int preserve_perms;
e66b9b1f 303@@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode
c44efccb
WD
304 {
305 if (dry_run) return 0;
306 RETURN_ERROR_IF_RO_OR_LO;
307+
308+ /* For --fake-super, we create a normal file with mode 0600. */
309+ if (am_root < 0) {
310+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
311+ if (fd < 0 || close(fd) < 0)
312+ return -1;
313+ return 0;
314+ }
315+
316 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
317 if (S_ISFIFO(mode))
318 return mkfifo(pathname, mode);
c44efccb
WD
319--- old/t_unsafe.c
320+++ new/t_unsafe.c
321@@ -24,7 +24,11 @@
322
323 #include "rsync.h"
324
325-int dry_run, read_only, list_only, verbose;
326+int dry_run = 0;
327+int am_root = 0;
328+int read_only = 0;
329+int list_only = 0;
330+int verbose = 0;
331 int preserve_perms = 0;
332
333 int
334--- old/tls.c
335+++ new/tls.c
336@@ -39,6 +39,7 @@
337
338 /* These are to make syscall.o shut up. */
339 int dry_run = 0;
340+int am_root = 0; /* TODO: add option to set this to -1. */
341 int read_only = 1;
342 int list_only = 0;
343 int preserve_perms = 0;
344--- old/trimslash.c
345+++ new/trimslash.c
346@@ -23,6 +23,7 @@
347
348 /* These are to make syscall.o shut up. */
349 int dry_run = 0;
350+int am_root = 0;
351 int read_only = 1;
352 int list_only = 0;
353 int preserve_perms = 0;
354--- old/xattr.c
355+++ new/xattr.c
e66b9b1f 356@@ -26,11 +26,15 @@
c44efccb
WD
357 #ifdef SUPPORT_XATTRS
358
359 extern int dry_run;
360+extern int am_root;
e66b9b1f 361+extern mode_t orig_umask;
c44efccb
WD
362 extern unsigned int file_struct_len;
363
364 #define RSYNC_XAL_INITIAL 5
e66b9b1f
WD
365 #define RSYNC_XAL_LIST_INITIAL 100
366
367+#define FAKE_XATTR "user.rsync%stat"
368+
369 typedef struct {
370 char *name;
371 char *datum;
372@@ -130,9 +134,15 @@ static int rsync_xal_get(const char *fna
c44efccb
WD
373 if (name_size == 0)
374 return 0;
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);
377+ rsync_xa *rxas;
378
379 len = strlen(name) + 1;
380+ if (am_root < 0 && len == sizeof FAKE_XATTR
d30eff93 381+ && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
c44efccb
WD
382+ continue;
383+
384+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
385+
386 datum_size = sys_lgetxattr(fname, name, NULL, 0);
387 if (datum_size < 0) {
388 if (errno == ENOTSUP)
e66b9b1f 389@@ -285,10 +295,19 @@ void receive_xattr(struct file_struct *f
c44efccb
WD
390 out_of_memory("receive_xattr");
391 read_buf(f, ptr, name_len);
392 read_buf(f, ptr + name_len, datum_len);
393+
394+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
d30eff93 395+ && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
c44efccb
WD
396+ free(ptr);
397+ temp_xattr.count--;
398+ continue;
399+ }
400+
401 rxa->name_len = name_len;
402 rxa->datum_len = datum_len;
403 rxa->name = ptr;
404 rxa->datum = ptr + name_len;
405+
406 #ifdef HAVE_OSX_XATTRS
1e883fdf
WD
407 if (strncmp(rxa->name, UNIQUE_PREFIX, UPRE_LEN) == 0) {
408 rxa->name_len -= UPRE_LEN;
35d8f76e 409@@ -365,4 +384,103 @@ int set_xattr(const char *fname, const s
e66b9b1f
WD
410 return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
411 }
412
413+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *st)
414+{
415+ int mode, rdev_major, rdev_minor, uid, gid, len;
416+ char buf[256];
417+
418+ if (fname)
419+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
420+ else
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)
424+ return -1;
425+ return -1;
426+ }
427+ buf[len] = '\0';
428+
429+ if (sscanf(buf, "%o %d,%d %d:%d",
430+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
431+ errno = EINVAL;
432+ return -1;
433+ }
434+
435+ st->st_mode = mode;
436+ st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
437+ st->st_uid = uid;
438+ st->st_gid = gid;
439+
440+ return 0;
441+}
442+
443+int set_stat_xattr(const char *fname, struct file_struct *file)
444+{
445+ STRUCT_STAT fst, xst;
446+ dev_t rdev;
35d8f76e 447+ mode_t mode;
e66b9b1f
WD
448+
449+ if (dry_run)
450+ return 0;
451+
452+ if (x_stat(fname, &fst, &xst) < 0)
453+ return -1;
454+
455+ if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
456+ rdev = file->u.rdev;
457+ else
458+ rdev = 0;
459+
35d8f76e
WD
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);
e66b9b1f
WD
465+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
466+ fst.st_rdev = 0; /* just in case */
467+
35d8f76e 468+ if (mode == file->mode && fst.st_rdev == rdev
e66b9b1f
WD
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)
472+ return -2;
473+ return 0;
474+ }
475+
476+ if (xst.st_mode != file->mode || xst.st_rdev != rdev
477+ || xst.st_uid != file->uid || xst.st_gid != file->gid) {
478+ char buf[256];
479+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
480+ (int)file->mode,
481+ (int)major(rdev), (int)minor(rdev),
482+ (int)file->uid, (int)file->gid);
483+ return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
484+ }
485+ return 0;
486+}
487+
488+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
489+{
490+ int ret = do_stat(fname, fst);
491+ if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
492+ xst->st_mode = 0;
493+ return ret;
494+}
495+
496+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
497+{
498+ int ret = do_lstat(fname, fst);
499+ if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
500+ xst->st_mode = 0;
501+ return ret;
502+}
503+
504+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
505+{
506+ int ret = do_fstat(fd, fst);
507+ if (get_stat_xattr(NULL, fd, xst? xst : fst) != 0 && xst)
508+ xst->st_mode = 0;
509+ return ret;
510+}
511+
512 #endif /* SUPPORT_XATTRS */