basic derive functionality
This commit is contained in:
parent
bdcc597b9a
commit
7ac28455cc
@ -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
522
src/btrfs_structs.rs
Normal 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)
|
||||
}
|
||||
}
|
455
src/main.rs
455
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<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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user