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