Nothing to see here, move along meow
1use crate::block_io::BlockIo;
2use crate::cache::BlockCache;
3use crate::error::FsError;
4use crate::integrity;
5use lancer_core::fs::{BTREE_NODE_SIZE, BTreeNode, BlockRef};
6
7const MAX_TREE_DEPTH: usize = 5;
8
9pub struct FreemapReserved {
10 region_start: u64,
11 half_size: u64,
12 active_half: u8,
13 next_block: u64,
14 end_block: u64,
15}
16
17impl FreemapReserved {
18 pub fn init(total_blocks: u64) -> Self {
19 let reserved_count = crate::freemap::compute_reserved_count(total_blocks);
20 let half_size = reserved_count / 2;
21 let region_start = 2u64;
22 Self {
23 region_start,
24 half_size,
25 active_half: 0,
26 next_block: region_start,
27 end_block: region_start + half_size,
28 }
29 }
30
31 pub fn alloc_reserved(&mut self) -> Result<u64, FsError> {
32 match self.next_block < self.end_block {
33 true => {
34 let block = self.next_block;
35 self.next_block += 1;
36 Ok(block)
37 }
38 false => Err(FsError::ReservedExhausted),
39 }
40 }
41
42 pub fn flip_after_commit(&mut self) {
43 self.active_half = 1 - self.active_half;
44 let half_start = self.region_start + self.active_half as u64 * self.half_size;
45 self.next_block = half_start;
46 self.end_block = half_start + self.half_size;
47 }
48
49 pub fn remaining(&self) -> u64 {
50 self.end_block.saturating_sub(self.next_block)
51 }
52}
53
54struct WalkState {
55 path: [(u64, usize); MAX_TREE_DEPTH],
56 depth: usize,
57 leaf_block: u64,
58}
59
60enum CowWalk {
61 FoundLeaf(u64),
62 Err(FsError),
63}
64
65impl From<FsError> for CowWalk {
66 fn from(e: FsError) -> Self {
67 CowWalk::Err(e)
68 }
69}
70
71fn walk_to_leaf(
72 cache: &mut BlockCache,
73 bio: &mut BlockIo,
74 root: &BlockRef,
75 target_key: u64,
76) -> Result<WalkState, FsError> {
77 if root.is_null() {
78 return Err(FsError::NotFound);
79 }
80
81 let start_block = crate::blockref_block_num(root);
82 let mut path = [(0u64, 0usize); MAX_TREE_DEPTH];
83 let mut depth = 0usize;
84
85 let result = (0..MAX_TREE_DEPTH + 1).try_fold(start_block, |current_block, _| {
86 let node_data = cache.cache_read(bio, current_block).map_err(CowWalk::Err)?;
87 let node = unsafe { &*(node_data.as_ptr() as *const BTreeNode) };
88
89 match node.is_valid() {
90 true => {}
91 false => return Err(CowWalk::Err(FsError::CorruptNode)),
92 }
93
94 let child_idx = node.find_child_index(target_key);
95 match child_idx < node.entry_count() {
96 false => Err(CowWalk::Err(FsError::NotFound)),
97 true => {
98 match depth < MAX_TREE_DEPTH {
99 true => {
100 path[depth] = (current_block, child_idx);
101 depth += 1;
102 }
103 false => return Err(CowWalk::Err(FsError::CorruptNode)),
104 }
105 match node.is_leaf() {
106 true => Err(CowWalk::FoundLeaf(crate::blockref_block_num(
107 &node.entries[child_idx],
108 ))),
109 false => Ok(crate::blockref_block_num(&node.entries[child_idx])),
110 }
111 }
112 }
113 });
114
115 let leaf_block = match result {
116 Ok(_) => return Err(FsError::CorruptNode),
117 Err(CowWalk::FoundLeaf(block)) => block,
118 Err(CowWalk::Err(e)) => return Err(e),
119 };
120
121 Ok(WalkState {
122 path,
123 depth,
124 leaf_block,
125 })
126}
127
128pub fn cow_freemap_path(
129 cache: &mut BlockCache,
130 bio: &mut BlockIo,
131 freemap_root: &BlockRef,
132 modified_leaf_key: u64,
133 reserved: &mut FreemapReserved,
134) -> Result<BlockRef, FsError> {
135 let walk = walk_to_leaf(cache, bio, freemap_root, modified_leaf_key)?;
136
137 let new_leaf_block = reserved.alloc_reserved()?;
138 let leaf_data = cache.cache_read(bio, walk.leaf_block)?;
139 let mut leaf_copy = [0u8; BTREE_NODE_SIZE];
140 leaf_copy.copy_from_slice(leaf_data);
141 let leaf_hashes = integrity::compute_block_hashes(&leaf_copy);
142 cache.cache_write(bio, new_leaf_block, &leaf_copy)?;
143
144 let init_state = (
145 new_leaf_block,
146 leaf_hashes.content_hash,
147 leaf_hashes.integrity_crc,
148 );
149
150 let (child_new_block, child_hash, child_crc) = (0..walk.depth).rev().try_fold(
151 init_state,
152 |(child_block, child_content_hash, child_integrity_crc), level| {
153 let (parent_block, child_idx) = walk.path[level];
154
155 let parent_data = cache.cache_read(bio, parent_block)?;
156 let mut node_copy = [0u8; BTREE_NODE_SIZE];
157 node_copy.copy_from_slice(parent_data);
158
159 let node = unsafe { &mut *(node_copy.as_mut_ptr() as *mut BTreeNode) };
160 let old_ref = &node.entries[child_idx];
161 node.entries[child_idx] = BlockRef::new(
162 crate::block_num_to_phys(child_block),
163 old_ref.block_size_log2().unwrap_or(12),
164 old_ref.key,
165 old_ref.transaction_id,
166 child_content_hash,
167 child_integrity_crc,
168 old_ref
169 .block_type_enum()
170 .unwrap_or(lancer_core::fs::BlockType::Freemap),
171 old_ref
172 .compression_enum()
173 .unwrap_or(lancer_core::fs::Compression::None),
174 old_ref.logical_size,
175 old_ref.flags,
176 );
177
178 let parent_hashes = integrity::compute_block_hashes(&node_copy);
179 let new_parent_block = reserved.alloc_reserved()?;
180 cache.cache_write(bio, new_parent_block, &node_copy)?;
181
182 Ok::<(u64, u128, u32), FsError>((
183 new_parent_block,
184 parent_hashes.content_hash,
185 parent_hashes.integrity_crc,
186 ))
187 },
188 )?;
189
190 let mut new_root_ref = *freemap_root;
191 new_root_ref.physical_addr =
192 crate::block_num_to_phys(child_new_block) | (freemap_root.physical_addr & 0xF);
193 new_root_ref.content_hash = child_hash.to_le_bytes();
194 new_root_ref.integrity_crc = child_crc;
195 Ok(new_root_ref)
196}