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