btrfs_explorer/src/addrmap.rs

115 lines
3.0 KiB
Rust

use std::rc::Rc;
use crate::btrfs_structs::{ParseBin, Key, ChunkItem, Value, Superblock, ParseError, NODE_SIZE};
use crate::btrfs_lookup::Tree;
use crate::nodereader::NodeReader;
#[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<AddressMap, ParseError> {
let superblock = Superblock::parse(&image[0x10000..])?;
let bootstrap_addr = AddressMap::from_superblock(&superblock)?;
let reader = Rc::new(NodeReader::with_addrmap(image, bootstrap_addr)?);
let chunk_tree = Tree {
image,
reader,
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<AddressMap, ParseError> {
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<u64> {
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<u64>;
fn node_at_log<'a>(&self, image: &'a [u8], log: u64) -> Result<&'a [u8], ParseError> {
if let Some(phys_addr) = self.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)
}
}
}