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 0 : ulong * _sbuf_cursor ) {
30 0 : char * ubuf = (char *) _ubuf;
31 0 : 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 0 : 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 0 : 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 0 : ulong ubuf_csz = (((ulong)(uchar)cbuf[0]) )
49 0 : | (((ulong)(uchar)cbuf[1]) << 8)
50 0 : | (((ulong)(uchar)cbuf[2]) << 16); /* In [1,2^24) */
51 :
52 0 : ulong cbuf_sz = ubuf_csz + 3UL;
53 0 : 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 0 : 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 0 : int is_small = ubuf_usz<=sbuf_thresh;
67 0 : if( is_small ) { /* app dependent branch prob */
68 0 : ulong sbuf_cursor = *_sbuf_cursor;
69 0 : if( (sbuf_sz-sbuf_cursor)<ubuf_usz ) sbuf_cursor = 0UL; /* cmov */
70 0 : ubuf = (char *)_sbuf + sbuf_cursor;
71 0 : *_sbuf_cursor = sbuf_cursor + ubuf_usz;
72 0 : }
73 :
74 : /* Restore the buffer */
75 :
76 0 : int res = LZ4_decompress_safe_continue( lz4, cbuf+3UL, ubuf, (int)ubuf_csz, (int)ubuf_usz );
77 0 : 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 0 : if( is_small ) memcpy( _ubuf, ubuf, ubuf_usz ); /* app dependent branch prob */
85 :
86 0 : return cbuf_sz;
87 0 : }
88 : #endif
89 :
90 : fd_restore_t *
91 : fd_restore_init_stream( void * mem,
92 : int fd,
93 : void * rbuf,
94 0 : ulong rbuf_sz ) {
95 :
96 : /* Check input args */
97 :
98 0 : if( FD_UNLIKELY( !mem ) ) {
99 0 : FD_LOG_WARNING(( "NULL mem" ));
100 0 : return NULL;
101 0 : }
102 :
103 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_RESTORE_ALIGN ) ) ) {
104 0 : FD_LOG_WARNING(( "misaligned mem" ));
105 0 : return NULL;
106 0 : }
107 :
108 0 : if( FD_UNLIKELY( fd<0 ) ) {
109 0 : FD_LOG_WARNING(( "bad fd" ));
110 0 : return NULL;
111 0 : }
112 :
113 0 : if( FD_UNLIKELY( !rbuf ) ) {
114 0 : FD_LOG_WARNING(( "NULL rbuf" ));
115 0 : return NULL;
116 0 : }
117 :
118 0 : if( FD_UNLIKELY( rbuf_sz<FD_RESTORE_RBUF_MIN ) ) {
119 0 : FD_LOG_WARNING(( "rbuf_sz too small" ));
120 0 : return NULL;
121 0 : }
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 0 : ulong sz;
129 0 : ulong off;
130 :
131 0 : int err = fd_io_sz( fd, &sz );
132 0 : if( FD_LIKELY( !err ) ) err = fd_io_seek( fd, 0L, FD_IO_SEEK_TYPE_CUR, &off );
133 0 : if( FD_UNLIKELY( err ) ) { /* fd does not appear seekable */
134 0 : off = 0L;
135 0 : sz = ULONG_MAX;
136 0 : } 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 0 : # if FD_HAS_LZ4
144 0 : LZ4_streamDecode_t * lz4 = LZ4_createStreamDecode();
145 0 : 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 0 : fd_restore_t * restore = (fd_restore_t *)mem;
156 :
157 0 : restore->fd = fd; /* streaming mode */
158 0 : restore->frame_style = 0; /* not in frame */
159 0 : restore->lz4 = (void *)lz4;
160 0 : restore->sbuf_cursor = 0UL;
161 0 : restore->sz = sz;
162 0 : restore->off = off;
163 0 : restore->rbuf.mem = (uchar *)rbuf;
164 0 : restore->rbuf.sz = rbuf_sz;
165 0 : restore->rbuf.lo = 0UL;
166 0 : restore->rbuf.ready = 0UL;
167 :
168 0 : return restore;
169 0 : }
170 :
171 : fd_restore_t *
172 : fd_restore_init_mmio( void * mem,
173 : void const * mmio,
174 0 : ulong mmio_sz ) {
175 :
176 : /* Check input args */
177 :
178 0 : if( FD_UNLIKELY( !mem ) ) {
179 0 : FD_LOG_WARNING(( "NULL mem" ));
180 0 : return NULL;
181 0 : }
182 :
183 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_RESTORE_ALIGN ) ) ) {
184 0 : FD_LOG_WARNING(( "misaligned mem" ));
185 0 : return NULL;
186 0 : }
187 :
188 0 : if( FD_UNLIKELY( (!mmio) & (!!mmio_sz) ) ) {
189 0 : FD_LOG_WARNING(( "NULL mmio with non-zero mmio_sz" ));
190 0 : return NULL;
191 0 : }
192 :
193 0 : 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 0 : # if FD_HAS_LZ4
201 0 : LZ4_streamDecode_t * lz4 = LZ4_createStreamDecode();
202 0 : 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 0 : fd_restore_t * restore = (fd_restore_t *)mem;
213 :
214 0 : restore->fd = -1; /* mmio mode */
215 0 : restore->frame_style = 0; /* not in frame */
216 0 : restore->lz4 = (void *)lz4;
217 0 : restore->sbuf_cursor = 0UL;
218 0 : restore->sz = mmio_sz;
219 0 : restore->off = 0UL;
220 0 : restore->mmio.mem = (uchar const *)mmio;
221 :
222 0 : return restore;
223 0 : }
224 :
225 : void *
226 0 : fd_restore_fini( fd_restore_t * restore ) {
227 :
228 0 : if( FD_UNLIKELY( !restore ) ) {
229 0 : FD_LOG_WARNING(( "NULL restore" ));
230 0 : return NULL;
231 0 : }
232 :
233 0 : if( FD_UNLIKELY( fd_restore_in_frame( restore ) ) ) {
234 0 : FD_LOG_WARNING(( "in a frame" ));
235 0 : restore->frame_style = -1; /* failed */
236 0 : return NULL;
237 0 : }
238 :
239 0 : # 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 0 : if( FD_UNLIKELY( LZ4_freeStreamDecode( (LZ4_streamDecode_t *)restore->lz4 ) ) )
246 0 : FD_LOG_WARNING(( "LZ4 freeStreamDecode error, attempting to continue" ));
247 :
248 0 : # endif
249 :
250 0 : return restore;
251 0 : }
252 :
253 : int
254 : fd_restore_open_advanced( fd_restore_t * restore,
255 : int frame_style,
256 0 : ulong * _off ) {
257 :
258 0 : if( FD_UNLIKELY( !restore ) ) {
259 0 : FD_LOG_WARNING(( "NULL restore" ));
260 0 : return FD_CHECKPT_ERR_INVAL;
261 0 : }
262 :
263 0 : if( FD_UNLIKELY( !fd_restore_can_open( restore ) ) ) {
264 0 : FD_LOG_WARNING(( "in a frame or failed" ));
265 0 : restore->frame_style = -1; /* failed */
266 0 : return FD_CHECKPT_ERR_INVAL;
267 0 : }
268 :
269 0 : 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 0 : frame_style = fd_int_if( !!frame_style, frame_style, FD_CHECKPT_FRAME_STYLE_DEFAULT );
276 :
277 0 : switch( frame_style ) {
278 :
279 0 : case FD_CHECKPT_FRAME_STYLE_RAW: {
280 0 : break;
281 0 : }
282 :
283 0 : # if FD_HAS_LZ4
284 0 : case FD_CHECKPT_FRAME_STYLE_LZ4: {
285 0 : 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 0 : restore->sbuf_cursor = 0UL;
291 0 : break;
292 0 : }
293 0 : # endif
294 :
295 0 : default: {
296 0 : FD_LOG_WARNING(( "unsupported frame_style" ));
297 0 : restore->frame_style = -1; /* failed */
298 0 : return FD_CHECKPT_ERR_UNSUP;
299 0 : }
300 :
301 0 : }
302 :
303 0 : restore->frame_style = frame_style;
304 :
305 0 : *_off = restore->off;
306 0 : return FD_CHECKPT_SUCCESS;
307 0 : }
308 :
309 : int
310 : fd_restore_close_advanced( fd_restore_t * restore,
311 0 : ulong * _off ) {
312 :
313 0 : if( FD_UNLIKELY( !restore ) ) {
314 0 : FD_LOG_WARNING(( "NULL restore" ));
315 0 : return FD_CHECKPT_ERR_INVAL;
316 0 : }
317 :
318 0 : if( FD_UNLIKELY( !fd_restore_in_frame( restore ) ) ) {
319 0 : FD_LOG_WARNING(( "not in a frame" ));
320 0 : restore->frame_style = -1; /* failed */
321 0 : return FD_CHECKPT_ERR_INVAL;
322 0 : }
323 :
324 0 : 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 0 : restore->frame_style = 0;
331 :
332 0 : *_off = restore->off;
333 0 : return FD_CHECKPT_SUCCESS;
334 0 : }
335 :
336 : int
337 : fd_restore_seek( fd_restore_t * restore,
338 0 : ulong off ) {
339 :
340 0 : if( FD_UNLIKELY( !restore ) ) {
341 0 : FD_LOG_WARNING(( "NULL restore" ));
342 0 : return FD_CHECKPT_ERR_INVAL;
343 0 : }
344 :
345 0 : if( FD_UNLIKELY( !fd_restore_can_open( restore ) ) ) {
346 0 : FD_LOG_WARNING(( "restore in frame or failed" ));
347 0 : restore->frame_style = -1;/* failed */
348 0 : return FD_CHECKPT_ERR_INVAL;
349 0 : }
350 :
351 0 : ulong sz = restore->sz;
352 0 : 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 0 : if( FD_UNLIKELY( off>sz ) ) {
359 0 : FD_LOG_WARNING(( "bad off" ));
360 0 : restore->frame_style = -1;/* failed */
361 0 : return FD_CHECKPT_ERR_INVAL;
362 0 : }
363 :
364 : /* Note: off<=sz<=LONG_MAX here */
365 :
366 0 : if( fd_restore_is_mmio( restore ) ) { /* mmio mode, app dependent branch prob */
367 :
368 0 : restore->off = off;
369 :
370 0 : } 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 0 : {
397 :
398 0 : ulong idx;
399 0 : int err = fd_io_seek( restore->fd, (long)off, FD_IO_SEEK_TYPE_SET, &idx );
400 0 : 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 0 : 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 0 : restore->off = off;
413 0 : restore->rbuf.lo = 0UL;
414 0 : restore->rbuf.ready = 0UL;
415 :
416 0 : }
417 :
418 0 : }
419 :
420 0 : return FD_CHECKPT_SUCCESS;
421 0 : }
422 :
423 : static int
424 : fd_restore_private_buf( fd_restore_t * restore,
425 : void * buf,
426 : ulong sz,
427 0 : ulong max ) {
428 :
429 0 : if( FD_UNLIKELY( !restore ) ) {
430 0 : FD_LOG_WARNING(( "NULL restore" ));
431 0 : return FD_CHECKPT_ERR_INVAL;
432 0 : }
433 :
434 0 : if( FD_UNLIKELY( !fd_restore_in_frame( restore ) ) ) {
435 0 : FD_LOG_WARNING(( "not in a frame" ));
436 0 : restore->frame_style = -1; /* failed */
437 0 : return FD_CHECKPT_ERR_INVAL;
438 0 : }
439 :
440 0 : if( FD_UNLIKELY( !sz ) ) return FD_CHECKPT_SUCCESS; /* nothing to do */
441 :
442 0 : if( FD_UNLIKELY( sz>max ) ) {
443 0 : FD_LOG_WARNING(( "sz too large" ));
444 0 : restore->frame_style = -1; /* failed */
445 0 : return FD_CHECKPT_ERR_INVAL;
446 0 : }
447 :
448 0 : if( FD_UNLIKELY( !buf ) ) {
449 0 : FD_LOG_WARNING(( "NULL buf with non-zero sz" ));
450 0 : restore->frame_style = -1; /* failed */
451 0 : return FD_CHECKPT_ERR_INVAL;
452 0 : }
453 :
454 0 : ulong off = restore->off;
455 :
456 0 : switch( restore->frame_style ) {
457 :
458 0 : case FD_CHECKPT_FRAME_STYLE_RAW: {
459 :
460 0 : if( fd_restore_is_mmio( restore ) ) { /* mmio mode, app dependent branch prob */
461 :
462 0 : ulong mmio_sz = restore->sz;
463 :
464 0 : 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 0 : memcpy( buf, restore->mmio.mem + off, sz );
471 :
472 0 : } else { /* streaming mode */
473 :
474 0 : int err = fd_io_buffered_read( restore->fd, buf, sz, restore->rbuf.mem, restore->rbuf.sz,
475 0 : &restore->rbuf.lo, &restore->rbuf.ready );
476 :
477 0 : 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 0 : }
484 :
485 0 : off += sz; /* at most mmio_sz */
486 0 : break;
487 0 : }
488 :
489 0 : # if FD_HAS_LZ4
490 0 : case FD_CHECKPT_FRAME_STYLE_LZ4: {
491 :
492 0 : LZ4_streamDecode_t * lz4 = (LZ4_streamDecode_t *)restore->lz4;
493 :
494 0 : if( fd_restore_is_mmio( restore ) ) { /* mmio mode */
495 :
496 0 : uchar const * mmio = restore->mmio.mem;
497 0 : ulong mmio_sz = restore->sz;
498 :
499 0 : uchar * chunk = (uchar *)buf;
500 0 : do {
501 0 : ulong chunk_usz = fd_ulong_min( sz, FD_CHECKPT_PRIVATE_CHUNK_USZ_MAX );
502 :
503 0 : ulong chunk_csz = fd_restore_private_lz4( lz4, chunk, chunk_usz, mmio + off, mmio_sz - off,
504 0 : restore->sbuf, FD_RESTORE_PRIVATE_SBUF_SZ, FD_RESTORE_META_MAX,
505 0 : &restore->sbuf_cursor ); /* logs details */
506 0 : if( FD_UNLIKELY( !chunk_csz ) ) {
507 0 : restore->frame_style = -1; /* failed */
508 0 : return FD_CHECKPT_ERR_COMP;
509 0 : }
510 :
511 0 : off += chunk_csz; /* at most mmio_sz */
512 :
513 0 : chunk += chunk_usz;
514 0 : sz -= chunk_usz;
515 0 : } while( sz );
516 :
517 0 : } else { /* streaming mode */
518 :
519 0 : int fd = restore->fd;
520 0 : uchar * rbuf = restore->rbuf.mem;
521 0 : ulong rbuf_sz = restore->rbuf.sz;
522 0 : ulong rbuf_lo = restore->rbuf.lo;
523 0 : ulong rbuf_ready = restore->rbuf.ready;
524 :
525 0 : uchar * chunk = (uchar *)buf;
526 0 : do {
527 0 : 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 0 : # define BUFFER(min_ready) \
547 0 : 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 0 : \
551 0 : if( FD_LIKELY( (rbuf_lo>0UL) & (rbuf_ready>0UL) ) ) memmove( rbuf, rbuf+rbuf_lo, rbuf_ready ); \
552 0 : \
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 0 : \
557 0 : ulong rsz; \
558 0 : int err = fd_io_read( fd, rbuf+rbuf_ready, min_ready-rbuf_ready, rbuf_sz-rbuf_ready, &rsz ); \
559 0 : 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 0 : \
565 0 : rbuf_ready += rsz; /* in [min_ready,rbuf_sz] */ \
566 0 : rbuf_lo = 0UL; \
567 0 : }
568 :
569 0 : BUFFER( 4UL )
570 :
571 0 : ulong chunk_csz = 3UL + ( ((ulong)rbuf[ rbuf_lo ] )
572 0 : | ((ulong)rbuf[ rbuf_lo+1UL ] << 8)
573 0 : | ((ulong)rbuf[ rbuf_lo+2UL ] << 16) );
574 :
575 0 : 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 0 : BUFFER( chunk_csz );
592 :
593 : /* Decompress the compressed chunk in rbuf */
594 :
595 0 : ulong res = fd_restore_private_lz4( lz4, chunk, chunk_usz, rbuf + rbuf_lo, rbuf_ready,
596 0 : restore->sbuf, FD_RESTORE_PRIVATE_SBUF_SZ, FD_RESTORE_META_MAX,
597 0 : &restore->sbuf_cursor ); /* logs details */
598 0 : if( FD_UNLIKELY( !res ) ) {
599 0 : restore->frame_style = -1; /* failed */
600 0 : return FD_CHECKPT_ERR_COMP;
601 0 : }
602 :
603 0 : 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 0 : # undef BUFFER
610 :
611 0 : rbuf_lo += chunk_csz;
612 0 : rbuf_ready -= chunk_csz;
613 :
614 0 : off += chunk_csz;
615 :
616 0 : chunk += chunk_usz;
617 0 : sz -= chunk_usz;
618 0 : } while( sz );
619 :
620 0 : restore->rbuf.lo = rbuf_lo;
621 0 : restore->rbuf.ready = rbuf_ready;
622 :
623 0 : }
624 :
625 0 : break;
626 0 : }
627 0 : # endif
628 :
629 0 : 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 0 : }
634 :
635 0 : }
636 :
637 0 : restore->off = off;
638 0 : return FD_CHECKPT_SUCCESS;
639 0 : }
640 :
641 : int
642 : fd_restore_meta( fd_restore_t * restore,
643 : void * buf,
644 0 : ulong sz ) {
645 0 : return fd_restore_private_buf( restore, buf, sz, FD_CHECKPT_META_MAX );
646 0 : }
647 :
648 : int
649 : fd_restore_data( fd_restore_t * restore,
650 : void * buf,
651 0 : ulong sz ) {
652 0 : return fd_restore_private_buf( restore, buf, sz, ULONG_MAX );
653 0 : }
|