File: | src/usr.bin/rsync/blocks.c |
Warning: | line 110, column 36 Division by zero |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
33 | struct blkhash { | |||
34 | const struct blk *blk; | |||
35 | TAILQ_ENTRY(blkhash)struct { struct blkhash *tqe_next; struct blkhash **tqe_prev; } entries; | |||
36 | }; | |||
37 | ||||
38 | TAILQ_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 | */ | |||
45 | struct 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 | */ | |||
64 | struct blktab * | |||
65 | blkhash_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 | */ | |||
88 | int | |||
89 | blkhash_set(struct blktab *p, const struct blkset *bset) | |||
90 | { | |||
91 | size_t i, idx; | |||
92 | ||||
93 | if (bset == NULL((void*)0)) | |||
| ||||
94 | return 1; | |||
95 | ||||
96 | /* Wipe clean the table. */ | |||
97 | ||||
98 | for (i = 0; i < p->qsz; i++) | |||
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)) { | |||
105 | ERR("reallocarray")rsync_err( ("reallocarray")); | |||
106 | return 0; | |||
107 | } | |||
108 | for (i = 0; i < bset->blksz; i++) { | |||
109 | p->blks[i].blk = &bset->blks[i]; | |||
110 | idx = bset->blks[i].chksum_short % p->qsz; | |||
| ||||
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 | */ | |||
121 | void | |||
122 | blkhash_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 | */ | |||
135 | static const struct blk * | |||
136 | blk_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 | */ | |||
242 | void | |||
243 | blk_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 | */ | |||
328 | void | |||
329 | blk_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 | */ | |||
352 | struct blkset * | |||
353 | blk_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; | |||
441 | out: | |||
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 | */ | |||
451 | int | |||
452 | blk_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 | */ | |||
492 | int | |||
493 | blk_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; | |||
538 | out: | |||
539 | free(buf); | |||
540 | return rc; | |||
541 | } |