Annotation of src/usr.bin/rsync/sender.c, Revision 1.2
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;
1.2 ! benno 68: }
1.1 benno 69:
1.2 ! benno 70: /*
1.1 benno 71: * Then the file list in any mode.
72: * Finally, the IO error (always zero for us).
73: */
1.2 ! benno 74:
1.1 benno 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;
1.2 ! benno 81: }
1.1 benno 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: }