Bug Summary

File:src/usr.bin/rsync/blocks.c
Warning:line 110, column 36
Division by zero

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name blocks.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/rsync/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/rsync/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/rsync/blocks.c
1/* $OpenBSD: blocks.c,v 1.21 2021/11/03 14:42:12 deraadt Exp $ */
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/queue.h>
18#include <sys/stat.h>
19
20#include <assert.h>
21#include <endian.h>
22#include <errno(*__errno()).h>
23#include <inttypes.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28
29#include <openssl/md4.h>
30
31#include "extern.h"
32
33struct blkhash {
34 const struct blk *blk;
35 TAILQ_ENTRY(blkhash)struct { struct blkhash *tqe_next; struct blkhash **tqe_prev;
}
entries;
36};
37
38TAILQ_HEAD(blkhashq, blkhash)struct blkhashq { struct blkhash *tqh_first; struct blkhash *
*tqh_last; }
;
39
40/*
41 * Table used by the sender for looking up blocks.
42 * The blocks are those sent by the receiver; we're looking up using
43 * hashes computed from our local file.
44 */
45struct blktab {
46 struct blkhashq *q; /* entries in the hashtable */
47 size_t qsz; /* size of the hashtable */
48 struct blkhash *blks; /* pre-allocated hashtable entries */
49};
50
51/*
52 * This is the number of buckets in the hashtable.
53 * Use the same that GPL rsync uses.
54 * (It should be dynamic?)
55 */
56#define BLKTAB_SZ65536 65536
57
58/*
59 * Initialise an empty hashtable with BLKTAB_SZ entries in it.
60 * Populate it for each file with blkhash_set.
61 * When we've processed all files, call blkhash_free.
62 * Returns NULL on allocation failure.
63 */
64struct blktab *
65blkhash_alloc(void)
66{
67 struct blktab *p;
68
69 if ((p = calloc(1, sizeof(struct blktab))) == NULL((void*)0)) {
70 ERR("calloc")rsync_err( ("calloc"));
71 return NULL((void*)0);
72 }
73 p->qsz = BLKTAB_SZ65536;
74 p->q = calloc(p->qsz, sizeof(struct blkhashq));
75 if (p->q == NULL((void*)0)) {
76 ERR("calloc")rsync_err( ("calloc"));
77 free(p);
78 return NULL((void*)0);
79 }
80 return p;
81}
82
83/*
84 * Populate the hashtable with an incoming file's blocks.
85 * This will clear out any existing hashed data.
86 * Returns zero on allocation failure, non-zero otherwise.
87 */
88int
89blkhash_set(struct blktab *p, const struct blkset *bset)
90{
91 size_t i, idx;
92
93 if (bset == NULL((void*)0))
1
Assuming 'bset' is not equal to NULL
2
Taking false branch
94 return 1;
95
96 /* Wipe clean the table. */
97
98 for (i = 0; i < p->qsz; i++)
3
Assuming 'i' is >= field 'qsz'
4
Loop condition is false. Execution continues on line 103
99 TAILQ_INIT(&p->q[i])do { (&p->q[i])->tqh_first = ((void*)0); (&p->
q[i])->tqh_last = &(&p->q[i])->tqh_first; } while
(0)
;
100
101 /* Fill in the hashtable. */
102
103 p->blks = reallocarray(p->blks, bset->blksz, sizeof(struct blkhash));
104 if (p->blks == NULL((void*)0)) {
5
Assuming field 'blks' is not equal to NULL
6
Taking false branch
105 ERR("reallocarray")rsync_err( ("reallocarray"));
106 return 0;
107 }
108 for (i = 0; i < bset->blksz; i++) {
7
Assuming 'i' is < field 'blksz'
8
Loop condition is true. Entering loop body
109 p->blks[i].blk = &bset->blks[i];
110 idx = bset->blks[i].chksum_short % p->qsz;
9
Division by zero
111 assert(idx < p->qsz)((idx < p->qsz) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/blocks.c"
, 111, __func__, "idx < p->qsz"))
;
112 TAILQ_INSERT_TAIL(&p->q[idx], &p->blks[i], entries)do { (&p->blks[i])->entries.tqe_next = ((void*)0); (
&p->blks[i])->entries.tqe_prev = (&p->q[idx]
)->tqh_last; *(&p->q[idx])->tqh_last = (&p->
blks[i]); (&p->q[idx])->tqh_last = &(&p->
blks[i])->entries.tqe_next; } while (0)
;
113 }
114
115 return 1;
116}
117
118/*
119 * Free as allocated with blkhash_alloc().
120 */
121void
122blkhash_free(struct blktab *p)
123{
124
125 free(p->blks);
126 free(p);
127}
128
129/*
130 * From our current position of "offs" in buffer "buf" of total size
131 * "size", see if we can find a matching block in our list of blocks.
132 * The "hint" refers to the block that *might* work.
133 * Returns the blk or NULL if no matching block was found.
134 */
135static const struct blk *
136blk_find(struct sess *sess, struct blkstat *st,
137 const struct blkset *blks, const char *path, int recomp)
138{
139 unsigned char md[MD4_DIGEST_LENGTH16];
140 uint32_t fhash;
141 off_t remain, osz;
142 int have_md = 0;
143 char *map;
144 const struct blkhashq *q;
145 const struct blkhash *ent;
146
147 remain = st->mapsz - st->offs;
148 assert(remain)((remain) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/blocks.c"
, 148, __func__, "remain"))
;
149 osz = MINIMUM(remain, (off_t)blks->len)(((remain) < ((off_t)blks->len)) ? (remain) : ((off_t)blks
->len))
;
150
151 /*
152 * First, compute our fast hash the hard way (if we're
153 * reentering this function from a previous block match, or the
154 * first time) or from our existing s1 and s2 values.
155 */
156
157 if (!recomp) {
158 fhash = (st->s1 & 0xFFFF) | (st->s2 << 16);
159 } else {
160 fhash = hash_fast(st->map + st->offs, (size_t)osz);
161 st->s1 = fhash & 0xFFFF;
162 st->s2 = fhash >> 16;
163 }
164
165 /*
166 * Start with our match hint.
167 * This just runs the fast and slow check with the hint.
168 */
169
170 if (st->hint < blks->blksz &&
171 fhash == blks->blks[st->hint].chksum_short &&
172 (size_t)osz == blks->blks[st->hint].len) {
173 hash_slow(st->map + st->offs, (size_t)osz, md, sess);
174 have_md = 1;
175 if (memcmp(md, blks->blks[st->hint].chksum_long, blks->csum) == 0) {
176 LOG4("%s: found matching hinted match: "rsync_log( 3, ("%s: found matching hinted match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, blks->blks[st->hint].idx
, (intmax_t)blks->blks[st->hint].offs, blks->blks[st
->hint].len)
177 "position %jd, block %zu (position %jd, size %zu)",rsync_log( 3, ("%s: found matching hinted match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, blks->blks[st->hint].idx
, (intmax_t)blks->blks[st->hint].offs, blks->blks[st
->hint].len)
178 path,rsync_log( 3, ("%s: found matching hinted match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, blks->blks[st->hint].idx
, (intmax_t)blks->blks[st->hint].offs, blks->blks[st
->hint].len)
179 (intmax_t)st->offs, blks->blks[st->hint].idx,rsync_log( 3, ("%s: found matching hinted match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, blks->blks[st->hint].idx
, (intmax_t)blks->blks[st->hint].offs, blks->blks[st
->hint].len)
180 (intmax_t)blks->blks[st->hint].offs,rsync_log( 3, ("%s: found matching hinted match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, blks->blks[st->hint].idx
, (intmax_t)blks->blks[st->hint].offs, blks->blks[st
->hint].len)
181 blks->blks[st->hint].len)rsync_log( 3, ("%s: found matching hinted match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, blks->blks[st->hint].idx
, (intmax_t)blks->blks[st->hint].offs, blks->blks[st
->hint].len)
;
182 return &blks->blks[st->hint];
183 }
184 }
185
186 /*
187 * Look for the fast hash modulus in our hashtable, filter for
188 * those matching the full hash and length, then move to the
189 * slow hash.
190 * The slow hash is computed only once.
191 */
192
193 q = &st->blktab->q[fhash % st->blktab->qsz];
194
195 TAILQ_FOREACH(ent, q, entries)for((ent) = ((q)->tqh_first); (ent) != ((void*)0); (ent) =
((ent)->entries.tqe_next))
{
196 if (fhash != ent->blk->chksum_short ||
197 (size_t)osz != ent->blk->len)
198 continue;
199
200 LOG4("%s: found matching fast match: "rsync_log( 3, ("%s: found matching fast match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, ent->blk->idx, (intmax_t
)ent->blk->offs, ent->blk->len)
201 "position %jd, block %zu (position %jd, size %zu)",rsync_log( 3, ("%s: found matching fast match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, ent->blk->idx, (intmax_t
)ent->blk->offs, ent->blk->len)
202 path, (intmax_t)st->offs, ent->blk->idx,rsync_log( 3, ("%s: found matching fast match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, ent->blk->idx, (intmax_t
)ent->blk->offs, ent->blk->len)
203 (intmax_t)ent->blk->offs, ent->blk->len)rsync_log( 3, ("%s: found matching fast match: " "position %jd, block %zu (position %jd, size %zu)"
), path, (intmax_t)st->offs, ent->blk->idx, (intmax_t
)ent->blk->offs, ent->blk->len)
;
204
205 if (have_md == 0) {
206 hash_slow(st->map + st->offs, (size_t)osz, md, sess);
207 have_md = 1;
208 }
209
210 if (memcmp(md, ent->blk->chksum_long, blks->csum))
211 continue;
212
213 LOG4("%s: sender verifies slow match", path)rsync_log( 3, ("%s: sender verifies slow match"), path);
214 return ent->blk;
215 }
216
217 /*
218 * Adjust our partial sums for the hashing.
219 * We first remove the first byte from the sum.
220 * We then, if we have space, add the first byte of the next
221 * block in the sequence.
222 */
223
224 map = st->map + st->offs;
225 st->s1 -= map[0];
226 st->s2 -= osz * map[0];
227
228 if (osz < remain) {
229 st->s1 += map[osz];
230 st->s2 += st->s1;
231 }
232
233 return NULL((void*)0);
234}
235
236/*
237 * Given a local file "path" and the blocks created by a remote machine,
238 * find out which blocks of our file they don't have and send them.
239 * This function is reentrant: it must be called while there's still
240 * data to send.
241 */
242void
243blk_match(struct sess *sess, const struct blkset *blks,
244 const char *path, struct blkstat *st)
245{
246 off_t last, end, sz;
247 int32_t tok;
248 size_t i;
249 const struct blk *blk;
250
251 /*
252 * If the file's empty or we don't have any blocks from the
253 * sender, then simply send the whole file.
254 * Otherwise, run the hash matching routine and send raw chunks
255 * and subsequent matching tokens.
256 */
257
258 if (st->mapsz && blks->blksz) {
259 /*
260 * Stop searching at the length of the file minus the
261 * size of the last block.
262 * The reason for this being that we don't need to do an
263 * incremental hash within the last block---if it
264 * doesn't match, it doesn't match.
265 */
266
267 end = st->mapsz + 1 - blks->blks[blks->blksz - 1].len;
268 last = st->offs;
269
270 for (i = 0; st->offs < end; st->offs++, i++) {
271 blk = blk_find(sess, st, blks, path, i == 0);
272 if (blk == NULL((void*)0))
273 continue;
274
275 sz = st->offs - last;
276 st->dirty += sz;
277 st->total += sz;
278 LOG4("%s: flushing %jd B before %zu B block %zu",rsync_log( 3, ("%s: flushing %jd B before %zu B block %zu"), path
, (intmax_t)sz, blk->len, blk->idx)
279 path, (intmax_t)sz,rsync_log( 3, ("%s: flushing %jd B before %zu B block %zu"), path
, (intmax_t)sz, blk->len, blk->idx)
280 blk->len, blk->idx)rsync_log( 3, ("%s: flushing %jd B before %zu B block %zu"), path
, (intmax_t)sz, blk->len, blk->idx)
;
281 tok = -(blk->idx + 1);
282
283 /*
284 * Write the data we have, then follow it with
285 * the tag of the block that matches.
286 */
287
288 st->curpos = last;
289 st->curlen = st->curpos + sz;
290 st->curtok = tok;
291 assert(st->curtok != 0)((st->curtok != 0) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/blocks.c"
, 291, __func__, "st->curtok != 0"))
;
292 st->curst = sz ? BLKSTAT_DATA : BLKSTAT_TOK;
293 st->total += blk->len;
294 st->offs += blk->len;
295 st->hint = blk->idx + 1;
296 return;
297 }
298
299 /* Emit remaining data and send terminator token. */
300
301 sz = st->mapsz - last;
302 LOG4("%s: flushing remaining %jd B",rsync_log( 3, ("%s: flushing remaining %jd B"), path, (intmax_t
)sz)
303 path, (intmax_t)sz)rsync_log( 3, ("%s: flushing remaining %jd B"), path, (intmax_t
)sz)
;
304
305 st->total += sz;
306 st->dirty += sz;
307 st->curpos = last;
308 st->curlen = st->curpos + sz;
309 st->curtok = 0;
310 st->curst = sz ? BLKSTAT_DATA : BLKSTAT_TOK;
311 } else {
312 st->curpos = 0;
313 st->curlen = st->mapsz;
314 st->curtok = 0;
315 st->curst = st->mapsz ? BLKSTAT_DATA : BLKSTAT_TOK;
316 st->dirty = st->total = st->mapsz;
317
318 LOG4("%s: flushing whole file %zu B",rsync_log( 3, ("%s: flushing whole file %zu B"), path, st->
mapsz)
319 path, st->mapsz)rsync_log( 3, ("%s: flushing whole file %zu B"), path, st->
mapsz)
;
320 }
321}
322
323/*
324 * Buffer the message from sender to the receiver indicating that the
325 * block set has been received.
326 * Symmetrises blk_send_ack().
327 */
328void
329blk_recv_ack(char buf[20], const struct blkset *blocks, int32_t idx)
330{
331 size_t pos = 0, sz;
332
333 sz = sizeof(int32_t) + /* index */
334 sizeof(int32_t) + /* block count */
335 sizeof(int32_t) + /* block length */
336 sizeof(int32_t) + /* checksum length */
337 sizeof(int32_t); /* block remainder */
338 assert(sz == 20)((sz == 20) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/blocks.c"
, 338, __func__, "sz == 20"))
;
339
340 io_buffer_int(buf, &pos, sz, idx);
341 io_buffer_int(buf, &pos, sz, blocks->blksz);
342 io_buffer_int(buf, &pos, sz, blocks->len);
343 io_buffer_int(buf, &pos, sz, blocks->csum);
344 io_buffer_int(buf, &pos, sz, blocks->rem);
345 assert(pos == sz)((pos == sz) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/blocks.c"
, 345, __func__, "pos == sz"))
;
346}
347
348/*
349 * Read all of the checksums for a file's blocks.
350 * Returns the set of blocks or NULL on failure.
351 */
352struct blkset *
353blk_recv(struct sess *sess, int fd, const char *path)
354{
355 struct blkset *s;
356 int32_t i;
357 size_t j;
358 struct blk *b;
359 off_t offs = 0;
360
361 if ((s = calloc(1, sizeof(struct blkset))) == NULL((void*)0)) {
362 ERR("calloc")rsync_err( ("calloc"));
363 return NULL((void*)0);
364 }
365
366 /*
367 * The block prologue consists of a few values that we'll need
368 * in reading the individual blocks for this file.
369 * FIXME: read into buffer and unbuffer.
370 */
371
372 if (!io_read_size(sess, fd, &s->blksz)) {
373 ERRX1("io_read_size")rsync_errx1( ("io_read_size"));
374 goto out;
375 } else if (!io_read_size(sess, fd, &s->len)) {
376 ERRX1("io_read_size")rsync_errx1( ("io_read_size"));
377 goto out;
378 } else if (!io_read_size(sess, fd, &s->csum)) {
379 ERRX1("io_read_int")rsync_errx1( ("io_read_int"));
380 goto out;
381 } else if (!io_read_size(sess, fd, &s->rem)) {
382 ERRX1("io_read_int")rsync_errx1( ("io_read_int"));
383 goto out;
384 } else if (s->rem && s->rem >= s->len) {
385 ERRX("block remainder is "rsync_errx( ("block remainder is " "greater than block size")
)
386 "greater than block size")rsync_errx( ("block remainder is " "greater than block size")
)
;
387 goto out;
388 }
389
390 LOG3("%s: read block prologue: %zu blocks of "rsync_log( 2, ("%s: read block prologue: %zu blocks of " "%zu B, %zu B remainder, %zu B checksum"
), path, s->blksz, s->len, s->rem, s->csum)
391 "%zu B, %zu B remainder, %zu B checksum", path,rsync_log( 2, ("%s: read block prologue: %zu blocks of " "%zu B, %zu B remainder, %zu B checksum"
), path, s->blksz, s->len, s->rem, s->csum)
392 s->blksz, s->len, s->rem, s->csum)rsync_log( 2, ("%s: read block prologue: %zu blocks of " "%zu B, %zu B remainder, %zu B checksum"
), path, s->blksz, s->len, s->rem, s->csum)
;
393
394 if (s->blksz) {
395 s->blks = calloc(s->blksz, sizeof(struct blk));
396 if (s->blks == NULL((void*)0)) {
397 ERR("calloc")rsync_err( ("calloc"));
398 goto out;
399 }
400 }
401
402 /*
403 * Read each block individually.
404 * FIXME: read buffer and unbuffer.
405 */
406
407 for (j = 0; j < s->blksz; j++) {
408 b = &s->blks[j];
409 if (!io_read_int(sess, fd, &i)) {
410 ERRX1("io_read_int")rsync_errx1( ("io_read_int"));
411 goto out;
412 }
413 b->chksum_short = i;
414
415 assert(s->csum <= sizeof(b->chksum_long))((s->csum <= sizeof(b->chksum_long)) ? (void)0 : __assert2
("/usr/src/usr.bin/rsync/blocks.c", 415, __func__, "s->csum <= sizeof(b->chksum_long)"
))
;
416 if (!io_read_buf(sess,
417 fd, b->chksum_long, s->csum)) {
418 ERRX1("io_read_buf")rsync_errx1( ("io_read_buf"));
419 goto out;
420 }
421
422 /*
423 * If we're the last block, then we're assigned the
424 * remainder of the data.
425 */
426
427 b->offs = offs;
428 b->idx = j;
429 b->len = (j == (s->blksz - 1) && s->rem) ?
430 s->rem : s->len;
431 offs += b->len;
432
433 LOG4("%s: read block %zu, length %zu B",rsync_log( 3, ("%s: read block %zu, length %zu B"), path, b->
idx, b->len)
434 path, b->idx, b->len)rsync_log( 3, ("%s: read block %zu, length %zu B"), path, b->
idx, b->len)
;
435 }
436
437 s->size = offs;
438 LOG3("%s: read blocks: %zu blocks, %jd B total blocked data",rsync_log( 2, ("%s: read blocks: %zu blocks, %jd B total blocked data"
), path, s->blksz, (intmax_t)s->size)
439 path, s->blksz, (intmax_t)s->size)rsync_log( 2, ("%s: read blocks: %zu blocks, %jd B total blocked data"
), path, s->blksz, (intmax_t)s->size)
;
440 return s;
441out:
442 free(s->blks);
443 free(s);
444 return NULL((void*)0);
445}
446
447/*
448 * Symmetrise blk_recv_ack(), except w/o the leading identifier.
449 * Return zero on failure, non-zero on success.
450 */
451int
452blk_send_ack(struct sess *sess, int fd, struct blkset *p)
453{
454 char buf[16];
455 size_t pos = 0, sz;
456
457 /* Put the entire send routine into a buffer. */
458
459 sz = sizeof(int32_t) + /* block count */
460 sizeof(int32_t) + /* block length */
461 sizeof(int32_t) + /* checksum length */
462 sizeof(int32_t); /* block remainder */
463 assert(sz <= sizeof(buf))((sz <= sizeof(buf)) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/blocks.c"
, 463, __func__, "sz <= sizeof(buf)"))
;
464
465 if (!io_read_buf(sess, fd, buf, sz)) {
466 ERRX1("io_read_buf")rsync_errx1( ("io_read_buf"));
467 return 0;
468 }
469
470 if (!io_unbuffer_size(buf, &pos, sz, &p->blksz))
471 ERRX1("io_unbuffer_size")rsync_errx1( ("io_unbuffer_size"));
472 else if (!io_unbuffer_size(buf, &pos, sz, &p->len))
473 ERRX1("io_unbuffer_size")rsync_errx1( ("io_unbuffer_size"));
474 else if (!io_unbuffer_size(buf, &pos, sz, &p->csum))
475 ERRX1("io_unbuffer_size")rsync_errx1( ("io_unbuffer_size"));
476 else if (!io_unbuffer_size(buf, &pos, sz, &p->rem))
477 ERRX1("io_unbuffer_size")rsync_errx1( ("io_unbuffer_size"));
478 else if (p->len && p->rem >= p->len)
479 ERRX1("non-zero length is less than remainder")rsync_errx1( ("non-zero length is less than remainder"));
480 else if (p->csum == 0 || p->csum > 16)
481 ERRX1("inappropriate checksum length")rsync_errx1( ("inappropriate checksum length"));
482 else
483 return 1;
484
485 return 0;
486}
487
488/*
489 * Transmit the metadata for set and blocks.
490 * Return zero on failure, non-zero on success.
491 */
492int
493blk_send(struct sess *sess, int fd, size_t idx,
494 const struct blkset *p, const char *path)
495{
496 char *buf;
497 size_t i, pos = 0, sz;
498 int rc = 0;
499
500 /* Put the entire send routine into a buffer. */
501
502 sz = sizeof(int32_t) + /* identifier */
503 sizeof(int32_t) + /* block count */
504 sizeof(int32_t) + /* block length */
505 sizeof(int32_t) + /* checksum length */
506 sizeof(int32_t) + /* block remainder */
507 p->blksz *
508 (sizeof(int32_t) + /* short checksum */
509 p->csum); /* long checksum */
510
511 if ((buf = malloc(sz)) == NULL((void*)0)) {
512 ERR("malloc")rsync_err( ("malloc"));
513 return 0;
514 }
515
516 io_buffer_int(buf, &pos, sz, idx);
517 io_buffer_int(buf, &pos, sz, p->blksz);
518 io_buffer_int(buf, &pos, sz, p->len);
519 io_buffer_int(buf, &pos, sz, p->csum);
520 io_buffer_int(buf, &pos, sz, p->rem);
521
522 for (i = 0; i < p->blksz; i++) {
523 io_buffer_int(buf, &pos, sz, p->blks[i].chksum_short);
524 io_buffer_buf(buf, &pos, sz, p->blks[i].chksum_long, p->csum);
525 }
526
527 assert(pos == sz)((pos == sz) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/blocks.c"
, 527, __func__, "pos == sz"))
;
528
529 if (!io_write_buf(sess, fd, buf, sz)) {
530 ERRX1("io_write_buf")rsync_errx1( ("io_write_buf"));
531 goto out;
532 }
533
534 LOG3("%s: sent block prologue: %zu blocks of %zu B, "rsync_log( 2, ("%s: sent block prologue: %zu blocks of %zu B, "
"%zu B remainder, %zu B checksum"), path, p->blksz, p->
len, p->rem, p->csum)
535 "%zu B remainder, %zu B checksum",rsync_log( 2, ("%s: sent block prologue: %zu blocks of %zu B, "
"%zu B remainder, %zu B checksum"), path, p->blksz, p->
len, p->rem, p->csum)
536 path, p->blksz, p->len, p->rem, p->csum)rsync_log( 2, ("%s: sent block prologue: %zu blocks of %zu B, "
"%zu B remainder, %zu B checksum"), path, p->blksz, p->
len, p->rem, p->csum)
;
537 rc = 1;
538out:
539 free(buf);
540 return rc;
541}