check size of items

This commit is contained in:
Florian Stecker 2024-03-01 13:31:34 -05:00
parent bc852d6b6a
commit b41547ddcb
4 changed files with 84 additions and 80 deletions

View File

@ -12,11 +12,11 @@ pub const NODE_SIZE: usize = 0x4000;
#[derive(Debug,Clone,Copy,AllVariants,PartialEq,Eq,PartialOrd,Ord)] #[derive(Debug,Clone,Copy,AllVariants,PartialEq,Eq,PartialOrd,Ord)]
#[repr(u8)] #[repr(u8)]
pub enum ItemType { pub enum ItemType {
Invalid = 0x00, // invalid Invalid = 0x00, // invalid, but seems to exist?
Inode = 0x01, // implemented Inode = 0x01, // implemented
Ref = 0x0c, // implemented Ref = 0x0c, // implemented
ExtRef = 0x0d, ExtRef = 0x0d,
XAttr = 0x18, XAttr = 0x18, // TODO
VerityDesc = 0x24, VerityDesc = 0x24,
VerityMerkle = 0x25, VerityMerkle = 0x25,
Orphan = 0x30, Orphan = 0x30,
@ -25,12 +25,12 @@ pub enum ItemType {
Dir = 0x54, // implemented (better with len feature; allow multiple?) Dir = 0x54, // implemented (better with len feature; allow multiple?)
DirIndex = 0x60, // implemented DirIndex = 0x60, // implemented
ExtentData = 0x6c, // implemented ExtentData = 0x6c, // implemented
ExtentCsum = 0x80, ExtentCsum = 0x80, // TODO
Root = 0x84, // implemented Root = 0x84, // implemented
RootBackRef = 0x90, RootBackRef = 0x90, // implemented
RootRef = 0x9c, RootRef = 0x9c, // implemented
Extent = 0xa8, // implemented (with only one version of extra data) Extent = 0xa8, // implemented (with only one version of extra data!!)
Metadata = 0xa9, // implemented (with only one version of extra data) Metadata = 0xa9, // implemented (with only one version of extra data!!)
TreeBlockRef = 0xb0, TreeBlockRef = 0xb0,
ExtentDataRef = 0xb2, ExtentDataRef = 0xb2,
ExtentRefV0 = 0xb4, ExtentRefV0 = 0xb4,
@ -53,7 +53,7 @@ pub enum ItemType {
UUIDSubvol = 0xfb, // implemented UUIDSubvol = 0xfb, // implemented
UUIDReceivedSubvol = 0xfc, UUIDReceivedSubvol = 0xfc,
String = 0xfd, String = 0xfd,
InvalidMax = 0xff, InvalidMax = 0xff, // invalid
} }
#[allow(unused)] #[allow(unused)]
@ -309,6 +309,8 @@ pub struct RootItem {
pub otime: Time, pub otime: Time,
pub stime: Time, pub stime: Time,
pub rtime: Time, pub rtime: Time,
data: Vec<u8>,
} }
#[allow(unused)] #[allow(unused)]
@ -320,7 +322,7 @@ pub struct DirItem {
pub name_len: u16, pub name_len: u16,
pub dir_type: u8, pub dir_type: u8,
// #[len = "name_len"] #[len = "name_len"]
pub name: CString, pub name: CString,
} }
@ -406,7 +408,7 @@ pub struct RefItem {
pub index: u64, pub index: u64,
pub name_len: u16, pub name_len: u16,
// #[len = "name_len"] #[len = "name_len"]
pub name: CString, pub name: CString,
} }
@ -416,6 +418,8 @@ pub struct RootRefItem {
pub directory: u64, pub directory: u64,
pub index: u64, pub index: u64,
pub name_len: u16, pub name_len: u16,
#[len = "name_len"]
pub name: CString, pub name: CString,
} }
@ -571,6 +575,14 @@ impl ParseBin for ItemType {
} }
} }
fn parse_check_size<T: ParseBin>(bytes: &[u8]) -> Result<T, ParseError> {
let (result, real_len) = T::parse_len(bytes)?;
if real_len != bytes.len() {
eprintln!("{} parsing incomplete! Parsed {} of {} bytes", std::any::type_name::<T>(), real_len, bytes.len());
}
Ok(result)
}
impl ParseBin for Node { impl ParseBin for Node {
fn parse_len(bytes: &[u8]) -> Result<(Node, usize), ParseError> { fn parse_len(bytes: &[u8]) -> Result<(Node, usize), ParseError> {
@ -604,45 +616,45 @@ impl ParseBin for Node {
let value = match key.key_type { let value = match key.key_type {
ItemType::BlockGroup => ItemType::BlockGroup =>
Value::BlockGroup(BlockGroupItem::parse(data_slice)?), Value::BlockGroup(parse_check_size(data_slice)?),
ItemType::Metadata => { ItemType::Metadata => {
let item = ExtentItem::parse(data_slice)?; let item: ExtentItem = parse_check_size(data_slice)?;
if item.flags != 2 || item.refs > 1 { if item.flags != 2 || item.refs > 1 {
println!("Metadata item with refs = {}, flags = {}, data = {:x?}", item.refs, item.flags, &data_slice[0x18..]); println!("Metadata item with refs = {}, flags = {}, data = {:x?}", item.refs, item.flags, &data_slice[0x18..]);
} }
Value::Extent(item) Value::Extent(item)
}, },
ItemType::Extent => ItemType::Extent =>
Value::Extent(ExtentItem::parse(data_slice)?), Value::Extent(parse_check_size(data_slice)?),
ItemType::Inode => ItemType::Inode =>
Value::Inode(InodeItem::parse(data_slice)?), Value::Inode(parse_check_size(data_slice)?),
ItemType::Root => ItemType::Root =>
Value::Root(RootItem::parse(data_slice)?), Value::Root(parse_check_size(data_slice)?),
ItemType::Dir => ItemType::Dir =>
Value::Dir(DirItem::parse(data_slice)?), Value::Dir(parse_check_size(data_slice)?),
ItemType::DirIndex => ItemType::DirIndex =>
Value::DirIndex(DirItem::parse(data_slice)?), Value::DirIndex(parse_check_size(data_slice)?),
ItemType::Chunk => ItemType::Chunk =>
Value::Chunk(ChunkItem::parse(data_slice)?), Value::Chunk(parse_check_size(data_slice)?),
ItemType::FreeSpaceInfo => ItemType::FreeSpaceInfo =>
Value::FreeSpaceInfo(FreeSpaceInfoItem::parse(data_slice)?), Value::FreeSpaceInfo(parse_check_size(data_slice)?),
ItemType::FreeSpaceExtent => ItemType::FreeSpaceExtent =>
Value::FreeSpaceExtent, Value::FreeSpaceExtent,
ItemType::UUIDSubvol => ItemType::UUIDSubvol =>
Value::UUIDSubvol(UUIDSubvolItem::parse(data_slice)?), Value::UUIDSubvol(parse_check_size(data_slice)?),
ItemType::Dev => ItemType::Dev =>
Value::Dev(DevItem::parse(data_slice)?), Value::Dev(parse_check_size(data_slice)?),
ItemType::DevExtent => ItemType::DevExtent =>
Value::DevExtent(DevExtentItem::parse(data_slice)?), Value::DevExtent(parse_check_size(data_slice)?),
ItemType::ExtentData => ItemType::ExtentData =>
Value::ExtentData(ExtentDataItem::parse(data_slice)?), Value::ExtentData(parse_check_size(data_slice)?),
ItemType::Ref => { ItemType::Ref => {
Value::Ref(RefItem::parse(data_slice)?) Value::Ref(parse_check_size(data_slice)?)
} }
ItemType::RootRef => ItemType::RootRef =>
Value::RootRef(RootRefItem::parse(data_slice)?), Value::RootRef(parse_check_size(data_slice)?),
ItemType::RootBackRef => ItemType::RootBackRef =>
Value::RootRef(RootRefItem::parse(data_slice)?), Value::RootRef(parse_check_size(data_slice)?),
_ => _ =>
Value::Unknown(Vec::from(data_slice)), Value::Unknown(Vec::from(data_slice)),
}; };

View File

@ -1,5 +1,5 @@
use crate::btrfs_structs::{Item, Key, ItemType, Value, ExtentDataBody}; use crate::btrfs_structs::{Item, Key, ItemType, Value, ExtentDataBody};
use crate::render_common::{DebugRender, Hex, size_name}; use crate::render_common::{Hex, size_name};
use maud::{Markup, html, DOCTYPE, PreEscaped}; use maud::{Markup, html, DOCTYPE, PreEscaped};
#[derive(Debug)] #[derive(Debug)]
@ -205,7 +205,7 @@ fn item_value_string(tree_id: u64, item: &Item) -> Markup {
} }
} }
fn item_details_string(tree_id: u64, item: &Item) -> Markup { fn item_details_string(_tree_id: u64, item: &Item) -> Markup {
match &item.value { match &item.value {
Value::Inode(inode_item) => { Value::Inode(inode_item) => {
html! { table { tbody { html! { table { tbody {
@ -222,7 +222,7 @@ fn item_details_string(tree_id: u64, item: &Item) -> Markup {
}, },
Value::ExtentData(extent_item) => { Value::ExtentData(extent_item) => {
match &extent_item.data { match &extent_item.data {
ExtentDataBody::Inline(data) => { ExtentDataBody::Inline(_data) => {
html! {} // we really want data as string / hex html! {} // we really want data as string / hex
}, },
ExtentDataBody::External(ext_extent) => { ExtentDataBody::External(ext_extent) => {
@ -255,7 +255,6 @@ fn item_details_string(tree_id: u64, item: &Item) -> Markup {
}}} }}}
}, },
Value::Root(root_item) => { Value::Root(root_item) => {
let inode = &root_item.inode;
html! { table { tbody { html! { table { tbody {
tr { td { "root dir id" } td { (format!("{:X}", root_item.root_dirid)) } } tr { td { "root dir id" } td { (format!("{:X}", root_item.root_dirid)) } }
tr { td { "logical address" } td { (format!("{:X}", root_item.bytenr)) } } tr { td { "logical address" } td { (format!("{:X}", root_item.bytenr)) } }

View File

@ -1,7 +1,7 @@
use std::{ use std::{
collections::HashMap, env, fs::{File, OpenOptions}, iter, collections::HashMap, env, fs::{File, OpenOptions}, iter,
}; };
use memmap2::{Mmap, MmapOptions}; use memmap2::MmapOptions;
use rouille::{Request, Response, router}; use rouille::{Request, Response, router};
use btrfs_explorer::{ use btrfs_explorer::{
btrfs_structs::{TreeID, Value::Extent, Value::BlockGroup, NODE_SIZE, ItemType}, btrfs_structs::{TreeID, Value::Extent, Value::BlockGroup, NODE_SIZE, ItemType},
@ -20,7 +20,6 @@ fn main() -> Result<(), MainError> {
let image = unsafe { Mmap::map(&file)? }; let image = unsafe { Mmap::map(&file)? };
*/ */
const O_DIRECT: i32 = 0x4000;
let file = OpenOptions::new().read(true).open(filename)?; let file = OpenOptions::new().read(true).open(filename)?;
let image = unsafe { MmapOptions::new().len(493921239040usize).map(&file)? }; let image = unsafe { MmapOptions::new().len(493921239040usize).map(&file)? };

View File

@ -1,9 +1,4 @@
extern crate proc_macro; use quote::{quote, format_ident};
extern crate syn;
#[macro_use]
extern crate quote;
extern crate proc_macro2;
use proc_macro2::Span; use proc_macro2::Span;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use syn::{DeriveInput, Data::Enum, parse_macro_input}; use syn::{DeriveInput, Data::Enum, parse_macro_input};
@ -40,7 +35,7 @@ pub fn derive_parse_bin(input: TokenStream) -> TokenStream {
syn::Data::Struct(struct_item) => { syn::Data::Struct(struct_item) => {
match struct_item.fields { match struct_item.fields {
syn::Fields::Named(fields_named) => { syn::Fields::Named(fields_named) => {
return derive_parse_bin_struct(&name, &fields_named.named); derive_parse_bin_struct(&name, &fields_named.named)
}, },
syn::Fields::Unnamed(fields_unnamed) => { syn::Fields::Unnamed(fields_unnamed) => {
if fields_unnamed.unnamed.len() != 1 { if fields_unnamed.unnamed.len() != 1 {
@ -48,13 +43,13 @@ pub fn derive_parse_bin(input: TokenStream) -> TokenStream {
} }
let inner_type = fields_unnamed.unnamed.into_iter().next().unwrap().ty; let inner_type = fields_unnamed.unnamed.into_iter().next().unwrap().ty;
return derive_parse_bin_alias(name, inner_type); derive_parse_bin_alias(name, inner_type)
}, },
_ => panic!("ParseBin on unit structs makes no sense!"), _ => panic!("ParseBin on unit structs makes no sense!"),
} }
}, },
_ => panic!("ParseBin only works on structs so far!"), _ => panic!("ParseBin only works on structs so far!"),
}; }
} }
fn derive_parse_bin_alias(name: syn::Ident, ty: syn::Type) -> TokenStream { fn derive_parse_bin_alias(name: syn::Ident, ty: syn::Type) -> TokenStream {
@ -113,49 +108,48 @@ fn derive_parse_bin_struct<'a, T>(name: &syn::Ident, fields: T) -> TokenStream
if let Some(varname) = veclen { if let Some(varname) = veclen {
let field_name_item = format_ident!("{}_item", field_name); let field_name_item = format_ident!("{}_item", field_name);
let field_type_item: &syn::Type = match &field_type { enum FieldType<'a> {
syn::Type::Path(tp) => { Vec(&'a syn::Type),
match tp.path.segments.iter().next() { CString,
Some(single) => { }
if &single.ident.to_string() == "Vec" {
match &single.arguments { let syn::Type::Path(tp) = &field_type else { panic!() };
syn::PathArguments::AngleBracketed(args) => { let single = tp.path.segments.iter().next().unwrap();
match args.args.iter().next() {
Some(firstarg) => { let field_type = if &single.ident.to_string() == "Vec" {
match firstarg { let syn::PathArguments::AngleBracketed(args) = &single.arguments else { panic!() };
syn::GenericArgument::Type(ty) => { let firstarg = args.args.iter().next().unwrap();
Some(ty) let syn::GenericArgument::Type(ty) = firstarg else { panic!() };
}, FieldType::Vec(ty)
_ => None, } else if &single.ident.to_string() == "CString" {
} FieldType::CString
}, } else {
None => None, panic!("The len attribute is only allowed on Vec<_> or CString")
} };
},
_ => None,
}
} else {
None
}
},
None => None,
}
},
_ => None,
}.expect("The len attribute is only allowed on type Vec<_>");
let varname_ident = syn::Ident::new(&varname, Span::call_site()); let varname_ident = syn::Ident::new(&varname, Span::call_site());
parsing_statements.push(quote!{
let mut #field_name = Vec::new();
for i in 0 .. #varname_ident.0 as usize {
let #field_name_item = <#field_type_item>::parse_len(&bytes[__parse_bin_derive_size..])?;
__parse_bin_derive_size += #field_name_item.1;
#field_name.push(#field_name_item.0);
}
});
combining_expressions.push(quote!(#field_name: #field_name)); match field_type {
FieldType::Vec(field_type_item) => {
parsing_statements.push(quote!{
let mut #field_name = Vec::new();
for i in 0 .. #varname_ident.0 as usize {
let #field_name_item = <#field_type_item>::parse_len(&bytes[__parse_bin_derive_size..])?;
__parse_bin_derive_size += #field_name_item.1;
#field_name.push(#field_name_item.0);
}
});
combining_expressions.push(quote!(#field_name: #field_name));
},
FieldType::CString => {
parsing_statements.push(quote!{
let #field_name = CString::parse_len(&bytes[__parse_bin_derive_size .. __parse_bin_derive_size + #varname_ident.0 as usize])?;
__parse_bin_derive_size += #varname_ident.0 as usize;
});
combining_expressions.push(quote!(#field_name: #field_name.0));
},
}
} else { } else {
parsing_statements.push(quote!{ parsing_statements.push(quote!{
let #field_name = <#field_type>::parse_len(&bytes[__parse_bin_derive_size..])?; let #field_name = <#field_type>::parse_len(&bytes[__parse_bin_derive_size..])?;