[BACK]Return to uploader.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rsync

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: }