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, ObjectItemV56,
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 = SuperBlockRecordV56;
225
226#[allow(clippy::large_enum_variant)]
227#[derive(Debug, Serialize, Deserialize, TypeFingerprint, Versioned)]
228pub enum SuperBlockRecordV56 {
229 Extent(Range<u64>),
232
233 ObjectItem(ObjectItemV56),
236
237 End,
239}
240
241#[allow(clippy::large_enum_variant)]
242#[derive(Migrate, Debug, Serialize, Deserialize, TypeFingerprint, Versioned)]
243#[migrate_to_version(SuperBlockRecordV56)]
244#[migrate_nodefault]
245pub enum SuperBlockRecordV55 {
246 Extent(Range<u64>),
247 ObjectItem(ObjectItemV55),
248 End,
249}
250
251#[allow(clippy::large_enum_variant)]
252#[derive(Migrate, Debug, Serialize, Deserialize, TypeFingerprint, Versioned)]
253#[migrate_to_version(SuperBlockRecordV55)]
254#[migrate_nodefault]
255pub enum SuperBlockRecordV54 {
256 Extent(Range<u64>),
257 ObjectItem(crate::object_store::object_record::ObjectItemV54),
258 End,
259}
260
261#[allow(clippy::large_enum_variant)]
262#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
263#[migrate_to_version(SuperBlockRecordV54)]
264#[migrate_nodefault]
265pub enum SuperBlockRecordV50 {
266 Extent(Range<u64>),
267 ObjectItem(ObjectItemV50),
268 End,
269}
270
271#[allow(clippy::large_enum_variant)]
272#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
273#[migrate_to_version(SuperBlockRecordV50)]
274pub enum SuperBlockRecordV49 {
275 Extent(Range<u64>),
276 ObjectItem(ObjectItemV49),
277 End,
278}
279
280#[allow(clippy::large_enum_variant)]
281#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
282#[migrate_to_version(SuperBlockRecordV49)]
283pub enum SuperBlockRecordV47 {
284 Extent(Range<u64>),
285 ObjectItem(ObjectItemV47),
286 End,
287}
288
289#[allow(clippy::large_enum_variant)]
290#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
291#[migrate_to_version(SuperBlockRecordV47)]
292pub enum SuperBlockRecordV46 {
293 Extent(Range<u64>),
294 ObjectItem(ObjectItemV46),
295 End,
296}
297
298#[allow(clippy::large_enum_variant)]
299#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
300#[migrate_to_version(SuperBlockRecordV46)]
301pub enum SuperBlockRecordV43 {
302 Extent(Range<u64>),
303 ObjectItem(ObjectItemV43),
304 End,
305}
306
307#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
308#[migrate_to_version(SuperBlockRecordV43)]
309pub enum SuperBlockRecordV41 {
310 Extent(Range<u64>),
311 ObjectItem(ObjectItemV41),
312 End,
313}
314
315#[derive(Migrate, Serialize, Deserialize, TypeFingerprint, Versioned)]
316#[migrate_to_version(SuperBlockRecordV41)]
317pub enum SuperBlockRecordV40 {
318 Extent(Range<u64>),
319 ObjectItem(ObjectItemV40),
320 End,
321}
322
323struct SuperBlockMetrics {
324 last_super_block_update_time_ms: UintProperty,
327
328 last_super_block_offset: UintProperty,
330}
331
332impl Default for SuperBlockMetrics {
333 fn default() -> Self {
334 SuperBlockMetrics {
335 last_super_block_update_time_ms: metrics::detail()
336 .create_uint("last_super_block_update_time_ms", 0),
337 last_super_block_offset: metrics::detail().create_uint("last_super_block_offset", 0),
338 }
339 }
340}
341
342async fn read(
345 device: Arc<dyn Device>,
346 block_size: u64,
347 instance: SuperBlockInstance,
348) -> Result<(SuperBlockHeader, SuperBlockInstance, ObjectStore), Error> {
349 let (super_block_header, mut reader) = SuperBlockHeader::read_header(device.clone(), instance)
350 .await
351 .context("failed to read superblock")?;
352 let root_parent = ObjectStore::new_root_parent(
353 device,
354 block_size,
355 super_block_header.root_parent_store_object_id,
356 );
357 root_parent.set_graveyard_directory_object_id(
358 super_block_header.root_parent_graveyard_directory_object_id,
359 );
360
361 loop {
362 let mutation = match reader.next_item().await? {
364 SuperBlockRecord::Extent(_) => bail!("Unexpected extent record"),
366 SuperBlockRecord::ObjectItem(item) => Mutation::insert_object(item.key, item.value),
367 SuperBlockRecord::End => break,
368 };
369 root_parent.apply_mutation(
370 mutation,
371 &ApplyContext {
372 mode: ApplyMode::Replay,
373 checkpoint: JournalCheckpoint { file_offset: 0, ..Default::default() },
377 },
378 AssocObj::None,
379 )?;
380 }
381 Ok((super_block_header, instance, root_parent))
382}
383
384async fn write<S: HandleOwner>(
387 super_block_header: &SuperBlockHeader,
388 items: LayerSet<ObjectKey, ObjectValue>,
389 handle: DataObjectHandle<S>,
390) -> Result<(), Error> {
391 let object_manager = handle.store().filesystem().object_manager().clone();
392 let mut writer =
399 SuperBlockWriter::new(handle, super_block_header, object_manager.metadata_reservation())
400 .await?;
401 let mut merger = items.merger();
402 let mut iter = LSMTree::major_iter(merger.query(Query::FullScan).await?).await?;
403 while let Some(item) = iter.get() {
404 writer.write_root_parent_item(item.cloned()).await?;
405 iter.advance().await?;
406 }
407 writer.finalize().await
408}
409
410pub fn compact_root_parent(
413 root_parent_store: &ObjectStore,
414) -> Result<LayerSet<ObjectKey, ObjectValue>, Error> {
415 let tree = root_parent_store.tree();
418 let layer_set = tree.layer_set();
419 {
420 let mut merger = layer_set.merger();
421 let mut iter = LSMTree::major_iter(merger.query(Query::FullScan).now_or_never().unwrap()?)
422 .now_or_never()
423 .unwrap()?;
424 let new_layer = LSMTree::new_mutable_layer();
425 while let Some(item_ref) = iter.get() {
426 new_layer.insert(item_ref.cloned())?;
427 iter.advance().now_or_never().unwrap()?;
428 }
429 tree.set_mutable_layer(new_layer);
430 }
431 Ok(layer_set)
432}
433
434pub(super) struct SuperBlockManager {
437 pub next_instance: Mutex<SuperBlockInstance>,
438 metrics: SuperBlockMetrics,
439}
440
441impl SuperBlockManager {
442 pub fn new() -> Self {
443 Self { next_instance: Mutex::new(SuperBlockInstance::A), metrics: Default::default() }
444 }
445
446 pub async fn load(
449 &self,
450 device: Arc<dyn Device>,
451 block_size: u64,
452 ) -> Result<(SuperBlockHeader, ObjectStore), Error> {
453 debug_assert!(MIN_SUPER_BLOCK_SIZE == block_size);
457
458 let (super_block, current_super_block, root_parent) = match futures::join!(
459 read(device.clone(), block_size, SuperBlockInstance::A),
460 read(device.clone(), block_size, SuperBlockInstance::B)
461 ) {
462 (Err(e1), Err(e2)) => {
463 bail!("Failed to load both superblocks due to {:?}\nand\n{:?}", e1, e2)
464 }
465 (Ok(result), Err(_)) => result,
466 (Err(_), Ok(result)) => result,
467 (Ok(result1), Ok(result2)) => {
468 if (result2.0.generation as i64).wrapping_sub(result1.0.generation as i64) > 0 {
470 result2
471 } else {
472 result1
473 }
474 }
475 };
476 info!(super_block:?, current_super_block:?; "loaded super-block");
477 *self.next_instance.lock() = current_super_block.next();
478 Ok((super_block, root_parent))
479 }
480
481 pub async fn save(
484 &self,
485 super_block_header: SuperBlockHeader,
486 filesystem: Arc<FxFilesystem>,
487 root_parent: LayerSet<ObjectKey, ObjectValue>,
488 ) -> Result<(), Error> {
489 let root_store = filesystem.root_store();
490 let object_id = {
491 let mut next_instance = self.next_instance.lock();
492 let object_id = next_instance.object_id();
493 *next_instance = next_instance.next();
494 object_id
495 };
496 let handle = ObjectStore::open_object(
497 &root_store,
498 object_id,
499 HandleOptions { skip_journal_checks: true, ..Default::default() },
500 None,
501 )
502 .await
503 .context("Failed to open superblock object")?;
504 write(&super_block_header, root_parent, handle).await?;
505 self.metrics
506 .last_super_block_offset
507 .set(super_block_header.super_block_journal_file_offset);
508 self.metrics.last_super_block_update_time_ms.set(
509 SystemTime::now()
510 .duration_since(SystemTime::UNIX_EPOCH)
511 .unwrap()
512 .as_millis()
513 .try_into()
514 .unwrap_or(0u64),
515 );
516 Ok(())
517 }
518}
519
520impl SuperBlockHeader {
521 pub fn new(
523 generation: u64,
524 root_parent_store_object_id: u64,
525 root_parent_graveyard_directory_object_id: u64,
526 root_store_object_id: u64,
527 allocator_object_id: u64,
528 journal_object_id: u64,
529 journal_checkpoint: JournalCheckpoint,
530 earliest_version: Version,
531 ) -> Self {
532 SuperBlockHeader {
533 guid: UuidWrapper::new(),
534 generation,
535 root_parent_store_object_id,
536 root_parent_graveyard_directory_object_id,
537 root_store_object_id,
538 allocator_object_id,
539 journal_object_id,
540 journal_checkpoint,
541 earliest_version,
542 ..Default::default()
543 }
544 }
545
546 async fn read_header(
549 device: Arc<dyn Device>,
550 target_super_block: SuperBlockInstance,
551 ) -> Result<(SuperBlockHeader, RecordReader), Error> {
552 let handle = BootstrapObjectHandle::new(
553 target_super_block.object_id(),
554 device,
555 target_super_block.first_extent(),
556 );
557 let mut reader = JournalReader::new(handle, &JournalCheckpoint::default());
558 reader.set_eof_ok();
559
560 reader.fill_buf().await?;
561
562 let mut super_block_header;
563 let super_block_version;
564 reader.consume({
565 let mut cursor = std::io::Cursor::new(reader.buffer());
566 let mut magic_bytes: [u8; 8] = [0; 8];
568 cursor.read_exact(&mut magic_bytes)?;
569 if magic_bytes.as_slice() != SUPER_BLOCK_MAGIC.as_slice() {
570 bail!("Invalid magic: {:?}", magic_bytes);
571 }
572 (super_block_header, super_block_version) =
573 SuperBlockHeader::deserialize_with_version(&mut cursor)?;
574
575 let mut stores = HashSet::new();
577 ensure!(
578 stores.insert(super_block_header.root_parent_store_object_id),
579 FxfsError::Inconsistent
580 );
581 ensure!(
582 stores.insert(super_block_header.root_store_object_id),
583 FxfsError::Inconsistent
584 );
585
586 let mut root_parent_objects = HashSet::new();
588 ensure!(
589 root_parent_objects
590 .insert(super_block_header.root_parent_graveyard_directory_object_id),
591 FxfsError::Inconsistent
592 );
593 ensure!(
594 root_parent_objects.insert(super_block_header.root_store_object_id),
595 FxfsError::Inconsistent
596 );
597 ensure!(
598 root_parent_objects.insert(super_block_header.journal_object_id),
599 FxfsError::Inconsistent
600 );
601
602 ensure!(
604 !stores.contains(&super_block_header.allocator_object_id),
605 FxfsError::Inconsistent
606 );
607
608 if super_block_version < EARLIEST_SUPPORTED_VERSION {
609 bail!("Unsupported SuperBlock version: {:?}", super_block_version);
610 }
611
612 if super_block_header.journal_checkpoint.version < EARLIEST_SUPPORTED_VERSION {
616 bail!(
617 "Unsupported JournalCheckpoint version: {:?}",
618 super_block_header.journal_checkpoint.version
619 );
620 }
621
622 if super_block_header.earliest_version < EARLIEST_SUPPORTED_VERSION {
623 bail!(
624 "Filesystem contains struct with unsupported version: {:?}",
625 super_block_header.earliest_version
626 );
627 }
628
629 cursor.position() as usize
630 });
631
632 if super_block_version < SMALL_SUPERBLOCK_VERSION {
636 reader.handle().push_extent(0, target_super_block.legacy_first_extent());
637 } else if super_block_version < FIRST_EXTENT_IN_SUPERBLOCK_VERSION {
638 reader.handle().push_extent(0, target_super_block.first_extent())
639 }
640
641 if super_block_header.guid.0.is_nil() {
643 super_block_header.guid = UuidWrapper::new();
644 }
645 reader.set_version(super_block_version);
646 Ok((super_block_header, RecordReader { reader }))
647 }
648}
649
650struct SuperBlockWriter<'a, S: HandleOwner> {
651 handle: DataObjectHandle<S>,
652 writer: JournalWriter,
653 existing_extents: VecDeque<FileExtent>,
654 size: u64,
655 reservation: &'a Reservation,
656}
657
658impl<'a, S: HandleOwner> SuperBlockWriter<'a, S> {
659 pub async fn new(
662 handle: DataObjectHandle<S>,
663 super_block_header: &SuperBlockHeader,
664 reservation: &'a Reservation,
665 ) -> Result<Self, Error> {
666 let existing_extents = handle.device_extents().await?;
667 let mut this = Self {
668 handle,
669 writer: JournalWriter::new(BLOCK_SIZE as usize, 0),
670 existing_extents: existing_extents.into_iter().collect(),
671 size: 0,
672 reservation,
673 };
674 this.writer.write_all(SUPER_BLOCK_MAGIC)?;
675 super_block_header.serialize_with_version(&mut this.writer)?;
676 Ok(this)
677 }
678
679 fn try_extend_existing(&mut self, target_size: u64) -> Result<(), Error> {
682 while self.size < target_size {
683 if let Some(extent) = self.existing_extents.pop_front() {
684 ensure!(
685 extent.logical_range().start == self.size,
686 "superblock file contains a hole."
687 );
688 self.size += extent.length();
689 SuperBlockRecord::Extent(extent.device_range().clone())
690 .serialize_into(&mut self.writer)?;
691 } else {
692 break;
693 }
694 }
695 Ok(())
696 }
697
698 pub async fn write_root_parent_item(&mut self, record: ObjectItem) -> Result<(), Error> {
699 let min_len = self.writer.journal_file_checkpoint().file_offset + SUPER_BLOCK_CHUNK_SIZE;
700 self.try_extend_existing(min_len)?;
701 if min_len > self.size {
702 let mut transaction = self
704 .handle
705 .new_transaction_with_options(Options {
706 skip_journal_checks: true,
707 borrow_metadata_space: true,
708 allocator_reservation: Some(self.reservation),
709 ..Default::default()
710 })
711 .await?;
712 let mut file_range = self.size..self.size + SUPER_BLOCK_CHUNK_SIZE;
713 let allocated = self
714 .handle
715 .preallocate_range(&mut transaction, &mut file_range)
716 .await
717 .context("preallocate superblock")?;
718 if file_range.start < file_range.end {
719 bail!("preallocate_range returned too little space");
720 }
721 transaction.commit().await?;
722 for device_range in allocated {
723 self.size += device_range.end - device_range.start;
724 SuperBlockRecord::Extent(device_range).serialize_into(&mut self.writer)?;
725 }
726 }
727 SuperBlockRecord::ObjectItem(record).serialize_into(&mut self.writer)?;
728 Ok(())
729 }
730
731 pub async fn finalize(mut self) -> Result<(), Error> {
732 SuperBlockRecord::End.serialize_into(&mut self.writer)?;
733 self.writer.pad_to_block()?;
734 let mut buf = self.handle.allocate_buffer(self.writer.flushable_bytes()).await;
735 let offset = self.writer.take_flushable(buf.as_mut());
736 self.handle.overwrite(offset, buf.as_mut(), OverwriteOptions::default()).await?;
737 let len =
738 std::cmp::max(MIN_SUPER_BLOCK_SIZE, self.writer.journal_file_checkpoint().file_offset)
739 + SUPER_BLOCK_CHUNK_SIZE;
740 self.handle
741 .truncate_with_options(
742 Options {
743 skip_journal_checks: true,
744 borrow_metadata_space: true,
745 ..Default::default()
746 },
747 len,
748 )
749 .await?;
750 Ok(())
751 }
752}
753
754pub struct RecordReader {
755 reader: JournalReader,
756}
757
758impl RecordReader {
759 pub async fn next_item(&mut self) -> Result<SuperBlockRecord, Error> {
760 loop {
761 match self.reader.deserialize().await? {
762 ReadResult::Reset(_) => bail!("Unexpected reset"),
763 ReadResult::ChecksumMismatch => bail!("Checksum mismatch"),
764 ReadResult::Some(SuperBlockRecord::Extent(extent)) => {
765 ensure!(extent.is_valid(), FxfsError::Inconsistent);
766 self.reader.handle().push_extent(0, extent)
767 }
768 ReadResult::Some(x) => return Ok(x),
769 }
770 }
771 }
772}
773
774#[cfg(test)]
775mod tests {
776 use super::{
777 MIN_SUPER_BLOCK_SIZE, SUPER_BLOCK_CHUNK_SIZE, SUPER_BLOCK_MAGIC, SuperBlockHeader,
778 SuperBlockInstance, SuperBlockManager, SuperBlockRecord, UuidWrapper, compact_root_parent,
779 write,
780 };
781 use crate::filesystem::{FxFilesystem, OpenFxFilesystem, SyncOptions};
782 use crate::object_handle::ReadObjectHandle;
783 use crate::object_store::journal::JournalCheckpoint;
784 use crate::object_store::journal::writer::JournalWriter;
785 use crate::object_store::transaction::{Options, lock_keys};
786 use crate::object_store::{
787 DataObjectHandle, HandleOptions, ObjectHandle, ObjectKey, ObjectStore,
788 };
789 use crate::serialized_types::{LATEST_VERSION, Versioned, VersionedLatest};
790 use std::io::Write;
791 use storage_device::DeviceHolder;
792 use storage_device::fake_device::FakeDevice;
793
794 const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
797 const TEST_DEVICE_BLOCK_COUNT: u64 = 16384;
798
799 async fn filesystem_and_super_block_handles()
800 -> (OpenFxFilesystem, DataObjectHandle<ObjectStore>, DataObjectHandle<ObjectStore>) {
801 let device =
802 DeviceHolder::new(FakeDevice::new(TEST_DEVICE_BLOCK_COUNT, TEST_DEVICE_BLOCK_SIZE));
803 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
804 fs.close().await.expect("Close failed");
805 let device = fs.take_device().await;
806 device.reopen(false);
807 let fs = FxFilesystem::open(device).await.expect("open failed");
808
809 let handle_a = ObjectStore::open_object(
810 &fs.object_manager().root_store(),
811 SuperBlockInstance::A.object_id(),
812 HandleOptions::default(),
813 None,
814 )
815 .await
816 .expect("open superblock failed");
817
818 let handle_b = ObjectStore::open_object(
819 &fs.object_manager().root_store(),
820 SuperBlockInstance::B.object_id(),
821 HandleOptions::default(),
822 None,
823 )
824 .await
825 .expect("open superblock failed");
826 (fs, handle_a, handle_b)
827 }
828
829 #[fuchsia::test]
830 async fn test_read_written_super_block() {
831 let (fs, _handle_a, _handle_b) = filesystem_and_super_block_handles().await;
832 const JOURNAL_OBJECT_ID: u64 = 5;
833
834 assert_eq!(
837 ObjectStore::open_object(
838 &fs.root_store(),
839 SuperBlockInstance::A.object_id(),
840 HandleOptions::default(),
841 None,
842 )
843 .await
844 .expect("open_object failed")
845 .get_size(),
846 MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
847 );
848
849 let mut created_object_ids = vec![];
852 const NUM_ENTRIES: u64 = 16384;
853 for _ in 0..NUM_ENTRIES {
854 let mut transaction = fs
855 .root_store()
856 .new_transaction(lock_keys![], Options::default())
857 .await
858 .expect("new_transaction failed");
859 created_object_ids.push(
860 ObjectStore::create_object(
861 &fs.object_manager().root_parent_store(),
862 &mut transaction,
863 HandleOptions::default(),
864 None,
865 )
866 .await
867 .expect("create_object failed")
868 .object_id(),
869 );
870 transaction.commit().await.expect("commit failed");
871 }
872
873 assert!(
877 ObjectStore::open_object(
878 &fs.root_store(),
879 SuperBlockInstance::A.object_id(),
880 HandleOptions::default(),
881 None,
882 )
883 .await
884 .expect("open_object failed")
885 .get_size()
886 > MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
887 );
888
889 let written_super_block_a =
890 SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::A)
891 .await
892 .expect("read failed");
893 let written_super_block_b =
894 SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::B)
895 .await
896 .expect("read failed");
897
898 assert!(!written_super_block_a.0.guid.0.is_nil());
900
901 assert_eq!(written_super_block_a.0.guid, written_super_block_b.0.guid);
904 assert_eq!(written_super_block_a.0.guid, written_super_block_b.0.guid);
905 assert!(written_super_block_a.0.generation != written_super_block_b.0.generation);
906 assert_eq!(
907 written_super_block_a.0.root_parent_store_object_id,
908 written_super_block_b.0.root_parent_store_object_id
909 );
910 assert_eq!(
911 written_super_block_a.0.root_parent_graveyard_directory_object_id,
912 written_super_block_b.0.root_parent_graveyard_directory_object_id
913 );
914 assert_eq!(written_super_block_a.0.root_store_object_id, fs.root_store().store_object_id());
915 assert_eq!(
916 written_super_block_a.0.root_store_object_id,
917 written_super_block_b.0.root_store_object_id
918 );
919 assert_eq!(written_super_block_a.0.allocator_object_id, fs.allocator().object_id());
920 assert_eq!(
921 written_super_block_a.0.allocator_object_id,
922 written_super_block_b.0.allocator_object_id
923 );
924 assert_eq!(written_super_block_a.0.journal_object_id, JOURNAL_OBJECT_ID);
925 assert_eq!(
926 written_super_block_a.0.journal_object_id,
927 written_super_block_b.0.journal_object_id
928 );
929 assert!(
930 written_super_block_a.0.journal_checkpoint.file_offset
931 != written_super_block_b.0.journal_checkpoint.file_offset
932 );
933 assert!(
934 written_super_block_a.0.super_block_journal_file_offset
935 != written_super_block_b.0.super_block_journal_file_offset
936 );
937 assert_eq!(written_super_block_a.0.earliest_version, LATEST_VERSION);
939 assert_eq!(
940 written_super_block_a.0.earliest_version,
941 written_super_block_b.0.earliest_version
942 );
943
944 for object_id in created_object_ids {
949 let mut transaction = fs
950 .root_store()
951 .new_transaction(lock_keys![], Options::default())
952 .await
953 .expect("new_transaction failed");
954 fs.object_manager()
955 .root_parent_store()
956 .adjust_refs(&mut transaction, object_id, -1)
957 .await
958 .expect("adjust_refs failed");
959 transaction.commit().await.expect("commit failed");
960 fs.object_manager()
961 .root_parent_store()
962 .tombstone_object(object_id, Options::default())
963 .await
964 .expect("tombstone failed");
965 }
966 for _ in 0..NUM_ENTRIES {
969 let mut transaction = fs
970 .root_store()
971 .new_transaction(lock_keys![], Options::default())
972 .await
973 .expect("new_transaction failed");
974 ObjectStore::create_object(
975 &fs.object_manager().root_store(),
976 &mut transaction,
977 HandleOptions::default(),
978 None,
979 )
980 .await
981 .expect("create_object failed");
982 transaction.commit().await.expect("commit failed");
983 }
984
985 assert_eq!(
986 ObjectStore::open_object(
987 &fs.root_store(),
988 SuperBlockInstance::A.object_id(),
989 HandleOptions::default(),
990 None,
991 )
992 .await
993 .expect("open_object failed")
994 .get_size(),
995 MIN_SUPER_BLOCK_SIZE + SUPER_BLOCK_CHUNK_SIZE
996 );
997 }
998
999 #[fuchsia::test]
1000 async fn test_generation_comparison_wrapping() {
1001 let device = DeviceHolder::new(FakeDevice::new(
1002 TEST_DEVICE_BLOCK_COUNT,
1003 MIN_SUPER_BLOCK_SIZE as u32,
1004 ));
1005 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1006 fs.close().await.expect("close");
1007 let device = fs.take_device().await;
1008 device.reopen(false);
1009
1010 let device_arc = (*device).clone();
1013 let write_sb = |instance: SuperBlockInstance, generation: u64| {
1014 let device = device_arc.clone();
1015 async move {
1016 let mut super_block_header = SuperBlockHeader::new(
1017 1, 3, 4, 5, 6, 7, JournalCheckpoint::default(),
1024 LATEST_VERSION,
1025 );
1026 super_block_header.generation = generation;
1027 super_block_header.journal_checkpoint.version = LATEST_VERSION;
1028
1029 let mut writer = JournalWriter::new(MIN_SUPER_BLOCK_SIZE as usize, 0);
1030 writer.write_all(SUPER_BLOCK_MAGIC).unwrap();
1031 super_block_header.serialize_with_version(&mut writer).unwrap();
1032 SuperBlockRecord::End.serialize_into(&mut writer).unwrap();
1033 writer.pad_to_block().unwrap();
1034
1035 let mut buf = device.allocate_buffer(writer.flushable_bytes()).await;
1036 writer.take_flushable(buf.as_mut());
1037 device
1038 .write(instance.first_extent().start, buf.as_ref())
1039 .await
1040 .expect("write failed");
1041 }
1042 };
1043
1044 write_sb(SuperBlockInstance::A, u64::MAX).await;
1046 write_sb(SuperBlockInstance::B, 0).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, 0).await;
1056 write_sb(SuperBlockInstance::B, u64::MAX).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, 0);
1063
1064 write_sb(SuperBlockInstance::A, 100).await;
1066 write_sb(SuperBlockInstance::B, 200).await;
1067 let manager = SuperBlockManager::new();
1068 let (header, _) = manager
1069 .load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64)
1070 .await
1071 .expect("load failed");
1072 assert_eq!(header.generation, 200);
1073 }
1074
1075 #[fuchsia::test]
1076 async fn test_generation_wrapping_on_flush() {
1077 let block_size = 4096;
1078 let mut device =
1079 DeviceHolder::new(FakeDevice::new(TEST_DEVICE_BLOCK_COUNT, block_size as u32));
1080 {
1081 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1082 let root_store = fs.root_store();
1083 let mut transaction = fs
1084 .root_store()
1085 .new_transaction(lock_keys![], Options::default())
1086 .await
1087 .expect("new_transaction failed");
1088 ObjectStore::create_object(
1089 &root_store,
1090 &mut transaction,
1091 HandleOptions::default(),
1092 None,
1093 )
1094 .await
1095 .expect("create_object failed");
1096 transaction.commit().await.expect("commit failed");
1097 fs.sync(SyncOptions::default()).await.expect("sync failed");
1098 fs.close().await.expect("close failed");
1099 device = fs.take_device().await;
1100 }
1101 device.reopen(false);
1102
1103 let manager = SuperBlockManager::new();
1104 let (mut header, _) =
1105 manager.load((*device).clone(), block_size as u64).await.expect("load failed");
1106
1107 {
1108 let fs = FxFilesystem::open(device).await.expect("open failed");
1109 header.generation = u64::MAX - 1;
1114 manager
1115 .save(header.clone(), (*fs).clone(), fs.root_parent_store().tree().layer_set())
1116 .await
1117 .expect("save 1 failed");
1118 header.generation = u64::MAX;
1119 manager
1120 .save(header, (*fs).clone(), fs.root_parent_store().tree().layer_set())
1121 .await
1122 .expect("save 2 failed");
1123 fs.close().await.expect("close failed");
1124 device = fs.take_device().await;
1125 device.reopen(false);
1126
1127 let fs = FxFilesystem::open(device).await.expect("open failed");
1128
1129 let root_store = fs.root_store();
1130 for _ in 0..6000 {
1131 let mut transaction = fs
1132 .root_store()
1133 .new_transaction(lock_keys![], Options::default())
1134 .await
1135 .expect("new_transaction failed");
1136 ObjectStore::create_object(
1137 &root_store,
1138 &mut transaction,
1139 HandleOptions::default(),
1140 None,
1141 )
1142 .await
1143 .expect("create_object failed");
1144 transaction.commit().await.expect("commit failed");
1145 }
1146 fs.sync(SyncOptions::default()).await.expect("sync failed");
1147 fs.close().await.expect("close failed");
1148 device = fs.take_device().await;
1149 }
1150 device.reopen(false);
1151
1152 let (header, _) =
1153 manager.load((*device).clone(), block_size as u64).await.expect("load failed");
1154 assert!(header.generation < 10);
1155 }
1156
1157 #[fuchsia::test]
1158 async fn test_guid_assign_on_read() {
1159 let (fs, handle_a, _handle_b) = filesystem_and_super_block_handles().await;
1160 const JOURNAL_OBJECT_ID: u64 = 5;
1161 let mut super_block_header_a = SuperBlockHeader::new(
1162 1,
1163 fs.object_manager().root_parent_store().store_object_id(),
1164 1000,
1165 fs.root_store().store_object_id(),
1166 fs.allocator().object_id(),
1167 JOURNAL_OBJECT_ID,
1168 JournalCheckpoint { file_offset: 1234, checksum: 5678, version: LATEST_VERSION },
1169 LATEST_VERSION,
1170 );
1171 super_block_header_a.guid = UuidWrapper::nil();
1173 write(
1174 &super_block_header_a,
1175 compact_root_parent(fs.object_manager().root_parent_store().as_ref())
1176 .expect("scan failed"),
1177 handle_a,
1178 )
1179 .await
1180 .expect("write failed");
1181 let super_block_header = SuperBlockHeader::read_header(fs.device(), SuperBlockInstance::A)
1182 .await
1183 .expect("read failed");
1184 assert!(!super_block_header.0.guid.0.is_nil());
1186 }
1187
1188 #[fuchsia::test]
1189 async fn test_init_wipes_superblocks() {
1190 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1191
1192 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1193 let root_store = fs.root_store();
1194 for _ in 0..6000 {
1196 let mut transaction = fs
1197 .root_store()
1198 .new_transaction(lock_keys![], Options::default())
1199 .await
1200 .expect("new_transaction failed");
1201 ObjectStore::create_object(
1202 &root_store,
1203 &mut transaction,
1204 HandleOptions::default(),
1205 None,
1206 )
1207 .await
1208 .expect("create_object failed");
1209 transaction.commit().await.expect("commit failed");
1210 }
1211 fs.close().await.expect("Close failed");
1212 let device = fs.take_device().await;
1213 device.reopen(false);
1214
1215 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1216 .await
1217 .expect("read failed");
1218 let header = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1219 .await
1220 .expect("read failed");
1221
1222 let old_guid = header.0.guid;
1223
1224 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1226 fs.close().await.expect("Close failed");
1227 let device = fs.take_device().await;
1228 device.reopen(false);
1229
1230 let a = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1231 .await
1232 .expect("read failed");
1233 let b = SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1234 .await
1235 .expect("read failed");
1236
1237 assert_eq!(a.0.guid, b.0.guid);
1238 assert_ne!(old_guid, a.0.guid);
1239 }
1240
1241 #[fuchsia::test]
1242 async fn test_alternating_super_blocks() {
1243 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1244
1245 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1246 fs.close().await.expect("Close failed");
1247 let device = fs.take_device().await;
1248 device.reopen(false);
1249
1250 let (super_block_header_a, _) =
1251 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1252 .await
1253 .expect("read failed");
1254
1255 let fs = FxFilesystem::open(device).await.expect("open failed");
1258 let root_store = fs.root_store();
1259 for _ in 0..6000 {
1261 let mut transaction = fs
1262 .root_store()
1263 .new_transaction(lock_keys![], Options::default())
1264 .await
1265 .expect("new_transaction failed");
1266 ObjectStore::create_object(
1267 &root_store,
1268 &mut transaction,
1269 HandleOptions::default(),
1270 None,
1271 )
1272 .await
1273 .expect("create_object failed");
1274 transaction.commit().await.expect("commit failed");
1275 }
1276 fs.close().await.expect("Close failed");
1277 let device = fs.take_device().await;
1278 device.reopen(false);
1279
1280 let (super_block_header_a_after, _) =
1281 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::A)
1282 .await
1283 .expect("read failed");
1284 let (super_block_header_b_after, _) =
1285 SuperBlockHeader::read_header(device.clone(), SuperBlockInstance::B)
1286 .await
1287 .expect("read failed");
1288
1289 assert_eq!(
1293 (super_block_header_b_after.generation as i64
1294 - super_block_header_a_after.generation as i64)
1295 .abs(),
1296 1
1297 );
1298
1299 assert!(
1301 std::cmp::max(
1302 super_block_header_a_after.generation,
1303 super_block_header_b_after.generation
1304 ) > super_block_header_a.generation
1305 );
1306
1307 assert_eq!(super_block_header_a_after.generation & 1, super_block_header_a.generation & 1);
1309 }
1310
1311 #[fuchsia::test]
1312 async fn test_root_parent_is_compacted() {
1313 let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1314
1315 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1316
1317 let mut transaction = fs
1318 .root_store()
1319 .new_transaction(lock_keys![], Options::default())
1320 .await
1321 .expect("new_transaction failed");
1322 let store = fs.root_parent_store();
1323 let handle =
1324 ObjectStore::create_object(&store, &mut transaction, HandleOptions::default(), None)
1325 .await
1326 .expect("create_object failed");
1327 store.add_to_graveyard(&mut transaction, handle.object_id());
1328 transaction.commit().await.expect("commit failed");
1329
1330 store
1331 .tombstone_object(handle.object_id(), Options::default())
1332 .await
1333 .expect("tombstone failed");
1334
1335 let root_store = fs.root_store();
1337 for _ in 0..6000 {
1338 let mut transaction = fs
1339 .root_store()
1340 .new_transaction(lock_keys![], Options::default())
1341 .await
1342 .expect("new_transaction failed");
1343 ObjectStore::create_object(
1344 &root_store,
1345 &mut transaction,
1346 HandleOptions::default(),
1347 None,
1348 )
1349 .await
1350 .expect("create_object failed");
1351 transaction.commit().await.expect("commit failed");
1352 }
1353
1354 assert_eq!(
1357 store.tree().find(&ObjectKey::object(handle.object_id())).await.expect("find failed"),
1358 None
1359 );
1360 }
1361
1362 #[fuchsia::test]
1363 async fn test_invalid_object_ids_validation() {
1364 let device = DeviceHolder::new(FakeDevice::new(
1365 TEST_DEVICE_BLOCK_COUNT,
1366 MIN_SUPER_BLOCK_SIZE as u32,
1367 ));
1368 let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1369 fs.close().await.expect("close");
1370 let device = fs.take_device().await;
1371 device.reopen(false);
1372
1373 let device_arc = (*device).clone();
1375 let write_sb = |instance: SuperBlockInstance,
1376 root_parent_store_object_id: u64,
1377 root_parent_graveyard_directory_object_id: u64,
1378 root_store_object_id: u64,
1379 allocator_object_id: u64,
1380 journal_object_id: u64| {
1381 let device = device_arc.clone();
1382 async move {
1383 let mut super_block_header = SuperBlockHeader::new(
1384 1, root_parent_store_object_id,
1386 root_parent_graveyard_directory_object_id,
1387 root_store_object_id,
1388 allocator_object_id,
1389 journal_object_id,
1390 JournalCheckpoint::default(),
1391 LATEST_VERSION,
1392 );
1393 super_block_header.journal_checkpoint.version = LATEST_VERSION;
1394
1395 let mut writer = JournalWriter::new(MIN_SUPER_BLOCK_SIZE as usize, 0);
1396 writer.write_all(SUPER_BLOCK_MAGIC).unwrap();
1397 super_block_header.serialize_with_version(&mut writer).unwrap();
1398 SuperBlockRecord::End.serialize_into(&mut writer).unwrap();
1399 writer.pad_to_block().unwrap();
1400
1401 let mut buf = device.allocate_buffer(writer.flushable_bytes()).await;
1402 writer.take_flushable(buf.as_mut());
1403 device
1404 .write(instance.first_extent().start, buf.as_ref())
1405 .await
1406 .expect("write failed");
1407 }
1408 };
1409
1410 let manager = SuperBlockManager::new();
1411
1412 write_sb(SuperBlockInstance::A, 3, 4, 3, 5, 6).await;
1414 write_sb(SuperBlockInstance::B, 3, 4, 3, 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, 3, 6).await;
1419 write_sb(SuperBlockInstance::B, 3, 4, 5, 3, 6).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, 5, 6).await;
1424 write_sb(SuperBlockInstance::B, 3, 4, 5, 5, 6).await;
1425 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_err());
1426
1427 write_sb(SuperBlockInstance::A, 3, 4, 5, 6, 4).await;
1429 write_sb(SuperBlockInstance::B, 3, 4, 5, 6, 4).await;
1430 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_err());
1431
1432 write_sb(SuperBlockInstance::A, 3, 4, 5, 6, 7).await;
1434 write_sb(SuperBlockInstance::B, 3, 4, 5, 6, 7).await;
1435 assert!(manager.load((*device).clone(), MIN_SUPER_BLOCK_SIZE as u64).await.is_ok());
1436 }
1437}