idk
This commit is contained in:
parent
de79b77940
commit
e0b1ddf3f6
|
@ -328,6 +328,22 @@ pub enum ExtentData {
|
|||
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 {
|
||||
fn parse(bytes: &[u8]) -> Result<Self> {
|
||||
let header = ExtentData1::parse(bytes)?;
|
||||
|
@ -966,6 +982,10 @@ where
|
|||
&self.name
|
||||
}
|
||||
|
||||
pub fn into_name(self) -> Vec<u8> {
|
||||
self.name
|
||||
}
|
||||
|
||||
pub fn name_as_str(&self) -> core::result::Result<&str, core::str::Utf8Error> {
|
||||
core::str::from_utf8(&self.name)
|
||||
}
|
||||
|
@ -1107,9 +1127,7 @@ impl TreeItem {
|
|||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TreeItem {
|
||||
pub fn as_chunk(&self) -> Option<&Chunk> {
|
||||
if let Self::Chunk(v) = self {
|
||||
Some(v)
|
||||
|
@ -1157,6 +1175,62 @@ impl TreeItem {
|
|||
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)]
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
use core::{
|
||||
cmp::Ordering,
|
||||
fmt::Debug,
|
||||
ops::{Bound, RangeBounds},
|
||||
};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
pub mod error {
|
||||
|
@ -65,3 +71,275 @@ impl Read for std::fs::File {
|
|||
pub mod file;
|
||||
pub mod tree;
|
||||
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::ops::{Range, RangeBounds};
|
||||
|
||||
use alloc::collections::btree_map::Entry;
|
||||
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 children = self.fs_root.find_range(&key)?;
|
||||
|
||||
let children = children
|
||||
.map(|(_, v)| {
|
||||
let dir_item = v.as_dir_index().expect("dir index");
|
||||
let id: u64 = dir_item.item().location.id().into();
|
||||
let a = children.map(|(_, v)| v.try_into_dir_index().expect("dir index"));
|
||||
|
||||
inode.clone().into_child(id, dir_item.name().clone())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(children)
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
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)?;
|
||||
}
|
||||
crate::path::Segment::File(child_name) => {
|
||||
inode = self
|
||||
.get_inode_children(&inode)?
|
||||
.iter()
|
||||
let child = self
|
||||
.get_inode_children_inodes(&inode)?
|
||||
.find(|child| {
|
||||
child.path.last().map(|bytes| bytes.as_slice()) == Some(child_name)
|
||||
})
|
||||
.ok_or(Error::INodeNotFound)?
|
||||
.clone()
|
||||
.clone();
|
||||
// silly borrow checker
|
||||
inode = child;
|
||||
}
|
||||
_ => 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 dir_entry.item().ty() == DirItemType::RegFile {
|
||||
let key =
|
||||
|
@ -513,11 +524,81 @@ impl<R: super::Read> Fs<R> {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
return Ok(Some(extents));
|
||||
Ok(extents)
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
} else {
|
||||
Err(Error::INodeNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
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(contents)
|
||||
}
|
||||
|
||||
fn find_inode_ref(&self, inode_id: u64) -> Result<Option<(Item, INodeRefEntry)>> {
|
||||
|
@ -674,7 +755,6 @@ mod tests {
|
|||
log::info!("inode: {inode_item:#?}");
|
||||
let extents = fs.get_inode_extents(inode_id)?;
|
||||
|
||||
if let Some(extents) = extents {
|
||||
for (_, extent) in extents {
|
||||
match extent {
|
||||
ExtentData::Inline { header, data } => {
|
||||
|
@ -684,7 +764,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::info!("range: [end]");
|
||||
|
||||
Ok(())
|
||||
|
@ -698,11 +777,11 @@ mod tests {
|
|||
let fs = v2.default_subvolume().expect("default subvol");
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
let hii = fs.get_inode_by_path(b"/home/user/hii.txt")?;
|
||||
|
@ -711,7 +790,6 @@ mod tests {
|
|||
let extents = fs.get_inode_extents(hii.id)?;
|
||||
assert_eq!(hii, hii2);
|
||||
|
||||
if let Some(extents) = extents {
|
||||
for (_offset, extent) in extents {
|
||||
match extent {
|
||||
ExtentData::Inline { header, data } => {
|
||||
|
@ -720,38 +798,22 @@ mod tests {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
let home = fs.get_inode_by_path(b"/home/user/btrfs/CMakeLists.txt")?;
|
||||
let extents = fs.get_inode_extents(home.id)?;
|
||||
for child in children {
|
||||
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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue