Annotation of src/usr.bin/rsync/uploader.c, Revision 1.35
1.35 ! job 1: /* $OpenBSD: uploader.c,v 1.34 2023/04/28 10:24:39 claudio Exp $ */
1.1 benno 2: /*
3: * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
1.12 florian 4: * Copyright (c) 2019 Florian Obser <florian@openbsd.org>
1.1 benno 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18: #include <sys/mman.h>
19: #include <sys/stat.h>
20:
21: #include <assert.h>
1.30 claudio 22: #include <err.h>
1.1 benno 23: #include <errno.h>
24: #include <fcntl.h>
25: #include <inttypes.h>
26: #include <math.h>
27: #include <stdio.h>
28: #include <stdlib.h>
29: #include <string.h>
30: #include <time.h>
31: #include <unistd.h>
32:
33: #include "extern.h"
34:
35: enum uploadst {
36: UPLOAD_FIND_NEXT = 0, /* find next to upload to sender */
1.25 claudio 37: UPLOAD_WRITE, /* wait to write to sender */
1.1 benno 38: UPLOAD_FINISHED /* nothing more to do in phase */
39: };
40:
41: /*
42: * Used to keep track of data flowing from the receiver to the sender.
43: * This is managed by the receiver process.
44: */
45: struct upload {
46: enum uploadst state;
47: char *buf; /* if not NULL, pending upload */
48: size_t bufsz; /* size of buf */
49: size_t bufmax; /* maximum size of buf */
50: size_t bufpos; /* position in buf */
51: size_t idx; /* current transfer index */
52: mode_t oumask; /* umask for creating files */
1.11 florian 53: char *root; /* destination directory path */
1.1 benno 54: int rootfd; /* destination directory */
55: size_t csumlen; /* checksum length */
56: int fdout; /* write descriptor to sender */
57: const struct flist *fl; /* file list */
58: size_t flsz; /* size of file list */
59: int *newdir; /* non-zero if mkdir'd */
60: };
61:
62: /*
63: * Log a directory by emitting the file and a trailing slash, just to
64: * show the operator that we're a directory.
65: */
66: static void
67: log_dir(struct sess *sess, const struct flist *f)
68: {
69: size_t sz;
70:
71: if (sess->opts->server)
72: return;
73: sz = strlen(f->path);
74: assert(sz > 0);
1.21 benno 75: LOG1("%s%s", f->path, (f->path[sz - 1] == '/') ? "" : "/");
1.1 benno 76: }
77:
78: /*
79: * Log a link by emitting the file and the target, just to show the
80: * operator that we're a link.
81: */
82: static void
1.26 claudio 83: log_symlink(struct sess *sess, const struct flist *f)
1.1 benno 84: {
85:
1.3 deraadt 86: if (!sess->opts->server)
1.21 benno 87: LOG1("%s -> %s", f->path, f->link);
1.1 benno 88: }
89:
90: /*
91: * Simply log the filename.
92: */
93: static void
94: log_file(struct sess *sess, const struct flist *f)
95: {
96:
1.3 deraadt 97: if (!sess->opts->server)
1.21 benno 98: LOG1("%s", f->path);
1.1 benno 99: }
100:
101: /*
102: * Prepare the overall block set's metadata.
103: * We always have at least one block.
104: * The block size is an important part of the algorithm.
105: * I use the same heuristic as the reference rsync, but implemented in a
106: * bit more of a straightforward way.
107: * In general, the individual block length is the rounded square root of
108: * the total file size.
109: * The minimum block length is 700.
110: */
111: static void
112: init_blkset(struct blkset *p, off_t sz)
113: {
114: double v;
115:
116: if (sz >= (BLOCK_SIZE_MIN * BLOCK_SIZE_MIN)) {
117: /* Simple rounded-up integer square root. */
118:
119: v = sqrt(sz);
120: p->len = ceil(v);
121:
1.2 benno 122: /*
1.1 benno 123: * Always be a multiple of eight.
124: * There's no reason to do this, but rsync does.
125: */
126:
127: if ((p->len % 8) > 0)
128: p->len += 8 - (p->len % 8);
129: } else
130: p->len = BLOCK_SIZE_MIN;
131:
132: p->size = sz;
1.4 deraadt 133: if ((p->blksz = sz / p->len) == 0)
1.1 benno 134: p->rem = sz;
135: else
136: p->rem = sz % p->len;
137:
138: /* If we have a remainder, then we need an extra block. */
139:
140: if (p->rem)
141: p->blksz++;
142: }
143:
144: /*
145: * For each block, prepare the block's metadata.
146: * We use the mapped "map" file to set our checksums.
147: */
148: static void
149: init_blk(struct blk *p, const struct blkset *set, off_t offs,
150: size_t idx, const void *map, const struct sess *sess)
151: {
152:
1.24 claudio 153: p->idx = idx;
1.1 benno 154: /* Block length inherits for all but the last. */
155: p->len = idx < set->blksz - 1 ? set->len : set->rem;
156: p->offs = offs;
157:
1.23 benno 158: p->chksum_short = hash_fast(map, p->len);
159: hash_slow(map, p->len, p->chksum_long, sess);
1.1 benno 160: }
161:
162: /*
1.17 benno 163: * Handle a symbolic link.
164: * If we encounter directories existing in the symbolic link's place,
165: * then try to unlink the directory.
166: * Otherwise, simply overwrite with the symbolic link by renaming.
1.1 benno 167: * Return <0 on failure 0 on success.
168: */
169: static int
1.30 claudio 170: pre_symlink(struct upload *p, struct sess *sess)
1.1 benno 171: {
1.9 florian 172: struct stat st;
173: const struct flist *f;
174: int rc, newlink = 0, updatelink = 0;
1.11 florian 175: char *b, *temp = NULL;
1.1 benno 176:
177: f = &p->fl[p->idx];
178: assert(S_ISLNK(f->st.mode));
179:
1.3 deraadt 180: if (!sess->opts->preserve_links) {
1.21 benno 181: WARNX("%s: ignoring symlink", f->path);
1.1 benno 182: return 0;
1.25 claudio 183: }
184: if (sess->opts->dry_run) {
1.26 claudio 185: log_symlink(sess, f);
1.1 benno 186: return 0;
187: }
188:
1.17 benno 189: /*
190: * See if the symlink already exists.
191: * If it's a directory, then try to unlink the directory prior
192: * to overwriting with a symbolic link.
193: * If it's a non-directory, we just overwrite it.
194: */
1.1 benno 195:
1.4 deraadt 196: assert(p->rootfd != -1);
1.1 benno 197: rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
1.28 claudio 198:
199: if (rc == -1 && errno != ENOENT) {
200: ERR("%s: fstatat", f->path);
201: return -1;
202: }
1.4 deraadt 203: if (rc != -1 && !S_ISLNK(st.st_mode)) {
1.17 benno 204: if (S_ISDIR(st.st_mode) &&
205: unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
1.21 benno 206: ERR("%s: unlinkat", f->path);
1.17 benno 207: return -1;
1.9 florian 208: }
1.17 benno 209: rc = -1;
1.1 benno 210: }
211:
212: /*
213: * If the symbolic link already exists, then make sure that it
214: * points to the correct place.
215: */
216:
1.9 florian 217: if (rc != -1) {
1.22 benno 218: b = symlinkat_read(p->rootfd, f->path);
1.4 deraadt 219: if (b == NULL) {
1.21 benno 220: ERRX1("symlinkat_read");
1.1 benno 221: return -1;
222: }
223: if (strcmp(f->link, b)) {
224: free(b);
225: b = NULL;
1.21 benno 226: LOG3("%s: updating symlink: %s", f->path, f->link);
1.9 florian 227: updatelink = 1;
1.2 benno 228: }
1.1 benno 229: free(b);
1.9 florian 230: b = NULL;
231: }
232:
1.17 benno 233: /*
234: * Create the temporary file as a symbolic link, then rename the
235: * temporary file as the real one, overwriting anything there.
236: */
237:
1.9 florian 238: if (rc == -1 || updatelink) {
1.21 benno 239: LOG3("%s: creating symlink: %s", f->path, f->link);
1.22 benno 240: if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
1.21 benno 241: ERRX1("mktemplate");
1.9 florian 242: return -1;
243: }
1.11 florian 244: if (mkstemplinkat(f->link, p->rootfd, temp) == NULL) {
1.21 benno 245: ERR("mkstemplinkat");
1.11 florian 246: free(temp);
1.9 florian 247: return -1;
248: }
249: newlink = 1;
1.1 benno 250: }
251:
1.17 benno 252: rsync_set_metadata_at(sess, newlink,
253: p->rootfd, f, newlink ? temp : f->path);
1.11 florian 254:
255: if (newlink) {
256: if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
1.21 benno 257: ERR("%s: renameat %s", temp, f->path);
1.11 florian 258: (void)unlinkat(p->rootfd, temp, 0);
259: free(temp);
260: return -1;
261: }
262: free(temp);
263: }
264:
1.26 claudio 265: log_symlink(sess, f);
1.11 florian 266: return 0;
267: }
268:
269: /*
1.30 claudio 270: * See pre_symlink(), but for devices.
1.17 benno 271: * FIXME: this is very similar to the other pre_xxx() functions.
1.11 florian 272: * Return <0 on failure 0 on success.
273: */
274: static int
275: pre_dev(struct upload *p, struct sess *sess)
276: {
277: struct stat st;
278: const struct flist *f;
279: int rc, newdev = 0, updatedev = 0;
280: char *temp = NULL;
281:
282: f = &p->fl[p->idx];
283: assert(S_ISBLK(f->st.mode) || S_ISCHR(f->st.mode));
284:
285: if (!sess->opts->devices || getuid() != 0) {
1.21 benno 286: WARNX("skipping non-regular file %s", f->path);
1.11 florian 287: return 0;
1.25 claudio 288: }
289: if (sess->opts->dry_run) {
1.11 florian 290: log_file(sess, f);
291: return 0;
292: }
293:
1.17 benno 294: /*
295: * See if the dev already exists.
296: * If a non-device exists in its place, we'll replace that.
297: * If it replaces a directory, remove the directory first.
298: */
299:
1.16 benno 300: assert(p->rootfd != -1);
1.11 florian 301: rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
302:
1.28 claudio 303: if (rc == -1 && errno != ENOENT) {
304: ERR("%s: fstatat", f->path);
305: return -1;
306: }
1.11 florian 307: if (rc != -1 && !(S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) {
1.17 benno 308: if (S_ISDIR(st.st_mode) &&
309: unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
1.21 benno 310: ERR("%s: unlinkat", f->path);
1.17 benno 311: return -1;
1.11 florian 312: }
1.17 benno 313: rc = -1;
1.11 florian 314: }
315:
1.17 benno 316: /* Make sure existing device is of the correct type. */
1.1 benno 317:
1.11 florian 318: if (rc != -1) {
319: if ((f->st.mode & (S_IFCHR|S_IFBLK)) !=
1.17 benno 320: (st.st_mode & (S_IFCHR|S_IFBLK)) ||
321: f->st.rdev != st.st_rdev) {
1.21 benno 322: LOG3("%s: updating device", f->path);
1.11 florian 323: updatedev = 1;
324: }
325: }
326:
327: if (rc == -1 || updatedev) {
328: newdev = 1;
1.22 benno 329: if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
1.21 benno 330: ERRX1("mktemplate");
1.11 florian 331: return -1;
332: }
1.17 benno 333: if (mkstempnodat(p->rootfd, temp,
334: f->st.mode & (S_IFCHR|S_IFBLK), f->st.rdev) == NULL) {
1.21 benno 335: ERR("mkstempnodat");
1.11 florian 336: free(temp);
337: return -1;
338: }
339: }
340:
1.17 benno 341: rsync_set_metadata_at(sess, newdev,
1.22 benno 342: p->rootfd, f, newdev ? temp : f->path);
1.11 florian 343:
344: if (newdev) {
345: if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
1.21 benno 346: ERR("%s: renameat %s", temp, f->path);
1.11 florian 347: (void)unlinkat(p->rootfd, temp, 0);
348: free(temp);
349: return -1;
350: }
351: free(temp);
352: }
1.17 benno 353:
1.11 florian 354: log_file(sess, f);
355: return 0;
356: }
357:
358: /*
1.30 claudio 359: * See pre_symlink(), but for FIFOs.
1.17 benno 360: * FIXME: this is very similar to the other pre_xxx() functions.
1.11 florian 361: * Return <0 on failure 0 on success.
362: */
363: static int
364: pre_fifo(struct upload *p, struct sess *sess)
365: {
366: struct stat st;
367: const struct flist *f;
368: int rc, newfifo = 0;
369: char *temp = NULL;
370:
371: f = &p->fl[p->idx];
372: assert(S_ISFIFO(f->st.mode));
373:
374: if (!sess->opts->specials) {
1.21 benno 375: WARNX("skipping non-regular file %s", f->path);
1.11 florian 376: return 0;
1.25 claudio 377: }
378: if (sess->opts->dry_run) {
1.11 florian 379: log_file(sess, f);
380: return 0;
381: }
382:
1.17 benno 383: /*
384: * See if the fifo already exists.
385: * If it exists as a non-FIFO, unlink it (if a directory) then
386: * mark it from replacement.
387: */
388:
1.16 benno 389: assert(p->rootfd != -1);
1.11 florian 390: rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
1.8 deraadt 391:
1.28 claudio 392: if (rc == -1 && errno != ENOENT) {
393: ERR("%s: fstatat", f->path);
394: return -1;
395: }
1.11 florian 396: if (rc != -1 && !S_ISFIFO(st.st_mode)) {
1.17 benno 397: if (S_ISDIR(st.st_mode) &&
398: unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
1.21 benno 399: ERR("%s: unlinkat", f->path);
1.17 benno 400: return -1;
1.11 florian 401: }
1.17 benno 402: rc = -1;
1.11 florian 403: }
404:
405: if (rc == -1) {
406: newfifo = 1;
1.22 benno 407: if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
1.21 benno 408: ERRX1("mktemplate");
1.11 florian 409: return -1;
410: }
411: if (mkstempfifoat(p->rootfd, temp) == NULL) {
1.21 benno 412: ERR("mkstempfifoat");
1.11 florian 413: free(temp);
1.1 benno 414: return -1;
415: }
416: }
1.2 benno 417:
1.17 benno 418: rsync_set_metadata_at(sess, newfifo,
419: p->rootfd, f, newfifo ? temp : f->path);
1.11 florian 420:
421: if (newfifo) {
422: if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
1.21 benno 423: ERR("%s: renameat %s", temp, f->path);
1.11 florian 424: (void)unlinkat(p->rootfd, temp, 0);
425: free(temp);
426: return -1;
427: }
428: free(temp);
429: }
1.17 benno 430:
1.11 florian 431: log_file(sess, f);
432: return 0;
433: }
1.1 benno 434:
1.11 florian 435: /*
1.30 claudio 436: * See pre_symlink(), but for socket files.
1.17 benno 437: * FIXME: this is very similar to the other pre_xxx() functions.
1.11 florian 438: * Return <0 on failure 0 on success.
439: */
440: static int
441: pre_sock(struct upload *p, struct sess *sess)
442: {
443: struct stat st;
444: const struct flist *f;
445: int rc, newsock = 0;
446: char *temp = NULL;
447:
448: f = &p->fl[p->idx];
449: assert(S_ISSOCK(f->st.mode));
450:
451: if (!sess->opts->specials) {
1.21 benno 452: WARNX("skipping non-regular file %s", f->path);
1.11 florian 453: return 0;
1.25 claudio 454: }
455: if (sess->opts->dry_run) {
1.11 florian 456: log_file(sess, f);
457: return 0;
458: }
459:
1.17 benno 460: /*
461: * See if the fifo already exists.
462: * If it exists as a non-FIFO, unlink it (if a directory) then
463: * mark it from replacement.
464: */
465:
1.18 deraadt 466: assert(p->rootfd != -1);
1.11 florian 467: rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
468:
1.28 claudio 469: if (rc == -1 && errno != ENOENT) {
470: ERR("%s: fstatat", f->path);
471: return -1;
472: }
1.11 florian 473: if (rc != -1 && !S_ISSOCK(st.st_mode)) {
1.17 benno 474: if (S_ISDIR(st.st_mode) &&
475: unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
1.21 benno 476: ERR("%s: unlinkat", f->path);
1.17 benno 477: return -1;
1.11 florian 478: }
1.17 benno 479: rc = -1;
1.11 florian 480: }
481:
482: if (rc == -1) {
483: newsock = 1;
1.22 benno 484: if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) {
1.21 benno 485: ERRX1("mktemplate");
1.11 florian 486: return -1;
487: }
488: if (mkstempsock(p->root, temp) == NULL) {
1.21 benno 489: ERR("mkstempsock");
1.11 florian 490: free(temp);
1.1 benno 491: return -1;
492: }
1.9 florian 493: }
494:
1.17 benno 495: rsync_set_metadata_at(sess, newsock,
496: p->rootfd, f, newsock ? temp : f->path);
1.11 florian 497:
498: if (newsock) {
499: if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) {
1.21 benno 500: ERR("%s: renameat %s", temp, f->path);
1.11 florian 501: (void)unlinkat(p->rootfd, temp, 0);
502: free(temp);
1.9 florian 503: return -1;
504: }
1.11 florian 505: free(temp);
1.1 benno 506: }
1.17 benno 507:
1.11 florian 508: log_file(sess, f);
1.1 benno 509: return 0;
510: }
511:
512: /*
513: * If not found, create the destination directory in prefix order.
514: * Create directories using the existing umask.
515: * Return <0 on failure 0 on success.
516: */
517: static int
518: pre_dir(const struct upload *p, struct sess *sess)
519: {
520: struct stat st;
1.2 benno 521: int rc;
1.1 benno 522: const struct flist *f;
523:
524: f = &p->fl[p->idx];
525: assert(S_ISDIR(f->st.mode));
526:
1.3 deraadt 527: if (!sess->opts->recursive) {
1.21 benno 528: WARNX("%s: ignoring directory", f->path);
1.1 benno 529: return 0;
1.25 claudio 530: }
531: if (sess->opts->dry_run) {
1.1 benno 532: log_dir(sess, f);
533: return 0;
534: }
535:
1.4 deraadt 536: assert(p->rootfd != -1);
1.1 benno 537: rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
1.17 benno 538:
1.4 deraadt 539: if (rc == -1 && errno != ENOENT) {
1.21 benno 540: ERR("%s: fstatat", f->path);
1.1 benno 541: return -1;
1.28 claudio 542: }
543: if (rc != -1 && !S_ISDIR(st.st_mode)) {
1.21 benno 544: ERRX("%s: not a directory", f->path);
1.1 benno 545: return -1;
1.4 deraadt 546: } else if (rc != -1) {
1.2 benno 547: /*
1.1 benno 548: * FIXME: we should fchmod the permissions here as well,
549: * as we may locally have shut down writing into the
550: * directory and that doesn't work.
551: */
1.21 benno 552: LOG3("%s: updating directory", f->path);
1.1 benno 553: return 0;
554: }
555:
556: /*
557: * We want to make the directory with default permissions (using
558: * our old umask, which we've since unset), then adjust
559: * permissions (assuming preserve_perms or new) afterward in
560: * case it's u-w or something.
561: */
562:
1.21 benno 563: LOG3("%s: creating directory", f->path);
1.4 deraadt 564: if (mkdirat(p->rootfd, f->path, 0777 & ~p->oumask) == -1) {
1.21 benno 565: ERR("%s: mkdirat", f->path);
1.1 benno 566: return -1;
567: }
568:
569: p->newdir[p->idx] = 1;
570: log_dir(sess, f);
571: return 0;
572: }
573:
574: /*
575: * Process the directory time and mode for "idx" in the file list.
576: * Returns zero on failure, non-zero on success.
577: */
578: static int
579: post_dir(struct sess *sess, const struct upload *u, size_t idx)
580: {
581: struct timespec tv[2];
582: int rc;
583: struct stat st;
584: const struct flist *f;
585:
586: f = &u->fl[idx];
587: assert(S_ISDIR(f->st.mode));
588:
589: /* We already warned about the directory in pre_process_dir(). */
590:
1.3 deraadt 591: if (!sess->opts->recursive)
1.1 benno 592: return 1;
1.25 claudio 593: if (sess->opts->dry_run)
1.1 benno 594: return 1;
595:
1.4 deraadt 596: if (fstatat(u->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW) == -1) {
1.21 benno 597: ERR("%s: fstatat", f->path);
1.1 benno 598: return 0;
1.25 claudio 599: }
600: if (!S_ISDIR(st.st_mode)) {
1.21 benno 601: WARNX("%s: not a directory", f->path);
1.1 benno 602: return 0;
603: }
604:
1.2 benno 605: /*
1.1 benno 606: * Update the modification time if we're a new directory *or* if
607: * we're preserving times and the time has changed.
1.6 florian 608: * FIXME: run rsync_set_metadata()?
1.1 benno 609: */
610:
1.35 ! job 611: if (!sess->opts->ignore_dir_times) {
! 612: if (u->newdir[idx] ||
! 613: (sess->opts->preserve_times &&
! 614: st.st_mtime != f->st.mtime)) {
! 615: tv[0].tv_sec = time(NULL);
! 616: tv[0].tv_nsec = 0;
! 617: tv[1].tv_sec = f->st.mtime;
! 618: tv[1].tv_nsec = 0;
! 619: rc = utimensat(u->rootfd, f->path, tv, 0);
! 620: if (rc == -1) {
! 621: ERR("%s: utimensat", f->path);
! 622: return 0;
! 623: }
! 624: LOG4("%s: updated date", f->path);
1.1 benno 625: }
626: }
627:
628: /*
629: * Update the mode if we're a new directory *or* if we're
630: * preserving modes and it has changed.
631: */
632:
1.2 benno 633: if (u->newdir[idx] ||
1.20 deraadt 634: (sess->opts->preserve_perms && st.st_mode != f->st.mode)) {
1.1 benno 635: rc = fchmodat(u->rootfd, f->path, f->st.mode, 0);
1.4 deraadt 636: if (rc == -1) {
1.21 benno 637: ERR("%s: fchmodat", f->path);
1.1 benno 638: return 0;
639: }
1.21 benno 640: LOG4("%s: updated mode", f->path);
1.1 benno 641: }
642:
643: return 1;
644: }
645:
646: /*
1.30 claudio 647: * Check if file exists in the specified root directory.
648: * Returns:
649: * -1 on error
650: * 0 if file is considered the same
651: * 1 if file exists and is possible match
652: * 2 if file exists but quick check failed
653: * 3 if file does not exist
654: * The stat pointer st is only valid for 0, 1, and 2 returns.
655: */
656: static int
1.34 claudio 657: check_file(int rootfd, const struct flist *f, struct stat *st,
658: struct sess *sess)
1.33 deraadt 659: {
1.30 claudio 660: if (fstatat(rootfd, f->path, st, AT_SYMLINK_NOFOLLOW) == -1) {
661: if (errno == ENOENT)
662: return 3;
663:
664: ERR("%s: fstatat", f->path);
665: return -1;
666: }
667:
668: /* non-regular file needs attention */
669: if (!S_ISREG(st->st_mode))
670: return 2;
671:
1.34 claudio 672: /* TODO: add support for --checksum */
673:
674: /* if ignore_times is on file needs attention */
675: if (sess->opts->ignore_times)
676: return 2;
677:
1.30 claudio 678: /* quick check if file is the same */
679: if (st->st_size == f->st.size) {
1.34 claudio 680: if (sess->opts->size_only)
681: return 0;
1.30 claudio 682: if (st->st_mtime == f->st.mtime)
683: return 0;
684: return 1;
685: }
686:
687: /* file needs attention */
688: return 2;
689: }
690:
691: /*
1.1 benno 692: * Try to open the file at the current index.
1.25 claudio 693: * If the file does not exist, returns with >0.
1.1 benno 694: * Return <0 on failure, 0 on success w/nothing to be done, >0 on
695: * success and the file needs attention.
696: */
697: static int
1.30 claudio 698: pre_file(const struct upload *p, int *filefd, off_t *size,
1.25 claudio 699: struct sess *sess)
1.1 benno 700: {
701: const struct flist *f;
1.30 claudio 702: struct stat st;
703: int i, rc, match = -1;
1.1 benno 704:
705: f = &p->fl[p->idx];
706: assert(S_ISREG(f->st.mode));
707:
708: if (sess->opts->dry_run) {
709: log_file(sess, f);
1.3 deraadt 710: if (!io_write_int(sess, p->fdout, p->idx)) {
1.21 benno 711: ERRX1("io_write_int");
1.1 benno 712: return -1;
713: }
1.32 claudio 714: return 0;
715: }
716:
717: if (sess->opts->max_size >= 0 && f->st.size > sess->opts->max_size) {
718: WARNX("skipping over max-size file %s", f->path);
719: return 0;
720: }
721: if (sess->opts->min_size >= 0 && f->st.size < sess->opts->min_size) {
722: WARNX("skipping under min-size file %s", f->path);
1.1 benno 723: return 0;
724: }
725:
726: /*
727: * For non dry-run cases, we'll write the acknowledgement later
1.25 claudio 728: * in the rsync_uploader() function.
1.1 benno 729: */
730:
1.30 claudio 731: *size = 0;
1.26 claudio 732: *filefd = -1;
1.25 claudio 733:
1.34 claudio 734: rc = check_file(p->rootfd, f, &st, sess);
1.30 claudio 735: if (rc == -1)
1.25 claudio 736: return -1;
1.30 claudio 737: if (rc == 2 && !S_ISREG(st.st_mode)) {
738: if (S_ISDIR(st.st_mode) &&
1.26 claudio 739: unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
740: ERR("%s: unlinkat", f->path);
741: return -1;
742: }
1.25 claudio 743: }
1.30 claudio 744: if (rc == 0) {
1.26 claudio 745: if (!rsync_set_metadata_at(sess, 0, p->rootfd, f, f->path)) {
1.25 claudio 746: ERRX1("rsync_set_metadata");
747: return -1;
748: }
1.30 claudio 749: LOG3("%s: skipping: up to date", f->path);
1.25 claudio 750: return 0;
751: }
752:
1.30 claudio 753: /* check alternative locations for better match */
754: for (i = 0; sess->opts->basedir[i] != NULL; i++) {
755: const char *root = sess->opts->basedir[i];
756: int dfd, x;
757:
1.31 deraadt 758: dfd = openat(p->rootfd, root, O_RDONLY | O_DIRECTORY);
1.30 claudio 759: if (dfd == -1)
760: err(ERR_FILE_IO, "%s: openat", root);
1.34 claudio 761: x = check_file(dfd, f, &st, sess);
1.30 claudio 762: /* found a match */
763: if (x == 0) {
764: if (rc >= 0) {
765: /* found better match, delete file in rootfd */
766: if (unlinkat(p->rootfd, f->path, 0) == -1 &&
767: errno != ENOENT) {
768: ERR("%s: unlinkat", f->path);
769: return -1;
770: }
771: }
772: LOG3("%s: skipping: up to date in %s", f->path, root);
773: /* TODO: depending on mode link or copy file */
774: close(dfd);
775: return 0;
776: } else if (x == 1 && match == -1) {
777: /* found a local file that is a close match */
778: match = i;
779: }
780: close(dfd);
781: }
782: if (match != -1) {
783: /* copy match from basedir into root as a start point */
784: copy_file(p->rootfd, sess->opts->basedir[match], f);
785: if (fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW) ==
786: -1) {
787: ERR("%s: fstatat", f->path);
788: return -1;
789: }
790: }
791:
792: *size = st.st_size;
1.31 deraadt 793: *filefd = openat(p->rootfd, f->path, O_RDONLY | O_NOFOLLOW);
1.26 claudio 794: if (*filefd == -1 && errno != ENOENT) {
795: ERR("%s: openat", f->path);
796: return -1;
797: }
798:
1.25 claudio 799: /* file needs attention */
800: return 1;
1.1 benno 801: }
802:
803: /*
804: * Allocate an uploader object in the correct state to start.
805: * Returns NULL on failure or the pointer otherwise.
806: * On success, upload_free() must be called with the allocated pointer.
807: */
808: struct upload *
1.22 benno 809: upload_alloc(const char *root, int rootfd, int fdout,
1.1 benno 810: size_t clen, const struct flist *fl, size_t flsz, mode_t msk)
811: {
812: struct upload *p;
813:
1.4 deraadt 814: if ((p = calloc(1, sizeof(struct upload))) == NULL) {
1.21 benno 815: ERR("calloc");
1.1 benno 816: return NULL;
817: }
818:
819: p->state = UPLOAD_FIND_NEXT;
820: p->oumask = msk;
1.11 florian 821: p->root = strdup(root);
822: if (p->root == NULL) {
1.21 benno 823: ERR("strdup");
1.11 florian 824: free(p);
825: return NULL;
826: }
1.1 benno 827: p->rootfd = rootfd;
828: p->csumlen = clen;
829: p->fdout = fdout;
830: p->fl = fl;
831: p->flsz = flsz;
832: p->newdir = calloc(flsz, sizeof(int));
1.4 deraadt 833: if (p->newdir == NULL) {
1.21 benno 834: ERR("calloc");
1.11 florian 835: free(p->root);
1.1 benno 836: free(p);
837: return NULL;
838: }
839: return p;
840: }
841:
842: /*
843: * Perform all cleanups and free.
844: * Passing a NULL to this function is ok.
845: */
846: void
847: upload_free(struct upload *p)
848: {
849:
1.4 deraadt 850: if (p == NULL)
1.1 benno 851: return;
1.11 florian 852: free(p->root);
1.1 benno 853: free(p->newdir);
854: free(p->buf);
855: free(p);
856: }
857:
858: /*
859: * Iterates through all available files and conditionally gets the file
860: * ready for processing to check whether it's up to date.
861: * If not up to date or empty, sends file information to the sender.
862: * If returns 0, we've processed all files there are to process.
863: * If returns >0, we're waiting for POLLIN or POLLOUT data.
864: * Otherwise returns <0, which is an error.
865: */
866: int
1.2 benno 867: rsync_uploader(struct upload *u, int *fileinfd,
1.1 benno 868: struct sess *sess, int *fileoutfd)
869: {
1.5 florian 870: struct blkset blk;
1.23 benno 871: void *mbuf, *bufp;
872: ssize_t msz;
873: size_t i, pos, sz;
1.30 claudio 874: off_t offs, filesize;
1.5 florian 875: int c;
1.1 benno 876:
1.26 claudio 877: /* Once finished this should never get called again. */
1.4 deraadt 878: assert(u->state != UPLOAD_FINISHED);
1.1 benno 879:
880: /*
881: * If we have an upload in progress, then keep writing until the
882: * buffer has been fully written.
883: * We must only have the output file descriptor working and also
884: * have a valid buffer to write.
885: */
886:
1.25 claudio 887: if (u->state == UPLOAD_WRITE) {
1.14 deraadt 888: assert(u->buf != NULL);
1.4 deraadt 889: assert(*fileoutfd != -1);
890: assert(*fileinfd == -1);
1.1 benno 891:
892: /*
893: * Unfortunately, we need to chunk these: if we're
894: * the server side of things, then we're multiplexing
895: * output and need to wrap this in chunks.
896: * This is a major deficiency of rsync.
897: * FIXME: add a "fast-path" mode that simply dumps out
898: * the buffer non-blocking if we're not mplexing.
899: */
900:
901: if (u->bufpos < u->bufsz) {
902: sz = MAX_CHUNK < (u->bufsz - u->bufpos) ?
903: MAX_CHUNK : (u->bufsz - u->bufpos);
1.2 benno 904: c = io_write_buf(sess, u->fdout,
1.1 benno 905: u->buf + u->bufpos, sz);
1.4 deraadt 906: if (c == 0) {
1.21 benno 907: ERRX1("io_write_nonblocking");
1.1 benno 908: return -1;
909: }
910: u->bufpos += sz;
911: if (u->bufpos < u->bufsz)
912: return 1;
913: }
914:
1.2 benno 915: /*
1.1 benno 916: * Let the UPLOAD_FIND_NEXT state handle things if we
917: * finish, as we'll need to write a POLLOUT message and
918: * not have a writable descriptor yet.
919: */
920:
921: u->state = UPLOAD_FIND_NEXT;
922: u->idx++;
923: return 1;
924: }
925:
926: /*
927: * If we invoke the uploader without a file currently open, then
928: * we iterate through til the next available regular file and
929: * start the opening process.
930: * This means we must have the output file descriptor working.
931: */
932:
1.4 deraadt 933: if (u->state == UPLOAD_FIND_NEXT) {
934: assert(*fileinfd == -1);
935: assert(*fileoutfd != -1);
1.1 benno 936:
937: for ( ; u->idx < u->flsz; u->idx++) {
938: if (S_ISDIR(u->fl[u->idx].st.mode))
939: c = pre_dir(u, sess);
940: else if (S_ISLNK(u->fl[u->idx].st.mode))
1.30 claudio 941: c = pre_symlink(u, sess);
1.1 benno 942: else if (S_ISREG(u->fl[u->idx].st.mode))
1.30 claudio 943: c = pre_file(u, fileinfd, &filesize, sess);
1.11 florian 944: else if (S_ISBLK(u->fl[u->idx].st.mode) ||
945: S_ISCHR(u->fl[u->idx].st.mode))
946: c = pre_dev(u, sess);
947: else if (S_ISFIFO(u->fl[u->idx].st.mode))
948: c = pre_fifo(u, sess);
949: else if (S_ISSOCK(u->fl[u->idx].st.mode))
950: c = pre_sock(u, sess);
1.1 benno 951: else
952: c = 0;
953:
954: if (c < 0)
955: return -1;
956: else if (c > 0)
957: break;
958: }
959:
1.2 benno 960: /*
1.1 benno 961: * Whether we've finished writing files or not, we
962: * disable polling on the output channel.
963: */
964:
965: *fileoutfd = -1;
966: if (u->idx == u->flsz) {
1.4 deraadt 967: assert(*fileinfd == -1);
1.3 deraadt 968: if (!io_write_int(sess, u->fdout, -1)) {
1.21 benno 969: ERRX1("io_write_int");
1.1 benno 970: return -1;
971: }
972: u->state = UPLOAD_FINISHED;
1.21 benno 973: LOG4("uploader: finished");
1.1 benno 974: return 0;
975: }
976:
977: /* Go back to the event loop, if necessary. */
978:
1.25 claudio 979: u->state = UPLOAD_WRITE;
1.1 benno 980: }
981:
982: /* Initialies our blocks. */
983:
1.25 claudio 984: assert(u->state == UPLOAD_WRITE);
1.1 benno 985: memset(&blk, 0, sizeof(struct blkset));
986: blk.csum = u->csumlen;
987:
1.30 claudio 988: if (*fileinfd != -1 && filesize > 0) {
989: init_blkset(&blk, filesize);
1.23 benno 990: assert(blk.blksz);
991:
992: blk.blks = calloc(blk.blksz, sizeof(struct blk));
993: if (blk.blks == NULL) {
994: ERR("calloc");
1.1 benno 995: close(*fileinfd);
996: *fileinfd = -1;
997: return -1;
998: }
999:
1.25 claudio 1000: if ((mbuf = malloc(blk.len)) == NULL) {
1001: ERR("malloc");
1.1 benno 1002: close(*fileinfd);
1003: *fileinfd = -1;
1.27 claudio 1004: free(blk.blks);
1.1 benno 1005: return -1;
1006: }
1007:
1008: offs = 0;
1.23 benno 1009: i = 0;
1010: do {
1011: msz = pread(*fileinfd, mbuf, blk.len, offs);
1.25 claudio 1012: if ((size_t)msz != blk.len && (size_t)msz != blk.rem) {
1.23 benno 1013: ERR("pread");
1014: close(*fileinfd);
1015: *fileinfd = -1;
1.25 claudio 1016: free(mbuf);
1.27 claudio 1017: free(blk.blks);
1.23 benno 1018: return -1;
1019: }
1020: init_blk(&blk.blks[i], &blk, offs, i, mbuf, sess);
1.1 benno 1021: offs += blk.len;
1.23 benno 1022: LOG3(
1023: "i=%ld, offs=%lld, msz=%ld, blk.len=%lu, blk.rem=%lu",
1024: i, offs, msz, blk.len, blk.rem);
1025: i++;
1026: } while (i < blk.blksz);
1.1 benno 1027:
1.25 claudio 1028: free(mbuf);
1.1 benno 1029: close(*fileinfd);
1030: *fileinfd = -1;
1.21 benno 1031: LOG3("%s: mapped %jd B with %zu blocks",
1.19 deraadt 1032: u->fl[u->idx].path, (intmax_t)blk.size,
1033: blk.blksz);
1.1 benno 1034: } else {
1.4 deraadt 1035: if (*fileinfd != -1) {
1.1 benno 1036: close(*fileinfd);
1037: *fileinfd = -1;
1038: }
1039: blk.len = MAX_CHUNK; /* Doesn't matter. */
1.21 benno 1040: LOG3("%s: not mapped", u->fl[u->idx].path);
1.1 benno 1041: }
1042:
1.4 deraadt 1043: assert(*fileinfd == -1);
1.1 benno 1044:
1045: /* Make sure the block metadata buffer is big enough. */
1046:
1.2 benno 1047: u->bufsz =
1.33 deraadt 1048: sizeof(int32_t) + /* identifier */
1049: sizeof(int32_t) + /* block count */
1050: sizeof(int32_t) + /* block length */
1051: sizeof(int32_t) + /* checksum length */
1052: sizeof(int32_t) + /* block remainder */
1053: blk.blksz *
1054: (sizeof(int32_t) + /* short checksum */
1055: blk.csum); /* long checksum */
1.1 benno 1056:
1057: if (u->bufsz > u->bufmax) {
1.4 deraadt 1058: if ((bufp = realloc(u->buf, u->bufsz)) == NULL) {
1.21 benno 1059: ERR("realloc");
1.27 claudio 1060: free(blk.blks);
1.1 benno 1061: return -1;
1062: }
1063: u->buf = bufp;
1064: u->bufmax = u->bufsz;
1065: }
1066:
1067: u->bufpos = pos = 0;
1.22 benno 1068: io_buffer_int(u->buf, &pos, u->bufsz, u->idx);
1069: io_buffer_int(u->buf, &pos, u->bufsz, blk.blksz);
1070: io_buffer_int(u->buf, &pos, u->bufsz, blk.len);
1071: io_buffer_int(u->buf, &pos, u->bufsz, blk.csum);
1072: io_buffer_int(u->buf, &pos, u->bufsz, blk.rem);
1.1 benno 1073: for (i = 0; i < blk.blksz; i++) {
1.22 benno 1074: io_buffer_int(u->buf, &pos, u->bufsz,
1.1 benno 1075: blk.blks[i].chksum_short);
1.22 benno 1076: io_buffer_buf(u->buf, &pos, u->bufsz,
1.1 benno 1077: blk.blks[i].chksum_long, blk.csum);
1078: }
1079: assert(pos == u->bufsz);
1080:
1081: /* Reenable the output poller and clean up. */
1082:
1083: *fileoutfd = u->fdout;
1084: free(blk.blks);
1085: return 1;
1086: }
1087:
1088: /*
1089: * Fix up the directory permissions and times post-order.
1090: * We can't fix up directory permissions in place because the server may
1091: * want us to have overly-tight permissions---say, those that don't
1092: * allow writing into the directory.
1093: * We also need to do our directory times post-order because making
1094: * files within the directory will change modification times.
1095: * Returns zero on failure, non-zero on success.
1096: */
1097: int
1098: rsync_uploader_tail(struct upload *u, struct sess *sess)
1099: {
1100: size_t i;
1101:
1102:
1.3 deraadt 1103: if (!sess->opts->preserve_times &&
1.20 deraadt 1104: !sess->opts->preserve_perms)
1.1 benno 1105: return 1;
1106:
1.21 benno 1107: LOG2("fixing up directory times and permissions");
1.1 benno 1108:
1109: for (i = 0; i < u->flsz; i++)
1110: if (S_ISDIR(u->fl[i].st.mode))
1.3 deraadt 1111: if (!post_dir(sess, u, i))
1.1 benno 1112: return 0;
1113:
1114: return 1;
1115: }