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