Got rid of some accumulated patch fuzz.
[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.
689f0001 327@@ -912,6 +913,33 @@ 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
WD
354+
355+See also the "fake super" setting in the daemon's rsyncd.conf file.
e04211a5 356+This option is overridden by both bf(--super) and bf(--no-super).
7ac2aef2
WD
357+
358 dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take
359 up less space on the destination. Conflicts with bf(--inplace) because it's
360 not possible to overwrite data in a sparse fashion.
361--- old/rsyncd.conf.yo
362+++ new/rsyncd.conf.yo
363@@ -226,6 +226,11 @@ file transfers to and from that module s
364 was run as root. This complements the "uid" option. The default is gid -2,
365 which is normally the group "nobody".
366
367+dit(bf(fake super)) Setting "fake super = yes" for a module causes the
368+daemon side to behave as if the bf(--fake-user) command-line option had
369+been specified. This allows the full attributes of a file to be stored
370+without having to have the daemon actually running as root.
371+
372 dit(bf(filter)) The "filter" option allows you to specify a space-separated
373 list of filter rules that the daemon will not allow to be read or written.
374 This is only superficially equivalent to the client specifying these
c44efccb
WD
375--- old/syscall.c
376+++ new/syscall.c
689f0001 377@@ -27,6 +27,7 @@
c44efccb
WD
378 #endif
379
380 extern int dry_run;
381+extern int am_root;
382 extern int read_only;
383 extern int list_only;
384 extern int preserve_perms;
689f0001 385@@ -78,6 +79,15 @@ int do_mknod(const char *pathname, mode_
c44efccb
WD
386 {
387 if (dry_run) return 0;
388 RETURN_ERROR_IF_RO_OR_LO;
389+
390+ /* For --fake-super, we create a normal file with mode 0600. */
391+ if (am_root < 0) {
392+ int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
393+ if (fd < 0 || close(fd) < 0)
394+ return -1;
395+ return 0;
396+ }
397+
398 #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO
399 if (S_ISFIFO(mode))
400 return mkfifo(pathname, mode);
c44efccb
WD
401--- old/t_unsafe.c
402+++ new/t_unsafe.c
689f0001 403@@ -23,7 +23,11 @@
c44efccb
WD
404
405 #include "rsync.h"
406
407-int dry_run, read_only, list_only, verbose;
408+int dry_run = 0;
409+int am_root = 0;
410+int read_only = 0;
411+int list_only = 0;
412+int verbose = 0;
413 int preserve_perms = 0;
414
415 int
416--- old/tls.c
417+++ new/tls.c
418@@ -39,6 +39,7 @@
419
420 /* These are to make syscall.o shut up. */
421 int dry_run = 0;
3f24a3a8 422+int am_root = 0;
c44efccb
WD
423 int read_only = 1;
424 int list_only = 0;
425 int preserve_perms = 0;
426--- old/trimslash.c
427+++ new/trimslash.c
689f0001 428@@ -22,6 +22,7 @@
c44efccb
WD
429
430 /* These are to make syscall.o shut up. */
431 int dry_run = 0;
432+int am_root = 0;
433 int read_only = 1;
434 int list_only = 0;
435 int preserve_perms = 0;
689f0001
WD
436--- old/xattrs.c
437+++ new/xattrs.c
438@@ -53,11 +53,16 @@ extern int checksum_seed;
651a8b74 439 #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
e66b9b1f 440
651a8b74 441 #ifdef HAVE_LINUX_XATTRS
d1e711ce 442-#define RPRE_LEN 0
cf5738ec 443+#define MIGHT_NEED_RPRE (am_root < 0)
d1e711ce 444+#define RSYNC_PREFIX USER_PREFIX "rsync."
651a8b74 445 #else
cf5738ec 446+#define MIGHT_NEED_RPRE am_root
d1e711ce
WD
447 #define RSYNC_PREFIX "rsync."
448-#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
651a8b74 449 #endif
d1e711ce
WD
450+#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1)
451+
452+#define XSTAT_ATTR RSYNC_PREFIX "%stat"
651a8b74 453+#define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1)
d1e711ce 454
e66b9b1f 455 typedef struct {
651a8b74 456 char *datum, *name;
689f0001 457@@ -218,6 +223,10 @@ static int rsync_xal_get(const char *fna
3f24a3a8
WD
458 continue;
459 #endif
c44efccb 460
9b90e0b3 461+ if (am_root < 0 && name_len == XSTAT_LEN + 1
cf5738ec 462+ && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
c44efccb
WD
463+ continue;
464+
689f0001
WD
465 datum_len = name_len; /* Pass extra size to get_xattr_data() */
466 if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
467 return -1;
468@@ -236,6 +245,14 @@ static int rsync_xal_get(const char *fna
469 } else
470 name_offset = datum_len;
471
651a8b74 472+#ifdef HAVE_LINUX_XATTRS
d1e711ce
WD
473+ if (am_root < 0 && name_len > RPRE_LEN
474+ && HAS_PREFIX(name, RSYNC_PREFIX)) {
475+ name += RPRE_LEN;
476+ name_len -= RPRE_LEN;
651a8b74
WD
477+ }
478+#endif
689f0001 479+
3f24a3a8 480 rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
689f0001
WD
481 rxas->name = ptr + name_offset;
482 memcpy(rxas->name, name, name_len);
483@@ -576,13 +593,9 @@ void receive_xattr(struct file_struct *f
484 size_t name_len = read_abbrevint(f);
485 size_t datum_len = read_abbrevint(f);
486 size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
cf5738ec 487-#ifdef HAVE_LINUX_XATTRS
689f0001 488- size_t extra_len = 0;
cf5738ec 489-#else
689f0001
WD
490- size_t extra_len = am_root ? RPRE_LEN : 0;
491+ size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
492 if (dget_len + extra_len < dget_len)
493 out_of_memory("receive_xattr"); /* overflow */
651a8b74 494-#endif
689f0001
WD
495 if (dget_len + extra_len + name_len < dget_len)
496 out_of_memory("receive_xattr"); /* overflow */
497 ptr = new_array(char, dget_len + extra_len + name_len);
498@@ -598,9 +611,14 @@ void receive_xattr(struct file_struct *f
499 }
651a8b74 500 #ifdef HAVE_LINUX_XATTRS
689f0001
WD
501 /* Non-root can only save the user namespace. */
502- if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) {
503- free(ptr);
504- continue;
cf5738ec
WD
505+ if (am_root <= 0 && !HAS_PREFIX(name, USER_PREFIX)) {
506+ if (!am_root) {
507+ free(ptr);
508+ continue;
509+ }
d1e711ce 510+ name -= RPRE_LEN;
cf5738ec 511+ name_len += RPRE_LEN;
d1e711ce 512+ memcpy(name, RSYNC_PREFIX, RPRE_LEN);
689f0001 513 }
cf5738ec 514 #else
689f0001
WD
515 /* This OS only has a user namespace, so we either
516@@ -618,6 +636,11 @@ void receive_xattr(struct file_struct *f
517 continue;
518 }
3f24a3a8 519 #endif
689f0001
WD
520+ if (am_root < 0 && name_len == XSTAT_LEN + 1
521+ && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0) {
522+ free(ptr);
523+ continue;
524+ }
525 rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1);
526 rxa->name = name;
527 rxa->datum = ptr;
528@@ -772,4 +795,150 @@ int set_xattr(const char *fname, const s
529 return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp);
e66b9b1f
WD
530 }
531
1fdd9ea6 532+int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
e66b9b1f
WD
533+{
534+ int mode, rdev_major, rdev_minor, uid, gid, len;
535+ char buf[256];
536+
e04211a5 537+ if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))
1fdd9ea6
WD
538+ return -1;
539+
540+ if (xst)
541+ *xst = *fst;
e66b9b1f 542+ else
1fdd9ea6
WD
543+ xst = fst;
544+ if (fname) {
545+ fd = -1;
651a8b74 546+ len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1);
1fdd9ea6
WD
547+ } else {
548+ fname = "fd";
651a8b74 549+ len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1);
1fdd9ea6
WD
550+ }
551+ if (len >= (int)sizeof buf) {
552+ len = -1;
553+ errno = ERANGE;
554+ }
555+ if (len < 0) {
e66b9b1f
WD
556+ if (errno == ENOTSUP || errno == ENOATTR)
557+ return -1;
1fdd9ea6
WD
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",
651a8b74 564+ XSTAT_ATTR, full_fname(fname));
e66b9b1f
WD
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) {
1fdd9ea6 571+ rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n",
651a8b74 572+ XSTAT_ATTR, full_fname(fname), buf);
1fdd9ea6 573+ exit_cleanup(RERR_FILEIO);
e66b9b1f
WD
574+ }
575+
1fdd9ea6
WD
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;
e66b9b1f
WD
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;
c2f699cc 588+ mode_t mode, fmode;
e66b9b1f
WD
589+
590+ if (dry_run)
591+ return 0;
592+
1fdd9ea6
WD
593+ if (read_only || list_only) {
594+ rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
651a8b74 595+ XSTAT_ATTR, full_fname(fname));
e66b9b1f 596+ return -1;
1fdd9ea6
WD
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+ }
e66b9b1f 604+
c2f699cc
WD
605+ fst.st_mode &= (_S_IFMT | CHMOD_BITS);
606+ fmode = file->mode & (_S_IFMT | CHMOD_BITS);
607+
8a3d5938
WD
608+ if (IS_DEVICE(fmode) || IS_SPECIAL(fmode)) {
609+ uint32 *devp = F_RDEV_P(file);
610+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
611+ } else
e66b9b1f
WD
612+ rdev = 0;
613+
7ac2aef2 614+ /* Dump the special permissions and enable full owner access. */
c2f699cc 615+ mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
7ac2aef2 616+ | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
35d8f76e
WD
617+ if (fst.st_mode != mode)
618+ do_chmod(fname, mode);
e66b9b1f
WD
619+ if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode))
620+ fst.st_rdev = 0; /* just in case */
621+
c2f699cc 622+ if (mode == fmode && fst.st_rdev == rdev
70891d26 623+ && fst.st_uid == F_UID(file) && fst.st_gid == F_GID(file)) {
e66b9b1f 624+ /* xst.st_mode will be 0 if there's no current stat xattr */
651a8b74 625+ if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
1fdd9ea6
WD
626+ rsyserr(FERROR, errno,
627+ "delete of stat xattr failed for %s",
628+ full_fname(fname));
629+ return -1;
630+ }
e66b9b1f
WD
631+ return 0;
632+ }
633+
c2f699cc 634+ if (xst.st_mode != fmode || xst.st_rdev != rdev
70891d26 635+ || xst.st_uid != F_UID(file) || xst.st_gid != F_GID(file)) {
e66b9b1f
WD
636+ char buf[256];
637+ int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u",
c2f699cc 638+ to_wire_mode(fmode),
e66b9b1f 639+ (int)major(rdev), (int)minor(rdev),
70891d26 640+ (int)F_UID(file), (int)F_GID(file));
651a8b74 641+ if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) {
1fdd9ea6
WD
642+ if (errno == EPERM && S_ISLNK(fst.st_mode))
643+ return 0;
644+ rsyserr(FERROR, errno,
645+ "failed to write xattr %s for %s",
651a8b74 646+ XSTAT_ATTR, full_fname(fname));
1fdd9ea6
WD
647+ return -1;
648+ }
e66b9b1f 649+ }
1fdd9ea6 650+
e66b9b1f
WD
651+ return 0;
652+}
653+
654+int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
655+{
656+ int ret = do_stat(fname, fst);
1fdd9ea6 657+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
658+ xst->st_mode = 0;
659+ return ret;
660+}
661+
662+int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
663+{
664+ int ret = do_lstat(fname, fst);
1fdd9ea6 665+ if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
e66b9b1f
WD
666+ xst->st_mode = 0;
667+ return ret;
668+}
669+
670+int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
671+{
672+ int ret = do_fstat(fd, fst);
1fdd9ea6 673+ if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
e66b9b1f
WD
674+ xst->st_mode = 0;
675+ return ret;
676+}
677+
678 #endif /* SUPPORT_XATTRS */