From 5828e12e85b7b66172c74e34bc5d1642e4036314 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 1 Aug 2025 21:32:16 -0600 Subject: [PATCH] Provide context for all allocations and deallocations --- src/transaction.rs | 228 ++++++++++++++++++++++++--------------------- 1 file changed, 120 insertions(+), 108 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 3ab47b3d03..329bc5e7d5 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -19,6 +19,34 @@ use crate::{ TreePtr, ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES, DIR_ENTRY_MAX_LENGTH, HEADER_RING, }; +trait AllocCtx { + fn allocate(&mut self, _addr: BlockAddr) {} + fn deallocate(&mut self, _addr: BlockAddr) {} +} + +pub struct FsCtx; +impl AllocCtx for FsCtx {} + +impl AllocCtx for TreeData { + fn allocate(&mut self, addr: BlockAddr) { + let blocks = self.data().blocks(); + self.data_mut().set_blocks( + blocks + .checked_add(addr.level().blocks::()) + .expect("node block count overflow"), + ); + } + + fn deallocate(&mut self, addr: BlockAddr) { + let blocks = self.data().blocks(); + self.data_mut().set_blocks( + blocks + .checked_sub(addr.level().blocks::()) + .expect("node block count underflow"), + ); + } +} + pub struct Transaction<'a, D: Disk> { fs: &'a mut FileSystem, //TODO: make private @@ -60,10 +88,15 @@ impl<'a, D: Disk> Transaction<'a, D> { /// Allocate a new block of size defined by `meta`, returning its address. /// - returns `Err(ENOSPC)` if a block of this size could not be alloated. /// - unsafe because order must be done carefully and changes must be flushed to disk - pub(crate) unsafe fn allocate(&mut self, meta: BlockMeta) -> Result { + pub(crate) unsafe fn allocate( + &mut self, + ctx: &mut dyn AllocCtx, + meta: BlockMeta, + ) -> Result { match self.allocator.allocate(meta) { Some(addr) => { self.allocator_log.push_back(AllocEntry::allocate(addr)); + ctx.allocate(addr); Ok(addr) } None => Err(Error::new(ENOSPC)), @@ -72,7 +105,7 @@ impl<'a, D: Disk> Transaction<'a, D> { /// Deallocate the given block. /// - unsafe because order must be done carefully and changes must be flushed to disk - pub(crate) unsafe fn deallocate(&mut self, addr: BlockAddr) { + pub(crate) unsafe fn deallocate(&mut self, ctx: &mut dyn AllocCtx, addr: BlockAddr) { //TODO: should we use some sort of not-null abstraction? assert!(!addr.is_null()); @@ -98,46 +131,22 @@ impl<'a, D: Disk> Transaction<'a, D> { // Deallocate later when syncing filesystem, to avoid re-use self.deallocate.push(addr); } + ctx.deallocate(addr); } - unsafe fn node_allocate( + unsafe fn deallocate_block( &mut self, - node: &mut TreeData, - meta: BlockMeta, - ) -> Result { - let addr = self.allocate(meta)?; - let blocks = node.data().blocks(); - node.data_mut().set_blocks( - blocks - .checked_add(addr.level().blocks()) - .expect("node allocated too many blocks"), - ); - Ok(addr) - } - - unsafe fn node_deallocate(&mut self, node: &mut TreeData, addr: BlockAddr) -> bool { - if !addr.is_null() { - self.deallocate(addr); - let blocks = node.data().blocks(); - node.data_mut().set_blocks( - blocks - .checked_sub(addr.level().blocks()) - .expect("node deallocated too many blocks"), - ); + ctx: &mut dyn AllocCtx, + ptr: BlockPtr, + ) -> bool { + if !ptr.is_null() { + self.deallocate(ctx, ptr.addr()); true } else { false } } - unsafe fn node_deallocate_block( - &mut self, - node: &mut TreeData, - ptr: BlockPtr, - ) -> bool { - self.node_deallocate(node, ptr.addr()) - } - /// Drain `self.allocator_log` and `self.deallocate`, /// updating the [`AllocList`] with the resulting state. /// @@ -210,7 +219,7 @@ impl<'a, D: Disk> Transaction<'a, D> { while new_blocks.len() * ALLOC_LIST_ENTRIES <= self.allocator_log.len() + self.deallocate.len() { - new_blocks.push(unsafe { self.allocate(BlockMeta::default())? }); + new_blocks.push(unsafe { self.allocate(&mut FsCtx, BlockMeta::default())? }); } // De-allocate old blocks (after allocation to prevent re-use) @@ -426,16 +435,17 @@ impl<'a, D: Disk> Transaction<'a, D> { /// Write block data to a new address, returning new address pub fn sync_block>( &mut self, + ctx: &mut dyn AllocCtx, mut block: BlockData, ) -> Result> { // Swap block to new address let meta = block.addr().meta(); - let old_addr = block.swap_addr(unsafe { self.allocate(meta)? }); + let old_addr = block.swap_addr(unsafe { self.allocate(ctx, meta)? }); // Deallocate old address (will only take effect after sync_allocator, which helps to // prevent re-use before a new header is written if !old_addr.is_null() { unsafe { - self.deallocate(old_addr); + self.deallocate(ctx, old_addr); } } // Write new block @@ -559,14 +569,14 @@ impl<'a, D: Disk> Transaction<'a, D> { l0.data_mut().ptrs[i0] = block_ptr.cast(); l1.data_mut() .set_branch_full(i1, l0.data().tree_list_is_full()); - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; + l1.data_mut().ptrs[i1] = self.sync_block(&mut FsCtx, l0)?; l2.data_mut() .set_branch_full(i2, l1.data().tree_list_is_full()); - l2.data_mut().ptrs[i2] = self.sync_block(l1)?; + l2.data_mut().ptrs[i2] = self.sync_block(&mut FsCtx, l1)?; l3.data_mut() .set_branch_full(i3, l2.data().tree_list_is_full()); - l3.data_mut().ptrs[i3] = self.sync_block(l2)?; - self.header.tree = self.sync_block(l3)?; + l3.data_mut().ptrs[i3] = self.sync_block(&mut FsCtx, l2)?; + self.header.tree = self.sync_block(&mut FsCtx, l3)?; self.header_changed = true; return Ok(tree_ptr); @@ -603,37 +613,37 @@ impl<'a, D: Disk> Transaction<'a, D> { l0.data_mut().set_branch_full(i0, false); l0.data_mut().ptrs[i0] = BlockPtr::default(); let l0_ptr = if l0.data().tree_list_is_empty() { - unsafe { self.deallocate(l0.addr()) }; + unsafe { self.deallocate(&mut FsCtx, l0.addr()) }; BlockPtr::default() } else { - self.sync_block(l0)? + self.sync_block(&mut FsCtx, l0)? }; l1.data_mut().set_branch_full(i1, false); l1.data_mut().ptrs[i1] = l0_ptr; let l1_ptr = if l1.data().tree_list_is_empty() { - unsafe { self.deallocate(l1.addr()) }; + unsafe { self.deallocate(&mut FsCtx, l1.addr()) }; BlockPtr::default() } else { - self.sync_block(l1)? + self.sync_block(&mut FsCtx, l1)? }; l2.data_mut().set_branch_full(i2, false); l2.data_mut().ptrs[i2] = l1_ptr; let l2_ptr = if l2.data().tree_list_is_empty() { - unsafe { self.deallocate(l2.addr()) }; + unsafe { self.deallocate(&mut FsCtx, l2.addr()) }; BlockPtr::default() } else { - self.sync_block(l2)? + self.sync_block(&mut FsCtx, l2)? }; l3.data_mut().set_branch_full(i3, false); l3.data_mut().ptrs[i3] = l2_ptr; let l3_ptr = if l3.data().tree_list_is_empty() { - unsafe { self.deallocate(l3.addr()) }; + unsafe { self.deallocate(&mut FsCtx, l3.addr()) }; BlockPtr::default() } else { - self.sync_block(l3)? + self.sync_block(&mut FsCtx, l3)? }; self.header.tree = l3_ptr; @@ -669,11 +679,11 @@ impl<'a, D: Disk> Transaction<'a, D> { raw.data_mut().copy_from_slice(node.data()); // Write updates to newly allocated blocks - l0.data_mut().ptrs[i0] = self.sync_block(raw)?; - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; - l2.data_mut().ptrs[i2] = self.sync_block(l1)?; - l3.data_mut().ptrs[i3] = self.sync_block(l2)?; - self.header.tree = self.sync_block(l3)?; + l0.data_mut().ptrs[i0] = self.sync_block(&mut FsCtx, raw)?; + l1.data_mut().ptrs[i1] = self.sync_block(&mut FsCtx, l0)?; + l2.data_mut().ptrs[i2] = self.sync_block(&mut FsCtx, l1)?; + l3.data_mut().ptrs[i3] = self.sync_block(&mut FsCtx, l2)?; + self.header.tree = self.sync_block(&mut FsCtx, l3)?; self.header_changed = true; } @@ -834,7 +844,7 @@ impl<'a, D: Disk> Transaction<'a, D> { unsafe { let parent = self.read_tree(parent_ptr)?; let node_block_data = BlockData::new( - self.allocate(BlockMeta::default())?, + self.allocate(&mut FsCtx, BlockMeta::default())?, Node::new( mode, parent.data().uid(), @@ -880,7 +890,7 @@ impl<'a, D: Disk> Transaction<'a, D> { // Create the first DirList block let dir = BlockData::::empty(BlockAddr::default()).unwrap(); - let dir_ptr = self.sync_block(dir)?; + let dir_ptr = self.sync_block(&mut parent, dir)?; // Add the DirList directly to the parent directory parent.data_mut().level0[1] = unsafe { dir_ptr.cast() }; @@ -929,7 +939,7 @@ impl<'a, D: Disk> Transaction<'a, D> { let mut sibling = BlockData::>::empty(BlockAddr::default()).unwrap(); let _ = mem::replace(sibling.data_mut(), unallocated_sibling); - let sibling_block_ptr = self.sync_block(sibling)?; + let sibling_block_ptr = self.sync_block(&mut parent, sibling)?; let sibling_htree_ptr = HTreePtr::new(sibling_htree_hash, sibling_block_ptr); let sibling_record_ptr: HTreePtr = unsafe { sibling_htree_ptr.cast() }; @@ -938,7 +948,7 @@ impl<'a, D: Disk> Transaction<'a, D> { .data() .find_max_htree_hash() .ok_or(Error::new(EIO))?; - let root_block_ptr = self.sync_block(htree_root)?; + let root_block_ptr = self.sync_block(&mut parent, htree_root)?; let root_htree_ptr = HTreePtr::new(root_htree_hash, root_block_ptr); let root_record_ptr: HTreePtr = unsafe { root_htree_ptr.cast() }; @@ -948,7 +958,7 @@ impl<'a, D: Disk> Transaction<'a, D> { new_root.data_mut().ptrs[0] = sibling_record_ptr; let unexpected_sibling = htree::add_inner_node(new_root.data_mut(), root_record_ptr)?; assert!(unexpected_sibling.is_none()); - let new_root_ptr = self.sync_block(new_root)?; + let new_root_ptr = self.sync_block(&mut parent, new_root)?; // Add the parent node pointer, increase the level, and increase one block size per allocated block parent.data_mut().level0[0] = BlockPtr::marker(htree_levels as u8 + 1); @@ -957,7 +967,7 @@ impl<'a, D: Disk> Transaction<'a, D> { parent.data_mut().set_size(size); } else if htree_levels > 0 { // Update the parent node with the new root pointer - let root_block_ptr = self.sync_block(htree_root)?; + let root_block_ptr = self.sync_block(&mut parent, htree_root)?; parent.data_mut().level0[1] = unsafe { root_block_ptr.cast() }; } else { // Update the parent with the DirList block, ignoring the fake htree_root @@ -999,7 +1009,7 @@ impl<'a, D: Disk> Transaction<'a, D> { let mut dir = self.read_block(dir_ptr)?; let unallocated_sibling = htree::add_dir_entry(dir.data_mut(), &mut htree_ptr.htree_hash, dir_entry)?; - let dir_record_ptr = unsafe { self.sync_block(dir)?.cast() }; + let dir_record_ptr = unsafe { self.sync_block(parent_dir_node, dir)?.cast() }; parent_htree_node.ptrs[htree_ptr_idx] = HTreePtr::new(htree_ptr.htree_hash, dir_record_ptr); @@ -1007,7 +1017,7 @@ impl<'a, D: Disk> Transaction<'a, D> { // The DirList block was split, so we need to add it to the h-tree let mut dir = BlockData::::empty(BlockAddr::default()).unwrap(); let _ = mem::replace(dir.data_mut(), new_unallocated_dir); - let dir_ptr = self.sync_block(dir)?; + let dir_ptr = self.sync_block(parent_dir_node, dir)?; let dir_htree_ptr = HTreePtr::new(new_hash, dir_ptr); let dir_record_ptr: HTreePtr = unsafe { dir_htree_ptr.cast() }; let size = parent_dir_node.data().size() + record_byte_size; @@ -1032,7 +1042,7 @@ impl<'a, D: Disk> Transaction<'a, D> { // Write the muteated H-tree block back to disk and update the parent node's pointer let htree_hash = htree_block.data().find_max_htree_hash().unwrap(); - let htree_block_ptr = self.sync_block(htree_block)?; + let htree_block_ptr = self.sync_block(parent_dir_node, htree_block)?; let htree_record_ptr: BlockPtr = unsafe { htree_block_ptr.cast() }; parent_htree_node.ptrs[htree_ptr_idx] = HTreePtr::new(htree_hash, htree_record_ptr); @@ -1041,7 +1051,7 @@ impl<'a, D: Disk> Transaction<'a, D> { let mut sibling = BlockData::>::empty(BlockAddr::default()).unwrap(); let _ = mem::replace(sibling.data_mut(), new_unallocated_sibling); - let sibling_ptr = self.sync_block(sibling)?; + let sibling_ptr = self.sync_block(parent_dir_node, sibling)?; let sibling_htree_ptr = HTreePtr::new(new_hash, sibling_ptr); let sibling_record_ptr: HTreePtr = unsafe { sibling_htree_ptr.cast() }; let size = parent_dir_node.data().size() + record_byte_size; @@ -1145,7 +1155,7 @@ impl<'a, D: Disk> Transaction<'a, D> { // Dealocate the htree_root only if it was a real root node in the H-tree if htree_levels > 0 { unsafe { - self.deallocate(htree_root.addr()); + self.deallocate(&mut parent, htree_root.addr()); } let record_byte_size = parent.data().record_level().bytes(); let size = parent.data().size() - record_byte_size; @@ -1155,7 +1165,7 @@ impl<'a, D: Disk> Transaction<'a, D> { parent.data_mut().level0[1] = BlockPtr::default(); } else if htree_levels > 0 { // Update the real htree_root and update the ptr in the parent - let htree_root_block_ptr = self.sync_block(htree_root)?; + let htree_root_block_ptr = self.sync_block(&mut parent, htree_root)?; parent.data_mut().level0[1] = unsafe { htree_root_block_ptr.cast() }; } else { // The htree_root is fake, so update the parent with the ptr to the one and only directory list @@ -1167,7 +1177,7 @@ impl<'a, D: Disk> Transaction<'a, D> { self.sync_tree(parent)?; self.remove_tree(node.ptr())?; unsafe { - self.deallocate(node_addr); + self.deallocate(&mut FsCtx, node_addr); } Ok(Some(node_id)) @@ -1216,14 +1226,14 @@ impl<'a, D: Disk> Transaction<'a, D> { if let Some(new_tree_hash) = new_htree_hash { // The entry_ptr needs to be updated in the parent_htree_node - let dir_block_ptr = self.sync_block(dir_list)?; + let dir_block_ptr = self.sync_block(parent_dir_node, dir_list)?; let dir_record_ptr: BlockPtr = unsafe { dir_block_ptr.cast() }; parent_htree_node.ptrs[entry_idx] = HTreePtr::new(new_tree_hash, dir_record_ptr); } else { // The entry needs to be removed from the parent_htree_noce parent_htree_node.ptrs[entry_idx] = HTreePtr::default(); - unsafe { self.deallocate(dir_list.addr()) }; + unsafe { self.deallocate(parent_dir_node, dir_list.addr()) }; let size = parent_dir_node.data().size() - record_byte_size; parent_dir_node.data_mut().set_size(size); } @@ -1256,14 +1266,14 @@ impl<'a, D: Disk> Transaction<'a, D> { if let Some(new_htree_hash) = htree_node.data().find_max_htree_hash() { // The entry_ptr needs to be updated in the parent_htree_node - let htree_block_ptr = self.sync_block(htree_node)?; + let htree_block_ptr = self.sync_block(parent_dir_node, htree_node)?; let htree_record_ptr: BlockPtr = unsafe { htree_block_ptr.cast() }; parent_htree_node.ptrs[entry_idx] = HTreePtr::new(new_htree_hash, htree_record_ptr); } else { // The htree_node is now empty, so remove it parent_htree_node.ptrs[entry_idx] = HTreePtr::default(); - unsafe { self.deallocate(htree_node.addr()) }; + unsafe { self.deallocate(parent_dir_node, htree_node.addr()) }; let size = parent_dir_node.data().size() - record_byte_size; parent_dir_node.data_mut().set_size(size); } @@ -1374,54 +1384,54 @@ impl<'a, D: Disk> Transaction<'a, D> { match NodeLevel::new(record_offset).ok_or(Error::new(ERANGE))? { NodeLevel::L0(i0) => { let ptr = node.data_mut().level0[i0].clear(); - self.node_deallocate_block(node, ptr); + self.deallocate_block(node, ptr); } NodeLevel::L1(i1, i0) => { let mut l0 = self.read_block_or_empty(node.data().level1[i1])?; - self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear()); + self.deallocate_block(node, l0.data_mut().ptrs[i0].clear()); if l0.data().is_empty() { let ptr = node.data_mut().level1[i1].clear(); - self.node_deallocate_block(node, ptr); + self.deallocate_block(node, ptr); } else { - node.data_mut().level1[i1] = self.sync_block(l0)?; + node.data_mut().level1[i1] = self.sync_block(node, l0)?; } } NodeLevel::L2(i2, i1, i0) => { let mut l1 = self.read_block_or_empty(node.data().level2[i2])?; let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?; - self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear()); + self.deallocate_block(node, l0.data_mut().ptrs[i0].clear()); if l0.data().is_empty() { - self.node_deallocate_block(node, l1.data_mut().ptrs[i1].clear()); + self.deallocate_block(node, l1.data_mut().ptrs[i1].clear()); } else { - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; + l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?; } if l1.data().is_empty() { let ptr = node.data_mut().level2[i2].clear(); - self.node_deallocate_block(node, ptr); + self.deallocate_block(node, ptr); } else { - node.data_mut().level2[i2] = self.sync_block(l1)?; + node.data_mut().level2[i2] = self.sync_block(node, l1)?; } } NodeLevel::L3(i3, i2, i1, i0) => { let mut l2 = self.read_block_or_empty(node.data().level3[i3])?; let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?; let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?; - self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear()); + self.deallocate_block(node, l0.data_mut().ptrs[i0].clear()); if l0.data().is_empty() { - self.node_deallocate_block(node, l1.data_mut().ptrs[i1].clear()); + self.deallocate_block(node, l1.data_mut().ptrs[i1].clear()); } else { - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; + l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?; } if l1.data().is_empty() { - self.node_deallocate_block(node, l2.data_mut().ptrs[i2].clear()); + self.deallocate_block(node, l2.data_mut().ptrs[i2].clear()); } else { - l2.data_mut().ptrs[i2] = self.sync_block(l1)?; + l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?; } if l2.data().is_empty() { let ptr = node.data_mut().level3[i3].clear(); - self.node_deallocate_block(node, ptr); + self.deallocate_block(node, ptr); } else { - node.data_mut().level3[i3] = self.sync_block(l2)?; + node.data_mut().level3[i3] = self.sync_block(node, l2)?; } } NodeLevel::L4(i4, i3, i2, i1, i0) => { @@ -1429,27 +1439,27 @@ impl<'a, D: Disk> Transaction<'a, D> { let mut l2 = self.read_block_or_empty(l3.data().ptrs[i3])?; let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?; let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?; - self.node_deallocate_block(node, l0.data_mut().ptrs[i0].clear()); + self.deallocate_block(node, l0.data_mut().ptrs[i0].clear()); if l0.data().is_empty() { - self.node_deallocate_block(node, l1.data_mut().ptrs[i1].clear()); + self.deallocate_block(node, l1.data_mut().ptrs[i1].clear()); } else { - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; + l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?; } if l1.data().is_empty() { - self.node_deallocate_block(node, l2.data_mut().ptrs[i2].clear()); + self.deallocate_block(node, l2.data_mut().ptrs[i2].clear()); } else { - l2.data_mut().ptrs[i2] = self.sync_block(l1)?; + l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?; } if l2.data().is_empty() { - self.node_deallocate_block(node, l3.data_mut().ptrs[i3].clear()); + self.deallocate_block(node, l3.data_mut().ptrs[i3].clear()); } else { - l3.data_mut().ptrs[i3] = self.sync_block(l2)?; + l3.data_mut().ptrs[i3] = self.sync_block(node, l2)?; } if l3.data().is_empty() { let ptr = node.data_mut().level4[i4].clear(); - self.node_deallocate_block(node, ptr); + self.deallocate_block(node, ptr); } else { - node.data_mut().level4[i4] = self.sync_block(l3)?; + node.data_mut().level4[i4] = self.sync_block(node, l3)?; } } } @@ -1474,15 +1484,15 @@ impl<'a, D: Disk> Transaction<'a, D> { let mut l0 = self.read_block_or_empty(node.data().level1[i1])?; l0.data_mut().ptrs[i0] = ptr; - node.data_mut().level1[i1] = self.sync_block(l0)?; + node.data_mut().level1[i1] = self.sync_block(node, l0)?; } NodeLevel::L2(i2, i1, i0) => { let mut l1 = self.read_block_or_empty(node.data().level2[i2])?; let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?; l0.data_mut().ptrs[i0] = ptr; - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; - node.data_mut().level2[i2] = self.sync_block(l1)?; + l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?; + node.data_mut().level2[i2] = self.sync_block(node, l1)?; } NodeLevel::L3(i3, i2, i1, i0) => { let mut l2 = self.read_block_or_empty(node.data().level3[i3])?; @@ -1490,9 +1500,9 @@ impl<'a, D: Disk> Transaction<'a, D> { let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?; l0.data_mut().ptrs[i0] = ptr; - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; - l2.data_mut().ptrs[i2] = self.sync_block(l1)?; - node.data_mut().level3[i3] = self.sync_block(l2)?; + l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?; + l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?; + node.data_mut().level3[i3] = self.sync_block(node, l2)?; } NodeLevel::L4(i4, i3, i2, i1, i0) => { let mut l3 = self.read_block_or_empty(node.data().level4[i4])?; @@ -1501,10 +1511,10 @@ impl<'a, D: Disk> Transaction<'a, D> { let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?; l0.data_mut().ptrs[i0] = ptr; - l1.data_mut().ptrs[i1] = self.sync_block(l0)?; - l2.data_mut().ptrs[i2] = self.sync_block(l1)?; - l3.data_mut().ptrs[i3] = self.sync_block(l2)?; - node.data_mut().level4[i4] = self.sync_block(l3)?; + l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?; + l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?; + l3.data_mut().ptrs[i3] = self.sync_block(node, l2)?; + node.data_mut().level4[i4] = self.sync_block(node, l3)?; } } } @@ -1717,7 +1727,7 @@ impl<'a, D: Disk> Transaction<'a, D> { } // CoW record using its current level - let new_addr = unsafe { self.node_allocate(node, record.addr().meta())? }; + let new_addr = unsafe { self.allocate(node, record.addr().meta())? }; let mut old_addr = record.swap_addr(new_addr); // If the record was resized we need to dealloc the original ptr @@ -1734,8 +1744,10 @@ impl<'a, D: Disk> Transaction<'a, D> { node_changed = true; // Deallocate old record - unsafe { - self.node_deallocate(node, old_addr); + if !old_addr.is_null() { + unsafe { + self.deallocate(node, old_addr); + } } }