From 7ac28455cc9d5a2111b8728a98e2df76141b3b54 Mon Sep 17 00:00:00 2001 From: Florian Stecker Date: Thu, 27 Jul 2023 16:00:08 -0400 Subject: [PATCH] basic derive functionality --- Cargo.toml | 1 + src/btrfs_structs.rs | 522 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 459 ++----------------------------------- 3 files changed, 541 insertions(+), 441 deletions(-) create mode 100644 src/btrfs_structs.rs diff --git a/Cargo.toml b/Cargo.toml index 36817bb..390e56f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +binparse_derive = { path = "../binparse_derive" } memmap2 = "0.7.1" rouille = "3.6.2" diff --git a/src/btrfs_structs.rs b/src/btrfs_structs.rs new file mode 100644 index 0000000..432db3f --- /dev/null +++ b/src/btrfs_structs.rs @@ -0,0 +1,522 @@ +use binparse_derive::AllVariants; +use binparse_derive::ParseBin; +use std::fmt; +use std::ffi::CString; + +/***** BTRFS structures *****/ + +const NODE_SIZE: usize = 0x4000; + +#[allow(unused)] +#[derive(Debug,Clone,Copy,AllVariants)] +#[repr(u8)] +pub enum ItemType { + Invalid = 0x00, + Inode = 0x01, // implemented + Ref = 0x0c, // TODO + ExtRef = 0x0d, + XAttr = 0x18, + VerityDesc = 0x24, + VerityMerkle = 0x25, + Orphan = 0x30, + DirLog = 0x3c, + DirLogIndex = 0x48, + Dir = 0x54, // implemented (better with len feature) + DirIndex = 0x60, // TODO + ExtentData = 0x6c, // implemented + ExtentCsum = 0x80, + Root = 0x84, // implemented + RootBackref = 0x90, + RootRef = 0x9c, + Extent = 0xa8, // implemented (except extra data) + Metadata = 0xa9, // implemented (except extra data) + TreeBlockRef = 0xb0, + ExtentDataRef = 0xb2, + ExtentRefV0 = 0xb4, + SharedBlockRef = 0xb6, + SharedDataRef = 0xb8, + BlockGroup = 0xc0, // implemented + FreeSpaceInfo = 0xc6, // implemented + FreeSpaceExtent = 0xc7, // implemented + FreeSpaceBitmap = 0xc8, + DevExtent = 0xcc, // implemented + Dev = 0xd8, // implemented + Chunk = 0xe4, // implemented? (awaiting len feature) + QGroupStatus = 0xf0, + QGroupInfo = 0xf2, + QGroupLimit = 0xf4, + QGroupRelation = 0xf6, + Temporary = 0xf8, + Persistent = 0xf9, // TODO? + DevReplace = 0xfa, + UUIDSubvol = 0xfb, // implemented + UUIDReceivedSubvol = 0xfc, + String = 0xfd, +} + +#[allow(unused)] +#[derive(Debug,Clone,Copy,ParseBin)] +pub struct Key { + key_id: u64, + key_type: ItemType, + key_offset: u64, +} + +#[allow(unused)] +#[derive(Debug)] +pub enum Value { + Extent(ExtentItem), + BlockGroup(BlockGroupItem), + Inode(InodeItem), + Chunk(ChunkItem), + Root(RootItem), + Dir(DirItem), + FreeSpaceInfo(FreeSpaceInfoItem), + FreeSpaceExtent, + UUIDSubvol(UUIDSubvolItem), + Dev(DevItem), + DevExtent(DevExtentItem), + ExtentData(ExtentDataItem), + Unknown(Vec), +} + +#[allow(unused)] +#[allow(unused)] +#[derive(Debug)] +pub struct Item { + key: Key, + value: Value, +} + +#[allow(unused)] +#[derive(ParseBin)] +pub struct Checksum([u8; 32]); + +#[allow(unused)] +#[derive(ParseBin)] +pub struct UUID([u8; 16]); + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct NodeHeader { + csum: Checksum, + fs_uid: UUID, + bytenr: u64, + flags: u64, + chunk_tree_uid: UUID, + generation: u64, + owner: u64, + nritems: u32, + level: u8, +} + +#[allow(unused)] +#[derive(Debug)] +pub struct Leaf { + header: NodeHeader, + items: Vec, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct BlockGroupItem { + used: u64, + chunk_objectid: u64, + flags: u64, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct ExtentItem { + refs: u64, + generation: u64, + flags: u64, + data: Vec, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct Time { + sec: u64, + nsec: u32, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct InodeItem { + 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, + #[skip_bytes = 32] + atime: Time, + ctime: Time, + mtime: Time, + otime: Time, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct ChunkItem { + size: u64, + root: u64, + stripelen: u64, + chunktype: u64, + align: u32, + width: u32, + sectorsize: u32, + nrstripes: u16, + substripes: u16, + +// #[vector_length = nrstripes] +// stripes: Vec, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct ChunkItemStripe { + devid: u64, + offset: u64, + devuuid: UUID, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct RootItem { + inode: InodeItem, + generation: u64, + root_dirid: u64, + bytenr: u64, + byte_limit: u64, + bytes_used: u64, + last_snapshot: u64, + flags: u64, + refs: u32, + drop_progress: Key, + drop_level: u8, + level: u8, + + generation_v2: u64, + uuid: UUID, + parent_uuid: UUID, + received_uuid: UUID, + ctransid: u64, + otransid: u64, + stransid: u64, + rtransid: u64, + ctime: Time, + otime: Time, + stime: Time, + rtime: Time, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct DirItem { + location: Key, + transid: u64, + data_len: u16, + name_len: u16, + dir_type: u8, + + #[len = "name_len"] + name: CString, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct FreeSpaceInfoItem { + extent_count: u32, + flags: u32, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct UUIDSubvolItem { + subvol_id: u64, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct DevItem { + devid: u64, + total_bytes: u64, + bytes_used: u64, + io_align: u32, + io_width: u32, + sector_size: u32, + dev_type: u64, + generation: u64, + start_offset: u64, + dev_group: u32, + seek_speed: u8, + bandwidth: u8, + uuid: UUID, + fsid: UUID, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct DevExtentItem { + chunk_tree: u64, + chunk_objectid: u64, + chunk_offset: u64, + length: u64, + chunk_tree_uuid: UUID, +} + +#[allow(unused)] +#[derive(Debug)] +pub struct ExtentDataItem { + header: ExtentDataHeader, + data: ExtentDataBody, +} + +#[allow(unused)] +#[derive(Debug)] +pub enum ExtentDataBody { + Inline(Vec), + External(ExternalExtent), +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct ExternalExtent { + disk_bytenr: u64, + disk_num_bytes: u64, + offset: u64, + num_bytes: u64, +} + +#[allow(unused)] +#[derive(Debug,ParseBin)] +pub struct ExtentDataHeader { + generation: u64, + ram_bytes: u64, + compression: u8, + encryption: u8, + other_encoding: u16, + extent_type: u8, +} + +/***** trait for parsing, and implementations for basic types *****/ +// most of the more complex types will be parsed using derive macros + +pub trait ParseBin where Self: Sized { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), String>; + + fn parse(bytes: &[u8]) -> Result { + Self::parse_len(bytes).map(|x|x.0) + } +} + +impl ParseBin for u8 { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), String> { + if bytes.len() < 1 { + Err("not enough data".into()) + } else { + Ok((bytes[0], 1)) + } + } +} + +impl ParseBin for u16 { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), String> { + if bytes.len() < 2 { + Err("not enough data".into()) + } else { + let result = u16::from_le_bytes(bytes[0..2].try_into().unwrap()); + Ok((result, 2)) + } + } +} + +impl ParseBin for u32 { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), String> { + if bytes.len() < 4 { + Err("not enough data".into()) + } else { + let result = u32::from_le_bytes(bytes[0..4].try_into().unwrap()); + Ok((result, 4)) + } + } +} + +impl ParseBin for u64 { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), String> { + if bytes.len() < 8 { + Err("not enough data".into()) + } else { + let result = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); + Ok((result, 8)) + } + } +} + +impl ParseBin for [u8; N] { + fn parse_len(bytes: &[u8]) -> Result<([u8; N], usize), String> { + if bytes.len() < N { + Err("not enough data".into()) + } else { + Ok((bytes[0..N].try_into().unwrap(), 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), String> { + Ok((Vec::from(bytes), bytes.len())) + } +} + +impl ParseBin for CString { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), String> { + let mut chars = Vec::from(bytes); + chars.push(0); + Ok((CString::from_vec_with_nul(chars).unwrap(), bytes.len())) + } +} + +/***** parsing ItemType and Leaf *****/ + +impl From for u8 { + fn from(value: ItemType) -> u8 { + value as u8 + } +} + +impl From for ItemType { + fn from(value: u8) -> ItemType { + let variants = ItemType::all_variants(); + match variants.binary_search_by_key(&value, |x|u8::from(*x)) { + Ok(idx) => variants[idx], + Err(_) => ItemType::Invalid, + } + } +} + +impl ParseBin for ItemType { + fn parse_len(bytes: &[u8]) -> Result<(ItemType, usize), String> { + u8::parse(bytes).map(|x|(ItemType::from(x),1)) + } +} + +impl ParseBin for Leaf { + fn parse_len(bytes: &[u8]) -> Result<(Leaf, usize), String> { + if bytes.len() < 0x65 { + return Err("not enough data".into()); + } + + let header = NodeHeader::parse(&bytes[0..0x65])?; + let mut items = Vec::new(); + + for i in 0..header.nritems as usize { + let key = Key::parse(&bytes[i*0x19 + 0x65 .. i*0x19 + 0x65 + 0x11])?; + let offset = u32::parse(&bytes[i*0x19 + 0x65 + 0x11 .. i*0x19 + 0x65 + 0x15])?; + let size = u32::parse(&bytes[i*0x19 + 0x65 + 0x15 .. i*0x19 + 0x65 + 0x19])?; + + let data_slice = &bytes[0x65 + offset as usize .. + 0x65 + offset as usize + size as usize]; + + let value = match key.key_type { + ItemType::BlockGroup => + Value::BlockGroup(BlockGroupItem::parse(data_slice)?), + ItemType::Metadata => + Value::Extent(ExtentItem::parse(data_slice)?), + ItemType::Extent => + Value::Extent(ExtentItem::parse(data_slice)?), + ItemType::Inode => + Value::Inode(InodeItem::parse(data_slice)?), + ItemType::Root => + Value::Root(RootItem::parse(data_slice)?), + ItemType::Dir => + Value::Dir(DirItem::parse(data_slice)?), + ItemType::Chunk => + Value::Chunk(ChunkItem::parse(data_slice)?), + ItemType::FreeSpaceInfo => + Value::FreeSpaceInfo(FreeSpaceInfoItem::parse(data_slice)?), + ItemType::FreeSpaceExtent => + Value::FreeSpaceExtent, + ItemType::UUIDSubvol => + Value::UUIDSubvol(UUIDSubvolItem::parse(data_slice)?), + ItemType::Dev => + Value::Dev(DevItem::parse(data_slice)?), + ItemType::DevExtent => + Value::DevExtent(DevExtentItem::parse(data_slice)?), + ItemType::ExtentData => + Value::ExtentData(ExtentDataItem::parse(data_slice)?), + + _ => + Value::Unknown(Vec::from(data_slice)), + }; + + items.push(Item { key, value }); + } + + let result = Leaf { + header, + items, + }; + + Ok((result, NODE_SIZE)) + } +} + +// ExtentDataItem needs a custom implementation because it can have inline or external data +impl ParseBin for ExtentDataItem { + fn parse_len(bytes: &[u8]) -> Result<(Self, usize), String> { + 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)}, + header_size + body_size)) + } else { // inline extent + let data_slice = &bytes[header_size..]; + return Ok((ExtentDataItem { + header: header, + data: ExtentDataBody::Inline(Vec::from(data_slice)) + }, header_size + data_slice.len())) + } + } +} + + +/***** prettier debug output for UUIDs and checksums *****/ + +impl fmt::Debug for UUID { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let UUID([x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15]) = self; + write!(f, "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-\ + {:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15) + } +} + +impl fmt::Debug for Checksum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Checksum( + [x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, + x24, x25, x26, x27, x28, x29, x30, x31]) = self; + write!(f, "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}\ + {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}\ + {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}\ + {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, + x24, x25, x26, x27, x28, x29, x30, x31) + } +} diff --git a/src/main.rs b/src/main.rs index 0e6d6ae..46af62a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,24 @@ +mod btrfs_structs; + use memmap2::Mmap; use std::fs::File; use rouille::Request; use rouille::Response; +use btrfs_structs::{ItemType, ParseBin, Key, Item, Leaf, NodeHeader}; -const ACTIVE_NODES: &'static[usize] = &[0x14000, 0x18000, 0x1c000, 0x20000, 0x28000, 0x2c000, 0x3c000, 0x40000]; +//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)? }; + +// println!("{:#?}", Leaf::parse(&image[0x253c000..0x2540000])); + println!("{:#?}", Leaf::parse(&image[0x1d04000..0x1d08000])); + + Ok(()) +} + +/* fn main() -> Result<(), std::io::Error> { let file = File::open("../image")?; let image = unsafe { Mmap::map(&file)? }; @@ -81,172 +95,9 @@ fn http_main_list(image: &[u8], addr: &AddressTranslation, req: &Request) -> Res 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 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 = 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> { let phys = trans.to_phys(log)?; Some(read_node(image, phys as usize)) @@ -298,278 +149,4 @@ fn read_node(image: &[u8], offset: usize) -> Box { 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, -} - -#[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), -} - -#[derive(Debug)] -struct BtrfsExtentItem { - refs: u64, - generation: u64, - flags: u64, - data: Vec, -} - -#[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, -} - -#[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 { - 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 - } - } -} +*/