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, Stack},
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 self,
32 convert::TryFrom,
33 fs::{remove_file, File, OpenOptions},
34 io::{Seek, SeekFrom, Write},
35 mem::{self, MaybeUninit},
36 path::{Path, PathBuf},
37 ptr, slice,
38 sync::{
39 atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
40 Mutex,
41 },
42 },
43 thiserror::Error,
44};
45
46pub mod test_utils;
47#[cfg(test)]
48use solana_sdk::account::accounts_equal;
49
50pub const STORE_META_OVERHEAD: usize = 136;
53
54const _: () = assert!(
56 STORE_META_OVERHEAD
57 == mem::size_of::<StoredMeta>()
58 + mem::size_of::<AccountMeta>()
59 + mem::size_of::<AccountHash>()
60);
61
62pub fn aligned_stored_size(data_len: usize) -> usize {
66 u64_align!(STORE_META_OVERHEAD + data_len)
67}
68
69pub const MAXIMUM_APPEND_VEC_FILE_SIZE: u64 = 16 * 1024 * 1024 * 1024; #[derive(Error, Debug)]
72pub enum AppendVecError {
74 #[error("too small file size {0} for AppendVec")]
75 FileSizeTooSmall(usize),
76
77 #[error("too large file size {0} for AppendVec")]
78 FileSizeTooLarge(usize),
79
80 #[error("incorrect layout/length/data in the appendvec at path {}", .0.display())]
81 IncorrectLayout(PathBuf),
82
83 #[error("offset ({0}) is larger than file size ({1})")]
84 OffsetOutOfBounds(usize, usize),
85}
86
87#[derive(Debug, Copy, Clone)]
90pub(crate) struct ValidSlice<'a>(&'a [u8]);
91
92impl<'a> ValidSlice<'a> {
93 #[inline(always)]
94 pub(crate) fn new(data: &'a [u8]) -> Self {
95 Self(data)
96 }
97
98 #[inline(always)]
99 pub(crate) fn len(&self) -> usize {
100 self.0.len()
101 }
102
103 #[inline(always)]
104 #[cfg(all(unix, test))]
105 pub(crate) fn slice(&self) -> &[u8] {
106 self.0
107 }
108}
109
110#[derive(PartialEq, Eq, Debug)]
113pub struct AppendVecStoredAccountMeta<'append_vec> {
114 pub meta: &'append_vec StoredMeta,
115 pub account_meta: &'append_vec AccountMeta,
117 pub(crate) data: &'append_vec [u8],
118 pub(crate) offset: usize,
119 pub(crate) stored_size: usize,
120 pub(crate) hash: &'append_vec AccountHash,
121}
122
123impl<'append_vec> AppendVecStoredAccountMeta<'append_vec> {
124 pub fn pubkey(&self) -> &'append_vec Pubkey {
125 &self.meta.pubkey
126 }
127
128 pub fn hash(&self) -> &'append_vec AccountHash {
129 self.hash
130 }
131
132 pub fn stored_size(&self) -> usize {
133 self.stored_size
134 }
135
136 pub fn offset(&self) -> usize {
137 self.offset
138 }
139
140 pub fn data(&self) -> &'append_vec [u8] {
141 self.data
142 }
143
144 pub fn data_len(&self) -> u64 {
145 self.meta.data_len
146 }
147
148 pub fn meta(&self) -> &StoredMeta {
149 self.meta
150 }
151
152 pub(crate) fn sanitize(&self) -> bool {
153 self.sanitize_executable() && self.sanitize_lamports()
154 }
155
156 fn sanitize_executable(&self) -> bool {
157 self.ref_executable_byte() & !1 == 0
159 }
160
161 fn sanitize_lamports(&self) -> bool {
162 self.account_meta.lamports != 0
164 || self.to_account_shared_data() == AccountSharedData::default()
165 }
166
167 fn ref_executable_byte(&self) -> &u8 {
168 let executable_bool: &bool = &self.account_meta.executable;
171 let executable_bool_ptr = ptr::from_ref(executable_bool);
172 let executable_byte: &u8 = unsafe { &*(executable_bool_ptr.cast()) };
174 executable_byte
175 }
176}
177
178impl<'append_vec> ReadableAccount for AppendVecStoredAccountMeta<'append_vec> {
179 fn lamports(&self) -> u64 {
180 self.account_meta.lamports
181 }
182 fn data(&self) -> &'append_vec [u8] {
183 self.data()
184 }
185 fn owner(&self) -> &'append_vec Pubkey {
186 &self.account_meta.owner
187 }
188 fn executable(&self) -> bool {
189 self.account_meta.executable
190 }
191 fn rent_epoch(&self) -> Epoch {
192 self.account_meta.rent_epoch
193 }
194}
195
196pub(crate) struct IndexInfo {
198 pub stored_size_aligned: usize,
201 pub index_info: IndexInfoInner,
203}
204
205pub(crate) struct IndexInfoInner {
207 pub offset: usize,
209 pub pubkey: Pubkey,
210 pub lamports: u64,
211 pub rent_epoch: Epoch,
212 pub executable: bool,
213 pub data_len: u64,
214}
215
216impl ZeroLamport for IndexInfoInner {
217 #[inline(always)]
218 fn is_zero_lamport(&self) -> bool {
219 self.lamports == 0
220 }
221}
222
223impl ZeroLamport for IndexInfo {
224 #[inline(always)]
225 fn is_zero_lamport(&self) -> bool {
226 self.index_info.is_zero_lamport()
227 }
228}
229
230#[derive(Debug)]
232struct AccountOffsets {
233 offset_to_end_of_data: usize,
235 next_account_offset: usize,
237 stored_size_aligned: usize,
239}
240
241#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
242#[derive(Debug)]
243enum AppendVecFileBacking {
244 Mmap(Mmap),
246 #[cfg_attr(not(unix), allow(dead_code))]
248 File(File),
249}
250
251#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
252#[derive(Debug)]
253struct Mmap {
254 mmap: MmapMut,
255}
256
257#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
262#[derive(Debug)]
263pub struct AppendVec {
264 path: PathBuf,
266
267 backing: AppendVecFileBacking,
269
270 append_lock: Mutex<()>,
272
273 current_len: AtomicUsize,
275
276 file_size: u64,
278
279 remove_file_on_drop: AtomicBool,
281
282 is_dirty: AtomicBool,
288}
289
290const PAGE_SIZE: usize = 4 * 1024;
291const SCAN_BUFFER_SIZE: usize = 1024 * 1024;
293
294pub struct AppendVecStat {
295 pub open_as_mmap: AtomicU64,
296 pub open_as_file_io: AtomicU64,
297 pub files_open: AtomicU64,
298 pub files_dirty: AtomicU64,
299}
300
301lazy_static! {
302 pub static ref APPEND_VEC_STATS: AppendVecStat = AppendVecStat {
303 open_as_mmap: AtomicU64::new(0),
304 open_as_file_io: AtomicU64::new(0),
305 files_open: AtomicU64::new(0),
306 files_dirty: AtomicU64::new(0),
307 };
308}
309
310impl Drop for AppendVec {
311 fn drop(&mut self) {
312 APPEND_VEC_STATS.files_open.fetch_sub(1, Ordering::Relaxed);
313
314 if *self.is_dirty.get_mut() {
315 APPEND_VEC_STATS.files_dirty.fetch_sub(1, Ordering::Relaxed);
316 }
317
318 match &self.backing {
319 AppendVecFileBacking::Mmap(_) => {
320 APPEND_VEC_STATS
321 .open_as_mmap
322 .fetch_sub(1, Ordering::Relaxed);
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.files_open.fetch_add(1, Ordering::Relaxed);
389
390 APPEND_VEC_STATS
391 .open_as_mmap
392 .fetch_add(1, Ordering::Relaxed);
393
394 AppendVec {
395 path: file,
396 backing: AppendVecFileBacking::Mmap(Mmap { mmap }),
397 append_lock: Mutex::new(()),
400 current_len: AtomicUsize::new(initial_len),
401 file_size: size as u64,
402 remove_file_on_drop: AtomicBool::new(true),
403 is_dirty: AtomicBool::new(false),
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 let should_flush = self.is_dirty.swap(false, Ordering::AcqRel);
435 if should_flush {
436 match &self.backing {
437 AppendVecFileBacking::Mmap(mmap) => {
438 mmap.mmap.flush()?;
439 }
440 AppendVecFileBacking::File(file) => {
441 file.sync_all()?;
442 }
443 }
444 APPEND_VEC_STATS.files_dirty.fetch_sub(1, Ordering::Relaxed);
445 }
446 Ok(())
447 }
448
449 pub fn reset(&self) {
450 let _lock = self.append_lock.lock().unwrap();
453 self.current_len.store(0, Ordering::Release);
454 }
455
456 #[cfg_attr(not(unix), allow(dead_code))]
459 pub(crate) fn reopen_as_readonly(&self) -> Option<Self> {
460 #[cfg(not(unix))]
461 return None;
463
464 #[cfg(unix)]
465 match &self.backing {
466 AppendVecFileBacking::File(_file) => {
467 None
469 }
470 AppendVecFileBacking::Mmap(_mmap) => {
471 self.remove_file_on_drop.store(false, Ordering::Release);
474
475 std::sync::atomic::fence(Ordering::AcqRel);
478
479 let mut new = AppendVec::new_from_file_unchecked(
481 self.path.clone(),
482 self.len(),
483 StorageAccess::File,
484 )
485 .ok()?;
486 if self.is_dirty.swap(false, Ordering::AcqRel) {
487 *new.is_dirty.get_mut() = true;
489 }
490 Some(new)
491 }
492 }
493 }
494
495 pub fn remaining_bytes(&self) -> u64 {
497 self.capacity()
498 .saturating_sub(u64_align!(self.len()) as u64)
499 }
500
501 pub fn len(&self) -> usize {
502 self.current_len.load(Ordering::Acquire)
503 }
504
505 pub fn is_empty(&self) -> bool {
506 self.len() == 0
507 }
508
509 pub fn capacity(&self) -> u64 {
510 self.file_size
511 }
512
513 pub fn new_from_file(
514 path: impl Into<PathBuf>,
515 current_len: usize,
516 storage_access: StorageAccess,
517 ) -> Result<(Self, usize)> {
518 let path = path.into();
519 let new = Self::new_from_file_unchecked(path, current_len, storage_access)?;
520
521 let (sanitized, num_accounts) = new.sanitize_layout_and_length();
522 if !sanitized {
523 return Err(AccountsFileError::AppendVecError(
524 AppendVecError::IncorrectLayout(new.path.clone()),
525 ));
526 }
527
528 Ok((new, num_accounts))
529 }
530
531 #[cfg_attr(not(unix), allow(unused_variables))]
533 pub fn new_from_file_unchecked(
534 path: impl Into<PathBuf>,
535 current_len: usize,
536 storage_access: StorageAccess,
537 ) -> Result<Self> {
538 let path = path.into();
539 let file_size = std::fs::metadata(&path)?.len();
540 Self::sanitize_len_and_size(current_len, file_size as usize)?;
541
542 let data = OpenOptions::new()
543 .read(true)
544 .write(true)
545 .create(false)
546 .open(&path)?;
547
548 #[cfg(unix)]
549 if storage_access == StorageAccess::File {
551 APPEND_VEC_STATS.files_open.fetch_add(1, Ordering::Relaxed);
552
553 APPEND_VEC_STATS
554 .open_as_file_io
555 .fetch_add(1, Ordering::Relaxed);
556
557 return Ok(AppendVec {
558 path,
559 backing: AppendVecFileBacking::File(data),
560 append_lock: Mutex::new(()),
561 current_len: AtomicUsize::new(current_len),
562 file_size,
563 remove_file_on_drop: AtomicBool::new(true),
564 is_dirty: AtomicBool::new(false),
565 });
566 }
567
568 let mmap = unsafe {
569 let result = MmapMut::map_mut(&data);
570 if result.is_err() {
571 info!("memory map error: {:?}. This may be because vm.max_map_count is not set correctly.", result);
573 }
574 result?
575 };
576
577 APPEND_VEC_STATS.files_open.fetch_add(1, Ordering::Relaxed);
578
579 APPEND_VEC_STATS
580 .open_as_mmap
581 .fetch_add(1, Ordering::Relaxed);
582
583 Ok(AppendVec {
584 path,
585 backing: AppendVecFileBacking::Mmap(Mmap { mmap }),
586 append_lock: Mutex::new(()),
587 current_len: AtomicUsize::new(current_len),
588 file_size,
589 remove_file_on_drop: AtomicBool::new(true),
590 is_dirty: AtomicBool::new(false),
591 })
592 }
593
594 #[cfg(feature = "dev-context-only-utils")]
596 pub fn new_for_store_tool(path: impl Into<PathBuf>) -> Result<Self> {
597 let path = path.into();
598 let file_size = std::fs::metadata(&path)?.len();
599 Self::new_from_file_unchecked(path, file_size as usize, StorageAccess::default())
600 }
601
602 fn sanitize_layout_and_length(&self) -> (bool, usize) {
603 let mut num_accounts = 0;
609 let mut matches = true;
610 let mut last_offset = 0;
611 self.scan_accounts(|account| {
612 if !matches || !account.sanitize() {
613 matches = false;
614 return;
615 }
616 last_offset = account.offset() + account.stored_size();
617 num_accounts += 1;
618 });
619 if !matches {
620 return (false, num_accounts);
621 }
622 let aligned_current_len = u64_align!(self.current_len.load(Ordering::Acquire));
623
624 (last_offset == aligned_current_len, num_accounts)
625 }
626
627 fn get_slice(slice: ValidSlice, offset: usize, size: usize) -> Option<(&[u8], usize)> {
632 let end = offset.wrapping_add(size);
635 slice
636 .0
637 .get(offset..end)
638 .map(|subslice| (subslice, u64_align!(end)))
639 }
640
641 fn append_ptr(&self, offset: &mut usize, src: *const u8, len: usize) {
644 let pos = u64_align!(*offset);
645 match &self.backing {
646 AppendVecFileBacking::Mmap(mmap_only) => {
647 let data = &mmap_only.mmap[pos..(pos + len)];
648 unsafe {
652 let dst = data.as_ptr() as *mut _;
653 ptr::copy(src, dst, len);
654 };
655 *offset = pos + len;
656 }
657 AppendVecFileBacking::File(_file) => {
658 unimplemented!();
659 }
660 }
661 }
662
663 fn append_ptrs_locked(&self, offset: &mut usize, vals: &[(*const u8, usize)]) -> Option<usize> {
668 let mut end = *offset;
669 for val in vals {
670 end = u64_align!(end);
671 end += val.1;
672 }
673
674 if (self.file_size as usize) < end {
675 return None;
676 }
677
678 let pos = u64_align!(*offset);
679 for val in vals {
680 self.append_ptr(offset, val.0, val.1)
681 }
682 self.current_len.store(*offset, Ordering::Release);
683 Some(pos)
684 }
685
686 fn get_type<T>(slice: ValidSlice, offset: usize) -> Option<(&T, usize)> {
690 let (data, next) = Self::get_slice(slice, offset, mem::size_of::<T>())?;
691 let ptr = data.as_ptr().cast();
692 Some((unsafe { &*ptr }, next))
695 }
696
697 fn get_valid_slice_from_mmap<'a>(&self, mmap: &'a MmapMut) -> ValidSlice<'a> {
701 ValidSlice(&mmap[..self.len()])
702 }
703
704 pub fn get_stored_account_meta_callback<Ret>(
707 &self,
708 offset: usize,
709 mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>) -> Ret,
710 ) -> Option<Ret> {
711 match &self.backing {
712 AppendVecFileBacking::Mmap(Mmap { mmap }) => {
713 let slice = self.get_valid_slice_from_mmap(mmap);
714 let (meta, next): (&StoredMeta, _) = Self::get_type(slice, offset)?;
715 let (account_meta, next): (&AccountMeta, _) = Self::get_type(slice, next)?;
716 let (hash, next): (&AccountHash, _) = Self::get_type(slice, next)?;
717 let (data, next) = Self::get_slice(slice, next, meta.data_len as usize)?;
718 let stored_size = next - offset;
719 Some(callback(StoredAccountMeta::AppendVec(
720 AppendVecStoredAccountMeta {
721 meta,
722 account_meta,
723 data,
724 offset,
725 stored_size,
726 hash,
727 },
728 )))
729 }
730 AppendVecFileBacking::File(file) => {
731 let mut buf = [MaybeUninit::<u8>::uninit(); PAGE_SIZE];
733 let bytes_read = read_into_buffer(file, self.len(), offset, unsafe {
735 slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len())
736 })
737 .ok()?;
738 let valid_bytes = ValidSlice(unsafe {
740 slice::from_raw_parts(buf.as_ptr() as *const u8, bytes_read)
741 });
742 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
743 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
744 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
745 let data_len = meta.data_len;
746 let remaining_bytes_for_data = bytes_read - next;
747 Some(if remaining_bytes_for_data >= data_len as usize {
748 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
750 let stored_size = next;
751 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
752 meta,
753 account_meta,
754 data,
755 offset,
756 stored_size,
757 hash,
758 });
759 callback(account)
760 } else {
761 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
763 let mut data: Box<[MaybeUninit<u8>]> = Box::new_uninit_slice(data_len as usize);
764 let bytes_read = read_into_buffer(file, self.len(), offset + next, unsafe {
768 slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, data_len as usize)
769 })
770 .ok()?;
771 if bytes_read < data_len as usize {
772 return None;
774 }
775 let data = unsafe { data.assume_init() };
777 let stored_size = aligned_stored_size(data_len as usize);
778 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
779 meta,
780 account_meta,
781 data: &data[..],
782 offset,
783 stored_size,
784 hash,
785 });
786 callback(account)
787 })
788 }
789 }
790 }
791
792 pub(crate) fn get_account_shared_data(&self, offset: usize) -> Option<AccountSharedData> {
796 match &self.backing {
797 AppendVecFileBacking::Mmap(_) => self
798 .get_stored_account_meta_callback(offset, |account| {
799 account.to_account_shared_data()
800 }),
801 AppendVecFileBacking::File(file) => {
802 let mut buf = MaybeUninit::<[u8; PAGE_SIZE]>::uninit();
803 let bytes_read =
804 read_into_buffer(file, self.len(), offset, unsafe { &mut *buf.as_mut_ptr() })
805 .ok()?;
806 let valid_bytes = ValidSlice(unsafe {
808 slice::from_raw_parts(buf.as_ptr() as *const u8, bytes_read)
809 });
810 let (meta, next): (&StoredMeta, _) = Self::get_type(valid_bytes, 0)?;
811 let (account_meta, next): (&AccountMeta, _) = Self::get_type(valid_bytes, next)?;
812 let (hash, next): (&AccountHash, _) = Self::get_type(valid_bytes, next)?;
813 let data_len = meta.data_len;
814 let remaining_bytes_for_data = bytes_read - next;
815 Some(if remaining_bytes_for_data >= data_len as usize {
816 let (data, next) = Self::get_slice(valid_bytes, next, meta.data_len as usize)?;
818 let stored_size = next;
819 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
820 meta,
821 account_meta,
822 data,
823 offset,
824 stored_size,
825 hash,
826 });
827 account.to_account_shared_data()
829 } else {
830 assert!(data_len <= MAX_PERMITTED_DATA_LENGTH, "{data_len}");
832 let mut data = Vec::with_capacity(data_len as usize);
833 let slice = data.spare_capacity_mut();
834 let bytes_read = read_into_buffer(file, self.len(), offset + next, unsafe {
837 slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, data_len as usize)
838 })
839 .ok()?;
840 if bytes_read < data_len as usize {
841 return None;
843 }
844 unsafe { data.set_len(data_len as usize) };
846 AccountSharedData::create(
847 account_meta.lamports,
848 data,
849 account_meta.owner,
850 account_meta.executable,
851 account_meta.rent_epoch,
852 )
853 })
854 }
855 }
856 }
857
858 pub fn account_matches_owners(
863 &self,
864 offset: usize,
865 owners: &[Pubkey],
866 ) -> std::result::Result<usize, MatchAccountOwnerError> {
867 self.get_stored_account_meta_callback(offset, |stored_account_meta| {
868 if stored_account_meta.lamports() == 0 {
869 Err(MatchAccountOwnerError::NoMatch)
870 } else {
871 owners
872 .iter()
873 .position(|entry| stored_account_meta.owner() == entry)
874 .ok_or(MatchAccountOwnerError::NoMatch)
875 }
876 })
877 .unwrap_or(Err(MatchAccountOwnerError::UnableToLoad))
878 }
879
880 #[cfg(test)]
881 pub fn get_account_test(
882 &self,
883 offset: usize,
884 ) -> Option<(StoredMeta, solana_sdk::account::AccountSharedData)> {
885 let sizes = self.get_account_sizes(&[offset]);
886 let result = self.get_stored_account_meta_callback(offset, |r_callback| {
887 let r2 = self.get_account_shared_data(offset);
888 assert!(accounts_equal(&r_callback, r2.as_ref().unwrap()));
889 assert_eq!(sizes, vec![r_callback.stored_size()]);
890 let meta = r_callback.meta().clone();
891 Some((meta, r_callback.to_account_shared_data()))
892 });
893 if result.is_none() {
894 assert!(self
895 .get_stored_account_meta_callback(offset, |_| {})
896 .is_none());
897 assert!(self.get_account_shared_data(offset).is_none());
898 assert!(sizes.is_empty());
900 }
901 result.flatten()
902 }
903
904 pub fn path(&self) -> &Path {
906 self.path.as_path()
907 }
908
909 fn next_account_offset(start_offset: usize, stored_meta: &StoredMeta) -> AccountOffsets {
916 let stored_size_unaligned = STORE_META_OVERHEAD
917 .checked_add(stored_meta.data_len as usize)
918 .expect("stored size cannot overflow");
919 let stored_size_aligned = u64_align!(stored_size_unaligned);
920 let offset_to_end_of_data = start_offset + stored_size_unaligned;
921 let next_account_offset = start_offset + stored_size_aligned;
922
923 AccountOffsets {
924 next_account_offset,
925 offset_to_end_of_data,
926 stored_size_aligned,
927 }
928 }
929
930 pub(crate) fn scan_index(&self, mut callback: impl FnMut(IndexInfo)) {
933 let self_len = self.len();
935 match &self.backing {
936 AppendVecFileBacking::Mmap(Mmap { mmap }) => {
937 let mut offset = 0;
938 let slice = self.get_valid_slice_from_mmap(mmap);
939 loop {
940 let Some((stored_meta, next)) = Self::get_type::<StoredMeta>(slice, offset)
941 else {
942 break;
944 };
945 let Some((account_meta, _)) = Self::get_type::<AccountMeta>(slice, next) else {
946 break;
948 };
949 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
950 break;
952 }
953 let next = Self::next_account_offset(offset, stored_meta);
954 if next.offset_to_end_of_data > self_len {
955 break;
957 }
958 callback(IndexInfo {
959 index_info: {
960 IndexInfoInner {
961 pubkey: stored_meta.pubkey,
962 lamports: account_meta.lamports,
963 offset,
964 data_len: stored_meta.data_len,
965 executable: account_meta.executable,
966 rent_epoch: account_meta.rent_epoch,
967 }
968 },
969 stored_size_aligned: next.stored_size_aligned,
970 });
971 offset = next.next_account_offset;
972 }
973 }
974 AppendVecFileBacking::File(file) => {
975 let mut reader = BufferedReader::<Stack<PAGE_SIZE>>::new_stack(
976 self_len,
977 file,
978 mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>(),
979 );
980 while let Ok(BufferedReaderStatus::Success) = reader.read() {
981 let (offset, bytes) = reader.get_offset_and_data();
982 let (stored_meta, next) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
983 let (account_meta, _) = Self::get_type::<AccountMeta>(bytes, next).unwrap();
984 if account_meta.lamports == 0 && stored_meta.pubkey == Pubkey::default() {
985 break;
987 }
988 let next = Self::next_account_offset(offset, stored_meta);
989 if next.offset_to_end_of_data > self_len {
990 break;
992 }
993 callback(IndexInfo {
994 index_info: {
995 IndexInfoInner {
996 pubkey: stored_meta.pubkey,
997 lamports: account_meta.lamports,
998 offset,
999 data_len: stored_meta.data_len,
1000 executable: account_meta.executable,
1001 rent_epoch: account_meta.rent_epoch,
1002 }
1003 },
1004 stored_size_aligned: next.stored_size_aligned,
1005 });
1006 reader.advance_offset(next.stored_size_aligned);
1007 }
1008 }
1009 }
1010 }
1011
1012 #[allow(clippy::blocks_in_conditions)]
1014 pub fn scan_accounts(&self, mut callback: impl for<'local> FnMut(StoredAccountMeta<'local>)) {
1015 match &self.backing {
1016 AppendVecFileBacking::Mmap(_mmap) => {
1017 let mut offset = 0;
1018 while self
1019 .get_stored_account_meta_callback(offset, |account| {
1020 offset += account.stored_size();
1021 if account.is_zero_lamport() && account.pubkey() == &Pubkey::default() {
1022 return false;
1024 }
1025
1026 callback(account);
1027 true
1028 })
1029 .unwrap_or_default()
1030 {}
1031 }
1032 AppendVecFileBacking::File(file) => {
1033 let mut reader = BufferedReader::new_heap(
1034 SCAN_BUFFER_SIZE,
1035 self.len(),
1036 file,
1037 STORE_META_OVERHEAD,
1038 );
1039 while let Ok(BufferedReaderStatus::Success) = reader.read() {
1040 let (offset, bytes_subset) = reader.get_offset_and_data();
1041 let (meta, next): (&StoredMeta, _) = Self::get_type(bytes_subset, 0).unwrap();
1042 let (account_meta, next): (&AccountMeta, _) =
1043 Self::get_type(bytes_subset, next).unwrap();
1044 let (hash, next): (&AccountHash, _) =
1045 Self::get_type(bytes_subset, next).unwrap();
1046 let data_len = meta.data_len as usize;
1047 if bytes_subset.len() - next >= data_len {
1048 let data = &bytes_subset.0[next..(next + data_len)];
1050 let stored_size = u64_align!(next + data_len);
1051 let account = StoredAccountMeta::AppendVec(AppendVecStoredAccountMeta {
1052 meta,
1053 account_meta,
1054 data,
1055 offset,
1056 stored_size,
1057 hash,
1058 });
1059 callback(account);
1060 reader.advance_offset(stored_size);
1061 } else {
1062 reader.resize(STORE_META_OVERHEAD + MAX_PERMITTED_DATA_LENGTH as usize);
1064 reader.set_required_data_len(STORE_META_OVERHEAD.saturating_add(data_len))
1066 }
1067 }
1068 }
1069 }
1070 }
1071
1072 pub(crate) fn get_account_sizes(&self, sorted_offsets: &[usize]) -> Vec<usize> {
1074 let self_len = self.len();
1076 let mut account_sizes = Vec::with_capacity(sorted_offsets.len());
1077 match &self.backing {
1078 AppendVecFileBacking::Mmap(Mmap { mmap }) => {
1079 let slice = self.get_valid_slice_from_mmap(mmap);
1080 for &offset in sorted_offsets {
1081 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1082 break;
1083 };
1084 let next = Self::next_account_offset(offset, stored_meta);
1085 if next.offset_to_end_of_data > self_len {
1086 break;
1088 }
1089 account_sizes.push(next.stored_size_aligned);
1090 }
1091 }
1092 AppendVecFileBacking::File(file) => {
1093 let mut buffer = [MaybeUninit::<u8>::uninit(); mem::size_of::<StoredMeta>()];
1094 for &offset in sorted_offsets {
1095 let Some(bytes_read) = read_into_buffer(file, self_len, offset, unsafe {
1097 slice::from_raw_parts_mut(
1098 buffer.as_mut_ptr() as *mut u8,
1099 mem::size_of::<StoredMeta>(),
1100 )
1101 })
1102 .ok() else {
1103 break;
1104 };
1105 let bytes = ValidSlice(unsafe {
1107 slice::from_raw_parts(buffer.as_ptr() as *const u8, bytes_read)
1108 });
1109 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(bytes, 0) else {
1110 break;
1111 };
1112 let next = Self::next_account_offset(offset, stored_meta);
1113 if next.offset_to_end_of_data > self_len {
1114 break;
1116 }
1117 account_sizes.push(next.stored_size_aligned);
1118 }
1119 }
1120 }
1121 account_sizes
1122 }
1123
1124 pub fn scan_pubkeys(&self, mut callback: impl FnMut(&Pubkey)) {
1130 let self_len = self.len();
1132 match &self.backing {
1133 AppendVecFileBacking::Mmap(Mmap { mmap }) => {
1134 let mut offset = 0;
1135 let slice = self.get_valid_slice_from_mmap(mmap);
1136 loop {
1137 let Some((stored_meta, _)) = Self::get_type::<StoredMeta>(slice, offset) else {
1138 break;
1140 };
1141 let next = Self::next_account_offset(offset, stored_meta);
1142 if next.offset_to_end_of_data > self_len {
1143 break;
1145 }
1146 callback(&stored_meta.pubkey);
1147 offset = next.next_account_offset;
1148 }
1149 }
1150 AppendVecFileBacking::File(file) => {
1151 let mut reader = BufferedReader::<Stack<PAGE_SIZE>>::new_stack(
1152 self_len,
1153 file,
1154 mem::size_of::<StoredMeta>(),
1155 );
1156 while let Ok(BufferedReaderStatus::Success) = reader.read() {
1157 let (offset, bytes) = reader.get_offset_and_data();
1158 let (stored_meta, _) = Self::get_type::<StoredMeta>(bytes, 0).unwrap();
1159 let next = Self::next_account_offset(offset, stored_meta);
1160 if next.offset_to_end_of_data > self_len {
1161 break;
1163 }
1164 callback(&stored_meta.pubkey);
1165 reader.advance_offset(next.stored_size_aligned);
1167 }
1168 }
1169 }
1170 }
1171
1172 pub fn append_accounts<'a>(
1180 &self,
1181 accounts: &impl StorableAccounts<'a>,
1182 skip: usize,
1183 ) -> Option<StoredAccountsInfo> {
1184 let _lock = self.append_lock.lock().unwrap();
1185 let default_hash = Hash::default();
1186 let mut offset = self.len();
1187 let len = accounts.len();
1188 let offsets_len = len - skip + 1;
1192 let mut offsets = Vec::with_capacity(offsets_len);
1193 let mut stop = false;
1194 for i in skip..len {
1195 if stop {
1196 break;
1197 }
1198 accounts.account_default_if_zero_lamport(i, |account| {
1199 let account_meta = AccountMeta {
1200 lamports: account.lamports(),
1201 owner: *account.owner(),
1202 rent_epoch: account.rent_epoch(),
1203 executable: account.executable(),
1204 };
1205
1206 let stored_meta = StoredMeta {
1207 pubkey: *account.pubkey(),
1208 data_len: account.data().len() as u64,
1209 write_version_obsolete: 0,
1210 };
1211 let stored_meta_ptr = ptr::from_ref(&stored_meta).cast();
1212 let account_meta_ptr = ptr::from_ref(&account_meta).cast();
1213 let hash_ptr = bytemuck::bytes_of(&default_hash).as_ptr();
1214 let data_ptr = account.data().as_ptr();
1215 let ptrs = [
1216 (stored_meta_ptr, mem::size_of::<StoredMeta>()),
1217 (account_meta_ptr, mem::size_of::<AccountMeta>()),
1218 (hash_ptr, mem::size_of::<AccountHash>()),
1219 (data_ptr, stored_meta.data_len as usize),
1220 ];
1221 if let Some(start_offset) = self.append_ptrs_locked(&mut offset, &ptrs) {
1222 offsets.push(start_offset)
1223 } else {
1224 stop = true;
1225 }
1226 });
1227 }
1228
1229 if !offsets.is_empty() {
1230 let was_dirty = self.is_dirty.swap(true, Ordering::AcqRel);
1233 if !was_dirty {
1234 APPEND_VEC_STATS.files_dirty.fetch_add(1, Ordering::Relaxed);
1235 }
1236 }
1237
1238 (!offsets.is_empty()).then(|| {
1239 offsets.push(u64_align!(offset));
1244 let size = offsets.windows(2).map(|offset| offset[1] - offset[0]).sum();
1245 offsets.pop();
1246
1247 StoredAccountsInfo { offsets, size }
1248 })
1249 }
1250
1251 pub(crate) fn can_append(&self) -> bool {
1252 match &self.backing {
1253 AppendVecFileBacking::File(_file) => false,
1254 AppendVecFileBacking::Mmap(_mmap) => true,
1255 }
1256 }
1257
1258 pub(crate) fn internals_for_archive(&self) -> InternalsForArchive {
1260 match &self.backing {
1261 AppendVecFileBacking::File(_file) => InternalsForArchive::FileIo(self.path()),
1262 AppendVecFileBacking::Mmap(Mmap { mmap }) => InternalsForArchive::Mmap(mmap),
1264 }
1265 }
1266}
1267
1268#[cfg(test)]
1269pub mod tests {
1270 use {
1271 super::{test_utils::*, *},
1272 assert_matches::assert_matches,
1273 memoffset::offset_of,
1274 rand::{thread_rng, Rng},
1275 solana_sdk::{
1276 account::{Account, AccountSharedData},
1277 clock::Slot,
1278 },
1279 std::{mem::ManuallyDrop, time::Instant},
1280 test_case::test_case,
1281 };
1282
1283 impl AppendVec {
1284 pub(crate) fn set_current_len_for_tests(&self, len: usize) {
1285 self.current_len.store(len, Ordering::Release);
1286 }
1287
1288 fn append_account_test(&self, data: &(StoredMeta, AccountSharedData)) -> Option<usize> {
1289 let slot_ignored = Slot::MAX;
1290 let accounts = [(&data.0.pubkey, &data.1)];
1291 let slice = &accounts[..];
1292 let storable_accounts = (slot_ignored, slice);
1293
1294 self.append_accounts(&storable_accounts, 0)
1295 .map(|res| res.offsets[0])
1296 }
1297 }
1298
1299 impl StoredAccountMeta<'_> {
1300 pub(crate) fn ref_executable_byte(&self) -> &u8 {
1301 match self {
1302 Self::AppendVec(av) => av.ref_executable_byte(),
1303 Self::Hot(_) => unreachable!(),
1305 }
1306 }
1307 }
1308
1309 impl AppendVecStoredAccountMeta<'_> {
1310 fn set_data_len_unsafe(&self, new_data_len: u64) {
1311 unsafe {
1313 #[allow(invalid_reference_casting)]
1314 ptr::write(
1315 std::mem::transmute::<*const u64, *mut u64>(&self.meta.data_len),
1316 new_data_len,
1317 );
1318 }
1319 }
1320
1321 fn get_executable_byte(&self) -> u8 {
1322 let executable_bool: bool = self.executable();
1323 let executable_byte: u8 = unsafe { std::mem::transmute::<bool, u8>(executable_bool) };
1325 executable_byte
1326 }
1327
1328 fn set_executable_as_byte(&self, new_executable_byte: u8) {
1329 unsafe {
1331 #[allow(invalid_reference_casting)]
1332 ptr::write(
1333 std::mem::transmute::<*const bool, *mut u8>(&self.account_meta.executable),
1334 new_executable_byte,
1335 );
1336 }
1337 }
1338 }
1339
1340 static_assertions::assert_eq_align!(u64, StoredMeta, AccountMeta);
1342
1343 #[test]
1344 fn test_account_meta_default() {
1345 let def1 = AccountMeta::default();
1346 let def2 = AccountMeta::from(&Account::default());
1347 assert_eq!(&def1, &def2);
1348 let def2 = AccountMeta::from(&AccountSharedData::default());
1349 assert_eq!(&def1, &def2);
1350 let def2 = AccountMeta::from(Some(&AccountSharedData::default()));
1351 assert_eq!(&def1, &def2);
1352 let none: Option<&AccountSharedData> = None;
1353 let def2 = AccountMeta::from(none);
1354 assert_eq!(&def1, &def2);
1355 }
1356
1357 #[test]
1358 fn test_account_meta_non_default() {
1359 let def1 = AccountMeta {
1360 lamports: 1,
1361 owner: Pubkey::new_unique(),
1362 executable: true,
1363 rent_epoch: 3,
1364 };
1365 let def2_account = Account {
1366 lamports: def1.lamports,
1367 owner: def1.owner,
1368 executable: def1.executable,
1369 rent_epoch: def1.rent_epoch,
1370 data: Vec::new(),
1371 };
1372 let def2 = AccountMeta::from(&def2_account);
1373 assert_eq!(&def1, &def2);
1374 let def2 = AccountMeta::from(&AccountSharedData::from(def2_account.clone()));
1375 assert_eq!(&def1, &def2);
1376 let def2 = AccountMeta::from(Some(&AccountSharedData::from(def2_account)));
1377 assert_eq!(&def1, &def2);
1378 }
1379
1380 #[test]
1381 #[should_panic(expected = "AppendVecError(FileSizeTooSmall(0))")]
1382 fn test_append_vec_new_bad_size() {
1383 let path = get_append_vec_path("test_append_vec_new_bad_size");
1384 let _av = AppendVec::new(&path.path, true, 0);
1385 }
1386
1387 #[test_case(StorageAccess::Mmap)]
1388 #[test_case(StorageAccess::File)]
1389 fn test_append_vec_new_from_file_bad_size(storage_access: StorageAccess) {
1390 let file = get_append_vec_path("test_append_vec_new_from_file_bad_size");
1391 let path = &file.path;
1392
1393 let _data = OpenOptions::new()
1394 .read(true)
1395 .write(true)
1396 .create_new(true)
1397 .open(path)
1398 .expect("create a test file for mmap");
1399
1400 let result = AppendVec::new_from_file(path, 0, storage_access);
1401 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1402 }
1403
1404 #[test]
1405 fn test_append_vec_sanitize_len_and_size_too_small() {
1406 const LEN: usize = 0;
1407 const SIZE: usize = 0;
1408 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1409 assert_matches!(result, Err(ref message) if message.to_string().contains("too small file size 0 for AppendVec"));
1410 }
1411
1412 #[test]
1413 fn test_append_vec_sanitize_len_and_size_maximum() {
1414 const LEN: usize = 0;
1415 const SIZE: usize = 16 * 1024 * 1024 * 1024;
1416 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1417 assert_matches!(result, Ok(_));
1418 }
1419
1420 #[test]
1421 fn test_append_vec_sanitize_len_and_size_too_large() {
1422 const LEN: usize = 0;
1423 const SIZE: usize = 16 * 1024 * 1024 * 1024 + 1;
1424 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1425 assert_matches!(result, Err(ref message) if message.to_string().contains("too large file size 17179869185 for AppendVec"));
1426 }
1427
1428 #[test]
1429 fn test_append_vec_sanitize_len_and_size_full_and_same_as_current_len() {
1430 const LEN: usize = 1024 * 1024;
1431 const SIZE: usize = 1024 * 1024;
1432 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1433 assert_matches!(result, Ok(_));
1434 }
1435
1436 #[test]
1437 fn test_append_vec_sanitize_len_and_size_larger_current_len() {
1438 const LEN: usize = 1024 * 1024 + 1;
1439 const SIZE: usize = 1024 * 1024;
1440 let result = AppendVec::sanitize_len_and_size(LEN, SIZE);
1441 assert_matches!(result, Err(ref message) if message.to_string().contains("is larger than file size (1048576)"));
1442 }
1443
1444 #[test]
1445 fn test_append_vec_one() {
1446 let path = get_append_vec_path("test_append");
1447 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1448 let account = create_test_account(0);
1449 let index = av.append_account_test(&account).unwrap();
1450 assert_eq!(av.get_account_test(index).unwrap(), account);
1451 truncate_and_test(av, index);
1452 }
1453
1454 fn truncate_and_test(av: AppendVec, index: usize) {
1457 let hash = std::mem::size_of::<AccountHash>();
1459 for _ in 0..hash {
1460 av.current_len.fetch_sub(1, Ordering::Relaxed);
1461 assert_eq!(av.get_account_test(index), None);
1462 }
1463 av.current_len.fetch_sub(1, Ordering::Relaxed);
1465 assert_eq!(av.get_account_test(index), None);
1466 }
1467
1468 #[test]
1469 fn test_append_vec_one_with_data() {
1470 let path = get_append_vec_path("test_append");
1471 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1472 let data_len = 1;
1473 let account = create_test_account(data_len);
1474 let index = av.append_account_test(&account).unwrap();
1475 assert_eq!(
1477 STORE_META_OVERHEAD + data_len,
1478 av.current_len.load(Ordering::Relaxed)
1479 );
1480 assert_eq!(av.get_account_test(index).unwrap(), account);
1481 truncate_and_test(av, index);
1482 }
1483
1484 #[test]
1485 fn test_remaining_bytes() {
1486 let path = get_append_vec_path("test_append");
1487 let sz = 1024 * 1024;
1488 let sz64 = sz as u64;
1489 let av = AppendVec::new(&path.path, true, sz);
1490 assert_eq!(av.capacity(), sz64);
1491 assert_eq!(av.remaining_bytes(), sz64);
1492
1493 let mut av_len = 0;
1495 let account = create_test_account(0);
1496 av.append_account_test(&account).unwrap();
1497 av_len += STORE_META_OVERHEAD;
1498 assert_eq!(av.capacity(), sz64);
1499 assert_eq!(av.remaining_bytes(), sz64 - (STORE_META_OVERHEAD as u64));
1500 assert_eq!(av.len(), av_len);
1501
1502 let account = create_test_account(1);
1504 let account_storage_len = STORE_META_OVERHEAD + 1;
1505 av_len += account_storage_len;
1506 av.append_account_test(&account).unwrap();
1507 assert_eq!(av.capacity(), sz64);
1508 assert_eq!(av.len(), av_len);
1509 let alignment_bytes = u64_align!(av_len) - av_len; assert_eq!(alignment_bytes, 7);
1511 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1512
1513 let account = create_test_account(1);
1515 av.append_account_test(&account).unwrap();
1516 let account_storage_len = STORE_META_OVERHEAD + 1;
1517 av_len += alignment_bytes; av_len += account_storage_len;
1519 assert_eq!(av.capacity(), sz64);
1520 assert_eq!(av.len(), av_len);
1521 assert_eq!(av.remaining_bytes(), sz64 - u64_align!(av_len) as u64);
1522 }
1523
1524 #[test]
1525 fn test_append_vec_data() {
1526 let path = get_append_vec_path("test_append_data");
1527 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1528 let account = create_test_account(5);
1529 let index = av.append_account_test(&account).unwrap();
1530 assert_eq!(av.get_account_test(index).unwrap(), account);
1531 let account1 = create_test_account(6);
1532 let index1 = av.append_account_test(&account1).unwrap();
1533 assert_eq!(av.get_account_test(index).unwrap(), account);
1534 assert_eq!(av.get_account_test(index1).unwrap(), account1);
1535 }
1536
1537 #[test]
1538 fn test_account_matches_owners() {
1539 let path = get_append_vec_path("test_append_data");
1540 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1541 let owners: Vec<Pubkey> = (0..2).map(|_| Pubkey::new_unique()).collect();
1542
1543 let mut account = create_test_account(5);
1544 account.1.set_owner(owners[0]);
1545 let index = av.append_account_test(&account).unwrap();
1546 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1547
1548 let mut account1 = create_test_account(6);
1549 account1.1.set_owner(owners[1]);
1550 let index1 = av.append_account_test(&account1).unwrap();
1551 assert_eq!(av.account_matches_owners(index1, &owners), Ok(1));
1552 assert_eq!(av.account_matches_owners(index, &owners), Ok(0));
1553
1554 let mut account2 = create_test_account(6);
1555 account2.1.set_owner(Pubkey::new_unique());
1556 let index2 = av.append_account_test(&account2).unwrap();
1557 assert_eq!(
1558 av.account_matches_owners(index2, &owners),
1559 Err(MatchAccountOwnerError::NoMatch)
1560 );
1561
1562 assert_eq!(
1564 av.account_matches_owners(usize::MAX - mem::size_of::<StoredMeta>(), &owners),
1565 Err(MatchAccountOwnerError::UnableToLoad)
1566 );
1567
1568 assert_eq!(
1569 av.account_matches_owners(
1570 usize::MAX - mem::size_of::<StoredMeta>() - mem::size_of::<AccountMeta>() + 1,
1571 &owners
1572 ),
1573 Err(MatchAccountOwnerError::UnableToLoad)
1574 );
1575 }
1576
1577 impl AppendVec {
1578 fn accounts_count(&self) -> usize {
1580 let mut count = 0;
1581 self.scan_accounts(|_| {
1582 count += 1;
1583 });
1584 count
1585 }
1586 }
1587
1588 #[test]
1589 fn test_append_vec_append_many() {
1590 let path = get_append_vec_path("test_append_many");
1591 let av = AppendVec::new(&path.path, true, 1024 * 1024);
1592 let size = 1000;
1593 let mut indexes = vec![];
1594 let now = Instant::now();
1595 let mut sizes = vec![];
1596 for sample in 0..size {
1597 let account = create_test_account(sample + 1);
1600 sizes.push(aligned_stored_size(account.1.data().len()));
1601 let pos = av.append_account_test(&account).unwrap();
1602 assert_eq!(av.get_account_test(pos).unwrap(), account);
1603 indexes.push(pos);
1604 assert_eq!(sizes, av.get_account_sizes(&indexes));
1605 }
1606 trace!("append time: {} ms", now.elapsed().as_millis());
1607
1608 let now = Instant::now();
1609 for _ in 0..size {
1610 let sample = thread_rng().gen_range(0..indexes.len());
1611 let account = create_test_account(sample + 1);
1612 assert_eq!(av.get_account_test(indexes[sample]).unwrap(), account);
1613 }
1614 trace!("random read time: {} ms", now.elapsed().as_millis());
1615
1616 let now = Instant::now();
1617 assert_eq!(indexes.len(), size);
1618 assert_eq!(indexes[0], 0);
1619 let mut sample = 0;
1620 assert_eq!(av.accounts_count(), size);
1621 av.scan_accounts(|v| {
1622 let account = create_test_account(sample + 1);
1623 let recovered = v.to_account_shared_data();
1624 assert_eq!(recovered, account.1);
1625 sample += 1;
1626 });
1627 trace!("sequential read time: {} ms", now.elapsed().as_millis());
1628 }
1629
1630 #[test_case(StorageAccess::Mmap)]
1631 #[test_case(StorageAccess::File)]
1632 fn test_new_from_file_crafted_zero_lamport_account(storage_access: StorageAccess) {
1633 let file = get_append_vec_path("test_append_bytes");
1677 let path = &file.path;
1678
1679 let accounts_len = 139;
1680 {
1681 let append_vec_data = [
1682 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 192, 118, 150, 1, 185, 209, 118,
1683 82, 154, 222, 172, 202, 110, 26, 218, 140, 143, 96, 61, 43, 212, 73, 203, 7, 190,
1684 88, 80, 222, 110, 114, 67, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1685 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,
1686 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,
1687 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,
1688 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,
1689 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,
1690 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,
1691 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1692 ];
1693
1694 let f = std::fs::File::create(path).unwrap();
1695 let mut writer = std::io::BufWriter::new(f);
1696 writer.write_all(append_vec_data.as_slice()).unwrap();
1697 }
1698
1699 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1700 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1701 }
1702
1703 #[test_case(StorageAccess::Mmap)]
1704 #[test_case(StorageAccess::File)]
1705 fn test_new_from_file_crafted_data_len(storage_access: StorageAccess) {
1706 let file = get_append_vec_path("test_new_from_file_crafted_data_len");
1707 let path = &file.path;
1708 let accounts_len = {
1709 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1711
1712 let crafted_data_len = 1;
1713
1714 av.append_account_test(&create_test_account(10)).unwrap();
1715
1716 av.get_stored_account_meta_callback(0, |account| {
1717 let StoredAccountMeta::AppendVec(account) = account else {
1718 panic!("StoredAccountMeta can only be AppendVec in this test.");
1719 };
1720 account.set_data_len_unsafe(crafted_data_len);
1721 assert_eq!(account.data_len(), crafted_data_len);
1722
1723 av.get_stored_account_meta_callback(0, |account| {
1725 assert_eq!(account.data_len() as u64, crafted_data_len);
1726 });
1727 });
1728
1729 av.flush().unwrap();
1730 av.len()
1731 };
1732 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1733 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1734 }
1735
1736 #[test]
1737 fn test_append_vec_reset() {
1738 let file = get_append_vec_path("test_append_vec_reset");
1739 let path = &file.path;
1740 let av = AppendVec::new(path, true, 1024 * 1024);
1741 av.append_account_test(&create_test_account(10)).unwrap();
1742
1743 assert!(!av.is_empty());
1744 av.reset();
1745 assert_eq!(av.len(), 0);
1746 }
1747
1748 #[test_case(StorageAccess::Mmap)]
1749 #[test_case(StorageAccess::File)]
1750 fn test_append_vec_flush(storage_access: StorageAccess) {
1751 let file = get_append_vec_path("test_append_vec_flush");
1752 let path = &file.path;
1753 let accounts_len = {
1754 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1756 av.append_account_test(&create_test_account(10)).unwrap();
1757 av.len()
1758 };
1759
1760 let (av, num_account) =
1761 AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1762 av.flush().unwrap();
1763 assert_eq!(num_account, 1);
1764 }
1765
1766 #[test_case(StorageAccess::Mmap)]
1767 #[test_case(StorageAccess::File)]
1768 fn test_append_vec_reopen_as_readonly(storage_access: StorageAccess) {
1769 let file = get_append_vec_path("test_append_vec_flush");
1770 let path = &file.path;
1771 let accounts_len = {
1772 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1774 av.append_account_test(&create_test_account(10)).unwrap();
1775 av.len()
1776 };
1777 let (av, _) = AppendVec::new_from_file(path, accounts_len, storage_access).unwrap();
1778 let reopen = av.reopen_as_readonly();
1779 if storage_access == StorageAccess::File {
1780 assert!(reopen.is_none());
1781 } else {
1782 assert!(reopen.is_some());
1783 }
1784 }
1785
1786 #[test_case(StorageAccess::Mmap)]
1787 #[test_case(StorageAccess::File)]
1788 fn test_new_from_file_too_large_data_len(storage_access: StorageAccess) {
1789 let file = get_append_vec_path("test_new_from_file_too_large_data_len");
1790 let path = &file.path;
1791 let accounts_len = {
1792 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1794
1795 let too_large_data_len = u64::MAX;
1796 av.append_account_test(&create_test_account(10)).unwrap();
1797
1798 av.get_stored_account_meta_callback(0, |account| {
1799 let StoredAccountMeta::AppendVec(account) = account else {
1800 panic!("StoredAccountMeta can only be AppendVec in this test.");
1801 };
1802 account.set_data_len_unsafe(too_large_data_len);
1803 assert_eq!(account.data_len(), too_large_data_len);
1804 })
1805 .unwrap();
1806
1807 assert!(av
1809 .get_stored_account_meta_callback(0, |_| {
1810 panic!("unexpected");
1811 })
1812 .is_none());
1813 av.flush().unwrap();
1814 av.len()
1815 };
1816 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1817 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1818 }
1819
1820 #[test_case(StorageAccess::Mmap)]
1821 #[test_case(StorageAccess::File)]
1822 fn test_new_from_file_crafted_executable(storage_access: StorageAccess) {
1823 let file = get_append_vec_path("test_new_from_crafted_executable");
1824 let path = &file.path;
1825 let accounts_len = {
1826 let av = ManuallyDrop::new(AppendVec::new(path, true, 1024 * 1024));
1828 av.append_account_test(&create_test_account(10)).unwrap();
1829 let offset_1 = {
1830 let mut executable_account = create_test_account(10);
1831 executable_account.1.set_executable(true);
1832 av.append_account_test(&executable_account).unwrap()
1833 };
1834
1835 let crafted_executable = u8::MAX - 1;
1836
1837 av.get_stored_account_meta_callback(0, |account| {
1840 assert_eq!(*account.ref_executable_byte(), 0);
1841 let StoredAccountMeta::AppendVec(account) = account else {
1842 panic!("StoredAccountMeta can only be AppendVec in this test.");
1843 };
1844 account.set_executable_as_byte(crafted_executable);
1845 })
1846 .unwrap();
1847 av.get_stored_account_meta_callback(offset_1, |account| {
1848 assert_eq!(*account.ref_executable_byte(), 1);
1849 })
1850 .unwrap();
1851
1852 av.get_stored_account_meta_callback(0, |account| {
1854 let StoredAccountMeta::AppendVec(account) = account else {
1855 panic!("StoredAccountMeta can only be AppendVec in this test.");
1856 };
1857
1858 assert!(!account.sanitize_executable());
1860
1861 {
1863 let executable_bool: &bool = &account.account_meta.executable;
1864 assert!(!*executable_bool);
1867 assert_eq!(*account.ref_executable_byte(), crafted_executable);
1868 }
1869
1870 {
1872 let executable_bool: bool = account.executable();
1873 assert!(!executable_bool);
1874 assert_eq!(account.get_executable_byte(), 0); }
1876 })
1877 .unwrap();
1878
1879 av.flush().unwrap();
1880 av.len()
1881 };
1882 let result = AppendVec::new_from_file(path, accounts_len, storage_access);
1883 assert_matches!(result, Err(ref message) if message.to_string().contains("incorrect layout/length/data"));
1884 }
1885
1886 #[test]
1887 fn test_type_layout() {
1888 assert_eq!(offset_of!(StoredMeta, write_version_obsolete), 0x00);
1889 assert_eq!(offset_of!(StoredMeta, data_len), 0x08);
1890 assert_eq!(offset_of!(StoredMeta, pubkey), 0x10);
1891 assert_eq!(mem::size_of::<StoredMeta>(), 0x30);
1892
1893 assert_eq!(offset_of!(AccountMeta, lamports), 0x00);
1894 assert_eq!(offset_of!(AccountMeta, rent_epoch), 0x08);
1895 assert_eq!(offset_of!(AccountMeta, owner), 0x10);
1896 assert_eq!(offset_of!(AccountMeta, executable), 0x30);
1897 assert_eq!(mem::size_of::<AccountMeta>(), 0x38);
1898 }
1899
1900 #[test_case(StorageAccess::Mmap)]
1901 #[test_case(StorageAccess::File)]
1902 fn test_get_account_shared_data_from_truncated_file(storage_access: StorageAccess) {
1903 let file = get_append_vec_path("test_get_account_shared_data_from_truncated_file");
1904 let path = &file.path;
1905
1906 {
1907 let data_len: usize = 2 * PAGE_SIZE;
1910 let account = create_test_account_with(data_len);
1911 let av = ManuallyDrop::new(AppendVec::new(path, true, aligned_stored_size(data_len)));
1913 av.append_account_test(&account).unwrap();
1914 av.flush().unwrap();
1915 }
1916
1917 let truncated_accounts_len: usize = PAGE_SIZE;
1919 let av = AppendVec::new_from_file_unchecked(path, truncated_accounts_len, storage_access)
1920 .unwrap();
1921 let account = av.get_account_shared_data(0);
1922 assert!(account.is_none()); let result = av.get_stored_account_meta_callback(0, |_| true);
1925 assert!(result.is_none()); }
1927
1928 #[test_case(StorageAccess::Mmap)]
1929 #[test_case(StorageAccess::File)]
1930 fn test_get_account_sizes(storage_access: StorageAccess) {
1931 const NUM_ACCOUNTS: usize = 37;
1932 let pubkeys: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
1933 .take(NUM_ACCOUNTS)
1934 .collect();
1935
1936 let mut rng = thread_rng();
1937 let mut accounts = Vec::with_capacity(pubkeys.len());
1938 let mut stored_sizes = Vec::with_capacity(pubkeys.len());
1939 for _ in &pubkeys {
1940 let lamports = rng.gen();
1941 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1942 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1943 accounts.push(account);
1944 stored_sizes.push(aligned_stored_size(data_len));
1945 }
1946 let accounts = accounts;
1947 let stored_sizes = stored_sizes;
1948 let total_stored_size = stored_sizes.iter().sum();
1949
1950 let temp_file = get_append_vec_path("test_get_account_sizes");
1951 let account_offsets = {
1952 let append_vec = AppendVec::new(&temp_file.path, true, total_stored_size);
1953 let append_vec = ManuallyDrop::new(append_vec);
1955 let slot = 77; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
1957 let stored_accounts_info = append_vec
1958 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
1959 .unwrap();
1960 append_vec.flush().unwrap();
1961 stored_accounts_info.offsets
1962 };
1963
1964 let (append_vec, _) =
1967 AppendVec::new_from_file(&temp_file.path, total_stored_size, storage_access).unwrap();
1968
1969 let account_sizes = append_vec.get_account_sizes(account_offsets.as_slice());
1970 assert_eq!(account_sizes, stored_sizes);
1971 }
1972
1973 fn test_scan_helper(
1978 storage_access: StorageAccess,
1979 modify_fn: impl Fn(&PathBuf, usize) -> usize,
1980 check_fn: impl Fn(&AppendVec, &[Pubkey], &[usize], &[AccountSharedData]),
1981 ) {
1982 const NUM_ACCOUNTS: usize = 37;
1983 let pubkeys: Vec<_> = std::iter::repeat_with(solana_pubkey::new_rand)
1984 .take(NUM_ACCOUNTS)
1985 .collect();
1986
1987 let mut rng = thread_rng();
1988 let mut accounts = Vec::with_capacity(pubkeys.len());
1989 let mut total_stored_size = 0;
1990 for _ in &pubkeys {
1991 let lamports = rng.gen();
1992 let data_len = rng.gen_range(0..MAX_PERMITTED_DATA_LENGTH) as usize;
1993 let account = AccountSharedData::new(lamports, data_len, &Pubkey::default());
1994 accounts.push(account);
1995 total_stored_size += aligned_stored_size(data_len);
1996 }
1997 let accounts = accounts;
1998 let total_stored_size = total_stored_size;
1999
2000 let temp_file = get_append_vec_path("test_scan");
2001 let account_offsets = {
2002 let append_vec =
2004 ManuallyDrop::new(AppendVec::new(&temp_file.path, true, total_stored_size));
2005 let slot = 42; let storable_accounts: Vec<_> = std::iter::zip(&pubkeys, &accounts).collect();
2007 let stored_accounts_info = append_vec
2008 .append_accounts(&(slot, storable_accounts.as_slice()), 0)
2009 .unwrap();
2010 append_vec.flush().unwrap();
2011 stored_accounts_info.offsets
2012 };
2013
2014 let total_stored_size = modify_fn(&temp_file.path, total_stored_size);
2015 let append_vec = ManuallyDrop::new(
2018 AppendVec::new_from_file_unchecked(&temp_file.path, total_stored_size, storage_access)
2019 .unwrap(),
2020 );
2021
2022 check_fn(&append_vec, &pubkeys, &account_offsets, &accounts);
2023 }
2024
2025 fn test_scan_pubkeys_helper(
2027 storage_access: StorageAccess,
2028 modify_fn: impl Fn(&PathBuf, usize) -> usize,
2029 ) {
2030 test_scan_helper(
2031 storage_access,
2032 modify_fn,
2033 |append_vec, pubkeys, _account_offsets, _accounts| {
2034 let mut i = 0;
2035 append_vec.scan_pubkeys(|pubkey| {
2036 assert_eq!(pubkey, pubkeys.get(i).unwrap());
2037 i += 1;
2038 });
2039 assert_eq!(i, pubkeys.len());
2040 },
2041 )
2042 }
2043
2044 #[test_case(StorageAccess::Mmap)]
2046 #[test_case(StorageAccess::File)]
2047 fn test_scan_pubkeys(storage_access: StorageAccess) {
2048 test_scan_pubkeys_helper(storage_access, |_, size| size);
2049 }
2050
2051 #[test_case(StorageAccess::Mmap)]
2053 #[test_case(StorageAccess::File)]
2054 fn test_scan_pubkeys_incomplete_data(storage_access: StorageAccess) {
2055 test_scan_pubkeys_helper(storage_access, |path, size| {
2056 let mut f = OpenOptions::new()
2059 .read(true)
2060 .append(true)
2061 .open(path)
2062 .unwrap();
2063 f.write_all(&[0xFF]).unwrap();
2064 size + 1
2065 });
2066 }
2067
2068 #[test_case(StorageAccess::Mmap)]
2070 #[test_case(StorageAccess::File)]
2071 fn test_scan_pubkeys_missing_account_data(storage_access: StorageAccess) {
2072 test_scan_pubkeys_helper(storage_access, |path, size| {
2073 let fake_stored_meta = StoredMeta {
2074 write_version_obsolete: 0,
2075 data_len: 100,
2076 pubkey: solana_pubkey::new_rand(),
2077 };
2078 let fake_account_meta = AccountMeta {
2079 lamports: 100,
2080 rent_epoch: 10,
2081 owner: solana_pubkey::new_rand(),
2082 executable: false,
2083 };
2084
2085 let stored_meta_slice: &[u8] = unsafe {
2086 std::slice::from_raw_parts(
2087 (&fake_stored_meta as *const StoredMeta) as *const u8,
2088 mem::size_of::<StoredMeta>(),
2089 )
2090 };
2091 let account_meta_slice: &[u8] = unsafe {
2092 std::slice::from_raw_parts(
2093 (&fake_account_meta as *const AccountMeta) as *const u8,
2094 mem::size_of::<AccountMeta>(),
2095 )
2096 };
2097
2098 let mut f = OpenOptions::new()
2099 .read(true)
2100 .append(true)
2101 .open(path)
2102 .unwrap();
2103
2104 f.write_all(stored_meta_slice).unwrap();
2105 f.write_all(account_meta_slice).unwrap();
2106
2107 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2108 });
2109 }
2110
2111 fn test_scan_index_helper(
2113 storage_access: StorageAccess,
2114 modify_fn: impl Fn(&PathBuf, usize) -> usize,
2115 ) {
2116 test_scan_helper(
2117 storage_access,
2118 modify_fn,
2119 |append_vec, pubkeys, account_offsets, accounts| {
2120 let mut i = 0;
2121 append_vec.scan_index(|index_info| {
2122 let pubkey = pubkeys.get(i).unwrap();
2123 let account = accounts.get(i).unwrap();
2124 let offset = account_offsets.get(i).unwrap();
2125
2126 assert_eq!(
2127 index_info.stored_size_aligned,
2128 aligned_stored_size(account.data().len()),
2129 );
2130 assert_eq!(index_info.index_info.offset, *offset);
2131 assert_eq!(index_info.index_info.pubkey, *pubkey);
2132 assert_eq!(index_info.index_info.lamports, account.lamports());
2133 assert_eq!(index_info.index_info.rent_epoch, account.rent_epoch());
2134 assert_eq!(index_info.index_info.executable, account.executable());
2135 assert_eq!(index_info.index_info.data_len, account.data().len() as u64);
2136
2137 i += 1;
2138 });
2139 assert_eq!(i, accounts.len());
2140 },
2141 )
2142 }
2143
2144 #[test_case(StorageAccess::Mmap)]
2145 #[test_case(StorageAccess::File)]
2146 fn test_scan_index(storage_access: StorageAccess) {
2147 test_scan_index_helper(storage_access, |_, size| size);
2148 }
2149
2150 #[test_case(StorageAccess::Mmap)]
2152 #[test_case(StorageAccess::File)]
2153 fn test_scan_index_incomplete_data(storage_access: StorageAccess) {
2154 test_scan_index_helper(storage_access, |path, size| {
2155 let mut f = OpenOptions::new()
2158 .read(true)
2159 .append(true)
2160 .open(path)
2161 .unwrap();
2162 f.write_all(&[0xFF]).unwrap();
2163 size + 1
2164 });
2165 }
2166
2167 #[test_case(StorageAccess::Mmap)]
2169 #[test_case(StorageAccess::File)]
2170 fn test_scan_index_missing_account_data(storage_access: StorageAccess) {
2171 test_scan_index_helper(storage_access, |path, size| {
2172 let fake_stored_meta = StoredMeta {
2173 write_version_obsolete: 0,
2174 data_len: 100,
2175 pubkey: solana_pubkey::new_rand(),
2176 };
2177 let fake_account_meta = AccountMeta {
2178 lamports: 100,
2179 rent_epoch: 10,
2180 owner: solana_pubkey::new_rand(),
2181 executable: false,
2182 };
2183
2184 let stored_meta_slice: &[u8] = unsafe {
2185 std::slice::from_raw_parts(
2186 (&fake_stored_meta as *const StoredMeta) as *const u8,
2187 mem::size_of::<StoredMeta>(),
2188 )
2189 };
2190 let account_meta_slice: &[u8] = unsafe {
2191 std::slice::from_raw_parts(
2192 (&fake_account_meta as *const AccountMeta) as *const u8,
2193 mem::size_of::<AccountMeta>(),
2194 )
2195 };
2196
2197 let mut f = OpenOptions::new()
2198 .read(true)
2199 .append(true)
2200 .open(path)
2201 .unwrap();
2202
2203 f.write_all(stored_meta_slice).unwrap();
2204 f.write_all(account_meta_slice).unwrap();
2205
2206 size + mem::size_of::<StoredMeta>() + mem::size_of::<AccountMeta>()
2207 });
2208 }
2209
2210 #[test_case(false)]
2214 #[test_case(true)]
2215 fn test_is_dirty(begins_dirty: bool) {
2216 let file = get_append_vec_path("test_is_dirty");
2217
2218 let mut av1 = AppendVec::new(&file.path, true, 1024 * 1024);
2219 *av1.remove_file_on_drop.get_mut() = false;
2221
2222 assert!(!*av1.is_dirty.get_mut());
2224
2225 if begins_dirty {
2226 av1.append_account_test(&create_test_account(10)).unwrap();
2227 }
2228 assert_eq!(*av1.is_dirty.get_mut(), begins_dirty);
2229
2230 let mut av2 = av1.reopen_as_readonly().unwrap();
2231 *av2.remove_file_on_drop.get_mut() = false;
2233
2234 assert!(!*av1.is_dirty.get_mut());
2236 assert_eq!(*av2.is_dirty.get_mut(), begins_dirty);
2237
2238 assert!(av2.flush().is_ok());
2240 assert!(!*av2.is_dirty.get_mut());
2242
2243 assert!(av1.flush().is_ok());
2245 assert!(!*av1.is_dirty.get_mut());
2247 }
2248}