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
WD
8 patch -p1 <patches/acls.diff
9 patch -p1 <patches/xattrs.diff
10 patch -p1 <patches/fake-super.diff
c44efccb 11 ./prepare-source
1e883fdf
WD
12 ./configure --enable-xattr-support
13 make
14
03019e41
WD
15If you want ACL support too, use this configure command instead of the one
16above:
1e883fdf 17
1e883fdf 18 ./configure --enable-acl-support --enable-xattr-support
c44efccb 19
e66b9b1f
WD
20--- old/backup.c
21+++ new/backup.c
3d3ad7f1 22@@ -128,7 +128,7 @@ static int make_bak_dir(char *fullpath)
e66b9b1f
WD
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));
3d3ad7f1 31@@ -199,7 +199,7 @@ static int keep_backup(const char *fname
e66b9b1f 32 int ret_code;
c44efccb 33
e66b9b1f
WD
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;
c44efccb
WD
40--- old/clientserver.c
41+++ new/clientserver.c
3d3ad7f1 42@@ -627,6 +627,11 @@ static int rsync_module(int f_in, int f_
611f9f3c
WD
43 ret = parse_arguments(&argc, (const char ***) &argv, 0);
44 quiet = 0; /* Don't let someone try to be tricky. */
c44efccb 45
611f9f3c 46+ if (lp_fake_super(i))
c44efccb 47+ am_root = -1;
611f9f3c
WD
48+ else if (am_root < 0) /* Treat --fake-super from client as --super. */
49+ am_root = 2;
c44efccb 50+
611f9f3c
WD
51 if (filesfrom_fd == 0)
52 filesfrom_fd = f_in;
53
e66b9b1f
WD
54--- old/flist.c
55+++ new/flist.c
3d3ad7f1 56@@ -193,7 +193,7 @@ static int readlink_stat(const char *pat
e66b9b1f
WD
57 }
58 return 0;
59 #else
60- return do_stat(path, stp);
61+ return x_stat(path, stp, NULL);
62 #endif
63 }
64
3d3ad7f1 65@@ -201,17 +201,17 @@ int link_stat(const char *path, STRUCT_S
e66b9b1f
WD
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
3d3ad7f1 87@@ -246,26 +246,6 @@ static int is_excluded(char *fname, int
1fdd9ea6
WD
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
8a3d5938 114@@ -877,7 +857,7 @@ struct file_struct *make_file(const char
e66b9b1f
WD
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",
8a3d5938 123@@ -1050,7 +1030,7 @@ struct file_struct *make_file(const char
e66b9b1f
WD
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;
3d3ad7f1 130 file->len32 = 0;
e66b9b1f 131 file->mode = st2.st_mode;
c44efccb
WD
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)
c44bcb0d 166@@ -815,7 +819,7 @@ BOOL lp_load(char *pszFname, int globals
c44efccb
WD
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;
e04211a5 182+int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
c44efccb
WD
183 int am_server = 0;
184 int am_sender = 0;
185 int am_generator = 0;
8a3d5938 186@@ -325,6 +325,9 @@ void usage(enum logcode F)
c44efccb
WD
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");
e04211a5 190+#ifdef SUPPORT_XATTRS
7ac2aef2 191+ rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
e04211a5 192+#endif
c44efccb
WD
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");
8a3d5938 196@@ -450,6 +453,7 @@ static struct poptOption long_options[]
c44efccb
WD
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 },
8a3d5938 204@@ -1176,6 +1180,14 @@ int parse_arguments(int *argc, const cha
e04211a5
WD
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");
c44efccb
WD
219--- old/rsync.c
220+++ new/rsync.c
70891d26
WD
221@@ -249,7 +249,9 @@ int set_file_attrs(char *fname, struct f
222 (long)sxp->st.st_gid, (long)F_GID(file));
c44efccb
WD
223 }
224 }
3ae9c1ee 225- if (do_lchown(fname,
e04211a5 226+ if (am_root < 0) {
3ae9c1ee 227+ ;
e04211a5 228+ } else if (do_lchown(fname,
70891d26
WD
229 change_uid ? F_UID(file) : sxp->st.st_uid,
230 change_gid ? F_GID(file) : sxp->st.st_gid) != 0) {
3ae9c1ee 231 /* shouldn't have attempted to change uid or gid
70891d26 232@@ -258,7 +260,7 @@ int set_file_attrs(char *fname, struct f
c44efccb
WD
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 */
70891d26 241@@ -275,6 +277,8 @@ int set_file_attrs(char *fname, struct f
1fdd9ea6 242 #ifdef SUPPORT_XATTRS
e66b9b1f
WD
243 if (preserve_xattrs && set_xattr(fname, file, sxp) == 0)
244 updated = 1;
1fdd9ea6
WD
245+ if (am_root < 0)
246+ set_stat_xattr(fname, file);
c44efccb 247 #endif
e66b9b1f
WD
248 #ifdef SUPPORT_ACLS
249 /* It's OK to call set_acl() now, even for a dir, as the generator
70891d26 250@@ -289,7 +293,7 @@ int set_file_attrs(char *fname, struct f
c44efccb 251
e66b9b1f 252 #ifdef HAVE_CHMOD
3d3ad7f1 253 if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
e66b9b1f 254- int ret = do_chmod(fname, new_mode);
9b91764c 255+ int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
e66b9b1f
WD
256 if (ret < 0) {
257 rsyserr(FERROR, errno,
258 "failed to set permissions on %s",
1fdd9ea6
WD
259--- old/rsync.h
260+++ new/rsync.h
8a3d5938 261@@ -783,6 +783,12 @@ typedef struct {
1fdd9ea6
WD
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, ...);
8a3d5938 274@@ -1001,6 +1007,26 @@ int inet_pton(int af, const char *src, v
1fdd9ea6
WD
275 const char *get_panic_action(void);
276 #endif
c44bcb0d 277
1fdd9ea6
WD
278+static inline int to_wire_mode(mode_t mode)
279+{
280+#ifdef SUPPORT_LINKS
281+#if _S_IFLNK != 0120000
282+ if (S_ISLNK(mode))
283+ return (mode & ~(_S_IFMT)) | 0120000;
284+#endif
285+#endif
286+ return mode;
287+}
288+
289+static inline mode_t from_wire_mode(int mode)
290+{
291+#if _S_IFLNK != 0120000
292+ if ((mode & (_S_IFMT)) == 0120000)
293+ return (mode & ~(_S_IFMT)) | _S_IFLNK;
294+#endif
295+ return mode;
296+}
c44bcb0d
WD
297+
298 static inline int
299 isDigit(const char *ptr)
300 {
7ac2aef2
WD
301--- old/rsync.yo
302+++ new/rsync.yo
303@@ -333,6 +333,7 @@ to the detailed description below for a
304 -t, --times preserve times
305 -O, --omit-dir-times omit directories when preserving times
306 --super receiver attempts super-user activities
307+ --fake-super store/recover privileged attrs using xattrs
308 -S, --sparse handle sparse files efficiently
309 -n, --dry-run show what would have been transferred
310 -W, --whole-file copy files whole (without rsync algorithm)
8a3d5938 311@@ -846,7 +847,7 @@ permission value can be applied to the f
e04211a5
WD
312 dit(bf(-o, --owner)) This option causes rsync to set the owner of the
313 destination file to be the same as the source file, but only if the
314 receiving rsync is being run as the super-user (see also the bf(--super)
315-option to force rsync to attempt super-user activities).
316+and bf(--fake-super) options).
317 Without this option, the owner is set to the invoking user on the
318 receiving side.
319
8a3d5938 320@@ -869,7 +870,7 @@ default, but may fall back to using the
e04211a5
WD
321 dit(bf(--devices)) This option causes rsync to transfer character and
322 block device files to the remote system to recreate these devices.
323 This option has no effect if the receiving rsync is not run as the
324-super-user and bf(--super) is not specified.
325+super-user (see also the bf(--super) and bf(--fake-super) options).
326
327 dit(bf(--specials)) This option causes rsync to transfer special files
328 such as named sockets and fifos.
8a3d5938 329@@ -899,6 +900,33 @@ also for ensuring that you will get erro
7ac2aef2
WD
330 being running as the super-user. To turn off super-user activities, the
331 super-user can use bf(--no-super).
332
e04211a5
WD
333+dit(bf(--fake-super)) When this option is enabled, rsync simulates
334+super-user activities by saving/restoring the privileged attributes via a
335+special extended attribute that is attached to each file (as needed). This
336+includes the file's owner and group (if it is not the default), the file's
337+device info (device & special files are created as empty text files), and
338+any permission bits that we won't allow to be set on the real file (e.g.
339+the real file gets u-s,g-s,o-t for safety) or that would limit the owner's
340+access (since the real super-user can always access/change a file or
341+directory, the files we create can always be accessed/changed by the
342+creating user).
7ac2aef2
WD
343+
344+The bf(--fake-super) option only affects the side where the option is used.
345+To affect the remote side of a remote-shell connection, specify an rsync
346+path:
347+
348+quote(tt( rsync -av --rsync-path="rsync --fake-super" /src/ host:/dest/))
349+
e04211a5
WD
350+Since there is only one "side" in a local copy, this option affects both
351+the sending and recieving of files. You'll need to specify a copy using
352+"localhost" if you need to avoid this. Note, however, that it is always
353+safe to copy from some non-fake-super files into some fake-super files
354+using a local bf(--fake-super) command because the non-fake source files
355+will just have their normal attributes.
7ac2aef2
WD
356+
357+See also the "fake super" setting in the daemon's rsyncd.conf file.
e04211a5 358+This option is overridden by both bf(--super) and bf(--no-super).
7ac2aef2
WD
359+
360 dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take
361 up less space on the destination. Conflicts with bf(--inplace) because it's
362 not possible to overwrite data in a sparse fashion.
363--- old/rsyncd.conf.yo
364+++ new/rsyncd.conf.yo
365@@ -226,6 +226,11 @@ file transfers to and from that module s
366 was run as root. This complements the "uid" option. The default is gid -2,
367 which is normally the group "nobody".
368
369+dit(bf(fake super)) Setting "fake super = yes" for a module causes the
370+daemon side to behave as if the bf(--fake-user) command-line option had
371+been specified. This allows the full attributes of a file to be stored
372+without having to have the daemon actually running as root.
373+
374 dit(bf(filter)) The "filter" option allows you to specify a space-separated
375 list of filter rules that the daemon will not allow to be read or written.
376 This is only superficially equivalent to the client specifying these
c44efccb
WD
377--- old/syscall.c
378+++ new/syscall.c
e66b9b1f 379@@ -28,6 +28,7 @@
c44efccb
WD
380 #endif
381
382 extern int dry_run;
383+extern int am_root;
384 extern int read_only;
385 extern int list_only;
386 extern int preserve_perms;
70891d26 387@@ -79,6 +80,15 @@ int do_mknod(const char *pathname, mode_
c44efccb
WD
388 {
389 if (dry_run) return 0;
390 RETURN_ERROR_IF_RO_OR_LO;
391+
392+ /* For --fake-super, we create a normal file with mode 0600. */
393+ if (am_root < 0) {
394+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
395+ if (fd < 0 || close(fd) < 0)
396+ return -1;
397+ return 0;
398+ }
399+
400 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
401 if (S_ISFIFO(mode))
402 return mkfifo(pathname, mode);
c44efccb
WD
403--- old/t_unsafe.c
404+++ new/t_unsafe.c
405@@ -24,7 +24,11 @@
406
407 #include "rsync.h"
408
409-int dry_run, read_only, list_only, verbose;
410+int dry_run = 0;
411+int am_root = 0;
412+int read_only = 0;
413+int list_only = 0;
414+int verbose = 0;
415 int preserve_perms = 0;
416
417 int
418--- old/tls.c
419+++ new/tls.c
420@@ -39,6 +39,7 @@
421
422 /* These are to make syscall.o shut up. */
423 int dry_run = 0;
3f24a3a8 424+int am_root = 0;
c44efccb
WD
425 int read_only = 1;
426 int list_only = 0;
427 int preserve_perms = 0;
428--- old/trimslash.c
429+++ new/trimslash.c
430@@ -23,6 +23,7 @@
431
432 /* These are to make syscall.o shut up. */
433 int dry_run = 0;
434+int am_root = 0;
435 int read_only = 1;
436 int list_only = 0;
437 int preserve_perms = 0;
438--- old/xattr.c
439+++ new/xattr.c
8a3d5938 440@@ -42,11 +42,16 @@ extern int list_only;
651a8b74 441 #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
e66b9b1f 442
651a8b74 443 #ifdef HAVE_LINUX_XATTRS
d1e711ce 444-#define RPRE_LEN 0
cf5738ec 445+#define MIGHT_NEED_RPRE (am_root < 0)
d1e711ce 446+#define RSYNC_PREFIX USER_PREFIX "rsync."
651a8b74 447 #else
cf5738ec 448+#define MIGHT_NEED_RPRE am_root
d1e711ce
WD
449 #define RSYNC_PREFIX "rsync."
450-#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
651a8b74 451 #endif
d1e711ce
WD
452+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
453+
454+#define XSTAT_ATTR RSYNC_PREFIX "%stat"
651a8b74 455+#define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1)
d1e711ce 456
e66b9b1f 457 typedef struct {
651a8b74 458 char *datum, *name;
8a3d5938 459@@ -147,6 +152,10 @@ static int rsync_xal_get(const char *fna
3f24a3a8
WD
460 continue;
461 #endif
c44efccb 462
9b90e0b3 463+ if (am_root < 0 && name_len == XSTAT_LEN + 1
cf5738ec 464+ && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
c44efccb
WD
465+ continue;
466+
651a8b74
WD
467 datum_len = sys_lgetxattr(fname, name, NULL, 0);
468 if (datum_len < 0) {
469 if (errno == ENOTSUP)
8a3d5938 470@@ -176,6 +185,13 @@ static int rsync_xal_get(const char *fna
651a8b74
WD
471 return -1;
472 }
473 }
474+#ifdef HAVE_LINUX_XATTRS
d1e711ce
WD
475+ if (am_root < 0 && name_len > RPRE_LEN
476+ && HAS_PREFIX(name, RSYNC_PREFIX)) {
477+ name += RPRE_LEN;
478+ name_len -= RPRE_LEN;
651a8b74
WD
479+ }
480+#endif
3f24a3a8 481 rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
651a8b74 482 rxas->name = ptr + datum_len;
78698a67 483 rxas->datum = ptr;
8a3d5938 484@@ -296,13 +312,9 @@ void receive_xattr(struct file_struct *f
cf5738ec 485 rsync_xa *rxa;
651a8b74
WD
486 size_t name_len = read_int(f);
487 size_t datum_len = read_int(f);
cf5738ec
WD
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;
d1e711ce 493 if (datum_len + extra_len < datum_len)
651a8b74
WD
494 out_of_memory("receive_xattr"); /* overflow */
495-#endif
d1e711ce
WD
496 if (name_len + datum_len + extra_len < name_len)
497 out_of_memory("receive_xattr"); /* overflow */
498 ptr = new_array(char, name_len + datum_len + extra_len);
8a3d5938 499@@ -313,9 +325,14 @@ void receive_xattr(struct file_struct *f
651a8b74
WD
500 read_buf(f, ptr, datum_len);
501 #ifdef HAVE_LINUX_XATTRS
502 /* Non-root can only save the user namespace. */
cf5738ec
WD
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+ }
d1e711ce 511+ name -= RPRE_LEN;
cf5738ec 512+ name_len += RPRE_LEN;
d1e711ce 513+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
cf5738ec
WD
514 }
515 #else
516 /* This OS only has a user namespace, so we either
8a3d5938 517@@ -333,6 +350,12 @@ void receive_xattr(struct file_struct *f
d1e711ce 518 continue;
3f24a3a8
WD
519 }
520 #endif
9b90e0b3 521+ if (am_root < 0 && name_len == XSTAT_LEN + 1
cf5738ec
WD
522+ && name[RPRE_LEN] == '%'
523+ && strcmp(name, XSTAT_ATTR) == 0) {
c44efccb 524+ free(ptr);
c44efccb
WD
525+ continue;
526+ }
651a8b74
WD
527 rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
528 rxa->name = name;
78698a67 529 rxa->datum = ptr;
8a3d5938 530@@ -410,4 +433,150 @@ int set_xattr(const char *fname, const s
e66b9b1f
WD
531 return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */
532 }
533
1fdd9ea6 534+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
e66b9b1f
WD
535+{
536+ int mode, rdev_major, rdev_minor, uid, gid, len;
537+ char buf[256];
538+
e04211a5 539+ if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
1fdd9ea6
WD
540+ return -1;
541+
542+ if (xst)
543+ *xst = *fst;
e66b9b1f 544+ else
1fdd9ea6
WD
545+ xst = fst;
546+ if (fname) {
547+ fd = -1;
651a8b74 548+ len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
1fdd9ea6
WD
549+ } else {
550+ fname = "fd";
651a8b74 551+ len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
1fdd9ea6
WD
552+ }
553+ if (len >= (int)sizeof buf) {
554+ len = -1;
555+ errno = ERANGE;
556+ }
557+ if (len < 0) {
e66b9b1f
WD
558+ if (errno == ENOTSUP || errno == ENOATTR)
559+ return -1;
1fdd9ea6
WD
560+ if (errno == EPERM && S_ISLNK(fst->st_mode)) {
561+ xst->st_uid = 0;
562+ xst->st_gid = 0;
563+ return 0;
564+ }
565+ rsyserr(FERROR, errno, "failed to read xattr %s for %s",
651a8b74 566+ XSTAT_ATTR, full_fname(fname));
e66b9b1f
WD
567+ return -1;
568+ }
569+ buf[len] = '\0';
570+
571+ if (sscanf(buf, "%o %d,%d %d:%d",
572+ &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) {
1fdd9ea6 573+ rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
651a8b74 574+ XSTAT_ATTR, full_fname(fname), buf);
1fdd9ea6 575+ exit_cleanup(RERR_FILEIO);
e66b9b1f
WD
576+ }
577+
1fdd9ea6
WD
578+ xst->st_mode = from_wire_mode(mode);
579+ xst->st_rdev = MAKEDEV(rdev_major, rdev_minor);
580+ xst->st_uid = uid;
581+ xst->st_gid = gid;
e66b9b1f
WD
582+
583+ return 0;
584+}
585+
586+int set_stat_xattr(const char *fname, struct file_struct *file)
587+{
588+ STRUCT_STAT fst, xst;
589+ dev_t rdev;
c2f699cc 590+ mode_t mode, fmode;
e66b9b1f
WD
591+
592+ if (dry_run)
593+ return 0;
594+
1fdd9ea6
WD
595+ if (read_only || list_only) {
596+ rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
651a8b74 597+ XSTAT_ATTR, full_fname(fname));
e66b9b1f 598+ return -1;
1fdd9ea6
WD
599+ }
600+
601+ if (x_lstat(fname, &fst, &xst) < 0) {
602+ rsyserr(FERROR, errno, "failed to re-stat %s",
603+ full_fname(fname));
604+ return -1;
605+ }
e66b9b1f 606+
c2f699cc
WD
607+ fst.st_mode &= (_S_IFMT | CHMOD_BITS);
608+ fmode = file->mode & (_S_IFMT | CHMOD_BITS);
609+
8a3d5938
WD
610+ if (IS_DEVICE(fmode) || IS_SPECIAL(fmode)) {
611+ uint32 *devp = F_RDEV_P(file);
612+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
613+ } else
e66b9b1f
WD
614+ rdev = 0;
615+
7ac2aef2 616+ /* Dump the special permissions and enable full owner access. */
c2f699cc 617+ mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
7ac2aef2 618+ | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
35d8f76e
WD
619+ if (fst.st_mode != mode)
620+ do_chmod(fname, mode);
e66b9b1f
WD
621+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
622+ fst.st_rdev = 0; /* just in case */
623+
c2f699cc 624+ if (mode == fmode && fst.st_rdev == rdev
70891d26 625+ && fst.st_uid == F_UID(file) && fst.st_gid == F_GID(file)) {
e66b9b1f 626+ /* xst.st_mode will be 0 if there's no current stat xattr */
651a8b74 627+ if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
1fdd9ea6
WD
628+ rsyserr(FERROR, errno,
629+ "delete of stat xattr failed for %s",
630+ full_fname(fname));
631+ return -1;
632+ }
e66b9b1f
WD
633+ return 0;
634+ }
635+
c2f699cc 636+ if (xst.st_mode != fmode || xst.st_rdev != rdev
70891d26 637+ || xst.st_uid != F_UID(file) || xst.st_gid != F_GID(file)) {
e66b9b1f
WD
638+ char buf[256];
639+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
c2f699cc 640+ to_wire_mode(fmode),
e66b9b1f 641+ (int)major(rdev), (int)minor(rdev),
70891d26 642+ (int)F_UID(file), (int)F_GID(file));
651a8b74 643+ if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) {
1fdd9ea6
WD
644+ if (errno == EPERM && S_ISLNK(fst.st_mode))
645+ return 0;
646+ rsyserr(FERROR, errno,
647+ "failed to write xattr %s for %s",
651a8b74 648+ XSTAT_ATTR, full_fname(fname));
1fdd9ea6
WD
649+ return -1;
650+ }
e66b9b1f 651+ }
1fdd9ea6 652+
e66b9b1f
WD
653+ return 0;
654+}
655+
656+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
657+{
658+ int ret = do_stat(fname, fst);
1fdd9ea6 659+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
660+ xst->st_mode = 0;
661+ return ret;
662+}
663+
664+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
665+{
666+ int ret = do_lstat(fname, fst);
1fdd9ea6 667+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
668+ xst->st_mode = 0;
669+ return ret;
670+}
671+
672+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
673+{
674+ int ret = do_fstat(fd, fst);
1fdd9ea6 675+ if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
e66b9b1f
WD
676+ xst->st_mode = 0;
677+ return ret;
678+}
679+
680 #endif /* SUPPORT_XATTRS */