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