diff --git a/btrfs_explorer/src/btrfs_structs.rs b/btrfs_explorer/src/btrfs_structs.rs index 9131f72..27d33cb 100644 --- a/btrfs_explorer/src/btrfs_structs.rs +++ b/btrfs_explorer/src/btrfs_structs.rs @@ -12,11 +12,11 @@ pub const NODE_SIZE: usize = 0x4000; #[derive(Debug,Clone,Copy,AllVariants,PartialEq,Eq,PartialOrd,Ord)] #[repr(u8)] pub enum ItemType { - Invalid = 0x00, // invalid + Invalid = 0x00, // invalid, but seems to exist? Inode = 0x01, // implemented Ref = 0x0c, // implemented ExtRef = 0x0d, - XAttr = 0x18, + XAttr = 0x18, // TODO VerityDesc = 0x24, VerityMerkle = 0x25, Orphan = 0x30, @@ -25,12 +25,12 @@ pub enum ItemType { Dir = 0x54, // implemented (better with len feature; allow multiple?) DirIndex = 0x60, // implemented ExtentData = 0x6c, // implemented - ExtentCsum = 0x80, + ExtentCsum = 0x80, // TODO Root = 0x84, // implemented - RootBackRef = 0x90, - RootRef = 0x9c, - Extent = 0xa8, // implemented (with only one version of extra data) - Metadata = 0xa9, // implemented (with only one version of extra data) + RootBackRef = 0x90, // implemented + RootRef = 0x9c, // implemented + Extent = 0xa8, // implemented (with only one version of extra data!!) + Metadata = 0xa9, // implemented (with only one version of extra data!!) TreeBlockRef = 0xb0, ExtentDataRef = 0xb2, ExtentRefV0 = 0xb4, @@ -53,7 +53,7 @@ pub enum ItemType { UUIDSubvol = 0xfb, // implemented UUIDReceivedSubvol = 0xfc, String = 0xfd, - InvalidMax = 0xff, + InvalidMax = 0xff, // invalid } #[allow(unused)] @@ -309,6 +309,8 @@ pub struct RootItem { pub otime: Time, pub stime: Time, pub rtime: Time, + + data: Vec, } #[allow(unused)] @@ -320,7 +322,7 @@ pub struct DirItem { pub name_len: u16, pub dir_type: u8, -// #[len = "name_len"] + #[len = "name_len"] pub name: CString, } @@ -406,7 +408,7 @@ pub struct RefItem { pub index: u64, pub name_len: u16, - // #[len = "name_len"] + #[len = "name_len"] pub name: CString, } @@ -416,6 +418,8 @@ pub struct RootRefItem { pub directory: u64, pub index: u64, pub name_len: u16, + + #[len = "name_len"] pub name: CString, } @@ -571,6 +575,14 @@ impl ParseBin for ItemType { } } +fn parse_check_size(bytes: &[u8]) -> Result { + let (result, real_len) = T::parse_len(bytes)?; + if real_len != bytes.len() { + eprintln!("{} parsing incomplete! Parsed {} of {} bytes", std::any::type_name::(), real_len, bytes.len()); + } + Ok(result) +} + impl ParseBin for Node { fn parse_len(bytes: &[u8]) -> Result<(Node, usize), ParseError> { @@ -604,45 +616,45 @@ impl ParseBin for Node { let value = match key.key_type { ItemType::BlockGroup => - Value::BlockGroup(BlockGroupItem::parse(data_slice)?), + Value::BlockGroup(parse_check_size(data_slice)?), ItemType::Metadata => { - let item = ExtentItem::parse(data_slice)?; + let item: ExtentItem = parse_check_size(data_slice)?; if item.flags != 2 || item.refs > 1 { println!("Metadata item with refs = {}, flags = {}, data = {:x?}", item.refs, item.flags, &data_slice[0x18..]); } Value::Extent(item) }, ItemType::Extent => - Value::Extent(ExtentItem::parse(data_slice)?), + Value::Extent(parse_check_size(data_slice)?), ItemType::Inode => - Value::Inode(InodeItem::parse(data_slice)?), + Value::Inode(parse_check_size(data_slice)?), ItemType::Root => - Value::Root(RootItem::parse(data_slice)?), + Value::Root(parse_check_size(data_slice)?), ItemType::Dir => - Value::Dir(DirItem::parse(data_slice)?), + Value::Dir(parse_check_size(data_slice)?), ItemType::DirIndex => - Value::DirIndex(DirItem::parse(data_slice)?), + Value::DirIndex(parse_check_size(data_slice)?), ItemType::Chunk => - Value::Chunk(ChunkItem::parse(data_slice)?), + Value::Chunk(parse_check_size(data_slice)?), ItemType::FreeSpaceInfo => - Value::FreeSpaceInfo(FreeSpaceInfoItem::parse(data_slice)?), + Value::FreeSpaceInfo(parse_check_size(data_slice)?), ItemType::FreeSpaceExtent => Value::FreeSpaceExtent, ItemType::UUIDSubvol => - Value::UUIDSubvol(UUIDSubvolItem::parse(data_slice)?), + Value::UUIDSubvol(parse_check_size(data_slice)?), ItemType::Dev => - Value::Dev(DevItem::parse(data_slice)?), + Value::Dev(parse_check_size(data_slice)?), ItemType::DevExtent => - Value::DevExtent(DevExtentItem::parse(data_slice)?), + Value::DevExtent(parse_check_size(data_slice)?), ItemType::ExtentData => - Value::ExtentData(ExtentDataItem::parse(data_slice)?), + Value::ExtentData(parse_check_size(data_slice)?), ItemType::Ref => { - Value::Ref(RefItem::parse(data_slice)?) + Value::Ref(parse_check_size(data_slice)?) } ItemType::RootRef => - Value::RootRef(RootRefItem::parse(data_slice)?), + Value::RootRef(parse_check_size(data_slice)?), ItemType::RootBackRef => - Value::RootRef(RootRefItem::parse(data_slice)?), + Value::RootRef(parse_check_size(data_slice)?), _ => Value::Unknown(Vec::from(data_slice)), }; diff --git a/btrfs_explorer/src/render_tree.rs b/btrfs_explorer/src/render_tree.rs index 7d725c7..ad9f166 100644 --- a/btrfs_explorer/src/render_tree.rs +++ b/btrfs_explorer/src/render_tree.rs @@ -1,5 +1,5 @@ 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}; #[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 { Value::Inode(inode_item) => { html! { table { tbody { @@ -222,7 +222,7 @@ fn item_details_string(tree_id: u64, item: &Item) -> Markup { }, Value::ExtentData(extent_item) => { match &extent_item.data { - ExtentDataBody::Inline(data) => { + ExtentDataBody::Inline(_data) => { html! {} // we really want data as string / hex }, ExtentDataBody::External(ext_extent) => { @@ -255,7 +255,6 @@ fn item_details_string(tree_id: u64, item: &Item) -> Markup { }}} }, Value::Root(root_item) => { - let inode = &root_item.inode; html! { table { tbody { tr { td { "root dir id" } td { (format!("{:X}", root_item.root_dirid)) } } tr { td { "logical address" } td { (format!("{:X}", root_item.bytenr)) } } diff --git a/btrfs_explorer_bin/src/main.rs b/btrfs_explorer_bin/src/main.rs index 4e63ae0..f6c0041 100644 --- a/btrfs_explorer_bin/src/main.rs +++ b/btrfs_explorer_bin/src/main.rs @@ -1,7 +1,7 @@ use std::{ collections::HashMap, env, fs::{File, OpenOptions}, iter, }; -use memmap2::{Mmap, MmapOptions}; +use memmap2::MmapOptions; use rouille::{Request, Response, router}; use btrfs_explorer::{ btrfs_structs::{TreeID, Value::Extent, Value::BlockGroup, NODE_SIZE, ItemType}, @@ -20,7 +20,6 @@ fn main() -> Result<(), MainError> { let image = unsafe { Mmap::map(&file)? }; */ - const O_DIRECT: i32 = 0x4000; let file = OpenOptions::new().read(true).open(filename)?; let image = unsafe { MmapOptions::new().len(493921239040usize).map(&file)? }; diff --git a/btrfs_parse_derive/src/lib.rs b/btrfs_parse_derive/src/lib.rs index c16a15e..f7bfb20 100644 --- a/btrfs_parse_derive/src/lib.rs +++ b/btrfs_parse_derive/src/lib.rs @@ -1,9 +1,4 @@ -extern crate proc_macro; -extern crate syn; -#[macro_use] -extern crate quote; -extern crate proc_macro2; - +use quote::{quote, format_ident}; use proc_macro2::Span; use proc_macro::TokenStream; 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) => { match struct_item.fields { 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) => { 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; - 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 only works on structs so far!"), - }; + } } 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 { let field_name_item = format_ident!("{}_item", field_name); - let field_type_item: &syn::Type = match &field_type { - syn::Type::Path(tp) => { - match tp.path.segments.iter().next() { - Some(single) => { - if &single.ident.to_string() == "Vec" { - match &single.arguments { - syn::PathArguments::AngleBracketed(args) => { - match args.args.iter().next() { - Some(firstarg) => { - match firstarg { - syn::GenericArgument::Type(ty) => { - Some(ty) - }, - _ => None, - } - }, - None => None, - } - }, - _ => None, - } - } else { - None - } - }, - None => None, - } - }, - _ => None, - }.expect("The len attribute is only allowed on type Vec<_>"); + enum FieldType<'a> { + Vec(&'a syn::Type), + CString, + } + + let syn::Type::Path(tp) = &field_type else { panic!() }; + let single = tp.path.segments.iter().next().unwrap(); + + let field_type = if &single.ident.to_string() == "Vec" { + let syn::PathArguments::AngleBracketed(args) = &single.arguments else { panic!() }; + let firstarg = args.args.iter().next().unwrap(); + let syn::GenericArgument::Type(ty) = firstarg else { panic!() }; + FieldType::Vec(ty) + } else if &single.ident.to_string() == "CString" { + FieldType::CString + } else { + panic!("The len attribute is only allowed on Vec<_> or CString") + }; 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 { parsing_statements.push(quote!{ let #field_name = <#field_type>::parse_len(&bytes[__parse_bin_derive_size..])?;