use std::rc::Rc; use crate::btrfs_structs::{ParseBin, Key, ChunkItem, Value, Superblock, ParseError, NODE_SIZE}; use crate::btrfs_lookup::Tree; #[derive(Debug, Clone)] pub struct AddressMap(pub Vec<(u64,u64,Vec<(u64,u64)>)>); // TODO: support for internal nodes, multiple devices? impl AddressMap { pub fn new(image: &[u8]) -> Result { let superblock = Superblock::parse(&image[0x10000..])?; let bootstrap_addr = AddressMap::from_superblock(&superblock)?; let chunk_tree = Tree { image: image, addr_map: Rc::new(bootstrap_addr), root_addr_log: superblock.chunk_root, }; let mut addr_map = Vec::new(); for item in chunk_tree.iter() { let chunk_key = item.key; if let Value::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() )); } } // almost certainly unnecessary? addr_map.sort_by_key(|x|x.0); // addr_map.iter().for_each(|x| println!("{:x?}", x)); Ok(AddressMap(addr_map)) } pub fn from_superblock(superblock: &Superblock) -> Result { let sys_chunk_array_size = superblock.sys_chunk_array_size; let sys_chunk_array: &[u8] = &superblock.sys_chunk_array; let mut addr_map = Vec::new(); let mut len: usize = 0; while len < sys_chunk_array_size as usize { let chunk_key: Key = Key::parse(&sys_chunk_array[len .. ])?; let chunk_value: ChunkItem = ChunkItem::parse(&sys_chunk_array[len+0x11 .. ])?; 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); Ok(AddressMap(addr_map)) } } impl LogToPhys for AddressMap { fn to_phys(&self, log: u64) -> Option { let index = match self.0.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.0[index].0; let size = self.0[index].1; let phys_offset = self.0[index].2[0].1; if log >= log_offset && log < log_offset + size { Some(phys_offset + (log - log_offset)) } else { None } } } pub fn node_at_log<'a, T: LogToPhys>(image: &'a [u8], addr: &T, log: u64) -> Result<&'a [u8], ParseError> { if let Some(phys_addr) = addr.to_phys(log) { Ok(&image[phys_addr as usize .. phys_addr as usize + NODE_SIZE]) } else { err!("Logical address {:x} could not be translated to physical address", log) } } pub trait LogToPhys { fn to_phys(&self, log: u64) -> Option; }