Split the major & minor device numbers.
[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
d30eff93 237+#define FAKE_XATTR "user.rsync%stat"
c44efccb
WD
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);
d30eff93 275@@ -215,23 +226,71 @@ int do_mkstemp(char *template, mode_t pe
c44efccb
WD
276 #endif
277 }
278
279+int get_stat_xattr(const char *fname, STRUCT_STAT *st)
280+{
d30eff93 281+ int mode, rdev_major, rdev_minor, uid, gid, len;
c44efccb
WD
282+ char buf[256];
283+
284+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
285+ if (len < 0 || len >= (int)sizeof buf) {
286+ if (errno == ENOTSUP || errno == ENOATTR)
287+ return 0;
288+ return -1;
289+ }
290+ buf[len] = '\0';
291+
d30eff93
WD
292+ if (sscanf(buf, "%o %d,%d %d:%d",
293+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 4) {
c44efccb
WD
294+ errno = EINVAL;
295+ return -1;
296+ }
297+
298+ st->st_mode = mode;
d30eff93 299+ st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
c44efccb
WD
300+ st->st_uid = uid;
301+ st->st_gid = gid;
302+
303+ return 0;
304+}
305+
306+int set_stat_xattr(const char *fname, STRUCT_STAT *st)
307+{
308+ char buf[256];
309+ int len;
310+ if (dry_run) return 0;
311+ RETURN_ERROR_IF_RO_OR_LO;
d30eff93
WD
312+ len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
313+ (int)st->st_mode,
314+ (int)major(st->st_rdev), (int)minor(st->st_rdev),
c44efccb
WD
315+ (int)st->st_uid, (int)st->st_gid);
316+ return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
317+}
318+
319 int do_stat(const char *fname, STRUCT_STAT *st)
320 {
321+ int ret;
322 #ifdef USE_STAT64_FUNCS
323- return stat64(fname, st);
324+ ret = stat64(fname, st);
325 #else
326- return stat(fname, st);
327+ ret = stat(fname, st);
328 #endif
329+ if (am_root < 0 && ret == 0)
330+ get_stat_xattr(fname, st);
331+ return ret;
332 }
333
334 int do_lstat(const char *fname, STRUCT_STAT *st)
335 {
336 #ifdef SUPPORT_LINKS
337+ int ret;
338 # ifdef USE_STAT64_FUNCS
339- return lstat64(fname, st);
340+ ret = lstat64(fname, st);
341 # else
342- return lstat(fname, st);
343+ ret = lstat(fname, st);
344 # endif
345+ if (am_root < 0 && ret == 0)
346+ get_stat_xattr(fname, st);
347+ return ret;
348 #else
349 return do_stat(fname, st);
350 #endif
351--- old/t_unsafe.c
352+++ new/t_unsafe.c
353@@ -24,7 +24,11 @@
354
355 #include "rsync.h"
356
357-int dry_run, read_only, list_only, verbose;
358+int dry_run = 0;
359+int am_root = 0;
360+int read_only = 0;
361+int list_only = 0;
362+int verbose = 0;
363 int preserve_perms = 0;
364
365 int
366--- old/tls.c
367+++ new/tls.c
368@@ -39,6 +39,7 @@
369
370 /* These are to make syscall.o shut up. */
371 int dry_run = 0;
372+int am_root = 0; /* TODO: add option to set this to -1. */
373 int read_only = 1;
374 int list_only = 0;
375 int preserve_perms = 0;
376--- old/trimslash.c
377+++ new/trimslash.c
378@@ -23,6 +23,7 @@
379
380 /* These are to make syscall.o shut up. */
381 int dry_run = 0;
382+int am_root = 0;
383 int read_only = 1;
384 int list_only = 0;
385 int preserve_perms = 0;
386--- old/xattr.c
387+++ new/xattr.c
388@@ -26,6 +26,7 @@
389 #ifdef SUPPORT_XATTRS
390
391 extern int dry_run;
392+extern int am_root;
393 extern unsigned int file_struct_len;
394
395 #define RSYNC_XAL_INITIAL 5
396@@ -130,9 +131,15 @@ static int rsync_xal_get(const char *fna
397 if (name_size == 0)
398 return 0;
399 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
400- rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
401+ rsync_xa *rxas;
402
403 len = strlen(name) + 1;
404+ if (am_root < 0 && len == sizeof FAKE_XATTR
d30eff93 405+ && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
c44efccb
WD
406+ continue;
407+
408+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
409+
410 datum_size = sys_lgetxattr(fname, name, NULL, 0);
411 if (datum_size < 0) {
412 if (errno == ENOTSUP)
413@@ -285,10 +292,19 @@ void receive_xattr(struct file_struct *f
414 out_of_memory("receive_xattr");
415 read_buf(f, ptr, name_len);
416 read_buf(f, ptr + name_len, datum_len);
417+
418+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
d30eff93 419+ && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
c44efccb
WD
420+ free(ptr);
421+ temp_xattr.count--;
422+ continue;
423+ }
424+
425 rxa->name_len = name_len;
426 rxa->datum_len = datum_len;
427 rxa->name = ptr;
428 rxa->datum = ptr + name_len;
429+
430 #ifdef HAVE_OSX_XATTRS
431 if (strncmp(rxa->name, unique_prefix, upre_len) == 0) {
432 rxa->name_len -= upre_len;