integration tests, made more things publicly accessible
- switching from Cell to RefCell for NodePtr - deep cloneing and destroying of trees - BoxedNode is a NonNull now instead of an Rc - implemented Read for `&[u8]`
This commit is contained in:
parent
e1f59b1b46
commit
3aa8ecbd77
|
@ -51,6 +51,16 @@ pub trait Read {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Read for &[u8] {
|
||||||
|
fn read(&self, dst: &mut [u8], address: u64) -> error::Result<()> {
|
||||||
|
let src = self
|
||||||
|
.get(address as usize..address as usize + dst.len())
|
||||||
|
.ok_or(error::Error::ReadFailed)?;
|
||||||
|
dst.copy_from_slice(src);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(any(feature = "std", test), unix))]
|
#[cfg(all(any(feature = "std", test), unix))]
|
||||||
impl Read for std::fs::File {
|
impl Read for std::fs::File {
|
||||||
fn read(&self, dst: &mut [u8], address: u64) -> error::Result<()> {
|
fn read(&self, dst: &mut [u8], address: u64) -> error::Result<()> {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use core::cell::Cell;
|
use alloc::boxed::Box;
|
||||||
|
use core::cell::RefCell;
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
use crate::structs::{Header, Item, Key, KeyPtr, KnownObjectId, ObjectType, TreeItem};
|
use crate::structs::{Header, Item, Key, KeyPtr, KnownObjectId, ObjectType, TreeItem};
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
|
@ -22,12 +24,24 @@ pub struct BTreeLeafNode {
|
||||||
pub items: Vec<Item>,
|
pub items: Vec<Item>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum NodePtr {
|
pub enum NodePtr {
|
||||||
Unvisited(KeyPtr),
|
Unvisited(KeyPtr),
|
||||||
Visited { key: KeyPtr, node: BoxedNode }, // TODO: this doesnt need to be an Rc, can just be a NonNull with manual memory management
|
Visited { key: KeyPtr, node: BoxedNode }, // TODO: this doesnt need to be an Rc, can just be a NonNull with manual memory management
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for NodePtr {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Unvisited(arg0) => Self::Unvisited(arg0.clone()),
|
||||||
|
Self::Visited { key, node } => Self::Visited {
|
||||||
|
key: key.clone(),
|
||||||
|
node: Node::clone_from_nonnull(node),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NodePtr {
|
impl NodePtr {
|
||||||
pub fn key_ptr(&self) -> &KeyPtr {
|
pub fn key_ptr(&self) -> &KeyPtr {
|
||||||
match self {
|
match self {
|
||||||
|
@ -46,15 +60,25 @@ impl NodePtr {
|
||||||
pub fn key(&self) -> &Key {
|
pub fn key(&self) -> &Key {
|
||||||
&self.key_ptr().key
|
&self.key_ptr().key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn destroy(self) {
|
||||||
|
match self {
|
||||||
|
Self::Visited { node, .. } => {
|
||||||
|
_ = node;
|
||||||
|
// TODO: from box drop
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An internal node in a btrfs tree, containing `KeyPtr`s to other internal nodes or leaf nodes.
|
/// An internal node in a btrfs tree, containing `KeyPtr`s to other internal nodes or leaf nodes.
|
||||||
#[derive(Derivative)]
|
#[derive(Derivative, Clone)]
|
||||||
#[derivative(Debug)]
|
#[derivative(Debug)]
|
||||||
pub struct BTreeInternalNode {
|
pub struct BTreeInternalNode {
|
||||||
pub header: Header,
|
pub header: Header,
|
||||||
#[derivative(Debug = "ignore")]
|
#[derivative(Debug = "ignore")]
|
||||||
children: Vec<Cell<NodePtr>>,
|
children: Vec<RefCell<NodePtr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BTreeInternalNode {
|
impl BTreeInternalNode {
|
||||||
|
@ -74,16 +98,16 @@ impl BTreeInternalNode {
|
||||||
|
|
||||||
fn visit_child_inner<R: super::Read>(
|
fn visit_child_inner<R: super::Read>(
|
||||||
&self,
|
&self,
|
||||||
child: &Cell<NodePtr>,
|
child: &RefCell<NodePtr>,
|
||||||
volume: &super::volume::Volume<R>,
|
volume: &super::volume::Volume<R>,
|
||||||
) -> Result<BoxedNode> {
|
) -> Result<BoxedNode> {
|
||||||
match unsafe { &*child.as_ptr() } {
|
match unsafe { &*child.as_ptr() } {
|
||||||
NodePtr::Unvisited(keyptr) => {
|
NodePtr::Unvisited(keyptr) => {
|
||||||
let node = volume
|
let node = volume
|
||||||
.read_keyptr(keyptr)
|
.read_keyptr(keyptr)
|
||||||
.and_then(|bytes| Node::from_bytes(bytes))
|
.and_then(|bytes| Node::boxed_from_bytes(bytes))?;
|
||||||
.map(|node| Rc::new(node))?;
|
|
||||||
child.set(NodePtr::Visited {
|
child.replace(NodePtr::Visited {
|
||||||
key: *keyptr,
|
key: *keyptr,
|
||||||
node: node.clone(),
|
node: node.clone(),
|
||||||
});
|
});
|
||||||
|
@ -131,13 +155,13 @@ impl PartialEq for BTreeLeafNode {
|
||||||
impl Eq for BTreeLeafNode {}
|
impl Eq for BTreeLeafNode {}
|
||||||
impl Eq for BTreeInternalNode {}
|
impl Eq for BTreeInternalNode {}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum BTreeNode {
|
pub enum BTreeNode {
|
||||||
Internal(BTreeInternalNode),
|
Internal(BTreeInternalNode),
|
||||||
Leaf(BTreeLeafNode),
|
Leaf(BTreeLeafNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Eq)]
|
#[derive(Derivative, Eq, Clone)]
|
||||||
#[derivative(Debug, PartialEq)]
|
#[derivative(Debug, PartialEq)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
inner: BTreeNode,
|
inner: BTreeNode,
|
||||||
|
@ -146,7 +170,41 @@ pub struct Node {
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoxedNode = Rc<Node>;
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct BoxedNode(NonNull<Node>);
|
||||||
|
|
||||||
|
impl Deref for BoxedNode {
|
||||||
|
type Target = Node;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NonNull<Node>> for BoxedNode {
|
||||||
|
fn from(value: NonNull<Node>) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Node> for BoxedNode {
|
||||||
|
fn from(value: Node) -> Self {
|
||||||
|
Self(unsafe { NonNull::new_unchecked(Box::leak(Box::new(value))) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxedNode {
|
||||||
|
pub fn as_ref(&self) -> &Node {
|
||||||
|
unsafe { self.0.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ptr(self) -> *mut Node {
|
||||||
|
self.0.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//type BoxedNode = NonNull<Node>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct NodeHandle {
|
pub struct NodeHandle {
|
||||||
|
@ -251,17 +309,26 @@ impl<R: super::Read> Clone for Tree<R> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
volume: self.volume.clone(),
|
volume: self.volume.clone(),
|
||||||
root: self.root.clone(),
|
root: Node::clone_from_nonnull(&self.root),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R: super::Read> Drop for Tree<R> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
log::debug!("======= cleaning up tree =======");
|
||||||
|
Node::destroy(self.root.clone());
|
||||||
|
log::debug!("======= [done] =======");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<R: super::Read> Tree<R> {
|
impl<R: super::Read> Tree<R> {
|
||||||
pub fn from_logical_offset(volume: Rc<Volume<R>>, logical: u64) -> Result<Self> {
|
pub fn from_logical_offset(volume: Rc<Volume<R>>, logical: u64) -> Result<Self> {
|
||||||
|
// TODO: this might read a very big range, far more than needed
|
||||||
let bytes = volume
|
let bytes = volume
|
||||||
.read_range_from_logical(logical)?
|
.read_range_from_logical(logical)?
|
||||||
.ok_or(Error::BadLogicalAddress)?; // TODO: make this a better error
|
.ok_or(Error::BadLogicalAddress)?; // TODO: make this a better error
|
||||||
let root = Rc::new(Node::from_bytes(bytes)?);
|
let root = Node::boxed_from_bytes(bytes)?;
|
||||||
|
|
||||||
Ok(Self { volume, root })
|
Ok(Self { volume, root })
|
||||||
}
|
}
|
||||||
|
@ -301,14 +368,14 @@ impl<R: super::Read> Tree<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_node_rev<K>(&self, key: &K) -> Result<Option<NodeHandle>>
|
fn find_node_rev<K>(&self, key: &K) -> Result<Option<NodeHandle>>
|
||||||
where
|
where
|
||||||
K: PartialEq<Key> + PartialOrd<Key>,
|
K: PartialEq<Key> + PartialOrd<Key>,
|
||||||
{
|
{
|
||||||
self.find_node_inner(key, NodeHandle::find_key_rev)
|
self.find_node_inner(key, NodeHandle::find_key_rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_node<K>(&self, key: &K) -> Result<Option<NodeHandle>>
|
fn find_node<K>(&self, key: &K) -> Result<Option<NodeHandle>>
|
||||||
where
|
where
|
||||||
K: PartialEq<Key> + PartialOrd<Key>,
|
K: PartialEq<Key> + PartialOrd<Key>,
|
||||||
{
|
{
|
||||||
|
@ -408,7 +475,7 @@ impl BTreeInternalNode {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.take(header.nritems.get() as usize)
|
.take(header.nritems.get() as usize)
|
||||||
.map(|ptr| Cell::new(NodePtr::Unvisited(ptr)))
|
.map(|ptr| RefCell::new(NodePtr::Unvisited(ptr)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(Self { header, children })
|
Ok(Self { header, children })
|
||||||
|
@ -619,12 +686,48 @@ impl PartialEq for RootOrEdge {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
|
pub fn clone_from_nonnull(this: &BoxedNode) -> BoxedNode {
|
||||||
|
(*this.as_ref()).clone().into()
|
||||||
|
}
|
||||||
|
pub fn boxed_from_bytes(bytes: Vec<u8>) -> Result<BoxedNode> {
|
||||||
|
Ok(Self::from_bytes(bytes)?.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {
|
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self> {
|
||||||
let inner = BTreeNode::parse(&bytes)?;
|
let inner = BTreeNode::parse(&bytes)?;
|
||||||
|
|
||||||
Ok(Self { inner, bytes })
|
Ok(Self { inner, bytes })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// must not be called if any outstanding live references to `this` exist
|
||||||
|
pub fn destroy(this: BoxedNode) {
|
||||||
|
log::debug!("Node::destroy");
|
||||||
|
{
|
||||||
|
log::debug!("free: {:?}", this);
|
||||||
|
let inner = &this.as_ref().inner;
|
||||||
|
match inner {
|
||||||
|
BTreeNode::Internal(node) => {
|
||||||
|
log::debug!("destroying children..");
|
||||||
|
for child in node.children.iter() {
|
||||||
|
match unsafe { &*child.as_ptr() } {
|
||||||
|
NodePtr::Visited { node, .. } => {
|
||||||
|
Self::destroy(node.clone());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::debug!("destroying children [end]");
|
||||||
|
}
|
||||||
|
BTreeNode::Leaf(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("dropping: {:?}", this);
|
||||||
|
unsafe {
|
||||||
|
drop(Box::from_raw(this.as_ptr()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// returns Ok(None) if `i` is out of bounds
|
/// returns Ok(None) if `i` is out of bounds
|
||||||
fn read_nth_key(&self, i: usize) -> Option<Key> {
|
fn read_nth_key(&self, i: usize) -> Option<Key> {
|
||||||
match &self.inner {
|
match &self.inner {
|
||||||
|
|
|
@ -82,8 +82,8 @@ pub struct Volume<R: super::Read> {
|
||||||
// TODO: find better name
|
// TODO: find better name
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Volume2<R: super::Read> {
|
pub struct Volume2<R: super::Read> {
|
||||||
inner: Rc<Volume<R>>,
|
pub inner: Rc<Volume<R>>,
|
||||||
roots: BTreeMap<KnownObjectId, (RootItem, Tree<R>)>,
|
pub roots: BTreeMap<KnownObjectId, (RootItem, Tree<R>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: find better name
|
// TODO: find better name
|
||||||
|
@ -222,6 +222,12 @@ impl<R: super::Read> Volume<R> {
|
||||||
|
|
||||||
pub fn read_range_from_logical(&self, logical: u64) -> Result<Option<Vec<u8>>> {
|
pub fn read_range_from_logical(&self, logical: u64) -> Result<Option<Vec<u8>>> {
|
||||||
if let Some(range) = self.range_from_logical(logical) {
|
if let Some(range) = self.range_from_logical(logical) {
|
||||||
|
log::debug!(
|
||||||
|
"reading [{}, {}) ({} bytes)",
|
||||||
|
range.start,
|
||||||
|
range.end,
|
||||||
|
range.end - range.start
|
||||||
|
);
|
||||||
Ok(Some(self.read_range(range)?))
|
Ok(Some(self.read_range(range)?))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -353,7 +359,7 @@ impl<R: super::Read> Volume2<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: super::Read> Fs<R> {
|
impl<R: super::Read> Fs<R> {
|
||||||
fn get_inode_item(&self, inode_id: u64) -> Result<Option<INodeItem>> {
|
pub fn get_inode_item(&self, inode_id: u64) -> Result<Option<INodeItem>> {
|
||||||
if let Some((item, inoderef)) = self.find_inode_ref(inode_id)? {
|
if let Some((item, inoderef)) = self.find_inode_ref(inode_id)? {
|
||||||
if let Some(diritem) = self.find_dir_index(item.key.offset.get(), &inoderef)? {
|
if let Some(diritem) = self.find_dir_index(item.key.offset.get(), &inoderef)? {
|
||||||
let inode = self.find_inode_item(&diritem)?;
|
let inode = self.find_inode_item(&diritem)?;
|
||||||
|
@ -365,7 +371,11 @@ impl<R: super::Read> Fs<R> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root_dir(&self) -> INode {
|
pub fn root_dir_id(&self) -> KnownObjectId {
|
||||||
|
self.root_item.root_dirid.get().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_root_dir(&self) -> INode {
|
||||||
INode {
|
INode {
|
||||||
id: self.root_item.root_dirid.get(),
|
id: self.root_item.root_dirid.get(),
|
||||||
path: vec![],
|
path: vec![],
|
||||||
|
@ -514,7 +524,7 @@ impl<R: super::Read> Fs<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inode_extents(&self, inode_id: u64) -> Result<Vec<(u64, ExtentData)>> {
|
pub fn get_inode_extents(&self, inode_id: u64) -> Result<Vec<(u64, ExtentData)>> {
|
||||||
if let Some(dir_entry) = self.get_inode_dir_index(inode_id)? {
|
if let Some(dir_entry) = self.get_inode_dir_index(inode_id)? {
|
||||||
if dir_entry.item().ty() == DirItemType::RegFile {
|
if dir_entry.item().ty() == DirItemType::RegFile {
|
||||||
let key =
|
let key =
|
||||||
|
@ -540,7 +550,7 @@ impl<R: super::Read> Fs<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_inode_raw<I: RangeBounds<u64>>(&self, inode: &INode, range: I) -> Result<Vec<u8>> {
|
pub fn read_inode_raw<I: RangeBounds<u64>>(&self, inode: &INode, range: I) -> Result<Vec<u8>> {
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
let extents = self.get_inode_extents(inode.id)?;
|
let extents = self.get_inode_extents(inode.id)?;
|
||||||
|
|
||||||
|
@ -649,6 +659,10 @@ impl<R: super::Read> Fs<R> {
|
||||||
super::tree::entry::Entry::Vacant(_) => Ok(None),
|
super::tree::entry::Entry::Vacant(_) => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fs_root(&self) -> &Tree<R> {
|
||||||
|
&self.fs_root
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -718,8 +732,8 @@ mod tests {
|
||||||
0x8dbfc2d2, // crc of "default"
|
0x8dbfc2d2, // crc of "default"
|
||||||
);
|
);
|
||||||
|
|
||||||
let subvol_root = root_tree.find_node(&key)?;
|
let subvol_root = root_tree.entry(&key)?;
|
||||||
let other = root_tree.find_node_rev(&key)?;
|
let other = root_tree.entry_rev(&key)?;
|
||||||
assert_eq!(subvol_root, other);
|
assert_eq!(subvol_root, other);
|
||||||
log::info!("{subvol_root:?}");
|
log::info!("{subvol_root:?}");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1 +1,131 @@
|
||||||
|
use btrfs::structs::{ExtentData, ObjectType};
|
||||||
|
use btrfs::v2::error::Result;
|
||||||
|
use btrfs::v2::tree::PartialKey;
|
||||||
|
use btrfs::v2::volume::*;
|
||||||
|
use include_blob::include_blob;
|
||||||
|
|
||||||
|
fn open_filesystem() -> Result<std::rc::Rc<Volume2<&'static [u8]>>> {
|
||||||
|
let filesystem_data = include_bytes!("../simple.img").as_slice();
|
||||||
|
|
||||||
|
let volume = Volume::new(filesystem_data)?.into_volume2()?;
|
||||||
|
|
||||||
|
Ok(volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn asdf() -> Result<()> {
|
||||||
|
let filesystem_data = include_bytes!("../simple.img").as_slice();
|
||||||
|
let volume = Volume::new(filesystem_data)?;
|
||||||
|
//.into_volume2();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn read_superblock() -> Result<()> {
|
||||||
|
let vol2 = open_filesystem()?;
|
||||||
|
let sb = vol2.inner.superblock();
|
||||||
|
println!("{sb:#?}");
|
||||||
|
|
||||||
|
assert!(sb.verify_magic());
|
||||||
|
assert!(sb.verify_checksum());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn iter_roots() -> Result<()> {
|
||||||
|
let vol2 = open_filesystem()?;
|
||||||
|
for (id, _) in vol2.roots.iter() {
|
||||||
|
log::info!("[{id:?}] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn iter_roots_rev() -> Result<()> {
|
||||||
|
let vol2 = open_filesystem()?;
|
||||||
|
for (id, _) in vol2.roots.iter().rev() {
|
||||||
|
log::info!("[{id:?}] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn iter_default_subvol() -> Result<()> {
|
||||||
|
let v2 = open_filesystem()?;
|
||||||
|
let fs = v2.default_subvolume().expect("default subvol");
|
||||||
|
|
||||||
|
log::info!("files 1:");
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
for (_id, entry) in fs.fs_root().iter() {
|
||||||
|
if let Some(_dir) = entry.as_dir_index() {
|
||||||
|
//log::info!("{}", dir.name_as_string_lossy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("files 1: [took {}ms]", now.elapsed().as_millis());
|
||||||
|
|
||||||
|
log::info!("files 2:");
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
for (_id, entry) in fs.fs_root().iter() {
|
||||||
|
if let Some(_dir) = entry.as_dir_index() {
|
||||||
|
//log::info!("{}", dir.name_as_string_lossy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("files 2: [took {}ms]", now.elapsed().as_millis());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn get_inode_items() -> Result<()> {
|
||||||
|
let v2 = open_filesystem()?;
|
||||||
|
let fs = v2.default_subvolume().expect("default subvol");
|
||||||
|
|
||||||
|
let search_key = PartialKey::new(Some(fs.root_dir_id()), Some(ObjectType::DirIndex), None);
|
||||||
|
|
||||||
|
// with range
|
||||||
|
|
||||||
|
log::info!("range:");
|
||||||
|
for (key, v) in fs.fs_root().find_range(&search_key)? {
|
||||||
|
let dirindex = v.as_dir_index().unwrap();
|
||||||
|
let inode_id: u64 = dirindex.item().location.id().into();
|
||||||
|
log::info!("[{key:?}] {v:#?}");
|
||||||
|
log::info!("inode: {inode_id}");
|
||||||
|
|
||||||
|
let inode_item = fs.get_inode_item(inode_id)?;
|
||||||
|
log::info!("inode: {inode_item:#?}");
|
||||||
|
let extents = fs.get_inode_extents(inode_id)?;
|
||||||
|
|
||||||
|
for (_, extent) in extents {
|
||||||
|
match extent {
|
||||||
|
ExtentData::Inline { header, data } => {
|
||||||
|
log::info!("{header:?}\n{}", String::from_utf8_lossy(&data));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("range: [end]");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn find_file() -> Result<()> {
|
||||||
|
let v2 = open_filesystem()?;
|
||||||
|
let fs = v2.default_subvolume().expect("default subvol");
|
||||||
|
|
||||||
|
let root_dir = fs.get_root_dir();
|
||||||
|
let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
|
||||||
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
|
let cmake_list = fs.get_inode_by_path(b"/quibble/LICENCE")?;
|
||||||
|
let file_contents = fs
|
||||||
|
.read_inode_raw(&cmake_list, ..100)
|
||||||
|
.expect("file contents");
|
||||||
|
log::info!("license file:");
|
||||||
|
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue