1use crate::errors::FxfsError;
26use crate::filesystem::{ApplyContext, ApplyMode, FxFilesystem, JournalingObject};
27use crate::log::*;
28use crate::lsm_tree::types::LayerIterator;
29use crate::lsm_tree::{LSMTree, LayerSet, Query};
30use crate::metrics;
31use crate::object_handle::ObjectHandle as _;
32use crate::object_store::allocator::Reservation;
33use crate::object_store::data_object_handle::{FileExtent, OverwriteOptions};
34use crate::object_store::journal::bootstrap_handle::BootstrapObjectHandle;
35use crate::object_store::journal::reader::{JournalReader, ReadResult};
36use crate::object_store::journal::writer::JournalWriter;
37use crate::object_store::journal::{BLOCK_SIZE, JournalCheckpoint, JournalCheckpointV32};
38use crate::object_store::object_record::{
39 ObjectItem, ObjectItemV40, ObjectItemV41, ObjectItemV43, ObjectItemV46, ObjectItemV47,
40 ObjectItemV49, ObjectItemV50, ObjectItemV55,
41};
42use crate::object_store::transaction::{AssocObj, Options};
43use crate::object_store::tree::MajorCompactable;
44use crate::object_store::{
45 DataObjectHandle, HandleOptions, HandleOwner, Mutation, ObjectKey, ObjectStore, ObjectValue,
46};
47use crate::range::RangeExt;
48use crate::serialized_types::{
49 EARLIEST_SUPPORTED_VERSION, FIRST_EXTENT_IN_SUPERBLOCK_VERSION, Migrate,
50 SMALL_SUPERBLOCK_VERSION, Version, Versioned, VersionedLatest, migrate_nodefault,
51 migrate_to_version,
52};
53use anyhow::{Context, Error, bail, ensure};
54use fprint::TypeFingerprint;
55use fuchsia_inspect::{Property as _, UintProperty};
56use fuchsia_sync::Mutex;
57use futures::FutureExt;
58use rustc_hash::FxHashMap as HashMap;
59use serde::{Deserialize, Serialize};
60use std::collections::{HashSet, VecDeque};
61use std::fmt;
62use std::io::{Read, Write};
63use std::ops::Range;
64use std::sync::Arc;
65use std::time::SystemTime;
66use storage_device::Device;
67use uuid::Uuid;
68
69const SUPER_BLOCK_A_OBJECT_ID: u64 = 1;
71const SUPER_BLOCK_B_OBJECT_ID: u64 = 2;
72
73pub const SUPER_BLOCK_CHUNK_SIZE: u64 = 65536;
75
76pub(crate) const MIN_SUPER_BLOCK_SIZE: u64 = 4096;
78const LEGACY_MIN_SUPER_BLOCK_SIZE: u64 = 524_288;
80
81const SUPER_BLOCK_MAGIC: &[u8; 8] = b"FxfsSupr";
83
84#[derive(Copy, Clone, Debug)]
89pub enum SuperBlockInstance {
90 A,
91 B,
92}
93
94impl SuperBlockInstance {
95 pub fn next(&self) -> SuperBlockInstance {
97 match self {
98 SuperBlockInstance::A => SuperBlockInstance::B,
99 SuperBlockInstance::B => SuperBlockInstance::A,
100 }
101 }
102
103 pub fn object_id(&self) -> u64 {
104 match self {
105 SuperBlockInstance::A => SUPER_BLOCK_A_OBJECT_ID,
106 SuperBlockInstance::B => SUPER_BLOCK_B_OBJECT_ID,
107 }
108 }
109
110 pub fn first_extent(&self) -> Range<u64> {
113 match self {
114 SuperBlockInstance::A => 0..MIN_SUPER_BLOCK_SIZE,
115 SuperBlockInstance::B => 524288..524288 + MIN_SUPER_BLOCK_SIZE,
116 }
117 }
118
119 pub fn legacy_first_extent(&self) -> Range<u64> {
121 match self {
122 SuperBlockInstance::A => 0..LEGACY_MIN_SUPER_BLOCK_SIZE,
123 SuperBlockInstance::B => LEGACY_MIN_SUPER_BLOCK_SIZE..2 * LEGACY_MIN_SUPER_BLOCK_SIZE,
124 }
125 }
126}
127
128pub type SuperBlockHeader = SuperBlockHeaderV32;
129
130#[derive(
131 Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned,
132)]
133pub struct SuperBlockHeaderV32 {
134 pub guid: UuidWrapperV32,
136
137 pub generation: u64,
141
142 pub root_parent_store_object_id: u64,
146
147 pub root_parent_graveyard_directory_object_id: u64,
150
151 pub root_store_object_id: u64,
154
155 pub allocator_object_id: u64,
157
158 pub journal_object_id: u64,
160
161 pub journal_checkpoint: JournalCheckpointV32,
163
164 pub super_block_journal_file_offset: u64,
168
169 pub journal_file_offsets: HashMap<u64, u64>,
171
172 pub borrowed_metadata_space: u64,
175
176 pub earliest_version: Version,
180}
181
182type UuidWrapper = UuidWrapperV32;
183#[derive(Clone, Default, Eq, PartialEq)]
184pub struct UuidWrapperV32(pub Uuid);
185
186impl UuidWrapper {
187 fn new() -> Self {
188 Self(Uuid::new_v4())
189 }
190 #[cfg(test)]
191 fn nil() -> Self {
192 Self(Uuid::nil())
193 }
194}
195
196impl fmt::Debug for UuidWrapper {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 f.write_str("<redacted>")
201 }
202}
203
204impl TypeFingerprint for UuidWrapper {
205 fn fingerprint() -> String {
206 "<[u8;16]>".to_owned()
207 }
208}
209
210impl Serialize for UuidWrapper {
213 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
214 self.0.as_bytes().serialize(serializer)
215 }
216}
217
218impl<'de> Deserialize<'de> for UuidWrapper {
219 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
220 <[u8; 16]>::deserialize(deserializer).map(|bytes| UuidWrapperV32(Uuid::from_bytes(bytes)))
221 }
222}
223
224pub type SuperBlockRecord = SuperBlockRecordV55;
225
226#[allow(clippy::large_enum_variant)]
227#[derive(Debug, Serialize, Deserialize, TypeFingerprint, Versioned)]
228pub enum SuperBlockRecordV55 {
229 Extent(Range<u64>),
232
233 ObjectItem(ObjectItemV55),
236
237 End,
239}
240
241#[allow(clippy::large_enum_variant)]
242#[derive(Migrate, Debug, Serialize, Deserialize, TypeFingerprint, Versioned)]
243#[migrate_to_version(SuperBlockRecordV55)]
244#[migrate_nodefault]
245pub enum SuperBlockRecordV54 {
246 Extent(Range<u64>),
247 ObjectItem(crate::object_store::object_record::ObjectItemV54),
248 End,
249}
250
251#[allow(clippy::large_enum_variant)]
252#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
253#[migrate_to_version(SuperBlockRecordV54)]
254#[migrate_nodefault]
255pub enum SuperBlockRecordV50 {
256 Extent(Range<u64>),
257 ObjectItem(ObjectItemV50),
258 End,
259}
260
261#[allow(clippy::large_enum_variant)]
262#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
263#[migrate_to_version(SuperBlockRecordV50)]
264pub enum SuperBlockRecordV49 {
265 Extent(Range<u64>),
266 ObjectItem(ObjectItemV49),
267 End,
268}
269
270#[allow(clippy::large_enum_variant)]
271#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
272#[migrate_to_version(SuperBlockRecordV49)]
273pub enum SuperBlockRecordV47 {
274 Extent(Range<u64>),
275 ObjectItem(ObjectItemV47),
276 End,
277}
278
279#[allow(clippy::large_enum_variant)]
280#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
281#[migrate_to_version(SuperBlockRecordV47)]
282pub enum SuperBlockRecordV46 {
283 Extent(Range<u64>),
284 ObjectItem(ObjectItemV46),
285 End,
286}
287
288#[allow(clippy::large_enum_variant)]
289#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
290#[migrate_to_version(SuperBlockRecordV46)]
291pub enum SuperBlockRecordV43 {
292 Extent(Range<u64>),
293 ObjectItem(ObjectItemV43),
294 End,
295}
296
297#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
298#[migrate_to_version(SuperBlockRecordV43)]
299pub enum SuperBlockRecordV41 {
300 Extent(Range<u64>),
301 ObjectItem(ObjectItemV41),
302 End,
303}
304
305#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
306#[migrate_to_version(SuperBlockRecordV41)]
307pub enum SuperBlockRecordV40 {
308 Extent(Range<u64>),
309 ObjectItem(ObjectItemV40),
310 End,
311}
312
313struct SuperBlockMetrics {
314 last_super_block_update_time_ms: UintProperty,
317
318 last_super_block_offset: UintProperty,
320}
321
322impl Default for SuperBlockMetrics {
323 fn default() -> Self {
324 SuperBlockMetrics {
325 last_super_block_update_time_ms: metrics::detail()
326 .create_uint("last_super_block_update_time_ms", 0),
327 last_super_block_offset: metrics::detail().create_uint("last_super_block_offset", 0),
328 }
329 }
330}
331
332async fn read(
335 device: Arc<dyn Device>,
336 block_size: u64,
337 instance: SuperBlockInstance,
338) -> Result<(SuperBlockHeader, SuperBlockInstance, ObjectStore), Error> {
339 let (super_block_header, mut reader) = SuperBlockHeader::read_header(device.clone(), instance)
340 .await
341 .context("failed to read superblock")?;
342 let root_parent = ObjectStore::new_root_parent(
343 device,
344 block_size,
345 super_block_header.root_parent_store_object_id,
346 );
347 root_parent.set_graveyard_directory_object_id(
348 super_block_header.root_parent_graveyard_directory_object_id,
349 );
350
351 loop {
352 let mutation = match reader.next_item().await? {
354 SuperBlockRecord::Extent(_) => bail!("Unexpected extent record"),
356 SuperBlockRecord::ObjectItem(item) => Mutation::insert_object(item.key, item.value),
357 SuperBlockRecord::End => break,
358 };
359 root_parent.apply_mutation(
360 mutation,
361 &ApplyContext {
362 mode: ApplyMode::Replay,
363 checkpoint: JournalCheckpoint { file_offset: 0, ..Default::default() },
367 },
368 AssocObj::None,
369 )?;
370 }
371 Ok((super_block_header, instance, root_parent))
372}
373
374async fn write<S: HandleOwner>(
377 super_block_header: &SuperBlockHeader,
378 items: LayerSet<ObjectKey, ObjectValue>,
379 handle: DataObjectHandle<S>,
380) -> Result<(), Error> {
381 let object_manager = handle.store().filesystem().object_manager().clone();
382 let mut writer =
389 SuperBlockWriter::new(handle, super_block_header, object_manager.metadata_reservation())
390 .await?;
391 let mut merger = items.merger();
392 let mut iter = LSMTree::major_iter(merger.query(Query::FullScan).await?).await?;
393 while let Some(item) = iter.get() {
394 writer.write_root_parent_item(item.cloned()).await?;
395 iter.advance().await?;
396 }
397 writer.finalize().await
398}
399
400pub fn compact_root_parent(
403 root_parent_store: &ObjectStore,
404) -> Result<LayerSet<ObjectKey, ObjectValue>, Error> {
405 let tree = root_parent_store.tree();
408 let layer_set = tree.layer_set();
409 {
410 let mut merger = layer_set.merger();
411 let mut iter = LSMTree::major_iter(merger.query(Query::FullScan).now_or_never().unwrap()?)
412 .now_or_never()
413 .unwrap()?;
414 let new_layer = LSMTree::new_mutable_layer();
415 while let Some(item_ref) = iter.get() {
416 new_layer.insert(item_ref.cloned())?;
417 iter.advance().now_or_never().unwrap()?;
418 }
419 tree.set_mutable_layer(new_layer);
420 }
421 Ok(layer_set)
422}
423
424pub(super) struct SuperBlockManager {
427 pub next_instance: Mutex<SuperBlockInstance>,
428 metrics: SuperBlockMetrics,
429}
430
431impl SuperBlockManager {
432 pub fn new() -> Self {
433 Self { next_instance: Mutex::new(SuperBlockInstance::A), metrics: Default::default() }
434 }
435
436 pub async fn load(
439 &self,
440 device: Arc<dyn Device>,
441 block_size: u64,
442 ) -> Result<(SuperBlockHeader, ObjectStore), Error> {
443 debug_assert!(MIN_SUPER_BLOCK_SIZE == block_size);
447
448 let (super_block, current_super_block, root_parent) = match futures::join!(
449 read(device.clone(), block_size, SuperBlockInstance::A),
450 read(device.clone(), block_size, SuperBlockInstance::B)
451 ) {
452 (Err(e1), Err(e2)) => {
453 bail!("Failed to load both superblocks due to {:?}\nand\n{:?}", e1, e2)
454 }
455 (Ok(result), Err(_)) => result,
456 (Err(_), Ok(result)) => result,
457 (Ok(result1), Ok(result2)) => {
458 if (result2.0.generation as i64).wrapping_sub(result1.0.generation as i64) > 0 {
460 result2
461 } else {
462 result1
463 }
464 }
465 };
466 info!(super_block:?, current_super_block:?; "loaded super-block");
467 *self.next_instance.lock() = current_super_block.next();
468 Ok((super_block, root_parent))
469 }
470
471 pub async fn save(
474 &self,
475 super_block_header: SuperBlockHeader,
476 filesystem: Arc<FxFilesystem>,
477 root_parent: LayerSet<ObjectKey, ObjectValue>,
478 ) -> Result<(), Error> {
479 let root_store = filesystem.root_store();
480 let object_id = {
481 let mut next_instance = self.next_instance.lock();
482 let object_id = next_instance.object_id();
483 *next_instance = next_instance.next();
484 object_id
485 };
486 let handle = ObjectStore::open_object(
487 &root_store,
488 object_id,
489 HandleOptions { skip_journal_checks: true, ..Default::default() },
490 None,
491 )
492 .await
493 .context("Failed to open superblock object")?;
494 write(&super_block_header, root_parent, handle).await?;
495 self.metrics
496 .last_super_block_offset
497 .set(super_block_header.super_block_journal_file_offset);
498 self.metrics.last_super_block_update_time_ms.set(
499 SystemTime::now()
500 .duration_since(SystemTime::UNIX_EPOCH)
501 .unwrap()
502 .as_millis()
503 .try_into()
504 .unwrap_or(0u64),
505 );
506 Ok(())
507 }
508}
509
510impl SuperBlockHeader {
511 pub fn new(
513 generation: u64,
514 root_parent_store_object_id: u64,
515 root_parent_graveyard_directory_object_id: u64,
516 root_store_object_id: u64,
517 allocator_object_id: u64,
518 journal_object_id: u64,
519 journal_checkpoint: JournalCheckpoint,
520 earliest_version: Version,
521 ) -> Self {
522 SuperBlockHeader {
523 guid: UuidWrapper::new(),
524 generation,
525 root_parent_store_object_id,
526 root_parent_graveyard_directory_object_id,
527 root_store_object_id,
528 allocator_object_id,
529 journal_object_id,
530 journal_checkpoint,
531 earliest_version,
532 ..Default::default()
533 }
534 }
535
536 async fn read_header(
539 device: Arc<dyn Device>,
540 target_super_block: SuperBlockInstance,
541 ) -> Result<(SuperBlockHeader, RecordReader), Error> {
542 let handle = BootstrapObjectHandle::new(
543 target_super_block.object_id(),
544 device,
545 target_super_block.first_extent(),
546 );
547 let mut reader = JournalReader::new(handle, &JournalCheckpoint::default());
548 reader.set_eof_ok();
549
550 reader.fill_buf().await?;
551
552 let mut super_block_header;
553 let super_block_version;
554 reader.consume({
555 let mut cursor = std::io::Cursor::new(reader.buffer());
556 let mut magic_bytes: [u8; 8] = [0; 8];
558 cursor.read_exact(&mut magic_bytes)?;
559 if magic_bytes.as_slice() != SUPER_BLOCK_MAGIC.as_slice() {
560 bail!("Invalid magic: {:?}", magic_bytes);
561 }
562 (super_block_header, super_block_version) =
563 SuperBlockHeader::deserialize_with_version(&mut cursor)?;
564
565 let mut stores = HashSet::new();
567 ensure!(
568 stores.insert(super_block_header.root_parent_store_object_id),
569 FxfsError::Inconsistent
570 );
571 ensure!(
572 stores.insert(super_block_header.root_store_object_id),
573 FxfsError::Inconsistent
574 );
575
576 let mut root_parent_objects = HashSet::new();
578 ensure!(
579 root_parent_objects
580 .insert(super_block_header.root_parent_graveyard_directory_object_id),
581 FxfsError::Inconsistent
582 );
583 ensure!(
584 root_parent_objects.insert(super_block_header.root_store_object_id),
585 FxfsError::Inconsistent
586 );
587 ensure!(
588 root_parent_objects.insert(super_block_header.journal_object_id),
589 FxfsError::Inconsistent
590 );
591
592 ensure!(
594 !stores.contains(&super_block_header.allocator_object_id),
595 FxfsError::Inconsistent
596 );
597
598 if super_block_version < EARLIEST_SUPPORTED_VERSION {
599 bail!("Unsupported SuperBlock version: {:?}", super_block_version);
600 }
601
602 if super_block_header.journal_checkpoint.version < EARLIEST_SUPPORTED_VERSION {
606 bail!(
607 "Unsupported JournalCheckpoint version: {:?}",
608 super_block_header.journal_checkpoint.version
609 );
610 }
611
612 if super_block_header.earliest_version < EARLIEST_SUPPORTED_VERSION {
613 bail!(
614 "Filesystem contains struct with unsupported version: {:?}",
615 super_block_header.earliest_version
616 );
617 }
618
619 cursor.position() as usize
620 });
621
622 if super_block_version < SMALL_SUPERBLOCK_VERSION {
626 reader.handle().push_extent(0, target_super_block.legacy_first_extent());
627 } else if super_block_version < FIRST_EXTENT_IN_SUPERBLOCK_VERSION {
628 reader.handle().push_extent(0, target_super_block.first_extent())
629 }
630
631 if super_block_header.guid.0.is_nil() {
633 super_block_header.guid = UuidWrapper::new();
634 }
635 reader.set_version(super_block_version);
636 Ok((super_block_header, RecordReader { reader }))
637 }
638}
639
640struct SuperBlockWriter<'a, S: HandleOwner> {
641 handle: DataObjectHandle<S>,
642 writer: JournalWriter,
643 existing_extents: VecDeque<FileExtent>,
644 size: u64,
645 reservation: &'a Reservation,
646}
647
648impl<'a, S: HandleOwner> SuperBlockWriter<'a, S> {
649 pub async fn new(
652 handle: DataObjectHandle<S>,
653 super_block_header: &SuperBlockHeader,
654 reservation: &'a Reservation,
655 ) -> Result<Self, Error> {
656 let existing_extents = handle.device_extents().await?;
657 let mut this = Self {
658 handle,
659 writer: JournalWriter::new(BLOCK_SIZE as usize, 0),
660 existing_extents: existing_extents.into_iter().collect(),
661 size: 0,
662 reservation,
663 };
664 this.writer.write_all(SUPER_BLOCK_MAGIC)?;
665 super_block_header.serialize_with_version(&mut this.writer)?;
666 Ok(this)
667 }
668
669 fn try_extend_existing(&mut self, target_size: u64) -> Result<(), Error> {
672 while self.size < target_size {
673 if let Some(extent) = self.existing_extents.pop_front() {
674 ensure!(
675 extent.logical_range().start == self.size,
676 "superblock file contains a hole."
677 );
678 self.size += extent.length();
679 SuperBlockRecord::Extent(extent.device_range().clone())
680 .serialize_into(&mut self.writer)?;
681 } else {
682 break;
683 }
684 }
685 Ok(())
686 }
687
688 pub async fn write_root_parent_item(&mut self, record: ObjectItem) -> Result<(), Error> {
689 let min_len = self.writer.journal_file_checkpoint().file_offset + SUPER_BLOCK_CHUNK_SIZE;
690 self.try_extend_existing(min_len)?;
691 if min_len > self.size {
692 let mut transaction = self
694 .handle
695 .new_transaction_with_options(Options {
696 skip_journal_checks: true,
697 borrow_metadata_space: true,
698 allocator_reservation: Some(self.reservation),
699 ..Default::default()
700 })
701 .await?;
702 let mut file_range = self.size..self.size + SUPER_BLOCK_CHUNK_SIZE;
703 let allocated = self
704 .handle
705 .preallocate_range(&mut transaction, &mut file_range)
706 .await
707 .context("preallocate superblock")?;
708 if file_range.start < file_range.end {
709 bail!("preallocate_range returned too little space");
710 }
711 transaction.commit().await?;
712 for device_range in allocated {
713 self.size += device_range.end - device_range.start;
714 SuperBlockRecord::Extent(device_range).serialize_into(&mut self.writer)?;
715 }
716 }
717 SuperBlockRecord::ObjectItem(record).serialize_into(&mut self.writer)?;
718 Ok(())
719 }
720
721 pub async fn finalize(mut self) -> Result<(), Error> {
722 SuperBlockRecord::End.serialize_into(&mut self.writer)?;
723 self.writer.pad_to_block()?;
724 let mut buf = self.handle.allocate_buffer(self.writer.flushable_bytes()).await;
725 let offset = self.writer.take_flushable(buf.as_mut());
726 self.handle.overwrite(offset, buf.as_mut(), OverwriteOptions::default()).await?;
727 let len =
728 std::cmp::max(MIN_SUPER_BLOCK_SIZE, self.writer.journal_file_checkpoint().file_offset)
729 + SUPER_BLOCK_CHUNK_SIZE;
730 self.handle
731 .truncate_with_options(
732 Options {
733 skip_journal_checks: true,
734 borrow_metadata_space: true,
735 ..Default::default()
736 },
737 len,
738 )
739 .await?;
740 Ok(())
741 }
742}
743
744pub struct RecordReader {
745 reader: JournalReader,
746}
747
748impl RecordReader {
749 pub async fn next_item(&mut self) -> Result<SuperBlockRecord, Error> {
750 loop {
751 match self.reader.deserialize().await? {
752 ReadResult::Reset(_) => bail!("Unexpected reset"),
753 ReadResult::ChecksumMismatch => bail!("Checksum mismatch"),
754 ReadResult::Some(SuperBlockRecord::Extent(extent)) => {
755 ensure!(extent.is_valid(), FxfsError::Inconsistent);
756 self.reader.handle().push_extent(0, extent)
757 }
758 ReadResult::Some(x) => return Ok(x),
759 }
760 }
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 use super::{
767 MIN_SUPER_BLOCK_SIZE, SUPER_BLOCK_CHUNK_SIZE, SUPER_BLOCK_MAGIC, SuperBlockHeader,
768 SuperBlockInstance, SuperBlockManager, SuperBlockRecord, UuidWrapper, compact_root_parent,
769 write,
770 };
771 use crate::filesystem::{FxFilesystem, OpenFxFilesystem, SyncOptions};
772 use crate::object_handle::ReadObjectHandle;
773 use crate::object_store::journal::JournalCheckpoint;
774 use crate::object_store::journal::writer::JournalWriter;
775 use crate::object_store::transaction::{Options, lock_keys};
776 use crate::object_store::{
777 DataObjectHandle, HandleOptions, ObjectHandle, ObjectKey, ObjectStore,
778 };
779 use crate::serialized_types::{LATEST_VERSION, Versioned, VersionedLatest};
780 use std::io::Write;
781 use storage_device::DeviceHolder;
782 use storage_device::fake_device::FakeDevice;
783
784 const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
787 const TEST_DEVICE_BLOCK_COUNT: u64 = 16384;
788
789 async fn filesystem_and_super_block_handles()
790 -> (OpenFxFilesystem, DataObjectHandle<ObjectStore>, DataObjectHandle<ObjectStore>) {
791 let device =
792 DeviceHolder::new(FakeDevice::new(TEST_DEVICE_BLOCK_COUNT, TEST_DEVICE_BLOCK_SIZE));
793 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
794 fs.close().await.expect("Close failed");
795 let device = fs.take_device().await;
796 device.reopen(false);
797 let fs = FxFilesystem::open(device).await.expect("open failed");
798
799 let handle_a = ObjectStore::open_object(
800 &fs.object_manager().root_store(),
801 SuperBlockInstance::A.object_id(),
802 HandleOptions::default(),
803 None,
804 )
805 .await
806 .expect("open superblock failed");
807
808 let handle_b = ObjectStore::open_object(
809 &fs.object_manager().root_store(),
810 SuperBlockInstance::B.object_id(),
811 HandleOptions::default(),
812 None,
813 )
814 .await
815 .expect("open superblock failed");
816 (fs, handle_a, handle_b)
817 }
818
819 #[fuchsia::test]
820 async fn test_read_written_super_block() {
821 let (fs, _handle_a, _handle_b) = filesystem_and_super_block_handles().await;
822 const JOURNAL_OBJECT_ID: u64 = 5;
823
824 assert_eq!(
827 ObjectStore::open_object(
828 &fs.root_store(),
829 SuperBlockInstance::A.object_id(),
830 HandleOptions::default(),
831 None,
832 )
833 .await
834 .expect("open_object failed")
835 .get_size(),
836 MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
837 );
838
839 let mut created_object_ids = vec![];
842 const NUM_ENTRIES: u64 = 16384;
843 for _ in 0..NUM_ENTRIES {
844 let mut transaction = fs
845 .root_store()
846 .new_transaction(lock_keys![], Options::default())
847 .await
848 .expect("new_transaction failed");
849 created_object_ids.push(
850 ObjectStore::create_object(
851 &fs.object_manager().root_parent_store(),
852 &mut transaction,
853 HandleOptions::default(),
854 None,
855 )
856 .await
857 .expect("create_object failed")
858 .object_id(),
859 );
860 transaction.commit().await.expect("commit failed");
861 }
862
863 assert!(
867 ObjectStore::open_object(
868 &fs.root_store(),
869 SuperBlockInstance::A.object_id(),
870 HandleOptions::default(),
871 None,
872 )
873 .await
874 .expect("open_object failed")
875 .get_size()
876 > MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
877 );
878
879 let written_super_block_a =
880 SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::A)
881 .await
882 .expect("read failed");
883 let written_super_block_b =
884 SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::B)
885 .await
886 .expect("read failed");
887
888 assert!(!written_super_block_a.0.guid.0.is_nil());
890
891 assert_eq!(written_super_block_a.0.guid, written_super_block_b.0.guid);
894 assert_eq!(written_super_block_a.0.guid, written_super_block_b.0.guid);
895 assert!(written_super_block_a.0.generation != written_super_block_b.0.generation);
896 assert_eq!(
897 written_super_block_a.0.root_parent_store_object_id,
898 written_super_block_b.0.root_parent_store_object_id
899 );
900 assert_eq!(
901 written_super_block_a.0.root_parent_graveyard_directory_object_id,
902 written_super_block_b.0.root_parent_graveyard_directory_object_id
903 );
904 assert_eq!(written_super_block_a.0.root_store_object_id, fs.root_store().store_object_id());
905 assert_eq!(
906 written_super_block_a.0.root_store_object_id,
907 written_super_block_b.0.root_store_object_id
908 );
909 assert_eq!(written_super_block_a.0.allocator_object_id, fs.allocator().object_id());
910 assert_eq!(
911 written_super_block_a.0.allocator_object_id,
912 written_super_block_b.0.allocator_object_id
913 );
914 assert_eq!(written_super_block_a.0.journal_object_id, JOURNAL_OBJECT_ID);
915 assert_eq!(
916 written_super_block_a.0.journal_object_id,
917 written_super_block_b.0.journal_object_id
918 );
919 assert!(
920 written_super_block_a.0.journal_checkpoint.file_offset
921 != written_super_block_b.0.journal_checkpoint.file_offset
922 );
923 assert!(
924 written_super_block_a.0.super_block_journal_file_offset
925 != written_super_block_b.0.super_block_journal_file_offset
926 );
927 assert_eq!(written_super_block_a.0.earliest_version, LATEST_VERSION);
929 assert_eq!(
930 written_super_block_a.0.earliest_version,
931 written_super_block_b.0.earliest_version
932 );
933
934 for object_id in created_object_ids {
939 let mut transaction = fs
940 .root_store()
941 .new_transaction(lock_keys![], Options::default())
942 .await
943 .expect("new_transaction failed");
944 fs.object_manager()
945 .root_parent_store()
946 .adjust_refs(&mut transaction, object_id, -1)
947 .await
948 .expect("adjust_refs failed");
949 transaction.commit().await.expect("commit failed");
950 fs.object_manager()
951 .root_parent_store()
952 .tombstone_object(object_id, Options::default())
953 .await
954 .expect("tombstone failed");
955 }
956 for _ in 0..NUM_ENTRIES {
959 let mut transaction = fs
960 .root_store()
961 .new_transaction(lock_keys![], Options::default())
962 .await
963 .expect("new_transaction failed");
964 ObjectStore::create_object(
965 &fs.object_manager().root_store(),
966 &mut transaction,
967 HandleOptions::default(),
968 None,
969 )
970 .await
971 .expect("create_object failed");
972 transaction.commit().await.expect("commit failed");
973 }
974
975 assert_eq!(
976 ObjectStore::open_object(
977 &fs.root_store(),
978 SuperBlockInstance::A.object_id(),
979 HandleOptions::default(),
980 None,
981 )
982 .await
983 .expect("open_object failed")
984 .get_size(),
985 MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
986 );
987 }
988
989 #[fuchsia::test]
990 async fn test_generation_comparison_wrapping() {
991 let device = DeviceHolder::new(FakeDevice::new(
992 TEST_DEVICE_BLOCK_COUNT,
993 MIN_SUPER_BLOCK_SIZE as u32,
994 ));
995 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
996 fs.close().await.expect("close");
997 let device = fs.take_device().await;
998 device.reopen(false);
999
1000 let device_arc = (*device).clone();
1003 let write_sb = |instance: SuperBlockInstance, generation: u64| {
1004 let device = device_arc.clone();
1005 async move {
1006 let mut super_block_header = SuperBlockHeader::new(
1007 1, 3, 4, 5, 6, 7, JournalCheckpoint::default(),
1014 LATEST_VERSION,
1015 );
1016 super_block_header.generation = generation;
1017 super_block_header.journal_checkpoint.version = LATEST_VERSION;
1018
1019 let mut writer = JournalWriter::new(MIN_SUPER_BLOCK_SIZE as usize, 0);
1020 writer.write_all(SUPER_BLOCK_MAGIC).unwrap();
1021 super_block_header.serialize_with_version(&mut writer).unwrap();
1022 SuperBlockRecord::End.serialize_into(&mut writer).unwrap();
1023 writer.pad_to_block().unwrap();
1024
1025 let mut buf = device.allocate_buffer(writer.flushable_bytes()).await;
1026 writer.take_flushable(buf.as_mut());
1027 device
1028 .write(instance.first_extent().start, buf.as_ref())
1029 .await
1030 .expect("write failed");
1031 }
1032 };
1033
1034 write_sb(SuperBlockInstance::A, u64::MAX).await;
1036 write_sb(SuperBlockInstance::B, 0).await;
1037 let manager = SuperBlockManager::new();
1038 let (header, _) = manager
1039 .load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64)
1040 .await
1041 .expect("load failed");
1042 assert_eq!(header.generation, 0);
1043
1044 write_sb(SuperBlockInstance::A, 0).await;
1046 write_sb(SuperBlockInstance::B, u64::MAX).await;
1047 let manager = SuperBlockManager::new();
1048 let (header, _) = manager
1049 .load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64)
1050 .await
1051 .expect("load failed");
1052 assert_eq!(header.generation, 0);
1053
1054 write_sb(SuperBlockInstance::A, 100).await;
1056 write_sb(SuperBlockInstance::B, 200).await;
1057 let manager = SuperBlockManager::new();
1058 let (header, _) = manager
1059 .load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64)
1060 .await
1061 .expect("load failed");
1062 assert_eq!(header.generation, 200);
1063 }
1064
1065 #[fuchsia::test]
1066 async fn test_generation_wrapping_on_flush() {
1067 let block_size = 4096;
1068 let mut device =
1069 DeviceHolder::new(FakeDevice::new(TEST_DEVICE_BLOCK_COUNT, block_size as u32));
1070 {
1071 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1072 let root_store = fs.root_store();
1073 let mut transaction = fs
1074 .root_store()
1075 .new_transaction(lock_keys![], Options::default())
1076 .await
1077 .expect("new_transaction failed");
1078 ObjectStore::create_object(
1079 &root_store,
1080 &mut transaction,
1081 HandleOptions::default(),
1082 None,
1083 )
1084 .await
1085 .expect("create_object failed");
1086 transaction.commit().await.expect("commit failed");
1087 fs.sync(SyncOptions::default()).await.expect("sync failed");
1088 fs.close().await.expect("close failed");
1089 device = fs.take_device().await;
1090 }
1091 device.reopen(false);
1092
1093 let manager = SuperBlockManager::new();
1094 let (mut header, _) =
1095 manager.load((*device).clone(), block_size as u64).await.expect("load failed");
1096
1097 {
1098 let fs = FxFilesystem::open(device).await.expect("open failed");
1099 header.generation = u64::MAX - 1;
1104 manager
1105 .save(header.clone(), (*fs).clone(), fs.root_parent_store().tree().layer_set())
1106 .await
1107 .expect("save 1 failed");
1108 header.generation = u64::MAX;
1109 manager
1110 .save(header, (*fs).clone(), fs.root_parent_store().tree().layer_set())
1111 .await
1112 .expect("save 2 failed");
1113 fs.close().await.expect("close failed");
1114 device = fs.take_device().await;
1115 device.reopen(false);
1116
1117 let fs = FxFilesystem::open(device).await.expect("open failed");
1118
1119 let root_store = fs.root_store();
1120 for _ in 0..6000 {
1121 let mut transaction = fs
1122 .root_store()
1123 .new_transaction(lock_keys![], Options::default())
1124 .await
1125 .expect("new_transaction failed");
1126 ObjectStore::create_object(
1127 &root_store,
1128 &mut transaction,
1129 HandleOptions::default(),
1130 None,
1131 )
1132 .await
1133 .expect("create_object failed");
1134 transaction.commit().await.expect("commit failed");
1135 }
1136 fs.sync(SyncOptions::default()).await.expect("sync failed");
1137 fs.close().await.expect("close failed");
1138 device = fs.take_device().await;
1139 }
1140 device.reopen(false);
1141
1142 let (header, _) =
1143 manager.load((*device).clone(), block_size as u64).await.expect("load failed");
1144 assert!(header.generation < 10);
1145 }
1146
1147 #[fuchsia::test]
1148 async fn test_guid_assign_on_read() {
1149 let (fs, handle_a, _handle_b) = filesystem_and_super_block_handles().await;
1150 const JOURNAL_OBJECT_ID: u64 = 5;
1151 let mut super_block_header_a = SuperBlockHeader::new(
1152 1,
1153 fs.object_manager().root_parent_store().store_object_id(),
1154 1000,
1155 fs.root_store().store_object_id(),
1156 fs.allocator().object_id(),
1157 JOURNAL_OBJECT_ID,
1158 JournalCheckpoint { file_offset: 1234, checksum: 5678, version: LATEST_VERSION },
1159 LATEST_VERSION,
1160 );
1161 super_block_header_a.guid = UuidWrapper::nil();
1163 write(
1164 &super_block_header_a,
1165 compact_root_parent(fs.object_manager().root_parent_store().as_ref())
1166 .expect("scan failed"),
1167 handle_a,
1168 )
1169 .await
1170 .expect("write failed");
1171 let super_block_header = SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::A)
1172 .await
1173 .expect("read failed");
1174 assert!(!super_block_header.0.guid.0.is_nil());
1176 }
1177
1178 #[fuchsia::test]
1179 async fn test_init_wipes_superblocks() {
1180 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1181
1182 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1183 let root_store = fs.root_store();
1184 for _ in 0..6000 {
1186 let mut transaction = fs
1187 .root_store()
1188 .new_transaction(lock_keys![], Options::default())
1189 .await
1190 .expect("new_transaction failed");
1191 ObjectStore::create_object(
1192 &root_store,
1193 &mut transaction,
1194 HandleOptions::default(),
1195 None,
1196 )
1197 .await
1198 .expect("create_object failed");
1199 transaction.commit().await.expect("commit failed");
1200 }
1201 fs.close().await.expect("Close failed");
1202 let device = fs.take_device().await;
1203 device.reopen(false);
1204
1205 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1206 .await
1207 .expect("read failed");
1208 let header = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1209 .await
1210 .expect("read failed");
1211
1212 let old_guid = header.0.guid;
1213
1214 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1216 fs.close().await.expect("Close failed");
1217 let device = fs.take_device().await;
1218 device.reopen(false);
1219
1220 let a = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1221 .await
1222 .expect("read failed");
1223 let b = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1224 .await
1225 .expect("read failed");
1226
1227 assert_eq!(a.0.guid, b.0.guid);
1228 assert_ne!(old_guid, a.0.guid);
1229 }
1230
1231 #[fuchsia::test]
1232 async fn test_alternating_super_blocks() {
1233 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1234
1235 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1236 fs.close().await.expect("Close failed");
1237 let device = fs.take_device().await;
1238 device.reopen(false);
1239
1240 let (super_block_header_a, _) =
1241 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1242 .await
1243 .expect("read failed");
1244
1245 let fs = FxFilesystem::open(device).await.expect("open failed");
1248 let root_store = fs.root_store();
1249 for _ in 0..6000 {
1251 let mut transaction = fs
1252 .root_store()
1253 .new_transaction(lock_keys![], Options::default())
1254 .await
1255 .expect("new_transaction failed");
1256 ObjectStore::create_object(
1257 &root_store,
1258 &mut transaction,
1259 HandleOptions::default(),
1260 None,
1261 )
1262 .await
1263 .expect("create_object failed");
1264 transaction.commit().await.expect("commit failed");
1265 }
1266 fs.close().await.expect("Close failed");
1267 let device = fs.take_device().await;
1268 device.reopen(false);
1269
1270 let (super_block_header_a_after, _) =
1271 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1272 .await
1273 .expect("read failed");
1274 let (super_block_header_b_after, _) =
1275 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1276 .await
1277 .expect("read failed");
1278
1279 assert_eq!(
1283 (super_block_header_b_after.generation as i64
1284 - super_block_header_a_after.generation as i64)
1285 .abs(),
1286 1
1287 );
1288
1289 assert!(
1291 std::cmp::max(
1292 super_block_header_a_after.generation,
1293 super_block_header_b_after.generation
1294 ) > super_block_header_a.generation
1295 );
1296
1297 assert_eq!(super_block_header_a_after.generation & 1, super_block_header_a.generation & 1);
1299 }
1300
1301 #[fuchsia::test]
1302 async fn test_root_parent_is_compacted() {
1303 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1304
1305 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1306
1307 let mut transaction = fs
1308 .root_store()
1309 .new_transaction(lock_keys![], Options::default())
1310 .await
1311 .expect("new_transaction failed");
1312 let store = fs.root_parent_store();
1313 let handle =
1314 ObjectStore::create_object(&store, &mut transaction, HandleOptions::default(), None)
1315 .await
1316 .expect("create_object failed");
1317 store.add_to_graveyard(&mut transaction, handle.object_id());
1318 transaction.commit().await.expect("commit failed");
1319
1320 store
1321 .tombstone_object(handle.object_id(), Options::default())
1322 .await
1323 .expect("tombstone failed");
1324
1325 let root_store = fs.root_store();
1327 for _ in 0..6000 {
1328 let mut transaction = fs
1329 .root_store()
1330 .new_transaction(lock_keys![], Options::default())
1331 .await
1332 .expect("new_transaction failed");
1333 ObjectStore::create_object(
1334 &root_store,
1335 &mut transaction,
1336 HandleOptions::default(),
1337 None,
1338 )
1339 .await
1340 .expect("create_object failed");
1341 transaction.commit().await.expect("commit failed");
1342 }
1343
1344 assert_eq!(
1347 store.tree().find(&ObjectKey::object(handle.object_id())).await.expect("find failed"),
1348 None
1349 );
1350 }
1351
1352 #[fuchsia::test]
1353 async fn test_invalid_object_ids_validation() {
1354 let device = DeviceHolder::new(FakeDevice::new(
1355 TEST_DEVICE_BLOCK_COUNT,
1356 MIN_SUPER_BLOCK_SIZE as u32,
1357 ));
1358 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1359 fs.close().await.expect("close");
1360 let device = fs.take_device().await;
1361 device.reopen(false);
1362
1363 let device_arc = (*device).clone();
1365 let write_sb = |instance: SuperBlockInstance,
1366 root_parent_store_object_id: u64,
1367 root_parent_graveyard_directory_object_id: u64,
1368 root_store_object_id: u64,
1369 allocator_object_id: u64,
1370 journal_object_id: u64| {
1371 let device = device_arc.clone();
1372 async move {
1373 let mut super_block_header = SuperBlockHeader::new(
1374 1, root_parent_store_object_id,
1376 root_parent_graveyard_directory_object_id,
1377 root_store_object_id,
1378 allocator_object_id,
1379 journal_object_id,
1380 JournalCheckpoint::default(),
1381 LATEST_VERSION,
1382 );
1383 super_block_header.journal_checkpoint.version = LATEST_VERSION;
1384
1385 let mut writer = JournalWriter::new(MIN_SUPER_BLOCK_SIZE as usize, 0);
1386 writer.write_all(SUPER_BLOCK_MAGIC).unwrap();
1387 super_block_header.serialize_with_version(&mut writer).unwrap();
1388 SuperBlockRecord::End.serialize_into(&mut writer).unwrap();
1389 writer.pad_to_block().unwrap();
1390
1391 let mut buf = device.allocate_buffer(writer.flushable_bytes()).await;
1392 writer.take_flushable(buf.as_mut());
1393 device
1394 .write(instance.first_extent().start, buf.as_ref())
1395 .await
1396 .expect("write failed");
1397 }
1398 };
1399
1400 let manager = SuperBlockManager::new();
1401
1402 write_sb(SuperBlockInstance::A, 3, 4, 3, 5, 6).await;
1404 write_sb(SuperBlockInstance::B, 3, 4, 3, 5, 6).await;
1405 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_err());
1406
1407 write_sb(SuperBlockInstance::A, 3, 4, 5, 3, 6).await;
1409 write_sb(SuperBlockInstance::B, 3, 4, 5, 3, 6).await;
1410 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_err());
1411
1412 write_sb(SuperBlockInstance::A, 3, 4, 5, 5, 6).await;
1414 write_sb(SuperBlockInstance::B, 3, 4, 5, 5, 6).await;
1415 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_err());
1416
1417 write_sb(SuperBlockInstance::A, 3, 4, 5, 6, 4).await;
1419 write_sb(SuperBlockInstance::B, 3, 4, 5, 6, 4).await;
1420 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_err());
1421
1422 write_sb(SuperBlockInstance::A, 3, 4, 5, 6, 7).await;
1424 write_sb(SuperBlockInstance::B, 3, 4, 5, 6, 7).await;
1425 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_ok());
1426 }
1427}