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