LCOV - code coverage report
Current view: top level - flamenco/snapshot - fd_snapshot_restore.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 352 418 84.2 %
Date: 2025-01-08 12:08:44 Functions: 23 23 100.0 %

          Line data    Source code
       1             : #include "fd_snapshot_restore_private.h"
       2             : #include "../../util/archive/fd_tar.h"
       3             : #include "../runtime/fd_acc_mgr.h"
       4             : #include "../runtime/fd_account.h"
       5             : 
       6             : #include <assert.h>
       7             : #include <errno.h>
       8             : #include <stdio.h>       /* sscanf */
       9             : #include <string.h>      /* strncmp */
      10             : #include <sys/random.h>  /* getrandom */
      11             : 
      12             : /* Snapshot Restore Buffer Handling ***********************************/
      13             : 
      14             : static void
      15         111 : fd_snapshot_restore_discard_buf( fd_snapshot_restore_t * self ) {
      16             :   /* self->buf might be NULL */
      17         111 :   fd_valloc_free( self->valloc, self->buf );
      18         111 :   self->buf     = NULL;
      19         111 :   self->buf_ctr = 0UL;
      20         111 :   self->buf_sz  = 0UL;
      21         111 :   self->buf_cap = 0UL;
      22         111 : }
      23             : 
      24             : static void *
      25             : fd_snapshot_restore_prepare_buf( fd_snapshot_restore_t * self,
      26          48 :                                  ulong                   sz ) {
      27             : 
      28          48 :   self->buf_ctr = 0UL;
      29          48 :   self->buf_sz  = 0UL;
      30             : 
      31          48 :   if( FD_LIKELY( sz <= self->buf_cap ) )
      32           0 :     return self->buf;
      33             : 
      34          48 :   fd_snapshot_restore_discard_buf( self );
      35          48 :   uchar * buf = fd_valloc_malloc( self->valloc, 1UL, sz );
      36          48 :   if( FD_UNLIKELY( !buf ) ) {
      37           6 :     self->failed = 1;
      38           6 :     return NULL;
      39           6 :   }
      40          42 :   self->buf     = buf;
      41          42 :   self->buf_cap = sz;
      42          42 :   return buf;
      43          48 : }
      44             : 
      45             : ulong
      46          66 : fd_snapshot_restore_align( void ) {
      47          66 :   return fd_ulong_max( alignof(fd_snapshot_restore_t), fd_snapshot_accv_map_align() );
      48          66 : }
      49             : 
      50             : ulong
      51           3 : fd_snapshot_restore_footprint( void ) {
      52           3 :   ulong l = FD_LAYOUT_INIT;
      53           3 :   l = FD_LAYOUT_APPEND( l, alignof(fd_snapshot_restore_t), sizeof(fd_snapshot_restore_t) );
      54           3 :   l = FD_LAYOUT_APPEND( l, fd_snapshot_accv_map_align(), fd_snapshot_accv_map_footprint() );
      55           3 :   return FD_LAYOUT_FINI( l, fd_snapshot_restore_align() );
      56           3 : }
      57             : 
      58             : fd_snapshot_restore_t *
      59             : fd_snapshot_restore_new( void *                               mem,
      60             :                          fd_acc_mgr_t *                       acc_mgr,
      61             :                          fd_funk_txn_t *                      funk_txn,
      62             :                          fd_valloc_t                          valloc,
      63             :                          void *                               cb_manifest_ctx,
      64             :                          fd_snapshot_restore_cb_manifest_fn_t cb_manifest,
      65          63 :                          fd_snapshot_restore_cb_status_cache_fn_t cb_status_cache ) {
      66             : 
      67          63 :   if( FD_UNLIKELY( !mem ) ) {
      68           3 :     FD_LOG_WARNING(( "NULL mem" ));
      69           3 :     return NULL;
      70           3 :   }
      71          60 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_snapshot_restore_align() ) ) ) {
      72           0 :     FD_LOG_WARNING(( "unaligned mem" ));
      73           0 :     return NULL;
      74           0 :   }
      75          60 :   if( FD_UNLIKELY( !acc_mgr ) ) {
      76           3 :     FD_LOG_WARNING(( "NULL acc_mgr" ));
      77           3 :     return NULL;
      78           3 :   }
      79          57 :   if( FD_UNLIKELY( !valloc.vt ) ) {
      80           0 :     FD_LOG_WARNING(( "NULL valloc" ));
      81           0 :     return NULL;
      82           0 :   }
      83          57 :   if( FD_UNLIKELY( !cb_manifest ) ) {
      84           3 :     FD_LOG_WARNING(( "NULL callback" ));
      85           3 :     return NULL;
      86           3 :   }
      87             : 
      88          54 :   FD_SCRATCH_ALLOC_INIT( l, mem );
      89          54 :   fd_snapshot_restore_t * self = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_snapshot_restore_t), sizeof(fd_snapshot_restore_t) );
      90           0 :   fd_memset( self, 0, sizeof(fd_snapshot_restore_t) );
      91          54 :   self->acc_mgr     = acc_mgr;
      92          54 :   self->funk_txn    = funk_txn;
      93          54 :   self->valloc      = valloc;
      94          54 :   self->state       = STATE_DONE;
      95          54 :   self->buf         = NULL;
      96          54 :   self->buf_sz      = 0UL;
      97          54 :   self->buf_ctr     = 0UL;
      98          54 :   self->buf_cap     = 0UL;
      99             : 
     100          54 :   self->cb_manifest     = cb_manifest;
     101          54 :   self->cb_manifest_ctx = cb_manifest_ctx;
     102             : 
     103          54 :   self->cb_status_cache     = cb_status_cache;
     104          54 :   self->cb_status_cache_ctx = cb_manifest_ctx;
     105             : 
     106          54 :   void * accv_map_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_snapshot_accv_map_align(), fd_snapshot_accv_map_footprint() );
     107           0 :   self->accv_map = fd_snapshot_accv_map_join( fd_snapshot_accv_map_new( accv_map_mem ) );
     108          54 :   FD_TEST( self->accv_map );
     109             : 
     110          54 :   return self;
     111          54 : }
     112             : 
     113             : void *
     114          54 : fd_snapshot_restore_delete( fd_snapshot_restore_t * self ) {
     115          54 :   if( FD_UNLIKELY( !self ) ) return NULL;
     116          54 :   fd_snapshot_restore_discard_buf( self );
     117          54 :   fd_snapshot_accv_map_delete( fd_snapshot_accv_map_leave( self->accv_map ) );
     118          54 :   fd_memset( self, 0, sizeof(fd_snapshot_restore_t) );
     119          54 :   return (void *)self;
     120          54 : }
     121             : 
     122             : /* Streaming state machine ********************************************/
     123             : 
     124             : /* fd_snapshot_expect_account_hdr sets up the snapshot restore to
     125             :    expect an account header on the next iteration.  Returns EINVAL if
     126             :    the current AppendVec doesn't fit an account header. */
     127             : 
     128             : static int
     129          33 : fd_snapshot_expect_account_hdr( fd_snapshot_restore_t * restore ) {
     130             : 
     131          33 :   ulong accv_sz = restore->accv_sz;
     132          33 :   if( accv_sz < sizeof(fd_solana_account_hdr_t) ) {
     133           9 :     if( FD_LIKELY( accv_sz==0UL ) ) {
     134           6 :       restore->state = STATE_READ_ACCOUNT_HDR;
     135           6 :       return 0;
     136           6 :     }
     137           3 :     FD_LOG_WARNING(( "encountered unexpected EOF while reading account header" ));
     138           3 :     restore->failed = 1;
     139           3 :     return EINVAL;
     140           9 :   }
     141             : 
     142          24 :   restore->state    = STATE_READ_ACCOUNT_HDR;
     143          24 :   restore->acc_data = NULL;
     144          24 :   restore->buf_ctr  = 0UL;
     145          24 :   restore->buf_sz   = sizeof(fd_solana_account_hdr_t);
     146          24 :   return 0;
     147          33 : }
     148             : 
     149             : /* fd_snapshot_restore_account_hdr deserializes an account header and
     150             :    allocates a corresponding funk record. */
     151             : 
     152             : static int
     153          24 : fd_snapshot_restore_account_hdr( fd_snapshot_restore_t * restore ) {
     154             : 
     155          24 :   fd_solana_account_hdr_t const * hdr = fd_type_pun_const( restore->buf );
     156             : 
     157             :   /* Prepare for account lookup */
     158          24 :   fd_acc_mgr_t *      acc_mgr  = restore->acc_mgr;
     159          24 :   fd_funk_txn_t *     funk_txn = restore->funk_txn;
     160          24 :   fd_pubkey_t const * key      = fd_type_pun_const( hdr->meta.pubkey );
     161          24 :   fd_borrowed_account_t rec[1]; fd_borrowed_account_init( rec );
     162          24 :   char key_cstr[ FD_BASE58_ENCODED_32_SZ ];
     163             : 
     164             :   /* Sanity checks */
     165          24 :   if( FD_UNLIKELY( hdr->meta.data_len > FD_ACC_SZ_MAX ) ) {
     166           0 :     FD_LOG_WARNING(( "accounts/%lu.%lu: account %s too large: data_len=%lu",
     167           0 :                      restore->accv_slot, restore->accv_id, fd_acct_addr_cstr( key_cstr, key->uc ), hdr->meta.data_len ));
     168           0 :     FD_LOG_HEXDUMP_WARNING(( "account header", hdr, sizeof(fd_solana_account_hdr_t) ));
     169           0 :     return EINVAL;
     170           0 :   }
     171             : 
     172          24 :   int is_dupe = 0;
     173             : 
     174             :   /* Check if account exists */
     175          24 :   rec->const_meta = fd_acc_mgr_view_raw( acc_mgr, funk_txn, key, &rec->const_rec, NULL, NULL );
     176          24 :   if( rec->const_meta )
     177          15 :     if( rec->const_meta->slot > restore->accv_slot )
     178           6 :       is_dupe = 1;
     179             : 
     180             :   /* Write account */
     181          24 :   if( !is_dupe ) {
     182          18 :     int write_result = fd_acc_mgr_modify( acc_mgr, funk_txn, key, /* do_create */ 1, hdr->meta.data_len, rec );
     183          18 :     if( FD_UNLIKELY( write_result != FD_ACC_MGR_SUCCESS ) ) {
     184           0 :       FD_LOG_WARNING(( "fd_acc_mgr_modify(%s) failed (%d)", fd_acct_addr_cstr( key_cstr, key->uc ), write_result ));
     185           0 :       return ENOMEM;
     186           0 :     }
     187          18 :     rec->meta->dlen = hdr->meta.data_len;
     188          18 :     rec->meta->slot = restore->accv_slot;
     189          18 :     memcpy( &rec->meta->hash, hdr->hash.uc, 32UL );
     190          18 :     memcpy( &rec->meta->info, &hdr->info, sizeof(fd_solana_account_meta_t) );
     191          18 :     restore->acc_data = rec->data;
     192          18 :   }
     193          24 :   ulong data_sz    = hdr->meta.data_len;
     194          24 :   restore->acc_sz  = data_sz;
     195          24 :   restore->acc_pad = fd_ulong_align_up( data_sz, FD_SNAPSHOT_ACC_ALIGN ) - data_sz;
     196             : 
     197             :   /* Next step */
     198          24 :   if( data_sz == 0UL )
     199           9 :     return fd_snapshot_expect_account_hdr( restore );
     200             : 
     201             :   /* Fail if account data is cut off */
     202          15 :   if( FD_UNLIKELY( restore->accv_sz < data_sz ) ) {
     203           6 :     FD_LOG_WARNING(( "accounts/%lu.%lu: account %s data exceeds past end of account vec (acc_sz=%lu accv_sz=%lu)",
     204           6 :                      restore->accv_slot, restore->accv_id, fd_acct_addr_cstr( key_cstr, key->uc ), data_sz, restore->accv_sz ));
     205           6 :     FD_LOG_HEXDUMP_WARNING(( "account header", hdr, sizeof(fd_solana_account_hdr_t) ));
     206           6 :     restore->failed = 1;
     207           6 :     return EINVAL;
     208           6 :   }
     209             : 
     210           9 :   restore->state    = STATE_READ_ACCOUNT_DATA;
     211           9 :   restore->buf_ctr  = 0UL;
     212           9 :   restore->buf_sz   = 0UL;
     213           9 :   return 0;
     214          15 : }
     215             : 
     216             : /* fd_snapshot_accv_index populates the index of account vecs.  This
     217             :    index will be used when loading accounts.  Returns errno-compatible
     218             :    error code. */
     219             : 
     220             : static int
     221             : fd_snapshot_accv_index( fd_snapshot_accv_map_t *               map,
     222           6 :                         fd_solana_accounts_db_fields_t const * fields ) {
     223             : 
     224           6 :   for( ulong i=0UL; i < fields->storages_len; i++ ) {
     225             : 
     226           0 :     fd_snapshot_slot_acc_vecs_t * slot = &fields->storages[ i ];
     227             : 
     228           0 :     for( ulong j=0UL; j < slot->account_vecs_len; j++ ) {
     229           0 :       fd_snapshot_acc_vec_t * accv = &slot->account_vecs[ j ];
     230             : 
     231             :       /* Insert new AppendVec */
     232           0 :       fd_snapshot_accv_key_t key = { .slot = slot->slot, .id = accv->id };
     233           0 :       fd_snapshot_accv_map_t * rec = fd_snapshot_accv_map_insert( map, key );
     234           0 :       if( FD_UNLIKELY( !rec ) ) {
     235           0 :         FD_LOG_WARNING(( "fd_snapshot_accv_map_insert failed" ));
     236           0 :         return ENOMEM;
     237           0 :       }
     238             : 
     239             :       /* Remember size */
     240           0 :       rec->sz = accv->file_sz;
     241           0 :     }
     242             : 
     243           0 :   }
     244             : 
     245           6 :   return 0;
     246           6 : }
     247             : 
     248             : /* fd_snapshot_restore_manifest imports a snapshot manifest into the
     249             :    given slot context.  Also populates the accv index.  Destroys the
     250             :    existing bank structure. */
     251             : 
     252             : static int
     253           9 : fd_snapshot_restore_manifest( fd_snapshot_restore_t * restore ) {
     254             : 
     255             :   /* Decode manifest placing dynamic data structures onto slot context
     256             :      heap.  Once the epoch context heap is separated out, we need to
     257             :      revisit this. */
     258             : 
     259           9 :   fd_solana_manifest_t manifest[1];
     260           9 :   fd_bincode_decode_ctx_t decode =
     261           9 :       { .data    = restore->buf,
     262           9 :         .dataend = restore->buf + restore->buf_sz,
     263           9 :         .valloc  = restore->valloc };
     264           9 :   int decode_err = fd_solana_manifest_decode( manifest, &decode );
     265           9 :   if( FD_UNLIKELY( decode_err!=FD_BINCODE_SUCCESS ) ) {
     266             :     /* TODO: The types generator does not yet handle OOM correctly.
     267             :              OOM failures won't always end up here, but could also
     268             :              result in a NULL pointer dereference. */
     269           3 :     FD_LOG_WARNING(( "fd_solana_manifest_decode failed (%d)", decode_err ));
     270           3 :     return EINVAL;
     271           3 :   }
     272             : 
     273           6 :   if( manifest->bank_incremental_snapshot_persistence ) {
     274           0 :     FD_LOG_NOTICE(( "Incremental snapshot has incremental snapshot persistence with full acc_hash=%s and incremental acc_hash=%s",
     275           0 :                     FD_BASE58_ENC_32_ALLOCA(&manifest->bank_incremental_snapshot_persistence->full_hash),
     276           0 :                     FD_BASE58_ENC_32_ALLOCA(&manifest->bank_incremental_snapshot_persistence->incremental_hash) ));
     277             : 
     278           6 :   } else {
     279           6 :     FD_LOG_NOTICE(( "Full snapshot acc_hash=%s", FD_BASE58_ENC_32_ALLOCA(&manifest->accounts_db.bank_hash_info.accounts_hash) ));
     280           6 :   }
     281             : 
     282             :   /* Move over accounts DB fields */
     283             : 
     284           6 :   fd_solana_accounts_db_fields_t accounts_db = manifest->accounts_db;
     285           6 :   fd_memset( &manifest->accounts_db, 0, sizeof(fd_solana_accounts_db_fields_t) );
     286             : 
     287             :   /* Remember slot number */
     288             : 
     289           6 :   ulong slot = manifest->bank.slot;
     290             : 
     291             :   /* Move over objects and recover state
     292             :      This destroys all remaining fields with the slot context valloc. */
     293             : 
     294           6 :   int err = restore->cb_manifest( restore->cb_manifest_ctx, manifest );
     295             : 
     296             :   /* Read AccountVec map */
     297             : 
     298           6 :   if( FD_LIKELY( !err ) )
     299           6 :     err = fd_snapshot_accv_index( restore->accv_map, &accounts_db );
     300             : 
     301             :   /* Discard superfluous fields that the callback didn't move */
     302             : 
     303           6 :   fd_bincode_destroy_ctx_t destroy = { .valloc = restore->valloc };
     304           6 :   fd_solana_accounts_db_fields_destroy( &accounts_db, &destroy );
     305             : 
     306             :   /* Discard buffer to reclaim heap space */
     307             : 
     308           6 :   fd_snapshot_restore_discard_buf( restore );
     309             : 
     310           6 :   restore->slot          = slot;
     311           6 :   restore->manifest_done = 1;
     312           6 :   return err;
     313           6 : }
     314             : 
     315             : /* fd_snapshot_restore_status_cache imports a status cache manifest into the
     316             :    given slot context. Destroys the existing status cache. */
     317             : 
     318             : static int
     319           6 : fd_snapshot_restore_status_cache( fd_snapshot_restore_t * restore ) {
     320             : 
     321           6 :   if( FD_UNLIKELY( !restore->cb_status_cache ) ) {
     322           0 :     fd_snapshot_restore_discard_buf( restore );
     323             : 
     324           0 :     restore->status_cache_done = 1;
     325           0 :     return 0;
     326           0 :   }
     327             :   /* Decode the status cache slot deltas and populate txn cache
     328             :      in slot ctx. */
     329             : 
     330           6 :   fd_bank_slot_deltas_t slot_deltas[1];
     331           6 :   fd_bincode_decode_ctx_t decode =
     332           6 :       { .data    = restore->buf,
     333           6 :         .dataend = restore->buf + restore->buf_sz,
     334           6 :         .valloc  = restore->valloc };
     335           6 :   int decode_err = fd_bank_slot_deltas_decode( slot_deltas, &decode );
     336           6 :   if( FD_UNLIKELY( decode_err!=FD_BINCODE_SUCCESS ) ) {
     337             :     /* TODO: The types generator does not yet handle OOM correctly.
     338             :              OOM failures won't always end up here, but could also
     339             :              result in a NULL pointer dereference. */
     340           3 :     FD_LOG_WARNING(( "fd_solana_manifest_decode failed (%d)", decode_err ));
     341           3 :     return EINVAL;
     342           3 :   }
     343             : 
     344             :   /* Move over objects and recover state. */
     345             :   /* TODO: we ignore the error from status cache restore for now since having the status cache is optional.
     346             :            Add status cache to all cases */
     347           3 :   restore->cb_status_cache( restore->cb_status_cache_ctx, slot_deltas );
     348             : 
     349             :   /* Discard superfluous fields that the callback didn't move */
     350             : 
     351           3 :   fd_bincode_destroy_ctx_t destroy = { .valloc = restore->valloc };
     352           3 :   fd_bank_slot_deltas_destroy( slot_deltas, &destroy );
     353             : 
     354             :   /* Discard buffer to reclaim heap space (which could be used by
     355             :      fd_funk accounts instead) */
     356             : 
     357           3 :   fd_snapshot_restore_discard_buf( restore );
     358             : 
     359           3 :   restore->status_cache_done = 1;
     360           3 :   return 0;
     361           6 : }
     362             : 
     363             : /* fd_snapshot_restore_accv_prepare prepares for consumption of an
     364             :    account vec file. */
     365             : 
     366             : static int
     367             : fd_snapshot_restore_accv_prepare( fd_snapshot_restore_t * const restore,
     368             :                                   fd_tar_meta_t const *   const meta,
     369          27 :                                   ulong                   const real_sz ) {
     370             : 
     371          27 :   if( FD_UNLIKELY( !fd_snapshot_restore_prepare_buf( restore, sizeof(fd_solana_account_hdr_t) ) ) ) {
     372           0 :     FD_LOG_WARNING(( "Failed to allocate read buffer while restoring accounts from snapshot" ));
     373           0 :     return ENOMEM;
     374           0 :   }
     375             : 
     376             :   /* Parse file name */
     377          27 :   ulong id, slot;
     378          27 :   if( FD_UNLIKELY( sscanf( meta->name, "accounts/%lu.%lu", &slot, &id )!=2 ) ) {
     379             :     /* Ignore entire file if file name invalid */
     380           0 :     restore->state  = STATE_DONE;
     381           0 :     restore->buf_sz = 0UL;
     382           0 :     return 0;
     383           0 :   }
     384             : 
     385             :   /* Reject if slot number is too high */
     386          27 :   if( FD_UNLIKELY( slot > restore->slot ) ) {
     387           3 :     FD_LOG_WARNING(( "%s has slot number %lu, which exceeds bank slot number %lu",
     388           3 :                      meta->name, slot, restore->slot ));
     389           3 :     restore->failed = 1;
     390           3 :     return EINVAL;
     391           3 :   }
     392             : 
     393             :   /* Lookup account vec file size */
     394          24 :   fd_snapshot_accv_key_t key = { .slot = slot, .id = id };
     395          24 :   fd_snapshot_accv_map_t * rec = fd_snapshot_accv_map_query( restore->accv_map, key, NULL );
     396          24 :   if( FD_UNLIKELY( !rec ) ) {
     397             :     /* Ignore account vec files that are not explicitly mentioned in the
     398             :        manifest. */
     399           0 :     FD_LOG_DEBUG(( "Ignoring %s (sz %lu)", meta->name, real_sz ));
     400           0 :     restore->state  = STATE_DONE;
     401           0 :     restore->buf_sz = 0UL;
     402           0 :     return 0;
     403           0 :   }
     404          24 :   ulong sz = rec->sz;
     405             : 
     406             :   /* Validate the supposed file size against real size */
     407          24 :   if( FD_UNLIKELY( sz > real_sz ) ) {
     408           3 :     FD_LOG_WARNING(( "AppendVec %lu.%lu is %lu bytes long according to manifest, but actually only %lu bytes",
     409           3 :                      slot, id, sz, real_sz ));
     410           3 :     restore->failed = 1;
     411           3 :     return EINVAL;
     412           3 :   }
     413          21 :   restore->accv_sz   = sz;
     414          21 :   restore->accv_slot = slot;
     415          21 :   restore->accv_id   = id;
     416             : 
     417             :   /* Prepare read of account header */
     418          21 :   FD_LOG_DEBUG(( "Loading account vec %s", meta->name ));
     419          21 :   return fd_snapshot_expect_account_hdr( restore );
     420          24 : }
     421             : 
     422             : /* fd_snapshot_restore_manifest_prepare prepares for consumption of the
     423             :    snapshot manifest. */
     424             : 
     425             : static int
     426             : fd_snapshot_restore_manifest_prepare( fd_snapshot_restore_t * restore,
     427          12 :                                       ulong                   sz ) {
     428             :   /* Only read once */
     429          12 :   if( restore->manifest_done ) {
     430           0 :     restore->state = STATE_IGNORE;
     431           0 :     return 0;
     432           0 :   }
     433             : 
     434             :   /* We don't support streaming manifest deserialization yet.  Thus,
     435             :      buffer the whole manifest in one place. */
     436          12 :   if( FD_UNLIKELY( !fd_snapshot_restore_prepare_buf( restore, sz ) ) ) {
     437           3 :     restore->failed = 1;
     438           3 :     return ENOMEM;
     439           3 :   }
     440             : 
     441           9 :   restore->state  = STATE_READ_MANIFEST;
     442           9 :   restore->buf_sz = sz;
     443             : 
     444           9 :   return 0;
     445          12 : }
     446             : 
     447             : /* fd_snapshot_restore_status_cache_prepare prepares for consumption of the
     448             :    status cache file. */
     449             : 
     450             : static int
     451             : fd_snapshot_restore_status_cache_prepare( fd_snapshot_restore_t * restore,
     452           9 :                                           ulong                   sz ) {
     453             :   /* Only read once */
     454           9 :   if( restore->status_cache_done ) {
     455           0 :     restore->state = STATE_IGNORE;
     456           0 :     return 0;
     457           0 :   }
     458             : 
     459             :   /* We don't support streaming manifest deserialization yet.  Thus,
     460             :      buffer the whole manifest in one place. */
     461           9 :   if( FD_UNLIKELY( !fd_snapshot_restore_prepare_buf( restore, sz ) ) ) {
     462           3 :     restore->failed = 1;
     463           3 :     return ENOMEM;
     464           3 :   }
     465             : 
     466           6 :   restore->state  = STATE_READ_STATUS_CACHE;
     467           6 :   restore->buf_sz = sz;
     468             : 
     469           6 :   return 0;
     470           9 : }
     471             : 
     472             : /* fd_snapshot_restore_file gets called by fd_tar before processing a
     473             :    new file.  We use this opportunity to init the state machine that
     474             :    will process the incoming file chunks, and set the buffer size if
     475             :    required. */
     476             : 
     477             : int
     478             : fd_snapshot_restore_file( void *                restore_,
     479             :                           fd_tar_meta_t const * meta,
     480          57 :                           ulong                 sz ) {
     481             : 
     482          57 :   fd_snapshot_restore_t * restore = restore_;
     483          57 :   if( restore->failed ) return EINVAL;
     484             : 
     485          54 :   restore->buf_ctr  = 0UL;   /* reset buffer */
     486          54 :   restore->acc_data = NULL;  /* reset account write state */
     487          54 :   restore->acc_sz   = 0UL;
     488          54 :   restore->acc_pad  = 0UL;
     489             : 
     490          54 :   if( (sz==0UL) | (!fd_tar_meta_is_reg( meta )) ) {
     491           3 :     restore->state = STATE_IGNORE;
     492           3 :     return 0;
     493           3 :   }
     494             : 
     495             :   /* Detect account vec files.  These are files that contain a vector
     496             :      of accounts in Solana Labs "AppendVec" format. */
     497          51 :   assert( sizeof("accounts/")<FD_TAR_NAME_SZ );
     498          51 :   if( 0==strncmp( meta->name, "accounts/", sizeof("accounts/")-1) ) {
     499          30 :     if( FD_UNLIKELY( !restore->manifest_done ) ) {
     500           3 :       FD_LOG_WARNING(( "Unsupported snapshot: encountered AppendVec before manifest" ));
     501           3 :       restore->failed = 1;
     502           3 :       return EINVAL;
     503           3 :     }
     504          27 :     return fd_snapshot_restore_accv_prepare( restore, meta, sz );
     505          30 :   }
     506             : 
     507             :   /* Snapshot manifest */
     508          21 :   assert( sizeof("snapshots/status_cache")<FD_TAR_NAME_SZ );
     509          21 :   if( 0==strncmp( meta->name, "snapshots/", sizeof("snapshots/")-1) &&
     510          21 :       0!=strcmp ( meta->name, "snapshots/status_cache" ) )
     511          12 :     return fd_snapshot_restore_manifest_prepare( restore, sz );
     512             : 
     513           9 :   else if( 0==strcmp ( meta->name, "snapshots/status_cache" ) )
     514           9 :     return fd_snapshot_restore_status_cache_prepare( restore, sz );
     515             : 
     516           0 :   restore->state = STATE_IGNORE;
     517           0 :   return 0;
     518          21 : }
     519             : 
     520             : /* fd_snapshot_read_buffered appends bytes to a buffer. */
     521             : 
     522             : static uchar const *
     523             : fd_snapshot_read_buffered( fd_snapshot_restore_t * restore,
     524             :                            uchar const *           buf,
     525         849 :                            ulong                   bufsz ) {
     526             :   /* Should not be called if read is complete */
     527         849 :   FD_TEST( restore->buf_ctr < restore->buf_sz );
     528             : 
     529             :   /* Determine number of bytes to buffer */
     530         849 :   ulong sz = restore->buf_sz - restore->buf_ctr;
     531         849 :   if( sz>bufsz ) sz = bufsz;
     532             : 
     533             :   /* Append to buffer */
     534         849 :   fd_memcpy( restore->buf + restore->buf_ctr, buf, sz );
     535         849 :   restore->buf_ctr += sz;
     536             : 
     537         849 :   return buf+sz;
     538         849 : }
     539             : 
     540             : /* fd_snapshot_read_is_complete returns 1 if all requested bytes have
     541             :    been buffered. */
     542             : 
     543             : FD_FN_PURE static inline int
     544         849 : fd_snapshot_read_is_complete( fd_snapshot_restore_t const * restore ) {
     545         849 :   return restore->buf_ctr == restore->buf_sz;
     546         849 : }
     547             : 
     548             : /* fd_snapshot_read_account_hdr_chunk reads a partial account header. */
     549             : 
     550             : static uchar const *
     551             : fd_snapshot_read_account_hdr_chunk( fd_snapshot_restore_t * restore,
     552             :                                     uchar const *           buf,
     553         834 :                                     ulong                   bufsz ) {
     554         834 :   if( !restore->accv_sz ) {
     555             :     /* Reached end of AppendVec */
     556           0 :     restore->state   = STATE_IGNORE;
     557           0 :     restore->buf_ctr = restore->buf_sz = 0UL;
     558           0 :     return buf;
     559           0 :   }
     560         834 :   bufsz = fd_ulong_min( bufsz, restore->accv_sz );
     561         834 :   uchar const * end = fd_snapshot_read_buffered( restore, buf, bufsz );
     562         834 :   restore->accv_sz -= (ulong)(end-buf);
     563         834 :   if( fd_snapshot_read_is_complete( restore ) )
     564          24 :     if( FD_UNLIKELY( 0!=fd_snapshot_restore_account_hdr( restore ) ) )
     565           6 :       return NULL;
     566         828 :   return end;
     567         834 : }
     568             : 
     569             : /* fd_snapshot_read_account_chunk reads partial account content. */
     570             : 
     571             : static uchar const *
     572             : fd_snapshot_read_account_chunk( fd_snapshot_restore_t * restore,
     573             :                                 uchar const *           buf,
     574          12 :                                 ulong                   bufsz ) {
     575             : 
     576          12 :   ulong data_sz = fd_ulong_min( restore->acc_sz, bufsz );
     577          12 :   if( FD_LIKELY( restore->acc_data ) ) {
     578           9 :     fd_memcpy( restore->acc_data, buf, data_sz );
     579           9 :     restore->acc_data += data_sz;
     580           9 :   }
     581          12 :   if( FD_UNLIKELY( data_sz > restore->accv_sz ) )
     582           0 :     FD_LOG_CRIT(( "OOB account vec read: data_sz=%lu accv_sz=%lu", data_sz, restore->accv_sz ));
     583             : 
     584          12 :   buf               += data_sz;
     585          12 :   bufsz             -= data_sz;
     586          12 :   restore->acc_sz   -= data_sz;
     587          12 :   restore->accv_sz  -= data_sz;
     588             : 
     589          12 :   if( restore->acc_sz == 0UL ) {
     590           9 :     ulong pad_sz = fd_ulong_min( fd_ulong_min( restore->acc_pad, bufsz ), restore->accv_sz );
     591           9 :     buf              += pad_sz;
     592           9 :     bufsz            -= pad_sz;
     593           9 :     restore->acc_pad -= pad_sz;
     594           9 :     restore->accv_sz -= pad_sz;
     595             : 
     596           9 :     if( restore->accv_sz == 0UL ) {
     597           6 :       restore->state = STATE_IGNORE;
     598           6 :       return buf;
     599           6 :     }
     600           3 :     if( restore->acc_pad == 0UL )
     601           3 :       return (0==fd_snapshot_expect_account_hdr( restore )) ? buf : NULL;
     602           3 :   }
     603             : 
     604           3 :   return buf;
     605          12 : }
     606             : 
     607             : /* fd_snapshot_read_manifest_chunk reads partial manifest content. */
     608             : 
     609             : static uchar const *
     610             : fd_snapshot_read_manifest_chunk( fd_snapshot_restore_t * restore,
     611             :                                  uchar const *           buf,
     612           9 :                                  ulong                   bufsz ) {
     613           9 :   uchar const * end = fd_snapshot_read_buffered( restore, buf, bufsz );
     614           9 :   if( fd_snapshot_read_is_complete( restore ) ) {
     615           9 :     int err = fd_snapshot_restore_manifest( restore );
     616           9 :     if( FD_UNLIKELY( err ) ) {
     617           3 :       FD_LOG_WARNING(( "fd_snapshot_restore_manifest failed" ));
     618           3 :       restore->failed = 1;
     619           3 :       return NULL;
     620           3 :     }
     621           6 :     restore->state = STATE_IGNORE;
     622           6 :   }
     623           6 :   return end;
     624           9 : }
     625             : 
     626             : /* fd_snapshot_read_status_cache_chunk reads partial status cache content. */
     627             : 
     628             : static uchar const *
     629             : fd_snapshot_read_status_cache_chunk( fd_snapshot_restore_t * restore,
     630             :                                      uchar const *           buf,
     631           6 :                                      ulong                   bufsz ) {
     632           6 :   uchar const * end = fd_snapshot_read_buffered( restore, buf, bufsz );
     633           6 :   if( fd_snapshot_read_is_complete( restore ) ) {
     634           6 :     int err = fd_snapshot_restore_status_cache( restore );
     635           6 :     if( FD_UNLIKELY( err ) ) {
     636           3 :       FD_LOG_WARNING(( "fd_snapshot_restore_status_cache failed" ));
     637           3 :       restore->failed = 1;
     638           3 :       return NULL;
     639           3 :     }
     640           3 :     restore->state = STATE_IGNORE;
     641           3 :   }
     642           3 :   return end;
     643           6 : }
     644             : 
     645             : /* fd_snapshot_restore_chunk1 consumes at least one byte from the given
     646             :    buffer (unless bufsz==0).  Returns pointer to first byte that has
     647             :    not been consumed yet. */
     648             : 
     649             : static uchar const *
     650             : fd_snapshot_restore_chunk1( fd_snapshot_restore_t * restore,
     651             :                             uchar const *           buf,
     652         867 :                             ulong                   bufsz ) {
     653             : 
     654         867 :   switch( restore->state ) {
     655           6 :   case STATE_IGNORE:
     656           6 :     return buf+bufsz;
     657           0 :   case STATE_DONE:
     658           0 :     FD_LOG_WARNING(( "unexpected trailing data" ));
     659           0 :     return NULL;
     660         834 :   case STATE_READ_ACCOUNT_HDR:
     661         834 :     return fd_snapshot_read_account_hdr_chunk  ( restore, buf, bufsz );
     662          12 :   case STATE_READ_ACCOUNT_DATA:
     663          12 :     return fd_snapshot_read_account_chunk      ( restore, buf, bufsz );
     664           9 :   case STATE_READ_MANIFEST:
     665           9 :     return fd_snapshot_read_manifest_chunk     ( restore, buf, bufsz );
     666           6 :   case STATE_READ_STATUS_CACHE:
     667           6 :     return fd_snapshot_read_status_cache_chunk ( restore, buf, bufsz );
     668           0 :   default:
     669           0 :     __builtin_unreachable();
     670         867 :   }
     671             : 
     672         867 : }
     673             : 
     674             : int
     675             : fd_snapshot_restore_chunk( void *       restore_,
     676             :                            void const * buf_,
     677         873 :                            ulong        bufsz ) {
     678             : 
     679         873 :   fd_snapshot_restore_t * restore = restore_;
     680         873 :   uchar const * buf               = buf_;
     681             : 
     682         873 :   if( restore->failed ) return EINVAL;
     683             : 
     684        1725 :   while( bufsz ) {
     685         867 :     uchar const * buf_new = fd_snapshot_restore_chunk1( restore, buf, bufsz );
     686         867 :     if( FD_UNLIKELY( !buf_new ) ) {
     687          12 :       FD_LOG_WARNING(( "Aborting snapshot read" ));
     688          12 :       return EINVAL;
     689          12 :     }
     690         855 :     bufsz -= (ulong)(buf_new-buf);
     691         855 :     buf    = buf_new;
     692         855 :   }
     693             : 
     694         858 :   return 0;
     695         870 : }
     696             : 
     697             : /* fd_snapshot_restore_t implements the consumer interface of a TAR
     698             :    reader. */
     699             : 
     700             : fd_tar_read_vtable_t const fd_snapshot_restore_tar_vt =
     701             :   { .file = fd_snapshot_restore_file,
     702             :     .read = fd_snapshot_restore_chunk };

Generated by: LCOV version 1.14