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