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