explanations and arguments

This commit is contained in:
Florian Stecker 2024-05-22 16:05:26 -04:00
parent 83bdade363
commit 61d7680af8
5 changed files with 80 additions and 68 deletions

View File

@ -193,7 +193,7 @@ fn render_chunk(data: ChunkResult) -> Markup {
}
};
render_page("", content)
render_page(&header, content)
}
fn render_allchunks(data: Vec<ChunkLineDisplay>) -> Markup {
@ -231,7 +231,7 @@ fn render_allchunks(data: Vec<ChunkLineDisplay>) -> Markup {
}
};
render_page("", content)
render_page("Physical disk layout", content)
}
fn explanation_allchunks() -> Markup {
@ -274,7 +274,7 @@ fn explanation_chunk() -> Markup {
p { "The colors indicate which B-tree the node belongs to. Most of them belong to the filesystem trees. There is one filesystem tree for every subvolume, but we draw them all in the same color here. The other trees are:" }
table {
table.legend {
tr {
td { table.blocks { tr { td style="background: darkgreen;" {} } } }
td { "root tree" }

View File

@ -1,7 +1,6 @@
use maud::{html, DOCTYPE, Markup, Render};
use std::fmt::{Debug, UpperHex};
pub const HTTP_PATH: &str = "";
use std::sync::OnceLock;
pub struct DebugRender<T>(pub T);
@ -43,7 +42,7 @@ pub fn render_page(title: &str, content: Markup) -> Markup {
html! {
(DOCTYPE)
head {
link rel="stylesheet" href={(HTTP_PATH) "/style.css"};
link rel="stylesheet" href={(http_path()) "/style.css"};
title {
(title)
}
@ -53,3 +52,13 @@ pub fn render_page(title: &str, content: Markup) -> Markup {
}
}
}
static HTTP_PATH: OnceLock<String> = OnceLock::new();
pub fn http_path() -> &'static str {
HTTP_PATH.get().expect("HTTP_PATH should have been initialized before usage.")
}
pub fn http_path_set(path: String) {
HTTP_PATH.set(path).expect("HTTP_PATH can only be set once.");
}

View File

@ -1,5 +1,5 @@
use crate::btrfs_structs::{Item, Key, ItemType, Value, ExtentDataBody};
use crate::render_common::{Hex, size_name, HTTP_PATH};
use crate::render_common::{Hex, size_name, http_path};
use maud::{Markup, html, DOCTYPE, PreEscaped};
use std::ffi::CStr;
@ -14,7 +14,6 @@ pub struct TableResult<'a> {
}
pub fn render_table(table: TableResult) -> Markup {
let header = if let Some(desc) = table.tree_desc {
format!("Tree {} ({})", table.tree_id, desc)
} else {
@ -23,19 +22,22 @@ pub fn render_table(table: TableResult) -> Markup {
let key_input_value = table.key_id.map_or(String::new(), |x| format!("{:X}", x));
let first_key_url = format!("{HTTP_PATH}/tree/{}",
table.tree_id);
let prev_key_url = format!("{HTTP_PATH}/tree/{}/to/{:016X}-{:02X}-{:016X}",
let first_key_url = format!("{}/tree/{}",
http_path(), table.tree_id);
let prev_key_url = format!("{}/tree/{}/to/{:016X}-{:02X}-{:016X}",
http_path(),
table.tree_id,
table.first_key.key_id,
u8::from(table.first_key.key_type),
table.first_key.key_offset);
let next_key_url = format!("{HTTP_PATH}/tree/{}/from/{:016X}-{:02X}-{:016X}",
let next_key_url = format!("{}/tree/{}/from/{:016X}-{:02X}-{:016X}",
http_path(),
table.tree_id,
table.last_key.key_id,
u8::from(table.last_key.key_type),
table.first_key.key_offset);
let last_key_url = format!("{HTTP_PATH}/tree/{}/to/{:016X}-{:02X}-{:016X}",
let last_key_url = format!("{}/tree/{}/to/{:016X}-{:02X}-{:016X}",
http_path(),
table.tree_id,
u64::wrapping_sub(0,1),
u8::wrapping_sub(0,1),
@ -100,20 +102,25 @@ pub fn render_table(table: TableResult) -> Markup {
html! {
(DOCTYPE)
head {
link rel="stylesheet" href={(HTTP_PATH) "/style.css"};
link rel="stylesheet" href={(http_path()) "/style.css"};
}
body {
h1 {
(header)
}
details {
summary { "Explanation" }
(explanation_tree())
}
@if table.tree_id != 1 {
a href={(HTTP_PATH) "/tree/1"} {
a href={(http_path()) "/tree/1"} {
"go back to root tree"
}
}
form method="get" action={(HTTP_PATH) "/tree/" (table.tree_id)} {
form method="get" action={(http_path()) "/tree/" (table.tree_id)} {
input type="text" name="key" value=(key_input_value);
input type="submit" value="Search";
}
@ -129,6 +136,26 @@ pub fn render_table(table: TableResult) -> Markup {
}
}
fn explanation_tree() -> Markup {
html! {
p {
"This page shows the content of a tree. It is essentially a list of items, each of which consist of a key and a value."
}
p {
"The key is shown in the boxes on the left. It is a triple of a 64-bit id, an 8-bit type, and a 64-bit offset. What each of them means depends on the tree we're in. You can search for a key id by using the search field below."
}
p {
"The value is summarized to the right of the key. To see the value in more detail, unfold the key by clicking on it."
}
p {
"Finally, to the very right, we have the logical address of the metadata node which the item is stored in."
}
}
}
fn key_type_class(key: Key) -> &'static str {
match key.key_type {
ItemType::Inode => "inode",
@ -147,7 +174,7 @@ fn row_id_desc(key: Key, tree_id: u64) -> (Markup, Markup, Markup) {
let x = format!("{:X}", key.key_id);
let y = format!("{:?} ({:02X})", key.key_type, u8::from(key.key_type));
let z = if key.key_type == ItemType::RootRef || key.key_type == ItemType::Ref {
format!("<a href=\"{HTTP_PATH}/tree/{}/{:X}\">{:X}</a>", tree_id, key.key_offset, key.key_offset)
format!("<a href=\"{}/tree/{}/{:X}\">{:X}</a>", http_path(), tree_id, key.key_offset, key.key_offset)
} else {
format!("{:X}", key.key_offset)
};
@ -157,7 +184,7 @@ fn row_id_desc(key: Key, tree_id: u64) -> (Markup, Markup, Markup) {
fn item_value_string(tree_id: u64, item: &Item) -> Markup {
match &item.value {
Value::Root(_) => {
html! { a href={(HTTP_PATH) "/tree/" (item.key.key_id)} { "go to tree " (item.key.key_id) } }
html! { a href={(http_path()) "/tree/" (item.key.key_id)} { "go to tree " (item.key.key_id) } }
},
Value::Dir(dir_item) | Value::DirIndex(dir_item) => {
let name = format!("{:?}", &dir_item.name);
@ -166,12 +193,12 @@ fn item_value_string(tree_id: u64, item: &Item) -> Markup {
(name)
" @ "
@if dir_item.location.key_type == ItemType::Root {
a href=(format!("{HTTP_PATH}/tree/{id}")) {
a href=(format!("{}/tree/{id}", http_path())) {
"subvolume "
(Hex(id))
}
} @else {
a href=(format!("{HTTP_PATH}/tree/{tree_id}/{id:x}")) {
a href=(format!("{}/tree/{tree_id}/{id:x}", http_path())) {
(Hex(id))
}
}

View File

@ -1,22 +1,32 @@
use std::{
env, fs::{File, OpenOptions},
env, fs::OpenOptions, ops::Deref, include_str,
};
use memmap2::MmapOptions;
use rouille::{Response, router};
use btrfs_explorer::main_error::MainError;
use btrfs_explorer::render_common::http_path_set;
const CSS_FILE: &'static str = include_str!("style.css");
fn main() -> Result<(), MainError> {
let filename = env::args().skip(1).next().ok_or("Argument required")?;
let args: Vec<String> = env::args().collect();
/*
let file = OpenOptions::new().read(true).open(filename)?;
let image = unsafe { Mmap::map(&file)? };
*/
if args.len() < 2 {
return Err("Argument required".into());
}
let filename: &str = args[1].as_ref();
let sockaddr: &str = args.get(2)
.map_or("localhost:8080", <String as Deref>::deref);
let http_path: String = args.get(3)
.map_or(String::new(), <String as ToOwned>::to_owned);
http_path_set(http_path);
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)? };
let image = unsafe { MmapOptions::new().map(&file)? };
rouille::start_server("127.0.0.1:8080", move |request| {
rouille::start_server(sockaddr, move |request| {
router!(
request,
(GET) ["/"] =>
@ -34,8 +44,8 @@ fn main() -> Result<(), MainError> {
(GET) ["/tree/{tree}/{method}/{key}", tree: String, method: String, key: String] =>
btrfs_explorer::http_tree::http_tree(&image, &tree, Some(&method), Some(&key), request).unwrap(),
(GET) ["/favicon.ico"] => Response::empty_404(),
(GET) ["/style.css"] => Response::from_file("text/css", File::open("style.css").unwrap()),
(GET) ["/htmx.min.js"] => Response::from_file("text/css", File::open("htmx.min.js").unwrap()),
(GET) ["/style.css"] => Response::from_data("text/css", CSS_FILE),// Response::from_file("text/css", File::open("style.css").unwrap()),
// (GET) ["/htmx.min.js"] => Response::from_file("text/css", File::open("htmx.min.js").unwrap()),
_ => Response::empty_404(),
)
});

View File

@ -2,43 +2,6 @@ body {
padding: 0.2em 2em;
}
table {
width: 100%;
}
table td {
padding: 0.1em 0.2em;
}
table th {
text-align: left;
border-bottom: 1px solid #ccc;
}
table > tbody > tr.view {
cursor: pointer;
}
table > tbody > tr.even {
background: #eee;
}
table > tbody > tr.highlight {
background: #0cc;
}
table > tbody > tr.fold {
display: none;
}
table > tbody > tr.fold > td {
padding-left: 1em;
}
table > tbody > tr.fold.open {
display: table-row;
}
div.nav {
padding: 5px;
background-color: #dde;
@ -171,7 +134,6 @@ table.blocks {
margin: 0 auto;
border-collapse: separate;
border-spacing: 2px;
width: 770px;
}
table.blocks td {
@ -179,3 +141,7 @@ table.blocks td {
width: 10px;
padding: 0;
}
table.legend {
margin: 0 auto;
}