LCOV - code coverage report
Current view: top level - flamenco/snapshot - fd_snapshot_restore.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 352 427 82.4 %
Date: 2025-07-01 05:00:49 Functions: 23 24 95.8 %

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

Generated by: LCOV version 1.14