diff --git a/btrfs_explorer/src/btrfs_lookup.rs b/btrfs_explorer/src/btrfs_lookup.rs index 7c52f3e..5eaf24f 100644 --- a/btrfs_explorer/src/btrfs_lookup.rs +++ b/btrfs_explorer/src/btrfs_lookup.rs @@ -2,7 +2,7 @@ use std::convert::identity; use std::rc::Rc; use std::ops::{Deref, RangeBounds, Bound}; -use crate::btrfs_structs::{Leaf, Key, Item, InteriorNode, Node, ParseError, ParseBin, Value, Superblock, ItemType, ZERO_KEY, LAST_KEY}; +use crate::btrfs_structs::{DirItem, InteriorNode, Item, ItemType, Key, Leaf, Node, ParseBin, ParseError, Superblock, Value, LAST_KEY, ZERO_KEY}; use crate::nodereader::NodeReader; /// Represents a B-Tree inside a filesystem image. Can be used to look up keys, @@ -32,7 +32,7 @@ impl<'a> Tree<'a> { .filter(|x| x.key.key_id == tree_id && x.key.key_type == ItemType::Root); let root_addr_log = match tree_root_item { - Some(Item { key: _, value: Value::Root(root)}) => root.bytenr, + Some(Item { key: _, range: _, value: Value::Root(root)}) => root.bytenr, _ => return Err("root item not found or invalid".into()) }; @@ -119,7 +119,7 @@ impl Tree<'_> { /***** iterator *****/ -pub struct RangeIter<'a, 'b> { +pub struct RangeIterWithAddr<'a, 'b> { tree: &'b Tree<'a>, start: Bound, @@ -128,6 +128,8 @@ pub struct RangeIter<'a, 'b> { backward_skip_fn: Box Key>, } +pub struct RangeIter<'a, 'b> (RangeIterWithAddr<'a, 'b>); + impl<'a> Tree<'a> { /// Given a tree, a range of indices, and two "skip functions", produces a double /// ended iterator which iterates through the keys contained in the range, in ascending @@ -144,33 +146,36 @@ impl<'a> Tree<'a> { R: RangeBounds, F1: Fn(Key) -> Key + 'static, F2: Fn(Key) -> Key + 'static { - RangeIter { + RangeIter(RangeIterWithAddr { tree: self, start: range.start_bound().cloned(), end: range.end_bound().cloned(), forward_skip_fn: Box::new(forward_skip_fn), backward_skip_fn: Box::new(backward_skip_fn), - } + }) } - pub fn range<'b, R: RangeBounds>(&'b self, range: R) -> RangeIter<'a, 'b> { - RangeIter { - tree: self, - start: range.start_bound().cloned(), - end: range.end_bound().cloned(), - forward_skip_fn: Box::new(identity), - backward_skip_fn: Box::new(identity), - } - } - - pub fn iter<'b>(&'b self) -> RangeIter<'a, 'b> { - RangeIter { + RangeIter(RangeIterWithAddr { tree: self, start: Bound::Unbounded, end: Bound::Unbounded, forward_skip_fn: Box::new(identity), backward_skip_fn: Box::new(identity), + }) + } + + pub fn range<'b, R: RangeBounds>(&'b self, range: R) -> RangeIter<'a, 'b> { + RangeIter(self.range_with_node_addr(range)) + } + + pub fn range_with_node_addr<'b, R: RangeBounds>(&'b self, range: R) -> RangeIterWithAddr<'a, 'b> { + RangeIterWithAddr { + tree: self, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + forward_skip_fn: Box::new(identity), + backward_skip_fn: Box::new(identity), } } } @@ -178,19 +183,19 @@ impl<'a> Tree<'a> { /// Get the first item under the node at logical address `addr`. /// This function panics if there are no items -fn get_first_item(tree: &Tree, addr: u64) -> Result { +fn get_first_item(tree: &Tree, addr: u64) -> Result<(Item, u64), ParseError> { match tree.reader.get_node(addr)?.deref() { Node::Interior(intnode) => get_first_item(tree, intnode.children[0].ptr), - Node::Leaf(leafnode) => Ok(leafnode.items[0].clone()), + Node::Leaf(leafnode) => Ok((leafnode.items[0].clone(), addr)), } } /// Get the last item under the node at logical address `addr`. /// This function panics if there are no items -fn get_last_item(tree: &Tree, addr: u64) -> Result { +fn get_last_item(tree: &Tree, addr: u64) -> Result<(Item, u64), ParseError> { match tree.reader.get_node(addr)?.deref() { Node::Interior(intnode) => get_last_item(tree, intnode.children.last().unwrap().ptr), - Node::Leaf(leafnode) => Ok(leafnode.items.last().unwrap().clone()), + Node::Leaf(leafnode) => Ok((leafnode.items.last().unwrap().clone(), addr)), } } @@ -201,7 +206,7 @@ enum FindKeyMode {LT, GT, GE, LE} /// the "closest" match. The exact meaning of "closest" is given by the `mode` argument: /// If `mode` is `LT`/`GT`/`GE`/`LE`, return the item with the greatest / least / greatest / least /// key less than / greater than / greater or equal to / less or equal to `key`. -fn find_closest_key(tree: &Tree, key: Key, mode: FindKeyMode) -> Result, ParseError> { +fn find_closest_key(tree: &Tree, key: Key, mode: FindKeyMode) -> Result, ParseError> { // in some cases, this task can't be accomplished by a single traversal // but we might have to go back up the tree; prev/next allows to quickly go back to the right node @@ -244,14 +249,14 @@ fn find_closest_key(tree: &Tree, key: Key, mode: FindKeyMode) -> Result { // the standard case, we found a key `k` with the guarantee that `k <= key` - let Item {key: k, value: v} = leafnode.items[idx].clone(); + let it = leafnode.items[idx].clone(); - if mode == FindKeyMode::LE || mode == FindKeyMode::LT && k < key || mode == FindKeyMode::GE && k == key { - return Ok(Some(Item {key: k, value: v})) - } else if mode == FindKeyMode::LT && k == key { + if mode == FindKeyMode::LE || mode == FindKeyMode::LT && it.key < key || mode == FindKeyMode::GE && it.key == key { + return Ok(Some((it, current))) + } else if mode == FindKeyMode::LT && it.key == key { // prev if idx > 0 { - return Ok(Some(leafnode.items[idx-1].clone())); + return Ok(Some((leafnode.items[idx-1].clone(), current))); } else { // use prev if let Some(addr) = prev { @@ -263,7 +268,7 @@ fn find_closest_key(tree: &Tree, key: Key, mode: FindKeyMode) -> Result Result(start: Bound, end: Bound) -> bool { } } -impl<'a, 'b> Iterator for RangeIter<'a, 'b> { - type Item = Item; +impl<'a, 'b> Iterator for RangeIterWithAddr<'a, 'b> { + type Item = (Item, u64); - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { if !range_valid(self.start.as_ref(), self.end.as_ref()) { return None; } @@ -317,11 +322,11 @@ impl<'a, 'b> Iterator for RangeIter<'a, 'b> { let result = find_closest_key(self.tree, start_key, mode) .expect("file system should be consistent (or this is a bug)"); - if let Some(item) = &result { + if let Some((item, _)) = &result { self.start = Bound::Excluded((self.forward_skip_fn)(item.key)); } - let end_filter = |item: &Item| { + let end_filter = |(item, _): &(Item, u64)| { match &self.end { &Bound::Included(x) => item.key <= x, &Bound::Excluded(x) => item.key < x, @@ -335,8 +340,8 @@ impl<'a, 'b> Iterator for RangeIter<'a, 'b> { } } -impl<'a, 'b> DoubleEndedIterator for RangeIter<'a, 'b> { - fn next_back(&mut self) -> Option { +impl<'a, 'b> DoubleEndedIterator for RangeIterWithAddr<'a, 'b> { + fn next_back(&mut self) -> Option { if !range_valid(self.start.as_ref(), self.end.as_ref()) { return None; } @@ -350,11 +355,11 @@ impl<'a, 'b> DoubleEndedIterator for RangeIter<'a, 'b> { let result = find_closest_key(self.tree, start_key, mode) .expect("file system should be consistent (or this is a bug)"); - if let Some(item) = &result { + if let Some((item,_)) = &result { self.end = Bound::Excluded((self.backward_skip_fn)(item.key)); } - let start_filter = |item: &Item| { + let start_filter = |(item, _): &(Item, u64)| { match &self.start { &Bound::Included(x) => item.key >= x, &Bound::Excluded(x) => item.key > x, @@ -367,3 +372,17 @@ impl<'a, 'b> DoubleEndedIterator for RangeIter<'a, 'b> { .map(|item|item.clone()) } } + +impl<'a, 'b> Iterator for RangeIter<'a, 'b> { + type Item = Item; + + fn next(&mut self) -> Option { + self.0.next().map(|x|x.0) + } +} + +impl<'a, 'b> DoubleEndedIterator for RangeIter<'a, 'b> { + fn next_back(&mut self) -> Option { + self.0.next_back().map(|x|x.0) + } +} diff --git a/btrfs_explorer/src/btrfs_structs.rs b/btrfs_explorer/src/btrfs_structs.rs index 27d33cb..c394831 100644 --- a/btrfs_explorer/src/btrfs_structs.rs +++ b/btrfs_explorer/src/btrfs_structs.rs @@ -1,9 +1,11 @@ use btrfs_parse_derive::AllVariants; use btrfs_parse_derive::ParseBin; +use std::any::TypeId; use std::fmt; use std::error; use std::ffi::CString; + /***** BTRFS structures *****/ pub const NODE_SIZE: usize = 0x4000; @@ -29,13 +31,13 @@ pub enum ItemType { Root = 0x84, // implemented 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, + Extent = 0xa8, // implemented + Metadata = 0xa9, // implemented + TreeBlockRef = 0xb0, // implemented (inside ExtentItem) + ExtentDataRef = 0xb2, // implemented (inside ExtentItem) ExtentRefV0 = 0xb4, - SharedBlockRef = 0xb6, - SharedDataRef = 0xb8, + SharedBlockRef = 0xb6, // implemented (inside ExtentItem) + SharedDataRef = 0xb8, // implemented (inside ExtentItem) BlockGroup = 0xc0, // implemented FreeSpaceInfo = 0xc6, // implemented FreeSpaceExtent = 0xc7, // implemented @@ -93,8 +95,8 @@ pub enum Value { Dev(DevItem), DevExtent(DevExtentItem), ExtentData(ExtentDataItem), - Ref(RefItem), - RootRef(RootRefItem), + Ref(Vec), + RootRef(Vec), Unknown(Vec), } @@ -103,6 +105,7 @@ pub enum Value { #[derive(Debug,Clone)] pub struct Item { pub key: Key, + pub range: (u32, u32), // start and end offset within node pub value: Value, } @@ -214,7 +217,7 @@ pub struct BlockGroupItem { } #[allow(unused)] -#[derive(Debug,Clone)] +#[derive(Debug,Clone,ParseBin)] pub struct ExtentItem { pub refs: u64, pub generation: u64, @@ -222,9 +225,16 @@ pub struct ExtentItem { // pub data: Vec, // this is only correct if flags == 2, fix later! - pub block_refs: Vec<(ItemType, u64)>, -// pub tree_block_key_type: ItemType, -// pub tree_block_key_id: u64, + pub block_refs: Vec, +} + +#[allow(unused)] +#[derive(Debug,Clone)] +pub enum BlockRef { + Tree { id: u64, }, + ExtentData { root: u64, id: u64, offset: u64, count: u32, }, + SharedData { offset: u64, count: u32, }, + SharedBlockRef { offset: u64 }, } #[allow(unused)] @@ -534,12 +544,31 @@ impl ParseBin for [u8; N] { } // we use Vec for "unknown extra data", so just eat up everything + impl ParseBin for Vec { fn parse_len(bytes: &[u8]) -> Result<(Self, usize), ParseError> { Ok((Vec::from(bytes), bytes.len())) } } +trait ParseBinVecFallback: ParseBin { } +impl ParseBinVecFallback for BlockRef { } + +impl ParseBin for Vec { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), ParseError> { + let mut result: Vec = Vec::new(); + let mut offset: usize = 0; + + while offset < bytes.len() { + let (item, len) = ::parse_len(&bytes[offset..])?; + result.push(item); + offset += len; + } + + Ok((result, bytes.len())) + } +} + impl ParseBin for CString { fn parse_len(bytes: &[u8]) -> Result<(Self, usize), ParseError> { let mut chars = Vec::from(bytes); @@ -617,14 +646,7 @@ impl ParseBin for Node { let value = match key.key_type { ItemType::BlockGroup => Value::BlockGroup(parse_check_size(data_slice)?), - ItemType::Metadata => { - 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 => + ItemType::Extent | ItemType::Metadata => Value::Extent(parse_check_size(data_slice)?), ItemType::Inode => Value::Inode(parse_check_size(data_slice)?), @@ -649,17 +671,32 @@ impl ParseBin for Node { ItemType::ExtentData => Value::ExtentData(parse_check_size(data_slice)?), ItemType::Ref => { - Value::Ref(parse_check_size(data_slice)?) + let mut result: Vec = vec![]; + let mut item_offset = 0; + + while item_offset < data_slice.len() { + let (item, len) = RefItem::parse_len(&data_slice[item_offset..])?; + result.push(item); + item_offset += len; + } + Value::Ref(result) } - ItemType::RootRef => - Value::RootRef(parse_check_size(data_slice)?), - ItemType::RootBackRef => - Value::RootRef(parse_check_size(data_slice)?), + ItemType::RootRef | ItemType::RootBackRef => { + let mut result: Vec = vec![]; + let mut item_offset = 0; + + while item_offset < data_slice.len() { + let (item, len) = RootRefItem::parse_len(&data_slice[item_offset..])?; + result.push(item); + item_offset += len; + } + Value::RootRef(result) + }, _ => Value::Unknown(Vec::from(data_slice)), }; - items.push(Item { key, value }); + items.push(Item { key, range: (0x65 + offset, 0x65 + offset + size), value }); } Ok((Node::Leaf(Leaf { header, items }), NODE_SIZE)) @@ -691,18 +728,19 @@ impl ParseBin for ExtentDataItem { let (header, header_size) = ExtentDataHeader::parse_len(bytes)?; if header.extent_type == 1 { // external extent let (body, body_size) = ExternalExtent::parse_len(&bytes[header_size..])?; - return Ok((ExtentDataItem { header: header, data: ExtentDataBody::External(body)}, + Ok((ExtentDataItem { header, data: ExtentDataBody::External(body)}, header_size + body_size)) } else { // inline extent let data_slice = &bytes[header_size..]; - return Ok((ExtentDataItem { - header: header, + Ok((ExtentDataItem { + header, data: ExtentDataBody::Inline(Vec::from(data_slice)) }, header_size + data_slice.len())) } } } +/* impl ParseBin for ExtentItem { fn parse_len(bytes: &[u8]) -> Result<(Self, usize), ParseError> { let refs = u64::parse(bytes)?; @@ -722,6 +760,35 @@ impl ParseBin for ExtentItem { Ok((ExtentItem { refs, generation, flags, block_refs }, 0x18 + refs as usize * 0x09)) } } +*/ + +impl ParseBin for BlockRef { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), ParseError> { + match ItemType::parse(bytes)? { + ItemType::ExtentDataRef => { + let root = u64::parse(&bytes[0x01..])?; + let id = u64::parse(&bytes[0x09..])?; + let offset = u64::parse(&bytes[0x11..])?; + let count = u32::parse(&bytes[0x19..])?; + Ok((BlockRef::ExtentData { root, id, offset, count }, 0x1d)) + }, + ItemType::SharedDataRef => { + let offset = u64::parse(&bytes[0x01..])?; + let count = u32::parse(&bytes[0x09..])?; + Ok((BlockRef::SharedData { offset, count }, 0x0d)) + }, + ItemType::TreeBlockRef => { + let id = u64::parse(&bytes[0x01..])?; + Ok((BlockRef::Tree { id }, 0x09)) + }, + ItemType::SharedBlockRef => { + let offset = u64::parse(&bytes[0x01..])?; + Ok((BlockRef::SharedBlockRef { offset }, 0x09)) + } + x => err!("unknown block ref type: {:?}", x) + } + } +} /***** prettier debug output for UUIDs and checksums *****/ diff --git a/btrfs_explorer/src/http_tree.rs b/btrfs_explorer/src/http_tree.rs index a344bcb..145e43f 100644 --- a/btrfs_explorer/src/http_tree.rs +++ b/btrfs_explorer/src/http_tree.rs @@ -1,4 +1,6 @@ -use std::str::FromStr; +use std::{ + str::FromStr, +}; use rouille::{Request, Response}; use crate::{ btrfs_structs::{ItemType, Item, Key, ZERO_KEY, LAST_KEY}, @@ -18,41 +20,45 @@ enum TreeDisplayMode { fn http_tree_internal(tree: &Tree, tree_id: u64, mode: TreeDisplayMode) -> Response { - let mut items: Vec; + let mut items: Vec<(Item, u64)>; let mut highlighted_key_id: Option = None; match mode { TreeDisplayMode::Highlight(key_id, before, after) => { let key = Key {key_id, key_type: ItemType::Invalid, key_offset: 0 }; - items = tree.range(..key).rev().take(before).collect(); + items = tree.range_with_node_addr(..key).rev().take(before).collect(); items.reverse(); - items.extend(tree.range(key..).take(after)); + items.extend(tree.range_with_node_addr(key..).take(after)); highlighted_key_id = Some(key_id); }, TreeDisplayMode::From(key, num_lines) => { - items = tree.range(key..).take(num_lines).collect(); + items = tree.range_with_node_addr(key..).take(num_lines).collect(); if items.len() < num_lines { items.reverse(); - items.extend(tree.range(..key).rev().take(num_lines - items.len())); + items.extend(tree.range_with_node_addr(..key).rev().take(num_lines - items.len())); items.reverse(); } }, TreeDisplayMode::To(key, num_lines) => { - items = tree.range(..key).rev().take(num_lines).collect(); + items = tree.range_with_node_addr(..key).rev().take(num_lines).collect(); items.reverse(); if items.len() < num_lines { - items.extend(tree.range(key..).take(num_lines - items.len())); + items.extend(tree.range_with_node_addr(key..).take(num_lines - items.len())); } } }; + let data_slice = |item: &Item, node_addr: u64| -> &[u8] { + tree.reader.get_raw_data(node_addr, item.range.0, item.range.1).unwrap() + }; + let table_result = TableResult { tree_id, tree_desc: root_key_desc(tree_id).map(|x|x.to_string()), key_id: highlighted_key_id, - items: items.iter().map(|it|(it,&[] as &[u8])).collect(), - first_key: items.first().map(|it|it.key).unwrap_or(LAST_KEY), - last_key: items.last().map(|it|it.key).unwrap_or(ZERO_KEY), + items: items.iter().map(|(it, addr)|(it, *addr, data_slice(it, *addr))).collect(), + first_key: items.first().map(|x|x.0.key).unwrap_or(LAST_KEY), + last_key: items.last().map(|x|x.0.key).unwrap_or(ZERO_KEY), }; Response::html(render_table(table_result)) diff --git a/btrfs_explorer/src/nodereader.rs b/btrfs_explorer/src/nodereader.rs index dd1f09e..8c3d5fe 100644 --- a/btrfs_explorer/src/nodereader.rs +++ b/btrfs_explorer/src/nodereader.rs @@ -2,6 +2,7 @@ use std::{ collections::HashMap, sync::Arc, cell::RefCell, + time::Instant, }; use crate::btrfs_structs::{Node, ParseError, ParseBin}; @@ -29,16 +30,25 @@ impl<'a> NodeReader<'a> { return Ok(Arc::clone(node)) } - println!("Reading node at {:X}", addr); + let start_time = Instant::now(); let node_data = self.addr_map.node_at_log(self.image, addr)?; let node = Arc::new(Node::parse(node_data)?); self.cache.borrow_mut().insert(addr, Arc::clone(&node)); + let t = Instant::now().duration_since(start_time); + + println!("Read node {:X} in {:?}", addr, t); + Ok(node) } + pub fn get_raw_data(&self, addr: u64, start: u32, end: u32) -> Result<&'a [u8], ParseError> { + let node_data = self.addr_map.node_at_log(self.image, addr)?; + Ok(&node_data[start as usize .. end as usize]) + } + pub fn addr_map(&self) -> &AddressMap { &self.addr_map } diff --git a/btrfs_explorer/src/render_tree.rs b/btrfs_explorer/src/render_tree.rs index ad9f166..5cacc90 100644 --- a/btrfs_explorer/src/render_tree.rs +++ b/btrfs_explorer/src/render_tree.rs @@ -1,13 +1,14 @@ use crate::btrfs_structs::{Item, Key, ItemType, Value, ExtentDataBody}; use crate::render_common::{Hex, size_name}; use maud::{Markup, html, DOCTYPE, PreEscaped}; +use std::ffi::CStr; #[derive(Debug)] pub struct TableResult<'a> { pub tree_id: u64, pub tree_desc: Option, pub key_id: Option, - pub items: Vec<(&'a Item, &'a [u8])>, + pub items: Vec<(&'a Item, u64, &'a [u8])>, // item, node addr, data pub first_key: Key, pub last_key: Key, } @@ -42,12 +43,13 @@ pub fn render_table(table: TableResult) -> Markup { let mut rows: Vec = Vec::new(); - for &(it, _it_data) in table.items.iter() { + for &(it, node_addr, it_data) in table.items.iter() { let highlighted = if table.key_id.filter(|x|*x == it.key.key_id).is_some() { "highlight" } else { "" }; let value_string = item_value_string(table.tree_id, it); let details_string = item_details_string(table.tree_id, it); let raw_string = format!("{:#?}", &it.value); let id_desc = row_id_desc(it.key, table.tree_id); + let hex_data: String = it_data.iter().map(|x|format!("{:02X} ", x)).collect(); rows.push(html! { details.item.(highlighted) { @@ -64,6 +66,9 @@ pub fn render_table(table: TableResult) -> Markup { span.itemvalue.(key_type_class(it.key)) { (&value_string) } + span.nodeaddr { + (Hex(node_addr)) + } } div.details { @@ -77,6 +82,15 @@ pub fn render_table(table: TableResult) -> Markup { (&raw_string) } } + + details { + summary { + "show hex data" + } + pre { + (&hex_data) + } + } } } }); @@ -151,8 +165,15 @@ fn item_value_string(tree_id: u64, item: &Item) -> Markup { html! { (name) " @ " - a href=(format!("/tree/{tree_id}/{id:x}")) { - (Hex(id)) + @if dir_item.location.key_type == ItemType::Root { + a href=(format!("/tree/{id}")) { + "subvolume " + (Hex(id)) + } + } @else { + a href=(format!("/tree/{tree_id}/{id:x}")) { + (Hex(id)) + } } } }, @@ -180,12 +201,17 @@ fn item_value_string(tree_id: u64, item: &Item) -> Markup { ExtentDataBody::External(ext_extent) => PreEscaped(format!("external, length {}", size_name(ext_extent.num_bytes))), }, - Value::Ref(ref_item) => - html! { (format!("{:?}", &ref_item.name)) }, - Value::RootRef(ref_item) => - html! { (format!("{:?}", &ref_item.name)) }, + Value::Ref(ref_item) => { + let names: Vec<&CStr> = ref_item.iter().map(|x|x.name.as_ref()).collect(); + html! { (format!("{:?}", &names)) } + + }, + Value::RootRef(ref_item) => { + let names: Vec<&CStr> = ref_item.iter().map(|x|x.name.as_ref()).collect(); + html! { (format!("{:?}", &names)) } + }, Value::Extent(extent_item) => - PreEscaped(format!("flags: {}, block_refs: {:?}", extent_item.flags, extent_item.block_refs)), + PreEscaped(format!("flags: {}, block_refs: {:X?}", extent_item.flags, extent_item.block_refs)), Value::BlockGroup(blockgroup_item) => PreEscaped(format!("{} used", size_name(blockgroup_item.used))), Value::DevExtent(dev_extent_item) => @@ -245,13 +271,14 @@ fn item_details_string(_tree_id: u64, item: &Item) -> Markup { }, Value::Ref(ref_item) => { html! { table { tbody { - tr { td { "name" } td { (format!("{:?}", ref_item.name)) } } - tr { td { "index" } td { (ref_item.index) } } + tr { td { "name" } td { (format!("{:?}", ref_item[0].name)) } } + tr { td { "index" } td { (ref_item[0].index) } } }}} }, Value::Dir(dir_item) | Value::DirIndex(dir_item) => { html! { table { tbody { tr { td { "name" } td { (format!("{:?}", dir_item.name)) } } + tr { td { "target key" } td { (format!("{:X} {:?} {:X}", dir_item.location.key_id, dir_item.location.key_type, dir_item.location.key_offset)) } } }}} }, Value::Root(root_item) => { @@ -278,9 +305,9 @@ fn item_details_string(_tree_id: u64, item: &Item) -> Markup { }, Value::RootRef(root_ref_item) => { html! { table { tbody { - tr { td { "name" } td { (format!("{:?}", root_ref_item.name)) } } - tr { td { "directory" } td { (root_ref_item.directory) } } - tr { td { "index" } td { (root_ref_item.index) } } + tr { td { "name" } td { (format!("{:?}", root_ref_item[0].name)) } } + tr { td { "directory" } td { (root_ref_item[0].directory) } } + tr { td { "index" } td { (root_ref_item[0].index) } } }}} }, _ => { diff --git a/btrfs_explorer_bin/src/main.rs b/btrfs_explorer_bin/src/main.rs index f6c0041..01d5bbe 100644 --- a/btrfs_explorer_bin/src/main.rs +++ b/btrfs_explorer_bin/src/main.rs @@ -171,7 +171,7 @@ fn http_main_boxes(image: &[u8], _req: &Request) -> Response { if let Some(met) = &mut metadata_items { let bg_start = bg_item.key.key_id; let node_addr = item.key.key_id; - let tree_id = e.block_refs.iter().filter(|&(t,_)|t == &ItemType::TreeBlockRef).count() as u64; + let tree_id = e.block_refs.iter().count() as u64; let index = (node_addr - bg_start) as usize / NODE_SIZE; if index < met.len() { met[index] = Some((tree_id, item.key.key_offset)); diff --git a/style.css b/style.css index 74cc929..4f85553 100644 --- a/style.css +++ b/style.css @@ -133,6 +133,17 @@ span.key_type.root { background-color: #111; } +span.nodeaddr { + color: white; + background-color: #999; + text-align: right; + border-radius: 4px; + padding: 3px; + float: right; + font-family: monospace; + font-size: 12pt; +} + .details table { border-collapse: collapse; margin-bottom: 10px; @@ -151,3 +162,7 @@ span.key_type.root { padding: 0; margin: 5px 0; } + +pre { + white-space: pre-wrap; +}