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),
}
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)]

View file

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

View file

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