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

Annotation of src/usr.bin/rsync/blocks.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 <endian.h>
        !            22: #include <errno.h>
        !            23: #include <fcntl.h>
        !            24: #include <inttypes.h>
        !            25: #include <stdio.h>
        !            26: #include <stdlib.h>
        !            27: #include <string.h>
        !            28: #include <unistd.h>
        !            29:
        !            30: #include "md4.h"
        !            31: #include "extern.h"
        !            32:
        !            33: /*
        !            34:  * Flush out "size" bytes of the buffer, doing all of the appropriate
        !            35:  * chunking of the data, then the subsequent token (or zero).
        !            36:  * This is symmetrised in blk_merge().
        !            37:  * Return zero on failure, non-zero on success.
        !            38:  */
        !            39: static int
        !            40: blk_flush(struct sess *sess, int fd,
        !            41:        const void *b, off_t size, int32_t token)
        !            42: {
        !            43:        off_t   i = 0, sz;
        !            44:
        !            45:        while (i < size) {
        !            46:                sz = MAX_CHUNK < (size - i) ?
        !            47:                        MAX_CHUNK : (size - i);
        !            48:                if ( ! io_write_int(sess, fd, sz)) {
        !            49:                        ERRX1(sess, "io_write_int");
        !            50:                        return 0;
        !            51:                } else if ( ! io_write_buf(sess, fd, b + i, sz)) {
        !            52:                        ERRX1(sess, "io_write_buf");
        !            53:                        return 0;
        !            54:                }
        !            55:                i += sz;
        !            56:        }
        !            57:
        !            58:        if ( ! io_write_int(sess, fd, token)) {
        !            59:                ERRX1(sess, "io_write_int");
        !            60:                return 0;
        !            61:        }
        !            62:
        !            63:        return 1;
        !            64: }
        !            65:
        !            66: /*
        !            67:  * From our current position of "offs" in buffer "buf" of total size
        !            68:  * "size", see if we can find a matching block in our list of blocks.
        !            69:  * The "hint" refers to the block that *might* work.
        !            70:  * Returns the blk or NULL if no matching block was found.
        !            71:  */
        !            72: static struct blk *
        !            73: blk_find(struct sess *sess, const void *buf, off_t size, off_t offs,
        !            74:        const struct blkset *blks, const char *path, size_t hint)
        !            75: {
        !            76:        unsigned char    md[MD4_DIGEST_LENGTH];
        !            77:        uint32_t         fhash;
        !            78:        off_t            remain, osz;
        !            79:        size_t           i;
        !            80:        int              have_md = 0;
        !            81:
        !            82:        /*
        !            83:         * First, compute our fast hash.
        !            84:         * FIXME: yes, this can be a rolling computation, but I'm
        !            85:         * deliberately making it simple first.
        !            86:         */
        !            87:
        !            88:        remain = size - offs;
        !            89:        assert(remain);
        !            90:        osz = remain < (off_t)blks->len ? remain : (off_t)blks->len;
        !            91:        fhash = hash_fast(buf + offs, (size_t)osz);
        !            92:        have_md = 0;
        !            93:
        !            94:        /*
        !            95:         * Start with our match hint.
        !            96:         * This just runs the fast and slow check with the hint.
        !            97:         */
        !            98:
        !            99:        if (hint < blks->blksz &&
        !           100:            fhash == blks->blks[hint].chksum_short &&
        !           101:            (size_t)osz == blks->blks[hint].len) {
        !           102:                hash_slow(buf + offs, (size_t)osz, md, sess);
        !           103:                have_md = 1;
        !           104:                if (0 == memcmp(md,
        !           105:                    blks->blks[hint].chksum_long, blks->csum)) {
        !           106:                        LOG4(sess, "%s: found matching hinted match: "
        !           107:                                "position %jd, block %zu "
        !           108:                                "(position %jd, size %zu)", path,
        !           109:                                (intmax_t)offs, blks->blks[hint].idx,
        !           110:                                (intmax_t)blks->blks[hint].offs,
        !           111:                                blks->blks[hint].len);
        !           112:                        return &blks->blks[hint];
        !           113:                }
        !           114:        }
        !           115:
        !           116:        /*
        !           117:         * Now loop and look for the fast hash.
        !           118:         * If it's found, move on to the slow hash.
        !           119:         */
        !           120:
        !           121:        for (i = 0; i < blks->blksz; i++) {
        !           122:                if (fhash != blks->blks[i].chksum_short)
        !           123:                        continue;
        !           124:                if ((size_t)osz != blks->blks[i].len)
        !           125:                        continue;
        !           126:
        !           127:                LOG4(sess, "%s: found matching fast match: "
        !           128:                        "position %jd, block %zu "
        !           129:                        "(position %jd, size %zu)", path,
        !           130:                        (intmax_t)offs, blks->blks[i].idx,
        !           131:                        (intmax_t)blks->blks[i].offs,
        !           132:                        blks->blks[i].len);
        !           133:
        !           134:                /* Compute slow hash on demand. */
        !           135:
        !           136:                if (0 == have_md) {
        !           137:                        hash_slow(buf + offs, (size_t)osz, md, sess);
        !           138:                        have_md = 1;
        !           139:                }
        !           140:
        !           141:                if (memcmp(md, blks->blks[i].chksum_long, blks->csum))
        !           142:                        continue;
        !           143:
        !           144:                LOG4(sess, "%s: sender verifies slow match", path);
        !           145:                return &blks->blks[i];
        !           146:        }
        !           147:
        !           148:        return NULL;
        !           149: }
        !           150:
        !           151: /*
        !           152:  * The main reconstruction algorithm on the sender side.
        !           153:  * Scans byte-wise over the input file, looking for matching blocks in
        !           154:  * what the server sent us.
        !           155:  * If a block is found, emit all data up until the block, then the token
        !           156:  * for the block.
        !           157:  * The receiving end can then reconstruct the file trivially.
        !           158:  * Return zero on failure, non-zero on success.
        !           159:  */
        !           160: static int
        !           161: blk_match_send(struct sess *sess, const char *path, int fd,
        !           162:        const void *buf, off_t size, const struct blkset *blks)
        !           163: {
        !           164:        off_t            offs, last, end, fromcopy = 0, fromdown = 0,
        !           165:                         total = 0, sz;
        !           166:        int32_t          tok;
        !           167:        struct blk      *blk;
        !           168:        size_t           hint = 0;
        !           169:
        !           170:        /*
        !           171:         * Stop searching at the length of the file minus the size of
        !           172:         * the last block.
        !           173:         * The reason for this being that we don't need to do an
        !           174:         * incremental hash within the last block---if it doesn't match,
        !           175:         * it doesn't match.
        !           176:         */
        !           177:
        !           178:        end = size + 1 - blks->blks[blks->blksz - 1].len;
        !           179:
        !           180:        for (last = offs = 0; offs < end; offs++) {
        !           181:                blk = blk_find(sess, buf, size,
        !           182:                        offs, blks, path, hint);
        !           183:                if (NULL == blk)
        !           184:                        continue;
        !           185:
        !           186:                sz = offs - last;
        !           187:                fromdown += sz;
        !           188:                total += sz;
        !           189:                LOG4(sess, "%s: flushing %jd B before %zu B "
        !           190:                        "block %zu", path, (intmax_t)sz, blk->len,
        !           191:                        blk->idx);
        !           192:                tok = -(blk->idx + 1);
        !           193:
        !           194:                /*
        !           195:                 * Write the data we have, then follow it with the tag
        !           196:                 * of the block that matches.
        !           197:                 * The receiver will then write our data, then the data
        !           198:                 * it already has in the matching block.
        !           199:                 */
        !           200:
        !           201:                if ( ! blk_flush(sess, fd, buf + last, sz, tok)) {
        !           202:                        ERRX1(sess, "blk_flush");
        !           203:                        return 0;
        !           204:                }
        !           205:
        !           206:                fromcopy += blk->len;
        !           207:                total += blk->len;
        !           208:                offs += blk->len - 1;
        !           209:                last = offs + 1;
        !           210:                hint = blk->idx + 1;
        !           211:        }
        !           212:
        !           213:        /* Emit remaining data and send terminator token. */
        !           214:
        !           215:        sz = size - last;
        !           216:        total += sz;
        !           217:        fromdown += sz;
        !           218:
        !           219:        LOG4(sess, "%s: flushing remaining %jd B", path, (intmax_t)sz);
        !           220:
        !           221:        if ( ! blk_flush(sess, fd, buf + last, sz, 0)) {
        !           222:                ERRX1(sess, "blk_flush");
        !           223:                return 0;
        !           224:        }
        !           225:
        !           226:        LOG3(sess, "%s: flushed (chunked) %jd B total, "
        !           227:                "%.2f%% upload ratio", path, (intmax_t)total,
        !           228:                100.0 * fromdown / total);
        !           229:        return 1;
        !           230: }
        !           231:
        !           232: /*
        !           233:  * Given a local file "path" and the blocks created by a remote machine,
        !           234:  * find out which blocks of our file they don't have and send them.
        !           235:  * Return zero on failure, non-zero on success.
        !           236:  */
        !           237: int
        !           238: blk_match(struct sess *sess, int fd,
        !           239:        const struct blkset *blks, const char *path)
        !           240: {
        !           241:        int              nfd, rc = 0, c;
        !           242:        struct stat      st;
        !           243:        void            *map = MAP_FAILED;
        !           244:        size_t           mapsz;
        !           245:        unsigned char    filemd[MD4_DIGEST_LENGTH];
        !           246:
        !           247:        /* Start by mapping our file into memory. */
        !           248:
        !           249:        if (-1 == (nfd = open(path, O_RDONLY, 0))) {
        !           250:                ERR(sess, "%s: open", path);
        !           251:                return 0;
        !           252:        } else if (-1 == fstat(nfd, &st)) {
        !           253:                ERR(sess, "%s: fstat", path);
        !           254:                close(nfd);
        !           255:                return 0;
        !           256:        }
        !           257:
        !           258:        /*
        !           259:         * We might possibly have a zero-length file, in which case the
        !           260:         * mmap() will fail, so only do this with non-zero files.
        !           261:         */
        !           262:
        !           263:        if ((mapsz = st.st_size) > 0) {
        !           264:                map = mmap(NULL, mapsz, PROT_READ, MAP_SHARED, nfd, 0);
        !           265:                if (MAP_FAILED == map) {
        !           266:                        ERR(sess, "%s: mmap", path);
        !           267:                        close(nfd);
        !           268:                        return 0;
        !           269:                }
        !           270:        }
        !           271:
        !           272:        /*
        !           273:         * If the file's empty or we don't have any blocks from the
        !           274:         * sender, then simply send the whole file.
        !           275:         * Otherwise, run the hash matching routine and send raw chunks
        !           276:         * and subsequent matching tokens.
        !           277:         * This part broadly symmetrises blk_merge().
        !           278:         */
        !           279:
        !           280:        if (st.st_size && blks->blksz) {
        !           281:                c = blk_match_send(sess, path,
        !           282:                        fd, map, st.st_size, blks);
        !           283:                if ( ! c) {
        !           284:                        ERRX1(sess, "blk_match_send");
        !           285:                        goto out;
        !           286:                }
        !           287:        } else {
        !           288:                if ( ! blk_flush(sess, fd, map, st.st_size, 0)) {
        !           289:                        ERRX1(sess, "blk_flush");
        !           290:                        return 0;
        !           291:                }
        !           292:                LOG3(sess, "%s: flushed (un-chunked) %jd B, 100%% "
        !           293:                        "upload ratio", path, (intmax_t)st.st_size);
        !           294:        }
        !           295:
        !           296:        /*
        !           297:         * Now write the full file hash.
        !           298:         * Since we're seeding the hash, this always gives us some sort
        !           299:         * of data even if the file's zero-length.
        !           300:         */
        !           301:
        !           302:        hash_file(map, st.st_size, filemd, sess);
        !           303:
        !           304:        if ( ! io_write_buf(sess, fd, filemd, MD4_DIGEST_LENGTH)) {
        !           305:                ERRX1(sess, "io_write_buf");
        !           306:                goto out;
        !           307:        }
        !           308:
        !           309:        rc = 1;
        !           310: out:
        !           311:        if (MAP_FAILED != map)
        !           312:                munmap(map, mapsz);
        !           313:        close(nfd);
        !           314:        return rc;
        !           315: }
        !           316:
        !           317: /* FIXME: remove. */
        !           318: void
        !           319: blkset_free(struct blkset *p)
        !           320: {
        !           321:
        !           322:        if (NULL == p)
        !           323:                return;
        !           324:        free(p->blks);
        !           325:        free(p);
        !           326: }
        !           327:
        !           328: /*
        !           329:  * Sent from the sender to the receiver to indicate that the block set
        !           330:  * has been received.
        !           331:  * Symmetrises blk_send_ack().
        !           332:  * Returns zero on failure, non-zero on success.
        !           333:  */
        !           334: int
        !           335: blk_recv_ack(struct sess *sess,
        !           336:        int fd, const struct blkset *blocks, int32_t idx)
        !           337: {
        !           338:
        !           339:        /* FIXME: put into static block. */
        !           340:
        !           341:        if ( ! io_write_int(sess, fd, idx))
        !           342:                ERRX1(sess, "io_write_int");
        !           343:        else if ( ! io_write_int(sess, fd, blocks->blksz))
        !           344:                ERRX1(sess, "io_write_int");
        !           345:        else if ( ! io_write_int(sess, fd, blocks->len))
        !           346:                ERRX1(sess, "io_write_int");
        !           347:        else if ( ! io_write_int(sess, fd, blocks->csum))
        !           348:                ERRX1(sess, "io_write_int");
        !           349:        else if ( ! io_write_int(sess, fd, blocks->rem))
        !           350:                ERRX1(sess, "io_write_int");
        !           351:        else
        !           352:                return 1;
        !           353:
        !           354:        return 0;
        !           355: }
        !           356:
        !           357: /*
        !           358:  * Read all of the checksums for a file's blocks.
        !           359:  * Returns the set of blocks or NULL on failure.
        !           360:  */
        !           361: struct blkset *
        !           362: blk_recv(struct sess *sess, int fd, const char *path)
        !           363: {
        !           364:        struct blkset   *s;
        !           365:        int32_t          i;
        !           366:        size_t           j;
        !           367:        struct blk      *b;
        !           368:        off_t            offs = 0;
        !           369:
        !           370:        if (NULL == (s = calloc(1, sizeof(struct blkset)))) {
        !           371:                ERR(sess, "calloc");
        !           372:                return NULL;
        !           373:        }
        !           374:
        !           375:        /*
        !           376:         * The block prologue consists of a few values that we'll need
        !           377:         * in reading the individual blocks for this file.
        !           378:         * FIXME: read into buffer and unbuffer.
        !           379:         */
        !           380:
        !           381:        if ( ! io_read_size(sess, fd, &s->blksz)) {
        !           382:                ERRX1(sess, "io_read_size");
        !           383:                goto out;
        !           384:        } else if ( ! io_read_size(sess, fd, &s->len)) {
        !           385:                ERRX1(sess, "io_read_size");
        !           386:                goto out;
        !           387:        } else if ( ! io_read_size(sess, fd, &s->csum)) {
        !           388:                ERRX1(sess, "io_read_int");
        !           389:                goto out;
        !           390:        } else if ( ! io_read_size(sess, fd, &s->rem)) {
        !           391:                ERRX1(sess, "io_read_int");
        !           392:                goto out;
        !           393:        } else if (s->rem && s->rem >= s->len) {
        !           394:                ERRX(sess, "block remainder is "
        !           395:                        "greater than block size");
        !           396:                goto out;
        !           397:        }
        !           398:
        !           399:        LOG3(sess, "%s: read block prologue: %zu blocks of "
        !           400:                "%zu B, %zu B remainder, %zu B checksum", path,
        !           401:                s->blksz, s->len, s->rem, s->csum);
        !           402:
        !           403:        if (s->blksz) {
        !           404:                s->blks = calloc(s->blksz, sizeof(struct blk));
        !           405:                if (NULL == s->blks) {
        !           406:                        ERR(sess, "calloc");
        !           407:                        goto out;
        !           408:                }
        !           409:        }
        !           410:
        !           411:        /*
        !           412:         * Read each block individually.
        !           413:         * FIXME: read buffer and unbuffer.
        !           414:         */
        !           415:
        !           416:        for (j = 0; j < s->blksz; j++) {
        !           417:                b = &s->blks[j];
        !           418:                if ( ! io_read_int(sess, fd, &i)) {
        !           419:                        ERRX1(sess, "io_read_int");
        !           420:                        goto out;
        !           421:                }
        !           422:                b->chksum_short = i;
        !           423:
        !           424:                assert(s->csum <= sizeof(b->chksum_long));
        !           425:                if ( ! io_read_buf(sess,
        !           426:                    fd, b->chksum_long, s->csum)) {
        !           427:                        ERRX1(sess, "io_read_buf");
        !           428:                        goto out;
        !           429:                }
        !           430:
        !           431:                /*
        !           432:                 * If we're the last block, then we're assigned the
        !           433:                 * remainder of the data.
        !           434:                 */
        !           435:
        !           436:                b->offs = offs;
        !           437:                b->idx = j;
        !           438:                b->len = (j == (s->blksz - 1) && s->rem) ?
        !           439:                        s->rem : s->len;
        !           440:                offs += b->len;
        !           441:
        !           442:                LOG4(sess, "%s: read block %zu, "
        !           443:                        "length %zu B", path, b->idx, b->len);
        !           444:        }
        !           445:
        !           446:        s->size = offs;
        !           447:        LOG3(sess, "%s: read blocks: %zu blocks, %jd B total "
        !           448:                "blocked data", path, s->blksz, (intmax_t)s->size);
        !           449:        return s;
        !           450: out:
        !           451:        blkset_free(s);
        !           452:        return NULL;
        !           453: }
        !           454:
        !           455: /*
        !           456:  * Symmetrise blk_recv_ack(), except w/o the leading identifier.
        !           457:  * Return zero on failure, non-zero on success.
        !           458:  */
        !           459: int
        !           460: blk_send_ack(struct sess *sess, int fd, struct blkset *p)
        !           461: {
        !           462:        char     buf[16];
        !           463:        size_t   pos = 0, sz;
        !           464:
        !           465:        /* Put the entire send routine into a buffer. */
        !           466:
        !           467:        sz = sizeof(int32_t) + /* block count */
        !           468:             sizeof(int32_t) + /* block length */
        !           469:             sizeof(int32_t) + /* checksum length */
        !           470:             sizeof(int32_t); /* block remainder */
        !           471:        assert(sz <= sizeof(buf));
        !           472:
        !           473:        if ( ! io_read_buf(sess, fd, buf, sz)) {
        !           474:                ERRX1(sess, "io_read_buf");
        !           475:                return 0;
        !           476:        }
        !           477:
        !           478:        if ( ! io_unbuffer_size(sess, buf, &pos, sz, &p->blksz))
        !           479:                ERRX1(sess, "io_unbuffer_size");
        !           480:        else if ( ! io_unbuffer_size(sess, buf, &pos, sz, &p->len))
        !           481:                ERRX1(sess, "io_unbuffer_size");
        !           482:        else if ( ! io_unbuffer_size(sess, buf, &pos, sz, &p->csum))
        !           483:                ERRX1(sess, "io_unbuffer_size");
        !           484:        else if ( ! io_unbuffer_size(sess, buf, &pos, sz, &p->rem))
        !           485:                ERRX1(sess, "io_unbuffer_size");
        !           486:        else if (p->len && p->rem >= p->len)
        !           487:                ERRX1(sess, "non-zero length is less than remainder");
        !           488:        else if (0 == p->csum || p->csum > 16)
        !           489:                ERRX1(sess, "inappropriate checksum length");
        !           490:        else
        !           491:                return 1;
        !           492:
        !           493:        return 0;
        !           494: }
        !           495:
        !           496: /*
        !           497:  * The receiver now reads raw data and block indices from the sender,
        !           498:  * and merges them into the temporary file.
        !           499:  * Returns zero on failure, non-zero on success.
        !           500:  */
        !           501: int
        !           502: blk_merge(struct sess *sess, int fd, int ffd,
        !           503:        const struct blkset *block, int outfd, const char *path,
        !           504:        const void *map, size_t mapsz, float *stats)
        !           505: {
        !           506:        size_t           sz, tok;
        !           507:        int32_t          rawtok;
        !           508:        char            *buf = NULL;
        !           509:        void            *pp;
        !           510:        ssize_t          ssz;
        !           511:        int              rc = 0;
        !           512:        unsigned char    md[MD4_DIGEST_LENGTH],
        !           513:                         ourmd[MD4_DIGEST_LENGTH];
        !           514:        off_t            total = 0, fromcopy = 0, fromdown = 0;
        !           515:        MD4_CTX          ctx;
        !           516:
        !           517:        MD4_Init(&ctx);
        !           518:
        !           519:        rawtok = htole32(sess->seed);
        !           520:        MD4_Update(&ctx, (unsigned char *)&rawtok, sizeof(int32_t));
        !           521:
        !           522:        for (;;) {
        !           523:                /*
        !           524:                 * This matches the sequence in blk_flush().
        !           525:                 * We read the size/token, then optionally the data.
        !           526:                 * The size >0 for reading data, 0 for no more data, and
        !           527:                 * <0 for a token indicator.
        !           528:                 */
        !           529:
        !           530:                if ( ! io_read_int(sess, fd, &rawtok)) {
        !           531:                        ERRX1(sess, "io_read_int");
        !           532:                        goto out;
        !           533:                } else if (0 == rawtok)
        !           534:                        break;
        !           535:
        !           536:                if (rawtok > 0) {
        !           537:                        sz = rawtok;
        !           538:                        if (NULL == (pp = realloc(buf, sz))) {
        !           539:                                ERR(sess, "realloc");
        !           540:                                goto out;
        !           541:                        }
        !           542:                        buf = pp;
        !           543:                        if ( ! io_read_buf(sess, fd, buf, sz)) {
        !           544:                                ERRX1(sess, "io_read_int");
        !           545:                                goto out;
        !           546:                        }
        !           547:
        !           548:                        if ((ssz = write(outfd, buf, sz)) < 0) {
        !           549:                                ERR(sess, "write: temporary file");
        !           550:                                goto out;
        !           551:                        } else if ((size_t)ssz != sz) {
        !           552:                                ERRX(sess, "write: short write");
        !           553:                                goto out;
        !           554:                        }
        !           555:
        !           556:                        fromdown += sz;
        !           557:                        total += sz;
        !           558:                        LOG4(sess, "%s: received %zd B block, now %jd "
        !           559:                                "B total", path, ssz, (intmax_t)total);
        !           560:
        !           561:                        MD4_Update(&ctx, buf, sz);
        !           562:                } else {
        !           563:                        tok = -rawtok - 1;
        !           564:                        if (tok >= block->blksz) {
        !           565:                                ERRX(sess, "token not in block set");
        !           566:                                goto out;
        !           567:                        }
        !           568:
        !           569:                        /*
        !           570:                         * Now we read from our block.
        !           571:                         * We should only be at this point if we have a
        !           572:                         * block to read from, i.e., if we were able to
        !           573:                         * map our origin file and create a block
        !           574:                         * profile from it.
        !           575:                         */
        !           576:
        !           577:                        assert(MAP_FAILED != map);
        !           578:
        !           579:                        ssz = write(outfd,
        !           580:                                map + block->blks[tok].offs,
        !           581:                                block->blks[tok].len);
        !           582:
        !           583:                        if (ssz < 0) {
        !           584:                                ERR(sess, "write: temporary file");
        !           585:                                goto out;
        !           586:                        } else if ((size_t)ssz != block->blks[tok].len) {
        !           587:                                ERRX(sess, "write: short write");
        !           588:                                goto out;
        !           589:                        }
        !           590:
        !           591:                        fromcopy += block->blks[tok].len;
        !           592:                        total += block->blks[tok].len;
        !           593:                        LOG4(sess, "%s: copied %zu B, now %jd "
        !           594:                                "B total", path, block->blks[tok].len,
        !           595:                                (intmax_t)total);
        !           596:
        !           597:                        MD4_Update(&ctx,
        !           598:                                map + block->blks[tok].offs,
        !           599:                                block->blks[tok].len);
        !           600:                }
        !           601:        }
        !           602:
        !           603:
        !           604:        /* Make sure our resulting MD4_ hashes match. */
        !           605:
        !           606:        MD4_Final(ourmd, &ctx);
        !           607:
        !           608:        if ( ! io_read_buf(sess, fd, md, MD4_DIGEST_LENGTH)) {
        !           609:                ERRX1(sess, "io_read_buf");
        !           610:                goto out;
        !           611:        } else if (memcmp(md, ourmd, MD4_DIGEST_LENGTH)) {
        !           612:                ERRX(sess, "%s: file hash does not match", path);
        !           613:                goto out;
        !           614:        }
        !           615:
        !           616:        *stats = 100.0 * fromdown / total;
        !           617:        rc = 1;
        !           618: out:
        !           619:        free(buf);
        !           620:        return rc;
        !           621: }
        !           622:
        !           623: /*
        !           624:  * Transmit the metadata for set and blocks.
        !           625:  * Return zero on failure, non-zero on success.
        !           626:  */
        !           627: int
        !           628: blk_send(struct sess *sess, int fd, size_t idx,
        !           629:        const struct blkset *p, const char *path)
        !           630: {
        !           631:        char    *buf;
        !           632:        size_t   i, pos = 0, sz;
        !           633:        int      rc = 0;
        !           634:
        !           635:        /* Put the entire send routine into a buffer. */
        !           636:
        !           637:        sz = sizeof(int32_t) + /* identifier */
        !           638:             sizeof(int32_t) + /* block count */
        !           639:             sizeof(int32_t) + /* block length */
        !           640:             sizeof(int32_t) + /* checksum length */
        !           641:             sizeof(int32_t) + /* block remainder */
        !           642:             p->blksz *
        !           643:             (sizeof(int32_t) + /* short checksum */
        !           644:              p->csum); /* long checksum */
        !           645:
        !           646:        if (NULL == (buf = malloc(sz))) {
        !           647:                ERR(sess, "malloc");
        !           648:                return 0;
        !           649:        }
        !           650:
        !           651:        io_buffer_int(sess, buf, &pos, sz, idx);
        !           652:        io_buffer_int(sess, buf, &pos, sz, p->blksz);
        !           653:        io_buffer_int(sess, buf, &pos, sz, p->len);
        !           654:        io_buffer_int(sess, buf, &pos, sz, p->csum);
        !           655:        io_buffer_int(sess, buf, &pos, sz, p->rem);
        !           656:
        !           657:        for (i = 0; i < p->blksz; i++) {
        !           658:                io_buffer_int(sess, buf, &pos,
        !           659:                        sz, p->blks[i].chksum_short);
        !           660:                io_buffer_buf(sess, buf, &pos, sz,
        !           661:                        p->blks[i].chksum_long, p->csum);
        !           662:        }
        !           663:
        !           664:        assert(pos == sz);
        !           665:
        !           666:        if ( ! io_write_buf(sess, fd, buf, sz)) {
        !           667:                ERRX1(sess, "io_write_buf");
        !           668:                goto out;
        !           669:        }
        !           670:
        !           671:        LOG3(sess, "%s: sent block prologue: %zu blocks of %zu B, "
        !           672:                "%zu B remainder, %zu B checksum", path,
        !           673:                p->blksz, p->len, p->rem, p->csum);
        !           674:        rc = 1;
        !           675: out:
        !           676:        free(buf);
        !           677:        return rc;
        !           678: }