Line data Source code
1 : #include "fd_ssparse.h"
2 :
3 : #include "../../../util/log/fd_log.h"
4 : #include "../../../util/archive/fd_tar.h"
5 : #include "../../../flamenco/runtime/fd_runtime_const.h"
6 :
7 : #include <stdio.h>
8 :
9 0 : #define FD_SSPARSE_STATE_TAR_HEADER (0)
10 0 : #define FD_SSPARSE_STATE_SCROLL_TAR_HEADER (1)
11 0 : #define FD_SSPARSE_STATE_VERSION (2)
12 0 : #define FD_SSPARSE_STATE_MANIFEST (3)
13 0 : #define FD_SSPARSE_STATE_ACCOUNT_HEADER (4)
14 0 : #define FD_SSPARSE_STATE_ACCOUNT_DATA (5)
15 0 : #define FD_SSPARSE_STATE_ACCOUNT_PADDING (6)
16 0 : #define FD_SSPARSE_STATE_STATUS_CACHE (7)
17 0 : #define FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE (8)
18 :
19 : struct fd_ssparse_private {
20 : int state;
21 :
22 : struct {
23 : int seen_zero_tar_frame;
24 : int seen_manifest;
25 : int seen_status_cache;
26 : int seen_version;
27 : } flags;
28 :
29 : uchar version[ 5UL ];
30 :
31 : struct {
32 : acc_vec_map_t * acc_vec_map;
33 : acc_vec_t * acc_vec_pool;
34 : } manifest;
35 :
36 : struct {
37 : uchar header[ 512UL ];
38 : ulong file_bytes;
39 : ulong file_bytes_consumed;
40 : ulong header_bytes_consumed;
41 : } tar;
42 :
43 : struct {
44 : uchar header[ 136UL ];
45 : ulong header_bytes_consumed;
46 : ulong data_bytes_consumed;
47 : ulong data_len;
48 : } account;
49 :
50 : ulong acc_vec_bytes;
51 : ulong slot;
52 : ulong bytes_consumed;
53 :
54 : ulong seed;
55 : ulong max_acc_vecs;
56 : ulong magic;
57 : };
58 :
59 : FD_FN_CONST ulong
60 0 : fd_ssparse_align( void ) {
61 0 : return fd_ulong_max( alignof(fd_ssparse_t), fd_ulong_max( acc_vec_pool_align(), acc_vec_map_align() ) );
62 0 : }
63 :
64 : FD_FN_CONST ulong
65 0 : fd_ssparse_footprint( ulong max_acc_vecs ) {
66 0 : ulong l = FD_LAYOUT_INIT;
67 0 : l = FD_LAYOUT_APPEND( l, fd_ssparse_align(), sizeof(fd_ssparse_t) );
68 0 : l = FD_LAYOUT_APPEND( l, acc_vec_pool_align(), acc_vec_pool_footprint( max_acc_vecs ) );
69 0 : l = FD_LAYOUT_APPEND( l, acc_vec_map_align(), acc_vec_map_footprint( max_acc_vecs ) );
70 0 : return FD_LAYOUT_FINI( l, fd_ssparse_align() );
71 0 : }
72 :
73 : void *
74 : fd_ssparse_new( void * shmem,
75 : ulong max_acc_vecs,
76 0 : ulong seed ) {
77 0 : if( FD_UNLIKELY( !shmem ) ) {
78 0 : FD_LOG_WARNING(( "NULL shmem" ));
79 0 : return NULL;
80 0 : }
81 :
82 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_ssparse_align() ) ) ) {
83 0 : FD_LOG_WARNING(( "unaligned shmem" ));
84 0 : return NULL;
85 0 : }
86 :
87 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
88 0 : fd_ssparse_t * ssparse = FD_SCRATCH_ALLOC_APPEND( l, fd_ssparse_align(), sizeof(fd_ssparse_t) );
89 0 : void * _acc_vec_pool = FD_SCRATCH_ALLOC_APPEND( l, acc_vec_pool_align(), acc_vec_pool_footprint( max_acc_vecs ) );
90 0 : void * _acc_vec_map = FD_SCRATCH_ALLOC_APPEND( l, acc_vec_map_align(), acc_vec_map_footprint( max_acc_vecs ) );
91 :
92 0 : ssparse->manifest.acc_vec_pool = acc_vec_pool_join( acc_vec_pool_new( _acc_vec_pool, max_acc_vecs ) );
93 0 : FD_TEST( ssparse->manifest.acc_vec_pool );
94 :
95 0 : ssparse->manifest.acc_vec_map = acc_vec_map_join( acc_vec_map_new( _acc_vec_map, max_acc_vecs, seed ) );
96 0 : FD_TEST( ssparse->manifest.acc_vec_map );
97 :
98 0 : ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
99 0 : fd_memset( &ssparse->flags, 0, sizeof(ssparse->flags) );
100 :
101 0 : ssparse->bytes_consumed = 0UL;
102 0 : ssparse->seed = seed;
103 0 : ssparse->max_acc_vecs = max_acc_vecs;
104 :
105 0 : ssparse->tar.header_bytes_consumed = 0UL;
106 0 : ssparse->tar.file_bytes_consumed = 0UL;
107 0 : ssparse->tar.file_bytes = 0UL;
108 :
109 0 : ssparse->account.header_bytes_consumed = 0UL;
110 0 : ssparse->account.data_bytes_consumed = 0UL;
111 0 : ssparse->account.data_len = 0UL;
112 0 : ssparse->acc_vec_bytes = 0UL;
113 :
114 0 : FD_COMPILER_MFENCE();
115 0 : ssparse->magic = FD_SSPARSE_MAGIC;
116 0 : FD_COMPILER_MFENCE();
117 :
118 0 : return (void *)ssparse;
119 0 : }
120 :
121 : fd_ssparse_t *
122 0 : fd_ssparse_join( void * shssparse ) {
123 0 : if( FD_UNLIKELY( !shssparse ) ) {
124 0 : FD_LOG_WARNING(( "NULL shssparse" ));
125 0 : return NULL;
126 0 : }
127 :
128 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shssparse, fd_ssparse_align() ) ) ) {
129 0 : FD_LOG_WARNING(( "misaligned shssparse" ));
130 0 : return NULL;
131 0 : }
132 :
133 0 : fd_ssparse_t * ssparse = (fd_ssparse_t *)shssparse;
134 :
135 0 : if( FD_UNLIKELY( ssparse->magic!=FD_SSPARSE_MAGIC ) ) {
136 0 : FD_LOG_WARNING(( "bad magic" ));
137 0 : return NULL;
138 0 : }
139 :
140 0 : return ssparse;
141 0 : }
142 :
143 : void
144 0 : fd_ssparse_reset( fd_ssparse_t * ssparse ) {
145 0 : ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
146 0 : fd_memset( &ssparse->flags, 0, sizeof(ssparse->flags) );
147 0 : ssparse->bytes_consumed = 0UL;
148 :
149 0 : ssparse->tar.header_bytes_consumed = 0UL;
150 0 : ssparse->tar.file_bytes_consumed = 0UL;
151 0 : ssparse->tar.file_bytes = 0UL;
152 :
153 0 : ssparse->account.header_bytes_consumed = 0UL;
154 0 : ssparse->account.data_bytes_consumed = 0UL;
155 0 : ssparse->account.data_len = 0UL;
156 0 : ssparse->acc_vec_bytes = 0UL;
157 :
158 0 : acc_vec_map_reset( ssparse->manifest.acc_vec_map );
159 0 : acc_vec_pool_reset( ssparse->manifest.acc_vec_pool );
160 0 : }
161 :
162 : static int
163 : advance_tar( fd_ssparse_t * ssparse,
164 : uchar const * data,
165 : ulong data_sz,
166 0 : fd_ssparse_advance_result_t * result ) {
167 0 : ulong consume = fd_ulong_min( data_sz, 512UL - ssparse->tar.header_bytes_consumed );
168 0 : if( FD_LIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
169 :
170 0 : fd_memcpy( ssparse->tar.header+ssparse->tar.header_bytes_consumed, data, consume );
171 0 : ssparse->bytes_consumed += consume;
172 0 : result->bytes_consumed = consume;
173 0 : ssparse->tar.header_bytes_consumed += consume;
174 :
175 0 : if( FD_UNLIKELY( ssparse->tar.header_bytes_consumed<512UL ) ) return FD_SSPARSE_ADVANCE_AGAIN;
176 :
177 0 : fd_tar_meta_t const * hdr = (fd_tar_meta_t const *)ssparse->tar.header;
178 0 : ssparse->tar.header_bytes_consumed = 0UL;
179 :
180 : /* "ustar\x00" and "ustar \x00" (overlaps with version) are both
181 : valid values for magic. These are POSIX ustar and OLDGNU versions
182 : respectively. */
183 0 : if( FD_UNLIKELY( memcmp( hdr->magic, FD_TAR_MAGIC, 5UL ) ) ) {
184 0 : int not_zero = 0;
185 0 : for( ulong i=0UL; i<512UL; i++ ) not_zero |= ssparse->tar.header[ i ];
186 0 : if( FD_UNLIKELY( not_zero ) ) {
187 0 : FD_LOG_WARNING(( "invalid tar header magic `%s`", hdr->magic ));
188 0 : return FD_SSPARSE_ADVANCE_ERROR;
189 0 : }
190 :
191 0 : if( FD_LIKELY( ssparse->flags.seen_zero_tar_frame ) ) {
192 0 : if( FD_UNLIKELY( !ssparse->flags.seen_version || !ssparse->flags.seen_manifest || !ssparse->flags.seen_status_cache ) ) {
193 0 : FD_LOG_WARNING(( "unexpected end of file before version or manifest or status cache" ));
194 0 : return FD_SSPARSE_ADVANCE_ERROR;
195 0 : }
196 :
197 0 : return FD_SSPARSE_ADVANCE_DONE;
198 0 : }
199 :
200 0 : ssparse->flags.seen_zero_tar_frame = 1;
201 0 : return FD_SSPARSE_ADVANCE_AGAIN;
202 0 : }
203 :
204 0 : if( FD_UNLIKELY( ssparse->flags.seen_zero_tar_frame ) ) {
205 0 : FD_LOG_WARNING(( "unexpected valid tar header after zero frame" ));
206 0 : return FD_SSPARSE_ADVANCE_ERROR;
207 0 : }
208 :
209 0 : ssparse->tar.file_bytes = fd_tar_meta_get_size( hdr );
210 0 : if( FD_UNLIKELY( ssparse->tar.file_bytes==ULONG_MAX ) ) {
211 0 : FD_LOG_WARNING(( "invalid tar header size %lu", ssparse->tar.file_bytes ));
212 0 : return FD_SSPARSE_ADVANCE_ERROR;
213 0 : }
214 :
215 0 : if( FD_UNLIKELY( hdr->typeflag==FD_TAR_TYPE_DIR ) ) return FD_SSPARSE_ADVANCE_AGAIN;
216 :
217 0 : if( FD_UNLIKELY( !fd_tar_meta_is_reg( hdr ) ) ) {
218 0 : FD_LOG_WARNING(( "invalid tar header type %d", hdr->typeflag ));
219 0 : return FD_SSPARSE_ADVANCE_ERROR;
220 0 : }
221 0 : if( FD_UNLIKELY( !ssparse->tar.file_bytes ) ) {
222 0 : FD_LOG_WARNING(( "invalid tar header size %lu", ssparse->tar.file_bytes ));
223 0 : return FD_SSPARSE_ADVANCE_ERROR;
224 0 : }
225 :
226 : /* TODO: Check every header field here for validity? */
227 :
228 0 : int desired_state;
229 0 : if( FD_LIKELY( !strncmp( hdr->name, "version", 7UL ) ) ) {
230 0 : desired_state = FD_SSPARSE_STATE_VERSION;
231 0 : if( FD_UNLIKELY( ssparse->tar.file_bytes!=5UL ) ) {
232 0 : FD_LOG_WARNING(( "invalid version file size %lu", ssparse->tar.file_bytes ));
233 0 : return FD_SSPARSE_ADVANCE_ERROR;
234 0 : }
235 0 : } else if( FD_LIKELY( !strncmp( hdr->name, "accounts/", 9UL ) ) ) {
236 0 : ssparse->account.header_bytes_consumed = 0UL;
237 0 : desired_state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
238 0 : ulong id, slot;
239 0 : if( FD_UNLIKELY( sscanf( hdr->name, "accounts/%lu.%lu", &slot, &id )!=2 ) ) {
240 0 : FD_LOG_WARNING(( "invalid account append vec name %s", hdr->name ));
241 0 : return FD_SSPARSE_ADVANCE_ERROR;
242 0 : }
243 :
244 0 : acc_vec_key_t key = { .slot = slot, .id = id };
245 0 : acc_vec_t const * acc_vec = acc_vec_map_ele_query_const( ssparse->manifest.acc_vec_map, &key, NULL, ssparse->manifest.acc_vec_pool );
246 0 : if( FD_UNLIKELY( !acc_vec ) ) {
247 0 : FD_LOG_WARNING(( "append vec %lu.%lu not found in manifest", slot, id ));
248 0 : return FD_SSPARSE_ADVANCE_ERROR;
249 0 : }
250 :
251 0 : ssparse->acc_vec_bytes = acc_vec->file_sz;
252 0 : if( FD_UNLIKELY( ssparse->acc_vec_bytes>ssparse->tar.file_bytes ) ) {
253 0 : FD_LOG_WARNING(( "invalid append vec file size %lu > %lu", ssparse->acc_vec_bytes, ssparse->tar.file_bytes ));
254 0 : return FD_SSPARSE_ADVANCE_ERROR;
255 0 : }
256 :
257 0 : ssparse->slot = slot;
258 0 : } else if( FD_LIKELY( !strncmp( hdr->name, "snapshots/status_cache", 22UL ) ) ) desired_state = FD_SSPARSE_STATE_STATUS_CACHE;
259 0 : else if( FD_LIKELY( !strncmp( hdr->name, "snapshots/", 10UL ) ) ) {
260 0 : desired_state = FD_SSPARSE_STATE_MANIFEST;
261 0 : } else {
262 0 : FD_LOG_WARNING(( "unexpected tar header name %s", hdr->name ));
263 0 : return FD_SSPARSE_ADVANCE_ERROR;
264 0 : }
265 :
266 0 : ssparse->tar.file_bytes_consumed = 0UL;
267 :
268 0 : switch( desired_state ) {
269 0 : case FD_SSPARSE_STATE_VERSION:
270 0 : if( FD_UNLIKELY( ssparse->flags.seen_version ) ) {
271 0 : FD_LOG_WARNING(( "unexpected duplicate verison file" ));
272 0 : return FD_SSPARSE_ADVANCE_ERROR;
273 0 : }
274 :
275 0 : ssparse->flags.seen_version = 1;
276 0 : ssparse->state = FD_SSPARSE_STATE_VERSION;
277 0 : break;
278 0 : case FD_SSPARSE_STATE_MANIFEST:
279 0 : if( FD_UNLIKELY( ssparse->flags.seen_manifest ) ) {
280 0 : FD_LOG_WARNING(( "unexpected duplicate manifest file" ));
281 0 : return FD_SSPARSE_ADVANCE_ERROR;
282 0 : }
283 :
284 0 : ssparse->flags.seen_manifest = 1;
285 0 : ssparse->state = FD_SSPARSE_STATE_MANIFEST;
286 0 : break;
287 0 : case FD_SSPARSE_STATE_ACCOUNT_HEADER:
288 0 : if( FD_UNLIKELY( !ssparse->flags.seen_manifest ) ) {
289 0 : FD_LOG_WARNING(( "unexpected account append vec file before manifest" ));
290 0 : return FD_SSPARSE_ADVANCE_ERROR;
291 0 : }
292 :
293 0 : ssparse->account.header_bytes_consumed = 0UL;
294 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
295 0 : break;
296 0 : case FD_SSPARSE_STATE_STATUS_CACHE:
297 0 : if( FD_UNLIKELY( ssparse->flags.seen_status_cache ) ) {
298 0 : FD_LOG_WARNING(( "unexpected status cache file" ));
299 0 : return FD_SSPARSE_ADVANCE_ERROR;
300 0 : }
301 :
302 0 : ssparse->flags.seen_status_cache = 1;
303 0 : ssparse->state = FD_SSPARSE_STATE_STATUS_CACHE;
304 0 : break;
305 0 : default:
306 0 : FD_LOG_ERR(( "unexpected tar header desired state %d", desired_state ));
307 0 : break;
308 0 : }
309 :
310 0 : return FD_SSPARSE_ADVANCE_AGAIN;
311 0 : }
312 :
313 : static int
314 : advance_version( fd_ssparse_t * ssparse,
315 : uchar const * data,
316 : ulong data_sz,
317 0 : fd_ssparse_advance_result_t * result ) {
318 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
319 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
320 :
321 0 : fd_memcpy( ssparse->version+ssparse->tar.file_bytes_consumed, data, consume );
322 :
323 0 : ssparse->tar.file_bytes_consumed += consume;
324 0 : ssparse->bytes_consumed += consume;
325 0 : result->bytes_consumed = consume;
326 :
327 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) return FD_SSPARSE_ADVANCE_AGAIN;
328 :
329 0 : FD_TEST( ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes );
330 0 : FD_TEST( ssparse->tar.file_bytes_consumed==5UL );
331 :
332 0 : if( FD_UNLIKELY( memcmp( ssparse->version, "1.2.0", 5UL ) ) ) {
333 0 : FD_LOG_WARNING(( "invalid version file %s", ssparse->version ));
334 0 : return FD_SSPARSE_ADVANCE_ERROR;
335 0 : }
336 :
337 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
338 0 : return FD_SSPARSE_ADVANCE_AGAIN;
339 0 : }
340 :
341 : static int
342 : advance_status_cache( fd_ssparse_t * ssparse,
343 : uchar const * data,
344 : ulong data_sz,
345 0 : fd_ssparse_advance_result_t * result ) {
346 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
347 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
348 :
349 0 : ssparse->tar.file_bytes_consumed += consume;
350 0 : ssparse->bytes_consumed += consume;
351 :
352 0 : result->bytes_consumed = consume;
353 0 : result->status_cache.data = data;
354 0 : result->status_cache.data_sz = consume;
355 :
356 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) {
357 0 : return FD_SSPARSE_ADVANCE_STATUS_CACHE;
358 0 : }
359 0 : else { /* ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes */
360 : /* finished parsing status cache */
361 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
362 0 : return FD_SSPARSE_ADVANCE_STATUS_CACHE;
363 0 : }
364 0 : }
365 :
366 : static int
367 : advance_manifest( fd_ssparse_t * ssparse,
368 : uchar const * data,
369 : ulong data_sz,
370 0 : fd_ssparse_advance_result_t * result ) {
371 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
372 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
373 :
374 0 : ssparse->tar.file_bytes_consumed += consume;
375 0 : ssparse->bytes_consumed += consume;
376 :
377 0 : result->bytes_consumed = consume;
378 0 : result->manifest.data = data;
379 0 : result->manifest.data_sz = consume;
380 0 : result->manifest.acc_vec_map = ssparse->manifest.acc_vec_map;
381 0 : result->manifest.acc_vec_pool = ssparse->manifest.acc_vec_pool;
382 :
383 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) {
384 0 : return FD_SSPARSE_ADVANCE_MANIFEST;
385 0 : }
386 0 : else { /* ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes */
387 : /* finished parsing manifest */
388 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
389 0 : return FD_SSPARSE_ADVANCE_MANIFEST;
390 0 : }
391 0 : }
392 :
393 : static int
394 : advance_next_tar( fd_ssparse_t * ssparse,
395 : uchar const * data,
396 : ulong data_sz,
397 0 : fd_ssparse_advance_result_t * result ) {
398 0 : (void)data;
399 : /* skip padding */
400 0 : ulong bytes_remaining = fd_ulong_align_up( ssparse->bytes_consumed, 512UL ) - ssparse->bytes_consumed;
401 0 : ulong pad_sz = bytes_remaining;
402 0 : pad_sz = fd_ulong_min( pad_sz, data_sz );
403 0 : if( FD_UNLIKELY( !pad_sz && bytes_remaining ) ) return FD_SSPARSE_ADVANCE_ERROR;
404 :
405 0 : ssparse->bytes_consumed += pad_sz;
406 0 : result->bytes_consumed = pad_sz;
407 0 : bytes_remaining -= pad_sz;
408 :
409 0 : if( FD_LIKELY( !bytes_remaining ) ) ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
410 0 : return FD_SSPARSE_ADVANCE_AGAIN;
411 0 : }
412 :
413 : static int
414 : advance_account_header( fd_ssparse_t * ssparse,
415 : uchar const * data,
416 : ulong data_sz,
417 0 : fd_ssparse_advance_result_t * result ) {
418 0 : ulong consume = fd_ulong_min( 136UL-ssparse->account.header_bytes_consumed, fd_ulong_min( data_sz, ssparse->acc_vec_bytes-ssparse->tar.file_bytes_consumed ) );
419 :
420 0 : if( FD_UNLIKELY( !consume ) ) {
421 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed==ssparse->acc_vec_bytes ) ) {
422 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE;
423 0 : return FD_SSPARSE_ADVANCE_AGAIN;
424 0 : } else {
425 0 : return FD_SSPARSE_ADVANCE_ERROR;
426 0 : }
427 0 : }
428 :
429 0 : if( FD_UNLIKELY( consume<136UL ) ) fd_memcpy( ssparse->account.header+ssparse->account.header_bytes_consumed, data, consume );
430 :
431 0 : ssparse->account.header_bytes_consumed += consume;
432 0 : ssparse->tar.file_bytes_consumed += consume;
433 0 : ssparse->bytes_consumed += consume;
434 0 : result->bytes_consumed = consume;
435 :
436 0 : if( FD_UNLIKELY( ssparse->account.header_bytes_consumed<136UL ) ) return FD_SSPARSE_ADVANCE_AGAIN;
437 :
438 0 : uchar const * hdr = ssparse->account.header;
439 0 : if( FD_LIKELY( consume==136UL ) ) hdr = data;
440 :
441 0 : result->account_header.data_len = fd_ulong_load_8_fast( hdr+8UL );
442 0 : if( FD_UNLIKELY( result->account_header.data_len>FD_RUNTIME_ACC_SZ_MAX ) ) {
443 0 : FD_LOG_WARNING(( "invalid account header data length %lu", result->account_header.data_len ));
444 0 : return FD_SSPARSE_ADVANCE_ERROR;
445 0 : }
446 :
447 0 : result->account_header.pubkey = hdr+16UL;
448 0 : result->account_header.lamports = fd_ulong_load_8_fast( hdr+48UL );
449 0 : result->account_header.rent_epoch = fd_ulong_load_8_fast( hdr+56UL );
450 0 : result->account_header.owner = hdr+64UL;
451 0 : result->account_header.executable = hdr[ 96UL ];
452 0 : if( FD_UNLIKELY( result->account_header.executable>1 ) ) {
453 0 : char pubkey_str[ FD_BASE58_ENCODED_32_SZ ];
454 0 : fd_base58_encode_32( result->account_header.pubkey, NULL, pubkey_str );
455 0 : FD_LOG_WARNING(( "invalid account header executable %d for account %s", result->account_header.executable, pubkey_str ));
456 0 : return FD_SSPARSE_ADVANCE_ERROR;
457 0 : }
458 0 : result->account_header.hash = hdr+104UL;
459 0 : result->account_header.slot = ssparse->slot;
460 :
461 0 : ssparse->account.data_len = result->account_header.data_len;
462 0 : ssparse->account.data_bytes_consumed = 0UL;
463 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_DATA;
464 :
465 0 : return FD_SSPARSE_ADVANCE_ACCOUNT_HEADER;
466 0 : }
467 :
468 : static int
469 : advance_account_data( fd_ssparse_t * ssparse,
470 : uchar const * data,
471 : ulong data_sz,
472 0 : fd_ssparse_advance_result_t * result ) {
473 0 : if( FD_UNLIKELY( ssparse->account.data_bytes_consumed==ssparse->account.data_len ) ) {
474 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
475 0 : return FD_SSPARSE_ADVANCE_AGAIN;
476 0 : }
477 :
478 0 : ulong consume = fd_ulong_min( data_sz, ssparse->acc_vec_bytes-ssparse->tar.file_bytes_consumed );
479 0 : if( FD_UNLIKELY( !consume ) ) {
480 0 : FD_LOG_WARNING(( "account data extends beyond append vec size" ));
481 0 : return FD_SSPARSE_ADVANCE_ERROR;
482 0 : }
483 :
484 0 : consume = fd_ulong_min( consume, ssparse->account.data_len-ssparse->account.data_bytes_consumed );
485 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
486 :
487 0 : ssparse->tar.file_bytes_consumed += consume;
488 0 : ssparse->bytes_consumed += consume;
489 0 : ssparse->account.data_bytes_consumed += consume;
490 0 : result->bytes_consumed = consume;
491 :
492 0 : result->account_data.data_sz = consume;
493 0 : result->account_data.data = data;
494 :
495 0 : FD_TEST( ssparse->account.data_bytes_consumed<=ssparse->account.data_len );
496 0 : if( FD_LIKELY( ssparse->account.data_bytes_consumed==ssparse->account.data_len ) ) {
497 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
498 0 : }
499 :
500 0 : return FD_SSPARSE_ADVANCE_ACCOUNT_DATA;
501 0 : }
502 :
503 : static int
504 : advance_account_padding( fd_ssparse_t * ssparse,
505 : uchar const * data,
506 : ulong data_sz,
507 0 : fd_ssparse_advance_result_t * result ) {
508 0 : (void)data;
509 :
510 0 : ulong pad_sz = fd_ulong_align_up( ssparse->tar.file_bytes_consumed, 8UL ) - ssparse->tar.file_bytes_consumed;
511 0 : pad_sz = fd_ulong_min( pad_sz, ssparse->acc_vec_bytes - ssparse->tar.file_bytes_consumed );
512 0 : if( FD_UNLIKELY( !pad_sz ) ) {
513 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed==ssparse->acc_vec_bytes ) ) ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
514 0 : else ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
515 :
516 0 : ssparse->account.header_bytes_consumed = 0UL;
517 0 : return FD_SSPARSE_ADVANCE_AGAIN;
518 0 : }
519 :
520 0 : ulong consume = fd_ulong_min( data_sz, pad_sz );
521 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
522 :
523 0 : ssparse->tar.file_bytes_consumed += consume;
524 0 : ssparse->bytes_consumed += consume;
525 0 : result->bytes_consumed = consume;
526 :
527 0 : ulong remaining = fd_ulong_align_up( ssparse->tar.file_bytes_consumed, 8UL ) - ssparse->tar.file_bytes_consumed;
528 0 : if( FD_LIKELY( !remaining ) ) {
529 0 : ssparse->account.header_bytes_consumed = 0UL;
530 0 : ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
531 0 : }
532 0 : return FD_SSPARSE_ADVANCE_AGAIN;
533 0 : }
534 :
535 : static int
536 : advance_account_garbage( fd_ssparse_t * ssparse,
537 : uchar const * data,
538 : ulong data_sz,
539 0 : fd_ssparse_advance_result_t * result ) {
540 0 : (void)data;
541 0 : ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
542 0 : if( FD_UNLIKELY( !consume ) ) return FD_SSPARSE_ADVANCE_ERROR;
543 :
544 0 : ssparse->tar.file_bytes_consumed += consume;
545 0 : ssparse->bytes_consumed += consume;
546 0 : result->bytes_consumed = consume;
547 :
548 0 : if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) return FD_SSPARSE_ADVANCE_AGAIN;
549 :
550 0 : ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
551 0 : return FD_SSPARSE_ADVANCE_AGAIN;
552 0 : }
553 :
554 : int
555 : fd_ssparse_advance( fd_ssparse_t * ssparse,
556 : uchar const * data,
557 : ulong data_sz,
558 0 : fd_ssparse_advance_result_t * result ) {
559 0 : result->bytes_consumed = 0UL;
560 :
561 0 : switch( ssparse->state ) {
562 0 : case FD_SSPARSE_STATE_TAR_HEADER: return advance_tar( ssparse, data, data_sz, result );
563 0 : case FD_SSPARSE_STATE_SCROLL_TAR_HEADER: return advance_next_tar( ssparse, data, data_sz, result );
564 0 : case FD_SSPARSE_STATE_VERSION: return advance_version( ssparse, data, data_sz, result );
565 0 : case FD_SSPARSE_STATE_MANIFEST: return advance_manifest( ssparse, data, data_sz, result );
566 0 : case FD_SSPARSE_STATE_ACCOUNT_HEADER: return advance_account_header( ssparse, data, data_sz, result );
567 0 : case FD_SSPARSE_STATE_ACCOUNT_DATA: return advance_account_data( ssparse, data, data_sz, result );
568 0 : case FD_SSPARSE_STATE_ACCOUNT_PADDING: return advance_account_padding( ssparse, data, data_sz, result );
569 0 : case FD_SSPARSE_STATE_STATUS_CACHE: return advance_status_cache( ssparse, data, data_sz, result );
570 0 : case FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE: return advance_account_garbage( ssparse, data, data_sz, result );
571 0 : default: FD_LOG_ERR(( "invalid state %d", ssparse->state ));
572 0 : }
573 0 : }
574 :
575 : int
576 : fd_ssparse_populate_acc_vec_map( fd_ssparse_t * ssparse,
577 : ulong * slots,
578 : ulong * ids,
579 : ulong * file_szs,
580 0 : ulong cnt ) {
581 0 : for( ulong i=0UL; i<cnt; i++ ) {
582 0 : acc_vec_key_t key = { .slot=slots[ i ], .id=ids[ i ] };
583 0 : if( FD_UNLIKELY( acc_vec_map_ele_query( ssparse->manifest.acc_vec_map, &key, NULL, ssparse->manifest.acc_vec_pool ) ) ) return -1;
584 0 : acc_vec_t * acc_vec = acc_vec_pool_ele_acquire( ssparse->manifest.acc_vec_pool );
585 0 : acc_vec->key.id = ids[ i ];
586 0 : acc_vec->key.slot = slots[ i ];
587 0 : acc_vec->file_sz = file_szs[ i ];
588 0 : acc_vec_map_ele_insert( ssparse->manifest.acc_vec_map, acc_vec, ssparse->manifest.acc_vec_pool );
589 0 : }
590 0 : return 0;
591 0 : }
|