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.7 kB View raw
1use lancer_lancerfs::file; 2use lancer_lancerfs::test_helpers::setup_fs; 3 4#[test] 5fn snapshot_preserves_directory_structure() { 6 let mut fs = setup_fs(4096); 7 let root = fs.root_block(); 8 let (_inode, block_a) = fs.create_file(root, b"file_a"); 9 10 let data_a: Vec<u8> = (0..4096u16).map(|i| (i % 199) as u8).collect(); 11 fs.write_file(block_a, 0, &data_a); 12 fs.commit(); 13 14 fs.create_snapshot(b"snap1"); 15 16 fs.create_file(root, b"file_b"); 17 fs.commit(); 18 19 assert!(fs.lookup_file(root, b"file_a").is_some()); 20 assert!(fs.lookup_file(root, b"file_b").is_some()); 21 22 let snap_ref = fs.snapshot_lookup(b"snap1").expect("snapshot must exist"); 23 24 let snap_root_entry = lancer_lancerfs::btree::btree_lookup( 25 &mut fs.pool, 26 &mut fs.cache, 27 &mut fs.bio, 28 &snap_ref.root_blockref, 29 fs.state.root_object_id, 30 snap_ref.txn_filter(), 31 ) 32 .unwrap() 33 .expect("snapshot must contain root inode"); 34 35 let snap_root_block = lancer_lancerfs::blockref_block_num(&snap_root_entry); 36 let snap_root_inode = file::read_inode(&mut fs.cache, &mut fs.bio, snap_root_block).unwrap(); 37 38 let snap_file_a = lancer_lancerfs::dir::dir_lookup( 39 &mut fs.pool, 40 &mut fs.cache, 41 &mut fs.bio, 42 &snap_root_inode, 43 b"file_a", 44 snap_ref.txn_filter(), 45 ) 46 .unwrap(); 47 assert!( 48 snap_file_a.is_some(), 49 "snapshot must contain file_a (created before snapshot)" 50 ); 51 52 let snap_file_b = lancer_lancerfs::dir::dir_lookup( 53 &mut fs.pool, 54 &mut fs.cache, 55 &mut fs.bio, 56 &snap_root_inode, 57 b"file_b", 58 snap_ref.txn_filter(), 59 ) 60 .unwrap(); 61 assert!( 62 snap_file_b.is_none(), 63 "snapshot must not contain file_b (created after snapshot)" 64 ); 65 66 let snap_entry = snap_file_a.unwrap(); 67 let snap_file_block = lancer_lancerfs::blockref_block_num(&snap_entry); 68 let snap_file_inode = file::read_inode(&mut fs.cache, &mut fs.bio, snap_file_block).unwrap(); 69 70 let mut snap_buf = vec![0u8; 4096]; 71 let n = file::file_read( 72 &mut fs.pool, 73 &mut fs.cache, 74 &mut fs.bio, 75 &snap_file_inode, 76 0, 77 &mut snap_buf, 78 ) 79 .unwrap(); 80 snap_buf.truncate(n); 81 82 assert_eq!( 83 snap_buf, data_a, 84 "unmodified file in snapshot must return original data" 85 ); 86} 87 88#[test] 89fn snapshot_lookup_returns_none_for_missing() { 90 let mut fs = setup_fs(4096); 91 let result = fs.snapshot_lookup(b"nonexistent"); 92 assert!(result.is_none()); 93} 94 95#[test] 96fn delete_snapshot_removes_it() { 97 let mut fs = setup_fs(4096); 98 let root = fs.root_block(); 99 let (_inode, block) = fs.create_file(root, b"file_for_snap"); 100 101 let data = vec![0xCCu8; 100]; 102 fs.write_file(block, 0, &data); 103 fs.commit(); 104 105 fs.create_snapshot(b"to_delete"); 106 assert!(fs.snapshot_lookup(b"to_delete").is_some()); 107 108 fs.delete_snapshot(b"to_delete"); 109 assert!(fs.snapshot_lookup(b"to_delete").is_none()); 110} 111 112#[test] 113fn multiple_snapshots_coexist() { 114 let mut fs = setup_fs(4096); 115 let root = fs.root_block(); 116 let (_inode, block) = fs.create_file(root, b"multi_snap"); 117 118 let data1 = vec![0x11u8; 100]; 119 fs.write_file(block, 0, &data1); 120 fs.commit(); 121 fs.create_snapshot(b"v1"); 122 123 let data2 = vec![0x22u8; 100]; 124 fs.write_file(block, 0, &data2); 125 fs.commit(); 126 fs.create_snapshot(b"v2"); 127 128 assert!(fs.snapshot_lookup(b"v1").is_some()); 129 assert!(fs.snapshot_lookup(b"v2").is_some()); 130 131 let snap1 = fs.snapshot_lookup(b"v1").unwrap(); 132 let snap2 = fs.snapshot_lookup(b"v2").unwrap(); 133 assert_ne!(snap1.snapshot_txn_id, snap2.snapshot_txn_id); 134} 135 136#[test] 137fn delete_snapshot_bulkfree_reclaims_blocks() { 138 let mut fs = setup_fs(4096); 139 let root = fs.root_block(); 140 let (_inode, block) = fs.create_file(root, b"snapdata"); 141 142 let data: Vec<u8> = (0..4096u16).map(|i| (i % 199) as u8).collect(); 143 fs.write_file(block, 0, &data); 144 fs.commit(); 145 146 fs.create_snapshot(b"reclaim_snap"); 147 148 let data_new: Vec<u8> = (0..4096u16).map(|i| (i % 97) as u8).collect(); 149 fs.write_file(block, 0, &data_new); 150 fs.commit(); 151 152 let alloc_before = fs.count_allocated_blocks(); 153 154 fs.delete_snapshot(b"reclaim_snap"); 155 fs.commit(); 156 fs.run_bulkfree(); 157 158 let alloc_after = fs.count_allocated_blocks(); 159 assert!( 160 alloc_after < alloc_before, 161 "bulkfree should reclaim snapshot blocks: before={alloc_before}, after={alloc_after}" 162 ); 163} 164 165#[test] 166fn snapshot_data_isolation_after_overwrite() { 167 let mut fs = setup_fs(4096); 168 let root = fs.root_block(); 169 170 let (_inode, _block) = fs.create_file(root, b"cow_file"); 171 let data_a = vec![0xAAu8; 4096]; 172 fs.write_file_cow(b"cow_file", 0, &data_a); 173 fs.commit(); 174 175 fs.create_snapshot(b"before_overwrite"); 176 177 let data_b = vec![0xBBu8; 4096]; 178 fs.write_file_cow(b"cow_file", 0, &data_b); 179 fs.commit(); 180 181 let live_block = fs 182 .lookup_file(fs.root_block(), b"cow_file") 183 .expect("live file must exist"); 184 let live_data = fs.read_file(live_block, 0, 4096); 185 assert_eq!(live_data, data_b, "live tree must reflect overwritten data"); 186 187 fs.remount(); 188 189 let snap_ref = fs 190 .snapshot_lookup(b"before_overwrite") 191 .expect("snapshot must exist"); 192 let snap_root_entry = lancer_lancerfs::btree::btree_lookup( 193 &mut fs.pool, 194 &mut fs.cache, 195 &mut fs.bio, 196 &snap_ref.root_blockref, 197 fs.state.root_object_id, 198 snap_ref.txn_filter(), 199 ) 200 .unwrap() 201 .expect("snapshot must contain root inode"); 202 203 let snap_root_block = lancer_lancerfs::blockref_block_num(&snap_root_entry); 204 let snap_root_inode = file::read_inode(&mut fs.cache, &mut fs.bio, snap_root_block).unwrap(); 205 206 let snap_file_entry = lancer_lancerfs::dir::dir_lookup( 207 &mut fs.pool, 208 &mut fs.cache, 209 &mut fs.bio, 210 &snap_root_inode, 211 b"cow_file", 212 snap_ref.txn_filter(), 213 ) 214 .unwrap() 215 .expect("snapshot must contain cow_file"); 216 217 let snap_file_block = lancer_lancerfs::blockref_block_num(&snap_file_entry); 218 let snap_file_inode = file::read_inode(&mut fs.cache, &mut fs.bio, snap_file_block).unwrap(); 219 220 let mut snap_buf = vec![0u8; 4096]; 221 let n = file::file_read( 222 &mut fs.pool, 223 &mut fs.cache, 224 &mut fs.bio, 225 &snap_file_inode, 226 0, 227 &mut snap_buf, 228 ) 229 .unwrap(); 230 snap_buf.truncate(n); 231 232 assert_eq!( 233 snap_buf, data_a, 234 "snapshot must return original data (A), not overwritten data (B)" 235 ); 236} 237 238#[test] 239fn writable_snapshot_isolates_modifications() { 240 let mut fs = setup_fs(4096); 241 let root = fs.root_block(); 242 243 let (_inode, _block) = fs.create_file(root, b"original"); 244 fs.write_file_cow(b"original", 0, b"hello"); 245 fs.commit(); 246 247 fs.create_snapshot(b"snap2"); 248 249 let snap_ref = fs.snapshot_lookup(b"snap2").expect("snapshot must exist"); 250 let snap_txn = snap_ref.snapshot_txn_id; 251 let new_snap_tree = fs.snapshot_create_file(&snap_ref, b"snap_only_file"); 252 253 fs.writable_snapshot_commit(b"snap2", &new_snap_tree, snap_txn); 254 255 assert!( 256 fs.lookup_file(fs.root_block(), b"original").is_some(), 257 "live tree must still have 'original'" 258 ); 259 assert!( 260 fs.lookup_file(fs.root_block(), b"snap_only_file").is_none(), 261 "live tree must NOT have 'snap_only_file'" 262 ); 263 264 let updated_snap = fs 265 .snapshot_lookup(b"snap2") 266 .expect("snapshot must still exist"); 267 let snap_root_entry = lancer_lancerfs::btree::btree_lookup( 268 &mut fs.pool, 269 &mut fs.cache, 270 &mut fs.bio, 271 &updated_snap.root_blockref, 272 fs.state.root_object_id, 273 updated_snap.txn_filter(), 274 ) 275 .unwrap() 276 .expect("updated snapshot must contain root inode"); 277 278 let snap_root_block = lancer_lancerfs::blockref_block_num(&snap_root_entry); 279 let snap_root_inode = file::read_inode(&mut fs.cache, &mut fs.bio, snap_root_block).unwrap(); 280 281 let snap_has_original = lancer_lancerfs::dir::dir_lookup( 282 &mut fs.pool, 283 &mut fs.cache, 284 &mut fs.bio, 285 &snap_root_inode, 286 b"original", 287 None, 288 ) 289 .unwrap(); 290 assert!( 291 snap_has_original.is_some(), 292 "snapshot must contain 'original'" 293 ); 294 295 let snap_has_new = lancer_lancerfs::dir::dir_lookup( 296 &mut fs.pool, 297 &mut fs.cache, 298 &mut fs.bio, 299 &snap_root_inode, 300 b"snap_only_file", 301 None, 302 ) 303 .unwrap(); 304 assert!( 305 snap_has_new.is_some(), 306 "snapshot must contain 'snap_only_file'" 307 ); 308}