This commit is contained in:
Janis 2023-04-04 22:25:14 +02:00
parent de79b77940
commit e0b1ddf3f6
3 changed files with 475 additions and 61 deletions

View file

@ -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)]

View file

@ -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!()
}

View file

@ -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(())
} }