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