idk
This commit is contained in:
parent
de79b77940
commit
e0b1ddf3f6
|
@ -328,6 +328,22 @@ pub enum ExtentData {
|
||||||
Other(ExtentData2),
|
Other(ExtentData2),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExtentData {
|
||||||
|
pub fn header(&self) -> &ExtentData1 {
|
||||||
|
match self {
|
||||||
|
ExtentData::Inline { header, .. } => header,
|
||||||
|
ExtentData::Other(extent) => extent.extent_data1(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
ExtentData::Inline { data, .. } => data.len() as u64,
|
||||||
|
ExtentData::Other(extent) => extent.num_bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parseable for ExtentData {
|
impl Parseable for ExtentData {
|
||||||
fn parse(bytes: &[u8]) -> Result<Self> {
|
fn parse(bytes: &[u8]) -> Result<Self> {
|
||||||
let header = ExtentData1::parse(bytes)?;
|
let header = ExtentData1::parse(bytes)?;
|
||||||
|
@ -966,6 +982,10 @@ where
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_name(self) -> Vec<u8> {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
pub fn name_as_str(&self) -> core::result::Result<&str, core::str::Utf8Error> {
|
pub fn name_as_str(&self) -> core::result::Result<&str, core::str::Utf8Error> {
|
||||||
core::str::from_utf8(&self.name)
|
core::str::from_utf8(&self.name)
|
||||||
}
|
}
|
||||||
|
@ -1107,9 +1127,7 @@ impl TreeItem {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TreeItem {
|
|
||||||
pub fn as_chunk(&self) -> Option<&Chunk> {
|
pub fn as_chunk(&self) -> Option<&Chunk> {
|
||||||
if let Self::Chunk(v) = self {
|
if let Self::Chunk(v) = self {
|
||||||
Some(v)
|
Some(v)
|
||||||
|
@ -1157,6 +1175,62 @@ impl TreeItem {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_into_chunk(self) -> core::result::Result<Chunk, Self> {
|
||||||
|
if let Self::Chunk(v) = self {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_into_root(self) -> core::result::Result<RootItem, Self> {
|
||||||
|
if let Self::Root(v) = self {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_into_extent_data(self) -> core::result::Result<ExtentData, Self> {
|
||||||
|
if let Self::ExtentData(v) = self {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_into_dir_item(self) -> core::result::Result<Vec<DirItemEntry>, Self> {
|
||||||
|
if let Self::DirItem(v) = self {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_into_dir_index(self) -> core::result::Result<DirItemEntry, Self> {
|
||||||
|
if let Self::DirIndex(v) = self {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_into_inode_item(self) -> core::result::Result<INodeItem, Self> {
|
||||||
|
if let Self::INodeItem(v) = self {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_into_inode_ref(self) -> core::result::Result<INodeRefEntry, Self> {
|
||||||
|
if let Self::INodeRef(v) = self {
|
||||||
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
use core::{
|
||||||
|
cmp::Ordering,
|
||||||
|
fmt::Debug,
|
||||||
|
ops::{Bound, RangeBounds},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
pub mod error {
|
pub mod error {
|
||||||
|
@ -65,3 +71,275 @@ impl Read for std::fs::File {
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
pub mod volume;
|
pub mod volume;
|
||||||
|
|
||||||
|
pub fn cmp_start_bound<T: PartialOrd>(
|
||||||
|
rhs: &core::ops::Bound<T>,
|
||||||
|
lhs: &core::ops::Bound<T>,
|
||||||
|
) -> Option<core::cmp::Ordering> {
|
||||||
|
match rhs {
|
||||||
|
core::ops::Bound::Included(r) => match lhs {
|
||||||
|
core::ops::Bound::Included(l) => r.partial_cmp(&l),
|
||||||
|
core::ops::Bound::Excluded(l) => match r.partial_cmp(&l) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => Some(core::cmp::Ordering::Less),
|
||||||
|
i => i,
|
||||||
|
},
|
||||||
|
core::ops::Bound::Unbounded => Some(core::cmp::Ordering::Greater),
|
||||||
|
},
|
||||||
|
core::ops::Bound::Excluded(r) => match lhs {
|
||||||
|
core::ops::Bound::Excluded(l) => r.partial_cmp(&l),
|
||||||
|
core::ops::Bound::Included(l) => match r.partial_cmp(&l) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => Some(core::cmp::Ordering::Greater),
|
||||||
|
i => i,
|
||||||
|
},
|
||||||
|
core::ops::Bound::Unbounded => Some(core::cmp::Ordering::Greater),
|
||||||
|
},
|
||||||
|
core::ops::Bound::Unbounded => match lhs {
|
||||||
|
core::ops::Bound::Unbounded => Some(core::cmp::Ordering::Equal),
|
||||||
|
_ => Some(core::cmp::Ordering::Less),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmp_end_bound<T: PartialOrd>(
|
||||||
|
rhs: &core::ops::Bound<T>,
|
||||||
|
lhs: &core::ops::Bound<T>,
|
||||||
|
) -> Option<core::cmp::Ordering> {
|
||||||
|
match rhs {
|
||||||
|
core::ops::Bound::Included(r) => match lhs {
|
||||||
|
core::ops::Bound::Included(l) => r.partial_cmp(&l),
|
||||||
|
core::ops::Bound::Excluded(l) => match r.partial_cmp(&l) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => Some(core::cmp::Ordering::Greater),
|
||||||
|
i => i,
|
||||||
|
},
|
||||||
|
core::ops::Bound::Unbounded => Some(core::cmp::Ordering::Less),
|
||||||
|
},
|
||||||
|
core::ops::Bound::Excluded(r) => match lhs {
|
||||||
|
core::ops::Bound::Excluded(l) => r.partial_cmp(&l),
|
||||||
|
core::ops::Bound::Included(l) => match r.partial_cmp(&l) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => Some(core::cmp::Ordering::Greater),
|
||||||
|
i => i,
|
||||||
|
},
|
||||||
|
core::ops::Bound::Unbounded => Some(core::cmp::Ordering::Less),
|
||||||
|
},
|
||||||
|
core::ops::Bound::Unbounded => match lhs {
|
||||||
|
core::ops::Bound::Unbounded => Some(core::cmp::Ordering::Equal),
|
||||||
|
_ => Some(core::cmp::Ordering::Greater),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct StartBound<T: PartialOrd>(Bound<T>);
|
||||||
|
impl<T: PartialOrd> PartialOrd for StartBound<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
|
cmp_start_bound(&self.0, &other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Ord> Ord for StartBound<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||||
|
self.partial_cmp(other).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct EndBound<T: PartialOrd>(Bound<T>);
|
||||||
|
|
||||||
|
impl<T: PartialOrd> PartialOrd for EndBound<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
|
cmp_end_bound(&self.0, &other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Ord> Ord for EndBound<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||||
|
// safety: partial_cmp only returns None when returning T::partial_cmp
|
||||||
|
self.partial_cmp(other).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range_countains_bound<T: Ord + Debug, R: RangeBounds<T>>(
|
||||||
|
range: R,
|
||||||
|
bound: Bound<&T>,
|
||||||
|
) -> bool {
|
||||||
|
let start = &StartBound(bound);
|
||||||
|
let end = &EndBound(bound);
|
||||||
|
let r_start = &StartBound(range.start_bound());
|
||||||
|
let r_end = &EndBound(range.end_bound());
|
||||||
|
log::info!(
|
||||||
|
"start: {start:?} <=> {r_start:?}: {:?} {:?}",
|
||||||
|
start.cmp(r_start),
|
||||||
|
r_start.cmp(start)
|
||||||
|
);
|
||||||
|
log::info!(
|
||||||
|
"end: {end:?} <=> {r_end:?}: {:?} {:?}",
|
||||||
|
end.cmp(r_end),
|
||||||
|
r_end.cmp(end)
|
||||||
|
);
|
||||||
|
(start.cmp(r_start).is_ge() || r_start.cmp(start).is_le())
|
||||||
|
&& (end.cmp(r_end).is_ge() || r_end.cmp(end).is_le())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod bound_ord_tests {
|
||||||
|
use core::{cmp::Ordering, ops::RangeBounds};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use test_log::test;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn start_bound_ord() {
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&Bound::Unbounded, &Bound::Included(0)),
|
||||||
|
Some(Ordering::Less)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound::<i32>(&Bound::Unbounded, &Bound::Unbounded),
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&Bound::Included(0), &Bound::Included(0)),
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&Bound::Excluded(0), &Bound::Included(0)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
cmp_start_bound(&Bound::Excluded(0), &Bound::Included(1)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
// This is actually WRONG and is why we have to test both ways because I
|
||||||
|
// can't think of a way to actually determine how the 2 bounds are
|
||||||
|
// ordered without knowing the smallest discrete step size of T.
|
||||||
|
//
|
||||||
|
// In this case technically they should be equal, but for floats (which
|
||||||
|
// arent ord anyways?) this would be wrong
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&Bound::Included(1), &Bound::Excluded(0)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&Bound::Included(0), &Bound::Excluded(1)),
|
||||||
|
Some(Ordering::Less)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn end_bound_ord() {
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound::<i32>(&Bound::Unbounded, &Bound::Unbounded),
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&Bound::Unbounded, &Bound::Included(0)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&Bound::Included(0), &Bound::Included(0)),
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&Bound::Excluded(0), &Bound::Included(0)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
cmp_end_bound(&Bound::Excluded(0), &Bound::Included(1)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
// This is actually WRONG and is why we have to test both ways because I
|
||||||
|
// can't think of a way to actually determine how the 2 bounds are
|
||||||
|
// ordered without knowing the smallest discrete step size of T.
|
||||||
|
//
|
||||||
|
// In this case technically they should be equal, but for floats (which
|
||||||
|
// arent ord anyways?) this would be wrong
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&Bound::Included(1), &Bound::Excluded(0)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&Bound::Included(0), &Bound::Excluded(1)),
|
||||||
|
Some(Ordering::Less)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bound_ord() {
|
||||||
|
let r1 = 0..4;
|
||||||
|
let r2 = 2..3;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&r1.start_bound(), &r2.start_bound()),
|
||||||
|
Some(core::cmp::Ordering::Less)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&r1.end_bound(), &r2.end_bound()),
|
||||||
|
Some(core::cmp::Ordering::Greater)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&r2.start_bound(), &r1.start_bound()),
|
||||||
|
Some(core::cmp::Ordering::Greater)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&r2.end_bound(), &r1.end_bound()),
|
||||||
|
Some(core::cmp::Ordering::Less)
|
||||||
|
);
|
||||||
|
|
||||||
|
let r1 = 0..=8;
|
||||||
|
let r2 = 0..9;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cmp_start_bound(&r1.start_bound(), &r2.start_bound()),
|
||||||
|
Some(core::cmp::Ordering::Equal)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&r1.end_bound(), &r2.end_bound()),
|
||||||
|
Some(core::cmp::Ordering::Less)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cmp_end_bound(&r2.end_bound(), &r1.end_bound()),
|
||||||
|
Some(core::cmp::Ordering::Greater)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ranges_intersect<T, R1, R2>(rhs: R1, lhs: R2) -> ()
|
||||||
|
where
|
||||||
|
T: Ord,
|
||||||
|
R1: core::ops::RangeBounds<T>,
|
||||||
|
R2: core::ops::RangeBounds<T>,
|
||||||
|
{
|
||||||
|
let a = rhs.start_bound();
|
||||||
|
let b = rhs.end_bound();
|
||||||
|
let x = lhs.start_bound();
|
||||||
|
let y = lhs.end_bound();
|
||||||
|
|
||||||
|
// check that a <=> x is different than b <=> x
|
||||||
|
|
||||||
|
// a <=> x
|
||||||
|
match a {
|
||||||
|
Bound::Included(a) => match x {
|
||||||
|
Bound::Included(x) => a.partial_cmp(x),
|
||||||
|
Bound::Excluded(x) => match a.partial_cmp(x) {
|
||||||
|
Some(Ordering::Equal) => Some(Ordering::Less),
|
||||||
|
ord => ord,
|
||||||
|
},
|
||||||
|
Bound::Unbounded => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
Bound::Excluded(a) => match x {
|
||||||
|
Bound::Included(x) => match a.partial_cmp(x) {
|
||||||
|
Some(Ordering::Equal) => Some(Ordering::Greater),
|
||||||
|
ord => ord,
|
||||||
|
},
|
||||||
|
Bound::Excluded(x) => a.partial_cmp(x),
|
||||||
|
Bound::Unbounded => Some(Ordering::Less),
|
||||||
|
},
|
||||||
|
Bound::Unbounded => match x {
|
||||||
|
Bound::Unbounded => Some(Ordering::Equal),
|
||||||
|
_ => Some(Ordering::Less),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
|
use core::ops::{Range, RangeBounds};
|
||||||
|
|
||||||
use alloc::collections::btree_map::Entry;
|
use alloc::collections::btree_map::Entry;
|
||||||
use alloc::{collections::BTreeMap, rc::Rc, vec, vec::Vec};
|
use alloc::{collections::BTreeMap, rc::Rc, vec, vec::Vec};
|
||||||
|
@ -370,21 +371,30 @@ impl<R: super::Read> Fs<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inode_children(&self, inode: &INode) -> Result<Vec<INode>> {
|
pub fn get_inode_children_inodes<'a>(
|
||||||
|
&self,
|
||||||
|
inode: &'a INode,
|
||||||
|
) -> Result<impl Iterator<Item = INode> + 'a>
|
||||||
|
where
|
||||||
|
R: 'a,
|
||||||
|
{
|
||||||
|
self.get_inode_children(inode).map(|children| {
|
||||||
|
children.map(|child| {
|
||||||
|
let id: u64 = child.item().location.id().into();
|
||||||
|
|
||||||
|
inode.clone().into_child(id, child.into_name())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_inode_children(&self, inode: &INode) -> Result<impl Iterator<Item = DirItemEntry>> {
|
||||||
let key = PartialKey::new(Some(inode.id()), Some(ObjectType::DirIndex), None);
|
let key = PartialKey::new(Some(inode.id()), Some(ObjectType::DirIndex), None);
|
||||||
|
|
||||||
let children = self.fs_root.find_range(&key)?;
|
let children = self.fs_root.find_range(&key)?;
|
||||||
|
|
||||||
let children = children
|
let a = children.map(|(_, v)| v.try_into_dir_index().expect("dir index"));
|
||||||
.map(|(_, v)| {
|
|
||||||
let dir_item = v.as_dir_index().expect("dir index");
|
|
||||||
let id: u64 = dir_item.item().location.id().into();
|
|
||||||
|
|
||||||
inode.clone().into_child(id, dir_item.name().clone())
|
Ok(a)
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Ok(children)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inode_parent(&self, inode: &INode) -> Result<INode> {
|
pub fn get_inode_parent(&self, inode: &INode) -> Result<INode> {
|
||||||
|
@ -420,14 +430,15 @@ impl<R: super::Read> Fs<R> {
|
||||||
inode = self.get_inode_parent(&inode)?;
|
inode = self.get_inode_parent(&inode)?;
|
||||||
}
|
}
|
||||||
crate::path::Segment::File(child_name) => {
|
crate::path::Segment::File(child_name) => {
|
||||||
inode = self
|
let child = self
|
||||||
.get_inode_children(&inode)?
|
.get_inode_children_inodes(&inode)?
|
||||||
.iter()
|
|
||||||
.find(|child| {
|
.find(|child| {
|
||||||
child.path.last().map(|bytes| bytes.as_slice()) == Some(child_name)
|
child.path.last().map(|bytes| bytes.as_slice()) == Some(child_name)
|
||||||
})
|
})
|
||||||
.ok_or(Error::INodeNotFound)?
|
.ok_or(Error::INodeNotFound)?
|
||||||
.clone()
|
.clone();
|
||||||
|
// silly borrow checker
|
||||||
|
inode = child;
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -496,7 +507,7 @@ impl<R: super::Read> Fs<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inode_extents(&self, inode_id: u64) -> Result<Option<Vec<(u64, ExtentData)>>> {
|
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 =
|
||||||
|
@ -513,11 +524,81 @@ impl<R: super::Read> Fs<R> {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
return Ok(Some(extents));
|
Ok(extents)
|
||||||
|
} else {
|
||||||
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::INodeNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_inode_raw<I: RangeBounds<u64>>(&self, inode: &INode, range: I) -> Result<Vec<u8>> {
|
||||||
|
let mut contents = Vec::new();
|
||||||
|
let extents = self.get_inode_extents(inode.id)?;
|
||||||
|
|
||||||
|
let start = match range.start_bound() {
|
||||||
|
core::ops::Bound::Included(v) => *v,
|
||||||
|
core::ops::Bound::Excluded(v) => *v + 1,
|
||||||
|
core::ops::Bound::Unbounded => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let end = match range.end_bound() {
|
||||||
|
core::ops::Bound::Included(v) => Some(*v + 1),
|
||||||
|
core::ops::Bound::Excluded(v) => Some(*v),
|
||||||
|
core::ops::Bound::Unbounded => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (offset, extent) in extents.into_iter().filter(|(offset, extent)| {
|
||||||
|
let extent_start = *offset;
|
||||||
|
let extent_end = extent_start + extent.len();
|
||||||
|
|
||||||
|
let range_len = end.map(|end| end - start);
|
||||||
|
|
||||||
|
let start2 = start.min(extent_start);
|
||||||
|
let end = end.map(|end| end.max(extent_end));
|
||||||
|
let len = end.map(|end| (end - start2));
|
||||||
|
|
||||||
|
if let (Some(len), Some(range_len)) = (len, range_len) {
|
||||||
|
range_len + range_len < len
|
||||||
|
} else {
|
||||||
|
start2 < extent_end
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
//
|
||||||
|
let start = start.saturating_sub(offset);
|
||||||
|
|
||||||
|
let end = end.map(|end| end - offset);
|
||||||
|
|
||||||
|
log::info!("reading {}..{:?} from extent.", start, end);
|
||||||
|
|
||||||
|
let data: alloc::borrow::Cow<[u8]> = match &extent {
|
||||||
|
ExtentData::Inline { data, .. } => {
|
||||||
|
// TODO: handle compression and encryption
|
||||||
|
let data = if let Some(end) = end {
|
||||||
|
&data[start as usize..end as usize]
|
||||||
|
} else {
|
||||||
|
&data[start as usize..]
|
||||||
|
};
|
||||||
|
|
||||||
|
data.into()
|
||||||
|
}
|
||||||
|
ExtentData::Other(extent) => {
|
||||||
|
let address = extent.address() + extent.offset() + start;
|
||||||
|
let data = self
|
||||||
|
.volume
|
||||||
|
.inner
|
||||||
|
.read_range(address..address + end.unwrap_or(extent.num_bytes()))
|
||||||
|
.expect("bytes");
|
||||||
|
data.into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log::info!("reading {} bytes from file", data.len());
|
||||||
|
contents.extend_from_slice(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_inode_ref(&self, inode_id: u64) -> Result<Option<(Item, INodeRefEntry)>> {
|
fn find_inode_ref(&self, inode_id: u64) -> Result<Option<(Item, INodeRefEntry)>> {
|
||||||
|
@ -674,14 +755,12 @@ mod tests {
|
||||||
log::info!("inode: {inode_item:#?}");
|
log::info!("inode: {inode_item:#?}");
|
||||||
let extents = fs.get_inode_extents(inode_id)?;
|
let extents = fs.get_inode_extents(inode_id)?;
|
||||||
|
|
||||||
if let Some(extents) = extents {
|
for (_, extent) in extents {
|
||||||
for (_, extent) in extents {
|
match extent {
|
||||||
match extent {
|
ExtentData::Inline { header, data } => {
|
||||||
ExtentData::Inline { header, data } => {
|
log::info!("{header:?}\n{}", String::from_utf8_lossy(&data));
|
||||||
log::info!("{header:?}\n{}", String::from_utf8_lossy(&data));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -698,11 +777,11 @@ mod tests {
|
||||||
let fs = v2.default_subvolume().expect("default subvol");
|
let fs = v2.default_subvolume().expect("default subvol");
|
||||||
|
|
||||||
let root_dir = fs.get_root_dir();
|
let root_dir = fs.get_root_dir();
|
||||||
let children = fs.get_inode_children(&root_dir)?;
|
let children = fs.get_inode_children(&root_dir)?.collect::<Vec<_>>();
|
||||||
log::info!("chidlren: {:?}", children);
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
let home = fs.get_inode_by_path(b"/home/user")?;
|
let home = fs.get_inode_by_path(b"/home/user")?;
|
||||||
let children = fs.get_inode_children(&home)?;
|
let children = fs.get_inode_children(&home)?.collect::<Vec<_>>();
|
||||||
log::info!("chidlren: {:?}", children);
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
let hii = fs.get_inode_by_path(b"/home/user/hii.txt")?;
|
let hii = fs.get_inode_by_path(b"/home/user/hii.txt")?;
|
||||||
|
@ -711,48 +790,31 @@ mod tests {
|
||||||
let extents = fs.get_inode_extents(hii.id)?;
|
let extents = fs.get_inode_extents(hii.id)?;
|
||||||
assert_eq!(hii, hii2);
|
assert_eq!(hii, hii2);
|
||||||
|
|
||||||
if let Some(extents) = extents {
|
for (_offset, extent) in extents {
|
||||||
for (_offset, extent) in extents {
|
match extent {
|
||||||
match extent {
|
ExtentData::Inline { header, data } => {
|
||||||
ExtentData::Inline { header, data } => {
|
log::info!("{header:?}\n{}", String::from_utf8_lossy(&data));
|
||||||
log::info!("{header:?}\n{}", String::from_utf8_lossy(&data));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let btrfs = fs.get_inode_by_path(b"/home/user/btrfs")?;
|
let btrfs = fs.get_inode_by_path(b"/home/user/btrfs")?;
|
||||||
let children = fs.get_inode_children(&btrfs)?;
|
let children = fs.get_inode_children_inodes(&btrfs)?.collect::<Vec<_>>();
|
||||||
log::info!("chidlren: {:?}", children);
|
log::info!("chidlren: {:?}", children);
|
||||||
|
|
||||||
let home = fs.get_inode_by_path(b"/home/user/btrfs/CMakeLists.txt")?;
|
for child in children {
|
||||||
let extents = fs.get_inode_extents(home.id)?;
|
let file_contents = fs.read_inode_raw(&child, ..).expect("file contents");
|
||||||
|
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
||||||
if let Some(extents) = extents {
|
|
||||||
for (offset, extent) in extents {
|
|
||||||
log::info!("extent {:x}..", offset);
|
|
||||||
match extent {
|
|
||||||
ExtentData::Inline { header, data } => {
|
|
||||||
log::info!("{header:?}\n{}", String::from_utf8_lossy(&data));
|
|
||||||
}
|
|
||||||
ExtentData::Other(extent) => {
|
|
||||||
let address = extent.address() + extent.offset();
|
|
||||||
let data = fs
|
|
||||||
.volume
|
|
||||||
.inner
|
|
||||||
.read_range(address..address + extent.num_bytes())
|
|
||||||
.expect("bytes");
|
|
||||||
log::info!(
|
|
||||||
"{:?}\n{}",
|
|
||||||
extent.extent_data1(),
|
|
||||||
String::from_utf8_lossy(&data)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cmake_list = fs.get_inode_by_path(b"/home/user/btrfs/CMakeLists.txt")?;
|
||||||
|
let file_contents = fs
|
||||||
|
.read_inode_raw(&cmake_list, ..100)
|
||||||
|
.expect("file contents");
|
||||||
|
log::info!("cmakelists file:");
|
||||||
|
log::info!("{}", String::from_utf8_lossy(&file_contents));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue