initial version
This commit is contained in:
commit
bdcc597b9a
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "parsebtrfs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
memmap2 = "0.7.1"
|
||||||
|
rouille = "3.6.2"
|
575
src/main.rs
Normal file
575
src/main.rs
Normal file
@ -0,0 +1,575 @@
|
|||||||
|
use memmap2::Mmap;
|
||||||
|
use std::fs::File;
|
||||||
|
use rouille::Request;
|
||||||
|
use rouille::Response;
|
||||||
|
|
||||||
|
const ACTIVE_NODES: &'static[usize] = &[0x14000, 0x18000, 0x1c000, 0x20000, 0x28000, 0x2c000, 0x3c000, 0x40000];
|
||||||
|
|
||||||
|
fn main() -> Result<(), std::io::Error> {
|
||||||
|
let file = File::open("../image")?;
|
||||||
|
let image = unsafe { Mmap::map(&file)? };
|
||||||
|
|
||||||
|
let addr = AddressTranslation::new(&image);
|
||||||
|
|
||||||
|
rouille::start_server("127.0.0.1:8080", move |request| {
|
||||||
|
http_main_list(&image, &addr, request)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn http_main_boxes(image: &[u8], addr: &AddressTranslation, req: &Request) -> Response {
|
||||||
|
let chunk_offset = 0x02500000;
|
||||||
|
let nodes_in_chunk = 2048;
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
result.push_str("<body><table style=\"margin: 0 auto;\">\n<tr>\n");
|
||||||
|
|
||||||
|
for i in 0..nodes_in_chunk {
|
||||||
|
if i % 64 == 0 {
|
||||||
|
result.push_str("</tr>\n<tr>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = read_node(&image, chunk_offset + i*0x4000);
|
||||||
|
|
||||||
|
let active = node.generation > 0 && ACTIVE_NODES.contains(&(i*0x4000));
|
||||||
|
|
||||||
|
let newbox = format!("<td style=\"{}\"></td>\n",
|
||||||
|
if active {
|
||||||
|
"height:10px;width:10px;padding:0;background:black;"
|
||||||
|
} else {
|
||||||
|
"height:10px;width:10px;padding:0;background:lightgray;"
|
||||||
|
});
|
||||||
|
result.push_str(&newbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str("</tr>\n</table></body>");
|
||||||
|
|
||||||
|
Response::html(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn http_main_list(image: &[u8], addr: &AddressTranslation, req: &Request) -> Response {
|
||||||
|
let chunk_offset = 0x02500000;
|
||||||
|
let nodes_in_chunk = 2048;
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
result.push_str("<body>\n");
|
||||||
|
|
||||||
|
for i in 0..nodes_in_chunk {
|
||||||
|
let node = read_node(&image, chunk_offset + i*0x4000);
|
||||||
|
|
||||||
|
let active = ACTIVE_NODES.contains(&(i*0x4000));
|
||||||
|
let style = if active { "color:black;" } else { "color:lightgray;" };
|
||||||
|
|
||||||
|
let newline = format!("<p style=\"{}\">{:x} {} {} {}\n<ul>\n",
|
||||||
|
style,
|
||||||
|
chunk_offset + i*0x4000,
|
||||||
|
node.level,
|
||||||
|
node.items.len(),
|
||||||
|
node.generation);
|
||||||
|
result.push_str(&newline);
|
||||||
|
|
||||||
|
for item in &node.items {
|
||||||
|
let newline = format!("<li style=\"{}\">{:016x} {:?} {:x}</li>\n",
|
||||||
|
style,
|
||||||
|
item.key.key_id,
|
||||||
|
item.key.key_type,
|
||||||
|
item.key.key_offset);
|
||||||
|
result.push_str(&newline);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str("</ul></p>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Response::html(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************************************/
|
||||||
|
|
||||||
|
trait FromBytes {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for u8 {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> u8 {
|
||||||
|
bytes[offset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for u16 {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> u16 {
|
||||||
|
u16::from_le_bytes(bytes[offset..offset+2].try_into().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for u32 {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> u32 {
|
||||||
|
u32::from_le_bytes(bytes[offset..offset+4].try_into().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for u64 {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> u64 {
|
||||||
|
u64::from_le_bytes(bytes[offset..offset+8].try_into().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> FromBytes for [u8; N] {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> [u8; N] {
|
||||||
|
bytes[offset..offset+N].try_into().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for BtrfsKey {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> BtrfsKey {
|
||||||
|
BtrfsKey {
|
||||||
|
key_id: FromBytes::get(bytes, offset),
|
||||||
|
key_type: itemtype_from_code(FromBytes::get(bytes, offset+8)),
|
||||||
|
key_offset: FromBytes::get(bytes, offset+9),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for BtrfsTime {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> BtrfsTime {
|
||||||
|
BtrfsTime {
|
||||||
|
sec: FromBytes::get(bytes, offset),
|
||||||
|
nsec: FromBytes::get(bytes, offset+8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for BtrfsBlockGroupItem {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> BtrfsBlockGroupItem {
|
||||||
|
BtrfsBlockGroupItem {
|
||||||
|
used: FromBytes::get(bytes, 0),
|
||||||
|
chunk_objectid: FromBytes::get(bytes, 8),
|
||||||
|
flags: FromBytes::get(bytes, 16),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for BtrfsExtentItem {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> BtrfsExtentItem {
|
||||||
|
BtrfsExtentItem {
|
||||||
|
refs: FromBytes::get(bytes, 0),
|
||||||
|
generation: FromBytes::get(bytes, 8),
|
||||||
|
flags: FromBytes::get(bytes, 16),
|
||||||
|
data: Vec::from(&bytes[24..]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for BtrfsChunkItem {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> BtrfsChunkItem {
|
||||||
|
let data_slice = &bytes[offset..];
|
||||||
|
let nrstripes: u16 = FromBytes::get(data_slice, 0x2c);
|
||||||
|
let mut stripes: Vec<BtrfsChunkItemStripe> = Vec::new();
|
||||||
|
|
||||||
|
for i in 0..nrstripes as usize {
|
||||||
|
stripes.push(BtrfsChunkItemStripe {
|
||||||
|
devid: FromBytes::get(data_slice, 0x30 + i*0x20),
|
||||||
|
offset: FromBytes::get(data_slice, 0x38 + i*0x20),
|
||||||
|
devuuid: FromBytes::get(data_slice, 0x40 + i*0x20),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BtrfsChunkItem {
|
||||||
|
size: FromBytes::get(data_slice, 0x00),
|
||||||
|
root: FromBytes::get(data_slice, 0x08),
|
||||||
|
stripelen: FromBytes::get(data_slice, 0x10),
|
||||||
|
chunktype: FromBytes::get(data_slice, 0x18),
|
||||||
|
align: FromBytes::get(data_slice, 0x20),
|
||||||
|
width: FromBytes::get(data_slice, 0x24),
|
||||||
|
sectorsize: FromBytes::get(data_slice, 0x28),
|
||||||
|
nrstripes: FromBytes::get(data_slice, 0x2c),
|
||||||
|
substripes: FromBytes::get(data_slice, 0x2e),
|
||||||
|
stripes: stripes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for BtrfsInodeItem {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> BtrfsInodeItem {
|
||||||
|
let data_slice = &bytes[offset..];
|
||||||
|
BtrfsInodeItem {
|
||||||
|
generation: FromBytes::get(data_slice, 0x00),
|
||||||
|
transid: FromBytes::get(data_slice, 0x08),
|
||||||
|
size: FromBytes::get(data_slice, 0x10),
|
||||||
|
nbytes: FromBytes::get(data_slice, 0x18),
|
||||||
|
block_group: FromBytes::get(data_slice, 0x20),
|
||||||
|
nlink: FromBytes::get(data_slice, 0x28),
|
||||||
|
uid: FromBytes::get(data_slice, 0x2c),
|
||||||
|
gid: FromBytes::get(data_slice, 0x30),
|
||||||
|
mode: FromBytes::get(data_slice, 0x34),
|
||||||
|
rdev: FromBytes::get(data_slice, 0x38),
|
||||||
|
flags: FromBytes::get(data_slice, 0x40),
|
||||||
|
sequence: FromBytes::get(data_slice, 0x48),
|
||||||
|
atime: FromBytes::get(data_slice, 0x54),
|
||||||
|
ctime: FromBytes::get(data_slice, 0x60),
|
||||||
|
mtime: FromBytes::get(data_slice, 0x6c),
|
||||||
|
otime: FromBytes::get(data_slice, 0x78),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromBytes for BtrfsRootItem {
|
||||||
|
fn get(bytes: &[u8], offset: usize) -> BtrfsRootItem {
|
||||||
|
let data_slice = &bytes[offset..];
|
||||||
|
BtrfsRootItem {
|
||||||
|
inode: FromBytes::get(data_slice, 0x00),
|
||||||
|
generation: FromBytes::get(data_slice, 0xa0),
|
||||||
|
root_dirid: FromBytes::get(data_slice, 0xa8),
|
||||||
|
bytenr: FromBytes::get(data_slice, 0xb0),
|
||||||
|
byte_limit: FromBytes::get(data_slice, 0xb9),
|
||||||
|
bytes_used: FromBytes::get(data_slice, 0xc0),
|
||||||
|
last_snapshot: FromBytes::get(data_slice, 0xc8),
|
||||||
|
flags: FromBytes::get(data_slice, 0xd0),
|
||||||
|
refs: FromBytes::get(data_slice, 0xd8),
|
||||||
|
drop_progress: FromBytes::get(data_slice, 0xdc),
|
||||||
|
drop_level: FromBytes::get(data_slice, 0xed),
|
||||||
|
level: FromBytes::get(data_slice, 0xee),
|
||||||
|
|
||||||
|
/*
|
||||||
|
generation_v2: FromBytes::get(data_slice, 0xd3),
|
||||||
|
uuid: FromBytes::get(data_slice, 0xeb),
|
||||||
|
parent_uuid: FromBytes::get(data_slice, 0xfb),
|
||||||
|
received_uuid: FromBytes::get(data_slice, 0x10b),
|
||||||
|
ctransid: FromBytes::get(data_slice, 0x11b),
|
||||||
|
otransid: FromBytes::get(data_slice, 0x123),
|
||||||
|
stransid: FromBytes::get(data_slice, 0x12b),
|
||||||
|
rtransid: FromBytes::get(data_slice, 0x133),
|
||||||
|
ctime: FromBytes::get(data_slice, 0x13b),
|
||||||
|
otime: FromBytes::get(data_slice, 0x147),
|
||||||
|
stime: FromBytes::get(data_slice, 0x153),
|
||||||
|
rtime: FromBytes::get(data_slice, 0x15f),
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn read_node_log(image: &[u8], trans: &AddressTranslation, log: u64) -> Option<Box<BtrfsNode>> {
|
||||||
|
let phys = trans.to_phys(log)?;
|
||||||
|
Some(read_node(image, phys as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_node(image: &[u8], offset: usize) -> Box<BtrfsNode> {
|
||||||
|
let mut result = Box::new(BtrfsNode {
|
||||||
|
csum: FromBytes::get(image, offset),
|
||||||
|
fs_uid: FromBytes::get(image, offset + 0x20),
|
||||||
|
bytenr: FromBytes::get(image, offset + 0x30),
|
||||||
|
flags: FromBytes::get(image, offset + 0x38),
|
||||||
|
chunk_tree_uid: FromBytes::get(image, offset + 0x40),
|
||||||
|
generation: FromBytes::get(image, offset + 0x50),
|
||||||
|
owner: FromBytes::get(image, offset + 0x58),
|
||||||
|
nritems: FromBytes::get(image, offset + 0x60),
|
||||||
|
level: FromBytes::get(image, offset + 0x64),
|
||||||
|
items: Vec::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// assuming leaf for now
|
||||||
|
|
||||||
|
for i in 0..result.nritems as usize {
|
||||||
|
let key_id: u64 = FromBytes::get(image, offset + 0x65 + i*0x19);
|
||||||
|
let key_type_code: u8 = FromBytes::get(image, offset + 0x65 + i*0x19 + 0x08);
|
||||||
|
let key_offset: u64 = FromBytes::get(image, offset + 0x65 + i*0x19 + 0x09);
|
||||||
|
let data_offset: u32 = FromBytes::get(image, offset + 0x65 + i*0x19 + 0x11);
|
||||||
|
let data_size: u32 = FromBytes::get(image, offset + 0x65 + i*0x19 + 0x15);
|
||||||
|
|
||||||
|
let key_type = itemtype_from_code(key_type_code);
|
||||||
|
let data_slice = &image[(offset + 0x65 + data_offset as usize) .. (offset + 0x65 + data_offset as usize + data_size as usize)];
|
||||||
|
|
||||||
|
let value = match key_type {
|
||||||
|
BtrfsItemType::BlockGroup => BtrfsValue::BlockGroup(FromBytes::get(data_slice, 0)),
|
||||||
|
BtrfsItemType::Metadata => BtrfsValue::Extent(FromBytes::get(data_slice, 0)),
|
||||||
|
BtrfsItemType::Chunk => BtrfsValue::Chunk(FromBytes::get(data_slice, 0)),
|
||||||
|
BtrfsItemType::Root => BtrfsValue::Root(FromBytes::get(data_slice, 0)),
|
||||||
|
_ => BtrfsValue::Unknown(Vec::from(data_slice)),
|
||||||
|
};
|
||||||
|
|
||||||
|
result.items.push(BtrfsItem {
|
||||||
|
key: BtrfsKey {
|
||||||
|
key_id: key_id,
|
||||||
|
key_type: key_type,
|
||||||
|
key_offset: key_offset,
|
||||||
|
},
|
||||||
|
value: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn itemtype_from_code(code: u8) -> BtrfsItemType {
|
||||||
|
match BTRFS_ITEM_TYPE_VALUES.binary_search_by_key(&code, |x|*x as u8) {
|
||||||
|
Ok(i) => BTRFS_ITEM_TYPE_VALUES[i],
|
||||||
|
Err(_) => { panic!("expected BtrfsItemType, found {code}"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum BtrfsItemType {
|
||||||
|
Invalid = 0x00,
|
||||||
|
Inode = 0x01,
|
||||||
|
Ref = 0x0c,
|
||||||
|
ExtRef = 0x0d,
|
||||||
|
XAttr = 0x18,
|
||||||
|
VerityDesc = 0x24,
|
||||||
|
VerityMerkle = 0x25,
|
||||||
|
Orphan = 0x30,
|
||||||
|
DirLog = 0x3c,
|
||||||
|
DirLogIndex = 0x48,
|
||||||
|
Dir = 0x54,
|
||||||
|
DirIndex = 0x60,
|
||||||
|
ExtentData = 0x6c,
|
||||||
|
ExtentCsum = 0x80,
|
||||||
|
Root = 0x84,
|
||||||
|
RootBackref = 0x90,
|
||||||
|
RootRef = 0x9c,
|
||||||
|
Extent = 0xa8,
|
||||||
|
Metadata = 0xa9,
|
||||||
|
TreeBlockRef = 0xb0,
|
||||||
|
ExtentDataRef = 0xb2,
|
||||||
|
ExtentRefV0 = 0xb4,
|
||||||
|
SharedBlockRef = 0xb6,
|
||||||
|
SharedDataRef = 0xb8,
|
||||||
|
BlockGroup = 0xc0,
|
||||||
|
FreeSpaceInfo = 0xc6,
|
||||||
|
FreeSpaceExtent = 0xc7,
|
||||||
|
FreeSpaceBitmap = 0xc8,
|
||||||
|
DevExtent = 0xcc,
|
||||||
|
Dev = 0xd8,
|
||||||
|
Chunk = 0xe4,
|
||||||
|
QGroupStatus = 0xf0,
|
||||||
|
QGroupInfo = 0xf2,
|
||||||
|
QGroupLimit = 0xf4,
|
||||||
|
QGroupRelation = 0xf6,
|
||||||
|
Temporary = 0xf8,
|
||||||
|
Persistent = 0xf9,
|
||||||
|
DevReplace = 0xfa,
|
||||||
|
UUIDSubvol = 0xfb,
|
||||||
|
UUIDReceivedSubvol = 0xfc,
|
||||||
|
String = 0xfd,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BTRFS_ITEM_TYPE_VALUES: &[BtrfsItemType] = &[BtrfsItemType::Invalid, BtrfsItemType::Inode, BtrfsItemType::Ref, BtrfsItemType::ExtRef, BtrfsItemType::XAttr, BtrfsItemType::VerityDesc, BtrfsItemType::VerityMerkle, BtrfsItemType::Orphan, BtrfsItemType::DirLog, BtrfsItemType::DirLogIndex, BtrfsItemType::Dir, BtrfsItemType::DirIndex, BtrfsItemType::ExtentData, BtrfsItemType::ExtentCsum, BtrfsItemType::Root, BtrfsItemType::RootBackref, BtrfsItemType::RootRef, BtrfsItemType::Extent, BtrfsItemType::Metadata, BtrfsItemType::TreeBlockRef, BtrfsItemType::ExtentDataRef, BtrfsItemType::ExtentRefV0, BtrfsItemType::SharedBlockRef, BtrfsItemType::SharedDataRef, BtrfsItemType::BlockGroup, BtrfsItemType::FreeSpaceInfo, BtrfsItemType::FreeSpaceExtent, BtrfsItemType::FreeSpaceBitmap, BtrfsItemType::DevExtent, BtrfsItemType::Dev, BtrfsItemType::Chunk, BtrfsItemType::QGroupStatus, BtrfsItemType::QGroupInfo, BtrfsItemType::QGroupLimit, BtrfsItemType::QGroupRelation, BtrfsItemType::Temporary, BtrfsItemType::Persistent, BtrfsItemType::DevReplace, BtrfsItemType::UUIDSubvol, BtrfsItemType::UUIDReceivedSubvol, BtrfsItemType::String];
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsNode {
|
||||||
|
csum: [u8; 32],
|
||||||
|
fs_uid: BtrfsUUID,
|
||||||
|
bytenr: u64,
|
||||||
|
flags: u64,
|
||||||
|
chunk_tree_uid: BtrfsUUID,
|
||||||
|
generation: u64,
|
||||||
|
owner: u64,
|
||||||
|
nritems: u32,
|
||||||
|
level: u8,
|
||||||
|
|
||||||
|
items: Vec<BtrfsItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsItem {
|
||||||
|
key: BtrfsKey,
|
||||||
|
value: BtrfsValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsKey {
|
||||||
|
key_id: u64,
|
||||||
|
key_type: BtrfsItemType,
|
||||||
|
key_offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum BtrfsValue {
|
||||||
|
NodePtr(u64),
|
||||||
|
Extent(BtrfsExtentItem),
|
||||||
|
BlockGroup(BtrfsBlockGroupItem),
|
||||||
|
Chunk(BtrfsChunkItem),
|
||||||
|
Root(BtrfsRootItem),
|
||||||
|
Unknown(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsExtentItem {
|
||||||
|
refs: u64,
|
||||||
|
generation: u64,
|
||||||
|
flags: u64,
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsBlockGroupItem {
|
||||||
|
used: u64,
|
||||||
|
chunk_objectid: u64,
|
||||||
|
flags: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsDevItem {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsChunkItem {
|
||||||
|
size: u64,
|
||||||
|
root: u64,
|
||||||
|
stripelen: u64,
|
||||||
|
chunktype: u64,
|
||||||
|
align: u32,
|
||||||
|
width: u32,
|
||||||
|
sectorsize: u32,
|
||||||
|
nrstripes: u16,
|
||||||
|
substripes: u16,
|
||||||
|
|
||||||
|
stripes: Vec<BtrfsChunkItemStripe>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsChunkItemStripe {
|
||||||
|
devid: u64,
|
||||||
|
offset: u64,
|
||||||
|
devuuid: BtrfsUUID,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsTime {
|
||||||
|
sec: u64,
|
||||||
|
nsec: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
type BtrfsUUID = [u8; 16];
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsInodeItem {
|
||||||
|
generation: u64,
|
||||||
|
transid: u64,
|
||||||
|
size: u64,
|
||||||
|
nbytes: u64,
|
||||||
|
block_group: u64,
|
||||||
|
nlink: u32,
|
||||||
|
uid: u32,
|
||||||
|
gid: u32,
|
||||||
|
mode: u32,
|
||||||
|
rdev: u64,
|
||||||
|
flags: u64,
|
||||||
|
sequence: u64,
|
||||||
|
atime: BtrfsTime,
|
||||||
|
ctime: BtrfsTime,
|
||||||
|
mtime: BtrfsTime,
|
||||||
|
otime: BtrfsTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BtrfsRootItem {
|
||||||
|
inode: BtrfsInodeItem,
|
||||||
|
generation: u64,
|
||||||
|
root_dirid: u64,
|
||||||
|
bytenr: u64,
|
||||||
|
byte_limit: u64,
|
||||||
|
bytes_used: u64,
|
||||||
|
last_snapshot: u64,
|
||||||
|
flags: u64,
|
||||||
|
refs: u32,
|
||||||
|
drop_progress: BtrfsKey,
|
||||||
|
drop_level: u8,
|
||||||
|
level: u8,
|
||||||
|
|
||||||
|
/*
|
||||||
|
generation_v2: u64,
|
||||||
|
uuid: BtrfsUUID,
|
||||||
|
parent_uuid: BtrfsUUID,
|
||||||
|
received_uuid: BtrfsUUID,
|
||||||
|
ctransid: u64,
|
||||||
|
otransid: u64,
|
||||||
|
stransid: u64,
|
||||||
|
rtransid: u64,
|
||||||
|
ctime: BtrfsTime,
|
||||||
|
otime: BtrfsTime,
|
||||||
|
stime: BtrfsTime,
|
||||||
|
rtime: BtrfsTime,
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AddressTranslation {
|
||||||
|
addr_map: Vec<(u64,u64,Vec<(u64,u64)>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: support for internal nodes, multiple devices?
|
||||||
|
impl AddressTranslation {
|
||||||
|
fn new(image: &[u8]) -> AddressTranslation {
|
||||||
|
let bootstrap_addr = AddressTranslation::from_superblock(&image);
|
||||||
|
|
||||||
|
let chunk_root_log: u64 = FromBytes::get(&image, 0x10058);
|
||||||
|
println!("Chunk Tree Root Logical Address: {:016x}", chunk_root_log);
|
||||||
|
|
||||||
|
let chunk_root_phys = bootstrap_addr.to_phys(chunk_root_log).unwrap();
|
||||||
|
println!("Chunk Tree Root Physical Address: {:016x}", chunk_root_phys);
|
||||||
|
|
||||||
|
let chunk_root = read_node(&image, chunk_root_phys as usize);
|
||||||
|
|
||||||
|
let mut addr_map = Vec::new();
|
||||||
|
for item in chunk_root.items {
|
||||||
|
let chunk_key = item.key;
|
||||||
|
if let BtrfsValue::Chunk(chunk_value) = item.value {
|
||||||
|
addr_map.push((
|
||||||
|
chunk_key.key_offset,
|
||||||
|
chunk_value.size,
|
||||||
|
chunk_value.stripes.iter()
|
||||||
|
.map(|stripe|(stripe.devid, stripe.offset))
|
||||||
|
.collect()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_map.sort_by_key(|x|x.0);
|
||||||
|
println!("Address Table: {:?}", addr_map);
|
||||||
|
AddressTranslation { addr_map }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_superblock(image: &[u8]) -> AddressTranslation {
|
||||||
|
let sys_chunk_array_size: u32 = FromBytes::get(&image, 0x100a0);
|
||||||
|
let mut addr_map = Vec::new();
|
||||||
|
let mut len: usize = 0;
|
||||||
|
|
||||||
|
while len < sys_chunk_array_size as usize {
|
||||||
|
let chunk_key: BtrfsKey = FromBytes::get(&image, 0x1032b + len);
|
||||||
|
let chunk_value: BtrfsChunkItem = FromBytes::get(&image, 0x1033c + len);
|
||||||
|
|
||||||
|
addr_map.push((
|
||||||
|
chunk_key.key_offset,
|
||||||
|
chunk_value.size,
|
||||||
|
chunk_value.stripes.iter()
|
||||||
|
.map(|stripe|(stripe.devid, stripe.offset))
|
||||||
|
.collect()
|
||||||
|
));
|
||||||
|
len += 0x41 + 0x20 * chunk_value.stripes.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_map.sort_by_key(|x|x.0);
|
||||||
|
println!("Bootstrap Address Table: {:?}", addr_map);
|
||||||
|
AddressTranslation { addr_map }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_phys(&self, log: u64) -> Option<u64> {
|
||||||
|
let index = match self.addr_map.binary_search_by_key(&log, |x|x.0) {
|
||||||
|
Ok(idx) => idx as usize,
|
||||||
|
Err(idx) => if idx == 0 {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
idx-1 as usize
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let log_offset = self.addr_map[index].0;
|
||||||
|
let size = self.addr_map[index].1;
|
||||||
|
let phys_offset = self.addr_map[index].2[0].1;
|
||||||
|
|
||||||
|
if log >= log_offset && log < log_offset + size {
|
||||||
|
Some(phys_offset + (log - log_offset))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user