One more fix to get the consistent-chmod value right.
[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
34 ./configure
35 make
36
e66b9b1f
WD
37--- old/backup.c
38+++ new/backup.c
39@@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath)
40 if (p >= rel) {
41 /* Try to transfer the directory settings of the
42 * actual dir that the files are coming from. */
43- if (do_stat(rel, &sx.st) < 0) {
44+ if (x_stat(rel, &sx.st, NULL) < 0) {
45 rsyserr(FERROR, errno,
46 "make_bak_dir stat %s failed",
47 full_fname(rel));
48@@ -200,7 +200,7 @@ static int keep_backup(char *fname)
49 int ret_code;
c44efccb 50
e66b9b1f
WD
51 /* return if no file to keep */
52- if (do_lstat(fname, &sx.st) < 0)
53+ if (x_lstat(fname, &sx.st, NULL) < 0)
54 return 1;
55 #ifdef SUPPORT_ACLS
56 sx.acc_acl = sx.def_acl = NULL;
c44efccb
WD
57--- old/clientserver.c
58+++ new/clientserver.c
611f9f3c
WD
59@@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_
60 ret = parse_arguments(&argc, (const char ***) &argv, 0);
61 quiet = 0; /* Don't let someone try to be tricky. */
c44efccb 62
611f9f3c 63+ if (lp_fake_super(i))
c44efccb 64+ am_root = -1;
611f9f3c
WD
65+ else if (am_root < 0) /* Treat --fake-super from client as --super. */
66+ am_root = 2;
c44efccb 67+
611f9f3c
WD
68 if (filesfrom_fd == 0)
69 filesfrom_fd = f_in;
70
e66b9b1f
WD
71--- old/flist.c
72+++ new/flist.c
73@@ -181,7 +181,7 @@ static int readlink_stat(const char *pat
74 }
75 return 0;
76 #else
77- return do_stat(path, stp);
78+ return x_stat(path, stp, NULL);
79 #endif
80 }
81
82@@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S
83 {
84 #ifdef SUPPORT_LINKS
85 if (copy_links)
86- return do_stat(path, stp);
87- if (do_lstat(path, stp) < 0)
88+ return x_stat(path, stp, NULL);
89+ if (x_lstat(path, stp, NULL) < 0)
90 return -1;
91 if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
92 STRUCT_STAT st;
93- if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
94+ if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
95 *stp = st;
96 }
97 return 0;
98 #else
99- return do_stat(path, stp);
100+ return x_stat(path, stp, NULL);
101 #endif
102 }
103
104@@ -793,7 +793,7 @@ struct file_struct *make_file(char *fnam
105 if (save_errno == ENOENT) {
106 #ifdef SUPPORT_LINKS
107 /* Avoid "vanished" error if symlink points nowhere. */
108- if (copy_links && do_lstat(thisname, &st) == 0
109+ if (copy_links && x_lstat(thisname, &st, NULL) == 0
110 && S_ISLNK(st.st_mode)) {
111 io_error |= IOERR_GENERAL;
112 rprintf(FERROR, "symlink has no referent: %s\n",
113@@ -963,7 +963,7 @@ struct file_struct *make_file(char *fnam
114 int save_mode = file->mode;
115 file->mode = S_IFDIR; /* Find a directory with our name. */
116 if (flist_find(the_file_list, file) >= 0
117- && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
118+ && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
119 file->modtime = st2.st_mtime;
120 file->length = st2.st_size;
121 file->mode = st2.st_mode;
c44efccb
WD
122--- old/generator.c
123+++ new/generator.c
124@@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi
125 recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT,
126 code, f_out);
127
128- /* We need to ensure that any dirs we create have writeable
129+ /* We need to ensure that any dirs we create have rwx
130 * permissions during the time we are putting files within
131 * them. This is then fixed after the transfer is done. */
132 #ifdef HAVE_CHMOD
133- if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR)
134+ if (am_root <= 0 && S_ISDIR(file->mode)
135+ && (file->mode & S_IRWXU) != S_IRWXU
136 && dir_tweaking) {
137- mode_t mode = file->mode | S_IWUSR; /* user write */
138+ mode_t mode = file->mode | S_IRWXU; /* user rwx */
139 char *fname = local_name ? local_name : fbuf;
140 if (do_chmod(fname, mode) < 0) {
141 rsyserr(FERROR, errno,
142--- old/loadparm.c
143+++ new/loadparm.c
144@@ -150,6 +150,7 @@ typedef struct
145 int syslog_facility;
146 int timeout;
147
148+ BOOL fake_super;
149 BOOL ignore_errors;
150 BOOL ignore_nonreadable;
151 BOOL list;
152@@ -197,6 +198,7 @@ static service sDefault =
153 /* syslog_facility; */ LOG_DAEMON,
154 /* timeout; */ 0,
155
156+ /* fake_super; */ False,
157 /* ignore_errors; */ False,
158 /* ignore_nonreadable; */ False,
159 /* list; */ True,
160@@ -298,6 +300,7 @@ static struct parm_struct parm_table[] =
161 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
162 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
163 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
164+ {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
165 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
166 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
167 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
168@@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
169 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
170 FN_LOCAL_INTEGER(lp_timeout, timeout)
171
172+FN_LOCAL_BOOL(lp_fake_super, fake_super)
173 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
174 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
175 FN_LOCAL_BOOL(lp_list, list)
176@@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals
177
178 if (pszFname)
179 pstrcpy(n2,pszFname);
180- else if (am_server && !am_root)
181+ else if (am_server && am_root <= 0)
182 pstrcpy(n2,RSYNCD_USERCONF);
183 else
184 pstrcpy(n2,RSYNCD_SYSCONF);
185--- old/options.c
186+++ new/options.c
187@@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION;
188 int sparse_files = 0;
189 int do_compression = 0;
190 int def_compress_level = Z_DEFAULT_COMPRESSION;
191-int am_root = 0;
192+int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */
193 int am_server = 0;
194 int am_sender = 0;
195 int am_generator = 0;
196@@ -330,6 +330,7 @@ void usage(enum logcode F)
197 rprintf(F," -t, --times preserve times\n");
198 rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
199 rprintf(F," --super receiver attempts super-user activities\n");
200+ rprintf(F," --fake-super fake root by storing/reading ownership/etc in EAs\n");
201 rprintf(F," -S, --sparse handle sparse files efficiently\n");
202 rprintf(F," -n, --dry-run show what would have been transferred\n");
203 rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
204@@ -454,6 +455,7 @@ static struct poptOption long_options[]
205 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
206 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
207 {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
208+ {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
209 {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
210 {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
211 {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
e66b9b1f
WD
212--- old/receiver.c
213+++ new/receiver.c
214@@ -528,7 +528,7 @@ int recv_files(int f_in, struct file_lis
215 if (fd1 == -1) {
216 st.st_mode = 0;
217 st.st_size = 0;
218- } else if (do_fstat(fd1,&st) != 0) {
219+ } else if (x_fstat(fd1, &st, NULL) != 0) {
220 rsyserr(FERROR, errno, "fstat %s failed",
221 full_fname(fnamecmp));
222 discard_receive_data(f_in, file->length);
c44efccb
WD
223--- old/rsync.c
224+++ new/rsync.c
35d8f76e
WD
225@@ -49,7 +49,6 @@ extern int preserve_gid;
226 extern int inplace;
227 extern int keep_dirlinks;
228 extern int make_backups;
229-extern mode_t orig_umask;
230 extern struct stats stats;
231 extern struct chmod_mode_struct *daemon_chmod_modes;
232
233@@ -197,7 +196,9 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
234 (long)sxp->st.st_gid, (long)file->gid);
235 }
236 }
3ae9c1ee
WD
237- if (do_lchown(fname,
238+ if (am_root < 0)
239+ ;
240+ else if (do_lchown(fname,
c44efccb
WD
241 change_uid ? file->uid : sxp->st.st_uid,
242 change_gid ? file->gid : sxp->st.st_gid) != 0) {
3ae9c1ee 243 /* shouldn't have attempted to change uid or gid
35d8f76e 244@@ -206,7 +207,7 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
245 change_uid ? "chown" : "chgrp",
246 full_fname(fname));
247 goto cleanup;
248- }
249+ } else
250 /* a lchown had been done - we have to re-stat if the
251 * destination had the setuid or setgid bits set due
252 * to the side effect of the chown call */
35d8f76e 253@@ -224,6 +225,24 @@ int set_file_attrs(char *fname, struct f
e66b9b1f
WD
254 if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
255 updated = 1;
c44efccb 256 #endif
e66b9b1f
WD
257+
258+ if (am_root < 0 && !S_ISLNK(file->mode)) {
3ae9c1ee
WD
259+ switch (set_stat_xattr(fname, file)) {
260+ case 0:
261+ break;
262+ case -1:
c44efccb
WD
263+ rsyserr(FERROR, errno,
264+ "write of stat xattr failed for %s",
265+ full_fname(fname));
3ae9c1ee
WD
266+ break;
267+ case -2:
268+ rsyserr(FERROR, errno,
269+ "delete of stat xattr failed for %s",
270+ full_fname(fname));
271+ break;
c44efccb
WD
272+ }
273+ }
274+
e66b9b1f
WD
275 #ifdef SUPPORT_ACLS
276 /* It's OK to call set_acl() now, even for a dir, as the generator
277 * will enable owner-writability using chmod, if necessary.
35d8f76e 278@@ -237,7 +256,7 @@ int set_file_attrs(char *fname, struct f
c44efccb 279
e66b9b1f
WD
280 #ifdef HAVE_CHMOD
281 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
282- int ret = do_chmod(fname, new_mode);
9b91764c 283+ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
e66b9b1f
WD
284 if (ret < 0) {
285 rsyserr(FERROR, errno,
286 "failed to set permissions on %s",
c44efccb
WD
287--- old/syscall.c
288+++ new/syscall.c
e66b9b1f 289@@ -28,6 +28,7 @@
c44efccb
WD
290 #endif
291
292 extern int dry_run;
293+extern int am_root;
294 extern int read_only;
295 extern int list_only;
296 extern int preserve_perms;
e66b9b1f 297@@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode
c44efccb
WD
298 {
299 if (dry_run) return 0;
300 RETURN_ERROR_IF_RO_OR_LO;
301+
302+ /* For --fake-super, we create a normal file with mode 0600. */
303+ if (am_root < 0) {
304+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
305+ if (fd < 0 || close(fd) < 0)
306+ return -1;
307+ return 0;
308+ }
309+
310 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
311 if (S_ISFIFO(mode))
312 return mkfifo(pathname, mode);
c44efccb
WD
313--- old/t_unsafe.c
314+++ new/t_unsafe.c
315@@ -24,7 +24,11 @@
316
317 #include "rsync.h"
318
319-int dry_run, read_only, list_only, verbose;
320+int dry_run = 0;
321+int am_root = 0;
322+int read_only = 0;
323+int list_only = 0;
324+int verbose = 0;
325 int preserve_perms = 0;
326
327 int
328--- old/tls.c
329+++ new/tls.c
330@@ -39,6 +39,7 @@
331
332 /* These are to make syscall.o shut up. */
333 int dry_run = 0;
334+int am_root = 0; /* TODO: add option to set this to -1. */
335 int read_only = 1;
336 int list_only = 0;
337 int preserve_perms = 0;
338--- old/trimslash.c
339+++ new/trimslash.c
340@@ -23,6 +23,7 @@
341
342 /* These are to make syscall.o shut up. */
343 int dry_run = 0;
344+int am_root = 0;
345 int read_only = 1;
346 int list_only = 0;
347 int preserve_perms = 0;
348--- old/xattr.c
349+++ new/xattr.c
e66b9b1f 350@@ -26,11 +26,15 @@
c44efccb
WD
351 #ifdef SUPPORT_XATTRS
352
353 extern int dry_run;
354+extern int am_root;
e66b9b1f 355+extern mode_t orig_umask;
c44efccb
WD
356 extern unsigned int file_struct_len;
357
358 #define RSYNC_XAL_INITIAL 5
e66b9b1f
WD
359 #define RSYNC_XAL_LIST_INITIAL 100
360
361+#define FAKE_XATTR "user.rsync%stat"
362+
363 typedef struct {
364 char *name;
365 char *datum;
366@@ -130,9 +134,15 @@ static int rsync_xal_get(const char *fna
c44efccb
WD
367 if (name_size == 0)
368 return 0;
369 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
370- rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
371+ rsync_xa *rxas;
372
373 len = strlen(name) + 1;
374+ if (am_root < 0 && len == sizeof FAKE_XATTR
d30eff93 375+ && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
c44efccb
WD
376+ continue;
377+
378+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
379+
380 datum_size = sys_lgetxattr(fname, name, NULL, 0);
381 if (datum_size < 0) {
382 if (errno == ENOTSUP)
e66b9b1f 383@@ -285,10 +295,19 @@ void receive_xattr(struct file_struct *f
c44efccb
WD
384 out_of_memory("receive_xattr");
385 read_buf(f, ptr, name_len);
386 read_buf(f, ptr + name_len, datum_len);
387+
388+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
d30eff93 389+ && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
c44efccb
WD
390+ free(ptr);
391+ temp_xattr.count--;
392+ continue;
393+ }
394+
395 rxa->name_len = name_len;
396 rxa->datum_len = datum_len;
397 rxa->name = ptr;
398 rxa->datum = ptr + name_len;
399+
400 #ifdef HAVE_OSX_XATTRS
401 if (strncmp(rxa->name, unique_prefix, upre_len) == 0) {
402 rxa->name_len -= upre_len;
35d8f76e 403@@ -365,4 +384,103 @@ int set_xattr(const char *fname, const s
e66b9b1f
WD
404 return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
405 }
406
407+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *st)
408+{
409+ int mode, rdev_major, rdev_minor, uid, gid, len;
410+ char buf[256];
411+
412+ if (fname)
413+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
414+ else
415+ len = sys_fgetxattr(fd, FAKE_XATTR, buf, sizeof buf - 1);
416+ if (len < 0 || len >= (int)sizeof buf) {
417+ if (errno == ENOTSUP || errno == ENOATTR)
418+ return -1;
419+ return -1;
420+ }
421+ buf[len] = '\0';
422+
423+ if (sscanf(buf, "%o %d,%d %d:%d",
424+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
425+ errno = EINVAL;
426+ return -1;
427+ }
428+
429+ st->st_mode = mode;
430+ st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
431+ st->st_uid = uid;
432+ st->st_gid = gid;
433+
434+ return 0;
435+}
436+
437+int set_stat_xattr(const char *fname, struct file_struct *file)
438+{
439+ STRUCT_STAT fst, xst;
440+ dev_t rdev;
35d8f76e 441+ mode_t mode;
e66b9b1f
WD
442+
443+ if (dry_run)
444+ return 0;
445+
446+ if (x_stat(fname, &fst, &xst) < 0)
447+ return -1;
448+
449+ if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
450+ rdev = file->u.rdev;
451+ else
452+ rdev = 0;
453+
35d8f76e
WD
454+ /* Force the real file's mode to our liking. */
455+ mode = (fst.st_mode & ~CHMOD_BITS)
456+ | ((S_ISDIR(fst.st_mode) ? 0777 : 0666) & (~orig_umask | S_IRWXU));
457+ if (fst.st_mode != mode)
458+ do_chmod(fname, mode);
e66b9b1f
WD
459+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
460+ fst.st_rdev = 0; /* just in case */
461+
35d8f76e 462+ if (mode == file->mode && fst.st_rdev == rdev
e66b9b1f
WD
463+ && fst.st_uid == file->uid && fst.st_gid == file->gid) {
464+ /* xst.st_mode will be 0 if there's no current stat xattr */
465+ if (xst.st_mode && sys_lremovexattr(fname, FAKE_XATTR) < 0)
466+ return -2;
467+ return 0;
468+ }
469+
470+ if (xst.st_mode != file->mode || xst.st_rdev != rdev
471+ || xst.st_uid != file->uid || xst.st_gid != file->gid) {
472+ char buf[256];
473+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
474+ (int)file->mode,
475+ (int)major(rdev), (int)minor(rdev),
476+ (int)file->uid, (int)file->gid);
477+ return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
478+ }
479+ return 0;
480+}
481+
482+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
483+{
484+ int ret = do_stat(fname, fst);
485+ if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
486+ xst->st_mode = 0;
487+ return ret;
488+}
489+
490+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
491+{
492+ int ret = do_lstat(fname, fst);
493+ if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
494+ xst->st_mode = 0;
495+ return ret;
496+}
497+
498+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
499+{
500+ int ret = do_fstat(fd, fst);
501+ if (get_stat_xattr(NULL, fd, xst? xst : fst) != 0 && xst)
502+ xst->st_mode = 0;
503+ return ret;
504+}
505+
506 #endif /* SUPPORT_XATTRS */