- Tweaked the opening comments.
[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-
a864bc4f
WD
6attribute value instead of as real file-system attributes. See the changes
7to the manpages for details.
c44efccb
WD
8
9After applying this patch, run these commands for a successful build:
10
11 ./prepare-source
1e883fdf
WD
12 ./configure --enable-xattr-support
13 make
14
15or, if you want ACL support too:
16
17 ./prepare-source
18 ./configure --enable-acl-support --enable-xattr-support
c44efccb
WD
19 make
20
e66b9b1f
WD
21--- old/backup.c
22+++ new/backup.c
23@@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath)
24 if (p >= rel) {
25 /* Try to transfer the directory settings of the
26 * actual dir that the files are coming from. */
27- if (do_stat(rel, &sx.st) < 0) {
28+ if (x_stat(rel, &sx.st, NULL) < 0) {
29 rsyserr(FERROR, errno,
30 "make_bak_dir stat %s failed",
31 full_fname(rel));
32@@ -200,7 +200,7 @@ static int keep_backup(char *fname)
33 int ret_code;
c44efccb 34
e66b9b1f
WD
35 /* return if no file to keep */
36- if (do_lstat(fname, &sx.st) < 0)
37+ if (x_lstat(fname, &sx.st, NULL) < 0)
38 return 1;
39 #ifdef SUPPORT_ACLS
40 sx.acc_acl = sx.def_acl = NULL;
c44efccb
WD
41--- old/clientserver.c
42+++ new/clientserver.c
611f9f3c
WD
43@@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_
44 ret = parse_arguments(&argc, (const char ***) &argv, 0);
45 quiet = 0; /* Don't let someone try to be tricky. */
c44efccb 46
611f9f3c 47+ if (lp_fake_super(i))
c44efccb 48+ am_root = -1;
611f9f3c
WD
49+ else if (am_root < 0) /* Treat --fake-super from client as --super. */
50+ am_root = 2;
c44efccb 51+
611f9f3c
WD
52 if (filesfrom_fd == 0)
53 filesfrom_fd = f_in;
54
e66b9b1f
WD
55--- old/flist.c
56+++ new/flist.c
57@@ -181,7 +181,7 @@ static int readlink_stat(const char *pat
58 }
59 return 0;
60 #else
61- return do_stat(path, stp);
62+ return x_stat(path, stp, NULL);
63 #endif
64 }
65
66@@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S
67 {
68 #ifdef SUPPORT_LINKS
69 if (copy_links)
70- return do_stat(path, stp);
71- if (do_lstat(path, stp) < 0)
72+ return x_stat(path, stp, NULL);
73+ if (x_lstat(path, stp, NULL) < 0)
74 return -1;
75 if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
76 STRUCT_STAT st;
77- if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
78+ if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
79 *stp = st;
80 }
81 return 0;
82 #else
83- return do_stat(path, stp);
84+ return x_stat(path, stp, NULL);
85 #endif
86 }
87
1fdd9ea6
WD
88@@ -234,26 +234,6 @@ static int is_excluded(char *fname, int
89 return 0;
90 }
91
92-static int to_wire_mode(mode_t mode)
93-{
94-#ifdef SUPPORT_LINKS
95-#if _S_IFLNK != 0120000
96- if (S_ISLNK(mode))
97- return (mode & ~(_S_IFMT)) | 0120000;
98-#endif
99-#endif
100- return mode;
101-}
102-
103-static mode_t from_wire_mode(int mode)
104-{
105-#if _S_IFLNK != 0120000
106- if ((mode & (_S_IFMT)) == 0120000)
107- return (mode & ~(_S_IFMT)) | _S_IFLNK;
108-#endif
109- return mode;
110-}
111-
112 static void send_directory(int f, struct file_list *flist,
113 char *fbuf, int len);
114
115@@ -793,7 +773,7 @@ struct file_struct *make_file(char *fnam
e66b9b1f
WD
116 if (save_errno == ENOENT) {
117 #ifdef SUPPORT_LINKS
118 /* Avoid "vanished" error if symlink points nowhere. */
119- if (copy_links && do_lstat(thisname, &st) == 0
120+ if (copy_links && x_lstat(thisname, &st, NULL) == 0
121 && S_ISLNK(st.st_mode)) {
122 io_error |= IOERR_GENERAL;
123 rprintf(FERROR, "symlink has no referent: %s\n",
1fdd9ea6 124@@ -963,7 +943,7 @@ struct file_struct *make_file(char *fnam
e66b9b1f
WD
125 int save_mode = file->mode;
126 file->mode = S_IFDIR; /* Find a directory with our name. */
127 if (flist_find(the_file_list, file) >= 0
128- && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
129+ && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
130 file->modtime = st2.st_mtime;
131 file->length = st2.st_size;
132 file->mode = st2.st_mode;
c44efccb
WD
133--- old/loadparm.c
134+++ new/loadparm.c
135@@ -150,6 +150,7 @@ typedef struct
136 int syslog_facility;
137 int timeout;
138
139+ BOOL fake_super;
140 BOOL ignore_errors;
141 BOOL ignore_nonreadable;
142 BOOL list;
143@@ -197,6 +198,7 @@ static service sDefault =
144 /* syslog_facility; */ LOG_DAEMON,
145 /* timeout; */ 0,
146
147+ /* fake_super; */ False,
148 /* ignore_errors; */ False,
149 /* ignore_nonreadable; */ False,
150 /* list; */ True,
151@@ -298,6 +300,7 @@ static struct parm_struct parm_table[] =
152 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
153 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
154 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
155+ {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
156 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
157 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
158 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
159@@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
160 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
161 FN_LOCAL_INTEGER(lp_timeout, timeout)
162
163+FN_LOCAL_BOOL(lp_fake_super, fake_super)
164 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
165 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
166 FN_LOCAL_BOOL(lp_list, list)
167@@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals
168
169 if (pszFname)
170 pstrcpy(n2,pszFname);
171- else if (am_server && !am_root)
172+ else if (am_server && am_root <= 0)
173 pstrcpy(n2,RSYNCD_USERCONF);
174 else
175 pstrcpy(n2,RSYNCD_SYSCONF);
176--- old/options.c
177+++ new/options.c
178@@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION;
179 int sparse_files = 0;
180 int do_compression = 0;
181 int def_compress_level = Z_DEFAULT_COMPRESSION;
182-int am_root = 0;
e04211a5 183+int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
c44efccb
WD
184 int am_server = 0;
185 int am_sender = 0;
186 int am_generator = 0;
e04211a5 187@@ -329,6 +329,9 @@ void usage(enum logcode F)
c44efccb
WD
188 rprintf(F," -t, --times preserve times\n");
189 rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
190 rprintf(F," --super receiver attempts super-user activities\n");
e04211a5 191+#ifdef SUPPORT_XATTRS
7ac2aef2 192+ rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
e04211a5 193+#endif
c44efccb
WD
194 rprintf(F," -S, --sparse handle sparse files efficiently\n");
195 rprintf(F," -n, --dry-run show what would have been transferred\n");
196 rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
e04211a5 197@@ -453,6 +456,7 @@ static struct poptOption long_options[]
c44efccb
WD
198 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
199 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
200 {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
201+ {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
202 {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
203 {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
204 {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
e04211a5
WD
205@@ -1178,6 +1182,14 @@ int parse_arguments(int *argc, const cha
206 }
207 #endif
208
209+#ifndef SUPPORT_XATTRS
210+ if (am_root < 0) {
211+ snprintf(err_buf, sizeof err_buf,
212+ "--fake-super requires an rsync with extended attributes enabled\n");
213+ return 0;
214+ }
215+#endif
216+
217 if (write_batch && read_batch) {
218 snprintf(err_buf, sizeof err_buf,
219 "--write-batch and --read-batch can not be used together\n");
c44efccb
WD
220--- old/rsync.c
221+++ new/rsync.c
35d8f76e
WD
222@@ -49,7 +49,6 @@ extern int preserve_gid;
223 extern int inplace;
224 extern int keep_dirlinks;
225 extern int make_backups;
226-extern mode_t orig_umask;
227 extern struct stats stats;
228 extern struct chmod_mode_struct *daemon_chmod_modes;
229
230@@ -197,7 +196,9 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
231 (long)sxp->st.st_gid, (long)file->gid);
232 }
233 }
3ae9c1ee 234- if (do_lchown(fname,
e04211a5 235+ if (am_root < 0) {
3ae9c1ee 236+ ;
e04211a5 237+ } else if (do_lchown(fname,
c44efccb
WD
238 change_uid ? file->uid : sxp->st.st_uid,
239 change_gid ? file->gid : sxp->st.st_gid) != 0) {
3ae9c1ee 240 /* shouldn't have attempted to change uid or gid
35d8f76e 241@@ -206,7 +207,7 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
242 change_uid ? "chown" : "chgrp",
243 full_fname(fname));
244 goto cleanup;
245- }
246+ } else
247 /* a lchown had been done - we have to re-stat if the
248 * destination had the setuid or setgid bits set due
249 * to the side effect of the chown call */
1fdd9ea6
WD
250@@ -223,6 +224,8 @@ int set_file_attrs(char *fname, struct f
251 #ifdef SUPPORT_XATTRS
e66b9b1f
WD
252 if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
253 updated = 1;
1fdd9ea6
WD
254+ if (am_root < 0)
255+ set_stat_xattr(fname, file);
c44efccb 256 #endif
e66b9b1f
WD
257 #ifdef SUPPORT_ACLS
258 /* It's OK to call set_acl() now, even for a dir, as the generator
1fdd9ea6 259@@ -237,7 +240,7 @@ int set_file_attrs(char *fname, struct f
c44efccb 260
e66b9b1f
WD
261 #ifdef HAVE_CHMOD
262 if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) {
263- int ret = do_chmod(fname, new_mode);
9b91764c 264+ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
e66b9b1f
WD
265 if (ret < 0) {
266 rsyserr(FERROR, errno,
267 "failed to set permissions on %s",
1fdd9ea6
WD
268--- old/rsync.h
269+++ new/rsync.h
e04211a5 270@@ -707,6 +707,12 @@ typedef struct {
1fdd9ea6
WD
271
272 #include "proto.h"
273
274+#ifndef SUPPORT_XATTRS
275+#define x_stat(fn,fst,xst) do_stat(fn,fst)
276+#define x_lstat(fn,fst,xst) do_lstat(fn,fst)
277+#define x_fstat(fd,fst,xst) do_fstat(fd,fst)
278+#endif
279+
280 /* We have replacement versions of these if they're missing. */
281 #ifndef HAVE_ASPRINTF
282 int asprintf(char **ptr, const char *format, ...);
e04211a5 283@@ -924,3 +930,23 @@ int inet_pton(int af, const char *src, v
1fdd9ea6
WD
284 #ifdef MAINTAINER_MODE
285 const char *get_panic_action(void);
286 #endif
287+
288+static inline int to_wire_mode(mode_t mode)
289+{
290+#ifdef SUPPORT_LINKS
291+#if _S_IFLNK != 0120000
292+ if (S_ISLNK(mode))
293+ return (mode & ~(_S_IFMT)) | 0120000;
294+#endif
295+#endif
296+ return mode;
297+}
298+
299+static inline mode_t from_wire_mode(int mode)
300+{
301+#if _S_IFLNK != 0120000
302+ if ((mode & (_S_IFMT)) == 0120000)
303+ return (mode & ~(_S_IFMT)) | _S_IFLNK;
304+#endif
305+ return mode;
306+}
7ac2aef2
WD
307--- old/rsync.yo
308+++ new/rsync.yo
309@@ -333,6 +333,7 @@ to the detailed description below for a
310 -t, --times preserve times
311 -O, --omit-dir-times omit directories when preserving times
312 --super receiver attempts super-user activities
313+ --fake-super store/recover privileged attrs using xattrs
314 -S, --sparse handle sparse files efficiently
315 -n, --dry-run show what would have been transferred
316 -W, --whole-file copy files whole (without rsync algorithm)
e04211a5
WD
317@@ -846,7 +847,7 @@ permission value can be applied to the f
318 dit(bf(-o, --owner)) This option causes rsync to set the owner of the
319 destination file to be the same as the source file, but only if the
320 receiving rsync is being run as the super-user (see also the bf(--super)
321-option to force rsync to attempt super-user activities).
322+and bf(--fake-super) options).
323 Without this option, the owner is set to the invoking user on the
324 receiving side.
325
326@@ -869,7 +870,7 @@ default, but may fall back to using the
327 dit(bf(--devices)) This option causes rsync to transfer character and
328 block device files to the remote system to recreate these devices.
329 This option has no effect if the receiving rsync is not run as the
330-super-user and bf(--super) is not specified.
331+super-user (see also the bf(--super) and bf(--fake-super) options).
332
333 dit(bf(--specials)) This option causes rsync to transfer special files
334 such as named sockets and fifos.
335@@ -899,6 +900,33 @@ also for ensuring that you will get erro
7ac2aef2
WD
336 being running as the super-user. To turn off super-user activities, the
337 super-user can use bf(--no-super).
338
e04211a5
WD
339+dit(bf(--fake-super)) When this option is enabled, rsync simulates
340+super-user activities by saving/restoring the privileged attributes via a
341+special extended attribute that is attached to each file (as needed). This
342+includes the file's owner and group (if it is not the default), the file's
343+device info (device & special files are created as empty text files), and
344+any permission bits that we won't allow to be set on the real file (e.g.
345+the real file gets u-s,g-s,o-t for safety) or that would limit the owner's
346+access (since the real super-user can always access/change a file or
347+directory, the files we create can always be accessed/changed by the
348+creating user).
7ac2aef2
WD
349+
350+The bf(--fake-super) option only affects the side where the option is used.
351+To affect the remote side of a remote-shell connection, specify an rsync
352+path:
353+
354+quote(tt( rsync -av --rsync-path="rsync --fake-super" /src/ host:/dest/))
355+
e04211a5
WD
356+Since there is only one "side" in a local copy, this option affects both
357+the sending and recieving of files. You'll need to specify a copy using
358+"localhost" if you need to avoid this. Note, however, that it is always
359+safe to copy from some non-fake-super files into some fake-super files
360+using a local bf(--fake-super) command because the non-fake source files
361+will just have their normal attributes.
7ac2aef2
WD
362+
363+See also the "fake super" setting in the daemon's rsyncd.conf file.
e04211a5 364+This option is overridden by both bf(--super) and bf(--no-super).
7ac2aef2
WD
365+
366 dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take
367 up less space on the destination. Conflicts with bf(--inplace) because it's
368 not possible to overwrite data in a sparse fashion.
369--- old/rsyncd.conf.yo
370+++ new/rsyncd.conf.yo
371@@ -226,6 +226,11 @@ file transfers to and from that module s
372 was run as root. This complements the "uid" option. The default is gid -2,
373 which is normally the group "nobody".
374
375+dit(bf(fake super)) Setting "fake super = yes" for a module causes the
376+daemon side to behave as if the bf(--fake-user) command-line option had
377+been specified. This allows the full attributes of a file to be stored
378+without having to have the daemon actually running as root.
379+
380 dit(bf(filter)) The "filter" option allows you to specify a space-separated
381 list of filter rules that the daemon will not allow to be read or written.
382 This is only superficially equivalent to the client specifying these
c44efccb
WD
383--- old/syscall.c
384+++ new/syscall.c
e66b9b1f 385@@ -28,6 +28,7 @@
c44efccb
WD
386 #endif
387
388 extern int dry_run;
389+extern int am_root;
390 extern int read_only;
391 extern int list_only;
392 extern int preserve_perms;
e66b9b1f 393@@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode
c44efccb
WD
394 {
395 if (dry_run) return 0;
396 RETURN_ERROR_IF_RO_OR_LO;
397+
398+ /* For --fake-super, we create a normal file with mode 0600. */
399+ if (am_root < 0) {
400+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
401+ if (fd < 0 || close(fd) < 0)
402+ return -1;
403+ return 0;
404+ }
405+
406 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
407 if (S_ISFIFO(mode))
408 return mkfifo(pathname, mode);
c44efccb
WD
409--- old/t_unsafe.c
410+++ new/t_unsafe.c
411@@ -24,7 +24,11 @@
412
413 #include "rsync.h"
414
415-int dry_run, read_only, list_only, verbose;
416+int dry_run = 0;
417+int am_root = 0;
418+int read_only = 0;
419+int list_only = 0;
420+int verbose = 0;
421 int preserve_perms = 0;
422
423 int
424--- old/tls.c
425+++ new/tls.c
426@@ -39,6 +39,7 @@
427
428 /* These are to make syscall.o shut up. */
429 int dry_run = 0;
430+int am_root = 0; /* TODO: add option to set this to -1. */
431 int read_only = 1;
432 int list_only = 0;
433 int preserve_perms = 0;
434--- old/trimslash.c
435+++ new/trimslash.c
436@@ -23,6 +23,7 @@
437
438 /* These are to make syscall.o shut up. */
439 int dry_run = 0;
440+int am_root = 0;
441 int read_only = 1;
442 int list_only = 0;
443 int preserve_perms = 0;
444--- old/xattr.c
445+++ new/xattr.c
1fdd9ea6 446@@ -28,11 +28,15 @@
c44efccb 447 extern int dry_run;
1fdd9ea6
WD
448 extern int read_only;
449 extern int list_only;
c44efccb 450+extern int am_root;
e66b9b1f 451+extern mode_t orig_umask;
c44efccb
WD
452 extern unsigned int file_struct_len;
453
454 #define RSYNC_XAL_INITIAL 5
e66b9b1f
WD
455 #define RSYNC_XAL_LIST_INITIAL 100
456
457+#define FAKE_XATTR "user.rsync%stat"
458+
459 typedef struct {
460 char *name;
461 char *datum;
1fdd9ea6 462@@ -132,9 +136,15 @@ static int rsync_xal_get(const char *fna
c44efccb
WD
463 if (name_size == 0)
464 return 0;
465 for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) {
466- rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
467+ rsync_xa *rxas;
468
469 len = strlen(name) + 1;
470+ if (am_root < 0 && len == sizeof FAKE_XATTR
d30eff93 471+ && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0)
c44efccb
WD
472+ continue;
473+
474+ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
475+
476 datum_size = sys_lgetxattr(fname, name, NULL, 0);
477 if (datum_size < 0) {
478 if (errno == ENOTSUP)
1fdd9ea6 479@@ -287,10 +297,19 @@ void receive_xattr(struct file_struct *f
c44efccb
WD
480 out_of_memory("receive_xattr");
481 read_buf(f, ptr, name_len);
482 read_buf(f, ptr + name_len, datum_len);
483+
484+ if (am_root < 0 && name_len == sizeof FAKE_XATTR
d30eff93 485+ && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) {
c44efccb
WD
486+ free(ptr);
487+ temp_xattr.count--;
488+ continue;
489+ }
490+
491 rxa->name_len = name_len;
492 rxa->datum_len = datum_len;
493 rxa->name = ptr;
494 rxa->datum = ptr + name_len;
495+
496 #ifdef HAVE_OSX_XATTRS
1e883fdf
WD
497 if (strncmp(rxa->name, UNIQUE_PREFIX, UPRE_LEN) == 0) {
498 rxa->name_len -= UPRE_LEN;
1fdd9ea6 499@@ -372,4 +391,146 @@ int set_xattr(const char *fname, const s
e66b9b1f
WD
500 return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
501 }
502
1fdd9ea6 503+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
e66b9b1f
WD
504+{
505+ int mode, rdev_major, rdev_minor, uid, gid, len;
506+ char buf[256];
507+
e04211a5 508+ if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
1fdd9ea6
WD
509+ return -1;
510+
511+ if (xst)
512+ *xst = *fst;
e66b9b1f 513+ else
1fdd9ea6
WD
514+ xst = fst;
515+ if (fname) {
516+ fd = -1;
517+ len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1);
518+ } else {
519+ fname = "fd";
e66b9b1f 520+ len = sys_fgetxattr(fd, FAKE_XATTR, buf, sizeof buf - 1);
1fdd9ea6
WD
521+ }
522+ if (len >= (int)sizeof buf) {
523+ len = -1;
524+ errno = ERANGE;
525+ }
526+ if (len < 0) {
e66b9b1f
WD
527+ if (errno == ENOTSUP || errno == ENOATTR)
528+ return -1;
1fdd9ea6
WD
529+ if (errno == EPERM && S_ISLNK(fst->st_mode)) {
530+ xst->st_uid = 0;
531+ xst->st_gid = 0;
532+ return 0;
533+ }
534+ rsyserr(FERROR, errno, "failed to read xattr %s for %s",
535+ FAKE_XATTR, full_fname(fname));
e66b9b1f
WD
536+ return -1;
537+ }
538+ buf[len] = '\0';
539+
540+ if (sscanf(buf, "%o %d,%d %d:%d",
541+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
1fdd9ea6
WD
542+ rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
543+ FAKE_XATTR, full_fname(fname), buf);
544+ exit_cleanup(RERR_FILEIO);
e66b9b1f
WD
545+ }
546+
1fdd9ea6
WD
547+ xst->st_mode = from_wire_mode(mode);
548+ xst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
549+ xst->st_uid = uid;
550+ xst->st_gid = gid;
e66b9b1f
WD
551+
552+ return 0;
553+}
554+
555+int set_stat_xattr(const char *fname, struct file_struct *file)
556+{
557+ STRUCT_STAT fst, xst;
558+ dev_t rdev;
35d8f76e 559+ mode_t mode;
e66b9b1f
WD
560+
561+ if (dry_run)
562+ return 0;
563+
1fdd9ea6
WD
564+ if (read_only || list_only) {
565+ rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
566+ FAKE_XATTR, full_fname(fname));
e66b9b1f 567+ return -1;
1fdd9ea6
WD
568+ }
569+
570+ if (x_lstat(fname, &fst, &xst) < 0) {
571+ rsyserr(FERROR, errno, "failed to re-stat %s",
572+ full_fname(fname));
573+ return -1;
574+ }
e66b9b1f
WD
575+
576+ if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode))
577+ rdev = file->u.rdev;
578+ else
579+ rdev = 0;
580+
7ac2aef2
WD
581+ /* Dump the special permissions and enable full owner access. */
582+ mode = (fst.st_mode & ~CHMOD_BITS) | (file->mode & ACCESSPERMS)
583+ | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
35d8f76e
WD
584+ if (fst.st_mode != mode)
585+ do_chmod(fname, mode);
e66b9b1f
WD
586+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
587+ fst.st_rdev = 0; /* just in case */
588+
35d8f76e 589+ if (mode == file->mode && fst.st_rdev == rdev
e66b9b1f
WD
590+ && fst.st_uid == file->uid && fst.st_gid == file->gid) {
591+ /* xst.st_mode will be 0 if there's no current stat xattr */
1fdd9ea6
WD
592+ if (xst.st_mode && sys_lremovexattr(fname, FAKE_XATTR) < 0) {
593+ rsyserr(FERROR, errno,
594+ "delete of stat xattr failed for %s",
595+ full_fname(fname));
596+ return -1;
597+ }
e66b9b1f
WD
598+ return 0;
599+ }
600+
601+ if (xst.st_mode != file->mode || xst.st_rdev != rdev
602+ || xst.st_uid != file->uid || xst.st_gid != file->gid) {
603+ char buf[256];
604+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
e04211a5 605+ to_wire_mode(file->mode) & (_S_IFMT|CHMOD_BITS),
e66b9b1f
WD
606+ (int)major(rdev), (int)minor(rdev),
607+ (int)file->uid, (int)file->gid);
1fdd9ea6
WD
608+ if (sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0) < 0) {
609+ if (errno == EPERM && S_ISLNK(fst.st_mode))
610+ return 0;
611+ rsyserr(FERROR, errno,
612+ "failed to write xattr %s for %s",
613+ FAKE_XATTR, full_fname(fname));
614+ return -1;
615+ }
e66b9b1f 616+ }
1fdd9ea6 617+
e66b9b1f
WD
618+ return 0;
619+}
620+
621+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
622+{
623+ int ret = do_stat(fname, fst);
1fdd9ea6 624+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
625+ xst->st_mode = 0;
626+ return ret;
627+}
628+
629+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
630+{
631+ int ret = do_lstat(fname, fst);
1fdd9ea6 632+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
633+ xst->st_mode = 0;
634+ return ret;
635+}
636+
637+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
638+{
639+ int ret = do_fstat(fd, fst);
1fdd9ea6 640+ if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
e66b9b1f
WD
641+ xst->st_mode = 0;
642+ return ret;
643+}
644+
645 #endif /* SUPPORT_XATTRS */