basic derive functionality

This commit is contained in:
Florian Stecker 2023-07-27 16:00:08 -04:00
parent bdcc597b9a
commit 7ac28455cc
3 changed files with 541 additions and 441 deletions

View File

@ -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"

522
src/btrfs_structs.rs Normal file
View File

@ -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<u8>),
}
#[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<Item>,
}
#[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<u8>,
}
#[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<ChunkItemStripe>,
}
#[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<u8>),
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, String> {
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<const N: usize> 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<u8> for "unknown extra data", so just eat up everything
impl ParseBin for Vec<u8> {
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<ItemType> for u8 {
fn from(value: ItemType) -> u8 {
value as u8
}
}
impl From<u8> 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)
}
}

View File

@ -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<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))
@ -298,278 +149,4 @@ fn read_node(image: &[u8], offset: usize) -> Box<BtrfsNode> {
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
}
}
}
*/