sender.c revision 1.3
1/*	$Id: sender.c,v 1.3 2019/02/10 23:43:31 benno Exp $ */
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 */
36int
37rsync_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("stdio rpath unveil", 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;
224out:
225	flist_free(fl, flsz);
226	return rc;
227}
228