Nothing to see here, move along meow
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 8.8 kB View raw
1use crate::block_io::BlockIo; 2use crate::btree; 3use crate::cache::BlockCache; 4use crate::commit::SuperblockPair; 5use crate::cow::FreemapReserved; 6use crate::ditto::DittoRegion; 7use crate::error::FsError; 8use crate::freemap::FreemapAllocator; 9use crate::integrity; 10use crate::pool::NodePool; 11use crate::transaction; 12use lancer_core::fs::{BLOCK_SIZE_MIN_LOG2, BlockRef, BlockType, Compression, DEDUP_SHARDS}; 13 14const MAX_SNAPSHOT_COLLISION_SLOTS: usize = 8; 15 16pub fn snapshot_name_key(name: &[u8]) -> u64 { 17 integrity::xxhash64(name) & !0xF 18} 19 20#[allow(clippy::too_many_arguments)] 21pub fn create_snapshot( 22 pool: &mut NodePool, 23 cache: &mut BlockCache, 24 bio: &mut BlockIo, 25 freemap: &mut FreemapAllocator, 26 reserved: &mut FreemapReserved, 27 sb_pair: &mut SuperblockPair, 28 tree_root: &BlockRef, 29 dedup_roots: &[BlockRef; DEDUP_SHARDS], 30 snapshot_root: &BlockRef, 31 name: &[u8], 32 ditto: Option<&DittoRegion>, 33 scrub_cursor: u64, 34 next_object_id: u64, 35) -> Result<BlockRef, FsError> { 36 transaction::transaction_commit( 37 pool, 38 cache, 39 bio, 40 freemap, 41 reserved, 42 sb_pair, 43 tree_root, 44 dedup_roots, 45 snapshot_root, 46 ditto, 47 scrub_cursor, 48 next_object_id, 49 )?; 50 51 let snap_txn = sb_pair.active().transaction_id; 52 let current_tree_root = sb_pair.active().tree_root; 53 let current_snapshot_root = sb_pair.active().snapshot_root; 54 let current_dedup_roots = sb_pair.active().dedup_roots; 55 56 let base_key = snapshot_name_key(name); 57 58 let (final_key, new_snapshot_root) = (0..MAX_SNAPSHOT_COLLISION_SLOTS) 59 .map(|slot| base_key | slot as u64) 60 .find_map(|candidate_key| { 61 let snap_entry = BlockRef::new( 62 current_tree_root.physical_block_addr(), 63 current_tree_root 64 .block_size_log2() 65 .unwrap_or(BLOCK_SIZE_MIN_LOG2), 66 candidate_key, 67 snap_txn, 68 current_tree_root.content_hash_u128(), 69 current_tree_root.integrity_crc, 70 BlockType::Indirect, 71 Compression::None, 72 name.len() as u32, 73 0, 74 ); 75 btree::btree_insert( 76 pool, 77 cache, 78 bio, 79 &current_snapshot_root, 80 candidate_key, 81 snap_entry, 82 snap_txn, 83 ) 84 .ok() 85 .map(|root| (candidate_key, root)) 86 }) 87 .ok_or(FsError::TreeFull)?; 88 let _ = final_key; 89 90 transaction::transaction_commit( 91 pool, 92 cache, 93 bio, 94 freemap, 95 reserved, 96 sb_pair, 97 &current_tree_root, 98 &current_dedup_roots, 99 &new_snapshot_root, 100 ditto, 101 scrub_cursor, 102 next_object_id, 103 )?; 104 105 Ok(sb_pair.active().snapshot_root) 106} 107 108pub fn snapshot_lookup( 109 pool: &mut NodePool, 110 cache: &mut BlockCache, 111 bio: &mut BlockIo, 112 snapshot_root: &BlockRef, 113 name: &[u8], 114) -> Result<Option<SnapshotRef>, FsError> { 115 let base_key = snapshot_name_key(name); 116 (0..MAX_SNAPSHOT_COLLISION_SLOTS) 117 .map(|slot| base_key | slot as u64) 118 .try_fold(None, |found, candidate_key| match found { 119 Some(_) => Ok(found), 120 None => btree::btree_lookup(pool, cache, bio, snapshot_root, candidate_key, None).map( 121 |opt| { 122 opt.filter(|entry| entry.logical_size == name.len() as u32) 123 .map(|entry| SnapshotRef { 124 root_blockref: entry, 125 snapshot_txn_id: entry.transaction_id, 126 }) 127 }, 128 ), 129 }) 130} 131 132pub struct SnapshotRef { 133 pub root_blockref: BlockRef, 134 pub snapshot_txn_id: u64, 135} 136 137impl SnapshotRef { 138 pub fn txn_filter(&self) -> Option<u64> { 139 Some(self.snapshot_txn_id) 140 } 141} 142 143pub fn snapshot_read_lookup( 144 pool: &mut NodePool, 145 cache: &mut BlockCache, 146 bio: &mut BlockIo, 147 snap: &SnapshotRef, 148 key: u64, 149) -> Result<Option<BlockRef>, FsError> { 150 btree::btree_lookup( 151 pool, 152 cache, 153 bio, 154 &snap.root_blockref, 155 key, 156 snap.txn_filter(), 157 ) 158} 159 160fn find_snapshot_slot_key( 161 pool: &mut NodePool, 162 cache: &mut BlockCache, 163 bio: &mut BlockIo, 164 snapshot_root: &BlockRef, 165 name: &[u8], 166) -> Result<Option<u64>, FsError> { 167 let base_key = snapshot_name_key(name); 168 (0..MAX_SNAPSHOT_COLLISION_SLOTS) 169 .map(|slot| base_key | slot as u64) 170 .try_fold(None, |found, candidate_key| match found { 171 Some(_) => Ok(found), 172 None => btree::btree_lookup(pool, cache, bio, snapshot_root, candidate_key, None).map( 173 |opt| { 174 opt.filter(|entry| entry.logical_size == name.len() as u32) 175 .map(|_| candidate_key) 176 }, 177 ), 178 }) 179} 180 181#[allow(clippy::too_many_arguments)] 182pub fn delete_snapshot( 183 pool: &mut NodePool, 184 cache: &mut BlockCache, 185 bio: &mut BlockIo, 186 freemap: &mut FreemapAllocator, 187 reserved: &mut FreemapReserved, 188 sb_pair: &mut SuperblockPair, 189 snapshot_root: &BlockRef, 190 name: &[u8], 191 ditto: Option<&DittoRegion>, 192 scrub_cursor: u64, 193 next_object_id: u64, 194) -> Result<BlockRef, FsError> { 195 let actual_key = 196 find_snapshot_slot_key(pool, cache, bio, snapshot_root, name)?.ok_or(FsError::NotFound)?; 197 let txn = sb_pair.active().transaction_id; 198 let live_tree_root = sb_pair.active().tree_root; 199 let live_dedup_roots = sb_pair.active().dedup_roots; 200 201 let new_snapshot_root = btree::btree_delete(pool, cache, bio, snapshot_root, actual_key, txn)? 202 .unwrap_or(BlockRef::ZERO); 203 204 transaction::transaction_commit( 205 pool, 206 cache, 207 bio, 208 freemap, 209 reserved, 210 sb_pair, 211 &live_tree_root, 212 &live_dedup_roots, 213 &new_snapshot_root, 214 ditto, 215 scrub_cursor, 216 next_object_id, 217 )?; 218 219 Ok(sb_pair.active().snapshot_root) 220} 221 222#[cfg(test)] 223mod tests { 224 use super::*; 225 226 #[test] 227 fn snapshot_name_key_deterministic() { 228 assert_eq!(snapshot_name_key(b"backup"), snapshot_name_key(b"backup")); 229 } 230 231 #[test] 232 fn snapshot_name_key_different_names_differ() { 233 assert_ne!(snapshot_name_key(b"snap1"), snapshot_name_key(b"snap2")); 234 } 235 236 #[test] 237 fn snapshot_name_key_empty() { 238 let _ = snapshot_name_key(b""); 239 } 240 241 #[test] 242 fn snapshot_name_key_long_name() { 243 let long: Vec<u8> = (0..1024).map(|i| (i & 0xFF) as u8).collect(); 244 let key = snapshot_name_key(&long); 245 assert_eq!(key, snapshot_name_key(&long)); 246 } 247 248 #[test] 249 fn snapshot_ref_txn_filter() { 250 let snap = SnapshotRef { 251 root_blockref: BlockRef::ZERO, 252 snapshot_txn_id: 42, 253 }; 254 assert_eq!(snap.txn_filter(), Some(42)); 255 } 256} 257 258#[allow(clippy::too_many_arguments)] 259pub fn writable_snapshot_commit( 260 pool: &mut NodePool, 261 cache: &mut BlockCache, 262 bio: &mut BlockIo, 263 freemap: &mut FreemapAllocator, 264 reserved: &mut FreemapReserved, 265 sb_pair: &mut SuperblockPair, 266 snapshot_root: &BlockRef, 267 name: &[u8], 268 new_snap_tree_root: &BlockRef, 269 snap_txn: u64, 270 ditto: Option<&DittoRegion>, 271 scrub_cursor: u64, 272 next_object_id: u64, 273) -> Result<BlockRef, FsError> { 274 let actual_key = 275 find_snapshot_slot_key(pool, cache, bio, snapshot_root, name)?.ok_or(FsError::NotFound)?; 276 let live_tree_root = sb_pair.active().tree_root; 277 let live_dedup_roots = sb_pair.active().dedup_roots; 278 279 let after_delete = btree::btree_delete(pool, cache, bio, snapshot_root, actual_key, snap_txn)? 280 .unwrap_or(BlockRef::ZERO); 281 282 let updated_entry = BlockRef::new( 283 new_snap_tree_root.physical_block_addr(), 284 new_snap_tree_root 285 .block_size_log2() 286 .unwrap_or(BLOCK_SIZE_MIN_LOG2), 287 actual_key, 288 snap_txn, 289 new_snap_tree_root.content_hash_u128(), 290 new_snap_tree_root.integrity_crc, 291 BlockType::Indirect, 292 Compression::None, 293 name.len() as u32, 294 0, 295 ); 296 297 let new_snapshot_root = btree::btree_insert( 298 pool, 299 cache, 300 bio, 301 &after_delete, 302 actual_key, 303 updated_entry, 304 snap_txn, 305 )?; 306 307 transaction::transaction_commit( 308 pool, 309 cache, 310 bio, 311 freemap, 312 reserved, 313 sb_pair, 314 &live_tree_root, 315 &live_dedup_roots, 316 &new_snapshot_root, 317 ditto, 318 scrub_cursor, 319 next_object_id, 320 )?; 321 322 Ok(sb_pair.active().snapshot_root) 323}