remove old code

This commit is contained in:
Florian Stecker 2024-04-25 16:27:35 -04:00
parent 4d90426eee
commit 83bdade363
4 changed files with 10 additions and 243 deletions

View File

@ -2,7 +2,7 @@ use std::convert::identity;
use std::rc::Rc; use std::rc::Rc;
use std::ops::{Deref, RangeBounds, Bound}; use std::ops::{Deref, RangeBounds, Bound};
use crate::btrfs_structs::{DirItem, InteriorNode, Item, ItemType, Key, Leaf, Node, ParseBin, ParseError, Superblock, Value, LAST_KEY, ZERO_KEY}; use crate::btrfs_structs::{InteriorNode, Item, ItemType, Key, Leaf, Node, ParseBin, ParseError, Superblock, Value, LAST_KEY, ZERO_KEY};
use crate::nodereader::NodeReader; use crate::nodereader::NodeReader;
/// Represents a B-Tree inside a filesystem image. Can be used to look up keys, /// Represents a B-Tree inside a filesystem image. Can be used to look up keys,

View File

@ -1,8 +1,7 @@
use std::convert::identity; use maud::{Markup, html};
use maud::{Markup, html, DOCTYPE, PreEscaped};
use rouille::{Request, Response}; use rouille::{Request, Response};
use crate::{ use crate::{
btrfs_lookup::Tree, btrfs_structs::{self, BlockRef, ChunkItem, ItemType, Key, TreeID, Value}, key, main_error::MainError, render_common::{render_page, size_name} btrfs_lookup::Tree, btrfs_structs::{self, BlockRef, ItemType, TreeID, Value}, key, main_error::MainError, render_common::{render_page, size_name}
}; };
struct ChunkLineDisplay { struct ChunkLineDisplay {
@ -129,13 +128,11 @@ pub fn http_chunk(image: &[u8], offset: &str, _req: &Request) -> Result<Response
err!("No block group found at logical address {:x}", logical_offset) err!("No block group found at logical address {:x}", logical_offset)
} }
const COLORS: &[&str] = &["lightgray", "#e6194b", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#46f0f0", "#f032e6", "#bcf60c", "#fabebe", "#008080", "#e6beff", "#9a6324", "#fffac8", "#800000", "#aaffc3", "#808000", "#ffd8b1", "#000075", "#808080", "#000000"]; // const COLORS: &[&str] = &["lightgray", "#e6194b", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#46f0f0", "#f032e6", "#bcf60c", "#fabebe", "#008080", "#e6beff", "#9a6324", "#fffac8", "#800000", "#aaffc3", "#808000", "#ffd8b1", "#000075", "#808080", "#000000"];
fn render_chunk(data: ChunkResult) -> Markup { fn render_chunk(data: ChunkResult) -> Markup {
let header = format!("Metadata nodes in chunk at address {:x}", data.offset); let header = format!("Metadata nodes in chunk at address {:x}", data.offset);
let body = format!("{:?}", &data.refs);
let boxes: Vec<Vec<&str>> = data.refs let boxes: Vec<Vec<&str>> = data.refs
.chunks(64) .chunks(64)
.map(|row|row.iter() .map(|row|row.iter()

View File

@ -44,6 +44,9 @@ pub fn render_page(title: &str, content: Markup) -> Markup {
(DOCTYPE) (DOCTYPE)
head { head {
link rel="stylesheet" href={(HTTP_PATH) "/style.css"}; link rel="stylesheet" href={(HTTP_PATH) "/style.css"};
title {
(title)
}
} }
body { body {
(content) (content)

View File

@ -1,16 +1,9 @@
use std::{ use std::{
collections::HashMap, env, fs::{File, OpenOptions}, iter, env, fs::{File, OpenOptions},
}; };
use memmap2::MmapOptions; use memmap2::MmapOptions;
use rouille::{Request, Response, router}; use rouille::{Response, router};
use btrfs_explorer::{ use btrfs_explorer::main_error::MainError;
btrfs_structs::{TreeID, Value::Extent, Value::BlockGroup, NODE_SIZE, ItemType},
btrfs_lookup::Tree,
addrmap::AddressMap,
main_error::MainError,
};
const COLORS: &[&str] = &["#e6194b", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#46f0f0", "#f032e6", "#bcf60c", "#fabebe", "#008080", "#e6beff", "#9a6324", "#fffac8", "#800000", "#aaffc3", "#808000", "#ffd8b1", "#000075", "#808080", "#000000"];
fn main() -> Result<(), MainError> { fn main() -> Result<(), MainError> {
let filename = env::args().skip(1).next().ok_or("Argument required")?; let filename = env::args().skip(1).next().ok_or("Argument required")?;
@ -23,17 +16,6 @@ fn main() -> Result<(), MainError> {
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)? };
// return Ok(());
/*
let mystery_addr = 0x2f_2251_c000;
let addr_map = AddressMap::new(&image)?;
let mystery_addr_phys = addr_map.to_phys(mystery_addr).unwrap() as usize;
let mystery_node = Node::parse(&image[mystery_addr_phys .. ])?;
println!("{:#x?}", &mystery_node);
*/
rouille::start_server("127.0.0.1:8080", move |request| { rouille::start_server("127.0.0.1:8080", move |request| {
router!( router!(
request, request,
@ -58,218 +40,3 @@ fn main() -> Result<(), MainError> {
) )
}); });
} }
static CIRCLE_IMAGE: &str =
"data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAP0lEQVQY02NgoBn4//+//P///yf9\
////DRRP+v//vzw2hZP+Y4JJ2BS+waLwDUyeiVinIStchkV+GfmeoRoAAJqLWnEf4UboAAAAAElF\
TkSuQmCC";
static EXPLANATION_TEXT: &str = "\
<h3>Chunks</h3>
<p>On the highest level, btrfs splits the disk into <b>chunks</b> (also called <b>block groups</b>). They can have different sizes, with 1GiB being typical in a large file system. Each chunk can either contain data or metadata.<p>
<p>Here we look at the metadata chunks. They contain the B-treesm which btrfs gets its name from. They are key-value stores for different kinds of information. For example, the filesystem tree stores which files and directories are in the filesystem, and the extent tree stores which areas of the disk are in use. Each B-tree consists of a number of 16KiB <b>nodes</b>, here symbolized by colorful boxes, with the color indicating which tree the node belongs to. Most of the nodes are <b>leaves</b>, which contain the actual key-value pairs. The others are <b>interior nodes</b>, and we indicate them with a little white circle. They are important to find the leaf a key is stored in.</p>";
fn http_main_boxes(image: &[u8], _req: &Request) -> Response {
let mut treecolors: HashMap<u64, &str> = HashMap::new();
let mut result = String::new();
let explanation_tablerowformat = |c: &str, t: &str| format!(
"<tr>\
<td><table><tr><td style=\"height:10px;width:10px;padding:0;background:{};\"></td></tr></table></td>\
<td><table><tr><td style=\"height:10px;width:10px;padding:0;background:{};\"><img src=\"{}\" /></td></tr></table></td>\
<td>{}</td>\
</tr>\n",
c, c, CIRCLE_IMAGE, t);
let explanation_tablerowformat_leafonly = |c,t| format!(
"<tr>\
<td><table><tr><td style=\"height:10px;width:10px;padding:0;background:{};\"></td></tr></table></td>\
<td></td>\
<td>{}</td>\
</tr>\n",
c, t);
let cellformat = |c| format!(
"<td style=\"height:10px;width:10px;padding:0;background:{};\"></td>\n",
c);
let cellformat_higher = |c,_| format!(
"<td style=\"height:10px;width:10px;padding:0;background:{}\"><img src=\"{}\" /></td>\n",
c, CIRCLE_IMAGE);
result.push_str(&"<details>\n<summary>What am I seeing here?</summary>");
result.push_str(EXPLANATION_TEXT);
// tree explanations
result.push_str(&"<table style=\"margin: 0 auto;\">\n");
result.push_str(&explanation_tablerowformat_leafonly("lightgrey", "unused or outdated node"));
treecolors.insert(1, COLORS[treecolors.len() % COLORS.len()]);
result.push_str(&explanation_tablerowformat(treecolors[&1], "root tree"));
treecolors.insert(3, COLORS[treecolors.len() % COLORS.len()]);
result.push_str(&explanation_tablerowformat(treecolors[&3], "chunk tree"));
let roots = Tree::root(image).unwrap();
for item in roots.iter() {
if item.key.key_type == ItemType::Root {
let treedesc: String = match &item.key.key_id {
1 => format!("root tree"),
2 => format!("extent tree"),
3 => format!("chunk tree"),
4 => format!("device tree"),
5 => format!("filesystem tree"),
6 => format!("root directory"),
7 => format!("checksum tree"),
8 => format!("quota tree"),
9 => format!("UUID tree"),
10 => format!("free space tree"),
11 => format!("block group tree"),
0xffff_ffff_ffff_fff7 => format!("data reloc tree"),
x @ 0x100 ..= 0xffff_ffff_ffff_feff => format!("file tree, id = {}", x),
x => format!("other tree, id = {}", x),
};
treecolors.insert(item.key.key_id, COLORS[treecolors.len() % COLORS.len()]);
result.push_str(&explanation_tablerowformat(
treecolors[&item.key.key_id],
&treedesc
));
}
}
result.push_str(&"</table>\n");
result.push_str(&"</details>\n");
let extent_tree = Tree::new(&image, TreeID::Extent).unwrap();
let mut extent_tree_iterator = extent_tree.iter();
// current_blockgroup == None: haven't encountered a blockgroup yet
// metadata_items == None: current blockgroup is not metadata or system
let mut current_blockgroup = None;
let mut metadata_items: Option<Vec<Option<(u64, u64)>>> = None;
let metadata_blockgroups = iter::from_fn(|| {
while let Some(item) = extent_tree_iterator.next() {
// println!("Got key: {:x?}", &item.key);
match &item.value {
BlockGroup(bg) => {
println!("{:x?}", item.key);
let result = (current_blockgroup.take(), metadata_items.take());
let nodes_in_blockgroup = item.key.key_offset as usize / NODE_SIZE;
if bg.flags & 0x01 == 0 {
metadata_items = Some(vec![None; nodes_in_blockgroup]);
} else {
metadata_items = None;
}
current_blockgroup = Some(item);
if let (Some(bg), met) = result {
return Some((bg, met));
}
},
Extent(e) => {
if let Some(bg_item) = &current_blockgroup {
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().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));
} else {
println!("Warning: extent out of block group range: {:x?}", &item.key);
}
}
} else {
println!("Warning: extent without matching block group: {:x?}", &item.key);
}
},
_ => {},//panic!("Unexpected item in extent tree: {:x?}", item.key)
}
}
let result = (current_blockgroup.take(), metadata_items.take());
if let (Some(bg), met) = result {
return Some((bg, met));
} else {
return None;
}
});
let mut last_key = 0;
// colorful table
for (bg, nodes) in metadata_blockgroups {
if bg.key.key_id < last_key {
println!("Error: going backwards!");
break;
} else {
last_key = bg.key.key_id;
}
let bg_value = match &bg.value {
BlockGroup(bgv) => bgv,
_ => panic!("Expected BlockGroup value"),
};
// header
let addr_map: &AddressMap = extent_tree.reader.as_ref().addr_map();
result.push_str(
&format!(
"<h3 style=\"text-align: center;\">{:x} - {:x} ({}, {})</h3><p>Physical: {}</p>\n",
bg.key.key_id,
bg.key.key_id + bg.key.key_offset,
match bg.key.key_offset {
x if x <= (1<<11) => format!("{} B", x),
x if x <= (1<<21) => format!("{} KiB", x as f64 / (1u64<<10) as f64),
x if x <= (1<<31) => format!("{} MiB", x as f64 / (1u64<<20) as f64),
x if x <= (1<<41) => format!("{} GiB", x as f64 / (1u64<<30) as f64),
x if x <= (1<<51) => format!("{} TiB", x as f64 / (1u64<<40) as f64),
x @ _ => format!("{} PiB", x as f64 / (1u64<<50) as f64),
},
match bg_value.flags & 0x07 {
0x01 => "Data",
0x02 => "System",
0x04 => "Metadata",
_ => "???",
},
match addr_map.0.binary_search_by_key(&bg.key.key_id, |x|x.0) {
Ok(i) => format!("{:x?}", &addr_map.0[i].2),
_ => String::from(""),
}
)
);
if let Some(nodes) = nodes {
result.push_str("<table style=\"margin: 0 auto;\">\n<tr>\n");
for (i, &n) in nodes.iter().enumerate() {
if i % 64 == 0 && i != 0 {
result.push_str("</tr>\n<tr>\n");
}
if let Some((tid, level)) = n {
let color: Option<&str> = treecolors.get(&tid).map(|x|*x);
let color = color.unwrap_or_else(|| {
println!("Unknown color for id: {}", &tid);
let color: &str = COLORS[treecolors.len() % COLORS.len()];
treecolors.insert(tid, color);
color
});
if level == 0 {
result.push_str(&cellformat(color));
} else {
result.push_str(&cellformat_higher(color, level));
}
} else {
result.push_str(&cellformat("lightgrey"));
}
}
result.push_str("</tr>\n</table>\n");
}
}
Response::html(result)
}