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")
.first()
.expect("dir item entry")
.dir_item
.item()
.location
.id();

View file

@ -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<Self>;
}
macro_rules! impl_parse_try_from_ctx {
($($ty:ty),*) => {
$(impl $ty {
pub fn parse(bytes: &[u8]) -> Result<Self> {
$(impl Parseable for $ty {
fn parse(bytes: &[u8]) -> Result<Self> {
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<LE>,
name_len: U16<LE>,
}
impl NameLength for INodeRef {
fn name_length(&self) -> usize {
self.name_len.get() as usize
}
}
pub type INodeRefEntry = ItemWithName<INodeRef>;
#[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<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>> {
pub fn parse_all(bytes: &[u8]) -> Result<Vec<DirItemEntry>> {
let offset = &mut 0;
let entries = core::iter::from_fn(|| {
if *offset + size_of::<DirItem>() < bytes.len() {
Some(Self::parse_single_inner(&bytes[*offset..], offset))
if *offset + size_of::<Self>() < bytes.len() {
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 {
None
}
@ -829,17 +837,35 @@ impl DirItem {
}
}
#[derive(Clone)]
pub struct DirItemEntry {
pub dir_item: DirItem,
pub name: Vec<u8>,
pub trait NameLength {
fn name_length(&self) -> usize;
}
impl DirItemEntry {
pub fn new(dir_item: DirItem, name: Vec<u8>) -> Self {
Self { dir_item, name }
}
#[derive(Clone)]
pub struct ItemWithName<I>
where
I: NameLength + Parseable,
{
inner: I,
name: Vec<u8>,
}
impl<I> Debug for ItemWithName<I>
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<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> {
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<I> Parseable for ItemWithName<I>
where
I: NameLength + Parseable,
{
fn parse(bytes: &[u8]) -> Result<Self> {
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)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct InodeRef {
@ -906,6 +936,8 @@ pub enum TreeItem {
Root(RootItem),
DirItem(Vec<DirItemEntry>),
DirIndex(DirItemEntry),
INodeItem(INodeItem),
INodeRef(INodeRefEntry),
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 {
pub fn parse(item: &Item, bytes: &[u8]) -> Result<Self> {
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,
})
}

View file

@ -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<R: super::Read> Volume2<R> {
.expect("dir item")
.first()
.expect("dir item entry")
.dir_item
.item()
.location
.id();