Changed the unique_prefix and upre_len vars into defines.
[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 --enable-xattr-support
35     make
36
37 or, if you want ACL support too:
38
39     ./prepare-source
40     ./configure --enable-acl-support --enable-xattr-support
41     make
42
43 --- old/backup.c
44 +++ new/backup.c
45 @@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath)
46                 if (p >= rel) {
47                         /* Try to transfer the directory settings of the
48                          * actual dir that the files are coming from. */
49 -                       if (do_stat(rel, &sx.st) < 0) {
50 +                       if (x_stat(rel, &sx.st, NULL) < 0) {
51                                 rsyserr(FERROR, errno,
52                                         "make_bak_dir stat %s failed",
53                                         full_fname(rel));
54 @@ -200,7 +200,7 @@ static int keep_backup(char *fname)
55         int ret_code;
56  
57         /* return if no file to keep */
58 -       if (do_lstat(fname, &sx.st) < 0)
59 +       if (x_lstat(fname, &sx.st, NULL) < 0)
60                 return 1;
61  #ifdef SUPPORT_ACLS
62         sx.acc_acl = sx.def_acl = NULL;
63 --- old/clientserver.c
64 +++ new/clientserver.c
65 @@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_
66         ret = parse_arguments(&argc, (const char ***) &argv, 0);
67         quiet = 0; /* Don't let someone try to be tricky. */
68  
69 +       if (lp_fake_super(i))
70 +               am_root = -1;
71 +       else if (am_root < 0) /* Treat --fake-super from client as --super. */
72 +               am_root = 2;
73 +
74         if (filesfrom_fd == 0)
75                 filesfrom_fd = f_in;
76  
77 --- old/flist.c
78 +++ new/flist.c
79 @@ -181,7 +181,7 @@ static int readlink_stat(const char *pat
80         }
81         return 0;
82  #else
83 -       return do_stat(path, stp);
84 +       return x_stat(path, stp, NULL);
85  #endif
86  }
87  
88 @@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S
89  {
90  #ifdef SUPPORT_LINKS
91         if (copy_links)
92 -               return do_stat(path, stp);
93 -       if (do_lstat(path, stp) < 0)
94 +               return x_stat(path, stp, NULL);
95 +       if (x_lstat(path, stp, NULL) < 0)
96                 return -1;
97         if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
98                 STRUCT_STAT st;
99 -               if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
100 +               if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
101                         *stp = st;
102         }
103         return 0;
104  #else
105 -       return do_stat(path, stp);
106 +       return x_stat(path, stp, NULL);
107  #endif
108  }
109  
110 @@ -793,7 +793,7 @@ struct file_struct *make_file(char *fnam
111                 if (save_errno == ENOENT) {
112  #ifdef SUPPORT_LINKS
113                         /* Avoid "vanished" error if symlink points nowhere. */
114 -                       if (copy_links && do_lstat(thisname, &st) == 0
115 +                       if (copy_links && x_lstat(thisname, &st, NULL) == 0
116                             && S_ISLNK(st.st_mode)) {
117                                 io_error |= IOERR_GENERAL;
118                                 rprintf(FERROR, "symlink has no referent: %s\n",
119 @@ -963,7 +963,7 @@ struct file_struct *make_file(char *fnam
120                 int save_mode = file->mode;
121                 file->mode = S_IFDIR; /* Find a directory with our name. */
122                 if (flist_find(the_file_list, file) >= 0
123 -                   && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
124 +                   && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
125                         file->modtime = st2.st_mtime;
126                         file->length = st2.st_size;
127                         file->mode = st2.st_mode;
128 --- old/generator.c
129 +++ new/generator.c
130 @@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi
131                 recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT,
132                                code, f_out);
133  
134 -               /* We need to ensure that any dirs we create have writeable
135 +               /* We need to ensure that any dirs we create have rwx
136                  * permissions during the time we are putting files within
137                  * them.  This is then fixed after the transfer is done. */
138  #ifdef HAVE_CHMOD
139 -               if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR)
140 +               if (am_root <= 0 && S_ISDIR(file->mode)
141 +                   && (file->mode & S_IRWXU) != S_IRWXU
142                  && dir_tweaking) {
143 -                       mode_t mode = file->mode | S_IWUSR; /* user write */
144 +                       mode_t mode = file->mode | S_IRWXU; /* user rwx */
145                         char *fname = local_name ? local_name : fbuf;
146                         if (do_chmod(fname, mode) < 0) {
147                                 rsyserr(FERROR, errno,
148 --- old/loadparm.c
149 +++ new/loadparm.c
150 @@ -150,6 +150,7 @@ typedef struct
151         int syslog_facility;
152         int timeout;
153  
154 +       BOOL fake_super;
155         BOOL ignore_errors;
156         BOOL ignore_nonreadable;
157         BOOL list;
158 @@ -197,6 +198,7 @@ static service sDefault =
159   /* syslog_facility; */                LOG_DAEMON,
160   /* timeout; */                        0,
161  
162 + /* fake_super; */             False,
163   /* ignore_errors; */          False,
164   /* ignore_nonreadable; */     False,
165   /* list; */                   True,
166 @@ -298,6 +300,7 @@ static struct parm_struct parm_table[] =
167   {"dont compress",     P_STRING, P_LOCAL, &sDefault.dont_compress,     NULL,0},
168   {"exclude from",      P_STRING, P_LOCAL, &sDefault.exclude_from,      NULL,0},
169   {"exclude",           P_STRING, P_LOCAL, &sDefault.exclude,           NULL,0},
170 + {"fake super",        P_BOOL,   P_LOCAL, &sDefault.fake_super,        NULL,0},
171   {"filter",            P_STRING, P_LOCAL, &sDefault.filter,            NULL,0},
172   {"gid",               P_STRING, P_LOCAL, &sDefault.gid,               NULL,0},
173   {"hosts allow",       P_STRING, P_LOCAL, &sDefault.hosts_allow,       NULL,0},
174 @@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
175  FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
176  FN_LOCAL_INTEGER(lp_timeout, timeout)
177  
178 +FN_LOCAL_BOOL(lp_fake_super, fake_super)
179  FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
180  FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
181  FN_LOCAL_BOOL(lp_list, list)
182 @@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals
183  
184         if (pszFname)
185             pstrcpy(n2,pszFname);
186 -       else if (am_server && !am_root)
187 +       else if (am_server && am_root <= 0)
188             pstrcpy(n2,RSYNCD_USERCONF);
189         else
190             pstrcpy(n2,RSYNCD_SYSCONF);
191 --- old/options.c
192 +++ new/options.c
193 @@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION;
194  int sparse_files = 0;
195  int do_compression = 0;
196  int def_compress_level = Z_DEFAULT_COMPRESSION;
197 -int am_root = 0;
198 +int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */
199  int am_server = 0;
200  int am_sender = 0;
201  int am_generator = 0;
202 @@ -330,6 +330,7 @@ void usage(enum logcode F)
203    rprintf(F," -t, --times                 preserve times\n");
204    rprintf(F," -O, --omit-dir-times        omit directories when preserving times\n");
205    rprintf(F,"     --super                 receiver attempts super-user activities\n");
206 +  rprintf(F,"     --fake-super            fake root by storing/reading ownership/etc in EAs\n");
207    rprintf(F," -S, --sparse                handle sparse files efficiently\n");
208    rprintf(F," -n, --dry-run               show what would have been transferred\n");
209    rprintf(F," -W, --whole-file            copy files whole (without rsync algorithm)\n");
210 @@ -454,6 +455,7 @@ static struct poptOption long_options[] 
211    {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
212    {"super",            0,  POPT_ARG_VAL,    &am_root, 2, 0, 0 },
213    {"no-super",         0,  POPT_ARG_VAL,    &am_root, 0, 0, 0 },
214 +  {"fake-super",       0,  POPT_ARG_VAL,    &am_root, -1, 0, 0 },
215    {"owner",           'o', POPT_ARG_VAL,    &preserve_uid, 1, 0, 0 },
216    {"no-owner",         0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
217    {"no-o",             0,  POPT_ARG_VAL,    &preserve_uid, 0, 0, 0 },
218 --- old/receiver.c
219 +++ new/receiver.c
220 @@ -528,7 +528,7 @@ int recv_files(int f_in, struct file_lis
221                 if (fd1 == -1) {
222                         st.st_mode = 0;
223                         st.st_size = 0;
224 -               } else if (do_fstat(fd1,&st) != 0) {
225 +               } else if (x_fstat(fd1, &st, NULL) != 0) {
226                         rsyserr(FERROR, errno, "fstat %s failed",
227                                 full_fname(fnamecmp));
228                         discard_receive_data(f_in, file->length);
229 --- old/rsync.c
230 +++ new/rsync.c
231 @@ -49,7 +49,6 @@ extern int preserve_gid;
232  extern int inplace;
233  extern int keep_dirlinks;
234  extern int make_backups;
235 -extern mode_t orig_umask;
236  extern struct stats stats;
237  extern struct chmod_mode_struct *daemon_chmod_modes;
238  
239 @@ -197,7 +196,9 @@ int set_file_attrs(char *fname, struct f
240                                         (long)sxp->st.st_gid, (long)file->gid);
241                         }
242                 }
243 -               if (do_lchown(fname,
244 +               if (am_root < 0)
245 +                       ;
246 +               else if (do_lchown(fname,
247                     change_uid ? file->uid : sxp->st.st_uid,
248                     change_gid ? file->gid : sxp->st.st_gid) != 0) {
249                         /* shouldn't have attempted to change uid or gid
250 @@ -206,7 +207,7 @@ int set_file_attrs(char *fname, struct f
251                             change_uid ? "chown" : "chgrp",
252                             full_fname(fname));
253                         goto cleanup;
254 -               }
255 +               } else
256                 /* a lchown had been done - we have to re-stat if the
257                  * destination had the setuid or setgid bits set due
258                  * to the side effect of the chown call */
259 @@ -224,6 +225,24 @@ int set_file_attrs(char *fname, struct f
260         if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
261                 updated = 1;
262  #endif
263 +
264 +       if (am_root < 0 && !S_ISLNK(file->mode)) {
265 +               switch (set_stat_xattr(fname, file)) {
266 +               case 0:
267 +                       break;
268 +               case -1:
269 +                       rsyserr(FERROR, errno,
270 +                               "write of stat xattr failed for %s",
271 +                               full_fname(fname));
272 +                       break;
273 +               case -2:
274 +                       rsyserr(FERROR, errno,
275 +                               "delete of stat xattr failed for %s",
276 +                               full_fname(fname));
277 +                       break;
278 +               }
279 +       }
280 +
281  #ifdef SUPPORT_ACLS
282         /* It's OK to call set_acl() now, even for a dir, as the generator
283          * will enable owner-writability using chmod, if necessary.
284 @@ -237,7 +256,7 @@ int set_file_attrs(char *fname, struct f
285  
286  #ifdef HAVE_CHMOD
287         if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
288 -               int ret = do_chmod(fname, new_mode);
289 +               int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
290                 if (ret < 0) {
291                         rsyserr(FERROR, errno,
292                                 "failed to set permissions on %s",
293 --- old/syscall.c
294 +++ new/syscall.c
295 @@ -28,6 +28,7 @@
296  #endif
297  
298  extern int dry_run;
299 +extern int am_root;
300  extern int read_only;
301  extern int list_only;
302  extern int preserve_perms;
303 @@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode
304  {
305         if (dry_run) return 0;
306         RETURN_ERROR_IF_RO_OR_LO;
307 +
308 +       /* For --fake-super, we create a normal file with mode 0600. */
309 +       if (am_root < 0) {
310 +               int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
311 +               if (fd < 0 || close(fd) < 0)
312 +                       return -1;
313 +               return 0;
314 +       }
315 +
316  #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
317         if (S_ISFIFO(mode))
318                 return mkfifo(pathname, mode);
319 --- old/t_unsafe.c
320 +++ new/t_unsafe.c
321 @@ -24,7 +24,11 @@
322  
323  #include "rsync.h"
324  
325 -int dry_run, read_only, list_only, verbose;
326 +int dry_run = 0;
327 +int am_root = 0;
328 +int read_only = 0;
329 +int list_only = 0;
330 +int verbose = 0;
331  int preserve_perms = 0;
332  
333  int
334 --- old/tls.c
335 +++ new/tls.c
336 @@ -39,6 +39,7 @@
337  
338  /* These are to make syscall.o shut up. */
339  int dry_run = 0;
340 +int am_root = 0; /* TODO: add option to set this to -1. */
341  int read_only = 1;
342  int list_only = 0;
343  int preserve_perms = 0;
344 --- old/trimslash.c
345 +++ new/trimslash.c
346 @@ -23,6 +23,7 @@
347  
348  /* These are to make syscall.o shut up. */
349  int dry_run = 0;
350 +int am_root = 0;
351  int read_only = 1;
352  int list_only = 0;
353  int preserve_perms = 0;
354 --- old/xattr.c
355 +++ new/xattr.c
356 @@ -26,11 +26,15 @@
357  #ifdef SUPPORT_XATTRS
358  
359  extern int dry_run;
360 +extern int am_root;
361 +extern mode_t orig_umask;
362  extern unsigned int file_struct_len;
363  
364  #define RSYNC_XAL_INITIAL 5
365  #define RSYNC_XAL_LIST_INITIAL 100
366  
367 +#define FAKE_XATTR "user.rsync%stat"
368 +
369  typedef struct {
370         char *name;
371         char *datum;
372 @@ -130,9 +134,15 @@ static int rsync_xal_get(const char *fna
373         if (name_size == 0)
374                 return 0;
375         for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
376 -               rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
377 +               rsync_xa *rxas;
378  
379                 len = strlen(name) + 1;
380 +               if (am_root < 0 && len == sizeof FAKE_XATTR
381 +                && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
382 +                       continue;
383 +
384 +               rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
385 +
386                 datum_size = sys_lgetxattr(fname, name, NULL, 0);
387                 if (datum_size < 0) {
388                         if (errno == ENOTSUP)
389 @@ -285,10 +295,19 @@ void receive_xattr(struct file_struct *f
390                                 out_of_memory("receive_xattr");
391                         read_buf(f, ptr, name_len);
392                         read_buf(f, ptr + name_len, datum_len);
393 +
394 +                       if (am_root < 0 && name_len == sizeof FAKE_XATTR
395 +                        && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
396 +                               free(ptr);
397 +                               temp_xattr.count--;
398 +                               continue;
399 +                       }
400 +
401                         rxa->name_len = name_len;
402                         rxa->datum_len = datum_len;
403                         rxa->name = ptr;
404                         rxa->datum = ptr + name_len;
405 +
406  #ifdef HAVE_OSX_XATTRS
407                         if (strncmp(rxa->name, UNIQUE_PREFIX, UPRE_LEN) == 0) {
408                                 rxa->name_len -= UPRE_LEN;
409 @@ -365,4 +384,103 @@ int set_xattr(const char *fname, const s
410         return rsync_xal_set(fname, lst + ndx); /* TODO:  This needs to return 1 if no xattrs changed! */
411  }
412  
413 +int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *st)
414 +{
415 +       int mode, rdev_major, rdev_minor, uid, gid, len;
416 +       char buf[256];
417 +
418 +       if (fname)
419 +               len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
420 +       else
421 +               len = sys_fgetxattr(fd, FAKE_XATTR, buf, sizeof buf - 1);
422 +       if (len < 0 || len >= (int)sizeof buf) {
423 +               if (errno == ENOTSUP || errno == ENOATTR)
424 +                       return -1;
425 +               return -1;
426 +       }
427 +       buf[len] = '\0';
428 +
429 +       if (sscanf(buf, "%o %d,%d %d:%d",
430 +                  &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
431 +               errno = EINVAL;
432 +               return -1;
433 +       }
434 +
435 +       st->st_mode = mode;
436 +       st->st_rdev = MAKEDEV(rdev_major, rdev_minor);
437 +       st->st_uid = uid;
438 +       st->st_gid = gid;
439 +
440 +       return 0;
441 +}
442 +
443 +int set_stat_xattr(const char *fname, struct file_struct *file)
444 +{
445 +       STRUCT_STAT fst, xst;
446 +       dev_t rdev;
447 +       mode_t mode;
448 +
449 +       if (dry_run)
450 +               return 0;
451 +
452 +       if (x_stat(fname, &fst, &xst) < 0)
453 +               return -1;
454 +
455 +       if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
456 +               rdev = file->u.rdev;
457 +       else
458 +               rdev = 0;
459 +
460 +       /* Force the real file's mode to our liking. */
461 +       mode = (fst.st_mode & ~CHMOD_BITS)
462 +            | ((S_ISDIR(fst.st_mode) ? 0777 : 0666) & (~orig_umask | S_IRWXU));
463 +       if (fst.st_mode != mode)
464 +               do_chmod(fname, mode);
465 +       if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
466 +               fst.st_rdev = 0; /* just in case */
467 +
468 +       if (mode == file->mode && fst.st_rdev == rdev
469 +        && fst.st_uid == file->uid && fst.st_gid == file->gid) {
470 +               /* xst.st_mode will be 0 if there's no current stat xattr */
471 +               if (xst.st_mode && sys_lremovexattr(fname, FAKE_XATTR) < 0)
472 +                       return -2;
473 +               return 0;
474 +       }
475 +
476 +       if (xst.st_mode != file->mode || xst.st_rdev != rdev
477 +        || xst.st_uid != file->uid || xst.st_gid != file->gid) {
478 +               char buf[256];
479 +               int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
480 +                       (int)file->mode,
481 +                       (int)major(rdev), (int)minor(rdev),
482 +                       (int)file->uid, (int)file->gid);
483 +               return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0);
484 +       }
485 +       return 0;
486 +}
487 +
488 +int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
489 +{
490 +       int ret = do_stat(fname, fst);
491 +       if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
492 +               xst->st_mode = 0;
493 +       return ret;
494 +}
495 +
496 +int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
497 +{
498 +       int ret = do_lstat(fname, fst);
499 +       if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst)
500 +               xst->st_mode = 0;
501 +       return ret;
502 +}
503 +
504 +int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
505 +{
506 +       int ret = do_fstat(fd, fst);
507 +       if (get_stat_xattr(NULL, fd, xst? xst : fst) != 0 && xst)
508 +               xst->st_mode = 0;
509 +       return ret;
510 +}
511 +
512  #endif /* SUPPORT_XATTRS */