Annotation of src/usr.bin/rsync/receiver.c, Revision 1.28
1.28 ! claudio 1: /* $OpenBSD: receiver.c,v 1.27 2021/05/17 11:59:09 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;
175: size_t i, flsz = 0, dflsz = 0, excl;
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:
187: /* Client sends zero-length exclusions. */
188:
1.27 claudio 189: if (!sess->opts->server && !io_write_int(sess, fdout, 0)) {
1.23 benno 190: ERRX1("io_write_int");
1.1 benno 191: goto out;
192: }
193:
194: if (sess->opts->server && sess->opts->del) {
1.4 deraadt 195: if (!io_read_size(sess, fdin, &excl)) {
1.23 benno 196: ERRX1("io_read_size");
1.1 benno 197: goto out;
1.19 benno 198: } else if (excl != 0) {
1.23 benno 199: ERRX("exclusion list is non-empty");
1.1 benno 200: goto out;
201: }
202: }
203:
204: /*
205: * Start by receiving the file list and our mystery number.
206: * These we're going to be touching on our local system.
207: */
208:
1.4 deraadt 209: if (!flist_recv(sess, fdin, &fl, &flsz)) {
1.23 benno 210: ERRX1("flist_recv");
1.1 benno 211: goto out;
1.2 benno 212: }
213:
1.1 benno 214: /* The IO error is sent after the file list. */
215:
1.4 deraadt 216: if (!io_read_int(sess, fdin, &ioerror)) {
1.23 benno 217: ERRX1("io_read_int");
1.1 benno 218: goto out;
1.19 benno 219: } else if (ioerror != 0) {
1.23 benno 220: ERRX1("io_error is non-zero");
1.1 benno 221: goto out;
222: }
223:
1.5 deraadt 224: if (flsz == 0 && !sess->opts->server) {
1.23 benno 225: WARNX("receiver has empty file list: exiting");
1.1 benno 226: rc = 1;
227: goto out;
1.4 deraadt 228: } else if (!sess->opts->server)
1.23 benno 229: LOG1("Transfer starting: %zu files", flsz);
1.1 benno 230:
1.23 benno 231: LOG2("%s: receiver destination", root);
1.1 benno 232:
233: /*
234: * Create the path for our destination directory, if we're not
235: * in dry-run mode (which would otherwise crash w/the pledge).
236: * This uses our current umask: we might set the permissions on
237: * this directory in post_dir().
238: */
239:
1.4 deraadt 240: if (!sess->opts->dry_run) {
1.27 claudio 241: if ((tofree = strdup(root)) == NULL)
242: err(ERR_NOMEM, NULL);
243: if (mkpath(tofree) < 0)
244: err(ERR_FILE_IO, "%s: mkpath", tofree);
1.1 benno 245: free(tofree);
246: }
247:
248: /*
249: * Disable umask() so we can set permissions fully.
250: * Then open the directory iff we're not in dry_run.
251: */
252:
253: oumask = umask(0);
254:
1.4 deraadt 255: if (!sess->opts->dry_run) {
1.1 benno 256: dfd = open(root, O_RDONLY | O_DIRECTORY, 0);
1.27 claudio 257: if (dfd == -1)
258: err(ERR_FILE_IO, "%s: open", root);
1.1 benno 259: }
260:
261: /*
262: * Begin by conditionally getting all files we have currently
263: * available in our destination.
264: */
265:
1.2 benno 266: if (sess->opts->del &&
1.1 benno 267: sess->opts->recursive &&
1.4 deraadt 268: !flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
1.23 benno 269: ERRX1("flist_gen_local");
1.1 benno 270: goto out;
271: }
272:
273: /*
274: * Make our entire view of the file-system be limited to what's
275: * in the root directory.
276: * This prevents us from accidentally (or "under the influence")
277: * writing into other parts of the file-system.
278: */
279:
1.27 claudio 280: if (unveil(root, "rwc") == -1)
281: err(ERR_IPC, "%s: unveil", root);
282: if (unveil(NULL, NULL) == -1)
283: err(ERR_IPC, "unveil");
1.1 benno 284:
285: /* If we have a local set, go for the deletion. */
286:
1.4 deraadt 287: if (!flist_del(sess, dfd, dfl, dflsz)) {
1.23 benno 288: ERRX1("flist_del");
1.1 benno 289: goto out;
290: }
291:
292: /* Initialise poll events to listen from the sender. */
293:
294: pfd[PFD_SENDER_IN].fd = fdin;
295: pfd[PFD_UPLOADER_IN].fd = -1;
296: pfd[PFD_DOWNLOADER_IN].fd = -1;
297: pfd[PFD_SENDER_OUT].fd = fdout;
298:
299: pfd[PFD_SENDER_IN].events = POLLIN;
300: pfd[PFD_UPLOADER_IN].events = POLLIN;
301: pfd[PFD_DOWNLOADER_IN].events = POLLIN;
302: pfd[PFD_SENDER_OUT].events = POLLOUT;
303:
1.24 benno 304: ul = upload_alloc(root, dfd, fdout, CSUM_LENGTH_PHASE1, fl, flsz,
305: oumask);
306:
1.5 deraadt 307: if (ul == NULL) {
1.23 benno 308: ERRX1("upload_alloc");
1.1 benno 309: goto out;
310: }
311:
312: dl = download_alloc(sess, fdin, fl, flsz, dfd);
1.5 deraadt 313: if (dl == NULL) {
1.23 benno 314: ERRX1("download_alloc");
1.1 benno 315: goto out;
316: }
317:
1.23 benno 318: LOG2("%s: ready for phase 1 data", root);
1.1 benno 319:
320: for (;;) {
1.25 claudio 321: if ((c = poll(pfd, PFD__MAX, poll_timeout)) == -1) {
1.23 benno 322: ERR("poll");
1.16 florian 323: goto out;
324: } else if (c == 0) {
1.23 benno 325: ERRX("poll: timeout");
1.1 benno 326: goto out;
1.2 benno 327: }
1.1 benno 328:
1.2 benno 329: for (i = 0; i < PFD__MAX; i++)
1.1 benno 330: if (pfd[i].revents & (POLLERR|POLLNVAL)) {
1.23 benno 331: ERRX("poll: bad fd");
1.1 benno 332: goto out;
333: } else if (pfd[i].revents & POLLHUP) {
1.23 benno 334: ERRX("poll: hangup");
1.1 benno 335: goto out;
336: }
337:
338: /*
339: * If we have a read event and we're multiplexing, we
340: * might just have error messages in the pipe.
341: * It's important to flush these out so that we don't
342: * clog the pipe.
343: * Unset our polling status if there's nothing that
344: * remains in the pipe.
345: */
346:
347: if (sess->mplex_reads &&
1.26 claudio 348: (pfd[PFD_SENDER_IN].revents & POLLIN)) {
1.4 deraadt 349: if (!io_read_flush(sess, fdin)) {
1.23 benno 350: ERRX1("io_read_flush");
1.1 benno 351: goto out;
1.5 deraadt 352: } else if (sess->mplex_read_remain == 0)
1.1 benno 353: pfd[PFD_SENDER_IN].revents &= ~POLLIN;
354: }
355:
356:
357: /*
358: * We run the uploader if we have files left to examine
359: * (i < flsz) or if we have a file that we've opened and
360: * is read to mmap.
361: */
362:
1.26 claudio 363: if ((pfd[PFD_UPLOADER_IN].revents & POLLIN) ||
364: (pfd[PFD_SENDER_OUT].revents & POLLOUT)) {
1.2 benno 365: c = rsync_uploader(ul,
366: &pfd[PFD_UPLOADER_IN].fd,
1.1 benno 367: sess, &pfd[PFD_SENDER_OUT].fd);
368: if (c < 0) {
1.23 benno 369: ERRX1("rsync_uploader");
1.1 benno 370: goto out;
371: }
372: }
373:
1.2 benno 374: /*
1.1 benno 375: * We need to run the downloader when we either have
376: * read events from the sender or an asynchronous local
377: * open is ready.
378: * XXX: we don't disable PFD_SENDER_IN like with the
379: * uploader because we might stop getting error
380: * messages, which will otherwise clog up the pipes.
381: */
382:
1.26 claudio 383: if ((pfd[PFD_SENDER_IN].revents & POLLIN) ||
384: (pfd[PFD_DOWNLOADER_IN].revents & POLLIN)) {
1.2 benno 385: c = rsync_downloader(dl, sess,
1.1 benno 386: &pfd[PFD_DOWNLOADER_IN].fd);
387: if (c < 0) {
1.23 benno 388: ERRX1("rsync_downloader");
1.1 benno 389: goto out;
1.5 deraadt 390: } else if (c == 0) {
391: assert(phase == 0);
1.1 benno 392: phase++;
1.23 benno 393: LOG2("%s: receiver ready for phase 2 data", root);
1.1 benno 394: break;
395: }
396:
397: /*
398: * FIXME: if we have any errors during the
399: * download, most notably files getting out of
400: * sync between the send and the receiver, then
401: * here we should bump our checksum length and
402: * go into the second phase.
403: */
1.2 benno 404: }
1.1 benno 405: }
406:
407: /* Properly close us out by progressing through the phases. */
408:
1.5 deraadt 409: if (phase == 1) {
1.4 deraadt 410: if (!io_write_int(sess, fdout, -1)) {
1.23 benno 411: ERRX1("io_write_int");
1.1 benno 412: goto out;
1.26 claudio 413: }
414: if (!io_read_int(sess, fdin, &ioerror)) {
1.23 benno 415: ERRX1("io_read_int");
1.1 benno 416: goto out;
1.26 claudio 417: }
418: if (ioerror != -1) {
1.23 benno 419: ERRX("expected phase ack");
1.1 benno 420: goto out;
421: }
422: }
423:
424: /*
425: * Now all of our transfers are complete, so we can fix up our
426: * directory permissions.
427: */
428:
1.4 deraadt 429: if (!rsync_uploader_tail(ul, sess)) {
1.23 benno 430: ERRX1("rsync_uploader_tail");
1.1 benno 431: goto out;
432: }
433:
434: /* Process server statistics and say good-bye. */
435:
1.4 deraadt 436: if (!sess_stats_recv(sess, fdin)) {
1.23 benno 437: ERRX1("sess_stats_recv");
1.1 benno 438: goto out;
1.26 claudio 439: }
440: if (!io_write_int(sess, fdout, -1)) {
1.23 benno 441: ERRX1("io_write_int");
1.1 benno 442: goto out;
443: }
444:
1.23 benno 445: LOG2("receiver finished updating");
1.1 benno 446: rc = 1;
447: out:
1.5 deraadt 448: if (dfd != -1)
1.1 benno 449: close(dfd);
450: upload_free(ul);
451: download_free(dl);
452: flist_free(fl, flsz);
453: flist_free(dfl, dflsz);
454: return rc;
455: }