1*7eb67ff9Stb /* $OpenBSD: blocks.c,v 1.27 2024/09/27 13:13:14 tb Exp $ */ 260a32ee9Sbenno /* 360a32ee9Sbenno * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 460a32ee9Sbenno * 560a32ee9Sbenno * Permission to use, copy, modify, and distribute this software for any 660a32ee9Sbenno * purpose with or without fee is hereby granted, provided that the above 760a32ee9Sbenno * copyright notice and this permission notice appear in all copies. 860a32ee9Sbenno * 960a32ee9Sbenno * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1060a32ee9Sbenno * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1160a32ee9Sbenno * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1260a32ee9Sbenno * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1360a32ee9Sbenno * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1460a32ee9Sbenno * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1560a32ee9Sbenno * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1660a32ee9Sbenno */ 17ea555712Sflorian #include <sys/queue.h> 1860a32ee9Sbenno #include <sys/stat.h> 1960a32ee9Sbenno 2060a32ee9Sbenno #include <assert.h> 2160a32ee9Sbenno #include <endian.h> 2260a32ee9Sbenno #include <errno.h> 2360a32ee9Sbenno #include <inttypes.h> 2460a32ee9Sbenno #include <stdio.h> 2560a32ee9Sbenno #include <stdlib.h> 2660a32ee9Sbenno #include <string.h> 2760a32ee9Sbenno #include <unistd.h> 2860a32ee9Sbenno 2934b470cbStb #include <openssl/md4.h> 3034b470cbStb 3160a32ee9Sbenno #include "extern.h" 3260a32ee9Sbenno 33ea555712Sflorian struct blkhash { 34ea555712Sflorian const struct blk *blk; 35ea555712Sflorian TAILQ_ENTRY(blkhash) entries; 36ea555712Sflorian }; 37ea555712Sflorian 38ea555712Sflorian TAILQ_HEAD(blkhashq, blkhash); 39ea555712Sflorian 40ea555712Sflorian /* 41ea555712Sflorian * Table used by the sender for looking up blocks. 42ea555712Sflorian * The blocks are those sent by the receiver; we're looking up using 43ea555712Sflorian * hashes computed from our local file. 44ea555712Sflorian */ 45ea555712Sflorian struct blktab { 46ea555712Sflorian struct blkhashq *q; /* entries in the hashtable */ 47ea555712Sflorian size_t qsz; /* size of the hashtable */ 48ea555712Sflorian struct blkhash *blks; /* pre-allocated hashtable entries */ 49ea555712Sflorian }; 50ea555712Sflorian 51ea555712Sflorian /* 52ea555712Sflorian * This is the number of buckets in the hashtable. 53ea555712Sflorian * Use the same that GPL rsync uses. 54ea555712Sflorian * (It should be dynamic?) 55ea555712Sflorian */ 56ea555712Sflorian #define BLKTAB_SZ 65536 57ea555712Sflorian 58ea555712Sflorian /* 59ea555712Sflorian * Initialise an empty hashtable with BLKTAB_SZ entries in it. 60ea555712Sflorian * Populate it for each file with blkhash_set. 61ea555712Sflorian * When we've processed all files, call blkhash_free. 62ea555712Sflorian * Returns NULL on allocation failure. 63ea555712Sflorian */ 64ea555712Sflorian struct blktab * 65ea555712Sflorian blkhash_alloc(void) 66ea555712Sflorian { 67ea555712Sflorian struct blktab *p; 68ea555712Sflorian 69ea555712Sflorian if ((p = calloc(1, sizeof(struct blktab))) == NULL) { 70ea555712Sflorian ERR("calloc"); 71ea555712Sflorian return NULL; 72ea555712Sflorian } 73ea555712Sflorian p->qsz = BLKTAB_SZ; 74ea555712Sflorian p->q = calloc(p->qsz, sizeof(struct blkhashq)); 75ea555712Sflorian if (p->q == NULL) { 76ea555712Sflorian ERR("calloc"); 77ea555712Sflorian free(p); 78ea555712Sflorian return NULL; 79ea555712Sflorian } 80ea555712Sflorian return p; 81ea555712Sflorian } 82ea555712Sflorian 83ea555712Sflorian /* 84ea555712Sflorian * Populate the hashtable with an incoming file's blocks. 85ea555712Sflorian * This will clear out any existing hashed data. 86ea555712Sflorian * Returns zero on allocation failure, non-zero otherwise. 87ea555712Sflorian */ 88ea555712Sflorian int 89ea555712Sflorian blkhash_set(struct blktab *p, const struct blkset *bset) 90ea555712Sflorian { 91cd54cfb8Stb struct blkhash *blks; 92ea555712Sflorian size_t i, idx; 93ea555712Sflorian 9454ac4772Stb if (bset == NULL || bset->blksz == 0) 95ea555712Sflorian return 1; 96ea555712Sflorian 97ea555712Sflorian /* Wipe clean the table. */ 98ea555712Sflorian 99ea555712Sflorian for (i = 0; i < p->qsz; i++) 100ea555712Sflorian TAILQ_INIT(&p->q[i]); 101ea555712Sflorian 102ea555712Sflorian /* Fill in the hashtable. */ 103ea555712Sflorian 104cd54cfb8Stb blks = reallocarray(p->blks, bset->blksz, sizeof(struct blkhash)); 105cd54cfb8Stb if (blks == NULL) { 106ea555712Sflorian ERR("reallocarray"); 107cd54cfb8Stb free(p->blks); 108cd54cfb8Stb p->blks = NULL; 109ea555712Sflorian return 0; 110ea555712Sflorian } 111cd54cfb8Stb p->blks = blks; 112ea555712Sflorian for (i = 0; i < bset->blksz; i++) { 113ea555712Sflorian p->blks[i].blk = &bset->blks[i]; 114ea555712Sflorian idx = bset->blks[i].chksum_short % p->qsz; 115ea555712Sflorian assert(idx < p->qsz); 116ea555712Sflorian TAILQ_INSERT_TAIL(&p->q[idx], &p->blks[i], entries); 117ea555712Sflorian } 118ea555712Sflorian 119ea555712Sflorian return 1; 120ea555712Sflorian } 121ea555712Sflorian 122ea555712Sflorian /* 123ea555712Sflorian * Free as allocated with blkhash_alloc(). 124ea555712Sflorian */ 125ea555712Sflorian void 126ea555712Sflorian blkhash_free(struct blktab *p) 127ea555712Sflorian { 128*7eb67ff9Stb if (p == NULL) 129*7eb67ff9Stb return; 130a1cea692Sjob free(p->q); 131ea555712Sflorian free(p->blks); 132ea555712Sflorian free(p); 133ea555712Sflorian } 134ea555712Sflorian 13560a32ee9Sbenno /* 13660a32ee9Sbenno * From our current position of "offs" in buffer "buf" of total size 13760a32ee9Sbenno * "size", see if we can find a matching block in our list of blocks. 13860a32ee9Sbenno * The "hint" refers to the block that *might* work. 13960a32ee9Sbenno * Returns the blk or NULL if no matching block was found. 14060a32ee9Sbenno */ 141ea555712Sflorian static const struct blk * 142ea555712Sflorian blk_find(struct sess *sess, struct blkstat *st, 143ea555712Sflorian const struct blkset *blks, const char *path, int recomp) 14460a32ee9Sbenno { 14560a32ee9Sbenno unsigned char md[MD4_DIGEST_LENGTH]; 14660a32ee9Sbenno uint32_t fhash; 14760a32ee9Sbenno off_t remain, osz; 14860a32ee9Sbenno int have_md = 0; 149ea555712Sflorian char *map; 150ea555712Sflorian const struct blkhashq *q; 151ea555712Sflorian const struct blkhash *ent; 152ea555712Sflorian 153ea555712Sflorian remain = st->mapsz - st->offs; 154ea555712Sflorian assert(remain); 155ea555712Sflorian osz = MINIMUM(remain, (off_t)blks->len); 15660a32ee9Sbenno 15760a32ee9Sbenno /* 158ea555712Sflorian * First, compute our fast hash the hard way (if we're 159ea555712Sflorian * reentering this function from a previous block match, or the 160ea555712Sflorian * first time) or from our existing s1 and s2 values. 16160a32ee9Sbenno */ 16260a32ee9Sbenno 163ea555712Sflorian if (!recomp) { 164ea555712Sflorian fhash = (st->s1 & 0xFFFF) | (st->s2 << 16); 165ea555712Sflorian } else { 166ea555712Sflorian fhash = hash_fast(st->map + st->offs, (size_t)osz); 167ea555712Sflorian st->s1 = fhash & 0xFFFF; 168ea555712Sflorian st->s2 = fhash >> 16; 169ea555712Sflorian } 17060a32ee9Sbenno 17160a32ee9Sbenno /* 17260a32ee9Sbenno * Start with our match hint. 17360a32ee9Sbenno * This just runs the fast and slow check with the hint. 17460a32ee9Sbenno */ 17560a32ee9Sbenno 176ea555712Sflorian if (st->hint < blks->blksz && 177ea555712Sflorian fhash == blks->blks[st->hint].chksum_short && 178ea555712Sflorian (size_t)osz == blks->blks[st->hint].len) { 179ea555712Sflorian hash_slow(st->map + st->offs, (size_t)osz, md, sess); 18060a32ee9Sbenno have_md = 1; 181ea555712Sflorian if (memcmp(md, blks->blks[st->hint].chksum_long, blks->csum) == 0) { 182b2a7eac7Sbenno LOG4("%s: found matching hinted match: " 18302f20df6Sderaadt "position %jd, block %zu (position %jd, size %zu)", 18402f20df6Sderaadt path, 185ea555712Sflorian (intmax_t)st->offs, blks->blks[st->hint].idx, 186ea555712Sflorian (intmax_t)blks->blks[st->hint].offs, 187ea555712Sflorian blks->blks[st->hint].len); 188ea555712Sflorian return &blks->blks[st->hint]; 18960a32ee9Sbenno } 19060a32ee9Sbenno } 19160a32ee9Sbenno 19260a32ee9Sbenno /* 193ea555712Sflorian * Look for the fast hash modulus in our hashtable, filter for 194ea555712Sflorian * those matching the full hash and length, then move to the 195ea555712Sflorian * slow hash. 196ea555712Sflorian * The slow hash is computed only once. 19760a32ee9Sbenno */ 19860a32ee9Sbenno 199ea555712Sflorian q = &st->blktab->q[fhash % st->blktab->qsz]; 200ea555712Sflorian 201ea555712Sflorian TAILQ_FOREACH(ent, q, entries) { 202ea555712Sflorian if (fhash != ent->blk->chksum_short || 203ea555712Sflorian (size_t)osz != ent->blk->len) 20460a32ee9Sbenno continue; 20560a32ee9Sbenno 206b2a7eac7Sbenno LOG4("%s: found matching fast match: " 20702f20df6Sderaadt "position %jd, block %zu (position %jd, size %zu)", 208ea555712Sflorian path, (intmax_t)st->offs, ent->blk->idx, 209ea555712Sflorian (intmax_t)ent->blk->offs, ent->blk->len); 21060a32ee9Sbenno 211f1dcb30aSderaadt if (have_md == 0) { 212ea555712Sflorian hash_slow(st->map + st->offs, (size_t)osz, md, sess); 21360a32ee9Sbenno have_md = 1; 21460a32ee9Sbenno } 21560a32ee9Sbenno 216ea555712Sflorian if (memcmp(md, ent->blk->chksum_long, blks->csum)) 21760a32ee9Sbenno continue; 21860a32ee9Sbenno 219b2a7eac7Sbenno LOG4("%s: sender verifies slow match", path); 220ea555712Sflorian return ent->blk; 221ea555712Sflorian } 222ea555712Sflorian 223ea555712Sflorian /* 224ea555712Sflorian * Adjust our partial sums for the hashing. 225ea555712Sflorian * We first remove the first byte from the sum. 226ea555712Sflorian * We then, if we have space, add the first byte of the next 227ea555712Sflorian * block in the sequence. 228ea555712Sflorian */ 229ea555712Sflorian 230ea555712Sflorian map = st->map + st->offs; 231ea555712Sflorian st->s1 -= map[0]; 232ea555712Sflorian st->s2 -= osz * map[0]; 233ea555712Sflorian 234ea555712Sflorian if (osz < remain) { 235ea555712Sflorian st->s1 += map[osz]; 236ea555712Sflorian st->s2 += st->s1; 23760a32ee9Sbenno } 23860a32ee9Sbenno 23960a32ee9Sbenno return NULL; 24060a32ee9Sbenno } 24160a32ee9Sbenno 24260a32ee9Sbenno /* 24364a7cfb7Sflorian * Given a local file "path" and the blocks created by a remote machine, 24464a7cfb7Sflorian * find out which blocks of our file they don't have and send them. 24564a7cfb7Sflorian * This function is reentrant: it must be called while there's still 24664a7cfb7Sflorian * data to send. 24760a32ee9Sbenno */ 24864a7cfb7Sflorian void 24964a7cfb7Sflorian blk_match(struct sess *sess, const struct blkset *blks, 25064a7cfb7Sflorian const char *path, struct blkstat *st) 25160a32ee9Sbenno { 252042f4afaSclaudio off_t last, end = 0, sz; 25360a32ee9Sbenno int32_t tok; 254ea555712Sflorian size_t i; 255ea555712Sflorian const struct blk *blk; 25660a32ee9Sbenno 25760a32ee9Sbenno /* 25864a7cfb7Sflorian * If the file's empty or we don't have any blocks from the 25964a7cfb7Sflorian * sender, then simply send the whole file. 26064a7cfb7Sflorian * Otherwise, run the hash matching routine and send raw chunks 26164a7cfb7Sflorian * and subsequent matching tokens. 26264a7cfb7Sflorian */ 26364a7cfb7Sflorian 26464a7cfb7Sflorian if (st->mapsz && blks->blksz) { 26564a7cfb7Sflorian /* 26664a7cfb7Sflorian * Stop searching at the length of the file minus the 26764a7cfb7Sflorian * size of the last block. 26860a32ee9Sbenno * The reason for this being that we don't need to do an 26964a7cfb7Sflorian * incremental hash within the last block---if it 27064a7cfb7Sflorian * doesn't match, it doesn't match. 27160a32ee9Sbenno */ 27260a32ee9Sbenno 2730889042fSflorian end = st->mapsz + 1 - blks->blks[blks->blksz - 1].len; 274042f4afaSclaudio } 27560a32ee9Sbenno 276042f4afaSclaudio last = st->offs; 277ea555712Sflorian for (i = 0; st->offs < end; st->offs++, i++) { 278ea555712Sflorian blk = blk_find(sess, st, blks, path, i == 0); 279f1dcb30aSderaadt if (blk == NULL) 28060a32ee9Sbenno continue; 28160a32ee9Sbenno 2820889042fSflorian sz = st->offs - last; 2830889042fSflorian st->dirty += sz; 2840889042fSflorian st->total += sz; 285b2a7eac7Sbenno LOG4("%s: flushing %jd B before %zu B block %zu", 28602f20df6Sderaadt path, (intmax_t)sz, 28764a7cfb7Sflorian blk->len, blk->idx); 28860a32ee9Sbenno tok = -(blk->idx + 1); 28960a32ee9Sbenno 290dcad62ecSclaudio hash_file_buf(&st->ctx, st->map + last, sz + blk->len); 291dcad62ecSclaudio 29260a32ee9Sbenno /* 29364a7cfb7Sflorian * Write the data we have, then follow it with 29464a7cfb7Sflorian * the tag of the block that matches. 29560a32ee9Sbenno */ 29660a32ee9Sbenno 29764a7cfb7Sflorian st->curpos = last; 29864a7cfb7Sflorian st->curlen = st->curpos + sz; 29964a7cfb7Sflorian st->curtok = tok; 300ed5cc9fbSderaadt assert(st->curtok != 0); 30164a7cfb7Sflorian st->curst = sz ? BLKSTAT_DATA : BLKSTAT_TOK; 3020889042fSflorian st->total += blk->len; 3030889042fSflorian st->offs += blk->len; 3040889042fSflorian st->hint = blk->idx + 1; 305dcad62ecSclaudio 30664a7cfb7Sflorian return; 30760a32ee9Sbenno } 30860a32ee9Sbenno 30960a32ee9Sbenno /* Emit remaining data and send terminator token. */ 31060a32ee9Sbenno 3110889042fSflorian sz = st->mapsz - last; 312042f4afaSclaudio LOG4("%s: flushing %s %jd B", path, 313042f4afaSclaudio last == 0 ? "whole" : "remaining", (intmax_t)sz); 314042f4afaSclaudio 315042f4afaSclaudio hash_file_buf(&st->ctx, st->map + last, sz); 31664a7cfb7Sflorian 3170889042fSflorian st->total += sz; 3180889042fSflorian st->dirty += sz; 31964a7cfb7Sflorian st->curpos = last; 32064a7cfb7Sflorian st->curlen = st->curpos + sz; 32164a7cfb7Sflorian st->curtok = 0; 32264a7cfb7Sflorian st->curst = sz ? BLKSTAT_DATA : BLKSTAT_TOK; 32360a32ee9Sbenno } 32460a32ee9Sbenno 32560a32ee9Sbenno /* 32655cb9f91Sbenno * Buffer the message from sender to the receiver indicating that the 32755cb9f91Sbenno * block set has been received. 32860a32ee9Sbenno * Symmetrises blk_send_ack(). 32960a32ee9Sbenno */ 33055cb9f91Sbenno void 331ba617adaSbenno blk_recv_ack(char buf[20], const struct blkset *blocks, int32_t idx) 33260a32ee9Sbenno { 333ceac33e0Sflorian size_t pos = 0, sz; 33460a32ee9Sbenno 335ceac33e0Sflorian sz = sizeof(int32_t) + /* index */ 336ceac33e0Sflorian sizeof(int32_t) + /* block count */ 337ceac33e0Sflorian sizeof(int32_t) + /* block length */ 338ceac33e0Sflorian sizeof(int32_t) + /* checksum length */ 339ceac33e0Sflorian sizeof(int32_t); /* block remainder */ 34055cb9f91Sbenno assert(sz == 20); 34160a32ee9Sbenno 342ba617adaSbenno io_buffer_int(buf, &pos, sz, idx); 343ba617adaSbenno io_buffer_int(buf, &pos, sz, blocks->blksz); 344ba617adaSbenno io_buffer_int(buf, &pos, sz, blocks->len); 345ba617adaSbenno io_buffer_int(buf, &pos, sz, blocks->csum); 346ba617adaSbenno io_buffer_int(buf, &pos, sz, blocks->rem); 347ceac33e0Sflorian assert(pos == sz); 348ceac33e0Sflorian } 349ceac33e0Sflorian 35060a32ee9Sbenno /* 35160a32ee9Sbenno * Read all of the checksums for a file's blocks. 35260a32ee9Sbenno * Returns the set of blocks or NULL on failure. 35360a32ee9Sbenno */ 35460a32ee9Sbenno struct blkset * 35560a32ee9Sbenno blk_recv(struct sess *sess, int fd, const char *path) 35660a32ee9Sbenno { 35760a32ee9Sbenno struct blkset *s; 35860a32ee9Sbenno int32_t i; 35960a32ee9Sbenno size_t j; 36060a32ee9Sbenno struct blk *b; 36160a32ee9Sbenno off_t offs = 0; 36260a32ee9Sbenno 363f1dcb30aSderaadt if ((s = calloc(1, sizeof(struct blkset))) == NULL) { 364b2a7eac7Sbenno ERR("calloc"); 36560a32ee9Sbenno return NULL; 36660a32ee9Sbenno } 36760a32ee9Sbenno 36860a32ee9Sbenno /* 36960a32ee9Sbenno * The block prologue consists of a few values that we'll need 37060a32ee9Sbenno * in reading the individual blocks for this file. 37160a32ee9Sbenno * FIXME: read into buffer and unbuffer. 37260a32ee9Sbenno */ 37360a32ee9Sbenno 37460a32ee9Sbenno if (!io_read_size(sess, fd, &s->blksz)) { 375b2a7eac7Sbenno ERRX1("io_read_size"); 37660a32ee9Sbenno goto out; 37760a32ee9Sbenno } else if (!io_read_size(sess, fd, &s->len)) { 378b2a7eac7Sbenno ERRX1("io_read_size"); 37960a32ee9Sbenno goto out; 38060a32ee9Sbenno } else if (!io_read_size(sess, fd, &s->csum)) { 381b2a7eac7Sbenno ERRX1("io_read_int"); 38260a32ee9Sbenno goto out; 38360a32ee9Sbenno } else if (!io_read_size(sess, fd, &s->rem)) { 384b2a7eac7Sbenno ERRX1("io_read_int"); 38560a32ee9Sbenno goto out; 38660a32ee9Sbenno } else if (s->rem && s->rem >= s->len) { 387b2a7eac7Sbenno ERRX("block remainder is " 38860a32ee9Sbenno "greater than block size"); 38960a32ee9Sbenno goto out; 39060a32ee9Sbenno } 39160a32ee9Sbenno 392b2a7eac7Sbenno LOG3("%s: read block prologue: %zu blocks of " 39360a32ee9Sbenno "%zu B, %zu B remainder, %zu B checksum", path, 39460a32ee9Sbenno s->blksz, s->len, s->rem, s->csum); 39560a32ee9Sbenno 39660a32ee9Sbenno if (s->blksz) { 39760a32ee9Sbenno s->blks = calloc(s->blksz, sizeof(struct blk)); 398f1dcb30aSderaadt if (s->blks == NULL) { 399b2a7eac7Sbenno ERR("calloc"); 40060a32ee9Sbenno goto out; 40160a32ee9Sbenno } 40260a32ee9Sbenno } 40360a32ee9Sbenno 40460a32ee9Sbenno /* 40560a32ee9Sbenno * Read each block individually. 40660a32ee9Sbenno * FIXME: read buffer and unbuffer. 40760a32ee9Sbenno */ 40860a32ee9Sbenno 40960a32ee9Sbenno for (j = 0; j < s->blksz; j++) { 41060a32ee9Sbenno b = &s->blks[j]; 41160a32ee9Sbenno if (!io_read_int(sess, fd, &i)) { 412b2a7eac7Sbenno ERRX1("io_read_int"); 41360a32ee9Sbenno goto out; 41460a32ee9Sbenno } 41560a32ee9Sbenno b->chksum_short = i; 41660a32ee9Sbenno 41760a32ee9Sbenno assert(s->csum <= sizeof(b->chksum_long)); 41860a32ee9Sbenno if (!io_read_buf(sess, 41960a32ee9Sbenno fd, b->chksum_long, s->csum)) { 420b2a7eac7Sbenno ERRX1("io_read_buf"); 42160a32ee9Sbenno goto out; 42260a32ee9Sbenno } 42360a32ee9Sbenno 42460a32ee9Sbenno /* 42560a32ee9Sbenno * If we're the last block, then we're assigned the 42660a32ee9Sbenno * remainder of the data. 42760a32ee9Sbenno */ 42860a32ee9Sbenno 42960a32ee9Sbenno b->offs = offs; 43060a32ee9Sbenno b->idx = j; 43160a32ee9Sbenno b->len = (j == (s->blksz - 1) && s->rem) ? 43260a32ee9Sbenno s->rem : s->len; 43360a32ee9Sbenno offs += b->len; 43460a32ee9Sbenno 435b2a7eac7Sbenno LOG4("%s: read block %zu, length %zu B", 43602f20df6Sderaadt path, b->idx, b->len); 43760a32ee9Sbenno } 43860a32ee9Sbenno 43960a32ee9Sbenno s->size = offs; 440b2a7eac7Sbenno LOG3("%s: read blocks: %zu blocks, %jd B total blocked data", 44102f20df6Sderaadt path, s->blksz, (intmax_t)s->size); 44260a32ee9Sbenno return s; 44360a32ee9Sbenno out: 4440889042fSflorian free(s->blks); 4450889042fSflorian free(s); 44660a32ee9Sbenno return NULL; 44760a32ee9Sbenno } 44860a32ee9Sbenno 44960a32ee9Sbenno /* 45060a32ee9Sbenno * Symmetrise blk_recv_ack(), except w/o the leading identifier. 45160a32ee9Sbenno * Return zero on failure, non-zero on success. 45260a32ee9Sbenno */ 45360a32ee9Sbenno int 45460a32ee9Sbenno blk_send_ack(struct sess *sess, int fd, struct blkset *p) 45560a32ee9Sbenno { 45660a32ee9Sbenno char buf[16]; 45760a32ee9Sbenno size_t pos = 0, sz; 45860a32ee9Sbenno 45960a32ee9Sbenno /* Put the entire send routine into a buffer. */ 46060a32ee9Sbenno 46160a32ee9Sbenno sz = sizeof(int32_t) + /* block count */ 46260a32ee9Sbenno sizeof(int32_t) + /* block length */ 46360a32ee9Sbenno sizeof(int32_t) + /* checksum length */ 46460a32ee9Sbenno sizeof(int32_t); /* block remainder */ 46560a32ee9Sbenno assert(sz <= sizeof(buf)); 46660a32ee9Sbenno 46760a32ee9Sbenno if (!io_read_buf(sess, fd, buf, sz)) { 468b2a7eac7Sbenno ERRX1("io_read_buf"); 46960a32ee9Sbenno return 0; 47060a32ee9Sbenno } 47160a32ee9Sbenno 472ba617adaSbenno if (!io_unbuffer_size(buf, &pos, sz, &p->blksz)) 473b2a7eac7Sbenno ERRX1("io_unbuffer_size"); 474ba617adaSbenno else if (!io_unbuffer_size(buf, &pos, sz, &p->len)) 475b2a7eac7Sbenno ERRX1("io_unbuffer_size"); 476ba617adaSbenno else if (!io_unbuffer_size(buf, &pos, sz, &p->csum)) 477b2a7eac7Sbenno ERRX1("io_unbuffer_size"); 478ba617adaSbenno else if (!io_unbuffer_size(buf, &pos, sz, &p->rem)) 479b2a7eac7Sbenno ERRX1("io_unbuffer_size"); 48060a32ee9Sbenno else if (p->len && p->rem >= p->len) 481b2a7eac7Sbenno ERRX1("non-zero length is less than remainder"); 482f1dcb30aSderaadt else if (p->csum == 0 || p->csum > 16) 483b2a7eac7Sbenno ERRX1("inappropriate checksum length"); 48460a32ee9Sbenno else 48560a32ee9Sbenno return 1; 48660a32ee9Sbenno 48760a32ee9Sbenno return 0; 48860a32ee9Sbenno } 48960a32ee9Sbenno 49060a32ee9Sbenno /* 49160a32ee9Sbenno * Transmit the metadata for set and blocks. 49260a32ee9Sbenno * Return zero on failure, non-zero on success. 49360a32ee9Sbenno */ 49460a32ee9Sbenno int 49560a32ee9Sbenno blk_send(struct sess *sess, int fd, size_t idx, 49660a32ee9Sbenno const struct blkset *p, const char *path) 49760a32ee9Sbenno { 49860a32ee9Sbenno char *buf; 49960a32ee9Sbenno size_t i, pos = 0, sz; 50060a32ee9Sbenno int rc = 0; 50160a32ee9Sbenno 50260a32ee9Sbenno /* Put the entire send routine into a buffer. */ 50360a32ee9Sbenno 50460a32ee9Sbenno sz = sizeof(int32_t) + /* identifier */ 50560a32ee9Sbenno sizeof(int32_t) + /* block count */ 50660a32ee9Sbenno sizeof(int32_t) + /* block length */ 50760a32ee9Sbenno sizeof(int32_t) + /* checksum length */ 50860a32ee9Sbenno sizeof(int32_t) + /* block remainder */ 50960a32ee9Sbenno p->blksz * 51060a32ee9Sbenno (sizeof(int32_t) + /* short checksum */ 51160a32ee9Sbenno p->csum); /* long checksum */ 51260a32ee9Sbenno 513f1dcb30aSderaadt if ((buf = malloc(sz)) == NULL) { 514b2a7eac7Sbenno ERR("malloc"); 51560a32ee9Sbenno return 0; 51660a32ee9Sbenno } 51760a32ee9Sbenno 518ba617adaSbenno io_buffer_int(buf, &pos, sz, idx); 519ba617adaSbenno io_buffer_int(buf, &pos, sz, p->blksz); 520ba617adaSbenno io_buffer_int(buf, &pos, sz, p->len); 521ba617adaSbenno io_buffer_int(buf, &pos, sz, p->csum); 522ba617adaSbenno io_buffer_int(buf, &pos, sz, p->rem); 52360a32ee9Sbenno 52460a32ee9Sbenno for (i = 0; i < p->blksz; i++) { 525dc4a3938Sderaadt io_buffer_int(buf, &pos, sz, p->blks[i].chksum_short); 526ba617adaSbenno io_buffer_buf(buf, &pos, sz, p->blks[i].chksum_long, p->csum); 52760a32ee9Sbenno } 52860a32ee9Sbenno 52960a32ee9Sbenno assert(pos == sz); 53060a32ee9Sbenno 53160a32ee9Sbenno if (!io_write_buf(sess, fd, buf, sz)) { 532b2a7eac7Sbenno ERRX1("io_write_buf"); 53360a32ee9Sbenno goto out; 53460a32ee9Sbenno } 53560a32ee9Sbenno 536b2a7eac7Sbenno LOG3("%s: sent block prologue: %zu blocks of %zu B, " 53702f20df6Sderaadt "%zu B remainder, %zu B checksum", 53802f20df6Sderaadt path, p->blksz, p->len, p->rem, p->csum); 53960a32ee9Sbenno rc = 1; 54060a32ee9Sbenno out: 54160a32ee9Sbenno free(buf); 54260a32ee9Sbenno return rc; 54360a32ee9Sbenno } 544