- Fixed a problem with the sscanf() count (now 5, not 4).
[rsync/rsync-patches.git] / fake-super.diff
1 Depends-On-Patch: acls.diff
2 Depends-On-Patch: xattrs.diff
3
4 This patch adds a new option:  --fake-super, which tells rsync to copy in a
5 fake super-user mode that stores various file attributes in an extended-
6 attribute value instead of as real file-system attributes.  The items
7 affected 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
19 A daemon can set "fake super = yes" in the rsync.conf file for any module
20 that you'd like to run without root perms while pretending it has them (the
21 client cannot affect this).
22
23 The --fake-super option only affects the side where the option is used.  To
24 affect 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
28 For a local copy where you want to affect only one side or the other,
29 you'll need to turn the copy into a remote copy to localhost.
30
31 After 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 @@ -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. */
67  
68 +       if (lp_fake_super(i))
69 +               am_root = -1;
70 +       else if (am_root < 0) /* Treat --fake-super from client as --super. */
71 +               am_root = 2;
72 +
73         if (filesfrom_fd == 0)
74                 filesfrom_fd = f_in;
75  
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,7 +197,9 @@ int set_file_attrs(char *fname, struct f
169                                         (long)sxp->st.st_gid, (long)file->gid);
170                         }
171                 }
172 -               if (do_lchown(fname,
173 +               if (am_root < 0)
174 +                       ;
175 +               else if (do_lchown(fname,
176                     change_uid ? file->uid : sxp->st.st_uid,
177                     change_gid ? file->gid : sxp->st.st_gid) != 0) {
178                         /* shouldn't have attempted to change uid or gid
179 @@ -206,7 +208,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 +239,15 @@ 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 +               } else
201 +                       ret = do_chmod(fname, new_mode);
202                 if (ret < 0) {
203                         rsyserr(FERROR, errno,
204                                 "failed to set permissions on %s",
205 @@ -249,6 +259,23 @@ int set_file_attrs(char *fname, struct f
206         }
207  #endif
208  
209 +       if (am_root < 0) {
210 +               switch (set_stat_xattr(fname, file)) {
211 +               case 0:
212 +                       break;
213 +               case -1:
214 +                       rsyserr(FERROR, errno,
215 +                               "write of stat xattr failed for %s",
216 +                               full_fname(fname));
217 +                       break;
218 +               case -2:
219 +                       rsyserr(FERROR, errno,
220 +                               "delete of stat xattr failed for %s",
221 +                               full_fname(fname));
222 +                       break;
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.rsync%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 @@ -215,23 +226,98 @@ int do_mkstemp(char *template, mode_t pe
274  #endif
275  }
276  
277 +int get_stat_xattr(const char *fname, STRUCT_STAT *st)
278 +{
279 +       int mode, rdev_major, rdev_minor, uid, gid, len;
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)
285 +                       return -1;
286 +               return -1;
287 +       }
288 +       buf[len] = '\0';
289 +
290 +       if (sscanf(buf, "%o %d,%d %d:%d",
291 +                  &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
292 +               errno = EINVAL;
293 +               return -1;
294 +       }
295 +
296 +       st->st_mode = mode;
297 +       st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
298 +       st->st_uid = uid;
299 +       st->st_gid = gid;
300 +
301 +       return 0;
302 +}
303 +
304 +int set_stat_xattr(const char *fname, struct file_struct *file)
305 +{
306 +       STRUCT_STAT fst, xst;
307 +       int have_xattr;
308 +       dev_t rdev;
309 +       if (dry_run) return 0;
310 +       RETURN_ERROR_IF_RO_OR_LO;
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;
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
430 +                && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
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
444 +                        && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
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;