diff --git a/src/lib.rs b/src/lib.rs index 375a738b3e..89e30cefea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ pub use self::header::{Header, HEADER_RING}; pub use self::key::{Key, KeySlot, Salt}; #[cfg(feature = "std")] pub use self::mount::mount; -pub use self::node::{Node, NodeLevel, NodeLevelData}; +pub use self::node::{Node, NodeFlags, NodeLevel, NodeLevelData}; pub use self::record::RecordRaw; pub use self::transaction::Transaction; pub use self::tree::{Tree, TreeData, TreeList, TreePtr}; diff --git a/src/node.rs b/src/node.rs index 0786ce432a..13d35b9c58 100644 --- a/src/node.rs +++ b/src/node.rs @@ -181,8 +181,8 @@ pub struct Node { /// Padding pub padding: [u8; BLOCK_SIZE as usize - 4042], - /// Level data, kept private so inline data can be supported - level_data: NodeLevelData, + /// Level data, should not be used directly so inline data can be supported + pub(crate) level_data: NodeLevelData, } unsafe impl BlockTrait for Node { @@ -253,6 +253,14 @@ impl Node { 0 } .into(), + flags: if mode & Self::MODE_TYPE == Self::MODE_DIR { + // Directories must not use inline data (until h-tree supports it) + NodeFlags::empty() + } else { + NodeFlags::INLINE_DATA + } + .bits() + .into(), ..Default::default() } } diff --git a/src/transaction.rs b/src/transaction.rs index d42031ea75..24dc659ed6 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -15,17 +15,25 @@ use syscall::error::{ use crate::{ htree::{self, HTreeHash, HTreeNode, HTreePtr}, AllocEntry, AllocList, Allocator, BlockAddr, BlockData, BlockLevel, BlockMeta, BlockPtr, - BlockTrait, DirEntry, DirList, Disk, FileSystem, Header, Node, NodeLevel, NodeLevelData, - RecordRaw, TreeData, TreePtr, ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES, DIR_ENTRY_MAX_LENGTH, - HEADER_RING, + BlockTrait, DirEntry, DirList, Disk, FileSystem, Header, Node, NodeFlags, NodeLevel, + NodeLevelData, RecordRaw, TreeData, TreePtr, ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES, + DIR_ENTRY_MAX_LENGTH, HEADER_RING, }; pub(crate) fn level_data(node: &TreeData) -> Result<&NodeLevelData> { - node.data().level_data().ok_or(Error::new(EIO)) + node.data().level_data().ok_or_else(|| { + #[cfg(feature = "log")] + log::error!("LEVEL_DATA: NODE HAS INLINE DATA"); + Error::new(EIO) + }) } pub(crate) fn level_data_mut(node: &mut TreeData) -> Result<&mut NodeLevelData> { - node.data_mut().level_data_mut().ok_or(Error::new(EIO)) + node.data_mut().level_data_mut().ok_or_else(|| { + #[cfg(feature = "log")] + log::error!("LEVEL_DATA_MUT: NODE HAS INLINE DATA"); + Error::new(EIO) + }) } pub trait AllocCtx { @@ -1538,6 +1546,20 @@ impl<'a, D: Disk> Transaction<'a, D> { buf: &mut [u8], ) -> Result { let node_size = node.data().size(); + + // Try reading from inline data + if let Some(inline_data) = node.data().inline_data() { + if offset >= node_size { + return Ok(0); + } + + let end = min(node_size, offset + (buf.len() as u64)); + let len = end - offset; + buf[..len as usize].copy_from_slice(&inline_data[offset as usize..end as usize]); + offset += len; + return Ok(len as usize); + } + let record_level = node.data().record_level(); let mut bytes_read = 0; @@ -1631,7 +1653,7 @@ impl<'a, D: Disk> Transaction<'a, D> { self.write_node_inner(node, &mut offset, &zeroes[start as usize..end as usize])?; } assert_eq!(offset, size); - } else { + } else if !node.data().has_inline_data() { // Deallocate records for record in (size.div_ceil(record_level.bytes())..old_size / record_level.bytes()).rev() @@ -1670,7 +1692,7 @@ impl<'a, D: Disk> Transaction<'a, D> { Ok(()) } - pub fn write_node_inner( + fn write_node_inner_records( &mut self, node: &mut TreeData, offset: &mut u64, @@ -1772,6 +1794,49 @@ impl<'a, D: Disk> Transaction<'a, D> { Ok(node_changed) } + pub fn write_node_inner( + &mut self, + node: &mut TreeData, + offset: &mut u64, + buf: &[u8], + ) -> Result { + let mut node_changed = false; + + // Try writing to inline data + let node_size = node.data().size(); + let convert_inline = if let Some(inline_data) = node.data_mut().inline_data_mut() { + let end = *offset + (buf.len() as u64); + if end < inline_data.len() as u64 { + inline_data[*offset as usize..end as usize].copy_from_slice(buf); + *offset += buf.len() as u64; + if node.data().size() < *offset { + node.data_mut().set_size(*offset); + } + return Ok(true); + } else { + Some(inline_data[..node_size as usize].to_vec()) + } + } else { + None + }; + + if let Some(inline_data) = convert_inline { + // If inline data cannot fit, convert to records + let mut flags = node.data().flags(); + flags.remove(NodeFlags::INLINE_DATA); + node.data_mut().set_flags(flags); + node.data_mut().level_data = NodeLevelData::default(); + self.write_node_inner_records(node, &mut 0, &inline_data)?; + node_changed = true; + } + + if self.write_node_inner_records(node, offset, buf)? { + node_changed = true; + } + + Ok(node_changed) + } + /// Write the bytes at `buf` to `node` starting at `offset`. pub fn write_node( &mut self,