Line data Source code
1 : #include "fd_checkpt.h"
2 :
3 : #if FD_HAS_LZ4
4 : #include <lz4.h>
5 :
6 : /* fd_restore_private_lz4 decompresses the cbuf_max memory region
7 : pointed to by cbuf into the ubuf_usz memory region pointed to by ubuf
8 : using the given lz4 decompressor. Assumes lz4, ubuf and cbuf are
9 : valid and assumes ubuf_usz matches the corresponding
10 : fd_checkpt_private_lz4 call and cbuf is valid. On success, returns
11 : the number of leading bytes cbuf bytes that were used for the
12 : decompression (will be in [4,cbuf_max]) and the ubuf should not
13 : be modified until the stream is reset, closed or an additional 64 KiB
14 : has been decompressed. On failure, returns 0 and retains no interest
15 : in ubuf. In either case, this retains no interest in cbuf on return.
16 :
17 : _sbuf, sbuf_sz, sbuf_thresh, _sbuf_cursor specify the small buf
18 : scatter ring state. See fd_checkpt_private_lz4 for more details. */
19 :
20 : static ulong
21 : fd_restore_private_lz4( LZ4_streamDecode_t * lz4,
22 : void * _ubuf,
23 : ulong ubuf_usz,
24 : void const * _cbuf,
25 : ulong cbuf_max,
26 : void * _sbuf,
27 : ulong sbuf_sz,
28 : ulong sbuf_thresh,
29 1727067 : ulong * _sbuf_cursor ) {
30 1727067 : char * ubuf = (char *) _ubuf;
31 1727067 : char const * cbuf = (char const *)_cbuf;
32 :
33 : /* Verify ubuf_usz is in [1,LZ4_MAX_INPUT_SIZE] and cbuf_max is large
34 : enough to store a header and a non-trivial compressed body. */
35 :
36 1727067 : if( FD_UNLIKELY( !((1UL<=ubuf_usz) & (ubuf_usz<=(ulong)LZ4_MAX_INPUT_SIZE)) ) ) {
37 0 : FD_LOG_WARNING(( "bad ubuf_usz" ));
38 0 : return 0UL;
39 0 : }
40 :
41 1727067 : if( FD_UNLIKELY( cbuf_max<4UL ) ) { /* 3 bytes for header, 1 byte minimum for body */
42 0 : FD_LOG_WARNING(( "truncated header" ));
43 0 : return 0UL;
44 0 : }
45 :
46 : /* Restore and validate header */
47 :
48 1727067 : ulong ubuf_csz = (((ulong)(uchar)cbuf[0]) )
49 1727067 : | (((ulong)(uchar)cbuf[1]) << 8)
50 1727067 : | (((ulong)(uchar)cbuf[2]) << 16); /* In [1,2^24) */
51 :
52 1727067 : ulong cbuf_sz = ubuf_csz + 3UL;
53 1727067 : if( FD_UNLIKELY( !((4UL<=cbuf_sz) | (cbuf_sz<=FD_CHECKPT_PRIVATE_CSZ_MAX( ubuf_usz ))) ) ) {
54 0 : FD_LOG_WARNING(( "corrupt header" ));
55 0 : return 0UL;
56 0 : }
57 :
58 1727067 : if( FD_UNLIKELY( cbuf_sz>cbuf_max ) ) {
59 0 : FD_LOG_WARNING(( "truncated checkpt" ));
60 0 : return 0UL;
61 0 : }
62 :
63 : /* Small ubuf scatter optimization. See note in
64 : fd_checkpt_private_lz4 for details. */
65 :
66 1727067 : int is_small = ubuf_usz<=sbuf_thresh;
67 1727067 : if( is_small ) { /* app dependent branch prob */
68 1727067 : ulong sbuf_cursor = *_sbuf_cursor;
69 1727067 : if( (sbuf_sz-sbuf_cursor)<ubuf_usz ) sbuf_cursor = 0UL; /* cmov */
70 1727067 : ubuf = (char *)_sbuf + sbuf_cursor;
71 1727067 : *_sbuf_cursor = sbuf_cursor + ubuf_usz;
72 1727067 : }
73 :
74 : /* Restore the buffer */
75 :
76 1727067 : int res = LZ4_decompress_safe_continue( lz4, cbuf+3UL, ubuf, (int)ubuf_csz, (int)ubuf_usz );
77 1727067 : if( FD_UNLIKELY( res<=0 ) ) {
78 0 : FD_LOG_WARNING(( "LZ4_decompress_safe_continue error (%i)", res ));
79 0 : return 0UL;
80 0 : }
81 :
82 : /* Small ubuf scatter optimization */
83 :
84 1727067 : if( is_small ) memcpy( _ubuf, ubuf, ubuf_usz ); /* app dependent branch prob */
85 :
86 1727067 : return cbuf_sz;
87 1727067 : }
88 : #endif
89 :
90 : fd_restore_t *
91 : fd_restore_init_stream( void * mem,
92 : int fd,
93 : void * rbuf,
94 21156 : ulong rbuf_sz ) {
95 :
96 : /* Check input args */
97 :
98 21156 : if( FD_UNLIKELY( !mem ) ) {
99 3 : FD_LOG_WARNING(( "NULL mem" ));
100 3 : return NULL;
101 3 : }
102 :
103 21153 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_RESTORE_ALIGN ) ) ) {
104 3 : FD_LOG_WARNING(( "misaligned mem" ));
105 3 : return NULL;
106 3 : }
107 :
108 21150 : if( FD_UNLIKELY( fd<0 ) ) {
109 3 : FD_LOG_WARNING(( "bad fd" ));
110 3 : return NULL;
111 3 : }
112 :
113 21147 : if( FD_UNLIKELY( !rbuf ) ) {
114 3 : FD_LOG_WARNING(( "NULL rbuf" ));
115 3 : return NULL;
116 3 : }
117 :
118 21144 : if( FD_UNLIKELY( rbuf_sz<FD_RESTORE_RBUF_MIN ) ) {
119 3 : FD_LOG_WARNING(( "rbuf_sz too small" ));
120 3 : return NULL;
121 3 : }
122 :
123 : /* Get the position and size of the checkpt. If we can't (e.g. we are
124 : restoring from a non-seekable stream / pipe), treat the start of
125 : the checkpt as the fd's current position and the size as
126 : (practically) infinite. */
127 :
128 21141 : ulong sz;
129 21141 : ulong off;
130 :
131 21141 : int err = fd_io_sz( fd, &sz );
132 21141 : if( FD_LIKELY( !err ) ) err = fd_io_seek( fd, 0L, FD_IO_SEEK_TYPE_CUR, &off );
133 21141 : if( FD_UNLIKELY( err ) ) { /* fd does not appear seekable */
134 0 : off = 0L;
135 0 : sz = ULONG_MAX;
136 21141 : } else if( FD_UNLIKELY( !((off<=sz) & (sz<=(ulong)LONG_MAX)) ) ) { /* fd claimed to be seekable but parameters are weird */
137 0 : FD_LOG_WARNING(( "sz too large or unexpected file position" ));
138 0 : return NULL;
139 0 : }
140 :
141 : /* Create decompressor */
142 :
143 21141 : # if FD_HAS_LZ4
144 21141 : LZ4_streamDecode_t * lz4 = LZ4_createStreamDecode();
145 21141 : if( FD_UNLIKELY( !lz4 ) ) {
146 0 : FD_LOG_WARNING(( "lz4 error" ));
147 0 : return NULL;
148 0 : }
149 : # else
150 : void * lz4 = NULL;
151 : # endif
152 :
153 : /* Init restore */
154 :
155 21141 : fd_restore_t * restore = (fd_restore_t *)mem;
156 :
157 21141 : restore->fd = fd; /* streaming mode */
158 21141 : restore->frame_style = 0; /* not in frame */
159 21141 : restore->lz4 = (void *)lz4;
160 21141 : restore->sbuf_cursor = 0UL;
161 21141 : restore->sz = sz;
162 21141 : restore->off = off;
163 21141 : restore->rbuf.mem = (uchar *)rbuf;
164 21141 : restore->rbuf.sz = rbuf_sz;
165 21141 : restore->rbuf.lo = 0UL;
166 21141 : restore->rbuf.ready = 0UL;
167 :
168 21141 : return restore;
169 21141 : }
170 :
171 : fd_restore_t *
172 : fd_restore_init_mmio( void * mem,
173 : void const * mmio,
174 21213 : ulong mmio_sz ) {
175 :
176 : /* Check input args */
177 :
178 21213 : if( FD_UNLIKELY( !mem ) ) {
179 3 : FD_LOG_WARNING(( "NULL mem" ));
180 3 : return NULL;
181 3 : }
182 :
183 21210 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_RESTORE_ALIGN ) ) ) {
184 3 : FD_LOG_WARNING(( "misaligned mem" ));
185 3 : return NULL;
186 3 : }
187 :
188 21207 : if( FD_UNLIKELY( (!mmio) & (!!mmio_sz) ) ) {
189 3 : FD_LOG_WARNING(( "NULL mmio with non-zero mmio_sz" ));
190 3 : return NULL;
191 3 : }
192 :
193 21204 : if( FD_UNLIKELY( mmio_sz>(ulong)LONG_MAX ) ) {
194 0 : FD_LOG_WARNING(( "bad mmio_sz" ));
195 0 : return NULL;
196 0 : }
197 :
198 : /* Create decompressor */
199 :
200 21204 : # if FD_HAS_LZ4
201 21204 : LZ4_streamDecode_t * lz4 = LZ4_createStreamDecode();
202 21204 : if( FD_UNLIKELY( !lz4 ) ) {
203 0 : FD_LOG_WARNING(( "lz4 error" ));
204 0 : return NULL;
205 0 : }
206 : # else
207 : void * lz4 = NULL;
208 : # endif
209 :
210 : /* Init restore */
211 :
212 21204 : fd_restore_t * restore = (fd_restore_t *)mem;
213 :
214 21204 : restore->fd = -1; /* mmio mode */
215 21204 : restore->frame_style = 0; /* not in frame */
216 21204 : restore->lz4 = (void *)lz4;
217 21204 : restore->sbuf_cursor = 0UL;
218 21204 : restore->sz = mmio_sz;
219 21204 : restore->off = 0UL;
220 21204 : restore->mmio.mem = (uchar const *)mmio;
221 :
222 21204 : return restore;
223 21204 : }
224 :
225 : void *
226 42363 : fd_restore_fini( fd_restore_t * restore ) {
227 :
228 42363 : if( FD_UNLIKELY( !restore ) ) {
229 6 : FD_LOG_WARNING(( "NULL restore" ));
230 6 : return NULL;
231 6 : }
232 :
233 42357 : if( FD_UNLIKELY( fd_restore_in_frame( restore ) ) ) {
234 12 : FD_LOG_WARNING(( "in a frame" ));
235 12 : restore->frame_style = -1; /* failed */
236 12 : return NULL;
237 12 : }
238 :
239 42345 : # if FD_HAS_LZ4
240 :
241 : /* Note: Though this this doesn't seem to be officially documented,
242 : the lz4-1.9.4@lz4/lib/lz4.c:2575 suggests that this always returns
243 : 0. That is, 0 is success and non-zero is failure. */
244 :
245 42345 : if( FD_UNLIKELY( LZ4_freeStreamDecode( (LZ4_streamDecode_t *)restore->lz4 ) ) )
246 0 : FD_LOG_WARNING(( "LZ4 freeStreamDecode error, attempting to continue" ));
247 :
248 42345 : # endif
249 :
250 42345 : return restore;
251 42357 : }
252 :
253 : int
254 : fd_restore_open_advanced( fd_restore_t * restore,
255 : int frame_style,
256 422331 : ulong * _off ) {
257 :
258 422331 : if( FD_UNLIKELY( !restore ) ) {
259 6 : FD_LOG_WARNING(( "NULL restore" ));
260 6 : return FD_CHECKPT_ERR_INVAL;
261 6 : }
262 :
263 422325 : if( FD_UNLIKELY( !fd_restore_can_open( restore ) ) ) {
264 6 : FD_LOG_WARNING(( "in a frame or failed" ));
265 6 : restore->frame_style = -1; /* failed */
266 6 : return FD_CHECKPT_ERR_INVAL;
267 6 : }
268 :
269 422319 : if( FD_UNLIKELY( !_off ) ) {
270 0 : FD_LOG_WARNING(( "NULL _off" ));
271 0 : restore->frame_style = -1; /* failed */
272 0 : return FD_CHECKPT_ERR_INVAL;
273 0 : }
274 :
275 422319 : frame_style = fd_int_if( !!frame_style, frame_style, FD_CHECKPT_FRAME_STYLE_DEFAULT );
276 :
277 422319 : switch( frame_style ) {
278 :
279 210495 : case FD_CHECKPT_FRAME_STYLE_RAW: {
280 210495 : break;
281 0 : }
282 :
283 0 : # if FD_HAS_LZ4
284 211818 : case FD_CHECKPT_FRAME_STYLE_LZ4: {
285 211818 : if( FD_UNLIKELY( !LZ4_setStreamDecode( (LZ4_streamDecode_t *)restore->lz4, NULL, 0 ) ) ) {
286 0 : FD_LOG_WARNING(( "LZ4_setStreamDecode failed" ));
287 0 : restore->frame_style = -1; /* failed */
288 0 : return FD_CHECKPT_ERR_COMP;
289 0 : }
290 211818 : restore->sbuf_cursor = 0UL;
291 211818 : break;
292 211818 : }
293 0 : # endif
294 :
295 6 : default: {
296 6 : FD_LOG_WARNING(( "unsupported frame_style" ));
297 6 : restore->frame_style = -1; /* failed */
298 6 : return FD_CHECKPT_ERR_UNSUP;
299 211818 : }
300 :
301 422319 : }
302 :
303 422313 : restore->frame_style = frame_style;
304 :
305 422313 : *_off = restore->off;
306 422313 : return FD_CHECKPT_SUCCESS;
307 422319 : }
308 :
309 : int
310 : fd_restore_close_advanced( fd_restore_t * restore,
311 422259 : ulong * _off ) {
312 :
313 422259 : if( FD_UNLIKELY( !restore ) ) {
314 6 : FD_LOG_WARNING(( "NULL restore" ));
315 6 : return FD_CHECKPT_ERR_INVAL;
316 6 : }
317 :
318 422253 : if( FD_UNLIKELY( !fd_restore_in_frame( restore ) ) ) {
319 6 : FD_LOG_WARNING(( "not in a frame" ));
320 6 : restore->frame_style = -1; /* failed */
321 6 : return FD_CHECKPT_ERR_INVAL;
322 6 : }
323 :
324 422247 : if( FD_UNLIKELY( !_off ) ) {
325 0 : FD_LOG_WARNING(( "NULL _off" ));
326 0 : restore->frame_style = -1; /* failed */
327 0 : return FD_CHECKPT_ERR_INVAL;
328 0 : }
329 :
330 422247 : restore->frame_style = 0;
331 :
332 422247 : *_off = restore->off;
333 422247 : return FD_CHECKPT_SUCCESS;
334 422247 : }
335 :
336 : int
337 : fd_restore_seek( fd_restore_t * restore,
338 18303 : ulong off ) {
339 :
340 18303 : if( FD_UNLIKELY( !restore ) ) {
341 6 : FD_LOG_WARNING(( "NULL restore" ));
342 6 : return FD_CHECKPT_ERR_INVAL;
343 6 : }
344 :
345 18297 : if( FD_UNLIKELY( !fd_restore_can_open( restore ) ) ) {
346 12 : FD_LOG_WARNING(( "restore in frame or failed" ));
347 12 : restore->frame_style = -1;/* failed */
348 12 : return FD_CHECKPT_ERR_INVAL;
349 12 : }
350 :
351 18285 : ulong sz = restore->sz;
352 18285 : if( FD_UNLIKELY( sz>(ulong)LONG_MAX ) ) {
353 0 : FD_LOG_WARNING(( "restore not seekable" ));
354 0 : restore->frame_style = -1;/* failed */
355 0 : return FD_CHECKPT_ERR_INVAL;
356 0 : }
357 :
358 18285 : if( FD_UNLIKELY( off>sz ) ) {
359 3 : FD_LOG_WARNING(( "bad off" ));
360 3 : restore->frame_style = -1;/* failed */
361 3 : return FD_CHECKPT_ERR_INVAL;
362 3 : }
363 :
364 : /* Note: off<=sz<=LONG_MAX here */
365 :
366 18282 : if( fd_restore_is_mmio( restore ) ) { /* mmio mode, app dependent branch prob */
367 :
368 9279 : restore->off = off;
369 :
370 9279 : } else {
371 :
372 : /* Compute the fd offset range [off0,off1) currently buffered at
373 : rbuf [0,lo+ready). If off is in this range, update lo and ready
374 : accordingly. Otherwise, seek the underlying fd to off and flush
375 : rbuf. Note: though this theoretically could be used to support
376 : limited seeking within streams / pipes, we don't expose this as
377 : the API semantics would be tricky to make well defined, robust,
378 : predictable and easy to use. */
379 :
380 : /* Note: minimizing I/O seeks currently disabled because it is not a
381 : very important opt and it has no test coverage currently. Set
382 : this to 1 to enable. */
383 : # if 0
384 : ulong off_old = restore->off;
385 : ulong off0 = off_old - restore->rbuf.lo;
386 : ulong off1 = off_old + restore->rbuf.ready;
387 : if( FD_UNLIKELY( (off0<=off) & (off<off1) ) ) {
388 :
389 : restore->off = off;
390 : restore->rbuf.lo = off - off0;
391 : restore->rbuf.ready = off1 - off;
392 :
393 : } else
394 : # endif
395 :
396 9003 : {
397 :
398 9003 : ulong idx;
399 9003 : int err = fd_io_seek( restore->fd, (long)off, FD_IO_SEEK_TYPE_SET, &idx );
400 9003 : if( FD_UNLIKELY( err ) ) {
401 0 : FD_LOG_WARNING(( "fd_io_seek failed (%i-%s)", err, fd_io_strerror( err ) ));
402 0 : restore->frame_style = -1; /* failed */
403 0 : return FD_CHECKPT_ERR_IO;
404 0 : }
405 :
406 9003 : if( FD_UNLIKELY( idx!=off ) ) {
407 0 : FD_LOG_WARNING(( "unexpected fd_io_seek result" ));
408 0 : restore->frame_style = -1; /* failed */
409 0 : return FD_CHECKPT_ERR_IO;
410 0 : }
411 :
412 9003 : restore->off = off;
413 9003 : restore->rbuf.lo = 0UL;
414 9003 : restore->rbuf.ready = 0UL;
415 :
416 9003 : }
417 :
418 9003 : }
419 :
420 18282 : return FD_CHECKPT_SUCCESS;
421 18282 : }
422 :
423 : static int
424 : fd_restore_private_buf( fd_restore_t * restore,
425 : void * buf,
426 : ulong sz,
427 3590127 : ulong max ) {
428 :
429 3590127 : if( FD_UNLIKELY( !restore ) ) {
430 12 : FD_LOG_WARNING(( "NULL restore" ));
431 12 : return FD_CHECKPT_ERR_INVAL;
432 12 : }
433 :
434 3590115 : if( FD_UNLIKELY( !fd_restore_in_frame( restore ) ) ) {
435 54 : FD_LOG_WARNING(( "not in a frame" ));
436 54 : restore->frame_style = -1; /* failed */
437 54 : return FD_CHECKPT_ERR_INVAL;
438 54 : }
439 :
440 3590061 : if( FD_UNLIKELY( !sz ) ) return FD_CHECKPT_SUCCESS; /* nothing to do */
441 :
442 3196260 : if( FD_UNLIKELY( sz>max ) ) {
443 12 : FD_LOG_WARNING(( "sz too large" ));
444 12 : restore->frame_style = -1; /* failed */
445 12 : return FD_CHECKPT_ERR_INVAL;
446 12 : }
447 :
448 3196248 : if( FD_UNLIKELY( !buf ) ) {
449 24 : FD_LOG_WARNING(( "NULL buf with non-zero sz" ));
450 24 : restore->frame_style = -1; /* failed */
451 24 : return FD_CHECKPT_ERR_INVAL;
452 24 : }
453 :
454 3196224 : ulong off = restore->off;
455 :
456 3196224 : switch( restore->frame_style ) {
457 :
458 1593810 : case FD_CHECKPT_FRAME_STYLE_RAW: {
459 :
460 1593810 : if( fd_restore_is_mmio( restore ) ) { /* mmio mode, app dependent branch prob */
461 :
462 792669 : ulong mmio_sz = restore->sz;
463 :
464 792669 : if( FD_UNLIKELY( sz > (mmio_sz-off) ) ) {
465 0 : FD_LOG_WARNING(( "sz overflow" ));
466 0 : restore->frame_style = -1; /* failed */
467 0 : return FD_CHECKPT_ERR_IO;
468 0 : }
469 :
470 792669 : memcpy( buf, restore->mmio.mem + off, sz );
471 :
472 801141 : } else { /* streaming mode */
473 :
474 801141 : int err = fd_io_buffered_read( restore->fd, buf, sz, restore->rbuf.mem, restore->rbuf.sz,
475 801141 : &restore->rbuf.lo, &restore->rbuf.ready );
476 :
477 801141 : if( FD_UNLIKELY( err ) ) {
478 0 : FD_LOG_WARNING(( "fd_io_buffered_read failed (%i-%s)", err, fd_io_strerror( err ) ));
479 0 : restore->frame_style = -1; /* failed */
480 0 : return FD_CHECKPT_ERR_IO;
481 0 : }
482 :
483 801141 : }
484 :
485 1593810 : off += sz; /* at most mmio_sz */
486 1593810 : break;
487 1593810 : }
488 :
489 0 : # if FD_HAS_LZ4
490 1602414 : case FD_CHECKPT_FRAME_STYLE_LZ4: {
491 :
492 1602414 : LZ4_streamDecode_t * lz4 = (LZ4_streamDecode_t *)restore->lz4;
493 :
494 1602414 : if( fd_restore_is_mmio( restore ) ) { /* mmio mode */
495 :
496 798912 : uchar const * mmio = restore->mmio.mem;
497 798912 : ulong mmio_sz = restore->sz;
498 :
499 798912 : uchar * chunk = (uchar *)buf;
500 861735 : do {
501 861735 : ulong chunk_usz = fd_ulong_min( sz, FD_CHECKPT_PRIVATE_CHUNK_USZ_MAX );
502 :
503 861735 : ulong chunk_csz = fd_restore_private_lz4( lz4, chunk, chunk_usz, mmio + off, mmio_sz - off,
504 861735 : restore->sbuf, FD_RESTORE_PRIVATE_SBUF_SZ, FD_RESTORE_META_MAX,
505 861735 : &restore->sbuf_cursor ); /* logs details */
506 861735 : if( FD_UNLIKELY( !chunk_csz ) ) {
507 0 : restore->frame_style = -1; /* failed */
508 0 : return FD_CHECKPT_ERR_COMP;
509 0 : }
510 :
511 861735 : off += chunk_csz; /* at most mmio_sz */
512 :
513 861735 : chunk += chunk_usz;
514 861735 : sz -= chunk_usz;
515 861735 : } while( sz );
516 :
517 803502 : } else { /* streaming mode */
518 :
519 803502 : int fd = restore->fd;
520 803502 : uchar * rbuf = restore->rbuf.mem;
521 803502 : ulong rbuf_sz = restore->rbuf.sz;
522 803502 : ulong rbuf_lo = restore->rbuf.lo;
523 803502 : ulong rbuf_ready = restore->rbuf.ready;
524 :
525 803502 : uchar * chunk = (uchar *)buf;
526 865332 : do {
527 865332 : ulong chunk_usz = fd_ulong_min( sz, FD_CHECKPT_PRIVATE_CHUNK_USZ_MAX );
528 :
529 : /* Pre-buffer the header and the first body byte to figure out
530 : how large the compressed chunk actually is.
531 :
532 : Note: This can buffer bytes past the end of the checkpoint in
533 : the uncommon case of there being data past the end of the
534 : checkpoint (e.g. is a stream like stdin without an EOF or the
535 : checkpoint is embedded in a larger file). We could have
536 : fd_io_read below use min_sz-rbuf_ready for the min and max sz
537 : arguments to not overread (but then there isn't much point to
538 : using buffered reads). We could also make an unbuffered
539 : streaming a restore option (but it probably much slower if
540 : there are lots of tiny buffers). Regardless, overreading in
541 : such scenarios is an unavoidable possibility if the incoming
542 : file is corrupt anyway and the caller will usually be able to
543 : seek such streams. So we currently just allow it to get the
544 : benefits of buffering. */
545 :
546 865332 : # define BUFFER(min_ready) \
547 1730664 : if( FD_UNLIKELY( rbuf_ready<min_ready ) ) { /* If not enough bytes buffered */ \
548 : \
549 : /* Move the unprocessed bytes to the beginning of the buffer */ \
550 11283 : \
551 11283 : if( FD_LIKELY( (rbuf_lo>0UL) & (rbuf_ready>0UL) ) ) memmove( rbuf, rbuf+rbuf_lo, rbuf_ready ); \
552 11283 : \
553 : /* Read at least enough bytes to make progress and at most */ \
554 : /* enough bytes to fill the rbuf. If we hit EOF or another */ \
555 : /* error, the restore failed. */ \
556 11283 : \
557 11283 : ulong rsz; \
558 11283 : int err = fd_io_read( fd, rbuf+rbuf_ready, min_ready-rbuf_ready, rbuf_sz-rbuf_ready, &rsz ); \
559 11283 : if( FD_UNLIKELY( err ) ) { \
560 0 : FD_LOG_WARNING(( "fd_io_read failed (%i-%s)", err, fd_io_strerror( err ) )); \
561 0 : restore->frame_style = -1; /* failed */ \
562 0 : return FD_CHECKPT_ERR_IO; \
563 0 : } \
564 11283 : \
565 11283 : rbuf_ready += rsz; /* in [min_ready,rbuf_sz] */ \
566 11283 : rbuf_lo = 0UL; \
567 11283 : }
568 :
569 865332 : BUFFER( 4UL )
570 :
571 865332 : ulong chunk_csz = 3UL + ( ((ulong)rbuf[ rbuf_lo ] )
572 865332 : | ((ulong)rbuf[ rbuf_lo+1UL ] << 8)
573 865332 : | ((ulong)rbuf[ rbuf_lo+2UL ] << 16) );
574 :
575 865332 : if( FD_UNLIKELY( !((4UL<=chunk_csz) & (chunk_csz<=FD_CHECKPT_PRIVATE_CSZ_MAX( chunk_usz ))) ) ) {
576 0 : FD_LOG_WARNING(( "corrupt header" ));
577 0 : restore->frame_style = -1; /* failed */
578 0 : return FD_CHECKPT_ERR_COMP;
579 0 : }
580 :
581 : /* Buffer the compressed chunk. If the fd doesn't have
582 : chunk_csz bytes available (e.g. we hit EOF unexpectedly or
583 : other I/O error), this will fail the restore. Note that we
584 : haven't advanced rbuf_lo yet so we invoke buffer with the
585 : entire chunk_csz. Also note that at this point:
586 :
587 : rbuf_sz >= RBUF_MIN >= CSZ_MAX( USZ_MAX ) >= CSZ_MAX( chunk_usz ) >= chunk_csz
588 :
589 : such that we always can buffer chunk_csz bytes into rbuf. */
590 :
591 865332 : BUFFER( chunk_csz );
592 :
593 : /* Decompress the compressed chunk in rbuf */
594 :
595 865332 : ulong res = fd_restore_private_lz4( lz4, chunk, chunk_usz, rbuf + rbuf_lo, rbuf_ready,
596 865332 : restore->sbuf, FD_RESTORE_PRIVATE_SBUF_SZ, FD_RESTORE_META_MAX,
597 865332 : &restore->sbuf_cursor ); /* logs details */
598 865332 : if( FD_UNLIKELY( !res ) ) {
599 0 : restore->frame_style = -1; /* failed */
600 0 : return FD_CHECKPT_ERR_COMP;
601 0 : }
602 :
603 865332 : if( FD_UNLIKELY( res!=chunk_csz ) ) {
604 0 : FD_LOG_WARNING(( "corrupt body" ));
605 0 : restore->frame_style = -1; /* failed */
606 0 : return FD_CHECKPT_ERR_COMP;
607 0 : }
608 :
609 865332 : # undef BUFFER
610 :
611 865332 : rbuf_lo += chunk_csz;
612 865332 : rbuf_ready -= chunk_csz;
613 :
614 865332 : off += chunk_csz;
615 :
616 865332 : chunk += chunk_usz;
617 865332 : sz -= chunk_usz;
618 865332 : } while( sz );
619 :
620 803502 : restore->rbuf.lo = rbuf_lo;
621 803502 : restore->rbuf.ready = rbuf_ready;
622 :
623 803502 : }
624 :
625 1602414 : break;
626 1602414 : }
627 1602414 : # endif
628 :
629 1602414 : default: { /* never get here */
630 0 : FD_LOG_WARNING(( "unsupported frame style" ));
631 0 : restore->frame_style = -1; /* failed */
632 0 : return FD_CHECKPT_ERR_UNSUP;
633 1602414 : }
634 :
635 3196224 : }
636 :
637 3196224 : restore->off = off;
638 3196224 : return FD_CHECKPT_SUCCESS;
639 3196224 : }
640 :
641 : int
642 : fd_restore_meta( fd_restore_t * restore,
643 : void * buf,
644 432 : ulong sz ) {
645 432 : return fd_restore_private_buf( restore, buf, sz, FD_CHECKPT_META_MAX );
646 432 : }
647 :
648 : int
649 : fd_restore_data( fd_restore_t * restore,
650 : void * buf,
651 3589695 : ulong sz ) {
652 3589695 : return fd_restore_private_buf( restore, buf, sz, ULONG_MAX );
653 3589695 : }
|