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

Generated by: LCOV version 1.14