Annotation of src/usr.bin/rsync/receiver.c, Revision 1.1
1.1 ! benno 1: /* $Id$ */
! 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: };
! 42:
! 43: /*
! 44: * Pledges: unveil, rpath, cpath, wpath, stdio, fattr.
! 45: * Pledges (dry-run): -cpath, -wpath, -fattr.
! 46: */
! 47: int
! 48: rsync_receiver(struct sess *sess,
! 49: int fdin, int fdout, const char *root)
! 50: {
! 51: struct flist *fl = NULL, *dfl = NULL;
! 52: size_t i, flsz = 0, dflsz = 0, excl;
! 53: char *tofree;
! 54: int rc = 0, dfd = -1, phase = 0, c;
! 55: int32_t ioerror;
! 56: struct pollfd pfd[PFD__MAX];
! 57: struct download *dl = NULL;
! 58: struct upload *ul = NULL;
! 59: mode_t oumask;
! 60:
! 61: if (-1 == pledge("unveil rpath cpath wpath stdio fattr", NULL)) {
! 62: ERR(sess, "pledge");
! 63: goto out;
! 64: }
! 65:
! 66: /* Client sends zero-length exclusions. */
! 67:
! 68: if ( ! sess->opts->server &&
! 69: ! io_write_int(sess, fdout, 0)) {
! 70: ERRX1(sess, "io_write_int");
! 71: goto out;
! 72: }
! 73:
! 74: if (sess->opts->server && sess->opts->del) {
! 75: if ( ! io_read_size(sess, fdin, &excl)) {
! 76: ERRX1(sess, "io_read_size");
! 77: goto out;
! 78: } else if (0 != excl) {
! 79: ERRX(sess, "exclusion list is non-empty");
! 80: goto out;
! 81: }
! 82: }
! 83:
! 84: /*
! 85: * Start by receiving the file list and our mystery number.
! 86: * These we're going to be touching on our local system.
! 87: */
! 88:
! 89: if ( ! flist_recv(sess, fdin, &fl, &flsz)) {
! 90: ERRX1(sess, "flist_recv");
! 91: goto out;
! 92: }
! 93:
! 94: /* The IO error is sent after the file list. */
! 95:
! 96: if ( ! io_read_int(sess, fdin, &ioerror)) {
! 97: ERRX1(sess, "io_read_int");
! 98: goto out;
! 99: } else if (0 != ioerror) {
! 100: ERRX1(sess, "io_error is non-zero");
! 101: goto out;
! 102: }
! 103:
! 104: if (0 == flsz && ! sess->opts->server) {
! 105: WARNX(sess, "receiver has empty file list: exiting");
! 106: rc = 1;
! 107: goto out;
! 108: } else if ( ! sess->opts->server)
! 109: LOG1(sess, "Transfer starting: %zu files", flsz);
! 110:
! 111: LOG2(sess, "%s: receiver destination", root);
! 112:
! 113: /*
! 114: * Create the path for our destination directory, if we're not
! 115: * in dry-run mode (which would otherwise crash w/the pledge).
! 116: * This uses our current umask: we might set the permissions on
! 117: * this directory in post_dir().
! 118: */
! 119:
! 120: if ( ! sess->opts->dry_run) {
! 121: if (NULL == (tofree = strdup(root))) {
! 122: ERR(sess, "strdup");
! 123: goto out;
! 124: } else if (mkpath(sess, tofree) < 0) {
! 125: ERRX1(sess, "%s: mkpath", root);
! 126: free(tofree);
! 127: goto out;
! 128: }
! 129: free(tofree);
! 130: }
! 131:
! 132: /*
! 133: * Disable umask() so we can set permissions fully.
! 134: * Then open the directory iff we're not in dry_run.
! 135: */
! 136:
! 137: oumask = umask(0);
! 138:
! 139: if ( ! sess->opts->dry_run) {
! 140: dfd = open(root, O_RDONLY | O_DIRECTORY, 0);
! 141: if (-1 == dfd) {
! 142: ERR(sess, "%s: open", root);
! 143: goto out;
! 144: }
! 145: }
! 146:
! 147: /*
! 148: * Begin by conditionally getting all files we have currently
! 149: * available in our destination.
! 150: * XXX: THIS IS A BUG IN OPENBSD 6.4.
! 151: * For newer version of OpenBSD, this is safe to put after the
! 152: * unveil.
! 153: */
! 154:
! 155: if (sess->opts->del &&
! 156: sess->opts->recursive &&
! 157: ! flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
! 158: ERRX1(sess, "flist_gen_local");
! 159: goto out;
! 160: }
! 161:
! 162: /*
! 163: * Make our entire view of the file-system be limited to what's
! 164: * in the root directory.
! 165: * This prevents us from accidentally (or "under the influence")
! 166: * writing into other parts of the file-system.
! 167: */
! 168:
! 169: if (-1 == unveil(root, "rwc")) {
! 170: ERR(sess, "%s: unveil", root);
! 171: goto out;
! 172: } else if (-1 == unveil(NULL, NULL)) {
! 173: ERR(sess, "%s: unveil", root);
! 174: goto out;
! 175: }
! 176:
! 177: /* If we have a local set, go for the deletion. */
! 178:
! 179: if ( ! flist_del(sess, dfd, dfl, dflsz)) {
! 180: ERRX1(sess, "flist_del");
! 181: goto out;
! 182: }
! 183:
! 184: /* Initialise poll events to listen from the sender. */
! 185:
! 186: pfd[PFD_SENDER_IN].fd = fdin;
! 187: pfd[PFD_UPLOADER_IN].fd = -1;
! 188: pfd[PFD_DOWNLOADER_IN].fd = -1;
! 189: pfd[PFD_SENDER_OUT].fd = fdout;
! 190:
! 191: pfd[PFD_SENDER_IN].events = POLLIN;
! 192: pfd[PFD_UPLOADER_IN].events = POLLIN;
! 193: pfd[PFD_DOWNLOADER_IN].events = POLLIN;
! 194: pfd[PFD_SENDER_OUT].events = POLLOUT;
! 195:
! 196: ul = upload_alloc(sess, dfd, fdout,
! 197: CSUM_LENGTH_PHASE1, fl, flsz, oumask);
! 198: if (NULL == ul) {
! 199: ERRX1(sess, "upload_alloc");
! 200: goto out;
! 201: }
! 202:
! 203: dl = download_alloc(sess, fdin, fl, flsz, dfd);
! 204: if (NULL == dl) {
! 205: ERRX1(sess, "download_alloc");
! 206: goto out;
! 207: }
! 208:
! 209: LOG2(sess, "%s: ready for phase 1 data", root);
! 210:
! 211: for (;;) {
! 212: if (-1 == (c = poll(pfd, PFD__MAX, INFTIM))) {
! 213: ERR(sess, "poll");
! 214: goto out;
! 215: }
! 216:
! 217: for (i = 0; i < PFD__MAX; i++)
! 218: if (pfd[i].revents & (POLLERR|POLLNVAL)) {
! 219: ERRX(sess, "poll: bad fd");
! 220: goto out;
! 221: } else if (pfd[i].revents & POLLHUP) {
! 222: ERRX(sess, "poll: hangup");
! 223: goto out;
! 224: }
! 225:
! 226: /*
! 227: * If we have a read event and we're multiplexing, we
! 228: * might just have error messages in the pipe.
! 229: * It's important to flush these out so that we don't
! 230: * clog the pipe.
! 231: * Unset our polling status if there's nothing that
! 232: * remains in the pipe.
! 233: */
! 234:
! 235: if (sess->mplex_reads &&
! 236: (POLLIN & pfd[PFD_SENDER_IN].revents)) {
! 237: if ( ! io_read_flush(sess, fdin)) {
! 238: ERRX1(sess, "io_read_flush");
! 239: goto out;
! 240: } else if (0 == sess->mplex_read_remain)
! 241: pfd[PFD_SENDER_IN].revents &= ~POLLIN;
! 242: }
! 243:
! 244:
! 245: /*
! 246: * We run the uploader if we have files left to examine
! 247: * (i < flsz) or if we have a file that we've opened and
! 248: * is read to mmap.
! 249: */
! 250:
! 251: if ((POLLIN & pfd[PFD_UPLOADER_IN].revents) ||
! 252: (POLLOUT & pfd[PFD_SENDER_OUT].revents)) {
! 253: c = rsync_uploader(ul,
! 254: &pfd[PFD_UPLOADER_IN].fd,
! 255: sess, &pfd[PFD_SENDER_OUT].fd);
! 256: if (c < 0) {
! 257: ERRX1(sess, "rsync_uploader");
! 258: goto out;
! 259: }
! 260: }
! 261:
! 262: /*
! 263: * We need to run the downloader when we either have
! 264: * read events from the sender or an asynchronous local
! 265: * open is ready.
! 266: * XXX: we don't disable PFD_SENDER_IN like with the
! 267: * uploader because we might stop getting error
! 268: * messages, which will otherwise clog up the pipes.
! 269: */
! 270:
! 271: if ((POLLIN & pfd[PFD_SENDER_IN].revents) ||
! 272: (POLLIN & pfd[PFD_DOWNLOADER_IN].revents)) {
! 273: c = rsync_downloader(dl, sess,
! 274: &pfd[PFD_DOWNLOADER_IN].fd);
! 275: if (c < 0) {
! 276: ERRX1(sess, "rsync_downloader");
! 277: goto out;
! 278: } else if (0 == c) {
! 279: assert(0 == phase);
! 280: phase++;
! 281: LOG2(sess, "%s: receiver ready "
! 282: "for phase 2 data", root);
! 283: break;
! 284: }
! 285:
! 286: /*
! 287: * FIXME: if we have any errors during the
! 288: * download, most notably files getting out of
! 289: * sync between the send and the receiver, then
! 290: * here we should bump our checksum length and
! 291: * go into the second phase.
! 292: */
! 293: }
! 294: }
! 295:
! 296: /* Properly close us out by progressing through the phases. */
! 297:
! 298: if (1 == phase) {
! 299: if ( ! io_write_int(sess, fdout, -1)) {
! 300: ERRX1(sess, "io_write_int");
! 301: goto out;
! 302: } else if ( ! io_read_int(sess, fdin, &ioerror)) {
! 303: ERRX1(sess, "io_read_int");
! 304: goto out;
! 305: } else if (-1 != ioerror) {
! 306: ERRX(sess, "expected phase ack");
! 307: goto out;
! 308: }
! 309: }
! 310:
! 311: /*
! 312: * Now all of our transfers are complete, so we can fix up our
! 313: * directory permissions.
! 314: */
! 315:
! 316: if ( ! rsync_uploader_tail(ul, sess)) {
! 317: ERRX1(sess, "rsync_uploader_tail");
! 318: goto out;
! 319: }
! 320:
! 321: /* Process server statistics and say good-bye. */
! 322:
! 323: if ( ! sess_stats_recv(sess, fdin)) {
! 324: ERRX1(sess, "sess_stats_recv");
! 325: goto out;
! 326: } else if ( ! io_write_int(sess, fdout, -1)) {
! 327: ERRX1(sess, "io_write_int");
! 328: goto out;
! 329: }
! 330:
! 331: LOG2(sess, "receiver finished updating");
! 332: rc = 1;
! 333: out:
! 334: if (-1 != dfd)
! 335: close(dfd);
! 336: upload_free(ul);
! 337: download_free(dl);
! 338: flist_free(fl, flsz);
! 339: flist_free(dfl, dflsz);
! 340: return rc;
! 341: }