LCOV - code coverage report
Current view: top level - util/checkpt - fd_restore.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 295 382 77.2 %
Date: 2025-01-08 12:08:44 Functions: 10 10 100.0 %

          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 : }

Generated by: LCOV version 1.14