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