Nothing to see here, move along
1use crate::block_io::BlockIo;
2use crate::cache::BlockCache;
3use crate::error::FsError;
4use crate::integrity::crc32c;
5use lancer_core::fs::{BlockRef, DEDUP_SHARDS, SUPERBLOCK_SIZE, Superblock, SuperblockSlot};
6
7fn superblock_to_bytes(sb: &Superblock) -> [u8; SUPERBLOCK_SIZE] {
8 let mut buf = [0u8; SUPERBLOCK_SIZE];
9 let src = sb as *const Superblock as *const u8;
10 unsafe { core::ptr::copy_nonoverlapping(src, buf.as_mut_ptr(), SUPERBLOCK_SIZE) };
11 buf
12}
13
14fn superblock_from_bytes(buf: &[u8; SUPERBLOCK_SIZE]) -> Superblock {
15 unsafe { core::ptr::read_unaligned(buf.as_ptr() as *const Superblock) }
16}
17
18pub struct CommitRoots {
19 pub tree_root: BlockRef,
20 pub freemap_root: BlockRef,
21 pub dedup_roots: [BlockRef; DEDUP_SHARDS],
22 pub snapshot_root: BlockRef,
23 pub scrub_cursor: u64,
24 pub next_object_id: u64,
25}
26
27pub struct SuperblockPair {
28 slots: [Superblock; 2],
29}
30
31impl SuperblockPair {
32 pub fn load(a: Superblock, b: Superblock) -> Self {
33 Self { slots: [a, b] }
34 }
35
36 pub fn active(&self) -> &Superblock {
37 lancer_core::fs::select_superblock(&self.slots[0], &self.slots[1])
38 }
39
40 fn commit_target(&self) -> SuperblockSlot {
41 lancer_core::fs::commit_target(&self.slots[0], &self.slots[1])
42 }
43
44 fn update_slot(&mut self, slot: SuperblockSlot, sb: Superblock) {
45 match slot {
46 SuperblockSlot::A => self.slots[0] = sb,
47 SuperblockSlot::B => self.slots[1] = sb,
48 }
49 }
50}
51
52pub fn atomic_commit(
53 bio: &mut BlockIo,
54 cache: &mut BlockCache,
55 pair: &mut SuperblockPair,
56 roots: &CommitRoots,
57) -> Result<(), FsError> {
58 cache.cache_flush(bio)?;
59 bio.flush()?;
60
61 let current = pair.active();
62 let next_seq = current.next_sequence().ok_or(FsError::CorruptSuperblock)?;
63 let next_txn = current
64 .next_transaction()
65 .ok_or(FsError::CorruptSuperblock)?;
66
67 let mut new_sb = Superblock::new(current.total_blocks, current.block_size);
68 new_sb.sequence = next_seq;
69 new_sb.transaction_id = next_txn;
70 new_sb.tree_root = roots.tree_root;
71 new_sb.freemap_root = roots.freemap_root;
72 new_sb.dedup_roots = roots.dedup_roots;
73 new_sb.snapshot_root = roots.snapshot_root;
74 new_sb.scrub_cursor = roots.scrub_cursor;
75 new_sb.next_object_id = roots.next_object_id;
76 new_sb.checksum = 0;
77
78 let mut buf = superblock_to_bytes(&new_sb);
79 let checksum = crc32c(&buf[..SUPERBLOCK_SIZE - 4]);
80 buf[SUPERBLOCK_SIZE - 4..].copy_from_slice(&checksum.to_le_bytes());
81
82 let target = pair.commit_target();
83 bio.write_blocks(target.block_number(), 1, &buf)?;
84 bio.flush()?;
85
86 let final_sb = superblock_from_bytes(&buf);
87 pair.update_slot(target, final_sb);
88
89 Ok(())
90}
91
92pub fn verify_superblock_crc(sb: &Superblock) -> bool {
93 let buf = superblock_to_bytes(sb);
94 let computed = crc32c(&buf[..SUPERBLOCK_SIZE - 4]);
95 computed == sb.checksum
96}
97
98fn read_raw_superblock(bio: &mut BlockIo, slot: u64) -> Result<Superblock, FsError> {
99 let mut buf = [0u8; SUPERBLOCK_SIZE];
100 bio.read_blocks(slot, 1, &mut buf)?;
101 Ok(superblock_from_bytes(&buf))
102}
103
104fn validate_one(sb: Superblock) -> Option<Superblock> {
105 match sb.is_valid_magic() && verify_superblock_crc(&sb) {
106 true => Some(sb),
107 false => None,
108 }
109}
110
111pub fn load_validated_superblock_pair(bio: &mut BlockIo) -> Result<SuperblockPair, FsError> {
112 let sb_a = read_raw_superblock(bio, 0).ok().and_then(validate_one);
113 let sb_b = read_raw_superblock(bio, 1).ok().and_then(validate_one);
114
115 match (sb_a, sb_b) {
116 (Some(a), Some(b)) => Ok(SuperblockPair::load(a, b)),
117 (Some(a), None) => {
118 let clone = superblock_from_bytes(&superblock_to_bytes(&a));
119 Ok(SuperblockPair::load(a, clone))
120 }
121 (None, Some(b)) => {
122 let clone = superblock_from_bytes(&superblock_to_bytes(&b));
123 Ok(SuperblockPair::load(clone, b))
124 }
125 (None, None) => Err(FsError::CorruptSuperblock),
126 }
127}