1use {
8 crate::{
9 account_storage::meta::{AccountMeta, StoredAccountMeta, StoredMeta},
10 accounts_file::{
11 AccountsFileError, InternalsForArchive, MatchAccountOwnerError, Result, StorageAccess,
12 StoredAccountsInfo, ALIGN_BOUNDARY_OFFSET,
13 },
14 accounts_hash::AccountHash,
15 accounts_index::ZeroLamport,
16 buffered_reader::{BufferedReader, BufferedReaderStatus},
17 file_io::read_into_buffer,
18 storable_accounts::StorableAccounts,
19 u64_align,
20 },
21 log::*,
22 memmap2::MmapMut,
23 solana_pubkey::Pubkey,
24 solana_sdk::{
25 account::{AccountSharedData, ReadableAccount, WritableAccount},
26 hash::Hash,
27 stake_history::Epoch,
28 system_instruction::MAX_PERMITTED_DATA_LENGTH,
29 },
30 std::{
31 convert::TryFrom,
32 fs::{remove_file, File, OpenOptions},
33 io::{Seek, SeekFrom, Write},
34 mem,
35 path::{Path, PathBuf},
36 ptr,
37 sync::{
38 atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
39 Mutex,
40 },
41 },
42 thiserror::Error,
43};
44
45pub mod test_utils;
46#[cfg(test)]
47use solana_sdk::account::accounts_equal;
48
49pub const STORE_META_OVERHEAD: usize = 136;
52
53const _: () = assert!(
55 STORE_META_OVERHEAD
56 == mem::size_of::<StoredMeta>()
57 + mem::size_of::<AccountMeta>()
58 + mem::size_of::<AccountHash>()
59);
60
61pub fn aligned_stored_size(data_len: usize) -> usize {
65 u64_align!(STORE_META_OVERHEAD + data_len)
66}
67
68pub const MAXIMUM_APPEND_VEC_FILE_SIZE: u64 = 16 * 1024 * 1024 * 1024; #[derive(Error, Debug)]
71pub enum AppendVecError {
73 #[error("too small file size {0} for AppendVec")]
74 FileSizeTooSmall(usize),
75
76 #[error("too large file size {0} for AppendVec")]
77 FileSizeTooLarge(usize),
78
79 #[error("incorrect layout/length/data in the appendvec at path {}", .0.display())]
80 IncorrectLayout(PathBuf),
81
82 #[error("offset ({0}) is larger than file size ({1})")]
83 OffsetOutOfBounds(usize, usize),
84}
85
86#[derive(Debug, Copy, Clone)]
89pub(crate) struct ValidSlice<'a>(&'a [u8]);
90
91impl<'a> ValidSlice<'a> {
92 pub(crate) fn new(data: &'a [u8]) -> Self {
93 Self(data)
94 }
95
96 pub(crate) fn len(&self) -> usize {
97 self.0.len()
98 }
99
100 #[cfg(all(unix, test))]
101 pub(crate) fn slice(&self) -> &[u8] {
102 self.0
103 }
104}
105
106#[derive(PartialEq, Eq, Debug)]
109pub struct AppendVecStoredAccountMeta<'append_vec> {
110 pub meta: &'append_vec StoredMeta,
111 pub account_meta: &'append_vec AccountMeta,
113 pub(crate) data: &'append_vec [u8],
114 pub(crate) offset: usize,
115 pub(crate) stored_size: usize,
116 pub(crate) hash: &'append_vec AccountHash,
117}
118
119impl<'append_vec> AppendVecStoredAccountMeta<'append_vec> {
120 pub fn pubkey(&self) -> &'append_vec Pubkey {
121 &self.meta.pubkey
122 }
123
124 pub fn hash(&self) -> &'append_vec AccountHash {
125 self.hash
126 }
127
128 pub fn stored_size(&self) -> usize {
129 self.stored_size
130 }
131
132 pub fn offset(&self) -> usize {
133 self.offset
134 }
135
136 pub fn data(&self) -> &'append_vec [u8] {
137 self.data
138 }
139
140 pub fn data_len(&self) -> u64 {
141 self.meta.data_len
142 }
143
144 pub fn meta(&self) -> &StoredMeta {
145 self.meta
146 }
147
148 pub(crate) fn sanitize(&self) -> bool {
149 self.sanitize_executable() && self.sanitize_lamports()
150 }
151
152 fn sanitize_executable(&self) -> bool {
153 self.ref_executable_byte() & !1 == 0
155 }
156
157 fn sanitize_lamports(&self) -> bool {
158 self.account_meta.lamports != 0
160 || self.to_account_shared_data() == AccountSharedData::default()
161 }
162
163 fn ref_executable_byte(&self) -> &u8 {
164 let executable_bool: &bool = &self.account_meta.executable;
167 let executable_bool_ptr = ptr::from_ref(executable_bool);
168 let executable_byte: &u8 = unsafe { &*(executable_bool_ptr.cast()) };
170 executable_byte
171 }
172}
173
174impl<'append_vec> ReadableAccount for AppendVecStoredAccountMeta<'append_vec> {
175 fn lamports(&self) -> u64 {
176 self.account_meta.lamports
177 }
178 fn data(&self) -> &'append_vec [u8] {
179 self.data()
180 }
181 fn owner(&self) -> &'append_vec Pubkey {
182 &self.account_meta.owner
183 }
184 fn executable(&self) -> bool {
185 self.account_meta.executable
186 }
187 fn rent_epoch(&self) -> Epoch {
188 self.account_meta.rent_epoch
189 }
190}
191
192pub(crate) struct IndexInfo {
194 pub stored_size_aligned: usize,
197 pub index_info: IndexInfoInner,
199}
200
201pub(crate) struct IndexInfoInner {
203 pub offset: usize,
205 pub pubkey: Pubkey,
206 pub lamports: u64,
207 pub rent_epoch: Epoch,
208 pub executable: bool,
209 pub data_len: u64,
210}
211
212#[derive(Debug)]
214struct AccountOffsets {
215 offset_to_end_of_data: usize,
217 next_account_offset: usize,
219 stored_size_aligned: usize,
221}
222
223#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
224#[derive(Debug)]
225enum AppendVecFileBacking {
226 Mmap(Mmap),
228 #[cfg_attr(not(unix), allow(dead_code))]
230 File(File),
231}
232
233#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
234#[derive(Debug)]
235struct Mmap {
236 mmap: MmapMut,
237 is_dirty: AtomicBool,
243}
244
245#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
250#[derive(Debug)]
251pub struct AppendVec {
252 path: PathBuf,
254
255 backing: AppendVecFileBacking,
257
258 append_lock: Mutex<()>,
260
261 current_len: AtomicUsize,
263
264 file_size: u64,
266
267 remove_file_on_drop: AtomicBool,
269}
270
271const PAGE_SIZE: u64 = 4 * 1024;
272const SCAN_BUFFER_SIZE: usize =
274 page_align((STORE_META_OVERHEAD as u64 + MAX_PERMITTED_DATA_LENGTH) * 3) as usize;
275const fn page_align(size: u64) -> u64 {
276 (size + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1)
277}
278
279#[cfg_attr(feature = "dev-context-only-utils", qualifier_attr::qualifiers(pub))]
295const SCAN_BUFFER_SIZE_WITHOUT_DATA: usize = 1 << 16;
296
297pub struct AppendVecStat {
298 pub mmap_files_open: AtomicU64,
299 pub mmap_files_dirty: AtomicU64,
300 pub open_as_file_io: AtomicU64,
301}
302
303lazy_static! {
304 pub static ref APPEND_VEC_STATS: AppendVecStat = AppendVecStat {
305 mmap_files_open: AtomicU64::new(0),
306 mmap_files_dirty: AtomicU64::new(0),
307 open_as_file_io: AtomicU64::new(0),
308 };
309}
310
311impl Drop for AppendVec {
312 fn drop(&mut self) {
313 APPEND_VEC_STATS
314 .mmap_files_open
315 .fetch_sub(1, Ordering::Relaxed);
316 match &self.backing {
317 AppendVecFileBacking::Mmap(mmap_only) => {
318 if mmap_only.is_dirty.load(Ordering::Acquire) {
319 APPEND_VEC_STATS
320 .mmap_files_dirty
321 .fetch_sub(1, Ordering::Relaxed);
322 }
323 }
324 AppendVecFileBacking::File(_) => {
325 APPEND_VEC_STATS
326 .open_as_file_io
327 .fetch_sub(1, Ordering::Relaxed);
328 }
329 }
330 if self.remove_file_on_drop.load(Ordering::Acquire) {
331 if let Err(_err) = remove_file(&self.path) {
334 inc_new_counter_info!("append_vec_drop_fail", 1);
339 }
340 }
341 }
342}
343
344impl AppendVec {
345 pub fn new(file: impl Into<PathBuf>, create: bool, size: usize) -> Self {
346 let file = file.into();
347 let initial_len = 0;
348 AppendVec::sanitize_len_and_size(initial_len, size).unwrap();
349
350 if create {
351 let _ignored = remove_file(&file);
352 }
353
354 let mut data = OpenOptions::new()
355 .read(true)
356 .write(true)
357 .create(create)
358 .open(&file)
359 .map_err(|e| {
360 panic!(
361 "Unable to {} data file {} in current dir({:?}): {:?}",
362 if create { "create" } else { "open" },
363 file.display(),
364 std::env::current_dir(),
365 e
366 );
367 })
368 .unwrap();
369
370 data.seek(SeekFrom::Start((size - 1) as u64)).unwrap();
374 data.write_all(&[0]).unwrap();
375 data.rewind().unwrap();
376 data.flush().unwrap();
377
378 let mmap = unsafe { MmapMut::map_mut(&data) };
380 let mmap = mmap.unwrap_or_else(|e| {
381 error!(
382 "Failed to map the data file (size: {}): {}.\n
383 Please increase sysctl vm.max_map_count or equivalent for your platform.",
384 size, e
385 );
386 std::process::exit(1);
387 });
388 APPEND_VEC_STATS
389 .mmap_files_open
390 .fetch_add(1, Ordering::Relaxed);
391
392 AppendVec {
393 path: file,
394 backing: AppendVecFileBacking::Mmap(Mmap {
395 mmap,
396 is_dirty: AtomicBool::new(false),
397 }),
398 append_lock: Mutex::new(()),
401 current_len: AtomicUsize::new(initial_len),
402 file_size: size as u64,
403 remove_file_on_drop: AtomicBool::new(true),
404 }
405 }
406
407 fn sanitize_len_and_size(current_len: usize, file_size: usize) -> Result<()> {
408 if file_size == 0 {
409 Err(AccountsFileError::AppendVecError(
410 AppendVecError::FileSizeTooSmall(file_size),
411 ))
412 } else if usize::try_from(MAXIMUM_APPEND_VEC_FILE_SIZE)
413 .map(|max| file_size > max)
414 .unwrap_or(true)
415 {
416 Err(AccountsFileError::AppendVecError(
417 AppendVecError::FileSizeTooLarge(file_size),
418 ))
419 } else if current_len > file_size {
420 Err(AccountsFileError::AppendVecError(
421 AppendVecError::OffsetOutOfBounds(current_len, file_size),
422 ))
423 } else {
424 Ok(())
425 }
426 }
427
428 pub fn dead_bytes_due_to_zero_lamport_single_ref(&self, count: usize) -> usize {
429 aligned_stored_size(0) * count
430 }
431
432 pub fn flush(&self) -> Result<()> {
433 match &self.backing {
434 AppendVecFileBacking::Mmap(mmap_only) => {
435 let should_flush = mmap_only.is_dirty.swap(false, Ordering::AcqRel);
437 if should_flush {
438 mmap_only.mmap.flush()?;
439 APPEND_VEC_STATS
440 .mmap_files_dirty
441 .fetch_sub(1, Ordering::Relaxed);
442 }
443 Ok(())
444 }
445 AppendVecFileBacking::File(_file) => {
446 Ok(())
448 }
449 }
450 }
451
452 pub fn reset(&self) {
453 let _lock = self.append_lock.lock().unwrap();
456 self.current_len.store(0, Ordering::Release);
457 }
458
459 #[cfg_attr(not(unix), allow(dead_code))]
462 pub(crate) fn reopen_as_readonly(&self) -> Option<Self> {
463 #[cfg(not(unix))]
464 return None;
466
467 #[cfg(unix)]
468 match &self.backing {
469 AppendVecFileBacking::File(_file) => None,
471 AppendVecFileBacking::Mmap(_mmap) => {
472 self.flush().expect("flush must succeed");
474 self.remove_file_on_drop.store(false, Ordering::Release);
476
477 AppendVec::new_from_file_unchecked(
479 self.path.clone(),
480 self.len(),
481 StorageAccess::File,
482 )
483 .ok()
484 }
485 }
486 }
487
488 pub fn remaining_bytes(&self) -> u64 {
490 self.capacity()
491 .saturating_sub(u64_align!(self.len()) as u64)
492 }
493
494 pub fn len(&self) -> usize {
495 self.current_len.load(Ordering::Acquire)
496 }
497
498 pub fn is_empty(&self) -> bool {
499 self.len() == 0
500 }
501
502 pub fn capacity(&self) -> u64 {
503 self.file_size
504 }
505
506 pub fn new_from_file(
507 path: impl Into<PathBuf>,
508 current_len: usize,
509 storage_access: StorageAccess,
510 ) -> Result<(Self, usize)> {
511 let path = path.into();
512 let new = Self::new_from_file_unchecked(path, current_len, storage_access)?;
513
514 let (sanitized, num_accounts) = new.sanitize_layout_and_length();
515 if !sanitized {
516 return Err(AccountsFileError::AppendVecError(
517 AppendVecError::IncorrectLayout(new.path.clone()),
518 ));
519 }
520
521 Ok((new, num_accounts))
522 }
523
524 #[cfg_attr(not(unix), allow(unused_variables))]
526 pub fn new_from_file_unchecked(
527 path: impl Into<PathBuf>,
528 current_len: usize,
529 storage_access: StorageAccess,
530 ) -> Result<Self> {
531 let path = path.into();
532 let file_size = std::fs::metadata(&path)?.len();
533 Self::sanitize_len_and_size(current_len, file_size as usize)?;
534
535 let data = OpenOptions::new()
536 .read(true)
537 .write(true)
538 .create(false)
539 .open(&path)?;
540
541 #[cfg(unix)]
542 if storage_access == StorageAccess::File {
544 APPEND_VEC_STATS
545 .mmap_files_open
546 .fetch_add(1, Ordering::Relaxed);
547 APPEND_VEC_STATS
548 .open_as_file_io
549 .fetch_add(1, Ordering::Relaxed);
550
551 return Ok(AppendVec {
552 path,
553 backing: AppendVecFileBacking::File(data),
554 append_lock: Mutex::new(()),
555 current_len: AtomicUsize::new(current_len),
556 file_size,
557 remove_file_on_drop: AtomicBool::new(true),
558 });
559 }
560
561 let mmap = unsafe {
562 let result = MmapMut::map_mut(&data);
563 if result.is_err() {
564 info!("memory map error: {:?}. This may be because vm.max_map_count is not set correctly.", result);
566 }
567 result?
568 };
569 APPEND_VEC_STATS
570 .mmap_files_open
571 .fetch_add(1, Ordering::Relaxed);
572
573 Ok(AppendVec {
574 path,
575 backing: AppendVecFileBacking::Mmap(Mmap {
576 mmap,
577 is_dirty: AtomicBool::new(false),
578 }),
579 append_lock: Mutex::new(()),
580 current_len: AtomicUsize::new(current_len),
581 file_size,
582 remove_file_on_drop: AtomicBool::new(true),
583 })
584 }
585
586 #[cfg(feature = "dev-context-only-utils")]
588 pub fn new_for_store_tool(path: impl Into<PathBuf>) -> Result<Self> {
589 let path = path.into();
590 let file_size = std::fs::metadata(&path)?.len();
591 Self::new_from_file_unchecked(path, file_size as usize, StorageAccess::default())
592 }
593
594 fn sanitize_layout_and_length(&self) -> (bool, usize) {
595 let mut num_accounts = 0;
601 let mut matches = true;
602 let mut last_offset = 0;
603 self.scan_accounts(|account| {
604 if !matches || !account.sanitize() {
605 matches = false;
606 return;
607 }
608 last_offset = account.offset() + account.stored_size();
609 num_accounts += 1;
610 });
611 if !matches {
612 return (false, num_accounts);
613 }
614 let aligned_current_len = u64_align!(self.current_len.load(Ordering::Acquire));
615
616 (last_offset == aligned_current_len, num_accounts)
617 }
618
619 fn get_slice(slice: ValidSlice, offset: usize, size: usize) -> Option<(&[u8], usize)> {
624 let end = offset.wrapping_add(size);
627 slice
628 .0
629 .get(offset..end)
630 .map(|subslice| (subslice, u64_align!(end)))
631 }
632
633 fn append_ptr(&self, offset: &mut usize, src: *const u8, len: usize) {
636 let pos = u64_align!(*offset);
637 match &self.backing {
638 AppendVecFileBacking::Mmap(mmap_only) => {
639 let data = &mmap_only.mmap[pos..(pos + len)];
640 unsafe {
644 let dst = data.as_ptr() as *mut _;
645 ptr::copy(src, dst, len);
646 };
647 *offset = pos + len;
648 }
649 AppendVecFileBacking::File(_file) => {
650 unimplemented!();
651 }
652 }
653 }
654
655 fn append_ptrs_locked(&self, offset: &mut usize, vals: &[(*const u8, usize)]) -> Option<usize> {
660 let mut end = *offset;
661 for val in vals {
662 end = u64_align!(end);
663 end += val.1;
664 }
665
666 if (self.file_size as usize) < end {
667 return None;
668 }
669
670 let pos = u64_align!(*offset);
671 for val in vals {
672 self.append_ptr(offset, val.0, val.1)
673 }
674 self.current_len.store(*offset, Ordering::Release);
675 Some(pos)
676 }
677
678 fn get_type<T>(slice: ValidSlice, offset: usize) -> Option<(&T, usize)> {
682 let (data, next) = Self::get_slice(slice, offset, mem::size_of::<T>())?;
683 let ptr = data.as_ptr().cast();
684 Some((unsafe { &*ptr }, next))
687 }
688
689 fn get_valid_slice_from_mmap<'a>(&self, mmap: &'a MmapMut) -> ValidSlice<'a> {
693 ValidSlice(&mmap[..self.len()])
694 }
695
696 pub fn get_stored_account_meta_callback<Ret>(
699 &self,
700 offset: usize,
701 mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>) -> Ret,
702 ) -> Option<Ret> {
703 match &self.backing {
704 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
705 let slice = self.get_valid_slice_from_mmap(mmap);
706 let (meta, next): (&StoredMeta, _) = Self::get_type(slice, offset)?;
707 let (account_meta, next): (&AccountMeta, _) = Self::get_type(slice, next)?;
708 let (hash, next): (&AccountHash, _) = Self::get_type(slice, next)?;
709 let (data, next) = Self::get_slice(slice, next, meta.data_len as usize)?;
710 let stored_size = next - offset;
711 Some(callback(StoredAccountMeta::AppendVec(
712 AppendVecStoredAccountMeta {
713 meta,
714 account_meta,
715 data,
716 offset,
717 stored_size,
718 hash,
719 },
720 )))
721 }
722 AppendVecFileBacking::File(file) => {
723 let mut buf = [0u8; PAGE_SIZE as usize];
725 let bytes_read = read_into_buffer(file, self.len(), offset, &mut buf).ok()?;
726 let valid_bytes = ValidSlice(&buf[..bytes_read]);
727 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
728 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
729 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
730 let data_len = meta.data_len;
731 let remaining_bytes_for_data = bytes_read - next;
732 Some(if remaining_bytes_for_data >= data_len as usize {
733 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
735 let stored_size = next;
736 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
737 meta,
738 account_meta,
739 data,
740 offset,
741 stored_size,
742 hash,
743 });
744 callback(account)
745 } else {
746 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
748 let mut data = vec![0u8; data_len as usize];
749 let bytes_read =
752 read_into_buffer(file, self.len(), offset + next, &mut data).ok()?;
753 if bytes_read < data_len as usize {
754 return None;
756 }
757 let stored_size = aligned_stored_size(data_len as usize);
758 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
759 meta,
760 account_meta,
761 data: &data[..],
762 offset,
763 stored_size,
764 hash,
765 });
766 callback(account)
767 })
768 }
769 }
770 }
771
772 pub(crate) fn get_account_shared_data(&self, offset: usize) -> Option<AccountSharedData> {
776 match &self.backing {
777 AppendVecFileBacking::Mmap(_) => self
778 .get_stored_account_meta_callback(offset, |account| {
779 account.to_account_shared_data()
780 }),
781 AppendVecFileBacking::File(file) => {
782 let mut buf = [0u8; PAGE_SIZE as usize];
783 let bytes_read = read_into_buffer(file, self.len(), offset, &mut buf).ok()?;
784 let valid_bytes = ValidSlice(&buf[..bytes_read]);
785 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
786 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
787 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
788 let data_len = meta.data_len;
789 let remaining_bytes_for_data = bytes_read - next;
790 Some(if remaining_bytes_for_data >= data_len as usize {
791 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
793 let stored_size = next;
794 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
795 meta,
796 account_meta,
797 data,
798 offset,
799 stored_size,
800 hash,
801 });
802 account.to_account_shared_data()
804 } else {
805 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
807 let mut data = vec![0u8; data_len as usize];
808 let bytes_read =
810 read_into_buffer(file, self.len(), offset + next, &mut data).ok()?;
811 if bytes_read < data_len as usize {
812 return None;
814 }
815 AccountSharedData::create(
816 account_meta.lamports,
817 data,
818 account_meta.owner,
819 account_meta.executable,
820 account_meta.rent_epoch,
821 )
822 })
823 }
824 }
825 }
826
827 pub fn account_matches_owners(
832 &self,
833 offset: usize,
834 owners: &[Pubkey],
835 ) -> std::result::Result<usize, MatchAccountOwnerError> {
836 self.get_stored_account_meta_callback(offset, |stored_account_meta| {
837 if stored_account_meta.lamports() == 0 {
838 Err(MatchAccountOwnerError::NoMatch)
839 } else {
840 owners
841 .iter()
842 .position(|entry| stored_account_meta.owner() == entry)
843 .ok_or(MatchAccountOwnerError::NoMatch)
844 }
845 })
846 .unwrap_or(Err(MatchAccountOwnerError::UnableToLoad))
847 }
848
849 #[cfg(test)]
850 pub fn get_account_test(
851 &self,
852 offset: usize,
853 ) -> Option<(StoredMeta, solana_sdk::account::AccountSharedData)> {
854 let sizes = self.get_account_sizes(&[offset]);
855 let result = self.get_stored_account_meta_callback(offset, |r_callback| {
856 let r2 = self.get_account_shared_data(offset);
857 assert!(accounts_equal(&r_callback, r2.as_ref().unwrap()));
858 assert_eq!(sizes, vec![r_callback.stored_size()]);
859 let meta = r_callback.meta().clone();
860 Some((meta, r_callback.to_account_shared_data()))
861 });
862 if result.is_none() {
863 assert!(self
864 .get_stored_account_meta_callback(offset, |_| {})
865 .is_none());
866 assert!(self.get_account_shared_data(offset).is_none());
867 assert!(sizes.is_empty());
869 }
870 result.flatten()
871 }
872
873 pub fn path(&self) -> &Path {
875 self.path.as_path()
876 }
877
878 fn next_account_offset(start_offset: usize, stored_meta: &StoredMeta) -> AccountOffsets {
885 let stored_size_unaligned = STORE_META_OVERHEAD
886 .checked_add(stored_meta.data_len as usize)
887 .expect("stored size cannot overflow");
888 let stored_size_aligned = u64_align!(stored_size_unaligned);
889 let offset_to_end_of_data = start_offset + stored_size_unaligned;
890 let next_account_offset = start_offset + stored_size_aligned;
891
892 AccountOffsets {
893 next_account_offset,
894 offset_to_end_of_data,
895 stored_size_aligned,
896 }
897 }
898
899 pub(crate) fn scan_index(&self, mut callback: impl FnMut(IndexInfo)) {
902 let self_len = self.len();
904 match &self.backing {
905 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
906 let mut offset = 0;
907 let slice = self.get_valid_slice_from_mmap(mmap);
908 loop {
909 let Some((stored_meta, next)) = Self::get_type::<StoredMeta>(slice, offset)
910 else {
911 break;
913 };
914 let Some((account_meta, _)) = Self::get_type::<AccountMeta>(slice, next) else {
915 break;
917 };
918 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
919 break;
921 }
922 let next = Self::next_account_offset(offset, stored_meta);
923 if next.offset_to_end_of_data > self_len {
924 break;
926 }
927 callback(IndexInfo {
928 index_info: {
929 IndexInfoInner {
930 pubkey: stored_meta.pubkey,
931 lamports: account_meta.lamports,
932 offset,
933 data_len: stored_meta.data_len,
934 executable: account_meta.executable,
935 rent_epoch: account_meta.rent_epoch,
936 }
937 },
938 stored_size_aligned: next.stored_size_aligned,
939 });
940 offset = next.next_account_offset;
941 }
942 }
943 AppendVecFileBacking::File(file) => {
944 let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE, self_len);
945 let mut reader =
946 BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
947 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
948 let (offset, bytes) = reader.get_offset_and_data();
949 let (stored_meta, next) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
950 let (account_meta, _) = Self::get_type::<AccountMeta>(bytes, next).unwrap();
951 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
952 break;
954 }
955 let next = Self::next_account_offset(offset, stored_meta);
956 if next.offset_to_end_of_data > self_len {
957 break;
959 }
960 callback(IndexInfo {
961 index_info: {
962 IndexInfoInner {
963 pubkey: stored_meta.pubkey,
964 lamports: account_meta.lamports,
965 offset,
966 data_len: stored_meta.data_len,
967 executable: account_meta.executable,
968 rent_epoch: account_meta.rent_epoch,
969 }
970 },
971 stored_size_aligned: next.stored_size_aligned,
972 });
973 reader.advance_offset(next.stored_size_aligned);
974 }
975 }
976 }
977 }
978
979 #[allow(clippy::blocks_in_conditions)]
981 pub fn scan_accounts(&self, mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>)) {
982 match &self.backing {
983 AppendVecFileBacking::Mmap(_mmap) => {
984 let mut offset = 0;
985 while self
986 .get_stored_account_meta_callback(offset, |account| {
987 offset += account.stored_size();
988 if account.is_zero_lamport() && account.pubkey() == &Pubkey::default() {
989 return false;
991 }
992
993 callback(account);
994 true
995 })
996 .unwrap_or_default()
997 {}
998 }
999 AppendVecFileBacking::File(file) => {
1000 let mut reader =
1001 BufferedReader::new(SCAN_BUFFER_SIZE, self.len(), file, STORE_META_OVERHEAD);
1002 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
1003 let (offset, bytes_subset) = reader.get_offset_and_data();
1004 let (meta, next): (&StoredMeta, _) = Self::get_type(bytes_subset, 0).unwrap();
1005 let (account_meta, next): (&AccountMeta, _) =
1006 Self::get_type(bytes_subset, next).unwrap();
1007 let (hash, next): (&AccountHash, _) =
1008 Self::get_type(bytes_subset, next).unwrap();
1009 let data_len = meta.data_len;
1010 if bytes_subset.len() - next >= data_len as usize {
1011 let data = &bytes_subset.0[next..(next + data_len as usize)];
1013 let stored_size = u64_align!(next + (data_len as usize));
1014 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
1015 meta,
1016 account_meta,
1017 data,
1018 offset,
1019 stored_size,
1020 hash,
1021 });
1022 callback(account);
1023 reader.advance_offset(stored_size);
1024 } else {
1025 reader.set_required_data_len(
1027 STORE_META_OVERHEAD.saturating_add(data_len as usize),
1028 )
1029 }
1030 }
1031 }
1032 }
1033 }
1034
1035 pub(crate) fn get_account_sizes(&self, sorted_offsets: &[usize]) -> Vec<usize> {
1037 let self_len = self.len();
1039 let mut account_sizes = Vec::with_capacity(sorted_offsets.len());
1040 match &self.backing {
1041 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1042 let slice = self.get_valid_slice_from_mmap(mmap);
1043 for &offset in sorted_offsets {
1044 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1045 break;
1046 };
1047 let next = Self::next_account_offset(offset, stored_meta);
1048 if next.offset_to_end_of_data > self_len {
1049 break;
1051 }
1052 account_sizes.push(next.stored_size_aligned);
1053 }
1054 }
1055 AppendVecFileBacking::File(file) => {
1056 let mut buffer = [0u8; mem::size_of::<StoredMeta>()];
1057 for &offset in sorted_offsets {
1058 let Some(bytes_read) =
1059 read_into_buffer(file, self_len, offset, &mut buffer).ok()
1060 else {
1061 break;
1062 };
1063 let bytes = ValidSlice(&buffer[..bytes_read]);
1064 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(bytes, 0) else {
1065 break;
1066 };
1067 let next = Self::next_account_offset(offset, stored_meta);
1068 if next.offset_to_end_of_data > self_len {
1069 break;
1071 }
1072 account_sizes.push(next.stored_size_aligned);
1073 }
1074 }
1075 }
1076 account_sizes
1077 }
1078
1079 pub fn scan_pubkeys(&self, mut callback: impl FnMut(&Pubkey)) {
1085 let self_len = self.len();
1087 match &self.backing {
1088 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => {
1089 let mut offset = 0;
1090 let slice = self.get_valid_slice_from_mmap(mmap);
1091 loop {
1092 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1093 break;
1095 };
1096 let next = Self::next_account_offset(offset, stored_meta);
1097 if next.offset_to_end_of_data > self_len {
1098 break;
1100 }
1101 callback(&stored_meta.pubkey);
1102 offset = next.next_account_offset;
1103 }
1104 }
1105 AppendVecFileBacking::File(file) => {
1106 let buffer_size = std::cmp::min(SCAN_BUFFER_SIZE_WITHOUT_DATA, self_len);
1107 let mut reader =
1108 BufferedReader::new(buffer_size, self_len, file, STORE_META_OVERHEAD);
1109 while reader.read().ok() == Some(BufferedReaderStatus::Success) {
1110 let (offset, bytes) = reader.get_offset_and_data();
1111 let (stored_meta, _) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
1112 let next = Self::next_account_offset(offset, stored_meta);
1113 if next.offset_to_end_of_data > self.len() {
1114 break;
1116 }
1117 callback(&stored_meta.pubkey);
1118 reader.advance_offset(next.stored_size_aligned);
1120 }
1121 }
1122 }
1123 }
1124
1125 pub fn append_accounts<'a>(
1133 &self,
1134 accounts: &impl StorableAccounts<'a>,
1135 skip: usize,
1136 ) -> Option<StoredAccountsInfo> {
1137 let _lock = self.append_lock.lock().unwrap();
1138 let default_hash = Hash::default();
1139 let mut offset = self.len();
1140 let len = accounts.len();
1141 let offsets_len = len - skip + 1;
1145 let mut offsets = Vec::with_capacity(offsets_len);
1146 let mut stop = false;
1147 for i in skip..len {
1148 if stop {
1149 break;
1150 }
1151 accounts.account_default_if_zero_lamport(i, |account| {
1152 let account_meta = AccountMeta {
1153 lamports: account.lamports(),
1154 owner: *account.owner(),
1155 rent_epoch: account.rent_epoch(),
1156 executable: account.executable(),
1157 };
1158
1159 let stored_meta = StoredMeta {
1160 pubkey: *account.pubkey(),
1161 data_len: account.data().len() as u64,
1162 write_version_obsolete: 0,
1163 };
1164 let stored_meta_ptr = ptr::from_ref(&stored_meta).cast();
1165 let account_meta_ptr = ptr::from_ref(&account_meta).cast();
1166 let hash_ptr = bytemuck::bytes_of(&default_hash).as_ptr();
1167 let data_ptr = account.data().as_ptr();
1168 let ptrs = [
1169 (stored_meta_ptr, mem::size_of::<StoredMeta>()),
1170 (account_meta_ptr, mem::size_of::<AccountMeta>()),
1171 (hash_ptr, mem::size_of::<AccountHash>()),
1172 (data_ptr, stored_meta.data_len as usize),
1173 ];
1174 if let Some(start_offset) = self.append_ptrs_locked(&mut offset, &ptrs) {
1175 offsets.push(start_offset)
1176 } else {
1177 stop = true;
1178 }
1179 });
1180 }
1181
1182 match &self.backing {
1183 AppendVecFileBacking::Mmap(mmap_only) => {
1184 if !offsets.is_empty() {
1185 if !mmap_only.is_dirty.load(Ordering::Acquire) {
1191 mmap_only.is_dirty.store(true, Ordering::Release);
1192 APPEND_VEC_STATS
1193 .mmap_files_dirty
1194 .fetch_add(1, Ordering::Relaxed);
1195 }
1196 }
1197 }
1198 AppendVecFileBacking::File(_) => {}
1199 }
1200
1201 (!offsets.is_empty()).then(|| {
1202 offsets.push(u64_align!(offset));
1207 let size = offsets.windows(2).map(|offset| offset[1] - offset[0]).sum();
1208 offsets.pop();
1209
1210 StoredAccountsInfo { offsets, size }
1211 })
1212 }
1213
1214 pub(crate) fn can_append(&self) -> bool {
1215 match &self.backing {
1216 AppendVecFileBacking::File(_file) => false,
1217 AppendVecFileBacking::Mmap(_mmap) => true,
1218 }
1219 }
1220
1221 pub(crate) fn internals_for_archive(&self) -> InternalsForArchive {
1223 match &self.backing {
1224 AppendVecFileBacking::File(_file) => InternalsForArchive::FileIo(self.path()),
1225 AppendVecFileBacking::Mmap(Mmap { mmap, .. }) => InternalsForArchive::Mmap(mmap),
1227 }
1228 }
1229}
1230
1231#[cfg(test)]
1232pub mod tests {
1233 use {
1234 super::{test_utils::*, *},
1235 assert_matches::assert_matches,
1236 memoffset::offset_of,
1237 rand::{thread_rng, Rng},
1238 solana_sdk::{
1239 account::{Account, AccountSharedData},
1240 clock::Slot,
1241 },
1242 std::{mem::ManuallyDrop, time::Instant},
1243 test_case::test_case,
1244 };
1245
1246 impl AppendVec {
1247 pub(crate) fn set_current_len_for_tests(&self, len: usize) {
1248 self.current_len.store(len, Ordering::Release);
1249 }
1250
1251 fn append_account_test(&self, data: &(StoredMeta, AccountSharedData)) -> Option<usize> {
1252 let slot_ignored = Slot::MAX;
1253 let accounts = [(&data.0.pubkey, &data.1)];
1254 let slice = &accounts[..];
1255 let storable_accounts = (slot_ignored, slice);
1256
1257 self.append_accounts(&storable_accounts, 0)
1258 .map(|res| res.offsets[0])
1259 }
1260 }
1261
1262 impl StoredAccountMeta<'_> {
1263 pub(crate) fn ref_executable_byte(&self) -> &u8 {
1264 match self {
1265 Self::AppendVec(av) => av.ref_executable_byte(),
1266 Self::Hot(_) => unreachable!(),
1268 }
1269 }
1270 }
1271
1272 impl AppendVecStoredAccountMeta<'_> {
1273 fn set_data_len_unsafe(&self, new_data_len: u64) {
1274 unsafe {
1276 #[allow(invalid_reference_casting)]
1277 ptr::write(
1278 std::mem::transmute::<*const u64, *mut u64>(&self.meta.data_len),
1279 new_data_len,
1280 );
1281 }
1282 }
1283
1284 fn get_executable_byte(&self) -> u8 {
1285 let executable_bool: bool = self.executable();
1286 let executable_byte: u8 = unsafe { std::mem::transmute::<bool, u8>(executable_bool) };
1288 executable_byte
1289 }
1290
1291 fn set_executable_as_byte(&self, new_executable_byte: u8) {
1292 unsafe {
1294 #[allow(invalid_reference_casting)]
1295 ptr::write(
1296 std::mem::transmute::<*const bool, *mut u8>(&self.account_meta.executable),
1297 new_executable_byte,
1298 );
1299 }
1300 }
1301 }
1302
1303 static_assertions::assert_eq_align!(u64, StoredMeta, AccountMeta);
1305
1306 #[test]
1307 fn test_account_meta_default() {
1308 let def1 = AccountMeta::default();
1309 let def2 = AccountMeta::from(&Account::default());
1310 assert_eq!(&def1, &def2);
1311 let def2 = AccountMeta::from(&AccountSharedData::default());
1312 assert_eq!(&def1, &def2);
1313 let def2 = AccountMeta::from(Some(&AccountSharedData::default()));
1314 assert_eq!(&def1, &def2);
1315 let none: Option<&AccountSharedData> = None;
1316 let def2 = AccountMeta::from(none);
1317 assert_eq!(&def1, &def2);
1318 }
1319
1320 #[test]
1321 fn test_account_meta_non_default() {
1322 let def1 = AccountMeta {
1323 lamports: 1,
1324 owner: Pubkey::new_unique(),
1325 executable: true,
1326 rent_epoch: 3,
1327 };
1328 let def2_account = Account {
1329 lamports: def1.lamports,
1330 owner: def1.owner,
1331 executable: def1.executable,
1332 rent_epoch: def1.rent_epoch,
1333 data: Vec::new(),
1334 };
1335 let def2 = AccountMeta::from(&def2_account);
1336 assert_eq!(&def1, &def2);
1337 let def2 = AccountMeta::from(&AccountSharedData::from(def2_account.clone()));
1338 assert_eq!(&def1, &def2);
1339 let def2 = AccountMeta::from(Some(&AccountSharedData::from(def2_account)));
1340 assert_eq!(&def1, &def2);
1341 }
1342
1343 #[test]
1344 #[should_panic(expected = "AppendVecError(FileSizeTooSmall(0))")]
1345 fn test_append_vec_new_bad_size() {
1346 let path = get_append_vec_path("test_append_vec_new_bad_size");
1347 let _av = AppendVec::new(&path.path, true, 0);
1348 }
1349
1350 #[test_case(StorageAccess::Mmap)]
1351 #[test_case(StorageAccess::File)]
1352 fn test_append_vec_new_from_file_bad_size(storage_access: StorageAccess) {
1353 let file = get_append_vec_path("test_append_vec_new_from_file_bad_size");
1354 let path = &file.path;
1355
1356 let _data = OpenOptions::new()
1357 .read(true)
1358 .write(true)
1359 .create_new(true)
1360 .open(path)
1361 .expect("create a test file for mmap");
1362
1363 let result = AppendVec::new_from_file(path, 0, storage_access);
1364 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1365 }
1366
1367 #[test]
1368 fn test_append_vec_sanitize_len_and_size_too_small() {
1369 const LEN: usize = 0;
1370 const SIZE: usize = 0;
1371 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1372 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1373 }
1374
1375 #[test]
1376 fn test_append_vec_sanitize_len_and_size_maximum() {
1377 const LEN: usize = 0;
1378 const SIZE: usize = 16 * 1024 * 1024 * 1024;
1379 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1380 assert_matches!(result, Ok(_));
1381 }
1382
1383 #[test]
1384 fn test_append_vec_sanitize_len_and_size_too_large() {
1385 const LEN: usize = 0;
1386 const SIZE: usize = 16 * 1024 * 1024 * 1024 + 1;
1387 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1388 assert_matches!(result, Err(ref message) if message.to_string().contains("too large file size 17179869185 for AppendVec"));
1389 }
1390
1391 #[test]
1392 fn test_append_vec_sanitize_len_and_size_full_and_same_as_current_len() {
1393 const LEN: usize = 1024 * 1024;
1394 const SIZE: usize = 1024 * 1024;
1395 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1396 assert_matches!(result, Ok(_));
1397 }
1398
1399 #[test]
1400 fn test_append_vec_sanitize_len_and_size_larger_current_len() {
1401 const LEN: usize = 1024 * 1024 + 1;
1402 const SIZE: usize = 1024 * 1024;
1403 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1404 assert_matches!(result, Err(ref message) if message.to_string().contains("is larger than file size (1048576)"));
1405 }
1406
1407 #[test]
1408 fn test_append_vec_one() {
1409 let path = get_append_vec_path("test_append");
1410 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1411 let account = create_test_account(0);
1412 let index = av.append_account_test(&account).unwrap();
1413 assert_eq!(av.get_account_test(index).unwrap(), account);
1414 truncate_and_test(av, index);
1415 }
1416
1417 fn truncate_and_test(av: AppendVec, index: usize) {
1420 let hash = std::mem::size_of::<AccountHash>();
1422 for _ in 0..hash {
1423 av.current_len.fetch_sub(1, Ordering::Relaxed);
1424 assert_eq!(av.get_account_test(index), None);
1425 }
1426 av.current_len.fetch_sub(1, Ordering::Relaxed);
1428 assert_eq!(av.get_account_test(index), None);
1429 }
1430
1431 #[test]
1432 fn test_append_vec_one_with_data() {
1433 let path = get_append_vec_path("test_append");
1434 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1435 let data_len = 1;
1436 let account = create_test_account(data_len);
1437 let index = av.append_account_test(&account).unwrap();
1438 assert_eq!(
1440 STORE_META_OVERHEAD + data_len,
1441 av.current_len.load(Ordering::Relaxed)
1442 );
1443 assert_eq!(av.get_account_test(index).unwrap(), account);
1444 truncate_and_test(av, index);
1445 }
1446
1447 #[test]
1448 fn test_remaining_bytes() {
1449 let path = get_append_vec_path("test_append");
1450 let sz = 1024 * 1024;
1451 let sz64 = sz as u64;
1452 let av = AppendVec::new(&path.path, true, sz);
1453 assert_eq!(av.capacity(), sz64);
1454 assert_eq!(av.remaining_bytes(), sz64);
1455
1456 let mut av_len = 0;
1458 let account = create_test_account(0);
1459 av.append_account_test(&account).unwrap();
1460 av_len += STORE_META_OVERHEAD;
1461 assert_eq!(av.capacity(), sz64);
1462 assert_eq!(av.remaining_bytes(), sz64 - (STORE_META_OVERHEAD as u64));
1463 assert_eq!(av.len(), av_len);
1464
1465 let account = create_test_account(1);
1467 let account_storage_len = STORE_META_OVERHEAD + 1;
1468 av_len += account_storage_len;
1469 av.append_account_test(&account).unwrap();
1470 assert_eq!(av.capacity(), sz64);
1471 assert_eq!(av.len(), av_len);
1472 let alignment_bytes = u64_align!(av_len) - av_len; assert_eq!(alignment_bytes, 7);
1474 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1475
1476 let account = create_test_account(1);
1478 av.append_account_test(&account).unwrap();
1479 let account_storage_len = STORE_META_OVERHEAD + 1;
1480 av_len += alignment_bytes; av_len += account_storage_len;
1482 assert_eq!(av.capacity(), sz64);
1483 assert_eq!(av.len(), av_len);
1484 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1485 }
1486
1487 #[test]
1488 fn test_append_vec_data() {
1489 let path = get_append_vec_path("test_append_data");
1490 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1491 let account = create_test_account(5);
1492 let index = av.append_account_test(&account).unwrap();
1493 assert_eq!(av.get_account_test(index).unwrap(), account);
1494 let account1 = create_test_account(6);
1495 let index1 = av.append_account_test(&account1).unwrap();
1496 assert_eq!(av.get_account_test(index).unwrap(), account);
1497 assert_eq!(av.get_account_test(index1).unwrap(), account1);
1498 }
1499
1500 #[test]
1501 fn test_account_matches_owners() {
1502 let path = get_append_vec_path("test_append_data");
1503 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1504 let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
1505
1506 let mut account = create_test_account(5);
1507 account.1.set_owner(owners[0]);
1508 let index = av.append_account_test(&account).unwrap();
1509 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1510
1511 let mut account1 = create_test_account(6);
1512 account1.1.set_owner(owners[1]);
1513 let index1 = av.append_account_test(&account1).unwrap();
1514 assert_eq!(av.account_matches_owners(index1, &owners), Ok(1));
1515 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1516
1517 let mut account2 = create_test_account(6);
1518 account2.1.set_owner(Pubkey::new_unique());
1519 let index2 = av.append_account_test(&account2).unwrap();
1520 assert_eq!(
1521 av.account_matches_owners(index2, &owners),
1522 Err(MatchAccountOwnerError::NoMatch)
1523 );
1524
1525 assert_eq!(
1527 av.account_matches_owners(usize::MAX - mem::size_of::<StoredMeta>(), &owners),
1528 Err(MatchAccountOwnerError::UnableToLoad)
1529 );
1530
1531 assert_eq!(
1532 av.account_matches_owners(
1533 usize::MAX - mem::size_of::<StoredMeta>() - mem::size_of::<AccountMeta>() + 1,
1534 &owners
1535 ),
1536 Err(MatchAccountOwnerError::UnableToLoad)
1537 );
1538 }
1539
1540 impl AppendVec {
1541 fn accounts_count(&self) -> usize {
1543 let mut count = 0;
1544 self.scan_accounts(|_| {
1545 count += 1;
1546 });
1547 count
1548 }
1549 }
1550
1551 #[test]
1552 fn test_append_vec_append_many() {
1553 let path = get_append_vec_path("test_append_many");
1554 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1555 let size = 1000;
1556 let mut indexes = vec![];
1557 let now = Instant::now();
1558 let mut sizes = vec![];
1559 for sample in 0..size {
1560 let account = create_test_account(sample + 1);
1563 sizes.push(aligned_stored_size(account.1.data().len()));
1564 let pos = av.append_account_test(&account).unwrap();
1565 assert_eq!(av.get_account_test(pos).unwrap(), account);
1566 indexes.push(pos);
1567 assert_eq!(sizes, av.get_account_sizes(&indexes));
1568 }
1569 trace!("append time: {} ms", now.elapsed().as_millis());
1570
1571 let now = Instant::now();
1572 for _ in 0..size {
1573 let sample = thread_rng().gen_range(0..indexes.len());
1574 let account = create_test_account(sample + 1);
1575 assert_eq!(av.get_account_test(indexes[sample]).unwrap(), account);
1576 }
1577 trace!("random read time: {} ms", now.elapsed().as_millis());
1578
1579 let now = Instant::now();
1580 assert_eq!(indexes.len(), size);
1581 assert_eq!(indexes[0], 0);
1582 let mut sample = 0;
1583 assert_eq!(av.accounts_count(), size);
1584 av.scan_accounts(|v| {
1585 let account = create_test_account(sample + 1);
1586 let recovered = v.to_account_shared_data();
1587 assert_eq!(recovered, account.1);
1588 sample += 1;
1589 });
1590 trace!("sequential read time: {} ms", now.elapsed().as_millis());
1591 }
1592
1593 #[test_case(StorageAccess::Mmap)]
1594 #[test_case(StorageAccess::File)]
1595 fn test_new_from_file_crafted_zero_lamport_account(storage_access: StorageAccess) {
1596 let file = get_append_vec_path("test_append_bytes");
1640 let path = &file.path;
1641
1642 let accounts_len = 139;
1643 {
1644 let append_vec_data = [
1645 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 192, 118, 150, 1, 185, 209, 118,
1646 82, 154, 222, 172, 202, 110, 26, 218, 140, 143, 96, 61, 43, 212, 73, 203, 7, 190,
1647 88, 80, 222, 110, 114, 67, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1648 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1649 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1650 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1651 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1652 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1653 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1654 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1655 ];
1656
1657 let f = std::fs::File::create(path).unwrap();
1658 let mut writer = std::io::BufWriter::new(f);
1659 writer.write_all(append_vec_data.as_slice()).unwrap();
1660 }
1661
1662 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1663 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1664 }
1665
1666 #[test_case(StorageAccess::Mmap)]
1667 #[test_case(StorageAccess::File)]
1668 fn test_new_from_file_crafted_data_len(storage_access: StorageAccess) {
1669 let file = get_append_vec_path("test_new_from_file_crafted_data_len");
1670 let path = &file.path;
1671 let accounts_len = {
1672 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1674
1675 let crafted_data_len = 1;
1676
1677 av.append_account_test(&create_test_account(10)).unwrap();
1678
1679 av.get_stored_account_meta_callback(0, |account| {
1680 let StoredAccountMeta::AppendVec(account) = account else {
1681 panic!("StoredAccountMeta can only be AppendVec in this test.");
1682 };
1683 account.set_data_len_unsafe(crafted_data_len);
1684 assert_eq!(account.data_len(), crafted_data_len);
1685
1686 av.get_stored_account_meta_callback(0, |account| {
1688 assert_eq!(account.data_len() as u64, crafted_data_len);
1689 });
1690 });
1691
1692 av.flush().unwrap();
1693 av.len()
1694 };
1695 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1696 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1697 }
1698
1699 #[test]
1700 fn test_append_vec_reset() {
1701 let file = get_append_vec_path("test_append_vec_reset");
1702 let path = &file.path;
1703 let av = AppendVec::new(path, true, 1024 * 1024);
1704 av.append_account_test(&create_test_account(10)).unwrap();
1705
1706 assert!(!av.is_empty());
1707 av.reset();
1708 assert_eq!(av.len(), 0);
1709 }
1710
1711 #[test_case(StorageAccess::Mmap)]
1712 #[test_case(StorageAccess::File)]
1713 fn test_append_vec_flush(storage_access: StorageAccess) {
1714 let file = get_append_vec_path("test_append_vec_flush");
1715 let path = &file.path;
1716 let accounts_len = {
1717 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1719 av.append_account_test(&create_test_account(10)).unwrap();
1720 av.len()
1721 };
1722
1723 let (av, num_account) =
1724 AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1725 av.flush().unwrap();
1726 assert_eq!(num_account, 1);
1727 }
1728
1729 #[test_case(StorageAccess::Mmap)]
1730 #[test_case(StorageAccess::File)]
1731 fn test_append_vec_reopen_as_readonly(storage_access: StorageAccess) {
1732 let file = get_append_vec_path("test_append_vec_flush");
1733 let path = &file.path;
1734 let accounts_len = {
1735 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1737 av.append_account_test(&create_test_account(10)).unwrap();
1738 av.len()
1739 };
1740 let (av, _) = AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1741 let reopen = av.reopen_as_readonly();
1742 if storage_access == StorageAccess::File {
1743 assert!(reopen.is_none());
1744 } else {
1745 assert!(reopen.is_some());
1746 }
1747 }
1748
1749 #[test_case(StorageAccess::Mmap)]
1750 #[test_case(StorageAccess::File)]
1751 fn test_new_from_file_too_large_data_len(storage_access: StorageAccess) {
1752 let file = get_append_vec_path("test_new_from_file_too_large_data_len");
1753 let path = &file.path;
1754 let accounts_len = {
1755 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1757
1758 let too_large_data_len = u64::MAX;
1759 av.append_account_test(&create_test_account(10)).unwrap();
1760
1761 av.get_stored_account_meta_callback(0, |account| {
1762 let StoredAccountMeta::AppendVec(account) = account else {
1763 panic!("StoredAccountMeta can only be AppendVec in this test.");
1764 };
1765 account.set_data_len_unsafe(too_large_data_len);
1766 assert_eq!(account.data_len(), too_large_data_len);
1767 })
1768 .unwrap();
1769
1770 assert!(av
1772 .get_stored_account_meta_callback(0, |_| {
1773 panic!("unexpected");
1774 })
1775 .is_none());
1776 av.flush().unwrap();
1777 av.len()
1778 };
1779 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1780 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1781 }
1782
1783 #[test_case(StorageAccess::Mmap)]
1784 #[test_case(StorageAccess::File)]
1785 fn test_new_from_file_crafted_executable(storage_access: StorageAccess) {
1786 let file = get_append_vec_path("test_new_from_crafted_executable");
1787 let path = &file.path;
1788 let accounts_len = {
1789 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1791 av.append_account_test(&create_test_account(10)).unwrap();
1792 let offset_1 = {
1793 let mut executable_account = create_test_account(10);
1794 executable_account.1.set_executable(true);
1795 av.append_account_test(&executable_account).unwrap()
1796 };
1797
1798 let crafted_executable = u8::MAX - 1;
1799
1800 av.get_stored_account_meta_callback(0, |account| {
1803 assert_eq!(*account.ref_executable_byte(), 0);
1804 let StoredAccountMeta::AppendVec(account) = account else {
1805 panic!("StoredAccountMeta can only be AppendVec in this test.");
1806 };
1807 account.set_executable_as_byte(crafted_executable);
1808 })
1809 .unwrap();
1810 av.get_stored_account_meta_callback(offset_1, |account| {
1811 assert_eq!(*account.ref_executable_byte(), 1);
1812 })
1813 .unwrap();
1814
1815 av.get_stored_account_meta_callback(0, |account| {
1817 let StoredAccountMeta::AppendVec(account) = account else {
1818 panic!("StoredAccountMeta can only be AppendVec in this test.");
1819 };
1820
1821 assert!(!account.sanitize_executable());
1823
1824 {
1826 let executable_bool: &bool = &account.account_meta.executable;
1827 assert!(!*executable_bool);
1830 assert_eq!(*account.ref_executable_byte(), crafted_executable);
1831 }
1832
1833 {
1835 let executable_bool: bool = account.executable();
1836 assert!(!executable_bool);
1837 assert_eq!(account.get_executable_byte(), 0); }
1839 })
1840 .unwrap();
1841
1842 av.flush().unwrap();
1843 av.len()
1844 };
1845 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1846 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1847 }
1848
1849 #[test]
1850 fn test_type_layout() {
1851 assert_eq!(offset_of!(StoredMeta, write_version_obsolete), 0x00);
1852 assert_eq!(offset_of!(StoredMeta, data_len), 0x08);
1853 assert_eq!(offset_of!(StoredMeta, pubkey), 0x10);
1854 assert_eq!(mem::size_of::<StoredMeta>(), 0x30);
1855
1856 assert_eq!(offset_of!(AccountMeta, lamports), 0x00);
1857 assert_eq!(offset_of!(AccountMeta, rent_epoch), 0x08);
1858 assert_eq!(offset_of!(AccountMeta, owner), 0x10);
1859 assert_eq!(offset_of!(AccountMeta, executable), 0x30);
1860 assert_eq!(mem::size_of::<AccountMeta>(), 0x38);
1861 }
1862
1863 #[test_case(StorageAccess::Mmap)]
1864 #[test_case(StorageAccess::File)]
1865 fn test_get_account_shared_data_from_truncated_file(storage_access: StorageAccess) {
1866 let file = get_append_vec_path("test_get_account_shared_data_from_truncated_file");
1867 let path = &file.path;
1868
1869 {
1870 let data_len: usize = 2 * PAGE_SIZE as usize;
1873 let account = create_test_account_with(data_len);
1874 let av = ManuallyDrop::new(AppendVec::new(path, true, aligned_stored_size(data_len)));
1876 av.append_account_test(&account).unwrap();
1877 av.flush().unwrap();
1878 }
1879
1880 let truncated_accounts_len: usize = PAGE_SIZE as usize;
1882 let av = AppendVec::new_from_file_unchecked(path, truncated_accounts_len, storage_access)
1883 .unwrap();
1884 let account = av.get_account_shared_data(0);
1885 assert!(account.is_none()); let result = av.get_stored_account_meta_callback(0, |_| true);
1888 assert!(result.is_none()); }
1890
1891 #[test_case(StorageAccess::Mmap)]
1892 #[test_case(StorageAccess::File)]
1893 fn test_get_account_sizes(storage_access: StorageAccess) {
1894 const NUM_ACCOUNTS: usize = 37;
1895 let pubkeys: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
1896 .take(NUM_ACCOUNTS)
1897 .collect();
1898
1899 let mut rng = thread_rng();
1900 let mut accounts = Vec::with_capacity(pubkeys.len());
1901 let mut stored_sizes = Vec::with_capacity(pubkeys.len());
1902 for _ in &pubkeys {
1903 let lamports = rng.gen();
1904 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1905 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1906 accounts.push(account);
1907 stored_sizes.push(aligned_stored_size(data_len));
1908 }
1909 let accounts = accounts;
1910 let stored_sizes = stored_sizes;
1911 let total_stored_size = stored_sizes.iter().sum();
1912
1913 let temp_file = get_append_vec_path("test_get_account_sizes");
1914 let account_offsets = {
1915 let append_vec = AppendVec::new(&temp_file.path, true, total_stored_size);
1916 let append_vec = ManuallyDrop::new(append_vec);
1918 let slot = 77; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1920 let stored_accounts_info = append_vec
1921 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1922 .unwrap();
1923 append_vec.flush().unwrap();
1924 stored_accounts_info.offsets
1925 };
1926
1927 let (append_vec, _) =
1930 AppendVec::new_from_file(&temp_file.path, total_stored_size, storage_access).unwrap();
1931
1932 let account_sizes = append_vec.get_account_sizes(account_offsets.as_slice());
1933 assert_eq!(account_sizes, stored_sizes);
1934 }
1935
1936 fn test_scan_helper(
1941 storage_access: StorageAccess,
1942 modify_fn: impl Fn(&PathBuf, usize) -> usize,
1943 check_fn: impl Fn(&AppendVec, &[Pubkey], &[usize], &[AccountSharedData]),
1944 ) {
1945 const NUM_ACCOUNTS: usize = 37;
1946 let pubkeys: Vec<_> = std::iter::repeat_with(solana_pubkey::new_rand)
1947 .take(NUM_ACCOUNTS)
1948 .collect();
1949
1950 let mut rng = thread_rng();
1951 let mut accounts = Vec::with_capacity(pubkeys.len());
1952 let mut total_stored_size = 0;
1953 for _ in &pubkeys {
1954 let lamports = rng.gen();
1955 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1956 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1957 accounts.push(account);
1958 total_stored_size += aligned_stored_size(data_len);
1959 }
1960 let accounts = accounts;
1961 let total_stored_size = total_stored_size;
1962
1963 let temp_file = get_append_vec_path("test_scan");
1964 let account_offsets = {
1965 let append_vec =
1967 ManuallyDrop::new(AppendVec::new(&temp_file.path, true, total_stored_size));
1968 let slot = 42; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1970 let stored_accounts_info = append_vec
1971 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1972 .unwrap();
1973 append_vec.flush().unwrap();
1974 stored_accounts_info.offsets
1975 };
1976
1977 let total_stored_size = modify_fn(&temp_file.path, total_stored_size);
1978 let append_vec = ManuallyDrop::new(
1981 AppendVec::new_from_file_unchecked(&temp_file.path, total_stored_size, storage_access)
1982 .unwrap(),
1983 );
1984
1985 check_fn(&append_vec, &pubkeys, &account_offsets, &accounts);
1986 }
1987
1988 fn test_scan_pubkeys_helper(
1990 storage_access: StorageAccess,
1991 modify_fn: impl Fn(&PathBuf, usize) -> usize,
1992 ) {
1993 test_scan_helper(
1994 storage_access,
1995 modify_fn,
1996 |append_vec, pubkeys, _account_offsets, _accounts| {
1997 let mut i = 0;
1998 append_vec.scan_pubkeys(|pubkey| {
1999 assert_eq!(pubkey, pubkeys.get(i).unwrap());
2000 i += 1;
2001 });
2002 assert_eq!(i, pubkeys.len());
2003 },
2004 )
2005 }
2006
2007 #[test_case(StorageAccess::Mmap)]
2009 #[test_case(StorageAccess::File)]
2010 fn test_scan_pubkeys(storage_access: StorageAccess) {
2011 test_scan_pubkeys_helper(storage_access, |_, size| size);
2012 }
2013
2014 #[test_case(StorageAccess::Mmap)]
2016 #[test_case(StorageAccess::File)]
2017 fn test_scan_pubkeys_incomplete_data(storage_access: StorageAccess) {
2018 test_scan_pubkeys_helper(storage_access, |path, size| {
2019 let mut f = OpenOptions::new()
2022 .read(true)
2023 .append(true)
2024 .open(path)
2025 .unwrap();
2026 f.write_all(&[0xFF]).unwrap();
2027 size + 1
2028 });
2029 }
2030
2031 #[test_case(StorageAccess::Mmap)]
2033 #[test_case(StorageAccess::File)]
2034 fn test_scan_pubkeys_missing_account_data(storage_access: StorageAccess) {
2035 test_scan_pubkeys_helper(storage_access, |path, size| {
2036 let fake_stored_meta = StoredMeta {
2037 write_version_obsolete: 0,
2038 data_len: 100,
2039 pubkey: solana_pubkey::new_rand(),
2040 };
2041 let fake_account_meta = AccountMeta {
2042 lamports: 100,
2043 rent_epoch: 10,
2044 owner: solana_pubkey::new_rand(),
2045 executable: false,
2046 };
2047
2048 let stored_meta_slice: &[u8] = unsafe {
2049 std::slice::from_raw_parts(
2050 (&fake_stored_meta as *const StoredMeta) as *const u8,
2051 mem::size_of::<StoredMeta>(),
2052 )
2053 };
2054 let account_meta_slice: &[u8] = unsafe {
2055 std::slice::from_raw_parts(
2056 (&fake_account_meta as *const AccountMeta) as *const u8,
2057 mem::size_of::<AccountMeta>(),
2058 )
2059 };
2060
2061 let mut f = OpenOptions::new()
2062 .read(true)
2063 .append(true)
2064 .open(path)
2065 .unwrap();
2066
2067 f.write_all(stored_meta_slice).unwrap();
2068 f.write_all(account_meta_slice).unwrap();
2069
2070 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2071 });
2072 }
2073
2074 fn test_scan_index_helper(
2076 storage_access: StorageAccess,
2077 modify_fn: impl Fn(&PathBuf, usize) -> usize,
2078 ) {
2079 test_scan_helper(
2080 storage_access,
2081 modify_fn,
2082 |append_vec, pubkeys, account_offsets, accounts| {
2083 let mut i = 0;
2084 append_vec.scan_index(|index_info| {
2085 let pubkey = pubkeys.get(i).unwrap();
2086 let account = accounts.get(i).unwrap();
2087 let offset = account_offsets.get(i).unwrap();
2088
2089 assert_eq!(
2090 index_info.stored_size_aligned,
2091 aligned_stored_size(account.data().len()),
2092 );
2093 assert_eq!(index_info.index_info.offset, *offset);
2094 assert_eq!(index_info.index_info.pubkey, *pubkey);
2095 assert_eq!(index_info.index_info.lamports, account.lamports());
2096 assert_eq!(index_info.index_info.rent_epoch, account.rent_epoch());
2097 assert_eq!(index_info.index_info.executable, account.executable());
2098 assert_eq!(index_info.index_info.data_len, account.data().len() as u64);
2099
2100 i += 1;
2101 });
2102 assert_eq!(i, accounts.len());
2103 },
2104 )
2105 }
2106
2107 #[test_case(StorageAccess::Mmap)]
2108 #[test_case(StorageAccess::File)]
2109 fn test_scan_index(storage_access: StorageAccess) {
2110 test_scan_index_helper(storage_access, |_, size| size);
2111 }
2112
2113 #[test_case(StorageAccess::Mmap)]
2115 #[test_case(StorageAccess::File)]
2116 fn test_scan_index_incomplete_data(storage_access: StorageAccess) {
2117 test_scan_index_helper(storage_access, |path, size| {
2118 let mut f = OpenOptions::new()
2121 .read(true)
2122 .append(true)
2123 .open(path)
2124 .unwrap();
2125 f.write_all(&[0xFF]).unwrap();
2126 size + 1
2127 });
2128 }
2129
2130 #[test_case(StorageAccess::Mmap)]
2132 #[test_case(StorageAccess::File)]
2133 fn test_scan_index_missing_account_data(storage_access: StorageAccess) {
2134 test_scan_index_helper(storage_access, |path, size| {
2135 let fake_stored_meta = StoredMeta {
2136 write_version_obsolete: 0,
2137 data_len: 100,
2138 pubkey: solana_pubkey::new_rand(),
2139 };
2140 let fake_account_meta = AccountMeta {
2141 lamports: 100,
2142 rent_epoch: 10,
2143 owner: solana_pubkey::new_rand(),
2144 executable: false,
2145 };
2146
2147 let stored_meta_slice: &[u8] = unsafe {
2148 std::slice::from_raw_parts(
2149 (&fake_stored_meta as *const StoredMeta) as *const u8,
2150 mem::size_of::<StoredMeta>(),
2151 )
2152 };
2153 let account_meta_slice: &[u8] = unsafe {
2154 std::slice::from_raw_parts(
2155 (&fake_account_meta as *const AccountMeta) as *const u8,
2156 mem::size_of::<AccountMeta>(),
2157 )
2158 };
2159
2160 let mut f = OpenOptions::new()
2161 .read(true)
2162 .append(true)
2163 .open(path)
2164 .unwrap();
2165
2166 f.write_all(stored_meta_slice).unwrap();
2167 f.write_all(account_meta_slice).unwrap();
2168
2169 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2170 });
2171 }
2172}