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