Annotation of src/usr.bin/rsync/receiver.c, Revision 1.16
1.16 ! florian 1: /* $Id: receiver.c,v 1.15 2019/02/16 10:49:37 florian Exp $ */
1.1 benno 2:
3: /*
4: * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
1.15 florian 5: * Copyright (c) 2019 Florian Obser <florian@openbsd.org>
1.1 benno 6: *
7: * Permission to use, copy, modify, and distribute this software for any
8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
10: *
11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <sys/mman.h>
20: #include <sys/stat.h>
21:
22: #include <assert.h>
23: #include <errno.h>
24: #include <fcntl.h>
25: #include <inttypes.h>
26: #include <math.h>
27: #include <poll.h>
28: #include <stdio.h>
29: #include <stdlib.h>
30: #include <string.h>
31: #include <time.h>
32: #include <unistd.h>
33:
34: #include "extern.h"
35:
36: enum pfdt {
37: PFD_SENDER_IN = 0, /* input from the sender */
38: PFD_UPLOADER_IN, /* uploader input from a local file */
39: PFD_DOWNLOADER_IN, /* downloader input from a local file */
40: PFD_SENDER_OUT, /* output to the sender */
41: PFD__MAX
42: };
1.7 florian 43:
44: int
45: rsync_set_metadata(struct sess *sess, int newfile,
46: int fd, const struct flist *f, const char *path)
47: {
1.8 florian 48: uid_t uid = (uid_t)-1;
49: gid_t gid = (gid_t)-1;
1.7 florian 50: struct timespec tv[2];
51:
52: /*
53: * Conditionally adjust identifiers.
54: * If we have an EPERM, report it but continue on: this just
55: * means that we're mapping into an unknown (or disallowed)
56: * group identifier.
57: */
58:
1.8 florian 59: if (getuid() == 0 && sess->opts->preserve_uids)
60: uid = f->st.uid;
61: if (sess->opts->preserve_gids)
62: gid = f->st.gid;
63:
64: if (uid != (uid_t)-1 || gid != (gid_t)-1) {
1.7 florian 65: if (fchown(fd, uid, gid) == -1) {
66: if (errno != EPERM) {
67: ERR(sess, "%s: fchown", path);
68: return 0;
69: }
70: WARNX(sess, "%s: identity unknown or not available "
71: "to user.group: %u.%u", f->path, uid, gid);
72: } else
73: LOG4(sess, "%s: updated uid and/or gid", f->path);
74: }
75:
76: /* Conditionally adjust file modification time. */
77:
78: if (sess->opts->preserve_times) {
1.11 deraadt 79: struct timeval now;
80:
81: gettimeofday(&now, NULL);
82: TIMEVAL_TO_TIMESPEC(&now, &tv[0]);
1.7 florian 83: tv[1].tv_sec = f->st.mtime;
84: tv[1].tv_nsec = 0;
85: if (futimens(fd, tv) == -1) {
86: ERR(sess, "%s: futimens", path);
87: return 0;
88: }
89: LOG4(sess, "%s: updated date", f->path);
1.13 florian 90: }
91:
92: /* Conditionally adjust file permissions. */
93:
94: if (newfile || sess->opts->preserve_perms) {
95: if (fchmod(fd, f->st.mode) == -1) {
96: ERR(sess, "%s: fchmod", path);
97: return 0;
98: }
99: LOG4(sess, "%s: updated permissions", f->path);
1.7 florian 100: }
101:
102: return 1;
103: }
1.1 benno 104:
1.14 florian 105: int
106: rsync_set_metadata_at(struct sess *sess, int newfile, int rootfd,
107: const struct flist *f, const char *path)
108: {
109: uid_t uid = (uid_t)-1;
110: gid_t gid = (gid_t)-1;
111: struct timespec tv[2];
112:
113: /*
114: * Conditionally adjust identifiers.
115: * If we have an EPERM, report it but continue on: this just
116: * means that we're mapping into an unknown (or disallowed)
117: * group identifier.
118: */
119:
120: if (getuid() == 0 && sess->opts->preserve_uids)
121: uid = f->st.uid;
122: if (sess->opts->preserve_gids)
123: gid = f->st.gid;
124:
125: if (uid != (uid_t)-1 || gid != (gid_t)-1) {
126: if (fchownat(rootfd, path, uid, gid, AT_SYMLINK_NOFOLLOW) ==
127: -1) {
128: if (errno != EPERM) {
129: ERR(sess, "%s: fchownat", path);
130: return 0;
131: }
132: WARNX(sess, "%s: identity unknown or not available "
133: "to user.group: %u.%u", f->path, uid, gid);
134: } else
135: LOG4(sess, "%s: updated uid and/or gid", f->path);
136: }
137:
138: /* Conditionally adjust file modification time. */
139:
140: if (sess->opts->preserve_times) {
141: struct timeval now;
142:
143: gettimeofday(&now, NULL);
144: TIMEVAL_TO_TIMESPEC(&now, &tv[0]);
145: tv[1].tv_sec = f->st.mtime;
146: tv[1].tv_nsec = 0;
147: if (utimensat(rootfd, path, tv, AT_SYMLINK_NOFOLLOW) == -1) {
148: ERR(sess, "%s: utimensat", path);
149: return 0;
150: }
151: LOG4(sess, "%s: updated date", f->path);
152: }
153:
154: /* Conditionally adjust file permissions. */
155:
156: if (newfile || sess->opts->preserve_perms) {
157: if (fchmodat(rootfd, path, f->st.mode, AT_SYMLINK_NOFOLLOW) ==
158: -1) {
159: ERR(sess, "%s: fchmodat", path);
160: return 0;
161: }
162: LOG4(sess, "%s: updated permissions", f->path);
163: }
164:
165: return 1;
166: }
167:
1.2 benno 168: /*
1.14 florian 169: * Pledges: unveil, unix, rpath, cpath, wpath, stdio, fattr, chown.
170: * Pledges (dry-run): -unix, -cpath, -wpath, -fattr, -chown.
1.1 benno 171: */
172: int
1.9 deraadt 173: rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root)
1.1 benno 174: {
175: struct flist *fl = NULL, *dfl = NULL;
176: size_t i, flsz = 0, dflsz = 0, excl;
177: char *tofree;
178: int rc = 0, dfd = -1, phase = 0, c;
1.2 benno 179: int32_t ioerror;
1.1 benno 180: struct pollfd pfd[PFD__MAX];
181: struct download *dl = NULL;
1.2 benno 182: struct upload *ul = NULL;
1.1 benno 183: mode_t oumask;
184:
1.14 florian 185: if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) {
1.1 benno 186: ERR(sess, "pledge");
187: goto out;
188: }
189:
190: /* Client sends zero-length exclusions. */
191:
1.4 deraadt 192: if (!sess->opts->server &&
193: !io_write_int(sess, fdout, 0)) {
1.1 benno 194: ERRX1(sess, "io_write_int");
195: goto out;
196: }
197:
198: if (sess->opts->server && sess->opts->del) {
1.4 deraadt 199: if (!io_read_size(sess, fdin, &excl)) {
1.1 benno 200: ERRX1(sess, "io_read_size");
201: goto out;
202: } else if (0 != excl) {
203: ERRX(sess, "exclusion list is non-empty");
204: goto out;
205: }
206: }
207:
208: /*
209: * Start by receiving the file list and our mystery number.
210: * These we're going to be touching on our local system.
211: */
212:
1.4 deraadt 213: if (!flist_recv(sess, fdin, &fl, &flsz)) {
1.1 benno 214: ERRX1(sess, "flist_recv");
215: goto out;
1.2 benno 216: }
217:
1.1 benno 218: /* The IO error is sent after the file list. */
219:
1.4 deraadt 220: if (!io_read_int(sess, fdin, &ioerror)) {
1.1 benno 221: ERRX1(sess, "io_read_int");
222: goto out;
223: } else if (0 != ioerror) {
224: ERRX1(sess, "io_error is non-zero");
225: goto out;
226: }
227:
1.5 deraadt 228: if (flsz == 0 && !sess->opts->server) {
1.1 benno 229: WARNX(sess, "receiver has empty file list: exiting");
230: rc = 1;
231: goto out;
1.4 deraadt 232: } else if (!sess->opts->server)
1.1 benno 233: LOG1(sess, "Transfer starting: %zu files", flsz);
234:
235: LOG2(sess, "%s: receiver destination", root);
236:
237: /*
238: * Create the path for our destination directory, if we're not
239: * in dry-run mode (which would otherwise crash w/the pledge).
240: * This uses our current umask: we might set the permissions on
241: * this directory in post_dir().
242: */
243:
1.4 deraadt 244: if (!sess->opts->dry_run) {
1.5 deraadt 245: if ((tofree = strdup(root)) == NULL) {
1.1 benno 246: ERR(sess, "strdup");
247: goto out;
248: } else if (mkpath(sess, tofree) < 0) {
249: ERRX1(sess, "%s: mkpath", root);
250: free(tofree);
251: goto out;
252: }
253: free(tofree);
254: }
255:
256: /*
257: * Disable umask() so we can set permissions fully.
258: * Then open the directory iff we're not in dry_run.
259: */
260:
261: oumask = umask(0);
262:
1.4 deraadt 263: if (!sess->opts->dry_run) {
1.1 benno 264: dfd = open(root, O_RDONLY | O_DIRECTORY, 0);
1.5 deraadt 265: if (dfd == -1) {
1.1 benno 266: ERR(sess, "%s: open", root);
267: goto out;
268: }
269: }
270:
271: /*
272: * Begin by conditionally getting all files we have currently
273: * available in our destination.
274: */
275:
1.2 benno 276: if (sess->opts->del &&
1.1 benno 277: sess->opts->recursive &&
1.4 deraadt 278: !flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
1.1 benno 279: ERRX1(sess, "flist_gen_local");
280: goto out;
281: }
282:
283: /*
284: * Make our entire view of the file-system be limited to what's
285: * in the root directory.
286: * This prevents us from accidentally (or "under the influence")
287: * writing into other parts of the file-system.
288: */
289:
1.5 deraadt 290: if (unveil(root, "rwc") == -1) {
1.1 benno 291: ERR(sess, "%s: unveil", root);
292: goto out;
1.5 deraadt 293: } else if (unveil(NULL, NULL) == -1) {
1.1 benno 294: ERR(sess, "%s: unveil", root);
295: goto out;
296: }
297:
298: /* If we have a local set, go for the deletion. */
299:
1.4 deraadt 300: if (!flist_del(sess, dfd, dfl, dflsz)) {
1.1 benno 301: ERRX1(sess, "flist_del");
302: goto out;
303: }
304:
305: /* Initialise poll events to listen from the sender. */
306:
307: pfd[PFD_SENDER_IN].fd = fdin;
308: pfd[PFD_UPLOADER_IN].fd = -1;
309: pfd[PFD_DOWNLOADER_IN].fd = -1;
310: pfd[PFD_SENDER_OUT].fd = fdout;
311:
312: pfd[PFD_SENDER_IN].events = POLLIN;
313: pfd[PFD_UPLOADER_IN].events = POLLIN;
314: pfd[PFD_DOWNLOADER_IN].events = POLLIN;
315: pfd[PFD_SENDER_OUT].events = POLLOUT;
316:
1.14 florian 317: ul = upload_alloc(sess, root, dfd, fdout,
1.1 benno 318: CSUM_LENGTH_PHASE1, fl, flsz, oumask);
1.5 deraadt 319: if (ul == NULL) {
1.1 benno 320: ERRX1(sess, "upload_alloc");
321: goto out;
322: }
323:
324: dl = download_alloc(sess, fdin, fl, flsz, dfd);
1.5 deraadt 325: if (dl == NULL) {
1.1 benno 326: ERRX1(sess, "download_alloc");
327: goto out;
328: }
329:
330: LOG2(sess, "%s: ready for phase 1 data", root);
331:
332: for (;;) {
1.16 ! florian 333: if ((c = poll(pfd, PFD__MAX, POLL_TIMEOUT)) == -1) {
1.1 benno 334: ERR(sess, "poll");
1.16 ! florian 335: goto out;
! 336: } else if (c == 0) {
! 337: ERRX(sess, "poll: timeout");
1.1 benno 338: goto out;
1.2 benno 339: }
1.1 benno 340:
1.2 benno 341: for (i = 0; i < PFD__MAX; i++)
1.1 benno 342: if (pfd[i].revents & (POLLERR|POLLNVAL)) {
343: ERRX(sess, "poll: bad fd");
344: goto out;
345: } else if (pfd[i].revents & POLLHUP) {
346: ERRX(sess, "poll: hangup");
347: goto out;
348: }
349:
350: /*
351: * If we have a read event and we're multiplexing, we
352: * might just have error messages in the pipe.
353: * It's important to flush these out so that we don't
354: * clog the pipe.
355: * Unset our polling status if there's nothing that
356: * remains in the pipe.
357: */
358:
359: if (sess->mplex_reads &&
360: (POLLIN & pfd[PFD_SENDER_IN].revents)) {
1.4 deraadt 361: if (!io_read_flush(sess, fdin)) {
1.1 benno 362: ERRX1(sess, "io_read_flush");
363: goto out;
1.5 deraadt 364: } else if (sess->mplex_read_remain == 0)
1.1 benno 365: pfd[PFD_SENDER_IN].revents &= ~POLLIN;
366: }
367:
368:
369: /*
370: * We run the uploader if we have files left to examine
371: * (i < flsz) or if we have a file that we've opened and
372: * is read to mmap.
373: */
374:
375: if ((POLLIN & pfd[PFD_UPLOADER_IN].revents) ||
376: (POLLOUT & pfd[PFD_SENDER_OUT].revents)) {
1.2 benno 377: c = rsync_uploader(ul,
378: &pfd[PFD_UPLOADER_IN].fd,
1.1 benno 379: sess, &pfd[PFD_SENDER_OUT].fd);
380: if (c < 0) {
381: ERRX1(sess, "rsync_uploader");
382: goto out;
383: }
384: }
385:
1.2 benno 386: /*
1.1 benno 387: * We need to run the downloader when we either have
388: * read events from the sender or an asynchronous local
389: * open is ready.
390: * XXX: we don't disable PFD_SENDER_IN like with the
391: * uploader because we might stop getting error
392: * messages, which will otherwise clog up the pipes.
393: */
394:
1.2 benno 395: if ((POLLIN & pfd[PFD_SENDER_IN].revents) ||
1.1 benno 396: (POLLIN & pfd[PFD_DOWNLOADER_IN].revents)) {
1.2 benno 397: c = rsync_downloader(dl, sess,
1.1 benno 398: &pfd[PFD_DOWNLOADER_IN].fd);
399: if (c < 0) {
400: ERRX1(sess, "rsync_downloader");
401: goto out;
1.5 deraadt 402: } else if (c == 0) {
403: assert(phase == 0);
1.1 benno 404: phase++;
405: LOG2(sess, "%s: receiver ready "
406: "for phase 2 data", root);
407: break;
408: }
409:
410: /*
411: * FIXME: if we have any errors during the
412: * download, most notably files getting out of
413: * sync between the send and the receiver, then
414: * here we should bump our checksum length and
415: * go into the second phase.
416: */
1.2 benno 417: }
1.1 benno 418: }
419:
420: /* Properly close us out by progressing through the phases. */
421:
1.5 deraadt 422: if (phase == 1) {
1.4 deraadt 423: if (!io_write_int(sess, fdout, -1)) {
1.1 benno 424: ERRX1(sess, "io_write_int");
425: goto out;
1.4 deraadt 426: } else if (!io_read_int(sess, fdin, &ioerror)) {
1.1 benno 427: ERRX1(sess, "io_read_int");
428: goto out;
1.5 deraadt 429: } else if (ioerror != -1) {
1.1 benno 430: ERRX(sess, "expected phase ack");
431: goto out;
432: }
433: }
434:
435: /*
436: * Now all of our transfers are complete, so we can fix up our
437: * directory permissions.
438: */
439:
1.4 deraadt 440: if (!rsync_uploader_tail(ul, sess)) {
1.1 benno 441: ERRX1(sess, "rsync_uploader_tail");
442: goto out;
443: }
444:
445: /* Process server statistics and say good-bye. */
446:
1.4 deraadt 447: if (!sess_stats_recv(sess, fdin)) {
1.1 benno 448: ERRX1(sess, "sess_stats_recv");
449: goto out;
1.4 deraadt 450: } else if (!io_write_int(sess, fdout, -1)) {
1.1 benno 451: ERRX1(sess, "io_write_int");
452: goto out;
453: }
454:
455: LOG2(sess, "receiver finished updating");
456: rc = 1;
457: out:
1.5 deraadt 458: if (dfd != -1)
1.1 benno 459: close(dfd);
460: upload_free(ul);
461: download_free(dl);
462: flist_free(fl, flsz);
463: flist_free(dfl, dflsz);
464: return rc;
465: }