Fixed failing hunks.
[rsync/rsync-patches.git] / fake-super.diff
CommitLineData
c44efccb
WD
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-
a864bc4f
WD
3attribute value instead of as real file-system attributes. See the changes
4to the manpages for details.
c44efccb 5
03019e41 6To use this patch, run these commands for a successful build:
c44efccb 7
03019e41 8 patch -p1 <patches/fake-super.diff
689f0001 9 ./configure (optional if already run)
1e883fdf
WD
10 make
11
e66b9b1f
WD
12--- old/backup.c
13+++ new/backup.c
689f0001 14@@ -127,7 +127,7 @@ static int make_bak_dir(char *fullpath)
e66b9b1f
WD
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));
689f0001 23@@ -200,7 +200,7 @@ static int keep_backup(const char *fname
e66b9b1f 24 int ret_code;
c44efccb 25
e66b9b1f
WD
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;
c44efccb
WD
32--- old/clientserver.c
33+++ new/clientserver.c
a302c048
WD
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;
c44efccb 37
611f9f3c 38+ if (lp_fake_super(i))
c44efccb 39+ am_root = -1;
611f9f3c
WD
40+ else if (am_root < 0) /* Treat --fake-super from client as --super. */
41+ am_root = 2;
c44efccb 42+
611f9f3c
WD
43 if (filesfrom_fd == 0)
44 filesfrom_fd = f_in;
45
e66b9b1f
WD
46--- old/flist.c
47+++ new/flist.c
689f0001 48@@ -196,12 +196,12 @@ static int readlink_stat(const char *pat
fc068916
WD
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 }
e66b9b1f
WD
55 }
56 return 0;
57 #else
58- return do_stat(path, stp);
59+ return x_stat(path, stp, NULL);
60 #endif
61 }
62
689f0001 63@@ -209,17 +209,17 @@ int link_stat(const char *path, STRUCT_S
e66b9b1f
WD
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
689f0001 85@@ -254,26 +254,6 @@ static int is_excluded(char *fname, int
1fdd9ea6
WD
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-
fc068916
WD
109 static void send_directory(int f, struct file_list *flist, int ndx,
110 char *fbuf, int len, int flags);
1fdd9ea6 111
689f0001 112@@ -954,7 +934,7 @@ struct file_struct *make_file(const char
e66b9b1f
WD
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",
689f0001 121@@ -1126,7 +1106,7 @@ struct file_struct *make_file(const char
e66b9b1f
WD
122 int save_mode = file->mode;
123 file->mode = S_IFDIR; /* Find a directory with our name. */
fc068916 124 if (flist_find(dir_flist, file) >= 0
e66b9b1f
WD
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;
3d3ad7f1 128 file->len32 = 0;
e66b9b1f 129 file->mode = st2.st_mode;
c44efccb
WD
130--- old/loadparm.c
131+++ new/loadparm.c
689f0001 132@@ -149,6 +149,7 @@ typedef struct
c44efccb
WD
133 int syslog_facility;
134 int timeout;
135
136+ BOOL fake_super;
137 BOOL ignore_errors;
138 BOOL ignore_nonreadable;
139 BOOL list;
689f0001 140@@ -196,6 +197,7 @@ static service sDefault =
c44efccb
WD
141 /* syslog_facility; */ LOG_DAEMON,
142 /* timeout; */ 0,
143
144+ /* fake_super; */ False,
145 /* ignore_errors; */ False,
146 /* ignore_nonreadable; */ False,
147 /* list; */ True,
689f0001 148@@ -297,6 +299,7 @@ static struct parm_struct parm_table[] =
c44efccb
WD
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},
689f0001 156@@ -411,6 +414,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max
c44efccb
WD
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)
689f0001 164@@ -814,7 +818,7 @@ BOOL lp_load(char *pszFname, int globals
c44efccb
WD
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
689f0001 175@@ -72,7 +72,7 @@ int protocol_version = PROTOCOL_VERSION;
c44efccb
WD
176 int sparse_files = 0;
177 int do_compression = 0;
178 int def_compress_level = Z_DEFAULT_COMPRESSION;
179-int am_root = 0;
e04211a5 180+int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
c44efccb
WD
181 int am_server = 0;
182 int am_sender = 0;
183 int am_generator = 0;
fc068916 184@@ -326,6 +326,9 @@ void usage(enum logcode F)
c44efccb
WD
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");
e04211a5 188+#ifdef SUPPORT_XATTRS
7ac2aef2 189+ rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
e04211a5 190+#endif
c44efccb
WD
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");
689f0001 194@@ -455,6 +458,7 @@ static struct poptOption long_options[]
c44efccb
WD
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 },
689f0001 202@@ -1187,6 +1191,14 @@ int parse_arguments(int *argc, const cha
e04211a5
WD
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");
c44efccb
WD
217--- old/rsync.c
218+++ new/rsync.c
689f0001
WD
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
70891d26 229 (long)sxp->st.st_gid, (long)F_GID(file));
c44efccb
WD
230 }
231 }
3ae9c1ee 232- if (do_lchown(fname,
e04211a5 233+ if (am_root < 0) {
3ae9c1ee 234+ ;
e04211a5 235+ } else if (do_lchown(fname,
70891d26
WD
236 change_uid ? F_UID(file) : sxp->st.st_uid,
237 change_gid ? F_GID(file) : sxp->st.st_gid) != 0) {
3ae9c1ee 238 /* shouldn't have attempted to change uid or gid
689f0001 239@@ -309,7 +313,7 @@ int set_file_attrs(const char *fname, st
c44efccb
WD
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 */
689f0001 248@@ -336,7 +340,7 @@ int set_file_attrs(const char *fname, st
c44efccb 249
e66b9b1f 250 #ifdef HAVE_CHMOD
3d3ad7f1 251 if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
e66b9b1f 252- int ret = do_chmod(fname, new_mode);
9b91764c 253+ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
e66b9b1f
WD
254 if (ret < 0) {
255 rsyserr(FERROR, errno,
256 "failed to set permissions on %s",
1fdd9ea6
WD
257--- old/rsync.h
258+++ new/rsync.h
689f0001 259@@ -805,6 +805,12 @@ typedef struct {
1fdd9ea6
WD
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, ...);
689f0001 272@@ -1023,6 +1029,26 @@ int inet_pton(int af, const char *src, v
1fdd9ea6
WD
273 const char *get_panic_action(void);
274 #endif
c44bcb0d 275
1fdd9ea6
WD
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+}
c44bcb0d
WD
295+
296 static inline int
297 isDigit(const char *ptr)
298 {
7ac2aef2
WD
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)
689f0001 309@@ -859,7 +860,7 @@ permission value can be applied to the f
e04211a5
WD
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
689f0001 318@@ -882,7 +883,7 @@ default, but may fall back to using the
e04211a5
WD
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.
e6838bd9 327@@ -912,6 +913,34 @@ also for ensuring that you will get erro
7ac2aef2
WD
328 being running as the super-user. To turn off super-user activities, the
329 super-user can use bf(--no-super).
330
e04211a5
WD
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).
7ac2aef2
WD
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+
e04211a5
WD
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.
7ac2aef2 354+
e04211a5 355+This option is overridden by both bf(--super) and bf(--no-super).
e6838bd9
WD
356+
357+See also the "fake super" setting in the daemon's rsyncd.conf file.
7ac2aef2
WD
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
c44efccb
WD
376--- old/syscall.c
377+++ new/syscall.c
689f0001 378@@ -27,6 +27,7 @@
c44efccb
WD
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;
689f0001 386@@ -78,6 +79,15 @@ int do_mknod(const char *pathname, mode_
c44efccb
WD
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);
c44efccb
WD
402--- old/t_unsafe.c
403+++ new/t_unsafe.c
689f0001 404@@ -23,7 +23,11 @@
c44efccb
WD
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;
3f24a3a8 423+int am_root = 0;
c44efccb
WD
424 int read_only = 1;
425 int list_only = 0;
426 int preserve_perms = 0;
427--- old/trimslash.c
428+++ new/trimslash.c
689f0001 429@@ -22,6 +22,7 @@
c44efccb
WD
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;
689f0001
WD
437--- old/xattrs.c
438+++ new/xattrs.c
439@@ -53,11 +53,16 @@ extern int checksum_seed;
651a8b74 440 #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
e66b9b1f 441
651a8b74 442 #ifdef HAVE_LINUX_XATTRS
d1e711ce 443-#define RPRE_LEN 0
cf5738ec 444+#define MIGHT_NEED_RPRE (am_root < 0)
d1e711ce 445+#define RSYNC_PREFIX USER_PREFIX "rsync."
651a8b74 446 #else
cf5738ec 447+#define MIGHT_NEED_RPRE am_root
d1e711ce
WD
448 #define RSYNC_PREFIX "rsync."
449-#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
651a8b74 450 #endif
d1e711ce
WD
451+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
452+
453+#define XSTAT_ATTR RSYNC_PREFIX "%stat"
651a8b74 454+#define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1)
d1e711ce 455
e66b9b1f 456 typedef struct {
651a8b74 457 char *datum, *name;
689f0001 458@@ -218,6 +223,10 @@ static int rsync_xal_get(const char *fna
3f24a3a8
WD
459 continue;
460 #endif
c44efccb 461
9b90e0b3 462+ if (am_root < 0 && name_len == XSTAT_LEN + 1
cf5738ec 463+ && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
c44efccb
WD
464+ continue;
465+
689f0001
WD
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
651a8b74 473+#ifdef HAVE_LINUX_XATTRS
d1e711ce
WD
474+ if (am_root < 0 && name_len > RPRE_LEN
475+ && HAS_PREFIX(name, RSYNC_PREFIX)) {
476+ name += RPRE_LEN;
477+ name_len -= RPRE_LEN;
651a8b74
WD
478+ }
479+#endif
689f0001 480+
3f24a3a8 481 rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
689f0001
WD
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;
cf5738ec 488-#ifdef HAVE_LINUX_XATTRS
689f0001 489- size_t extra_len = 0;
cf5738ec 490-#else
689f0001
WD
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 */
651a8b74 495-#endif
689f0001
WD
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 }
651a8b74 501 #ifdef HAVE_LINUX_XATTRS
689f0001
WD
502 /* Non-root can only save the user namespace. */
503- if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
504- free(ptr);
505- continue;
cf5738ec
WD
506+ if (am_root <= 0 && !HAS_PREFIX(name, USER_PREFIX)) {
507+ if (!am_root) {
508+ free(ptr);
509+ continue;
510+ }
d1e711ce 511+ name -= RPRE_LEN;
cf5738ec 512+ name_len += RPRE_LEN;
d1e711ce 513+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
689f0001 514 }
cf5738ec 515 #else
689f0001
WD
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 }
3f24a3a8 520 #endif
689f0001
WD
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);
e66b9b1f
WD
531 }
532
1fdd9ea6 533+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
e66b9b1f
WD
534+{
535+ int mode, rdev_major, rdev_minor, uid, gid, len;
536+ char buf[256];
537+
e04211a5 538+ if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
1fdd9ea6
WD
539+ return -1;
540+
541+ if (xst)
542+ *xst = *fst;
e66b9b1f 543+ else
1fdd9ea6
WD
544+ xst = fst;
545+ if (fname) {
546+ fd = -1;
651a8b74 547+ len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
1fdd9ea6
WD
548+ } else {
549+ fname = "fd";
651a8b74 550+ len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
1fdd9ea6
WD
551+ }
552+ if (len >= (int)sizeof buf) {
553+ len = -1;
554+ errno = ERANGE;
555+ }
556+ if (len < 0) {
e66b9b1f
WD
557+ if (errno == ENOTSUP || errno == ENOATTR)
558+ return -1;
1fdd9ea6
WD
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",
651a8b74 565+ XSTAT_ATTR, full_fname(fname));
e66b9b1f
WD
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) {
1fdd9ea6 572+ rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
651a8b74 573+ XSTAT_ATTR, full_fname(fname), buf);
1fdd9ea6 574+ exit_cleanup(RERR_FILEIO);
e66b9b1f
WD
575+ }
576+
1fdd9ea6
WD
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;
e66b9b1f
WD
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;
c2f699cc 589+ mode_t mode, fmode;
e66b9b1f
WD
590+
591+ if (dry_run)
592+ return 0;
593+
1fdd9ea6
WD
594+ if (read_only || list_only) {
595+ rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
651a8b74 596+ XSTAT_ATTR, full_fname(fname));
e66b9b1f 597+ return -1;
1fdd9ea6
WD
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+ }
e66b9b1f 605+
c2f699cc
WD
606+ fst.st_mode &= (_S_IFMT | CHMOD_BITS);
607+ fmode = file->mode & (_S_IFMT | CHMOD_BITS);
608+
8a3d5938
WD
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
e66b9b1f
WD
613+ rdev = 0;
614+
7ac2aef2 615+ /* Dump the special permissions and enable full owner access. */
c2f699cc 616+ mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
7ac2aef2 617+ | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
35d8f76e
WD
618+ if (fst.st_mode != mode)
619+ do_chmod(fname, mode);
e66b9b1f
WD
620+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
621+ fst.st_rdev = 0; /* just in case */
622+
c2f699cc 623+ if (mode == fmode && fst.st_rdev == rdev
70891d26 624+ && fst.st_uid == F_UID(file) && fst.st_gid == F_GID(file)) {
e66b9b1f 625+ /* xst.st_mode will be 0 if there's no current stat xattr */
651a8b74 626+ if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
1fdd9ea6
WD
627+ rsyserr(FERROR, errno,
628+ "delete of stat xattr failed for %s",
629+ full_fname(fname));
630+ return -1;
631+ }
e66b9b1f
WD
632+ return 0;
633+ }
634+
c2f699cc 635+ if (xst.st_mode != fmode || xst.st_rdev != rdev
70891d26 636+ || xst.st_uid != F_UID(file) || xst.st_gid != F_GID(file)) {
e66b9b1f
WD
637+ char buf[256];
638+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
c2f699cc 639+ to_wire_mode(fmode),
e66b9b1f 640+ (int)major(rdev), (int)minor(rdev),
70891d26 641+ (int)F_UID(file), (int)F_GID(file));
651a8b74 642+ if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) {
1fdd9ea6
WD
643+ if (errno == EPERM && S_ISLNK(fst.st_mode))
644+ return 0;
645+ rsyserr(FERROR, errno,
646+ "failed to write xattr %s for %s",
651a8b74 647+ XSTAT_ATTR, full_fname(fname));
1fdd9ea6
WD
648+ return -1;
649+ }
e66b9b1f 650+ }
1fdd9ea6 651+
e66b9b1f
WD
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);
1fdd9ea6 658+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
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);
1fdd9ea6 666+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
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);
1fdd9ea6 674+ if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
e66b9b1f
WD
675+ xst->st_mode = 0;
676+ return ret;
677+}
678+
679 #endif /* SUPPORT_XATTRS */