My verison of Wesley W. Terpstra's --fake-super patch.
[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
64@@ -284,6 +284,9 @@ static int rsync_module(int f_in, int f_
65 pid_t pre_exec_pid = 0;
66 char *request = NULL;
67
68+ if (lp_fake_super(i) != 0)
69+ am_root = -1;
70+
71 if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
72 rprintf(FLOG, "rsync denied on module %s from %s (%s)\n",
73 name, host, addr);
74--- old/generator.c
75+++ new/generator.c
76@@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi
77 recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT,
78 code, f_out);
79
80- /* We need to ensure that any dirs we create have writeable
81+ /* We need to ensure that any dirs we create have rwx
82 * permissions during the time we are putting files within
83 * them. This is then fixed after the transfer is done. */
84 #ifdef HAVE_CHMOD
85- if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR)
86+ if (am_root <= 0 && S_ISDIR(file->mode)
87+ && (file->mode & S_IRWXU) != S_IRWXU
88 && dir_tweaking) {
89- mode_t mode = file->mode | S_IWUSR; /* user write */
90+ mode_t mode = file->mode | S_IRWXU; /* user rwx */
91 char *fname = local_name ? local_name : fbuf;
92 if (do_chmod(fname, mode) < 0) {
93 rsyserr(FERROR, errno,
94--- old/loadparm.c
95+++ new/loadparm.c
96@@ -150,6 +150,7 @@ typedef struct
97 int syslog_facility;
98 int timeout;
99
100+ BOOL fake_super;
101 BOOL ignore_errors;
102 BOOL ignore_nonreadable;
103 BOOL list;
104@@ -197,6 +198,7 @@ static service sDefault =
105 /* syslog_facility; */ LOG_DAEMON,
106 /* timeout; */ 0,
107
108+ /* fake_super; */ False,
109 /* ignore_errors; */ False,
110 /* ignore_nonreadable; */ False,
111 /* list; */ True,
112@@ -298,6 +300,7 @@ static struct parm_struct parm_table[] =
113 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
114 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
115 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
116+ {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
117 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
118 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
119 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
120@@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
121 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
122 FN_LOCAL_INTEGER(lp_timeout, timeout)
123
124+FN_LOCAL_BOOL(lp_fake_super, fake_super)
125 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
126 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
127 FN_LOCAL_BOOL(lp_list, list)
128@@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals
129
130 if (pszFname)
131 pstrcpy(n2,pszFname);
132- else if (am_server && !am_root)
133+ else if (am_server && am_root <= 0)
134 pstrcpy(n2,RSYNCD_USERCONF);
135 else
136 pstrcpy(n2,RSYNCD_SYSCONF);
137--- old/options.c
138+++ new/options.c
139@@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION;
140 int sparse_files = 0;
141 int do_compression = 0;
142 int def_compress_level = Z_DEFAULT_COMPRESSION;
143-int am_root = 0;
144+int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */
145 int am_server = 0;
146 int am_sender = 0;
147 int am_generator = 0;
148@@ -330,6 +330,7 @@ void usage(enum logcode F)
149 rprintf(F," -t, --times preserve times\n");
150 rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
151 rprintf(F," --super receiver attempts super-user activities\n");
152+ rprintf(F," --fake-super fake root by storing/reading ownership/etc in EAs\n");
153 rprintf(F," -S, --sparse handle sparse files efficiently\n");
154 rprintf(F," -n, --dry-run show what would have been transferred\n");
155 rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
156@@ -454,6 +455,7 @@ static struct poptOption long_options[]
157 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
158 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
159 {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
160+ {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
161 {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
162 {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
163 {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
164--- old/rsync.c
165+++ new/rsync.c
166@@ -197,6 +197,12 @@ int set_file_attrs(char *fname, struct f
167 (long)sxp->st.st_gid, (long)file->gid);
168 }
169 }
170+ if (am_root < 0) {
171+ if (change_uid)
172+ sxp->st.st_uid = file->uid;
173+ if (change_gid)
174+ sxp->st.st_gid = file->gid;
175+ } else
176 if (do_lchown(fname,
177 change_uid ? file->uid : sxp->st.st_uid,
178 change_gid ? file->gid : sxp->st.st_gid) != 0) {
179@@ -206,7 +212,7 @@ int set_file_attrs(char *fname, struct f
180 change_uid ? "chown" : "chgrp",
181 full_fname(fname));
182 goto cleanup;
183- }
184+ } else
185 /* a lchown had been done - we have to re-stat if the
186 * destination had the setuid or setgid bits set due
187 * to the side effect of the chown call */
188@@ -237,7 +243,16 @@ int set_file_attrs(char *fname, struct f
189
190 #ifdef HAVE_CHMOD
191 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
192- int ret = do_chmod(fname, new_mode);
193+ int ret;
194+ if (am_root < 0) {
195+ mode_t mode = 0666 & ~orig_umask;
196+ if ((sxp->st.st_mode & CHMOD_BITS) != mode)
197+ ret = do_chmod(fname, mode);
198+ else
199+ ret = 0;
200+ sxp->st.st_mode = new_mode;
201+ } else
202+ ret = do_chmod(fname, new_mode);
203 if (ret < 0) {
204 rsyserr(FERROR, errno,
205 "failed to set permissions on %s",
206@@ -249,6 +264,22 @@ int set_file_attrs(char *fname, struct f
207 }
208 #endif
209
210+ if (am_root < 0) {
211+ int write_it = updated;
212+ if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode)) {
213+ if (file->u.rdev != sxp->st.st_rdev) {
214+ sxp->st.st_rdev = file->u.rdev;
215+ write_it = 1;
216+ }
217+ } else
218+ sxp->st.st_rdev = 0;
219+ if (write_it && set_stat_xattr(fname, &sxp->st) < 0) {
220+ rsyserr(FERROR, errno,
221+ "write of stat xattr failed for %s",
222+ full_fname(fname));
223+ }
224+ }
225+
226 if (verbose > 1 && flags & ATTRS_REPORT) {
227 if (updated)
228 rprintf(FCLIENT, "%s\n", fname);
229--- old/rsync.h
230+++ new/rsync.h
231@@ -35,6 +35,8 @@
232
233 #define BACKUP_SUFFIX "~"
234
235+#define FAKE_XATTR "user.fake%stat"
236+
237 /* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is
238 incompatible with older versions :-( */
239 #define CHAR_OFFSET 0
240--- old/syscall.c
241+++ new/syscall.c
242@@ -22,12 +22,14 @@
243 */
244
245 #include "rsync.h"
246+#include "lib/sysxattr.h"
247
248 #if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H
249 #include <sys/un.h>
250 #endif
251
252 extern int dry_run;
253+extern int am_root;
254 extern int read_only;
255 extern int list_only;
256 extern int preserve_perms;
257@@ -79,6 +81,15 @@ int do_mknod(char *pathname, mode_t mode
258 {
259 if (dry_run) return 0;
260 RETURN_ERROR_IF_RO_OR_LO;
261+
262+ /* For --fake-super, we create a normal file with mode 0600. */
263+ if (am_root < 0) {
264+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
265+ if (fd < 0 || close(fd) < 0)
266+ return -1;
267+ return 0;
268+ }
269+
270 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
271 if (S_ISFIFO(mode))
272 return mkfifo(pathname, mode);
273@@ -133,6 +144,7 @@ int do_open(const char *pathname, int fl
274 }
275
276 #ifdef HAVE_CHMOD
277+
278 int do_chmod(const char *path, mode_t mode)
279 {
280 int code;
281@@ -215,23 +227,69 @@ int do_mkstemp(char *template, mode_t pe
282 #endif
283 }
284
285+int get_stat_xattr(const char *fname, STRUCT_STAT *st)
286+{
287+ int mode, rdev, uid, gid, len;
288+ char buf[256];
289+
290+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
291+ if (len < 0 || len >= (int)sizeof buf) {
292+ if (errno == ENOTSUP || errno == ENOATTR)
293+ return 0;
294+ return -1;
295+ }
296+ buf[len] = '\0';
297+
298+ if (sscanf(buf, "%o %d %d:%d", &mode, &rdev, &uid, &gid) != 4) {
299+ errno = EINVAL;
300+ return -1;
301+ }
302+
303+ st->st_mode = mode;
304+ st->st_rdev = rdev;
305+ st->st_uid = uid;
306+ st->st_gid = gid;
307+
308+ return 0;
309+}
310+
311+int set_stat_xattr(const char *fname, STRUCT_STAT *st)
312+{
313+ char buf[256];
314+ int len;
315+ if (dry_run) return 0;
316+ RETURN_ERROR_IF_RO_OR_LO;
317+ len = snprintf(buf, sizeof buf, "%o %d %d:%d",
318+ (int)st->st_mode, (int)st->st_rdev,
319+ (int)st->st_uid, (int)st->st_gid);
320+ return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
321+}
322+
323 int do_stat(const char *fname, STRUCT_STAT *st)
324 {
325+ int ret;
326 #ifdef USE_STAT64_FUNCS
327- return stat64(fname, st);
328+ ret = stat64(fname, st);
329 #else
330- return stat(fname, st);
331+ ret = stat(fname, st);
332 #endif
333+ if (am_root < 0 && ret == 0)
334+ get_stat_xattr(fname, st);
335+ return ret;
336 }
337
338 int do_lstat(const char *fname, STRUCT_STAT *st)
339 {
340 #ifdef SUPPORT_LINKS
341+ int ret;
342 # ifdef USE_STAT64_FUNCS
343- return lstat64(fname, st);
344+ ret = lstat64(fname, st);
345 # else
346- return lstat(fname, st);
347+ ret = lstat(fname, st);
348 # endif
349+ if (am_root < 0 && ret == 0)
350+ get_stat_xattr(fname, st);
351+ return ret;
352 #else
353 return do_stat(fname, st);
354 #endif
355--- old/t_unsafe.c
356+++ new/t_unsafe.c
357@@ -24,7 +24,11 @@
358
359 #include "rsync.h"
360
361-int dry_run, read_only, list_only, verbose;
362+int dry_run = 0;
363+int am_root = 0;
364+int read_only = 0;
365+int list_only = 0;
366+int verbose = 0;
367 int preserve_perms = 0;
368
369 int
370--- old/tls.c
371+++ new/tls.c
372@@ -39,6 +39,7 @@
373
374 /* These are to make syscall.o shut up. */
375 int dry_run = 0;
376+int am_root = 0; /* TODO: add option to set this to -1. */
377 int read_only = 1;
378 int list_only = 0;
379 int preserve_perms = 0;
380--- old/trimslash.c
381+++ new/trimslash.c
382@@ -23,6 +23,7 @@
383
384 /* These are to make syscall.o shut up. */
385 int dry_run = 0;
386+int am_root = 0;
387 int read_only = 1;
388 int list_only = 0;
389 int preserve_perms = 0;
390--- old/xattr.c
391+++ new/xattr.c
392@@ -26,6 +26,7 @@
393 #ifdef SUPPORT_XATTRS
394
395 extern int dry_run;
396+extern int am_root;
397 extern unsigned int file_struct_len;
398
399 #define RSYNC_XAL_INITIAL 5
400@@ -130,9 +131,15 @@ static int rsync_xal_get(const char *fna
401 if (name_size == 0)
402 return 0;
403 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
404- rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
405+ rsync_xa *rxas;
406
407 len = strlen(name) + 1;
408+ if (am_root < 0 && len == sizeof FAKE_XATTR
409+ && name[9] == '%' && strcmp(name, FAKE_XATTR) == 0)
410+ continue;
411+
412+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
413+
414 datum_size = sys_lgetxattr(fname, name, NULL, 0);
415 if (datum_size < 0) {
416 if (errno == ENOTSUP)
417@@ -285,10 +292,19 @@ void receive_xattr(struct file_struct *f
418 out_of_memory("receive_xattr");
419 read_buf(f, ptr, name_len);
420 read_buf(f, ptr + name_len, datum_len);
421+
422+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
423+ && ptr[9] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
424+ free(ptr);
425+ temp_xattr.count--;
426+ continue;
427+ }
428+
429 rxa->name_len = name_len;
430 rxa->datum_len = datum_len;
431 rxa->name = ptr;
432 rxa->datum = ptr + name_len;
433+
434 #ifdef HAVE_OSX_XATTRS
435 if (strncmp(rxa->name, unique_prefix, upre_len) == 0) {
436 rxa->name_len -= upre_len;