From a44303b4f6e3805d3dc20827cbee49f8649d74c5 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 15 Nov 2025 20:18:47 -0700 Subject: [PATCH] WIP: delete after close instead of unlink --- src/allocator.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++- src/header.rs | 11 +++++-- src/lib.rs | 2 +- src/transaction.rs | 2 +- 4 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/allocator.rs b/src/allocator.rs index 274ad1d965..66c622029e 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -2,10 +2,13 @@ use alloc::{collections::BTreeSet, vec::Vec}; use core::{fmt, mem, ops, slice}; use endian_num::Le; -use crate::{BlockAddr, BlockLevel, BlockMeta, BlockPtr, BlockTrait, BLOCK_SIZE}; +use crate::{BlockAddr, BlockLevel, BlockMeta, BlockPtr, BlockTrait, Node, TreePtr, BLOCK_SIZE}; pub const ALLOC_LIST_ENTRIES: usize = (BLOCK_SIZE as usize - mem::size_of::>()) / mem::size_of::(); +pub const RELEASE_LIST_ENTRIES: usize = (BLOCK_SIZE as usize + - mem::size_of::>()) + / mem::size_of::>(); /// The RedoxFS block allocator. This struct manages all "data" blocks in RedoxFS /// (i.e, all blocks that aren't reserved or part of the header chain). @@ -275,11 +278,79 @@ impl ops::DerefMut for AllocList { } } +/// A list of nodes pending release. +#[repr(C, packed)] +pub struct ReleaseList { + /// A pointer to the previous ReleaseList. + /// If this is the null pointer, this is the first element of the chain. + pub prev: BlockPtr, + + /// Allocation entries. + pub entries: [TreePtr; RELEASE_LIST_ENTRIES], +} + +unsafe impl BlockTrait for ReleaseList { + fn empty(level: BlockLevel) -> Option { + if level.0 == 0 { + Some(Self { + prev: BlockPtr::default(), + entries: [TreePtr::::default(); RELEASE_LIST_ENTRIES], + }) + } else { + None + } + } +} + +impl fmt::Debug for ReleaseList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let prev = self.prev; + let entries: Vec<_> = self + .entries + .iter() + .filter(|entry| !entry.is_null()) + .map(|entry| entry.id()) + .collect(); + f.debug_struct("ReleaseList") + .field("prev", &prev) + .field("entries", &entries) + .finish() + } +} + +impl ops::Deref for ReleaseList { + type Target = [u8]; + fn deref(&self) -> &[u8] { + unsafe { + slice::from_raw_parts( + self as *const ReleaseList as *const u8, + mem::size_of::(), + ) as &[u8] + } + } +} + +impl ops::DerefMut for ReleaseList { + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut( + self as *mut ReleaseList as *mut u8, + mem::size_of::(), + ) as &mut [u8] + } + } +} + #[test] fn alloc_node_size_test() { assert_eq!(mem::size_of::(), crate::BLOCK_SIZE as usize); } +#[test] +fn release_node_size_test() { + assert_eq!(mem::size_of::(), crate::BLOCK_SIZE as usize); +} + #[test] fn allocator_test() { let mut alloc = Allocator::default(); diff --git a/src/header.rs b/src/header.rs index 0fb01d0498..b47d6ff159 100644 --- a/src/header.rs +++ b/src/header.rs @@ -5,7 +5,7 @@ use endian_num::Le; use aes::Aes128; use xts_mode::{get_tweak_default, Xts128}; -use crate::{AllocList, BlockPtr, KeySlot, Tree, BLOCK_SIZE, SIGNATURE, VERSION}; +use crate::{AllocList, BlockPtr, KeySlot, ReleaseList, Tree, BLOCK_SIZE, SIGNATURE, VERSION}; pub const HEADER_RING: u64 = 256; @@ -29,8 +29,10 @@ pub struct Header { pub alloc: BlockPtr, /// Key slots pub key_slots: [KeySlot; 64], + /// Nodes pending release, may be null + pub release: BlockPtr, /// Padding - pub padding: [u8; BLOCK_SIZE as usize - 3176], + pub padding: [u8; BLOCK_SIZE as usize - 3192], /// encrypted hash of header data without hash, set to hash and padded if disk is not encrypted pub encrypted_hash: [u8; 16], /// hash of header data without hash @@ -159,7 +161,8 @@ impl Default for Header { tree: BlockPtr::::default(), alloc: BlockPtr::::default(), key_slots: [KeySlot::default(); 64], - padding: [0; BLOCK_SIZE as usize - 3176], + release: BlockPtr::::default(), + padding: [0; BLOCK_SIZE as usize - 3192], encrypted_hash: [0; 16], hash: 0.into(), } @@ -175,6 +178,7 @@ impl fmt::Debug for Header { let generation = self.generation; let tree = self.tree; let alloc = self.alloc; + let release = self.release; let hash = self.hash; f.debug_struct("Header") .field("signature", &signature) @@ -184,6 +188,7 @@ impl fmt::Debug for Header { .field("generation", &generation) .field("tree", &tree) .field("alloc", &alloc) + .field("release", &release) .field("hash", &hash) .finish() } diff --git a/src/lib.rs b/src/lib.rs index 89e30cefea..e85c5bbed7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ pub const DIR_ENTRY_MAX_LENGTH: usize = 252; pub static IS_UMT: AtomicUsize = AtomicUsize::new(0); -pub use self::allocator::{AllocEntry, AllocList, Allocator, ALLOC_LIST_ENTRIES}; +pub use self::allocator::{AllocEntry, AllocList, Allocator, ReleaseList, ALLOC_LIST_ENTRIES}; #[cfg(feature = "std")] pub use self::archive::{archive, archive_at}; pub use self::block::{ diff --git a/src/transaction.rs b/src/transaction.rs index 518f99b6c4..3f423ac140 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1151,7 +1151,6 @@ impl<'a, D: Disk> Transaction<'a, D> { false } else { node.data_mut().set_links(0); - self.truncate_node_inner(&mut node, 0)?; true }; @@ -1191,6 +1190,7 @@ impl<'a, D: Disk> Transaction<'a, D> { } if remove_node { + self.truncate_node_inner(&mut node, 0)?; self.sync_tree(parent)?; self.remove_tree(node.ptr())?; unsafe {