- Fixed a bug I introduced into the daemon's "fake super" handling.
[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
37--- old/Makefile.in
38+++ new/Makefile.in
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@
42
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
45
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)
51
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)
56
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)
61
62--- old/clientserver.c
63+++ new/clientserver.c
611f9f3c
WD
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. */
c44efccb 67
611f9f3c 68+ if (lp_fake_super(i))
c44efccb 69+ am_root = -1;
611f9f3c
WD
70+ else if (am_root < 0) /* Treat --fake-super from client as --super. */
71+ am_root = 2;
c44efccb 72+
611f9f3c
WD
73 if (filesfrom_fd == 0)
74 filesfrom_fd = f_in;
75
c44efccb
WD
76--- old/generator.c
77+++ new/generator.c
78@@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi
79 recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT,
80 code, f_out);
81
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. */
86 #ifdef HAVE_CHMOD
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
90 && dir_tweaking) {
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,
96--- old/loadparm.c
97+++ new/loadparm.c
98@@ -150,6 +150,7 @@ typedef struct
99 int syslog_facility;
100 int timeout;
101
102+ BOOL fake_super;
103 BOOL ignore_errors;
104 BOOL ignore_nonreadable;
105 BOOL list;
106@@ -197,6 +198,7 @@ static service sDefault =
107 /* syslog_facility; */ LOG_DAEMON,
108 /* timeout; */ 0,
109
110+ /* fake_super; */ False,
111 /* ignore_errors; */ False,
112 /* ignore_nonreadable; */ False,
113 /* list; */ True,
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)
125
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
131
132 if (pszFname)
133 pstrcpy(n2,pszFname);
134- else if (am_server && !am_root)
135+ else if (am_server && am_root <= 0)
136 pstrcpy(n2,RSYNCD_USERCONF);
137 else
138 pstrcpy(n2,RSYNCD_SYSCONF);
139--- old/options.c
140+++ new/options.c
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;
145-int am_root = 0;
146+int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */
147 int am_server = 0;
148 int am_sender = 0;
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 },
166--- old/rsync.c
167+++ new/rsync.c
168@@ -197,6 +197,12 @@ int set_file_attrs(char *fname, struct f
169 (long)sxp->st.st_gid, (long)file->gid);
170 }
171 }
172+ if (am_root < 0) {
173+ if (change_uid)
174+ sxp->st.st_uid = file->uid;
175+ if (change_gid)
176+ sxp->st.st_gid = file->gid;
177+ } else
178 if (do_lchown(fname,
179 change_uid ? file->uid : sxp->st.st_uid,
180 change_gid ? file->gid : sxp->st.st_gid) != 0) {
181@@ -206,7 +212,7 @@ int set_file_attrs(char *fname, struct f
182 change_uid ? "chown" : "chgrp",
183 full_fname(fname));
184 goto cleanup;
185- }
186+ } else
187 /* a lchown had been done - we have to re-stat if the
188 * destination had the setuid or setgid bits set due
189 * to the side effect of the chown call */
190@@ -237,7 +243,16 @@ int set_file_attrs(char *fname, struct f
191
192 #ifdef HAVE_CHMOD
193 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
194- int ret = do_chmod(fname, new_mode);
195+ int ret;
196+ if (am_root < 0) {
197+ mode_t mode = 0666 & ~orig_umask;
198+ if ((sxp->st.st_mode & CHMOD_BITS) != mode)
199+ ret = do_chmod(fname, mode);
200+ else
201+ ret = 0;
202+ sxp->st.st_mode = new_mode;
203+ } else
204+ ret = do_chmod(fname, new_mode);
205 if (ret < 0) {
206 rsyserr(FERROR, errno,
207 "failed to set permissions on %s",
208@@ -249,6 +264,22 @@ int set_file_attrs(char *fname, struct f
209 }
210 #endif
211
212+ if (am_root < 0) {
213+ int write_it = updated;
214+ if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode)) {
215+ if (file->u.rdev != sxp->st.st_rdev) {
216+ sxp->st.st_rdev = file->u.rdev;
217+ write_it = 1;
218+ }
219+ } else
220+ sxp->st.st_rdev = 0;
221+ if (write_it && set_stat_xattr(fname, &sxp->st) < 0) {
222+ rsyserr(FERROR, errno,
223+ "write of stat xattr failed for %s",
224+ full_fname(fname));
225+ }
226+ }
227+
228 if (verbose > 1 && flags & ATTRS_REPORT) {
229 if (updated)
230 rprintf(FCLIENT, "%s\n", fname);
231--- old/rsync.h
232+++ new/rsync.h
233@@ -35,6 +35,8 @@
234
235 #define BACKUP_SUFFIX "~"
236
237+#define FAKE_XATTR "user.fake%stat"
238+
239 /* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
240 incompatible with older versions :-( */
241 #define CHAR_OFFSET 0
242--- old/syscall.c
243+++ new/syscall.c
244@@ -22,12 +22,14 @@
245 */
246
247 #include "rsync.h"
248+#include "lib/sysxattr.h"
249
250 #if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H
251 #include <sys/un.h>
252 #endif
253
254 extern int dry_run;
255+extern int am_root;
256 extern int read_only;
257 extern int list_only;
258 extern int preserve_perms;
259@@ -79,6 +81,15 @@ int do_mknod(char *pathname, mode_t mode
260 {
261 if (dry_run) return 0;
262 RETURN_ERROR_IF_RO_OR_LO;
263+
264+ /* For --fake-super, we create a normal file with mode 0600. */
265+ if (am_root < 0) {
266+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
267+ if (fd < 0 || close(fd) < 0)
268+ return -1;
269+ return 0;
270+ }
271+
272 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
273 if (S_ISFIFO(mode))
274 return mkfifo(pathname, mode);
275@@ -133,6 +144,7 @@ int do_open(const char *pathname, int fl
276 }
277
278 #ifdef HAVE_CHMOD
279+
280 int do_chmod(const char *path, mode_t mode)
281 {
282 int code;
283@@ -215,23 +227,69 @@ int do_mkstemp(char *template, mode_t pe
284 #endif
285 }
286
287+int get_stat_xattr(const char *fname, STRUCT_STAT *st)
288+{
289+ int mode, rdev, uid, gid, len;
290+ char buf[256];
291+
292+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
293+ if (len < 0 || len >= (int)sizeof buf) {
294+ if (errno == ENOTSUP || errno == ENOATTR)
295+ return 0;
296+ return -1;
297+ }
298+ buf[len] = '\0';
299+
300+ if (sscanf(buf, "%o %d %d:%d", &mode, &rdev, &uid, &gid) != 4) {
301+ errno = EINVAL;
302+ return -1;
303+ }
304+
305+ st->st_mode = mode;
306+ st->st_rdev = rdev;
307+ st->st_uid = uid;
308+ st->st_gid = gid;
309+
310+ return 0;
311+}
312+
313+int set_stat_xattr(const char *fname, STRUCT_STAT *st)
314+{
315+ char buf[256];
316+ int len;
317+ if (dry_run) return 0;
318+ RETURN_ERROR_IF_RO_OR_LO;
319+ len = snprintf(buf, sizeof buf, "%o %d %d:%d",
320+ (int)st->st_mode, (int)st->st_rdev,
321+ (int)st->st_uid, (int)st->st_gid);
322+ return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
323+}
324+
325 int do_stat(const char *fname, STRUCT_STAT *st)
326 {
327+ int ret;
328 #ifdef USE_STAT64_FUNCS
329- return stat64(fname, st);
330+ ret = stat64(fname, st);
331 #else
332- return stat(fname, st);
333+ ret = stat(fname, st);
334 #endif
335+ if (am_root < 0 && ret == 0)
336+ get_stat_xattr(fname, st);
337+ return ret;
338 }
339
340 int do_lstat(const char *fname, STRUCT_STAT *st)
341 {
342 #ifdef SUPPORT_LINKS
343+ int ret;
344 # ifdef USE_STAT64_FUNCS
345- return lstat64(fname, st);
346+ ret = lstat64(fname, st);
347 # else
348- return lstat(fname, st);
349+ ret = lstat(fname, st);
350 # endif
351+ if (am_root < 0 && ret == 0)
352+ get_stat_xattr(fname, st);
353+ return ret;
354 #else
355 return do_stat(fname, st);
356 #endif
357--- old/t_unsafe.c
358+++ new/t_unsafe.c
359@@ -24,7 +24,11 @@
360
361 #include "rsync.h"
362
363-int dry_run, read_only, list_only, verbose;
364+int dry_run = 0;
365+int am_root = 0;
366+int read_only = 0;
367+int list_only = 0;
368+int verbose = 0;
369 int preserve_perms = 0;
370
371 int
372--- old/tls.c
373+++ new/tls.c
374@@ -39,6 +39,7 @@
375
376 /* These are to make syscall.o shut up. */
377 int dry_run = 0;
378+int am_root = 0; /* TODO: add option to set this to -1. */
379 int read_only = 1;
380 int list_only = 0;
381 int preserve_perms = 0;
382--- old/trimslash.c
383+++ new/trimslash.c
384@@ -23,6 +23,7 @@
385
386 /* These are to make syscall.o shut up. */
387 int dry_run = 0;
388+int am_root = 0;
389 int read_only = 1;
390 int list_only = 0;
391 int preserve_perms = 0;
392--- old/xattr.c
393+++ new/xattr.c
394@@ -26,6 +26,7 @@
395 #ifdef SUPPORT_XATTRS
396
397 extern int dry_run;
398+extern int am_root;
399 extern unsigned int file_struct_len;
400
401 #define RSYNC_XAL_INITIAL 5
402@@ -130,9 +131,15 @@ static int rsync_xal_get(const char *fna
403 if (name_size == 0)
404 return 0;
405 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
406- rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
407+ rsync_xa *rxas;
408
409 len = strlen(name) + 1;
410+ if (am_root < 0 && len == sizeof FAKE_XATTR
411+ && name[9] == '%' && strcmp(name, FAKE_XATTR) == 0)
412+ continue;
413+
414+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
415+
416 datum_size = sys_lgetxattr(fname, name, NULL, 0);
417 if (datum_size < 0) {
418 if (errno == ENOTSUP)
419@@ -285,10 +292,19 @@ void receive_xattr(struct file_struct *f
420 out_of_memory("receive_xattr");
421 read_buf(f, ptr, name_len);
422 read_buf(f, ptr + name_len, datum_len);
423+
424+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
425+ && ptr[9] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
426+ free(ptr);
427+ temp_xattr.count--;
428+ continue;
429+ }
430+
431 rxa->name_len = name_len;
432 rxa->datum_len = datum_len;
433 rxa->name = ptr;
434 rxa->datum = ptr + name_len;
435+
436 #ifdef HAVE_OSX_XATTRS
437 if (strncmp(rxa->name, unique_prefix, upre_len) == 0) {
438 rxa->name_len -= upre_len;