generalized item + name bytes pattern

This commit is contained in:
Janis 2023-03-31 01:14:43 +02:00
parent 33852e99b0
commit fa66bf415e
3 changed files with 96 additions and 48 deletions

View file

@ -357,7 +357,7 @@ impl<R: VolumeIo> Btrfs<R> {
.expect("dir item") .expect("dir item")
.first() .first()
.expect("dir item entry") .expect("dir item entry")
.dir_item .item()
.location .location
.id(); .id();

View file

@ -1,5 +1,5 @@
#![allow(dead_code)] #![allow(dead_code)]
use core::{mem::size_of, ops::Deref}; use core::{fmt::Debug, mem::size_of, ops::Deref};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use derivative::Derivative; use derivative::Derivative;
@ -67,7 +67,7 @@ unsafe impl Zeroable for ObjectId {}
//#[rustc_nonnull_optimization_guaranteed] //#[rustc_nonnull_optimization_guaranteed]
pub enum ObjectType { pub enum ObjectType {
INodeItem = 0x01, INodeItem = 0x01,
InodeRef = 0x0C, INodeRef = 0x0C,
InodeExtref = 0x0D, InodeExtref = 0x0D,
XattrItem = 0x18, XattrItem = 0x18,
OrphanInode = 0x30, OrphanInode = 0x30,
@ -215,18 +215,22 @@ macro_rules! impl_try_from_ctx {
}; };
} }
pub trait Parseable: Sized {
fn parse(bytes: &[u8]) -> Result<Self>;
}
macro_rules! impl_parse_try_from_ctx { macro_rules! impl_parse_try_from_ctx {
($($ty:ty),*) => { ($($ty:ty),*) => {
$(impl $ty { $(impl Parseable for $ty {
pub fn parse(bytes: &[u8]) -> Result<Self> { fn parse(bytes: &[u8]) -> Result<Self> {
Ok(bytes.pread(0)?) Ok(bytes.pread(0)?)
} }
})* })*
}; };
} }
impl_parse_try_from_ctx!(Chunk, Header, Key, RootItem); impl_parse_try_from_ctx!(Chunk, Header, Key, RootItem, INodeItem, INodeRef, DirItem);
impl_try_from_ctx!(Key, Chunk, Header, Superblock, RootItem); impl_try_from_ctx!(Key, Chunk, Header, Superblock, RootItem, INodeItem, INodeRef, DirItem);
const MAX_LABEL_SIZE: usize = 0x100; const MAX_LABEL_SIZE: usize = 0x100;
const SYS_CHUNK_ARRAY_SIZE: usize = 0x800; const SYS_CHUNK_ARRAY_SIZE: usize = 0x800;
@ -312,13 +316,20 @@ pub struct ExtentData2 {
} }
#[repr(C, packed(1))] #[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)] #[derive(Debug, Clone, Copy, FromBytes, AsBytes)]
pub struct INodeRef { pub struct INodeRef {
index: u64, index: U64<LE>,
n: u16, name_len: U16<LE>,
name: [u8; 1],
} }
impl NameLength for INodeRef {
fn name_length(&self) -> usize {
self.name_len.get() as usize
}
}
pub type INodeRefEntry = ItemWithName<INodeRef>;
#[repr(C, packed(1))] #[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)] #[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct INodeExtRef { pub struct INodeExtRef {
@ -793,32 +804,29 @@ pub struct DirItem {
ty: u8, ty: u8,
} }
impl NameLength for DirItem {
fn name_length(&self) -> usize {
self.name_len.get() as usize
}
}
impl DirItem { impl DirItem {
pub fn ty(&self) -> DirItemType { pub fn ty(&self) -> DirItemType {
DirItemType::from_primitive(self.ty) DirItemType::from_primitive(self.ty)
} }
pub fn parse_single(bytes: &[u8]) -> Result<DirItemEntry> { pub fn parse_all(bytes: &[u8]) -> Result<Vec<DirItemEntry>> {
let offset = &mut 0;
Self::parse_single_inner(bytes, offset)
}
fn parse_single_inner(bytes: &[u8], offset: &mut usize) -> Result<DirItemEntry> {
let dir_item = DirItem::read_from(&bytes[*offset..*offset + size_of::<DirItem>()])
.ok_or(Error::ReadFailed)?;
*offset += size_of::<DirItem>();
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<Vec<DirItemEntry>> {
let offset = &mut 0; let offset = &mut 0;
let entries = core::iter::from_fn(|| { let entries = core::iter::from_fn(|| {
if *offset + size_of::<DirItem>() < bytes.len() { if *offset + size_of::<Self>() < bytes.len() {
Some(Self::parse_single_inner(&bytes[*offset..], offset)) let item = DirItemEntry::parse(&bytes[*offset..]);
*offset += size_of::<Self>()
+ item
.as_ref()
.map(|item| item.item().name_length())
.unwrap_or(0);
Some(item)
} else { } else {
None None
} }
@ -829,17 +837,35 @@ impl DirItem {
} }
} }
pub trait NameLength {
fn name_length(&self) -> usize;
}
#[derive(Clone)] #[derive(Clone)]
pub struct DirItemEntry { pub struct ItemWithName<I>
pub dir_item: DirItem, where
pub name: Vec<u8>, I: NameLength + Parseable,
{
inner: I,
name: Vec<u8>,
} }
impl DirItemEntry { impl<I> Debug for ItemWithName<I>
pub fn new(dir_item: DirItem, name: Vec<u8>) -> Self { where
Self { dir_item, name } 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<I> ItemWithName<I>
where
I: NameLength + Parseable,
{
pub fn item(&self) -> &I {
&self.inner
}
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)
} }
@ -848,15 +874,19 @@ impl DirItemEntry {
} }
} }
impl core::fmt::Debug for DirItemEntry { impl<I> Parseable for ItemWithName<I>
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { where
f.debug_struct("DirItemEntry") I: NameLength + Parseable,
.field("dir_item", &self.dir_item) {
.field("name", &self.name_as_string_lossy()) fn parse(bytes: &[u8]) -> Result<Self> {
.finish() let inner = I::parse(bytes)?;
let name = bytes[size_of::<I>()..][..inner.name_length()].to_vec();
Ok(Self { inner, name })
} }
} }
pub type DirItemEntry = ItemWithName<DirItem>;
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)] #[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct InodeRef { pub struct InodeRef {
@ -906,6 +936,8 @@ pub enum TreeItem {
Root(RootItem), Root(RootItem),
DirItem(Vec<DirItemEntry>), DirItem(Vec<DirItemEntry>),
DirIndex(DirItemEntry), DirIndex(DirItemEntry),
INodeItem(INodeItem),
INodeRef(INodeRefEntry),
Unimplemented, Unimplemented,
} }
@ -933,13 +965,27 @@ impl From<DirItemEntry> for TreeItem {
} }
} }
impl From<INodeItem> for TreeItem {
fn from(value: INodeItem) -> Self {
Self::INodeItem(value)
}
}
impl From<INodeRefEntry> for TreeItem {
fn from(value: INodeRefEntry) -> Self {
Self::INodeRef(value)
}
}
impl TreeItem { impl TreeItem {
pub fn parse(item: &Item, bytes: &[u8]) -> Result<Self> { pub fn parse(item: &Item, bytes: &[u8]) -> Result<Self> {
Ok(match item.key.ty() { Ok(match item.key.ty() {
ObjectType::RootItem => RootItem::parse(bytes)?.into(), ObjectType::RootItem => RootItem::parse(bytes)?.into(),
ObjectType::ChunkItem => Chunk::parse(bytes)?.into(), ObjectType::ChunkItem => Chunk::parse(bytes)?.into(),
ObjectType::DirItem => DirItem::parse(bytes)?.into(), ObjectType::DirItem => DirItem::parse_all(bytes)?.into(),
ObjectType::DirIndex => DirItem::parse_single(bytes)?.into(), ObjectType::DirIndex => DirItemEntry::parse(bytes)?.into(),
ObjectType::INodeItem => INodeItem::parse(bytes)?.into(),
ObjectType::INodeRef => INodeRefEntry::parse(bytes)?.into(),
_ => TreeItem::Unimplemented, _ => TreeItem::Unimplemented,
}) })
} }

View file

@ -4,7 +4,9 @@ use alloc::collections::btree_map::Entry;
use alloc::{collections::BTreeMap, rc::Rc, vec, vec::Vec}; use alloc::{collections::BTreeMap, rc::Rc, vec, vec::Vec};
use scroll::Pread; 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 crate::{Error, Result};
use super::tree::Tree; use super::tree::Tree;
@ -326,7 +328,7 @@ impl<R: super::Read> Volume2<R> {
.expect("dir item") .expect("dir item")
.first() .first()
.expect("dir item entry") .expect("dir item entry")
.dir_item .item()
.location .location
.id(); .id();