Annotation of src/usr.bin/rsync/sender.c, Revision 1.1
1.1 ! benno 1: /* $Id$ */
! 2: /*
! 3: * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17: #include <sys/stat.h>
! 18:
! 19: #include <assert.h>
! 20: #include <inttypes.h>
! 21: #include <stdlib.h>
! 22: #include <string.h>
! 23: #include <unistd.h>
! 24:
! 25: #include "extern.h"
! 26:
! 27: /*
! 28: * A client sender manages the read-only source files and sends data to
! 29: * the receiver as requested.
! 30: * First it sends its list of files, then it waits for the server to
! 31: * request updates to individual files.
! 32: * Returns zero on failure, non-zero on success.
! 33: *
! 34: * Pledges: stdio, rpath, unveil.
! 35: */
! 36: int
! 37: rsync_sender(struct sess *sess, int fdin,
! 38: int fdout, size_t argc, char **argv)
! 39: {
! 40: struct flist *fl = NULL;
! 41: size_t flsz = 0, phase = 0, excl;
! 42: int rc = 0, c;
! 43: int32_t idx;
! 44: struct blkset *blks = NULL;
! 45:
! 46: if (-1 == pledge("unveil stdio rpath", NULL)) {
! 47: ERR(sess, "pledge");
! 48: return 0;
! 49: }
! 50:
! 51: /*
! 52: * Generate the list of files we want to send from our
! 53: * command-line input.
! 54: * This will also remove all invalid files.
! 55: */
! 56:
! 57: if ( ! flist_gen(sess, argc, argv, &fl, &flsz)) {
! 58: ERRX1(sess, "flist_gen");
! 59: goto out;
! 60: }
! 61:
! 62: /* Client sends zero-length exclusions if deleting. */
! 63:
! 64: if ( ! sess->opts->server && sess->opts->del &&
! 65: ! io_write_int(sess, fdout, 0)) {
! 66: ERRX1(sess, "io_write_int");
! 67: goto out;
! 68: }
! 69:
! 70: /*
! 71: * Then the file list in any mode.
! 72: * Finally, the IO error (always zero for us).
! 73: */
! 74:
! 75: if ( ! flist_send(sess, fdin, fdout, fl, flsz)) {
! 76: ERRX1(sess, "flist_send");
! 77: goto out;
! 78: } else if ( ! io_write_int(sess, fdout, 0)) {
! 79: ERRX1(sess, "io_write_int");
! 80: goto out;
! 81: }
! 82:
! 83: /* Exit if we're the server with zero files. */
! 84:
! 85: if (0 == flsz && sess->opts->server) {
! 86: WARNX(sess, "sender has empty file list: exiting");
! 87: rc = 1;
! 88: goto out;
! 89: } else if ( ! sess->opts->server)
! 90: LOG1(sess, "Transfer starting: %zu files", flsz);
! 91:
! 92: /*
! 93: * If we're the server, read our exclusion list.
! 94: * This is always 0 for now.
! 95: */
! 96:
! 97: if (sess->opts->server) {
! 98: if ( ! io_read_size(sess, fdin, &excl)) {
! 99: ERRX1(sess, "io_read_size");
! 100: goto out;
! 101: } else if (0 != excl) {
! 102: ERRX1(sess, "exclusion list is non-empty");
! 103: goto out;
! 104: }
! 105: }
! 106:
! 107: /*
! 108: * We have two phases: the first has a two-byte checksum, the
! 109: * second has a full 16-byte checksum.
! 110: */
! 111:
! 112: LOG2(sess, "sender transmitting phase 1 data");
! 113:
! 114: for (;;) {
! 115: if ( ! io_read_int(sess, fdin, &idx)) {
! 116: ERRX1(sess, "io_read_int");
! 117: goto out;
! 118: }
! 119:
! 120: /*
! 121: * If we receive an invalid index (-1), then we're
! 122: * either promoted to the second phase or it's time to
! 123: * exit, depending upon which phase we're in.
! 124: */
! 125:
! 126: if (-1 == idx) {
! 127: if ( ! io_write_int(sess, fdout, idx)) {
! 128: ERRX1(sess, "io_write_int");
! 129: goto out;
! 130: }
! 131:
! 132: /* FIXME: I don't understand this ack. */
! 133:
! 134: if (sess->opts->server && sess->rver > 27)
! 135: if ( ! io_write_int(sess, fdout, idx)) {
! 136: ERRX1(sess, "io_write_int");
! 137: goto out;
! 138: }
! 139:
! 140: if (phase++)
! 141: break;
! 142: LOG2(sess, "sender transmitting phase 2 data");
! 143: continue;
! 144: }
! 145:
! 146: /* Validate index and file type. */
! 147:
! 148: if (idx < 0 || (uint32_t)idx >= flsz) {
! 149: ERRX(sess, "file index out of bounds: "
! 150: "invalid %" PRId32 " out of %zu",
! 151: idx, flsz);
! 152: goto out;
! 153: } else if (S_ISDIR(fl[idx].st.mode)) {
! 154: ERRX(sess, "blocks requested for "
! 155: "directory: %s", fl[idx].path);
! 156: goto out;
! 157: } else if (S_ISLNK(fl[idx].st.mode)) {
! 158: ERRX(sess, "blocks requested for "
! 159: "symlink: %s", fl[idx].path);
! 160: goto out;
! 161: } else if ( ! S_ISREG(fl[idx].st.mode)) {
! 162: ERRX(sess, "blocks requested for "
! 163: "special: %s", fl[idx].path);
! 164: goto out;
! 165: }
! 166:
! 167: if ( ! sess->opts->server)
! 168: LOG1(sess, "%s", fl[idx].wpath);
! 169:
! 170: /* Dry-run doesn't do anything. */
! 171:
! 172: if (sess->opts->dry_run) {
! 173: if ( ! io_write_int(sess, fdout, idx)) {
! 174: ERRX1(sess, "io_write_int");
! 175: goto out;
! 176: }
! 177: continue;
! 178: }
! 179:
! 180: /*
! 181: * The server will now send us its view of the file.
! 182: * It does so by cutting a file into a series of blocks
! 183: * and checksumming each block.
! 184: * We can then compare the blocks in our file and those
! 185: * in theirs, and send them blocks they're missing or
! 186: * don't have.
! 187: */
! 188:
! 189: blks = blk_recv(sess, fdin, fl[idx].path);
! 190: if (NULL == blks) {
! 191: ERRX1(sess, "blk_recv");
! 192: goto out;
! 193: } else if ( ! blk_recv_ack(sess, fdout, blks, idx)) {
! 194: ERRX1(sess, "blk_recv_ack");
! 195: goto out;
! 196: }
! 197:
! 198: c = blk_match(sess, fdout, blks, fl[idx].path);
! 199: blkset_free(blks);
! 200:
! 201: if ( ! c) {
! 202: ERRX1(sess, "blk_match");
! 203: goto out;
! 204: }
! 205: }
! 206:
! 207: if ( ! sess_stats_send(sess, fdout)) {
! 208: ERRX1(sess, "sess_stats_end");
! 209: goto out;
! 210: }
! 211:
! 212: /* Final "goodbye" message. */
! 213:
! 214: if ( ! io_read_int(sess, fdin, &idx)) {
! 215: ERRX1(sess, "io_read_int");
! 216: goto out;
! 217: } else if (-1 != idx) {
! 218: ERRX(sess, "read incorrect update complete ack");
! 219: goto out;
! 220: }
! 221:
! 222: LOG2(sess, "sender finished updating");
! 223: rc = 1;
! 224: out:
! 225: flist_free(fl, flsz);
! 226: return rc;
! 227: }