From 03997b78e76b27eebc5be28a5e00ea44a662b74d Mon Sep 17 00:00:00 2001 From: Janis Date: Mon, 3 Apr 2023 00:11:44 +0200 Subject: [PATCH] stuff - outofbounds error - caching btrees with rcs after reading from disk/file --- btrfs/src/lib.rs | 7 +- btrfs/src/path.rs | 2 +- btrfs/src/v2/mod.rs | 5 ++ btrfs/src/v2/tree.rs | 171 +++++++++++++++++++++++++++++-------------- 4 files changed, 124 insertions(+), 61 deletions(-) diff --git a/btrfs/src/lib.rs b/btrfs/src/lib.rs index 11a4abf..9b794db 100644 --- a/btrfs/src/lib.rs +++ b/btrfs/src/lib.rs @@ -1,5 +1,5 @@ -//#![allow(dead_code)] -#![feature(error_in_core)] +#![allow(dead_code)] +#![feature(error_in_core, cell_update)] #![cfg_attr(not(any(feature = "std", test)), no_std)] use core::{ @@ -22,7 +22,6 @@ use tree::Tree; extern crate alloc; pub mod crc32c; -pub mod files; pub mod path; pub mod structs; pub mod tree; @@ -30,7 +29,7 @@ pub mod v2; #[cfg(feature = "std")] pub mod std_io { - use std::io::{Cursor, Read, Seek}; + use std::io::{Read, Seek}; use crate::{Error, VolumeIo}; diff --git a/btrfs/src/path.rs b/btrfs/src/path.rs index 7515e5f..39d582d 100644 --- a/btrfs/src/path.rs +++ b/btrfs/src/path.rs @@ -1,4 +1,4 @@ -use alloc::{collections::VecDeque, vec::Vec}; +use alloc::collections::VecDeque; const SEPERATOR: u8 = b'/'; diff --git a/btrfs/src/v2/mod.rs b/btrfs/src/v2/mod.rs index 5623d8f..417b8e9 100644 --- a/btrfs/src/v2/mod.rs +++ b/btrfs/src/v2/mod.rs @@ -23,6 +23,11 @@ pub mod error { NoDefaultSubvolFsRoot, #[error("INode could not be found in FsTree")] INodeNotFound, + #[error("attempted to access item out of bounds")] + OutOfBounds { + range: core::ops::Range, + index: usize, + }, #[error("Invalid checksum: expected {expected:#?} but got {actual:#?}")] InvalidChecksum { expected: [u8; 32], diff --git a/btrfs/src/v2/tree.rs b/btrfs/src/v2/tree.rs index f2f25bc..d901a88 100644 --- a/btrfs/src/v2/tree.rs +++ b/btrfs/src/v2/tree.rs @@ -1,3 +1,4 @@ +use core::cell::Cell; use core::fmt::Display; use core::mem::size_of; use core::ops::Deref; @@ -20,11 +21,98 @@ pub struct BTreeLeafNode { pub items: Vec, } -/// An internal node in a btrfs tree, containing `KeyPtr`s to other internal nodes or leaf nodes. #[derive(Debug, Clone)] +pub enum NodePtr { + Unvisited(KeyPtr), + Visited { key: KeyPtr, node: Rc }, // TODO: this doesnt need to be an Rc, can just be a NonNull with manual memory management +} + +impl NodePtr { + pub fn key_ptr(&self) -> &KeyPtr { + match self { + NodePtr::Unvisited(key) => key, + NodePtr::Visited { key, node } => key, + } + } + + pub fn node(&self) -> Option<&Rc> { + match self { + NodePtr::Unvisited(_) => None, + NodePtr::Visited { node, .. } => Some(&node), + } + } + + pub fn key(&self) -> &Key { + &self.key_ptr().key + } +} + +/// An internal node in a btrfs tree, containing `KeyPtr`s to other internal nodes or leaf nodes. +#[derive(Derivative)] +#[derivative(Debug)] pub struct BTreeInternalNode { pub header: Header, - pub children: Vec, + #[derivative(Debug = "ignore")] + children: Vec>, +} + +impl BTreeInternalNode { + pub fn visit_child( + &self, + idx: usize, + volume: &super::volume::Volume, + ) -> Result> { + match self.children.get(idx) { + Some(child) => self.visit_child_inner(child, volume), + None => Err(Error::OutOfBounds { + range: 0..self.children.len(), + index: idx, + }), + } + } + + fn visit_child_inner( + &self, + child: &Cell, + volume: &super::volume::Volume, + ) -> Result> { + match unsafe { &*child.as_ptr() } { + NodePtr::Unvisited(keyptr) => { + let node = volume + .read_keyptr(keyptr) + .and_then(|bytes| Node::from_bytes(bytes)) + .map(|node| Rc::new(node))?; + child.set(NodePtr::Visited { + key: *keyptr, + node: node.clone(), + }); + Ok(node) + } + NodePtr::Visited { node, .. } => Ok(node.clone()), + } + } + + pub fn visit_children_keys( + &self, + ) -> impl Iterator + DoubleEndedIterator + '_ { + self.children + .iter() + .enumerate() + .map(|(i, child)| (i, unsafe { *(&*child.as_ptr()).key() })) + } + + pub fn visit_children<'a, 'b, R: super::Read>( + &'a self, + volume: &'b super::volume::Volume, + ) -> impl Iterator>)> + 'a + where + 'b: 'a, + { + self.children + .iter() + .enumerate() + .map(|(i, child)| (i, self.visit_child_inner(child, volume))) + } } impl PartialEq for BTreeInternalNode { @@ -42,7 +130,7 @@ impl PartialEq for BTreeLeafNode { impl Eq for BTreeLeafNode {} impl Eq for BTreeInternalNode {} -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub enum BTreeNode { Internal(BTreeInternalNode), Leaf(BTreeLeafNode), @@ -126,10 +214,9 @@ impl Tree { } SearchResult::Edge(mut edge) => match &edge.node.inner { BTreeNode::Internal(internal) => { - let child_ptr = internal.children.get(edge.idx as usize).expect("adsf"); - - let bytes = self.volume.read_keyptr(child_ptr)?; - let child = Rc::new(Node::from_bytes(bytes)?); + let child = internal + .visit_child(edge.idx as usize, &self.volume) + .expect("child"); edge.parents.push((edge.node, edge.idx)); node = NodeHandle { parents: edge.parents, @@ -250,6 +337,7 @@ impl BTreeInternalNode { } }) .take(header.nritems.get() as usize) + .map(|ptr| Cell::new(NodePtr::Unvisited(ptr))) .collect::>(); Ok(Self { header, children }) @@ -589,21 +677,18 @@ impl NodeHandle { pub fn find_key_rev + PartialOrd>(self, key: &K) -> SearchResult { match &self.node.inner { BTreeNode::Internal(node) => { - for (i, child) in node.children.iter().enumerate().rev() { - match key.partial_cmp(&child.key) { + let idx = node + .visit_children_keys() + .rev() + .find_map(|(i, child)| match key.partial_cmp(&child) { Some(core::cmp::Ordering::Greater) | Some(core::cmp::Ordering::Equal) - | None => { - return SearchResult::Edge(NodeHandle { - idx: i as u32, - ..self - }); - } - _ => {} - } - } + | None => Some(i as u32), + _ => None, + }) + .unwrap_or(0); - SearchResult::Edge(NodeHandle { idx: 0, ..self }) + SearchResult::Edge(NodeHandle { idx, ..self }) } BTreeNode::Leaf(node) => { for (i, child) in node.items.iter().enumerate().rev() { @@ -624,28 +709,18 @@ impl NodeHandle { pub fn find_key + PartialOrd>(self, key: &K) -> SearchResult { match &self.node.inner { BTreeNode::Internal(node) => { - for (i, child) in node.children.iter().enumerate() { - match key.partial_cmp(&child.key) { + let idx = node + .visit_children_keys() + .find_map(|(i, child)| match key.partial_cmp(&child) { Some(core::cmp::Ordering::Less) => { - return SearchResult::Edge(Self { - idx: if i == 0 { 0 } else { i as u32 - 1 }, - ..self - }); + Some(if i == 0 { 0 } else { i as u32 - 1 }) } - Some(core::cmp::Ordering::Equal) | None => { - return SearchResult::Edge(NodeHandle { - idx: i as u32, - ..self - }); - } - _ => {} - } - } + Some(core::cmp::Ordering::Equal) | None => Some(i as u32), + _ => None, + }) + .unwrap_or(node.children.len() as u32 - 1); - SearchResult::Edge(NodeHandle { - idx: node.children.len() as u32 - 1, - ..self - }) + SearchResult::Edge(NodeHandle { idx, ..self }) } BTreeNode::Leaf(node) => { for (i, child) in node.items.iter().enumerate() { @@ -694,15 +769,7 @@ impl NodeHandle { } else { match &node.inner { BTreeNode::Internal(internal) => { - let child_ptr = *internal - .children - .get(idx as usize) - .expect("no children in node"); - let node = match volume - .read_keyptr(&child_ptr) - .and_then(|bytes| Node::from_bytes(bytes)) - .map(|node| Rc::new(node)) - { + let node = match internal.visit_child(idx as usize, volume) { Ok(child) => { parents.push((node, idx)); Ok(Self { @@ -757,15 +824,7 @@ impl NodeHandle { } else { match &node.inner { BTreeNode::Internal(internal) => { - let child_ptr = *internal - .children - .get(idx as usize) - .expect("no children in node"); - let node = match volume - .read_keyptr(&child_ptr) - .and_then(|bytes| Node::from_bytes(bytes)) - .map(|node| Rc::new(node)) - { + let node = match internal.visit_child(idx as usize, volume) { Ok(child) => { parents.push((node, idx)); Ok(Self {