xref: /openbsd-src/usr.bin/rsync/receiver.c (revision ce7279d89b71439c96c854f612f4ac93a461fdc4)
1*ce7279d8Sjsg /*	$OpenBSD: receiver.c,v 1.33 2024/05/21 05:00:48 jsg Exp $ */
260a32ee9Sbenno 
360a32ee9Sbenno /*
460a32ee9Sbenno  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5dbc83512Sflorian  * Copyright (c) 2019 Florian Obser <florian@openbsd.org>
660a32ee9Sbenno  *
760a32ee9Sbenno  * Permission to use, copy, modify, and distribute this software for any
860a32ee9Sbenno  * purpose with or without fee is hereby granted, provided that the above
960a32ee9Sbenno  * copyright notice and this permission notice appear in all copies.
1060a32ee9Sbenno  *
1160a32ee9Sbenno  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1260a32ee9Sbenno  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1360a32ee9Sbenno  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1460a32ee9Sbenno  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1560a32ee9Sbenno  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1660a32ee9Sbenno  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1760a32ee9Sbenno  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1860a32ee9Sbenno  */
1960a32ee9Sbenno #include <sys/mman.h>
2060a32ee9Sbenno #include <sys/stat.h>
2160a32ee9Sbenno 
2260a32ee9Sbenno #include <assert.h>
23dd135e81Sclaudio #include <err.h>
2460a32ee9Sbenno #include <errno.h>
2560a32ee9Sbenno #include <fcntl.h>
2660a32ee9Sbenno #include <inttypes.h>
2760a32ee9Sbenno #include <math.h>
2860a32ee9Sbenno #include <poll.h>
2960a32ee9Sbenno #include <stdio.h>
3060a32ee9Sbenno #include <stdlib.h>
3160a32ee9Sbenno #include <string.h>
3260a32ee9Sbenno #include <time.h>
3360a32ee9Sbenno #include <unistd.h>
3460a32ee9Sbenno 
3560a32ee9Sbenno #include "extern.h"
3660a32ee9Sbenno 
3760a32ee9Sbenno enum	pfdt {
3860a32ee9Sbenno 	PFD_SENDER_IN = 0, /* input from the sender */
3960a32ee9Sbenno 	PFD_UPLOADER_IN, /* uploader input from a local file */
4060a32ee9Sbenno 	PFD_DOWNLOADER_IN, /* downloader input from a local file */
4160a32ee9Sbenno 	PFD_SENDER_OUT, /* output to the sender */
4260a32ee9Sbenno 	PFD__MAX
4360a32ee9Sbenno };
4460a32ee9Sbenno 
45cc85e6afSflorian int
rsync_set_metadata(struct sess * sess,int newfile,int fd,const struct flist * f,const char * path)46cc85e6afSflorian rsync_set_metadata(struct sess *sess, int newfile,
47cc85e6afSflorian 	int fd, const struct flist *f, const char *path)
48cc85e6afSflorian {
497e1037e0Sflorian 	uid_t		 uid = (uid_t)-1;
507e1037e0Sflorian 	gid_t		 gid = (gid_t)-1;
51afb54730Sderaadt 	mode_t		 mode;
52a4c5e668Scheloha 	struct timespec	 ts[2];
53cc85e6afSflorian 
54cc85e6afSflorian 	/* Conditionally adjust file modification time. */
55cc85e6afSflorian 
56cc85e6afSflorian 	if (sess->opts->preserve_times) {
57a4c5e668Scheloha 		ts[0].tv_nsec = UTIME_NOW;
58a4c5e668Scheloha 		ts[1].tv_sec = f->st.mtime;
59a4c5e668Scheloha 		ts[1].tv_nsec = 0;
60a4c5e668Scheloha 		if (futimens(fd, ts) == -1) {
61b2a7eac7Sbenno 			ERR("%s: futimens", path);
62cc85e6afSflorian 			return 0;
63cc85e6afSflorian 		}
64b2a7eac7Sbenno 		LOG4("%s: updated date", f->path);
65cc85e6afSflorian 	}
66cc85e6afSflorian 
67afb54730Sderaadt 	/*
68afb54730Sderaadt 	 * Conditionally adjust identifiers.
69afb54730Sderaadt 	 * If we have an EPERM, report it but continue on: this just
70afb54730Sderaadt 	 * means that we're mapping into an unknown (or disallowed)
71afb54730Sderaadt 	 * group identifier.
72afb54730Sderaadt 	 */
73afb54730Sderaadt 	if (getuid() == 0 && sess->opts->preserve_uids)
74afb54730Sderaadt 		uid = f->st.uid;
75afb54730Sderaadt 	if (sess->opts->preserve_gids)
76afb54730Sderaadt 		gid = f->st.gid;
77afb54730Sderaadt 
78afb54730Sderaadt 	mode = f->st.mode;
79afb54730Sderaadt 	if (uid != (uid_t)-1 || gid != (gid_t)-1) {
80afb54730Sderaadt 		if (fchown(fd, uid, gid) == -1) {
81afb54730Sderaadt 			if (errno != EPERM) {
82b2a7eac7Sbenno 				ERR("%s: fchown", path);
83afb54730Sderaadt 				return 0;
84afb54730Sderaadt 			}
85afb54730Sderaadt 			if (getuid() == 0)
86b2a7eac7Sbenno 				WARNX("%s: identity unknown or not available "
87afb54730Sderaadt 				    "to user.group: %u.%u", f->path, uid, gid);
88afb54730Sderaadt 		} else
89b2a7eac7Sbenno 			LOG4("%s: updated uid and/or gid", f->path);
90afb54730Sderaadt 		mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
91afb54730Sderaadt 	}
92afb54730Sderaadt 
93cb6e4267Sflorian 	/* Conditionally adjust file permissions. */
94cb6e4267Sflorian 
95cb6e4267Sflorian 	if (newfile || sess->opts->preserve_perms) {
96afb54730Sderaadt 		if (fchmod(fd, mode) == -1) {
97b2a7eac7Sbenno 			ERR("%s: fchmod", path);
98cb6e4267Sflorian 			return 0;
99cb6e4267Sflorian 		}
100b2a7eac7Sbenno 		LOG4("%s: updated permissions", f->path);
101cb6e4267Sflorian 	}
102cb6e4267Sflorian 
103cc85e6afSflorian 	return 1;
104cc85e6afSflorian }
105cc85e6afSflorian 
106434f41cdSflorian int
rsync_set_metadata_at(struct sess * sess,int newfile,int rootfd,const struct flist * f,const char * path)107434f41cdSflorian rsync_set_metadata_at(struct sess *sess, int newfile, int rootfd,
108434f41cdSflorian 	const struct flist *f, const char *path)
109434f41cdSflorian {
110434f41cdSflorian 	uid_t		 uid = (uid_t)-1;
111434f41cdSflorian 	gid_t		 gid = (gid_t)-1;
112afb54730Sderaadt 	mode_t		 mode;
113a4c5e668Scheloha 	struct timespec	 ts[2];
114434f41cdSflorian 
115434f41cdSflorian 	/* Conditionally adjust file modification time. */
116434f41cdSflorian 
11716f87427Sclaudio 	if (sess->opts->preserve_times &&
11816f87427Sclaudio 	    !(S_ISLNK(f->st.mode) && sess->opts->ignore_link_times)) {
119a4c5e668Scheloha 		ts[0].tv_nsec = UTIME_NOW;
120a4c5e668Scheloha 		ts[1].tv_sec = f->st.mtime;
121a4c5e668Scheloha 		ts[1].tv_nsec = 0;
122a4c5e668Scheloha 		if (utimensat(rootfd, path, ts, AT_SYMLINK_NOFOLLOW) == -1) {
123b2a7eac7Sbenno 			ERR("%s: utimensat", path);
124434f41cdSflorian 			return 0;
125434f41cdSflorian 		}
126b2a7eac7Sbenno 		LOG4("%s: updated date", f->path);
127434f41cdSflorian 	}
128434f41cdSflorian 
129afb54730Sderaadt 	/*
130afb54730Sderaadt 	 * Conditionally adjust identifiers.
131afb54730Sderaadt 	 * If we have an EPERM, report it but continue on: this just
132afb54730Sderaadt 	 * means that we're mapping into an unknown (or disallowed)
133afb54730Sderaadt 	 * group identifier.
134afb54730Sderaadt 	 */
135afb54730Sderaadt 	if (getuid() == 0 && sess->opts->preserve_uids)
136afb54730Sderaadt 		uid = f->st.uid;
137afb54730Sderaadt 	if (sess->opts->preserve_gids)
138afb54730Sderaadt 		gid = f->st.gid;
139afb54730Sderaadt 
140afb54730Sderaadt 	mode = f->st.mode;
141afb54730Sderaadt 	if (uid != (uid_t)-1 || gid != (gid_t)-1) {
142afb54730Sderaadt 		if (fchownat(rootfd, path, uid, gid, AT_SYMLINK_NOFOLLOW) == -1) {
143afb54730Sderaadt 			if (errno != EPERM) {
144b2a7eac7Sbenno 				ERR("%s: fchownat", path);
145afb54730Sderaadt 				return 0;
146afb54730Sderaadt 			}
147afb54730Sderaadt 			if (getuid() == 0)
148b2a7eac7Sbenno 				WARNX("%s: identity unknown or not available "
149afb54730Sderaadt 				    "to user.group: %u.%u", f->path, uid, gid);
150afb54730Sderaadt 		} else
151b2a7eac7Sbenno 			LOG4("%s: updated uid and/or gid", f->path);
152afb54730Sderaadt 		mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
153afb54730Sderaadt 	}
154afb54730Sderaadt 
155434f41cdSflorian 	/* Conditionally adjust file permissions. */
156434f41cdSflorian 
157434f41cdSflorian 	if (newfile || sess->opts->preserve_perms) {
158afb54730Sderaadt 		if (fchmodat(rootfd, path, mode, AT_SYMLINK_NOFOLLOW) == -1) {
159b2a7eac7Sbenno 			ERR("%s: fchmodat", path);
160434f41cdSflorian 			return 0;
161434f41cdSflorian 		}
162b2a7eac7Sbenno 		LOG4("%s: updated permissions", f->path);
163434f41cdSflorian 	}
164434f41cdSflorian 
165434f41cdSflorian 	return 1;
166434f41cdSflorian }
167434f41cdSflorian 
168434f41cdSflorian /*
169434f41cdSflorian  * Pledges: unveil, unix, rpath, cpath, wpath, stdio, fattr, chown.
170434f41cdSflorian  * Pledges (dry-run): -unix, -cpath, -wpath, -fattr, -chown.
17160a32ee9Sbenno  */
17260a32ee9Sbenno int
rsync_receiver(struct sess * sess,int fdin,int fdout,const char * root)173b1d34d51Sderaadt rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root)
17460a32ee9Sbenno {
17560a32ee9Sbenno 	struct flist	*fl = NULL, *dfl = NULL;
17657987d16Sclaudio 	size_t		 i, flsz = 0, dflsz = 0;
17760a32ee9Sbenno 	char		*tofree;
17860a32ee9Sbenno 	int		 rc = 0, dfd = -1, phase = 0, c;
17960a32ee9Sbenno 	int32_t		 ioerror;
18060a32ee9Sbenno 	struct pollfd	 pfd[PFD__MAX];
18160a32ee9Sbenno 	struct download	*dl = NULL;
18260a32ee9Sbenno 	struct upload	*ul = NULL;
18360a32ee9Sbenno 	mode_t		 oumask;
18460a32ee9Sbenno 
185dd135e81Sclaudio 	if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1)
186dd135e81Sclaudio 		err(ERR_IPC, "pledge");
18760a32ee9Sbenno 
188e397242dSclaudio 	/*
189e397242dSclaudio 	 * Create the path for our destination directory, if we're not
190e397242dSclaudio 	 * in dry-run mode (which would otherwise crash w/the pledge).
191e397242dSclaudio 	 * This uses our current umask: we might set the permissions on
192e397242dSclaudio 	 * this directory in post_dir().
193e397242dSclaudio 	 */
194e397242dSclaudio 
195e397242dSclaudio 	if (!sess->opts->dry_run) {
196e397242dSclaudio 		if ((tofree = strdup(root)) == NULL)
197e397242dSclaudio 			err(ERR_NOMEM, NULL);
198e397242dSclaudio 		if (mkpath(tofree) < 0)
199e397242dSclaudio 			err(ERR_FILE_IO, "%s: mkpath", tofree);
200e397242dSclaudio 		free(tofree);
201e397242dSclaudio 	}
202e397242dSclaudio 
203e397242dSclaudio 	/*
204e397242dSclaudio 	 * Make our entire view of the file-system be limited to what's
205e397242dSclaudio 	 * in the root directory.
206e397242dSclaudio 	 * This prevents us from accidentally (or "under the influence")
207e397242dSclaudio 	 * writing into other parts of the file-system.
208e397242dSclaudio 	 */
209e397242dSclaudio 	if (sess->opts->basedir[0]) {
210e397242dSclaudio 		/*
211e397242dSclaudio 		 * XXX just unveil everything for read
212e397242dSclaudio 		 * Could unveil each basedir or maybe a common path
213e397242dSclaudio 		 * also the fact that relative path are relative to the
214e397242dSclaudio 		 * root does not help.
215e397242dSclaudio 		 */
216e397242dSclaudio 		if (unveil("/", "r") == -1)
217e397242dSclaudio 			err(ERR_IPC, "%s: unveil", root);
218e397242dSclaudio 	}
219e397242dSclaudio 
220e397242dSclaudio 	if (unveil(root, "rwc") == -1)
221e397242dSclaudio 		err(ERR_IPC, "%s: unveil", root);
222e397242dSclaudio 
223e397242dSclaudio 	if (unveil(NULL, NULL) == -1)
224e397242dSclaudio 		err(ERR_IPC, "unveil");
225e397242dSclaudio 
22657987d16Sclaudio 	/* Client sends exclusions. */
22757987d16Sclaudio 	if (!sess->opts->server)
22857987d16Sclaudio 		send_rules(sess, fdout);
22960a32ee9Sbenno 
23057987d16Sclaudio 	/* Server receives exclusions if delete is on. */
23157987d16Sclaudio 	if (sess->opts->server && sess->opts->del)
23257987d16Sclaudio 		recv_rules(sess, fdin);
23360a32ee9Sbenno 
23460a32ee9Sbenno 	/*
23560a32ee9Sbenno 	 * Start by receiving the file list and our mystery number.
23660a32ee9Sbenno 	 * These we're going to be touching on our local system.
23760a32ee9Sbenno 	 */
23860a32ee9Sbenno 
23960a32ee9Sbenno 	if (!flist_recv(sess, fdin, &fl, &flsz)) {
240b2a7eac7Sbenno 		ERRX1("flist_recv");
24160a32ee9Sbenno 		goto out;
24260a32ee9Sbenno 	}
24360a32ee9Sbenno 
24460a32ee9Sbenno 	/* The IO error is sent after the file list. */
24560a32ee9Sbenno 
24660a32ee9Sbenno 	if (!io_read_int(sess, fdin, &ioerror)) {
247b2a7eac7Sbenno 		ERRX1("io_read_int");
24860a32ee9Sbenno 		goto out;
2496304135bSbenno 	} else if (ioerror != 0) {
250b2a7eac7Sbenno 		ERRX1("io_error is non-zero");
25160a32ee9Sbenno 		goto out;
25260a32ee9Sbenno 	}
25360a32ee9Sbenno 
254f1dcb30aSderaadt 	if (flsz == 0 && !sess->opts->server) {
255b2a7eac7Sbenno 		WARNX("receiver has empty file list: exiting");
25660a32ee9Sbenno 		rc = 1;
25760a32ee9Sbenno 		goto out;
25860a32ee9Sbenno 	} else if (!sess->opts->server)
259b2a7eac7Sbenno 		LOG1("Transfer starting: %zu files", flsz);
26060a32ee9Sbenno 
261b2a7eac7Sbenno 	LOG2("%s: receiver destination", root);
26260a32ee9Sbenno 
26360a32ee9Sbenno 	/*
26460a32ee9Sbenno 	 * Disable umask() so we can set permissions fully.
26560a32ee9Sbenno 	 * Then open the directory iff we're not in dry_run.
26660a32ee9Sbenno 	 */
26760a32ee9Sbenno 
26860a32ee9Sbenno 	oumask = umask(0);
26960a32ee9Sbenno 
27060a32ee9Sbenno 	if (!sess->opts->dry_run) {
271b7041c07Sderaadt 		dfd = open(root, O_RDONLY | O_DIRECTORY);
272dd135e81Sclaudio 		if (dfd == -1)
273dd135e81Sclaudio 			err(ERR_FILE_IO, "%s: open", root);
27460a32ee9Sbenno 	}
27560a32ee9Sbenno 
27660a32ee9Sbenno 	/*
27760a32ee9Sbenno 	 * Begin by conditionally getting all files we have currently
27860a32ee9Sbenno 	 * available in our destination.
27960a32ee9Sbenno 	 */
28060a32ee9Sbenno 
28160a32ee9Sbenno 	if (sess->opts->del &&
28260a32ee9Sbenno 	    sess->opts->recursive &&
28360a32ee9Sbenno 	    !flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
284*ce7279d8Sjsg 		ERRX1("rsync_receiver");
28560a32ee9Sbenno 		goto out;
28660a32ee9Sbenno 	}
28760a32ee9Sbenno 
28860a32ee9Sbenno 	/* If we have a local set, go for the deletion. */
28960a32ee9Sbenno 
29060a32ee9Sbenno 	if (!flist_del(sess, dfd, dfl, dflsz)) {
291b2a7eac7Sbenno 		ERRX1("flist_del");
29260a32ee9Sbenno 		goto out;
29360a32ee9Sbenno 	}
29460a32ee9Sbenno 
29560a32ee9Sbenno 	/* Initialise poll events to listen from the sender. */
29660a32ee9Sbenno 
29760a32ee9Sbenno 	pfd[PFD_SENDER_IN].fd = fdin;
29860a32ee9Sbenno 	pfd[PFD_UPLOADER_IN].fd = -1;
29960a32ee9Sbenno 	pfd[PFD_DOWNLOADER_IN].fd = -1;
30060a32ee9Sbenno 	pfd[PFD_SENDER_OUT].fd = fdout;
30160a32ee9Sbenno 
30260a32ee9Sbenno 	pfd[PFD_SENDER_IN].events = POLLIN;
30360a32ee9Sbenno 	pfd[PFD_UPLOADER_IN].events = POLLIN;
30460a32ee9Sbenno 	pfd[PFD_DOWNLOADER_IN].events = POLLIN;
30560a32ee9Sbenno 	pfd[PFD_SENDER_OUT].events = POLLOUT;
30660a32ee9Sbenno 
307ba617adaSbenno 	ul = upload_alloc(root, dfd, fdout, CSUM_LENGTH_PHASE1, fl, flsz,
308ba617adaSbenno 	    oumask);
309ba617adaSbenno 
310f1dcb30aSderaadt 	if (ul == NULL) {
311b2a7eac7Sbenno 		ERRX1("upload_alloc");
31260a32ee9Sbenno 		goto out;
31360a32ee9Sbenno 	}
31460a32ee9Sbenno 
31560a32ee9Sbenno 	dl = download_alloc(sess, fdin, fl, flsz, dfd);
316f1dcb30aSderaadt 	if (dl == NULL) {
317b2a7eac7Sbenno 		ERRX1("download_alloc");
31860a32ee9Sbenno 		goto out;
31960a32ee9Sbenno 	}
32060a32ee9Sbenno 
321b2a7eac7Sbenno 	LOG2("%s: ready for phase 1 data", root);
32260a32ee9Sbenno 
32360a32ee9Sbenno 	for (;;) {
324a84b4914Sclaudio 		if ((c = poll(pfd, PFD__MAX, poll_timeout)) == -1) {
325b2a7eac7Sbenno 			ERR("poll");
32660a32ee9Sbenno 			goto out;
3270889042fSflorian 		} else if (c == 0) {
328b2a7eac7Sbenno 			ERRX("poll: timeout");
3290889042fSflorian 			goto out;
33060a32ee9Sbenno 		}
33160a32ee9Sbenno 
33260a32ee9Sbenno 		for (i = 0; i < PFD__MAX; i++)
33360a32ee9Sbenno 			if (pfd[i].revents & (POLLERR|POLLNVAL)) {
334b2a7eac7Sbenno 				ERRX("poll: bad fd");
33560a32ee9Sbenno 				goto out;
33660a32ee9Sbenno 			} else if (pfd[i].revents & POLLHUP) {
337b2a7eac7Sbenno 				ERRX("poll: hangup");
33860a32ee9Sbenno 				goto out;
33960a32ee9Sbenno 			}
34060a32ee9Sbenno 
34160a32ee9Sbenno 		/*
34260a32ee9Sbenno 		 * If we have a read event and we're multiplexing, we
34360a32ee9Sbenno 		 * might just have error messages in the pipe.
34460a32ee9Sbenno 		 * It's important to flush these out so that we don't
34560a32ee9Sbenno 		 * clog the pipe.
34660a32ee9Sbenno 		 * Unset our polling status if there's nothing that
34760a32ee9Sbenno 		 * remains in the pipe.
34860a32ee9Sbenno 		 */
34960a32ee9Sbenno 
35060a32ee9Sbenno 		if (sess->mplex_reads &&
351bb41c969Sclaudio 		    (pfd[PFD_SENDER_IN].revents & POLLIN)) {
35260a32ee9Sbenno 			if (!io_read_flush(sess, fdin)) {
353b2a7eac7Sbenno 				ERRX1("io_read_flush");
35460a32ee9Sbenno 				goto out;
355f1dcb30aSderaadt 			} else if (sess->mplex_read_remain == 0)
35660a32ee9Sbenno 				pfd[PFD_SENDER_IN].revents &= ~POLLIN;
35760a32ee9Sbenno 		}
35860a32ee9Sbenno 
35960a32ee9Sbenno 
36060a32ee9Sbenno 		/*
36160a32ee9Sbenno 		 * We run the uploader if we have files left to examine
36260a32ee9Sbenno 		 * (i < flsz) or if we have a file that we've opened and
36360a32ee9Sbenno 		 * is read to mmap.
36460a32ee9Sbenno 		 */
36560a32ee9Sbenno 
366bb41c969Sclaudio 		if ((pfd[PFD_UPLOADER_IN].revents & POLLIN) ||
367bb41c969Sclaudio 		    (pfd[PFD_SENDER_OUT].revents & POLLOUT)) {
36860a32ee9Sbenno 			c = rsync_uploader(ul,
36960a32ee9Sbenno 				&pfd[PFD_UPLOADER_IN].fd,
37060a32ee9Sbenno 				sess, &pfd[PFD_SENDER_OUT].fd);
37160a32ee9Sbenno 			if (c < 0) {
372b2a7eac7Sbenno 				ERRX1("rsync_uploader");
37360a32ee9Sbenno 				goto out;
37460a32ee9Sbenno 			}
37560a32ee9Sbenno 		}
37660a32ee9Sbenno 
37760a32ee9Sbenno 		/*
37860a32ee9Sbenno 		 * We need to run the downloader when we either have
37960a32ee9Sbenno 		 * read events from the sender or an asynchronous local
38060a32ee9Sbenno 		 * open is ready.
38160a32ee9Sbenno 		 * XXX: we don't disable PFD_SENDER_IN like with the
38260a32ee9Sbenno 		 * uploader because we might stop getting error
38360a32ee9Sbenno 		 * messages, which will otherwise clog up the pipes.
38460a32ee9Sbenno 		 */
38560a32ee9Sbenno 
386bb41c969Sclaudio 		if ((pfd[PFD_SENDER_IN].revents & POLLIN) ||
387bb41c969Sclaudio 		    (pfd[PFD_DOWNLOADER_IN].revents & POLLIN)) {
38860a32ee9Sbenno 			c = rsync_downloader(dl, sess,
38960a32ee9Sbenno 				&pfd[PFD_DOWNLOADER_IN].fd);
39060a32ee9Sbenno 			if (c < 0) {
391b2a7eac7Sbenno 				ERRX1("rsync_downloader");
39260a32ee9Sbenno 				goto out;
393f1dcb30aSderaadt 			} else if (c == 0) {
394f1dcb30aSderaadt 				assert(phase == 0);
39560a32ee9Sbenno 				phase++;
396b2a7eac7Sbenno 				LOG2("%s: receiver ready for phase 2 data", root);
39760a32ee9Sbenno 				break;
39860a32ee9Sbenno 			}
39960a32ee9Sbenno 
40060a32ee9Sbenno 			/*
40160a32ee9Sbenno 			 * FIXME: if we have any errors during the
40260a32ee9Sbenno 			 * download, most notably files getting out of
40360a32ee9Sbenno 			 * sync between the send and the receiver, then
40460a32ee9Sbenno 			 * here we should bump our checksum length and
40560a32ee9Sbenno 			 * go into the second phase.
40660a32ee9Sbenno 			 */
40760a32ee9Sbenno 		}
40860a32ee9Sbenno 	}
40960a32ee9Sbenno 
41060a32ee9Sbenno 	/* Properly close us out by progressing through the phases. */
41160a32ee9Sbenno 
412f1dcb30aSderaadt 	if (phase == 1) {
41360a32ee9Sbenno 		if (!io_write_int(sess, fdout, -1)) {
414b2a7eac7Sbenno 			ERRX1("io_write_int");
41560a32ee9Sbenno 			goto out;
416bb41c969Sclaudio 		}
417bb41c969Sclaudio 		if (!io_read_int(sess, fdin, &ioerror)) {
418b2a7eac7Sbenno 			ERRX1("io_read_int");
41960a32ee9Sbenno 			goto out;
420bb41c969Sclaudio 		}
421bb41c969Sclaudio 		if (ioerror != -1) {
422b2a7eac7Sbenno 			ERRX("expected phase ack");
42360a32ee9Sbenno 			goto out;
42460a32ee9Sbenno 		}
42560a32ee9Sbenno 	}
42660a32ee9Sbenno 
42760a32ee9Sbenno 	/*
42860a32ee9Sbenno 	 * Now all of our transfers are complete, so we can fix up our
42960a32ee9Sbenno 	 * directory permissions.
43060a32ee9Sbenno 	 */
43160a32ee9Sbenno 
43260a32ee9Sbenno 	if (!rsync_uploader_tail(ul, sess)) {
433b2a7eac7Sbenno 		ERRX1("rsync_uploader_tail");
43460a32ee9Sbenno 		goto out;
43560a32ee9Sbenno 	}
43660a32ee9Sbenno 
43760a32ee9Sbenno 	/* Process server statistics and say good-bye. */
43860a32ee9Sbenno 
43960a32ee9Sbenno 	if (!sess_stats_recv(sess, fdin)) {
440b2a7eac7Sbenno 		ERRX1("sess_stats_recv");
44160a32ee9Sbenno 		goto out;
442bb41c969Sclaudio 	}
443bb41c969Sclaudio 	if (!io_write_int(sess, fdout, -1)) {
444b2a7eac7Sbenno 		ERRX1("io_write_int");
44560a32ee9Sbenno 		goto out;
44660a32ee9Sbenno 	}
44760a32ee9Sbenno 
448b2a7eac7Sbenno 	LOG2("receiver finished updating");
44960a32ee9Sbenno 	rc = 1;
45060a32ee9Sbenno out:
451f1dcb30aSderaadt 	if (dfd != -1)
45260a32ee9Sbenno 		close(dfd);
45360a32ee9Sbenno 	upload_free(ul);
45460a32ee9Sbenno 	download_free(dl);
45560a32ee9Sbenno 	flist_free(fl, flsz);
45660a32ee9Sbenno 	flist_free(dfl, dflsz);
45760a32ee9Sbenno 	return rc;
45860a32ee9Sbenno }
459