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
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
 | 
					binparse_derive = { path = "../binparse_derive" }
 | 
				
			||||||
memmap2 = "0.7.1"
 | 
					memmap2 = "0.7.1"
 | 
				
			||||||
rouille = "3.6.2"
 | 
					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 memmap2::Mmap;
 | 
				
			||||||
use std::fs::File;
 | 
					use std::fs::File;
 | 
				
			||||||
use rouille::Request;
 | 
					use rouille::Request;
 | 
				
			||||||
use rouille::Response;
 | 
					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> {
 | 
					fn main() -> Result<(), std::io::Error> {
 | 
				
			||||||
	let file = File::open("../image")?;
 | 
						let file = File::open("../image")?;
 | 
				
			||||||
	let image = unsafe { Mmap::map(&file)? };
 | 
						let image = unsafe { Mmap::map(&file)? };
 | 
				
			||||||
@@ -81,172 +95,9 @@ fn http_main_list(image: &[u8], addr: &AddressTranslation, req: &Request) -> Res
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	Response::html(result)
 | 
						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>> {
 | 
					fn read_node_log(image: &[u8], trans: &AddressTranslation, log: u64) -> Option<Box<BtrfsNode>> {
 | 
				
			||||||
	let phys = trans.to_phys(log)?;
 | 
						let phys = trans.to_phys(log)?;
 | 
				
			||||||
	Some(read_node(image, phys as usize))
 | 
						Some(read_node(image, phys as usize))
 | 
				
			||||||
@@ -298,278 +149,4 @@ fn read_node(image: &[u8], offset: usize) -> Box<BtrfsNode> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	result
 | 
						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