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