Commit | Line | Data |
---|---|---|
c44efccb WD |
1 | This patch adds a new option: --fake-super, which tells rsync to copy in a |
2 | fake super-user mode that stores various file attributes in an extended- | |
a864bc4f WD |
3 | attribute value instead of as real file-system attributes. See the changes |
4 | to the manpages for details. | |
c44efccb | 5 | |
03019e41 | 6 | To 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 */ |