basic derive functionality
This commit is contained in:
		@@ -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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										459
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										459
									
								
								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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user