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