Fixed a problem with the new fgetxattr() stubs.
[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
3ae9c1ee 168@@ -197,7 +197,9 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
169 (long)sxp->st.st_gid, (long)file->gid);
170 }
171 }
3ae9c1ee
WD
172- if (do_lchown(fname,
173+ if (am_root < 0)
174+ ;
175+ else if (do_lchown(fname,
c44efccb
WD
176 change_uid ? file->uid : sxp->st.st_uid,
177 change_gid ? file->gid : sxp->st.st_gid) != 0) {
3ae9c1ee
WD
178 /* shouldn't have attempted to change uid or gid
179@@ -206,7 +208,7 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
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 */
3ae9c1ee 188@@ -237,7 +239,15 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
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;
c44efccb
WD
200+ } else
201+ ret = do_chmod(fname, new_mode);
202 if (ret < 0) {
203 rsyserr(FERROR, errno,
204 "failed to set permissions on %s",
3ae9c1ee 205@@ -249,6 +259,23 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
206 }
207 #endif
208
209+ if (am_root < 0) {
3ae9c1ee
WD
210+ switch (set_stat_xattr(fname, file)) {
211+ case 0:
212+ break;
213+ case -1:
c44efccb
WD
214+ rsyserr(FERROR, errno,
215+ "write of stat xattr failed for %s",
216+ full_fname(fname));
3ae9c1ee
WD
217+ break;
218+ case -2:
219+ rsyserr(FERROR, errno,
220+ "delete of stat xattr failed for %s",
221+ full_fname(fname));
222+ break;
c44efccb
WD
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
d30eff93 235+#define FAKE_XATTR "user.rsync%stat"
c44efccb
WD
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);
3ae9c1ee 273@@ -215,23 +226,98 @@ int do_mkstemp(char *template, mode_t pe
c44efccb
WD
274 #endif
275 }
276
277+int get_stat_xattr(const char *fname, STRUCT_STAT *st)
278+{
d30eff93 279+ int mode, rdev_major, rdev_minor, uid, gid, len;
c44efccb
WD
280+ char buf[256];
281+
282+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
283+ if (len < 0 || len >= (int)sizeof buf) {
284+ if (errno == ENOTSUP || errno == ENOATTR)
3ae9c1ee 285+ return -1;
c44efccb
WD
286+ return -1;
287+ }
288+ buf[len] = '\0';
289+
d30eff93 290+ if (sscanf(buf, "%o %d,%d %d:%d",
3ae9c1ee 291+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
c44efccb
WD
292+ errno = EINVAL;
293+ return -1;
294+ }
295+
296+ st->st_mode = mode;
d30eff93 297+ st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
c44efccb
WD
298+ st->st_uid = uid;
299+ st->st_gid = gid;
300+
301+ return 0;
302+}
303+
3ae9c1ee 304+int set_stat_xattr(const char *fname, struct file_struct *file)
c44efccb 305+{
3ae9c1ee
WD
306+ STRUCT_STAT fst, xst;
307+ int have_xattr;
308+ dev_t rdev;
c44efccb
WD
309+ if (dry_run) return 0;
310+ RETURN_ERROR_IF_RO_OR_LO;
3ae9c1ee
WD
311+
312+ am_root = 2; /* get real stat() w/o xattr overlay */
313+ do_stat(fname, &fst);
314+ am_root = -1;
315+ have_xattr = get_stat_xattr(fname, &xst) == 0;
316+
317+ if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
318+ rdev = file->u.rdev;
319+ else
320+ rdev = 0;
321+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
322+ fst.st_rdev = 0; /* just in case */
323+
324+ if (fst.st_mode == file->mode && fst.st_rdev == rdev
325+ && fst.st_uid == file->uid && fst.st_gid == file->gid) {
326+ if (have_xattr && sys_lremovexattr(fname, FAKE_XATTR) < 0)
327+ return -2;
328+ return 0;
329+ }
330+
331+ if (!have_xattr
332+ || xst.st_mode != file->mode || xst.st_rdev != rdev
333+ || xst.st_uid != file->uid || xst.st_gid != file->gid) {
334+ char buf[256];
335+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
336+ (int)file->mode,
337+ (int)major(rdev), (int)minor(rdev),
338+ (int)file->uid, (int)file->gid);
339+ return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
340+ }
341+ return 0;
c44efccb
WD
342+}
343+
344 int do_stat(const char *fname, STRUCT_STAT *st)
345 {
346+ int ret;
347 #ifdef USE_STAT64_FUNCS
348- return stat64(fname, st);
349+ ret = stat64(fname, st);
350 #else
351- return stat(fname, st);
352+ ret = stat(fname, st);
353 #endif
354+ if (am_root < 0 && ret == 0)
355+ get_stat_xattr(fname, st);
356+ return ret;
357 }
358
359 int do_lstat(const char *fname, STRUCT_STAT *st)
360 {
361 #ifdef SUPPORT_LINKS
362+ int ret;
363 # ifdef USE_STAT64_FUNCS
364- return lstat64(fname, st);
365+ ret = lstat64(fname, st);
366 # else
367- return lstat(fname, st);
368+ ret = lstat(fname, st);
369 # endif
370+ if (am_root < 0 && ret == 0)
371+ get_stat_xattr(fname, st);
372+ return ret;
373 #else
374 return do_stat(fname, st);
375 #endif
376--- old/t_unsafe.c
377+++ new/t_unsafe.c
378@@ -24,7 +24,11 @@
379
380 #include "rsync.h"
381
382-int dry_run, read_only, list_only, verbose;
383+int dry_run = 0;
384+int am_root = 0;
385+int read_only = 0;
386+int list_only = 0;
387+int verbose = 0;
388 int preserve_perms = 0;
389
390 int
391--- old/tls.c
392+++ new/tls.c
393@@ -39,6 +39,7 @@
394
395 /* These are to make syscall.o shut up. */
396 int dry_run = 0;
397+int am_root = 0; /* TODO: add option to set this to -1. */
398 int read_only = 1;
399 int list_only = 0;
400 int preserve_perms = 0;
401--- old/trimslash.c
402+++ new/trimslash.c
403@@ -23,6 +23,7 @@
404
405 /* These are to make syscall.o shut up. */
406 int dry_run = 0;
407+int am_root = 0;
408 int read_only = 1;
409 int list_only = 0;
410 int preserve_perms = 0;
411--- old/xattr.c
412+++ new/xattr.c
413@@ -26,6 +26,7 @@
414 #ifdef SUPPORT_XATTRS
415
416 extern int dry_run;
417+extern int am_root;
418 extern unsigned int file_struct_len;
419
420 #define RSYNC_XAL_INITIAL 5
421@@ -130,9 +131,15 @@ static int rsync_xal_get(const char *fna
422 if (name_size == 0)
423 return 0;
424 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
425- rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
426+ rsync_xa *rxas;
427
428 len = strlen(name) + 1;
429+ if (am_root < 0 && len == sizeof FAKE_XATTR
d30eff93 430+ && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
c44efccb
WD
431+ continue;
432+
433+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
434+
435 datum_size = sys_lgetxattr(fname, name, NULL, 0);
436 if (datum_size < 0) {
437 if (errno == ENOTSUP)
438@@ -285,10 +292,19 @@ void receive_xattr(struct file_struct *f
439 out_of_memory("receive_xattr");
440 read_buf(f, ptr, name_len);
441 read_buf(f, ptr + name_len, datum_len);
442+
443+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
d30eff93 444+ && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
c44efccb
WD
445+ free(ptr);
446+ temp_xattr.count--;
447+ continue;
448+ }
449+
450 rxa->name_len = name_len;
451 rxa->datum_len = datum_len;
452 rxa->name = ptr;
453 rxa->datum = ptr + name_len;
454+
455 #ifdef HAVE_OSX_XATTRS
456 if (strncmp(rxa->name, unique_prefix, upre_len) == 0) {
457 rxa->name_len -= upre_len;