Annotation of src/usr.bin/rsync/receiver.c, Revision 1.23
1.23 ! benno 1: /* $Id: receiver.c,v 1.22 2019/03/30 07:28:55 deraadt 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>
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: };
1.7 florian 43:
44: int
45: rsync_set_metadata(struct sess *sess, int newfile,
46: int fd, const struct flist *f, const char *path)
47: {
1.8 florian 48: uid_t uid = (uid_t)-1;
49: gid_t gid = (gid_t)-1;
1.22 deraadt 50: mode_t mode;
1.20 cheloha 51: struct timespec ts[2];
1.7 florian 52:
1.22 deraadt 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) {
1.23 ! benno 60: ERR("%s: futimens", path);
1.22 deraadt 61: return 0;
62: }
1.23 ! benno 63: LOG4("%s: updated date", f->path);
1.22 deraadt 64: }
65:
1.7 florian 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: */
1.8 florian 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:
1.22 deraadt 77: mode = f->st.mode;
1.8 florian 78: if (uid != (uid_t)-1 || gid != (gid_t)-1) {
1.7 florian 79: if (fchown(fd, uid, gid) == -1) {
80: if (errno != EPERM) {
1.23 ! benno 81: ERR("%s: fchown", path);
1.7 florian 82: return 0;
83: }
1.22 deraadt 84: if (getuid() == 0)
1.23 ! benno 85: WARNX("%s: identity unknown or not available "
1.22 deraadt 86: "to user.group: %u.%u", f->path, uid, gid);
1.7 florian 87: } else
1.23 ! benno 88: LOG4("%s: updated uid and/or gid", f->path);
1.22 deraadt 89: mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
1.13 florian 90: }
91:
92: /* Conditionally adjust file permissions. */
93:
94: if (newfile || sess->opts->preserve_perms) {
1.22 deraadt 95: if (fchmod(fd, mode) == -1) {
1.23 ! benno 96: ERR("%s: fchmod", path);
1.13 florian 97: return 0;
98: }
1.23 ! benno 99: LOG4("%s: updated permissions", f->path);
1.7 florian 100: }
101:
102: return 1;
103: }
1.1 benno 104:
1.14 florian 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;
1.22 deraadt 111: mode_t mode;
1.20 cheloha 112: struct timespec ts[2];
1.14 florian 113:
1.22 deraadt 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) {
1.23 ! benno 121: ERR("%s: utimensat", path);
1.22 deraadt 122: return 0;
123: }
1.23 ! benno 124: LOG4("%s: updated date", f->path);
1.22 deraadt 125: }
126:
1.14 florian 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:
1.22 deraadt 138: mode = f->st.mode;
1.14 florian 139: if (uid != (uid_t)-1 || gid != (gid_t)-1) {
1.22 deraadt 140: if (fchownat(rootfd, path, uid, gid, AT_SYMLINK_NOFOLLOW) == -1) {
1.14 florian 141: if (errno != EPERM) {
1.23 ! benno 142: ERR("%s: fchownat", path);
1.14 florian 143: return 0;
144: }
1.22 deraadt 145: if (getuid() == 0)
1.23 ! benno 146: WARNX("%s: identity unknown or not available "
1.22 deraadt 147: "to user.group: %u.%u", f->path, uid, gid);
1.14 florian 148: } else
1.23 ! benno 149: LOG4("%s: updated uid and/or gid", f->path);
1.22 deraadt 150: mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
1.14 florian 151: }
152:
153: /* Conditionally adjust file permissions. */
154:
155: if (newfile || sess->opts->preserve_perms) {
1.22 deraadt 156: if (fchmodat(rootfd, path, mode, AT_SYMLINK_NOFOLLOW) == -1) {
1.23 ! benno 157: ERR("%s: fchmodat", path);
1.14 florian 158: return 0;
159: }
1.23 ! benno 160: LOG4("%s: updated permissions", f->path);
1.14 florian 161: }
162:
163: return 1;
164: }
165:
1.2 benno 166: /*
1.14 florian 167: * Pledges: unveil, unix, rpath, cpath, wpath, stdio, fattr, chown.
168: * Pledges (dry-run): -unix, -cpath, -wpath, -fattr, -chown.
1.1 benno 169: */
170: int
1.9 deraadt 171: rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root)
1.1 benno 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;
1.2 benno 177: int32_t ioerror;
1.1 benno 178: struct pollfd pfd[PFD__MAX];
179: struct download *dl = NULL;
1.2 benno 180: struct upload *ul = NULL;
1.1 benno 181: mode_t oumask;
182:
1.14 florian 183: if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) {
1.23 ! benno 184: ERR("pledge");
1.1 benno 185: goto out;
186: }
187:
188: /* Client sends zero-length exclusions. */
189:
1.4 deraadt 190: if (!sess->opts->server &&
191: !io_write_int(sess, fdout, 0)) {
1.23 ! benno 192: ERRX1("io_write_int");
1.1 benno 193: goto out;
194: }
195:
196: if (sess->opts->server && sess->opts->del) {
1.4 deraadt 197: if (!io_read_size(sess, fdin, &excl)) {
1.23 ! benno 198: ERRX1("io_read_size");
1.1 benno 199: goto out;
1.19 benno 200: } else if (excl != 0) {
1.23 ! benno 201: ERRX("exclusion list is non-empty");
1.1 benno 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:
1.4 deraadt 211: if (!flist_recv(sess, fdin, &fl, &flsz)) {
1.23 ! benno 212: ERRX1("flist_recv");
1.1 benno 213: goto out;
1.2 benno 214: }
215:
1.1 benno 216: /* The IO error is sent after the file list. */
217:
1.4 deraadt 218: if (!io_read_int(sess, fdin, &ioerror)) {
1.23 ! benno 219: ERRX1("io_read_int");
1.1 benno 220: goto out;
1.19 benno 221: } else if (ioerror != 0) {
1.23 ! benno 222: ERRX1("io_error is non-zero");
1.1 benno 223: goto out;
224: }
225:
1.5 deraadt 226: if (flsz == 0 && !sess->opts->server) {
1.23 ! benno 227: WARNX("receiver has empty file list: exiting");
1.1 benno 228: rc = 1;
229: goto out;
1.4 deraadt 230: } else if (!sess->opts->server)
1.23 ! benno 231: LOG1("Transfer starting: %zu files", flsz);
1.1 benno 232:
1.23 ! benno 233: LOG2("%s: receiver destination", root);
1.1 benno 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:
1.4 deraadt 242: if (!sess->opts->dry_run) {
1.5 deraadt 243: if ((tofree = strdup(root)) == NULL) {
1.23 ! benno 244: ERR("strdup");
1.1 benno 245: goto out;
246: } else if (mkpath(sess, tofree) < 0) {
1.23 ! benno 247: ERRX1("%s: mkpath", root);
1.1 benno 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:
1.4 deraadt 261: if (!sess->opts->dry_run) {
1.1 benno 262: dfd = open(root, O_RDONLY | O_DIRECTORY, 0);
1.5 deraadt 263: if (dfd == -1) {
1.23 ! benno 264: ERR("%s: open", root);
1.1 benno 265: goto out;
266: }
267: }
268:
269: /*
270: * Begin by conditionally getting all files we have currently
271: * available in our destination.
272: */
273:
1.2 benno 274: if (sess->opts->del &&
1.1 benno 275: sess->opts->recursive &&
1.4 deraadt 276: !flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
1.23 ! benno 277: ERRX1("flist_gen_local");
1.1 benno 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:
1.5 deraadt 288: if (unveil(root, "rwc") == -1) {
1.23 ! benno 289: ERR("%s: unveil", root);
1.1 benno 290: goto out;
1.5 deraadt 291: } else if (unveil(NULL, NULL) == -1) {
1.23 ! benno 292: ERR("%s: unveil", root);
1.1 benno 293: goto out;
294: }
295:
296: /* If we have a local set, go for the deletion. */
297:
1.4 deraadt 298: if (!flist_del(sess, dfd, dfl, dflsz)) {
1.23 ! benno 299: ERRX1("flist_del");
1.1 benno 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:
1.14 florian 315: ul = upload_alloc(sess, root, dfd, fdout,
1.1 benno 316: CSUM_LENGTH_PHASE1, fl, flsz, oumask);
1.5 deraadt 317: if (ul == NULL) {
1.23 ! benno 318: ERRX1("upload_alloc");
1.1 benno 319: goto out;
320: }
321:
322: dl = download_alloc(sess, fdin, fl, flsz, dfd);
1.5 deraadt 323: if (dl == NULL) {
1.23 ! benno 324: ERRX1("download_alloc");
1.1 benno 325: goto out;
326: }
327:
1.23 ! benno 328: LOG2("%s: ready for phase 1 data", root);
1.1 benno 329:
330: for (;;) {
1.16 florian 331: if ((c = poll(pfd, PFD__MAX, POLL_TIMEOUT)) == -1) {
1.23 ! benno 332: ERR("poll");
1.16 florian 333: goto out;
334: } else if (c == 0) {
1.23 ! benno 335: ERRX("poll: timeout");
1.1 benno 336: goto out;
1.2 benno 337: }
1.1 benno 338:
1.2 benno 339: for (i = 0; i < PFD__MAX; i++)
1.1 benno 340: if (pfd[i].revents & (POLLERR|POLLNVAL)) {
1.23 ! benno 341: ERRX("poll: bad fd");
1.1 benno 342: goto out;
343: } else if (pfd[i].revents & POLLHUP) {
1.23 ! benno 344: ERRX("poll: hangup");
1.1 benno 345: goto out;
346: }
347:
348: /*
349: * If we have a read event and we're multiplexing, we
350: * might just have error messages in the pipe.
351: * It's important to flush these out so that we don't
352: * clog the pipe.
353: * Unset our polling status if there's nothing that
354: * remains in the pipe.
355: */
356:
357: if (sess->mplex_reads &&
358: (POLLIN & pfd[PFD_SENDER_IN].revents)) {
1.4 deraadt 359: if (!io_read_flush(sess, fdin)) {
1.23 ! benno 360: ERRX1("io_read_flush");
1.1 benno 361: goto out;
1.5 deraadt 362: } else if (sess->mplex_read_remain == 0)
1.1 benno 363: pfd[PFD_SENDER_IN].revents &= ~POLLIN;
364: }
365:
366:
367: /*
368: * We run the uploader if we have files left to examine
369: * (i < flsz) or if we have a file that we've opened and
370: * is read to mmap.
371: */
372:
373: if ((POLLIN & pfd[PFD_UPLOADER_IN].revents) ||
374: (POLLOUT & pfd[PFD_SENDER_OUT].revents)) {
1.2 benno 375: c = rsync_uploader(ul,
376: &pfd[PFD_UPLOADER_IN].fd,
1.1 benno 377: sess, &pfd[PFD_SENDER_OUT].fd);
378: if (c < 0) {
1.23 ! benno 379: ERRX1("rsync_uploader");
1.1 benno 380: goto out;
381: }
382: }
383:
1.2 benno 384: /*
1.1 benno 385: * We need to run the downloader when we either have
386: * read events from the sender or an asynchronous local
387: * open is ready.
388: * XXX: we don't disable PFD_SENDER_IN like with the
389: * uploader because we might stop getting error
390: * messages, which will otherwise clog up the pipes.
391: */
392:
1.2 benno 393: if ((POLLIN & pfd[PFD_SENDER_IN].revents) ||
1.1 benno 394: (POLLIN & pfd[PFD_DOWNLOADER_IN].revents)) {
1.2 benno 395: c = rsync_downloader(dl, sess,
1.1 benno 396: &pfd[PFD_DOWNLOADER_IN].fd);
397: if (c < 0) {
1.23 ! benno 398: ERRX1("rsync_downloader");
1.1 benno 399: goto out;
1.5 deraadt 400: } else if (c == 0) {
401: assert(phase == 0);
1.1 benno 402: phase++;
1.23 ! benno 403: LOG2("%s: receiver ready for phase 2 data", root);
1.1 benno 404: break;
405: }
406:
407: /*
408: * FIXME: if we have any errors during the
409: * download, most notably files getting out of
410: * sync between the send and the receiver, then
411: * here we should bump our checksum length and
412: * go into the second phase.
413: */
1.2 benno 414: }
1.1 benno 415: }
416:
417: /* Properly close us out by progressing through the phases. */
418:
1.5 deraadt 419: if (phase == 1) {
1.4 deraadt 420: if (!io_write_int(sess, fdout, -1)) {
1.23 ! benno 421: ERRX1("io_write_int");
1.1 benno 422: goto out;
1.4 deraadt 423: } else if (!io_read_int(sess, fdin, &ioerror)) {
1.23 ! benno 424: ERRX1("io_read_int");
1.1 benno 425: goto out;
1.5 deraadt 426: } else if (ioerror != -1) {
1.23 ! benno 427: ERRX("expected phase ack");
1.1 benno 428: goto out;
429: }
430: }
431:
432: /*
433: * Now all of our transfers are complete, so we can fix up our
434: * directory permissions.
435: */
436:
1.4 deraadt 437: if (!rsync_uploader_tail(ul, sess)) {
1.23 ! benno 438: ERRX1("rsync_uploader_tail");
1.1 benno 439: goto out;
440: }
441:
442: /* Process server statistics and say good-bye. */
443:
1.4 deraadt 444: if (!sess_stats_recv(sess, fdin)) {
1.23 ! benno 445: ERRX1("sess_stats_recv");
1.1 benno 446: goto out;
1.4 deraadt 447: } else if (!io_write_int(sess, fdout, -1)) {
1.23 ! benno 448: ERRX1("io_write_int");
1.1 benno 449: goto out;
450: }
451:
1.23 ! benno 452: LOG2("receiver finished updating");
1.1 benno 453: rc = 1;
454: out:
1.5 deraadt 455: if (dfd != -1)
1.1 benno 456: close(dfd);
457: upload_free(ul);
458: download_free(dl);
459: flist_free(fl, flsz);
460: flist_free(dfl, dflsz);
461: return rc;
462: }