LCOV - code coverage report
Current view: top level - flamenco/snapshot - fd_snapshot_restore.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 348 411 84.7 %
Date: 2024-11-13 11:58:15 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           3 : fd_snapshot_restore_align( void ) {
      47           3 :   return fd_ulong_max( alignof(fd_snapshot_restore_t), fd_snapshot_accv_map_align() );
      48           3 : }
      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             :   /* Move over accounts DB fields */
     274             : 
     275           6 :   fd_solana_accounts_db_fields_t accounts_db = manifest->accounts_db;
     276           6 :   fd_memset( &manifest->accounts_db, 0, sizeof(fd_solana_accounts_db_fields_t) );
     277             : 
     278             :   /* Remember slot number */
     279             : 
     280           6 :   ulong slot = manifest->bank.slot;
     281             : 
     282             :   /* Move over objects and recover state
     283             :      This destroys all remaining fields with the slot context valloc. */
     284             : 
     285           6 :   int err = restore->cb_manifest( restore->cb_manifest_ctx, manifest );
     286             : 
     287             :   /* Read AccountVec map */
     288             : 
     289           6 :   if( FD_LIKELY( !err ) )
     290           6 :     err = fd_snapshot_accv_index( restore->accv_map, &accounts_db );
     291             : 
     292             :   /* Discard superfluous fields that the callback didn't move */
     293             : 
     294           6 :   fd_bincode_destroy_ctx_t destroy = { .valloc = restore->valloc };
     295           6 :   fd_solana_accounts_db_fields_destroy( &accounts_db, &destroy );
     296             : 
     297             :   /* Discard buffer to reclaim heap space */
     298             : 
     299           6 :   fd_snapshot_restore_discard_buf( restore );
     300             : 
     301           6 :   restore->slot          = slot;
     302           6 :   restore->manifest_done = 1;
     303           6 :   return err;
     304           9 : }
     305             : 
     306             : /* fd_snapshot_restore_status_cache imports a status cache manifest into the
     307             :    given slot context. Destroys the existing status cache. */
     308             : 
     309             : static int
     310           6 : fd_snapshot_restore_status_cache( fd_snapshot_restore_t * restore ) {
     311             : 
     312           6 :   if( FD_UNLIKELY( !restore->cb_status_cache ) ) {
     313           0 :     fd_snapshot_restore_discard_buf( restore );
     314             : 
     315           0 :     restore->status_cache_done = 1;
     316           0 :     return 0;
     317           0 :   }
     318             :   /* Decode the status cache slot deltas and populate txn cache
     319             :      in slot ctx. */
     320             : 
     321           6 :   fd_bank_slot_deltas_t slot_deltas[1];
     322           6 :   fd_bincode_decode_ctx_t decode =
     323           6 :       { .data    = restore->buf,
     324           6 :         .dataend = restore->buf + restore->buf_sz,
     325           6 :         .valloc  = restore->valloc };
     326           6 :   int decode_err = fd_bank_slot_deltas_decode( slot_deltas, &decode );
     327           6 :   if( FD_UNLIKELY( decode_err!=FD_BINCODE_SUCCESS ) ) {
     328             :     /* TODO: The types generator does not yet handle OOM correctly.
     329             :              OOM failures won't always end up here, but could also
     330             :              result in a NULL pointer dereference. */
     331           3 :     FD_LOG_WARNING(( "fd_solana_manifest_decode failed (%d)", decode_err ));
     332           3 :     return EINVAL;
     333           3 :   }
     334             : 
     335             :   /* Move over objects and recover state. */
     336             :   /* TODO: we ignore the error from status cache restore for now since having the status cache is optional.
     337             :            Add status cache to all cases */
     338           3 :   restore->cb_status_cache( restore->cb_status_cache_ctx, slot_deltas );
     339             : 
     340             :   /* Discard superfluous fields that the callback didn't move */
     341             : 
     342           3 :   fd_bincode_destroy_ctx_t destroy = { .valloc = restore->valloc };
     343           3 :   fd_bank_slot_deltas_destroy( slot_deltas, &destroy );
     344             : 
     345             :   /* Discard buffer to reclaim heap space (which could be used by
     346             :      fd_funk accounts instead) */
     347             : 
     348           3 :   fd_snapshot_restore_discard_buf( restore );
     349             : 
     350           3 :   restore->status_cache_done = 1;
     351           3 :   return 0;
     352           6 : }
     353             : 
     354             : /* fd_snapshot_restore_accv_prepare prepares for consumption of an
     355             :    account vec file. */
     356             : 
     357             : static int
     358             : fd_snapshot_restore_accv_prepare( fd_snapshot_restore_t * const restore,
     359             :                                   fd_tar_meta_t const *   const meta,
     360          27 :                                   ulong                   const real_sz ) {
     361             : 
     362          27 :   if( FD_UNLIKELY( !fd_snapshot_restore_prepare_buf( restore, sizeof(fd_solana_account_hdr_t) ) ) ) {
     363           0 :     FD_LOG_WARNING(( "Failed to allocate read buffer while restoring accounts from snapshot" ));
     364           0 :     return ENOMEM;
     365           0 :   }
     366             : 
     367             :   /* Parse file name */
     368          27 :   ulong id, slot;
     369          27 :   if( FD_UNLIKELY( sscanf( meta->name, "accounts/%lu.%lu", &slot, &id )!=2 ) ) {
     370             :     /* Ignore entire file if file name invalid */
     371           0 :     restore->state  = STATE_DONE;
     372           0 :     restore->buf_sz = 0UL;
     373           0 :     return 0;
     374           0 :   }
     375             : 
     376             :   /* Reject if slot number is too high */
     377          27 :   if( FD_UNLIKELY( slot > restore->slot ) ) {
     378           3 :     FD_LOG_WARNING(( "%s has slot number %lu, which exceeds bank slot number %lu",
     379           3 :                      meta->name, slot, restore->slot ));
     380           3 :     restore->failed = 1;
     381           3 :     return EINVAL;
     382           3 :   }
     383             : 
     384             :   /* Lookup account vec file size */
     385          24 :   fd_snapshot_accv_key_t key = { .slot = slot, .id = id };
     386          24 :   fd_snapshot_accv_map_t * rec = fd_snapshot_accv_map_query( restore->accv_map, key, NULL );
     387          24 :   if( FD_UNLIKELY( !rec ) ) {
     388             :     /* Ignore account vec files that are not explicitly mentioned in the
     389             :        manifest. */
     390           0 :     FD_LOG_DEBUG(( "Ignoring %s (sz %lu)", meta->name, real_sz ));
     391           0 :     restore->state  = STATE_DONE;
     392           0 :     restore->buf_sz = 0UL;
     393           0 :     return 0;
     394           0 :   }
     395          24 :   ulong sz = rec->sz;
     396             : 
     397             :   /* Validate the supposed file size against real size */
     398          24 :   if( FD_UNLIKELY( sz > real_sz ) ) {
     399           3 :     FD_LOG_WARNING(( "AppendVec %lu.%lu is %lu bytes long according to manifest, but actually only %lu bytes",
     400           3 :                      slot, id, sz, real_sz ));
     401           3 :     restore->failed = 1;
     402           3 :     return EINVAL;
     403           3 :   }
     404          21 :   restore->accv_sz   = sz;
     405          21 :   restore->accv_slot = slot;
     406          21 :   restore->accv_id   = id;
     407             : 
     408             :   /* Prepare read of account header */
     409          21 :   FD_LOG_DEBUG(( "Loading account vec %s", meta->name ));
     410          21 :   return fd_snapshot_expect_account_hdr( restore );
     411          24 : }
     412             : 
     413             : /* fd_snapshot_restore_manifest_prepare prepares for consumption of the
     414             :    snapshot manifest. */
     415             : 
     416             : static int
     417             : fd_snapshot_restore_manifest_prepare( fd_snapshot_restore_t * restore,
     418          12 :                                       ulong                   sz ) {
     419             :   /* Only read once */
     420          12 :   if( restore->manifest_done ) {
     421           0 :     restore->state = STATE_IGNORE;
     422           0 :     return 0;
     423           0 :   }
     424             : 
     425             :   /* We don't support streaming manifest deserialization yet.  Thus,
     426             :      buffer the whole manifest in one place. */
     427          12 :   if( FD_UNLIKELY( !fd_snapshot_restore_prepare_buf( restore, sz ) ) ) {
     428           3 :     restore->failed = 1;
     429           3 :     return ENOMEM;
     430           3 :   }
     431             : 
     432           9 :   restore->state  = STATE_READ_MANIFEST;
     433           9 :   restore->buf_sz = sz;
     434             : 
     435           9 :   return 0;
     436          12 : }
     437             : 
     438             : /* fd_snapshot_restore_status_cache_prepare prepares for consumption of the
     439             :    status cache file. */
     440             : 
     441             : static int
     442             : fd_snapshot_restore_status_cache_prepare( fd_snapshot_restore_t * restore,
     443           9 :                                           ulong                   sz ) {
     444             :   /* Only read once */
     445           9 :   if( restore->status_cache_done ) {
     446           0 :     restore->state = STATE_IGNORE;
     447           0 :     return 0;
     448           0 :   }
     449             : 
     450             :   /* We don't support streaming manifest deserialization yet.  Thus,
     451             :      buffer the whole manifest in one place. */
     452           9 :   if( FD_UNLIKELY( !fd_snapshot_restore_prepare_buf( restore, sz ) ) ) {
     453           3 :     restore->failed = 1;
     454           3 :     return ENOMEM;
     455           3 :   }
     456             : 
     457           6 :   restore->state  = STATE_READ_STATUS_CACHE;
     458           6 :   restore->buf_sz = sz;
     459             : 
     460           6 :   return 0;
     461           9 : }
     462             : 
     463             : /* fd_snapshot_restore_file gets called by fd_tar before processing a
     464             :    new file.  We use this opportunity to init the state machine that
     465             :    will process the incoming file chunks, and set the buffer size if
     466             :    required. */
     467             : 
     468             : int
     469             : fd_snapshot_restore_file( void *                restore_,
     470             :                           fd_tar_meta_t const * meta,
     471          57 :                           ulong                 sz ) {
     472             : 
     473          57 :   fd_snapshot_restore_t * restore = restore_;
     474          57 :   if( restore->failed ) return EINVAL;
     475             : 
     476          54 :   restore->buf_ctr  = 0UL;   /* reset buffer */
     477          54 :   restore->acc_data = NULL;  /* reset account write state */
     478          54 :   restore->acc_sz   = 0UL;
     479          54 :   restore->acc_pad  = 0UL;
     480             : 
     481          54 :   if( (sz==0UL) | (!fd_tar_meta_is_reg( meta )) ) {
     482           3 :     restore->state = STATE_IGNORE;
     483           3 :     return 0;
     484           3 :   }
     485             : 
     486             :   /* Detect account vec files.  These are files that contain a vector
     487             :      of accounts in Solana Labs "AppendVec" format. */
     488          51 :   assert( sizeof("accounts/")<FD_TAR_NAME_SZ );
     489          51 :   if( 0==strncmp( meta->name, "accounts/", sizeof("accounts/")-1) ) {
     490          30 :     if( FD_UNLIKELY( !restore->manifest_done ) ) {
     491           3 :       FD_LOG_WARNING(( "Unsupported snapshot: encountered AppendVec before manifest" ));
     492           3 :       restore->failed = 1;
     493           3 :       return EINVAL;
     494           3 :     }
     495          27 :     return fd_snapshot_restore_accv_prepare( restore, meta, sz );
     496          30 :   }
     497             : 
     498             :   /* Snapshot manifest */
     499          21 :   assert( sizeof("snapshots/status_cache")<FD_TAR_NAME_SZ );
     500          21 :   if( 0==strncmp( meta->name, "snapshots/", sizeof("snapshots/")-1) &&
     501          21 :       0!=strcmp ( meta->name, "snapshots/status_cache" ) )
     502          12 :     return fd_snapshot_restore_manifest_prepare( restore, sz );
     503             : 
     504           9 :   else if( 0==strcmp ( meta->name, "snapshots/status_cache" ) )
     505           9 :     return fd_snapshot_restore_status_cache_prepare( restore, sz );
     506             : 
     507           0 :   restore->state = STATE_IGNORE;
     508           0 :   return 0;
     509          21 : }
     510             : 
     511             : /* fd_snapshot_read_buffered appends bytes to a buffer. */
     512             : 
     513             : static uchar const *
     514             : fd_snapshot_read_buffered( fd_snapshot_restore_t * restore,
     515             :                            uchar const *           buf,
     516         849 :                            ulong                   bufsz ) {
     517             :   /* Should not be called if read is complete */
     518         849 :   FD_TEST( restore->buf_ctr < restore->buf_sz );
     519             : 
     520             :   /* Determine number of bytes to buffer */
     521         849 :   ulong sz = restore->buf_sz - restore->buf_ctr;
     522         849 :   if( sz>bufsz ) sz = bufsz;
     523             : 
     524             :   /* Append to buffer */
     525         849 :   fd_memcpy( restore->buf + restore->buf_ctr, buf, sz );
     526         849 :   restore->buf_ctr += sz;
     527             : 
     528         849 :   return buf+sz;
     529         849 : }
     530             : 
     531             : /* fd_snapshot_read_is_complete returns 1 if all requested bytes have
     532             :    been buffered. */
     533             : 
     534             : FD_FN_PURE static inline int
     535         849 : fd_snapshot_read_is_complete( fd_snapshot_restore_t const * restore ) {
     536         849 :   return restore->buf_ctr == restore->buf_sz;
     537         849 : }
     538             : 
     539             : /* fd_snapshot_read_account_hdr_chunk reads a partial account header. */
     540             : 
     541             : static uchar const *
     542             : fd_snapshot_read_account_hdr_chunk( fd_snapshot_restore_t * restore,
     543             :                                     uchar const *           buf,
     544         834 :                                     ulong                   bufsz ) {
     545         834 :   if( !restore->accv_sz ) {
     546             :     /* Reached end of AppendVec */
     547           0 :     restore->state   = STATE_IGNORE;
     548           0 :     restore->buf_ctr = restore->buf_sz = 0UL;
     549           0 :     return buf;
     550           0 :   }
     551         834 :   bufsz = fd_ulong_min( bufsz, restore->accv_sz );
     552         834 :   uchar const * end = fd_snapshot_read_buffered( restore, buf, bufsz );
     553         834 :   restore->accv_sz -= (ulong)(end-buf);
     554         834 :   if( fd_snapshot_read_is_complete( restore ) )
     555          24 :     if( FD_UNLIKELY( 0!=fd_snapshot_restore_account_hdr( restore ) ) )
     556           6 :       return NULL;
     557         828 :   return end;
     558         834 : }
     559             : 
     560             : /* fd_snapshot_read_account_chunk reads partial account content. */
     561             : 
     562             : static uchar const *
     563             : fd_snapshot_read_account_chunk( fd_snapshot_restore_t * restore,
     564             :                                 uchar const *           buf,
     565          12 :                                 ulong                   bufsz ) {
     566             : 
     567          12 :   ulong data_sz = fd_ulong_min( restore->acc_sz, bufsz );
     568          12 :   if( FD_LIKELY( restore->acc_data ) ) {
     569           9 :     fd_memcpy( restore->acc_data, buf, data_sz );
     570           9 :     restore->acc_data += data_sz;
     571           9 :   }
     572          12 :   if( FD_UNLIKELY( data_sz > restore->accv_sz ) )
     573           0 :     FD_LOG_CRIT(( "OOB account vec read: data_sz=%lu accv_sz=%lu", data_sz, restore->accv_sz ));
     574             : 
     575          12 :   buf               += data_sz;
     576          12 :   bufsz             -= data_sz;
     577          12 :   restore->acc_sz   -= data_sz;
     578          12 :   restore->accv_sz  -= data_sz;
     579             : 
     580          12 :   if( restore->acc_sz == 0UL ) {
     581           9 :     ulong pad_sz = fd_ulong_min( fd_ulong_min( restore->acc_pad, bufsz ), restore->accv_sz );
     582           9 :     buf              += pad_sz;
     583           9 :     bufsz            -= pad_sz;
     584           9 :     restore->acc_pad -= pad_sz;
     585           9 :     restore->accv_sz -= pad_sz;
     586             : 
     587           9 :     if( restore->accv_sz == 0UL ) {
     588           6 :       restore->state = STATE_IGNORE;
     589           6 :       return buf;
     590           6 :     }
     591           3 :     if( restore->acc_pad == 0UL )
     592           3 :       return (0==fd_snapshot_expect_account_hdr( restore )) ? buf : NULL;
     593           3 :   }
     594             : 
     595           3 :   return buf;
     596          12 : }
     597             : 
     598             : /* fd_snapshot_read_manifest_chunk reads partial manifest content. */
     599             : 
     600             : static uchar const *
     601             : fd_snapshot_read_manifest_chunk( fd_snapshot_restore_t * restore,
     602             :                                  uchar const *           buf,
     603           9 :                                  ulong                   bufsz ) {
     604           9 :   uchar const * end = fd_snapshot_read_buffered( restore, buf, bufsz );
     605           9 :   if( fd_snapshot_read_is_complete( restore ) ) {
     606           9 :     int err = fd_snapshot_restore_manifest( restore );
     607           9 :     if( FD_UNLIKELY( err ) ) {
     608           3 :       FD_LOG_WARNING(( "fd_snapshot_restore_manifest failed" ));
     609           3 :       restore->failed = 1;
     610           3 :       return NULL;
     611           3 :     }
     612           6 :     restore->state = STATE_IGNORE;
     613           6 :   }
     614           6 :   return end;
     615           9 : }
     616             : 
     617             : /* fd_snapshot_read_status_cache_chunk reads partial status cache content. */
     618             : 
     619             : static uchar const *
     620             : fd_snapshot_read_status_cache_chunk( fd_snapshot_restore_t * restore,
     621             :                                      uchar const *           buf,
     622           6 :                                      ulong                   bufsz ) {
     623           6 :   uchar const * end = fd_snapshot_read_buffered( restore, buf, bufsz );
     624           6 :   if( fd_snapshot_read_is_complete( restore ) ) {
     625           6 :     int err = fd_snapshot_restore_status_cache( restore );
     626           6 :     if( FD_UNLIKELY( err ) ) {
     627           3 :       FD_LOG_WARNING(( "fd_snapshot_restore_status_cache failed" ));
     628           3 :       restore->failed = 1;
     629           3 :       return NULL;
     630           3 :     }
     631           3 :     restore->state = STATE_IGNORE;
     632           3 :   }
     633           3 :   return end;
     634           6 : }
     635             : 
     636             : /* fd_snapshot_restore_chunk1 consumes at least one byte from the given
     637             :    buffer (unless bufsz==0).  Returns pointer to first byte that has
     638             :    not been consumed yet. */
     639             : 
     640             : static uchar const *
     641             : fd_snapshot_restore_chunk1( fd_snapshot_restore_t * restore,
     642             :                             uchar const *           buf,
     643         867 :                             ulong                   bufsz ) {
     644             : 
     645         867 :   switch( restore->state ) {
     646           6 :   case STATE_IGNORE:
     647           6 :     return buf+bufsz;
     648           0 :   case STATE_DONE:
     649           0 :     FD_LOG_WARNING(( "unexpected trailing data" ));
     650           0 :     return NULL;
     651         834 :   case STATE_READ_ACCOUNT_HDR:
     652         834 :     return fd_snapshot_read_account_hdr_chunk  ( restore, buf, bufsz );
     653          12 :   case STATE_READ_ACCOUNT_DATA:
     654          12 :     return fd_snapshot_read_account_chunk      ( restore, buf, bufsz );
     655           9 :   case STATE_READ_MANIFEST:
     656           9 :     return fd_snapshot_read_manifest_chunk     ( restore, buf, bufsz );
     657           6 :   case STATE_READ_STATUS_CACHE:
     658           6 :     return fd_snapshot_read_status_cache_chunk ( restore, buf, bufsz );
     659           0 :   default:
     660           0 :     __builtin_unreachable();
     661         867 :   }
     662             : 
     663         867 : }
     664             : 
     665             : int
     666             : fd_snapshot_restore_chunk( void *       restore_,
     667             :                            void const * buf_,
     668         873 :                            ulong        bufsz ) {
     669             : 
     670         873 :   fd_snapshot_restore_t * restore = restore_;
     671         873 :   uchar const * buf               = buf_;
     672             : 
     673         873 :   if( restore->failed ) return EINVAL;
     674             : 
     675        1725 :   while( bufsz ) {
     676         867 :     uchar const * buf_new = fd_snapshot_restore_chunk1( restore, buf, bufsz );
     677         867 :     if( FD_UNLIKELY( !buf_new ) ) {
     678          12 :       FD_LOG_WARNING(( "Aborting snapshot read" ));
     679          12 :       return EINVAL;
     680          12 :     }
     681         855 :     bufsz -= (ulong)(buf_new-buf);
     682         855 :     buf    = buf_new;
     683         855 :   }
     684             : 
     685         858 :   return 0;
     686         870 : }
     687             : 
     688             : /* fd_snapshot_restore_t implements the consumer interface of a TAR
     689             :    reader. */
     690             : 
     691             : fd_tar_read_vtable_t const fd_snapshot_restore_tar_vt =
     692             :   { .file = fd_snapshot_restore_file,
     693             :     .read = fd_snapshot_restore_chunk };

Generated by: LCOV version 1.14