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::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 if super_block_version < EARLIEST_SUPPORTED_VERSION {
566 bail!("Unsupported SuperBlock version: {:?}", super_block_version);
567 }
568
569 if super_block_header.journal_checkpoint.version < EARLIEST_SUPPORTED_VERSION {
573 bail!(
574 "Unsupported JournalCheckpoint version: {:?}",
575 super_block_header.journal_checkpoint.version
576 );
577 }
578
579 if super_block_header.earliest_version < EARLIEST_SUPPORTED_VERSION {
580 bail!(
581 "Filesystem contains struct with unsupported version: {:?}",
582 super_block_header.earliest_version
583 );
584 }
585
586 cursor.position() as usize
587 });
588
589 if super_block_version < SMALL_SUPERBLOCK_VERSION {
593 reader.handle().push_extent(0, target_super_block.legacy_first_extent());
594 } else if super_block_version < FIRST_EXTENT_IN_SUPERBLOCK_VERSION {
595 reader.handle().push_extent(0, target_super_block.first_extent())
596 }
597
598 if super_block_header.guid.0.is_nil() {
600 super_block_header.guid = UuidWrapper::new();
601 }
602 reader.set_version(super_block_version);
603 Ok((super_block_header, RecordReader { reader }))
604 }
605}
606
607struct SuperBlockWriter<'a, S: HandleOwner> {
608 handle: DataObjectHandle<S>,
609 writer: JournalWriter,
610 existing_extents: VecDeque<FileExtent>,
611 size: u64,
612 reservation: &'a Reservation,
613}
614
615impl<'a, S: HandleOwner> SuperBlockWriter<'a, S> {
616 pub async fn new(
619 handle: DataObjectHandle<S>,
620 super_block_header: &SuperBlockHeader,
621 reservation: &'a Reservation,
622 ) -> Result<Self, Error> {
623 let existing_extents = handle.device_extents().await?;
624 let mut this = Self {
625 handle,
626 writer: JournalWriter::new(BLOCK_SIZE as usize, 0),
627 existing_extents: existing_extents.into_iter().collect(),
628 size: 0,
629 reservation,
630 };
631 this.writer.write_all(SUPER_BLOCK_MAGIC)?;
632 super_block_header.serialize_with_version(&mut this.writer)?;
633 Ok(this)
634 }
635
636 fn try_extend_existing(&mut self, target_size: u64) -> Result<(), Error> {
639 while self.size < target_size {
640 if let Some(extent) = self.existing_extents.pop_front() {
641 ensure!(
642 extent.logical_range().start == self.size,
643 "superblock file contains a hole."
644 );
645 self.size += extent.length();
646 SuperBlockRecord::Extent(extent.device_range().clone())
647 .serialize_into(&mut self.writer)?;
648 } else {
649 break;
650 }
651 }
652 Ok(())
653 }
654
655 pub async fn write_root_parent_item(&mut self, record: ObjectItem) -> Result<(), Error> {
656 let min_len = self.writer.journal_file_checkpoint().file_offset + SUPER_BLOCK_CHUNK_SIZE;
657 self.try_extend_existing(min_len)?;
658 if min_len > self.size {
659 let mut transaction = self
661 .handle
662 .new_transaction_with_options(Options {
663 skip_journal_checks: true,
664 borrow_metadata_space: true,
665 allocator_reservation: Some(self.reservation),
666 ..Default::default()
667 })
668 .await?;
669 let mut file_range = self.size..self.size + SUPER_BLOCK_CHUNK_SIZE;
670 let allocated = self
671 .handle
672 .preallocate_range(&mut transaction, &mut file_range)
673 .await
674 .context("preallocate superblock")?;
675 if file_range.start < file_range.end {
676 bail!("preallocate_range returned too little space");
677 }
678 transaction.commit().await?;
679 for device_range in allocated {
680 self.size += device_range.end - device_range.start;
681 SuperBlockRecord::Extent(device_range).serialize_into(&mut self.writer)?;
682 }
683 }
684 SuperBlockRecord::ObjectItem(record).serialize_into(&mut self.writer)?;
685 Ok(())
686 }
687
688 pub async fn finalize(mut self) -> Result<(), Error> {
689 SuperBlockRecord::End.serialize_into(&mut self.writer)?;
690 self.writer.pad_to_block()?;
691 let mut buf = self.handle.allocate_buffer(self.writer.flushable_bytes()).await;
692 let offset = self.writer.take_flushable(buf.as_mut());
693 self.handle.overwrite(offset, buf.as_mut(), OverwriteOptions::default()).await?;
694 let len =
695 std::cmp::max(MIN_SUPER_BLOCK_SIZE, self.writer.journal_file_checkpoint().file_offset)
696 + SUPER_BLOCK_CHUNK_SIZE;
697 self.handle
698 .truncate_with_options(
699 Options {
700 skip_journal_checks: true,
701 borrow_metadata_space: true,
702 ..Default::default()
703 },
704 len,
705 )
706 .await?;
707 Ok(())
708 }
709}
710
711pub struct RecordReader {
712 reader: JournalReader,
713}
714
715impl RecordReader {
716 pub async fn next_item(&mut self) -> Result<SuperBlockRecord, Error> {
717 loop {
718 match self.reader.deserialize().await? {
719 ReadResult::Reset(_) => bail!("Unexpected reset"),
720 ReadResult::ChecksumMismatch => bail!("Checksum mismatch"),
721 ReadResult::Some(SuperBlockRecord::Extent(extent)) => {
722 ensure!(extent.is_valid(), FxfsError::Inconsistent);
723 self.reader.handle().push_extent(0, extent)
724 }
725 ReadResult::Some(x) => return Ok(x),
726 }
727 }
728 }
729}
730
731#[cfg(test)]
732mod tests {
733 use super::{
734 MIN_SUPER_BLOCK_SIZE, SUPER_BLOCK_CHUNK_SIZE, SUPER_BLOCK_MAGIC, SuperBlockHeader,
735 SuperBlockInstance, SuperBlockManager, SuperBlockRecord, UuidWrapper, compact_root_parent,
736 write,
737 };
738 use crate::filesystem::{FxFilesystem, OpenFxFilesystem, SyncOptions};
739 use crate::object_handle::ReadObjectHandle;
740 use crate::object_store::journal::JournalCheckpoint;
741 use crate::object_store::journal::writer::JournalWriter;
742 use crate::object_store::transaction::{Options, lock_keys};
743 use crate::object_store::{
744 DataObjectHandle, HandleOptions, ObjectHandle, ObjectKey, ObjectStore,
745 };
746 use crate::serialized_types::{LATEST_VERSION, Versioned, VersionedLatest};
747 use std::io::Write;
748 use storage_device::DeviceHolder;
749 use storage_device::fake_device::FakeDevice;
750
751 const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
754 const TEST_DEVICE_BLOCK_COUNT: u64 = 16384;
755
756 async fn filesystem_and_super_block_handles()
757 -> (OpenFxFilesystem, DataObjectHandle<ObjectStore>, DataObjectHandle<ObjectStore>) {
758 let device =
759 DeviceHolder::new(FakeDevice::new(TEST_DEVICE_BLOCK_COUNT, TEST_DEVICE_BLOCK_SIZE));
760 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
761 fs.close().await.expect("Close failed");
762 let device = fs.take_device().await;
763 device.reopen(false);
764 let fs = FxFilesystem::open(device).await.expect("open failed");
765
766 let handle_a = ObjectStore::open_object(
767 &fs.object_manager().root_store(),
768 SuperBlockInstance::A.object_id(),
769 HandleOptions::default(),
770 None,
771 )
772 .await
773 .expect("open superblock failed");
774
775 let handle_b = ObjectStore::open_object(
776 &fs.object_manager().root_store(),
777 SuperBlockInstance::B.object_id(),
778 HandleOptions::default(),
779 None,
780 )
781 .await
782 .expect("open superblock failed");
783 (fs, handle_a, handle_b)
784 }
785
786 #[fuchsia::test]
787 async fn test_read_written_super_block() {
788 let (fs, _handle_a, _handle_b) = filesystem_and_super_block_handles().await;
789 const JOURNAL_OBJECT_ID: u64 = 5;
790
791 assert_eq!(
794 ObjectStore::open_object(
795 &fs.root_store(),
796 SuperBlockInstance::A.object_id(),
797 HandleOptions::default(),
798 None,
799 )
800 .await
801 .expect("open_object failed")
802 .get_size(),
803 MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
804 );
805
806 let mut created_object_ids = vec![];
809 const NUM_ENTRIES: u64 = 16384;
810 for _ in 0..NUM_ENTRIES {
811 let mut transaction = fs
812 .clone()
813 .new_transaction(lock_keys![], Options::default())
814 .await
815 .expect("new_transaction failed");
816 created_object_ids.push(
817 ObjectStore::create_object(
818 &fs.object_manager().root_parent_store(),
819 &mut transaction,
820 HandleOptions::default(),
821 None,
822 )
823 .await
824 .expect("create_object failed")
825 .object_id(),
826 );
827 transaction.commit().await.expect("commit failed");
828 }
829
830 assert!(
834 ObjectStore::open_object(
835 &fs.root_store(),
836 SuperBlockInstance::A.object_id(),
837 HandleOptions::default(),
838 None,
839 )
840 .await
841 .expect("open_object failed")
842 .get_size()
843 > MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
844 );
845
846 let written_super_block_a =
847 SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::A)
848 .await
849 .expect("read failed");
850 let written_super_block_b =
851 SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::B)
852 .await
853 .expect("read failed");
854
855 assert!(!written_super_block_a.0.guid.0.is_nil());
857
858 assert_eq!(written_super_block_a.0.guid, written_super_block_b.0.guid);
861 assert_eq!(written_super_block_a.0.guid, written_super_block_b.0.guid);
862 assert!(written_super_block_a.0.generation != written_super_block_b.0.generation);
863 assert_eq!(
864 written_super_block_a.0.root_parent_store_object_id,
865 written_super_block_b.0.root_parent_store_object_id
866 );
867 assert_eq!(
868 written_super_block_a.0.root_parent_graveyard_directory_object_id,
869 written_super_block_b.0.root_parent_graveyard_directory_object_id
870 );
871 assert_eq!(written_super_block_a.0.root_store_object_id, fs.root_store().store_object_id());
872 assert_eq!(
873 written_super_block_a.0.root_store_object_id,
874 written_super_block_b.0.root_store_object_id
875 );
876 assert_eq!(written_super_block_a.0.allocator_object_id, fs.allocator().object_id());
877 assert_eq!(
878 written_super_block_a.0.allocator_object_id,
879 written_super_block_b.0.allocator_object_id
880 );
881 assert_eq!(written_super_block_a.0.journal_object_id, JOURNAL_OBJECT_ID);
882 assert_eq!(
883 written_super_block_a.0.journal_object_id,
884 written_super_block_b.0.journal_object_id
885 );
886 assert!(
887 written_super_block_a.0.journal_checkpoint.file_offset
888 != written_super_block_b.0.journal_checkpoint.file_offset
889 );
890 assert!(
891 written_super_block_a.0.super_block_journal_file_offset
892 != written_super_block_b.0.super_block_journal_file_offset
893 );
894 assert_eq!(written_super_block_a.0.earliest_version, LATEST_VERSION);
896 assert_eq!(
897 written_super_block_a.0.earliest_version,
898 written_super_block_b.0.earliest_version
899 );
900
901 for object_id in created_object_ids {
906 let mut transaction = fs
907 .clone()
908 .new_transaction(lock_keys![], Options::default())
909 .await
910 .expect("new_transaction failed");
911 fs.object_manager()
912 .root_parent_store()
913 .adjust_refs(&mut transaction, object_id, -1)
914 .await
915 .expect("adjust_refs failed");
916 transaction.commit().await.expect("commit failed");
917 fs.object_manager()
918 .root_parent_store()
919 .tombstone_object(object_id, Options::default())
920 .await
921 .expect("tombstone failed");
922 }
923 for _ in 0..NUM_ENTRIES {
926 let mut transaction = fs
927 .clone()
928 .new_transaction(lock_keys![], Options::default())
929 .await
930 .expect("new_transaction failed");
931 ObjectStore::create_object(
932 &fs.object_manager().root_store(),
933 &mut transaction,
934 HandleOptions::default(),
935 None,
936 )
937 .await
938 .expect("create_object failed");
939 transaction.commit().await.expect("commit failed");
940 }
941
942 assert_eq!(
943 ObjectStore::open_object(
944 &fs.root_store(),
945 SuperBlockInstance::A.object_id(),
946 HandleOptions::default(),
947 None,
948 )
949 .await
950 .expect("open_object failed")
951 .get_size(),
952 MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
953 );
954 }
955
956 #[fuchsia::test]
957 async fn test_generation_comparison_wrapping() {
958 let device = DeviceHolder::new(FakeDevice::new(
959 TEST_DEVICE_BLOCK_COUNT,
960 MIN_SUPER_BLOCK_SIZE as u32,
961 ));
962 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
963 fs.close().await.expect("close");
964 let device = fs.take_device().await;
965 device.reopen(false);
966
967 let device_arc = (*device).clone();
970 let write_sb = |instance: SuperBlockInstance, generation: u64| {
971 let device = device_arc.clone();
972 async move {
973 let mut super_block_header = SuperBlockHeader::new(
974 1, 3, 4, 5, 6, 7, JournalCheckpoint::default(),
981 LATEST_VERSION,
982 );
983 super_block_header.generation = generation;
984 super_block_header.journal_checkpoint.version = LATEST_VERSION;
985
986 let mut writer = JournalWriter::new(MIN_SUPER_BLOCK_SIZE as usize, 0);
987 writer.write_all(SUPER_BLOCK_MAGIC).unwrap();
988 super_block_header.serialize_with_version(&mut writer).unwrap();
989 SuperBlockRecord::End.serialize_into(&mut writer).unwrap();
990 writer.pad_to_block().unwrap();
991
992 let mut buf = device.allocate_buffer(writer.flushable_bytes()).await;
993 writer.take_flushable(buf.as_mut());
994 device
995 .write(instance.first_extent().start, buf.as_ref())
996 .await
997 .expect("write failed");
998 }
999 };
1000
1001 write_sb(SuperBlockInstance::A, u64::MAX).await;
1003 write_sb(SuperBlockInstance::B, 0).await;
1004 let manager = SuperBlockManager::new();
1005 let (header, _) = manager
1006 .load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64)
1007 .await
1008 .expect("load failed");
1009 assert_eq!(header.generation, 0);
1010
1011 write_sb(SuperBlockInstance::A, 0).await;
1013 write_sb(SuperBlockInstance::B, u64::MAX).await;
1014 let manager = SuperBlockManager::new();
1015 let (header, _) = manager
1016 .load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64)
1017 .await
1018 .expect("load failed");
1019 assert_eq!(header.generation, 0);
1020
1021 write_sb(SuperBlockInstance::A, 100).await;
1023 write_sb(SuperBlockInstance::B, 200).await;
1024 let manager = SuperBlockManager::new();
1025 let (header, _) = manager
1026 .load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64)
1027 .await
1028 .expect("load failed");
1029 assert_eq!(header.generation, 200);
1030 }
1031
1032 #[fuchsia::test]
1033 async fn test_generation_wrapping_on_flush() {
1034 let block_size = 4096;
1035 let mut device =
1036 DeviceHolder::new(FakeDevice::new(TEST_DEVICE_BLOCK_COUNT, block_size as u32));
1037 {
1038 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1039 let root_store = fs.root_store();
1040 let mut transaction = fs
1041 .clone()
1042 .new_transaction(lock_keys![], Options::default())
1043 .await
1044 .expect("new_transaction failed");
1045 ObjectStore::create_object(
1046 &root_store,
1047 &mut transaction,
1048 HandleOptions::default(),
1049 None,
1050 )
1051 .await
1052 .expect("create_object failed");
1053 transaction.commit().await.expect("commit failed");
1054 fs.sync(SyncOptions::default()).await.expect("sync failed");
1055 fs.close().await.expect("close failed");
1056 device = fs.take_device().await;
1057 }
1058 device.reopen(false);
1059
1060 let manager = SuperBlockManager::new();
1061 let (mut header, _) =
1062 manager.load((*device).clone(), block_size as u64).await.expect("load failed");
1063
1064 {
1065 let fs = FxFilesystem::open(device).await.expect("open failed");
1066 header.generation = u64::MAX - 1;
1071 manager
1072 .save(header.clone(), (*fs).clone(), fs.root_parent_store().tree().layer_set())
1073 .await
1074 .expect("save 1 failed");
1075 header.generation = u64::MAX;
1076 manager
1077 .save(header, (*fs).clone(), fs.root_parent_store().tree().layer_set())
1078 .await
1079 .expect("save 2 failed");
1080 fs.close().await.expect("close failed");
1081 device = fs.take_device().await;
1082 device.reopen(false);
1083
1084 let fs = FxFilesystem::open(device).await.expect("open failed");
1085
1086 let root_store = fs.root_store();
1087 for _ in 0..6000 {
1088 let mut transaction = fs
1089 .clone()
1090 .new_transaction(lock_keys![], Options::default())
1091 .await
1092 .expect("new_transaction failed");
1093 ObjectStore::create_object(
1094 &root_store,
1095 &mut transaction,
1096 HandleOptions::default(),
1097 None,
1098 )
1099 .await
1100 .expect("create_object failed");
1101 transaction.commit().await.expect("commit failed");
1102 }
1103 fs.sync(SyncOptions::default()).await.expect("sync failed");
1104 fs.close().await.expect("close failed");
1105 device = fs.take_device().await;
1106 }
1107 device.reopen(false);
1108
1109 let (header, _) =
1110 manager.load((*device).clone(), block_size as u64).await.expect("load failed");
1111 assert!(header.generation < 10);
1112 }
1113
1114 #[fuchsia::test]
1115 async fn test_guid_assign_on_read() {
1116 let (fs, handle_a, _handle_b) = filesystem_and_super_block_handles().await;
1117 const JOURNAL_OBJECT_ID: u64 = 5;
1118 let mut super_block_header_a = SuperBlockHeader::new(
1119 1,
1120 fs.object_manager().root_parent_store().store_object_id(),
1121 1000,
1122 fs.root_store().store_object_id(),
1123 fs.allocator().object_id(),
1124 JOURNAL_OBJECT_ID,
1125 JournalCheckpoint { file_offset: 1234, checksum: 5678, version: LATEST_VERSION },
1126 LATEST_VERSION,
1127 );
1128 super_block_header_a.guid = UuidWrapper::nil();
1130 write(
1131 &super_block_header_a,
1132 compact_root_parent(fs.object_manager().root_parent_store().as_ref())
1133 .expect("scan failed"),
1134 handle_a,
1135 )
1136 .await
1137 .expect("write failed");
1138 let super_block_header = SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::A)
1139 .await
1140 .expect("read failed");
1141 assert!(!super_block_header.0.guid.0.is_nil());
1143 }
1144
1145 #[fuchsia::test]
1146 async fn test_init_wipes_superblocks() {
1147 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1148
1149 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1150 let root_store = fs.root_store();
1151 for _ in 0..6000 {
1153 let mut transaction = fs
1154 .clone()
1155 .new_transaction(lock_keys![], Options::default())
1156 .await
1157 .expect("new_transaction failed");
1158 ObjectStore::create_object(
1159 &root_store,
1160 &mut transaction,
1161 HandleOptions::default(),
1162 None,
1163 )
1164 .await
1165 .expect("create_object failed");
1166 transaction.commit().await.expect("commit failed");
1167 }
1168 fs.close().await.expect("Close failed");
1169 let device = fs.take_device().await;
1170 device.reopen(false);
1171
1172 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1173 .await
1174 .expect("read failed");
1175 let header = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1176 .await
1177 .expect("read failed");
1178
1179 let old_guid = header.0.guid;
1180
1181 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1183 fs.close().await.expect("Close failed");
1184 let device = fs.take_device().await;
1185 device.reopen(false);
1186
1187 let a = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1188 .await
1189 .expect("read failed");
1190 let b = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1191 .await
1192 .expect("read failed");
1193
1194 assert_eq!(a.0.guid, b.0.guid);
1195 assert_ne!(old_guid, a.0.guid);
1196 }
1197
1198 #[fuchsia::test]
1199 async fn test_alternating_super_blocks() {
1200 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1201
1202 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1203 fs.close().await.expect("Close failed");
1204 let device = fs.take_device().await;
1205 device.reopen(false);
1206
1207 let (super_block_header_a, _) =
1208 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1209 .await
1210 .expect("read failed");
1211
1212 let fs = FxFilesystem::open(device).await.expect("open failed");
1215 let root_store = fs.root_store();
1216 for _ in 0..6000 {
1218 let mut transaction = fs
1219 .clone()
1220 .new_transaction(lock_keys![], Options::default())
1221 .await
1222 .expect("new_transaction failed");
1223 ObjectStore::create_object(
1224 &root_store,
1225 &mut transaction,
1226 HandleOptions::default(),
1227 None,
1228 )
1229 .await
1230 .expect("create_object failed");
1231 transaction.commit().await.expect("commit failed");
1232 }
1233 fs.close().await.expect("Close failed");
1234 let device = fs.take_device().await;
1235 device.reopen(false);
1236
1237 let (super_block_header_a_after, _) =
1238 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1239 .await
1240 .expect("read failed");
1241 let (super_block_header_b_after, _) =
1242 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1243 .await
1244 .expect("read failed");
1245
1246 assert_eq!(
1250 (super_block_header_b_after.generation as i64
1251 - super_block_header_a_after.generation as i64)
1252 .abs(),
1253 1
1254 );
1255
1256 assert!(
1258 std::cmp::max(
1259 super_block_header_a_after.generation,
1260 super_block_header_b_after.generation
1261 ) > super_block_header_a.generation
1262 );
1263
1264 assert_eq!(super_block_header_a_after.generation & 1, super_block_header_a.generation & 1);
1266 }
1267
1268 #[fuchsia::test]
1269 async fn test_root_parent_is_compacted() {
1270 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1271
1272 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1273
1274 let mut transaction = fs
1275 .clone()
1276 .new_transaction(lock_keys![], Options::default())
1277 .await
1278 .expect("new_transaction failed");
1279 let store = fs.root_parent_store();
1280 let handle =
1281 ObjectStore::create_object(&store, &mut transaction, HandleOptions::default(), None)
1282 .await
1283 .expect("create_object failed");
1284 transaction.commit().await.expect("commit failed");
1285
1286 store
1287 .tombstone_object(handle.object_id(), Options::default())
1288 .await
1289 .expect("tombstone failed");
1290
1291 let root_store = fs.root_store();
1293 for _ in 0..6000 {
1294 let mut transaction = fs
1295 .clone()
1296 .new_transaction(lock_keys![], Options::default())
1297 .await
1298 .expect("new_transaction failed");
1299 ObjectStore::create_object(
1300 &root_store,
1301 &mut transaction,
1302 HandleOptions::default(),
1303 None,
1304 )
1305 .await
1306 .expect("create_object failed");
1307 transaction.commit().await.expect("commit failed");
1308 }
1309
1310 assert_eq!(
1313 store.tree().find(&ObjectKey::object(handle.object_id())).await.expect("find failed"),
1314 None
1315 );
1316 }
1317}