diff --git a/btrfs/src/lib.rs b/btrfs/src/lib.rs index 8daff04..799c0d4 100644 --- a/btrfs/src/lib.rs +++ b/btrfs/src/lib.rs @@ -357,7 +357,7 @@ impl Btrfs { .expect("dir item") .first() .expect("dir item entry") - .dir_item + .item() .location .id(); diff --git a/btrfs/src/structs.rs b/btrfs/src/structs.rs index f9531bc..c6d3e99 100644 --- a/btrfs/src/structs.rs +++ b/btrfs/src/structs.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -use core::{mem::size_of, ops::Deref}; +use core::{fmt::Debug, mem::size_of, ops::Deref}; use bytemuck::{Pod, Zeroable}; use derivative::Derivative; @@ -67,7 +67,7 @@ unsafe impl Zeroable for ObjectId {} //#[rustc_nonnull_optimization_guaranteed] pub enum ObjectType { INodeItem = 0x01, - InodeRef = 0x0C, + INodeRef = 0x0C, InodeExtref = 0x0D, XattrItem = 0x18, OrphanInode = 0x30, @@ -215,18 +215,22 @@ macro_rules! impl_try_from_ctx { }; } +pub trait Parseable: Sized { + fn parse(bytes: &[u8]) -> Result; +} + macro_rules! impl_parse_try_from_ctx { ($($ty:ty),*) => { - $(impl $ty { - pub fn parse(bytes: &[u8]) -> Result { + $(impl Parseable for $ty { + fn parse(bytes: &[u8]) -> Result { Ok(bytes.pread(0)?) } })* }; } -impl_parse_try_from_ctx!(Chunk, Header, Key, RootItem); -impl_try_from_ctx!(Key, Chunk, Header, Superblock, RootItem); +impl_parse_try_from_ctx!(Chunk, Header, Key, RootItem, INodeItem, INodeRef, DirItem); +impl_try_from_ctx!(Key, Chunk, Header, Superblock, RootItem, INodeItem, INodeRef, DirItem); const MAX_LABEL_SIZE: usize = 0x100; const SYS_CHUNK_ARRAY_SIZE: usize = 0x800; @@ -312,13 +316,20 @@ pub struct ExtentData2 { } #[repr(C, packed(1))] -#[derive(Debug, Clone, Copy, Pod, Zeroable)] +#[derive(Debug, Clone, Copy, FromBytes, AsBytes)] pub struct INodeRef { - index: u64, - n: u16, - name: [u8; 1], + index: U64, + name_len: U16, } +impl NameLength for INodeRef { + fn name_length(&self) -> usize { + self.name_len.get() as usize + } +} + +pub type INodeRefEntry = ItemWithName; + #[repr(C, packed(1))] #[derive(Debug, Clone, Copy, Pod, Zeroable)] pub struct INodeExtRef { @@ -793,32 +804,29 @@ pub struct DirItem { ty: u8, } +impl NameLength for DirItem { + fn name_length(&self) -> usize { + self.name_len.get() as usize + } +} + impl DirItem { pub fn ty(&self) -> DirItemType { DirItemType::from_primitive(self.ty) } - pub fn parse_single(bytes: &[u8]) -> Result { - let offset = &mut 0; - Self::parse_single_inner(bytes, offset) - } - - fn parse_single_inner(bytes: &[u8], offset: &mut usize) -> Result { - let dir_item = DirItem::read_from(&bytes[*offset..*offset + size_of::()]) - .ok_or(Error::ReadFailed)?; - *offset += size_of::(); - let name_len = dir_item.name_len.get() as usize; - let name = &bytes[*offset..*offset + name_len]; - *offset += name_len; - - Ok(DirItemEntry::new(dir_item, name.to_vec())) - } - - pub fn parse(bytes: &[u8]) -> Result> { + pub fn parse_all(bytes: &[u8]) -> Result> { let offset = &mut 0; let entries = core::iter::from_fn(|| { - if *offset + size_of::() < bytes.len() { - Some(Self::parse_single_inner(&bytes[*offset..], offset)) + if *offset + size_of::() < bytes.len() { + let item = DirItemEntry::parse(&bytes[*offset..]); + *offset += size_of::() + + item + .as_ref() + .map(|item| item.item().name_length()) + .unwrap_or(0); + + Some(item) } else { None } @@ -829,17 +837,35 @@ impl DirItem { } } -#[derive(Clone)] -pub struct DirItemEntry { - pub dir_item: DirItem, - pub name: Vec, +pub trait NameLength { + fn name_length(&self) -> usize; } -impl DirItemEntry { - pub fn new(dir_item: DirItem, name: Vec) -> Self { - Self { dir_item, name } - } +#[derive(Clone)] +pub struct ItemWithName +where + I: NameLength + Parseable, +{ + inner: I, + name: Vec, +} +impl Debug for ItemWithName +where + I: NameLength + Parseable + Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "({:?}, {:?})", &self.inner, &self.name_as_string_lossy()) + } +} + +impl ItemWithName +where + I: NameLength + Parseable, +{ + pub fn item(&self) -> &I { + &self.inner + } pub fn name_as_str(&self) -> core::result::Result<&str, core::str::Utf8Error> { core::str::from_utf8(&self.name) } @@ -848,15 +874,19 @@ impl DirItemEntry { } } -impl core::fmt::Debug for DirItemEntry { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("DirItemEntry") - .field("dir_item", &self.dir_item) - .field("name", &self.name_as_string_lossy()) - .finish() +impl Parseable for ItemWithName +where + I: NameLength + Parseable, +{ + fn parse(bytes: &[u8]) -> Result { + let inner = I::parse(bytes)?; + let name = bytes[size_of::()..][..inner.name_length()].to_vec(); + Ok(Self { inner, name }) } } +pub type DirItemEntry = ItemWithName; + #[repr(C, packed)] #[derive(Debug, Clone, Copy, Pod, Zeroable)] pub struct InodeRef { @@ -906,6 +936,8 @@ pub enum TreeItem { Root(RootItem), DirItem(Vec), DirIndex(DirItemEntry), + INodeItem(INodeItem), + INodeRef(INodeRefEntry), Unimplemented, } @@ -933,13 +965,27 @@ impl From for TreeItem { } } +impl From for TreeItem { + fn from(value: INodeItem) -> Self { + Self::INodeItem(value) + } +} + +impl From for TreeItem { + fn from(value: INodeRefEntry) -> Self { + Self::INodeRef(value) + } +} + impl TreeItem { pub fn parse(item: &Item, bytes: &[u8]) -> Result { Ok(match item.key.ty() { ObjectType::RootItem => RootItem::parse(bytes)?.into(), ObjectType::ChunkItem => Chunk::parse(bytes)?.into(), - ObjectType::DirItem => DirItem::parse(bytes)?.into(), - ObjectType::DirIndex => DirItem::parse_single(bytes)?.into(), + ObjectType::DirItem => DirItem::parse_all(bytes)?.into(), + ObjectType::DirIndex => DirItemEntry::parse(bytes)?.into(), + ObjectType::INodeItem => INodeItem::parse(bytes)?.into(), + ObjectType::INodeRef => INodeRefEntry::parse(bytes)?.into(), _ => TreeItem::Unimplemented, }) } diff --git a/btrfs/src/v2/volume.rs b/btrfs/src/v2/volume.rs index 7d8facb..44bbeb8 100644 --- a/btrfs/src/v2/volume.rs +++ b/btrfs/src/v2/volume.rs @@ -4,7 +4,9 @@ use alloc::collections::btree_map::Entry; use alloc::{collections::BTreeMap, rc::Rc, vec, vec::Vec}; use scroll::Pread; -use crate::structs::{Chunk, Key, KeyPtr, KnownObjectId, ObjectType, Stripe, Superblock, TreeItem}; +use crate::structs::{ + Chunk, Key, KeyPtr, KnownObjectId, ObjectType, RootItem, Stripe, Superblock, TreeItem, +}; use crate::{Error, Result}; use super::tree::Tree; @@ -326,7 +328,7 @@ impl Volume2 { .expect("dir item") .first() .expect("dir item entry") - .dir_item + .item() .location .id();