2024-02-24 16:24:25 +00:00
use crate ::btrfs_structs ::{ Item , Key , ItemType , Value , ExtentDataBody } ;
2024-03-01 18:31:34 +00:00
use crate ::render_common ::{ Hex , size_name } ;
2024-02-25 19:34:29 +00:00
use maud ::{ Markup , html , DOCTYPE , PreEscaped } ;
2024-03-06 05:50:43 +00:00
use std ::ffi ::CStr ;
2024-02-24 16:24:25 +00:00
#[ derive(Debug) ]
pub struct TableResult < ' a > {
pub tree_id : u64 ,
pub tree_desc : Option < String > ,
pub key_id : Option < u64 > ,
2024-03-06 05:50:43 +00:00
pub items : Vec < ( & ' a Item , u64 , & ' a [ u8 ] ) > , // item, node addr, data
2024-02-24 16:24:25 +00:00
pub first_key : Key ,
pub last_key : Key ,
}
2024-03-07 04:12:35 +00:00
const HTTP_PATH : & str = " /btrfs " ;
2024-02-25 19:34:29 +00:00
pub fn render_table ( table : TableResult ) -> Markup {
2024-02-24 16:24:25 +00:00
2024-02-25 19:34:29 +00:00
let header : String = if let Some ( desc ) = table . tree_desc {
format! ( " Tree {} ( {} ) " , table . tree_id , desc )
} else {
format! ( " Tree {} " , table . tree_id )
} ;
2024-02-24 16:24:25 +00:00
2024-02-25 19:34:29 +00:00
let key_input_value = table . key_id . map_or ( String ::new ( ) , | x | format! ( " {:X} " , x ) ) ;
2024-02-24 16:24:25 +00:00
2024-03-07 04:12:35 +00:00
let first_key_url = format! ( " {HTTP_PATH} /tree/ {} " ,
2024-02-25 19:34:29 +00:00
table . tree_id ) ;
2024-03-07 04:12:35 +00:00
let prev_key_url = format! ( " {HTTP_PATH} /tree/ {} /to/ {:016X} - {:02X} - {:016X} " ,
2024-02-25 19:34:29 +00:00
table . tree_id ,
table . first_key . key_id ,
u8 ::from ( table . first_key . key_type ) ,
table . first_key . key_offset ) ;
2024-03-07 04:12:35 +00:00
let next_key_url = format! ( " {HTTP_PATH} /tree/ {} /from/ {:016X} - {:02X} - {:016X} " ,
2024-02-25 19:34:29 +00:00
table . tree_id ,
table . last_key . key_id ,
u8 ::from ( table . last_key . key_type ) ,
table . first_key . key_offset ) ;
2024-03-07 04:12:35 +00:00
let last_key_url = format! ( " {HTTP_PATH} /tree/ {} /to/ {:016X} - {:02X} - {:016X} " ,
2024-02-25 19:34:29 +00:00
table . tree_id ,
u64 ::wrapping_sub ( 0 , 1 ) ,
u8 ::wrapping_sub ( 0 , 1 ) ,
u64 ::wrapping_sub ( 0 , 1 ) ) ;
2024-02-24 16:24:25 +00:00
2024-02-25 19:34:29 +00:00
let mut rows : Vec < Markup > = Vec ::new ( ) ;
2024-02-24 16:24:25 +00:00
2024-03-06 05:50:43 +00:00
for & ( it , node_addr , it_data ) in table . items . iter ( ) {
2024-02-25 19:34:29 +00:00
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 ) ;
2024-02-24 16:24:25 +00:00
let id_desc = row_id_desc ( it . key , table . tree_id ) ;
2024-03-06 05:50:43 +00:00
let hex_data : String = it_data . iter ( ) . map ( | x | format! ( " {:02X} " , x ) ) . collect ( ) ;
2024-02-24 16:24:25 +00:00
2024-02-25 19:34:29 +00:00
rows . push ( html! {
details . item . ( highlighted ) {
summary {
span . key . key_id . ( key_type_class ( it . key ) ) {
( id_desc . 0 )
}
span . key . key_type . ( key_type_class ( it . key ) ) {
( id_desc . 1 )
}
span . key . key_offset . ( key_type_class ( it . key ) ) {
( id_desc . 2 )
}
span . itemvalue . ( key_type_class ( it . key ) ) {
( & value_string )
}
2024-03-06 05:50:43 +00:00
span . nodeaddr {
( Hex ( node_addr ) )
}
2024-02-25 19:34:29 +00:00
}
div . details {
( & details_string )
details {
summary {
" show full value "
}
pre {
( & raw_string )
}
}
2024-03-06 05:50:43 +00:00
details {
summary {
" show hex data "
}
pre {
( & hex_data )
}
}
2024-02-25 19:34:29 +00:00
}
}
} ) ;
2024-02-24 16:24:25 +00:00
}
2024-02-25 19:34:29 +00:00
// the complete page
html! {
( DOCTYPE )
head {
2024-03-07 04:14:51 +00:00
link rel = " stylesheet " href = { ( HTTP_PATH ) " /style.css " } ;
2024-02-25 19:34:29 +00:00
}
body {
h1 {
( header )
}
@ if table . tree_id ! = 1 {
2024-03-07 04:12:35 +00:00
a href = { ( HTTP_PATH ) " /tree/1 " } {
2024-02-25 19:34:29 +00:00
" go back to root tree "
}
}
2024-03-07 04:12:35 +00:00
form method = " get " action = { ( HTTP_PATH ) " /tree/ " ( table . tree_id ) } {
2024-02-25 19:34:29 +00:00
input type = " text " name = " key " value = ( key_input_value ) ;
input type = " submit " value = " Search " ;
}
2024-02-24 16:24:25 +00:00
2024-02-25 19:34:29 +00:00
a . nav href = ( first_key_url ) { div . nav { " first " } }
a . nav href = ( prev_key_url ) { div . nav { " prev " } }
@ for row in & rows { ( row ) }
a . nav href = ( next_key_url ) { div . nav { " next " } }
a . nav href = ( last_key_url ) { div . nav { " last " } }
}
}
2024-02-24 16:24:25 +00:00
}
2024-02-25 19:34:29 +00:00
fn key_type_class ( key : Key ) -> & 'static str {
match key . key_type {
ItemType ::Inode = > " inode " ,
ItemType ::Ref = > " ref " ,
ItemType ::RootRef = > " ref " ,
ItemType ::RootBackRef = > " ref " ,
ItemType ::ExtentData = > " extent " ,
ItemType ::Dir = > " dir " ,
ItemType ::DirIndex = > " dir " ,
ItemType ::Root = > " root " ,
_ = > " " ,
}
}
fn row_id_desc ( key : Key , tree_id : u64 ) -> ( Markup , Markup , Markup ) {
2024-02-24 16:24:25 +00:00
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 {
2024-03-07 04:12:35 +00:00
format! ( " <a href= \" {HTTP_PATH} /tree/ {} / {:X} \" > {:X} </a> " , tree_id , key . key_offset , key . key_offset )
2024-02-24 16:24:25 +00:00
} else {
format! ( " {:X} " , key . key_offset )
} ;
2024-02-25 19:34:29 +00:00
( PreEscaped ( x ) , PreEscaped ( y ) , PreEscaped ( z ) )
2024-02-24 16:24:25 +00:00
}
2024-02-25 19:34:29 +00:00
fn item_value_string ( tree_id : u64 , item : & Item ) -> Markup {
match & item . value {
2024-02-24 16:24:25 +00:00
Value ::Root ( _ ) = > {
2024-03-07 04:12:35 +00:00
html! { a href = { ( HTTP_PATH ) " /tree/ " ( item . key . key_id ) } { " go to tree " ( item . key . key_id ) } }
2024-02-25 19:34:29 +00:00
} ,
Value ::Dir ( dir_item ) | Value ::DirIndex ( dir_item ) = > {
let name = format! ( " {:?} " , & dir_item . name ) ;
let id = dir_item . location . key_id ;
html! {
( name )
" @ "
2024-03-06 05:50:43 +00:00
@ if dir_item . location . key_type = = ItemType ::Root {
2024-03-07 04:12:35 +00:00
a href = ( format! ( " {HTTP_PATH} /tree/ {id} " ) ) {
2024-03-06 05:50:43 +00:00
" subvolume "
( Hex ( id ) )
}
} @ else {
2024-03-07 04:12:35 +00:00
a href = ( format! ( " {HTTP_PATH} /tree/ {tree_id} / {id:x} " ) ) {
2024-03-06 05:50:43 +00:00
( Hex ( id ) )
}
2024-02-25 19:34:29 +00:00
}
}
2024-02-24 16:24:25 +00:00
} ,
2024-02-28 05:43:37 +00:00
Value ::Inode ( inode_item ) = > {
let file_type = match inode_item . mode / ( 1 < < 12 ) {
4 = > " directory " ,
2 = > " character device " ,
6 = > " block device " ,
8 = > " regular file " ,
1 = > " FIFO " ,
10 = > " symbolic link " ,
12 = > " socket " ,
_ = > " unknown file type " ,
} ;
format_escape! ( " {}, mode {}{}{}{} " , file_type ,
( inode_item . mode / ( 1 < < 9 ) ) % 8 ,
( inode_item . mode / ( 1 < < 6 ) ) % 8 ,
( inode_item . mode / ( 1 < < 3 ) ) % 8 ,
( inode_item . mode / ( 1 < < 0 ) ) % 8 )
} ,
2024-02-24 16:24:25 +00:00
Value ::ExtentData ( extent_data_item ) = >
match & extent_data_item . data {
2024-02-25 19:34:29 +00:00
ExtentDataBody ::Inline ( data ) = >
PreEscaped ( format! ( " inline, length {} " , size_name ( data . len ( ) as u64 ) ) ) ,
2024-02-24 16:24:25 +00:00
ExtentDataBody ::External ( ext_extent ) = >
2024-02-25 19:34:29 +00:00
PreEscaped ( format! ( " external, length {} " , size_name ( ext_extent . num_bytes ) ) ) ,
2024-02-24 16:24:25 +00:00
} ,
2024-03-06 05:50:43 +00:00
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 ) ) }
} ,
2024-02-24 16:24:25 +00:00
Value ::Extent ( extent_item ) = >
2024-03-06 05:50:43 +00:00
PreEscaped ( format! ( " flags: {} , block_refs: {:X?} " , extent_item . flags , extent_item . block_refs ) ) ,
2024-02-24 16:24:25 +00:00
Value ::BlockGroup ( blockgroup_item ) = >
2024-02-25 19:34:29 +00:00
PreEscaped ( format! ( " {} used " , size_name ( blockgroup_item . used ) ) ) ,
2024-02-24 16:24:25 +00:00
Value ::DevExtent ( dev_extent_item ) = >
2024-02-25 19:34:29 +00:00
PreEscaped ( format! ( " chunk_tree: {} , chunk_offset: {:x} , length: {} " , dev_extent_item . chunk_tree , dev_extent_item . chunk_offset , size_name ( dev_extent_item . length ) ) ) ,
2024-02-24 16:24:25 +00:00
Value ::UUIDSubvol ( uuid_subvol_item ) = >
2024-02-25 19:34:29 +00:00
PreEscaped ( format! ( " subvolume id: {} " , uuid_subvol_item . subvol_id ) ) ,
2024-02-24 16:24:25 +00:00
Value ::FreeSpaceInfo ( free_space_info ) = >
2024-02-25 19:34:29 +00:00
PreEscaped ( format! ( " extent_count: {} , flags: {} " , free_space_info . extent_count , free_space_info . flags ) ) ,
2024-02-24 16:24:25 +00:00
Value ::Dev ( dev_item ) = >
2024-02-25 19:34:29 +00:00
PreEscaped ( format! ( " total_bytes: {} " , size_name ( dev_item . total_bytes ) ) ) ,
2024-02-24 16:24:25 +00:00
Value ::Chunk ( chunk_item ) = >
2024-02-28 05:43:37 +00:00
PreEscaped ( format! ( " size: {} " , size_name ( chunk_item . size ) ) ) ,
2024-02-25 19:34:29 +00:00
_ = > {
// println!("{:?} {:?}", item.key, item.valu);
PreEscaped ( String ::new ( ) )
} ,
}
}
2024-03-01 18:31:34 +00:00
fn item_details_string ( _tree_id : u64 , item : & Item ) -> Markup {
2024-02-25 19:34:29 +00:00
match & item . value {
Value ::Inode ( inode_item ) = > {
html! { table { tbody {
tr { td { " size " } td { ( inode_item . size ) } }
tr { td { " mode " } td { ( inode_item . mode ) } }
tr { td { " uid " } td { ( inode_item . uid ) } }
tr { td { " gid " } td { ( inode_item . gid ) } }
tr { td { " nlink " } td { ( inode_item . nlink ) } }
tr { td { " atime " } td { ( inode_item . atime . sec ) } }
tr { td { " ctime " } td { ( inode_item . ctime . sec ) } }
tr { td { " mtime " } td { ( inode_item . mtime . sec ) } }
tr { td { " otime " } td { ( inode_item . otime . sec ) } }
} } }
} ,
Value ::ExtentData ( extent_item ) = > {
match & extent_item . data {
2024-03-01 18:31:34 +00:00
ExtentDataBody ::Inline ( _data ) = > {
2024-02-25 19:34:29 +00:00
html! { } // we really want data as string / hex
} ,
ExtentDataBody ::External ( ext_extent ) = > {
html! {
p {
@ if ext_extent . disk_bytenr = = 0 {
( size_name ( ext_extent . num_bytes ) ) " of zeros. "
} @ else {
( format! ( " {} on disk, starting at offset {:X} within the extent at address {:X} ; {} in the file starting from offset {:X} . " , size_name ( ext_extent . disk_num_bytes ) , ext_extent . offset , ext_extent . disk_bytenr , size_name ( ext_extent . num_bytes ) , item . key . key_offset ) )
}
}
table { tbody {
tr { td { " compression " } td { ( extent_item . header . compression ) } }
tr { td { " encryption " } td { ( extent_item . header . encryption ) } }
tr { td { " other_encoding " } td { ( extent_item . header . other_encoding ) } }
} }
}
} ,
}
} ,
Value ::Ref ( ref_item ) = > {
html! { table { tbody {
2024-03-06 05:50:43 +00:00
tr { td { " name " } td { ( format! ( " {:?} " , ref_item [ 0 ] . name ) ) } }
tr { td { " index " } td { ( ref_item [ 0 ] . index ) } }
2024-02-25 19:34:29 +00:00
} } }
} ,
Value ::Dir ( dir_item ) | Value ::DirIndex ( dir_item ) = > {
html! { table { tbody {
tr { td { " name " } td { ( format! ( " {:?} " , dir_item . name ) ) } }
2024-03-06 05:50:43 +00:00
tr { td { " target key " } td { ( format! ( " {:X} {:?} {:X} " , dir_item . location . key_id , dir_item . location . key_type , dir_item . location . key_offset ) ) } }
2024-02-25 19:34:29 +00:00
} } }
} ,
Value ::Root ( root_item ) = > {
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 ) ) } }
tr { td { " bytes used " } td { ( size_name ( root_item . bytes_used ) ) } }
tr { td { " last snapshot " } td { ( root_item . last_snapshot ) } }
tr { td { " flags " } td { ( root_item . flags ) } }
tr { td { " refs " } td { ( root_item . refs ) } }
tr { td { " level " } td { ( root_item . level ) } }
tr { td { " UUID " } td { ( format! ( " {:?} " , root_item . uuid ) ) } }
tr { td { " parent UUID " } td { ( format! ( " {:?} " , root_item . parent_uuid ) ) } }
tr { td { " received UUID " } td { ( format! ( " {:?} " , root_item . received_uuid ) ) } }
tr { td { " ctransid " } td { ( root_item . ctransid ) } }
tr { td { " otransid " } td { ( root_item . otransid ) } }
tr { td { " stransid " } td { ( root_item . stransid ) } }
tr { td { " rtransid " } td { ( root_item . rtransid ) } }
tr { td { " ctime " } td { ( root_item . ctime . sec ) } }
tr { td { " otime " } td { ( root_item . otime . sec ) } }
tr { td { " stime " } td { ( root_item . stime . sec ) } }
tr { td { " rtime " } td { ( root_item . rtime . sec ) } }
} } }
} ,
2024-02-28 05:43:37 +00:00
Value ::RootRef ( root_ref_item ) = > {
html! { table { tbody {
2024-03-06 05:50:43 +00:00
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 ) } }
2024-02-28 05:43:37 +00:00
} } }
} ,
2024-02-24 16:24:25 +00:00
_ = > {
2024-02-25 19:34:29 +00:00
html! { }
2024-02-24 16:24:25 +00:00
} ,
}
}