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