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