Annotation of src/usr.bin/rsync/uploader.c, Revision 1.1
1.1 ! benno 1: /* $Id$ */
! 2: /*
! 3: * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17: #include <sys/mman.h>
! 18: #include <sys/stat.h>
! 19:
! 20: #include <assert.h>
! 21: #include <errno.h>
! 22: #include <fcntl.h>
! 23: #include <inttypes.h>
! 24: #include <math.h>
! 25: #include <poll.h>
! 26: #include <stdio.h>
! 27: #include <stdlib.h>
! 28: #include <string.h>
! 29: #include <time.h>
! 30: #include <unistd.h>
! 31:
! 32: #include "extern.h"
! 33:
! 34: enum uploadst {
! 35: UPLOAD_FIND_NEXT = 0, /* find next to upload to sender */
! 36: UPLOAD_WRITE_LOCAL, /* wait to write to sender */
! 37: UPLOAD_READ_LOCAL, /* wait to read from local file */
! 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 */
! 53: int rootfd; /* destination directory */
! 54: size_t csumlen; /* checksum length */
! 55: int fdout; /* write descriptor to sender */
! 56: const struct flist *fl; /* file list */
! 57: size_t flsz; /* size of file list */
! 58: int *newdir; /* non-zero if mkdir'd */
! 59: };
! 60:
! 61: /*
! 62: * Log a directory by emitting the file and a trailing slash, just to
! 63: * show the operator that we're a directory.
! 64: */
! 65: static void
! 66: log_dir(struct sess *sess, const struct flist *f)
! 67: {
! 68: size_t sz;
! 69:
! 70: if (sess->opts->server)
! 71: return;
! 72: sz = strlen(f->path);
! 73: assert(sz > 0);
! 74: LOG1(sess, "%s%s", f->path,
! 75: '/' == f->path[sz - 1] ? "" : "/");
! 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
! 83: log_link(struct sess *sess, const struct flist *f)
! 84: {
! 85:
! 86: if ( ! sess->opts->server)
! 87: LOG1(sess, "%s -> %s", f->path, f->link);
! 88: }
! 89:
! 90: /*
! 91: * Simply log the filename.
! 92: */
! 93: static void
! 94: log_file(struct sess *sess, const struct flist *f)
! 95: {
! 96:
! 97: if ( ! sess->opts->server)
! 98: LOG1(sess, "%s", f->path);
! 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:
! 122: /*
! 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;
! 133: if (0 == (p->blksz = sz / p->len))
! 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:
! 153: assert(MAP_FAILED != map);
! 154:
! 155: /* Block length inherits for all but the last. */
! 156:
! 157: p->idx = idx;
! 158: p->len = idx < set->blksz - 1 ? set->len : set->rem;
! 159: p->offs = offs;
! 160:
! 161: p->chksum_short = hash_fast(map + offs, p->len);
! 162: hash_slow(map + offs, p->len, p->chksum_long, sess);
! 163: }
! 164:
! 165: /*
! 166: * Return <0 on failure 0 on success.
! 167: */
! 168: static int
! 169: pre_link(struct upload *p, struct sess *sess)
! 170: {
! 171: int rc, newlink = 0;
! 172: char *b;
! 173: struct stat st;
! 174: struct timespec tv[2];
! 175: const struct flist *f;
! 176:
! 177: f = &p->fl[p->idx];
! 178: assert(S_ISLNK(f->st.mode));
! 179:
! 180: if ( ! sess->opts->preserve_links) {
! 181: WARNX(sess, "%s: ignoring symlink", f->path);
! 182: return 0;
! 183: } else if (sess->opts->dry_run) {
! 184: log_link(sess, f);
! 185: return 0;
! 186: }
! 187:
! 188: /* See if the symlink already exists. */
! 189:
! 190: assert(-1 != p->rootfd);
! 191: rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
! 192: if (-1 != rc && ! S_ISLNK(st.st_mode)) {
! 193: WARNX(sess, "%s: not a symlink", f->path);
! 194: return -1;
! 195: } else if (-1 == rc && ENOENT != errno) {
! 196: WARN(sess, "%s: fstatat", f->path);
! 197: return -1;
! 198: }
! 199:
! 200: /*
! 201: * If the symbolic link already exists, then make sure that it
! 202: * points to the correct place.
! 203: * FIXME: does symlinkat() set permissions on the link using the
! 204: * destination file or the default umask?
! 205: * Do we need a fchmod in here as well?
! 206: */
! 207:
! 208: if (-1 == rc) {
! 209: LOG3(sess, "%s: creating "
! 210: "symlink: %s", f->path, f->link);
! 211: if (-1 == symlinkat(f->link, p->rootfd, f->path)) {
! 212: WARN(sess, "%s: symlinkat", f->path);
! 213: return -1;
! 214: }
! 215: newlink = 1;
! 216: } else {
! 217: b = symlinkat_read(sess, p->rootfd, f->path);
! 218: if (NULL == b) {
! 219: ERRX1(sess, "%s: symlinkat_read", f->path);
! 220: return -1;
! 221: }
! 222: if (strcmp(f->link, b)) {
! 223: free(b);
! 224: b = NULL;
! 225: LOG3(sess, "%s: updating "
! 226: "symlink: %s", f->path, f->link);
! 227: if (-1 == unlinkat(p->rootfd, f->path, 0)) {
! 228: WARN(sess, "%s: unlinkat", f->path);
! 229: return -1;
! 230: }
! 231: if (-1 == symlinkat(f->link, p->rootfd, f->path)) {
! 232: WARN(sess, "%s: symlinkat", f->path);
! 233: return -1;
! 234: }
! 235: newlink = 1;
! 236: }
! 237: free(b);
! 238: }
! 239:
! 240: /* Optionally preserve times/perms on the symlink. */
! 241:
! 242: if (sess->opts->preserve_times) {
! 243: tv[0].tv_sec = time(NULL);
! 244: tv[0].tv_nsec = 0;
! 245: tv[1].tv_sec = f->st.mtime;
! 246: tv[1].tv_nsec = 0;
! 247: rc = utimensat(p->rootfd,
! 248: f->path, tv, AT_SYMLINK_NOFOLLOW);
! 249: if (-1 == rc) {
! 250: ERR(sess, "%s: utimensat", f->path);
! 251: return -1;
! 252: }
! 253: LOG4(sess, "%s: updated symlink date", f->path);
! 254: }
! 255:
! 256: /*
! 257: * FIXME: if newlink is set because we updated the symlink, we
! 258: * want to carry over the permissions from the last.
! 259: */
! 260:
! 261: if (newlink || sess->opts->preserve_perms) {
! 262: rc = fchmodat(p->rootfd, f->path,
! 263: f->st.mode, AT_SYMLINK_NOFOLLOW);
! 264: if (-1 == rc) {
! 265: ERR(sess, "%s: fchmodat", f->path);
! 266: return -1;
! 267: }
! 268: LOG4(sess, "%s: updated symlink mode", f->path);
! 269: }
! 270:
! 271: log_link(sess, f);
! 272: return 0;
! 273: }
! 274:
! 275: /*
! 276: * If not found, create the destination directory in prefix order.
! 277: * Create directories using the existing umask.
! 278: * Return <0 on failure 0 on success.
! 279: */
! 280: static int
! 281: pre_dir(const struct upload *p, struct sess *sess)
! 282: {
! 283: struct stat st;
! 284: int rc;
! 285: const struct flist *f;
! 286:
! 287: f = &p->fl[p->idx];
! 288: assert(S_ISDIR(f->st.mode));
! 289:
! 290: if ( ! sess->opts->recursive) {
! 291: WARNX(sess, "%s: ignoring directory", f->path);
! 292: return 0;
! 293: } else if (sess->opts->dry_run) {
! 294: log_dir(sess, f);
! 295: return 0;
! 296: }
! 297:
! 298: assert(-1 != p->rootfd);
! 299: rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW);
! 300: if (-1 == rc && ENOENT != errno) {
! 301: WARN(sess, "%s: fstatat", f->path);
! 302: return -1;
! 303: } else if (-1 != rc && ! S_ISDIR(st.st_mode)) {
! 304: WARNX(sess, "%s: not a directory", f->path);
! 305: return -1;
! 306: } else if (-1 != rc) {
! 307: /*
! 308: * FIXME: we should fchmod the permissions here as well,
! 309: * as we may locally have shut down writing into the
! 310: * directory and that doesn't work.
! 311: */
! 312: LOG3(sess, "%s: updating directory", f->path);
! 313: return 0;
! 314: }
! 315:
! 316: /*
! 317: * We want to make the directory with default permissions (using
! 318: * our old umask, which we've since unset), then adjust
! 319: * permissions (assuming preserve_perms or new) afterward in
! 320: * case it's u-w or something.
! 321: */
! 322:
! 323: LOG3(sess, "%s: creating directory", f->path);
! 324: if (-1 == mkdirat(p->rootfd, f->path, 0777 & ~p->oumask)) {
! 325: WARN(sess, "%s: mkdirat", f->path);
! 326: return -1;
! 327: }
! 328:
! 329: p->newdir[p->idx] = 1;
! 330: log_dir(sess, f);
! 331: return 0;
! 332: }
! 333:
! 334: /*
! 335: * Process the directory time and mode for "idx" in the file list.
! 336: * Returns zero on failure, non-zero on success.
! 337: */
! 338: static int
! 339: post_dir(struct sess *sess, const struct upload *u, size_t idx)
! 340: {
! 341: struct timespec tv[2];
! 342: int rc;
! 343: struct stat st;
! 344: const struct flist *f;
! 345:
! 346: f = &u->fl[idx];
! 347: assert(S_ISDIR(f->st.mode));
! 348:
! 349: /* We already warned about the directory in pre_process_dir(). */
! 350:
! 351: if ( ! sess->opts->recursive)
! 352: return 1;
! 353: else if (sess->opts->dry_run)
! 354: return 1;
! 355:
! 356: if (-1 == fstatat(u->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW)) {
! 357: ERR(sess, "%s: fstatat", f->path);
! 358: return 0;
! 359: } else if ( ! S_ISDIR(st.st_mode)) {
! 360: WARNX(sess, "%s: not a directory", f->path);
! 361: return 0;
! 362: }
! 363:
! 364: /*
! 365: * Update the modification time if we're a new directory *or* if
! 366: * we're preserving times and the time has changed.
! 367: */
! 368:
! 369: if (u->newdir[idx] ||
! 370: (sess->opts->preserve_times &&
! 371: st.st_mtime != f->st.mtime)) {
! 372: tv[0].tv_sec = time(NULL);
! 373: tv[0].tv_nsec = 0;
! 374: tv[1].tv_sec = f->st.mtime;
! 375: tv[1].tv_nsec = 0;
! 376: rc = utimensat(u->rootfd, f->path, tv, 0);
! 377: if (-1 == rc) {
! 378: ERR(sess, "%s: utimensat", f->path);
! 379: return 0;
! 380: }
! 381: LOG4(sess, "%s: updated date", f->path);
! 382: }
! 383:
! 384: /*
! 385: * Update the mode if we're a new directory *or* if we're
! 386: * preserving modes and it has changed.
! 387: */
! 388:
! 389: if (u->newdir[idx] ||
! 390: (sess->opts->preserve_perms &&
! 391: st.st_mode != f->st.mode)) {
! 392: rc = fchmodat(u->rootfd, f->path, f->st.mode, 0);
! 393: if (-1 == rc) {
! 394: ERR(sess, "%s: fchmodat", f->path);
! 395: return 0;
! 396: }
! 397: LOG4(sess, "%s: updated mode", f->path);
! 398: }
! 399:
! 400: return 1;
! 401: }
! 402:
! 403: /*
! 404: * Try to open the file at the current index.
! 405: * If the file does not exist, returns with success.
! 406: * Return <0 on failure, 0 on success w/nothing to be done, >0 on
! 407: * success and the file needs attention.
! 408: */
! 409: static int
! 410: pre_file(const struct upload *p, int *filefd, struct sess *sess)
! 411: {
! 412: const struct flist *f;
! 413:
! 414: f = &p->fl[p->idx];
! 415: assert(S_ISREG(f->st.mode));
! 416:
! 417: if (sess->opts->dry_run) {
! 418: log_file(sess, f);
! 419: if ( ! io_write_int(sess, p->fdout, p->idx)) {
! 420: ERRX1(sess, "io_write_int");
! 421: return -1;
! 422: }
! 423: return 0;
! 424: }
! 425:
! 426: /*
! 427: * For non dry-run cases, we'll write the acknowledgement later
! 428: * in the rsync_uploader() function because we need to wait for
! 429: * the open() call to complete.
! 430: * If the call to openat() fails with ENOENT, there's a
! 431: * fast-path between here and the write function, so we won't do
! 432: * any blocking between now and then.
! 433: */
! 434:
! 435: *filefd = openat(p->rootfd, f->path,
! 436: O_RDONLY | O_NOFOLLOW | O_NONBLOCK, 0);
! 437: if (-1 != *filefd || ENOENT == errno)
! 438: return 1;
! 439: ERR(sess, "%s: openat", f->path);
! 440: return -1;
! 441: }
! 442:
! 443: /*
! 444: * Allocate an uploader object in the correct state to start.
! 445: * Returns NULL on failure or the pointer otherwise.
! 446: * On success, upload_free() must be called with the allocated pointer.
! 447: */
! 448: struct upload *
! 449: upload_alloc(struct sess *sess, int rootfd, int fdout,
! 450: size_t clen, const struct flist *fl, size_t flsz, mode_t msk)
! 451: {
! 452: struct upload *p;
! 453:
! 454: if (NULL == (p = calloc(1, sizeof(struct upload)))) {
! 455: ERR(sess, "calloc");
! 456: return NULL;
! 457: }
! 458:
! 459: p->state = UPLOAD_FIND_NEXT;
! 460: p->oumask = msk;
! 461: p->rootfd = rootfd;
! 462: p->csumlen = clen;
! 463: p->fdout = fdout;
! 464: p->fl = fl;
! 465: p->flsz = flsz;
! 466: p->newdir = calloc(flsz, sizeof(int));
! 467: if (NULL == p->newdir) {
! 468: ERR(sess, "calloc");
! 469: free(p);
! 470: return NULL;
! 471: }
! 472: return p;
! 473: }
! 474:
! 475: /*
! 476: * Perform all cleanups and free.
! 477: * Passing a NULL to this function is ok.
! 478: */
! 479: void
! 480: upload_free(struct upload *p)
! 481: {
! 482:
! 483: if (NULL == p)
! 484: return;
! 485: free(p->newdir);
! 486: free(p->buf);
! 487: free(p);
! 488: }
! 489:
! 490: /*
! 491: * Iterates through all available files and conditionally gets the file
! 492: * ready for processing to check whether it's up to date.
! 493: * If not up to date or empty, sends file information to the sender.
! 494: * If returns 0, we've processed all files there are to process.
! 495: * If returns >0, we're waiting for POLLIN or POLLOUT data.
! 496: * Otherwise returns <0, which is an error.
! 497: */
! 498: int
! 499: rsync_uploader(struct upload *u, int *fileinfd,
! 500: struct sess *sess, int *fileoutfd)
! 501: {
! 502: struct blkset blk;
! 503: struct stat st;
! 504: void *map, *bufp;
! 505: size_t i, mapsz, pos, sz;
! 506: off_t offs;
! 507: int c;
! 508:
! 509: /* This should never get called. */
! 510:
! 511: assert(UPLOAD_FINISHED != u->state);
! 512:
! 513: /*
! 514: * If we have an upload in progress, then keep writing until the
! 515: * buffer has been fully written.
! 516: * We must only have the output file descriptor working and also
! 517: * have a valid buffer to write.
! 518: */
! 519:
! 520: if (UPLOAD_WRITE_LOCAL == u->state) {
! 521: assert(NULL != u->buf);
! 522: assert(-1 != *fileoutfd);
! 523: assert(-1 == *fileinfd);
! 524:
! 525: /*
! 526: * Unfortunately, we need to chunk these: if we're
! 527: * the server side of things, then we're multiplexing
! 528: * output and need to wrap this in chunks.
! 529: * This is a major deficiency of rsync.
! 530: * FIXME: add a "fast-path" mode that simply dumps out
! 531: * the buffer non-blocking if we're not mplexing.
! 532: */
! 533:
! 534: if (u->bufpos < u->bufsz) {
! 535: sz = MAX_CHUNK < (u->bufsz - u->bufpos) ?
! 536: MAX_CHUNK : (u->bufsz - u->bufpos);
! 537: c = io_write_buf(sess, u->fdout,
! 538: u->buf + u->bufpos, sz);
! 539: if (0 == c) {
! 540: ERRX1(sess, "io_write_nonblocking");
! 541: return -1;
! 542: }
! 543: u->bufpos += sz;
! 544: if (u->bufpos < u->bufsz)
! 545: return 1;
! 546: }
! 547:
! 548: /*
! 549: * Let the UPLOAD_FIND_NEXT state handle things if we
! 550: * finish, as we'll need to write a POLLOUT message and
! 551: * not have a writable descriptor yet.
! 552: */
! 553:
! 554: u->state = UPLOAD_FIND_NEXT;
! 555: u->idx++;
! 556: return 1;
! 557: }
! 558:
! 559: /*
! 560: * If we invoke the uploader without a file currently open, then
! 561: * we iterate through til the next available regular file and
! 562: * start the opening process.
! 563: * This means we must have the output file descriptor working.
! 564: */
! 565:
! 566: if (UPLOAD_FIND_NEXT == u->state) {
! 567: assert(-1 == *fileinfd);
! 568: assert(-1 != *fileoutfd);
! 569:
! 570: for ( ; u->idx < u->flsz; u->idx++) {
! 571: if (S_ISDIR(u->fl[u->idx].st.mode))
! 572: c = pre_dir(u, sess);
! 573: else if (S_ISLNK(u->fl[u->idx].st.mode))
! 574: c = pre_link(u, sess);
! 575: else if (S_ISREG(u->fl[u->idx].st.mode))
! 576: c = pre_file(u, fileinfd, sess);
! 577: else
! 578: c = 0;
! 579:
! 580: if (c < 0)
! 581: return -1;
! 582: else if (c > 0)
! 583: break;
! 584: }
! 585:
! 586: /*
! 587: * Whether we've finished writing files or not, we
! 588: * disable polling on the output channel.
! 589: */
! 590:
! 591: *fileoutfd = -1;
! 592: if (u->idx == u->flsz) {
! 593: assert(-1 == *fileinfd);
! 594: if ( ! io_write_int(sess, u->fdout, -1)) {
! 595: ERRX1(sess, "io_write_int");
! 596: return -1;
! 597: }
! 598: u->state = UPLOAD_FINISHED;
! 599: LOG4(sess, "uploader: finished");
! 600: return 0;
! 601: }
! 602:
! 603: /* Go back to the event loop, if necessary. */
! 604:
! 605: u->state = -1 == *fileinfd ?
! 606: UPLOAD_WRITE_LOCAL : UPLOAD_READ_LOCAL;
! 607: if (UPLOAD_READ_LOCAL == u->state)
! 608: return 1;
! 609: }
! 610:
! 611: /*
! 612: * If an input file is open, stat it and see if it's already up
! 613: * to date, in which case close it and go to the next one.
! 614: * Either way, we don't have a write channel open.
! 615: */
! 616:
! 617: if (UPLOAD_READ_LOCAL == u->state) {
! 618: assert (-1 != *fileinfd);
! 619: assert(-1 == *fileoutfd);
! 620:
! 621: if (-1 == fstat(*fileinfd, &st)) {
! 622: WARN(sess, "%s: fstat", u->fl[u->idx].path);
! 623: close(*fileinfd);
! 624: *fileinfd = -1;
! 625: return -1;
! 626: } else if ( ! S_ISREG(st.st_mode)) {
! 627: WARNX(sess, "%s: not regular", u->fl[u->idx].path);
! 628: close(*fileinfd);
! 629: *fileinfd = -1;
! 630: return -1;
! 631: }
! 632:
! 633: if (st.st_size == u->fl[u->idx].st.size &&
! 634: st.st_mtime == u->fl[u->idx].st.mtime) {
! 635: LOG3(sess, "%s: skipping: "
! 636: "up to date", u->fl[u->idx].path);
! 637: close(*fileinfd);
! 638: *fileinfd = -1;
! 639: *fileoutfd = u->fdout;
! 640: u->state = UPLOAD_FIND_NEXT;
! 641: u->idx++;
! 642: return 1;
! 643: }
! 644:
! 645: /* Fallthrough... */
! 646:
! 647: u->state = UPLOAD_WRITE_LOCAL;
! 648: }
! 649:
! 650: /* Initialies our blocks. */
! 651:
! 652: assert(UPLOAD_WRITE_LOCAL == u->state);
! 653: memset(&blk, 0, sizeof(struct blkset));
! 654: blk.csum = u->csumlen;
! 655:
! 656: if (-1 != *fileinfd && st.st_size > 0) {
! 657: mapsz = st.st_size;
! 658: map = mmap(NULL, mapsz,
! 659: PROT_READ, MAP_SHARED, *fileinfd, 0);
! 660: if (MAP_FAILED == map) {
! 661: WARN(sess, "%s: mmap", u->fl[u->idx].path);
! 662: close(*fileinfd);
! 663: *fileinfd = -1;
! 664: return -1;
! 665: }
! 666:
! 667: init_blkset(&blk, st.st_size);
! 668: assert(blk.blksz);
! 669:
! 670: blk.blks = calloc(blk.blksz, sizeof(struct blk));
! 671: if (NULL == blk.blks) {
! 672: ERR(sess, "calloc");
! 673: munmap(map, mapsz);
! 674: close(*fileinfd);
! 675: *fileinfd = -1;
! 676: return -1;
! 677: }
! 678:
! 679: offs = 0;
! 680: for (i = 0; i < blk.blksz; i++) {
! 681: init_blk(&blk.blks[i],
! 682: &blk, offs, i, map, sess);
! 683: offs += blk.len;
! 684: }
! 685:
! 686: munmap(map, mapsz);
! 687: close(*fileinfd);
! 688: *fileinfd = -1;
! 689: LOG3(sess, "%s: mapped %jd B with %zu blocks",
! 690: u->fl[u->idx].path, (intmax_t)blk.size,
! 691: blk.blksz);
! 692: } else {
! 693: if (-1 != *fileinfd) {
! 694: close(*fileinfd);
! 695: *fileinfd = -1;
! 696: }
! 697: blk.len = MAX_CHUNK; /* Doesn't matter. */
! 698: LOG3(sess, "%s: not mapped", u->fl[u->idx].path);
! 699: }
! 700:
! 701: assert(-1 == *fileinfd);
! 702:
! 703: /* Make sure the block metadata buffer is big enough. */
! 704:
! 705: u->bufsz =
! 706: sizeof(int32_t) + /* identifier */
! 707: sizeof(int32_t) + /* block count */
! 708: sizeof(int32_t) + /* block length */
! 709: sizeof(int32_t) + /* checksum length */
! 710: sizeof(int32_t) + /* block remainder */
! 711: blk.blksz *
! 712: (sizeof(int32_t) + /* short checksum */
! 713: blk.csum); /* long checksum */
! 714:
! 715: if (u->bufsz > u->bufmax) {
! 716: if (NULL == (bufp = realloc(u->buf, u->bufsz))) {
! 717: ERR(sess, "realloc");
! 718: return -1;
! 719: }
! 720: u->buf = bufp;
! 721: u->bufmax = u->bufsz;
! 722: }
! 723:
! 724: u->bufpos = pos = 0;
! 725: io_buffer_int(sess, u->buf, &pos, u->bufsz, u->idx);
! 726: io_buffer_int(sess, u->buf, &pos, u->bufsz, blk.blksz);
! 727: io_buffer_int(sess, u->buf, &pos, u->bufsz, blk.len);
! 728: io_buffer_int(sess, u->buf, &pos, u->bufsz, blk.csum);
! 729: io_buffer_int(sess, u->buf, &pos, u->bufsz, blk.rem);
! 730: for (i = 0; i < blk.blksz; i++) {
! 731: io_buffer_int(sess, u->buf, &pos, u->bufsz,
! 732: blk.blks[i].chksum_short);
! 733: io_buffer_buf(sess, u->buf, &pos, u->bufsz,
! 734: blk.blks[i].chksum_long, blk.csum);
! 735: }
! 736: assert(pos == u->bufsz);
! 737:
! 738: /* Reenable the output poller and clean up. */
! 739:
! 740: *fileoutfd = u->fdout;
! 741: free(blk.blks);
! 742: return 1;
! 743: }
! 744:
! 745: /*
! 746: * Fix up the directory permissions and times post-order.
! 747: * We can't fix up directory permissions in place because the server may
! 748: * want us to have overly-tight permissions---say, those that don't
! 749: * allow writing into the directory.
! 750: * We also need to do our directory times post-order because making
! 751: * files within the directory will change modification times.
! 752: * Returns zero on failure, non-zero on success.
! 753: */
! 754: int
! 755: rsync_uploader_tail(struct upload *u, struct sess *sess)
! 756: {
! 757: size_t i;
! 758:
! 759:
! 760: if ( ! sess->opts->preserve_times &&
! 761: ! sess->opts->preserve_perms)
! 762: return 1;
! 763:
! 764: LOG2(sess, "fixing up directory times and permissions");
! 765:
! 766: for (i = 0; i < u->flsz; i++)
! 767: if (S_ISDIR(u->fl[i].st.mode))
! 768: if ( ! post_dir(sess, u, i))
! 769: return 0;
! 770:
! 771: return 1;
! 772: }