- outofbounds error
- caching btrees  with rc<node>s after reading from disk/file
This commit is contained in:
Janis 2023-04-03 00:11:44 +02:00
parent 9602b399e0
commit 03997b78e7
4 changed files with 124 additions and 61 deletions

View file

@ -1,5 +1,5 @@
//#![allow(dead_code)] #![allow(dead_code)]
#![feature(error_in_core)] #![feature(error_in_core, cell_update)]
#![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(not(any(feature = "std", test)), no_std)]
use core::{ use core::{
@ -22,7 +22,6 @@ use tree::Tree;
extern crate alloc; extern crate alloc;
pub mod crc32c; pub mod crc32c;
pub mod files;
pub mod path; pub mod path;
pub mod structs; pub mod structs;
pub mod tree; pub mod tree;
@ -30,7 +29,7 @@ pub mod v2;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub mod std_io { pub mod std_io {
use std::io::{Cursor, Read, Seek}; use std::io::{Read, Seek};
use crate::{Error, VolumeIo}; use crate::{Error, VolumeIo};

View file

@ -1,4 +1,4 @@
use alloc::{collections::VecDeque, vec::Vec}; use alloc::collections::VecDeque;
const SEPERATOR: u8 = b'/'; const SEPERATOR: u8 = b'/';

View file

@ -23,6 +23,11 @@ pub mod error {
NoDefaultSubvolFsRoot, NoDefaultSubvolFsRoot,
#[error("INode could not be found in FsTree")] #[error("INode could not be found in FsTree")]
INodeNotFound, INodeNotFound,
#[error("attempted to access item out of bounds")]
OutOfBounds {
range: core::ops::Range<usize>,
index: usize,
},
#[error("Invalid checksum: expected {expected:#?} but got {actual:#?}")] #[error("Invalid checksum: expected {expected:#?} but got {actual:#?}")]
InvalidChecksum { InvalidChecksum {
expected: [u8; 32], expected: [u8; 32],

View file

@ -1,3 +1,4 @@
use core::cell::Cell;
use core::fmt::Display; use core::fmt::Display;
use core::mem::size_of; use core::mem::size_of;
use core::ops::Deref; use core::ops::Deref;
@ -20,11 +21,98 @@ pub struct BTreeLeafNode {
pub items: Vec<Item>, pub items: Vec<Item>,
} }
/// An internal node in a btrfs tree, containing `KeyPtr`s to other internal nodes or leaf nodes.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum NodePtr {
Unvisited(KeyPtr),
Visited { key: KeyPtr, node: Rc<Node> }, // 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<Node>> {
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 struct BTreeInternalNode {
pub header: Header, pub header: Header,
pub children: Vec<KeyPtr>, #[derivative(Debug = "ignore")]
children: Vec<Cell<NodePtr>>,
}
impl BTreeInternalNode {
pub fn visit_child<R: super::Read>(
&self,
idx: usize,
volume: &super::volume::Volume<R>,
) -> Result<Rc<Node>> {
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<R: super::Read>(
&self,
child: &Cell<NodePtr>,
volume: &super::volume::Volume<R>,
) -> Result<Rc<Node>> {
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<Item = (usize, Key)> + 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<R>,
) -> impl Iterator<Item = (usize, Result<Rc<Node>>)> + 'a
where
'b: 'a,
{
self.children
.iter()
.enumerate()
.map(|(i, child)| (i, self.visit_child_inner(child, volume)))
}
} }
impl PartialEq for BTreeInternalNode { impl PartialEq for BTreeInternalNode {
@ -42,7 +130,7 @@ impl PartialEq for BTreeLeafNode {
impl Eq for BTreeLeafNode {} impl Eq for BTreeLeafNode {}
impl Eq for BTreeInternalNode {} impl Eq for BTreeInternalNode {}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum BTreeNode { pub enum BTreeNode {
Internal(BTreeInternalNode), Internal(BTreeInternalNode),
Leaf(BTreeLeafNode), Leaf(BTreeLeafNode),
@ -126,10 +214,9 @@ impl<R: super::Read> Tree<R> {
} }
SearchResult::Edge(mut edge) => match &edge.node.inner { SearchResult::Edge(mut edge) => match &edge.node.inner {
BTreeNode::Internal(internal) => { BTreeNode::Internal(internal) => {
let child_ptr = internal.children.get(edge.idx as usize).expect("adsf"); let child = internal
.visit_child(edge.idx as usize, &self.volume)
let bytes = self.volume.read_keyptr(child_ptr)?; .expect("child");
let child = Rc::new(Node::from_bytes(bytes)?);
edge.parents.push((edge.node, edge.idx)); edge.parents.push((edge.node, edge.idx));
node = NodeHandle { node = NodeHandle {
parents: edge.parents, parents: edge.parents,
@ -250,6 +337,7 @@ impl BTreeInternalNode {
} }
}) })
.take(header.nritems.get() as usize) .take(header.nritems.get() as usize)
.map(|ptr| Cell::new(NodePtr::Unvisited(ptr)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Ok(Self { header, children }) Ok(Self { header, children })
@ -589,21 +677,18 @@ impl NodeHandle {
pub fn find_key_rev<K: PartialEq<Key> + PartialOrd<Key>>(self, key: &K) -> SearchResult { pub fn find_key_rev<K: PartialEq<Key> + PartialOrd<Key>>(self, key: &K) -> SearchResult {
match &self.node.inner { match &self.node.inner {
BTreeNode::Internal(node) => { BTreeNode::Internal(node) => {
for (i, child) in node.children.iter().enumerate().rev() { let idx = node
match key.partial_cmp(&child.key) { .visit_children_keys()
.rev()
.find_map(|(i, child)| match key.partial_cmp(&child) {
Some(core::cmp::Ordering::Greater) Some(core::cmp::Ordering::Greater)
| Some(core::cmp::Ordering::Equal) | Some(core::cmp::Ordering::Equal)
| None => { | None => Some(i as u32),
return SearchResult::Edge(NodeHandle { _ => None,
idx: i as u32, })
..self .unwrap_or(0);
});
}
_ => {}
}
}
SearchResult::Edge(NodeHandle { idx: 0, ..self }) SearchResult::Edge(NodeHandle { idx, ..self })
} }
BTreeNode::Leaf(node) => { BTreeNode::Leaf(node) => {
for (i, child) in node.items.iter().enumerate().rev() { for (i, child) in node.items.iter().enumerate().rev() {
@ -624,28 +709,18 @@ impl NodeHandle {
pub fn find_key<K: PartialEq<Key> + PartialOrd<Key>>(self, key: &K) -> SearchResult { pub fn find_key<K: PartialEq<Key> + PartialOrd<Key>>(self, key: &K) -> SearchResult {
match &self.node.inner { match &self.node.inner {
BTreeNode::Internal(node) => { BTreeNode::Internal(node) => {
for (i, child) in node.children.iter().enumerate() { let idx = node
match key.partial_cmp(&child.key) { .visit_children_keys()
.find_map(|(i, child)| match key.partial_cmp(&child) {
Some(core::cmp::Ordering::Less) => { Some(core::cmp::Ordering::Less) => {
return SearchResult::Edge(Self { Some(if i == 0 { 0 } else { i as u32 - 1 })
idx: if i == 0 { 0 } else { i as u32 - 1 },
..self
});
} }
Some(core::cmp::Ordering::Equal) | None => { Some(core::cmp::Ordering::Equal) | None => Some(i as u32),
return SearchResult::Edge(NodeHandle { _ => None,
idx: i as u32,
..self
});
}
_ => {}
}
}
SearchResult::Edge(NodeHandle {
idx: node.children.len() as u32 - 1,
..self
}) })
.unwrap_or(node.children.len() as u32 - 1);
SearchResult::Edge(NodeHandle { idx, ..self })
} }
BTreeNode::Leaf(node) => { BTreeNode::Leaf(node) => {
for (i, child) in node.items.iter().enumerate() { for (i, child) in node.items.iter().enumerate() {
@ -694,15 +769,7 @@ impl NodeHandle {
} else { } else {
match &node.inner { match &node.inner {
BTreeNode::Internal(internal) => { BTreeNode::Internal(internal) => {
let child_ptr = *internal let node = match internal.visit_child(idx as usize, volume) {
.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))
{
Ok(child) => { Ok(child) => {
parents.push((node, idx)); parents.push((node, idx));
Ok(Self { Ok(Self {
@ -757,15 +824,7 @@ impl NodeHandle {
} else { } else {
match &node.inner { match &node.inner {
BTreeNode::Internal(internal) => { BTreeNode::Internal(internal) => {
let child_ptr = *internal let node = match internal.visit_child(idx as usize, volume) {
.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))
{
Ok(child) => { Ok(child) => {
parents.push((node, idx)); parents.push((node, idx));
Ok(Self { Ok(Self {