last somewhat working version before reimplementing tree traversal
This commit is contained in:
parent
d5f7ff4c14
commit
bbc1970bbe
@ -1,9 +1,11 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::ops::Deref;
|
use std::ops::{Deref, RangeBounds};
|
||||||
|
|
||||||
use crate::btrfs_structs::{Leaf, Key, Item, InteriorNode, Node, ParseError, ParseBin, Value, Superblock, ItemType};
|
use crate::btrfs_structs::{Leaf, Key, Item, InteriorNode, Node, ParseError, ParseBin, Value, Superblock, ItemType};
|
||||||
use crate::addrmap::{node_at_log, LogToPhys, AddressMap};
|
use crate::addrmap::{node_at_log, LogToPhys, AddressMap};
|
||||||
|
|
||||||
|
/// represents a B-Tree inside a filesystem image. Can be used to look up keys,
|
||||||
|
/// and handles the tree traversal and the virtual address translation.
|
||||||
pub struct Tree<'a> {
|
pub struct Tree<'a> {
|
||||||
pub image: &'a [u8],
|
pub image: &'a [u8],
|
||||||
pub addr_map: Rc<AddressMap>,
|
pub addr_map: Rc<AddressMap>,
|
||||||
@ -16,7 +18,7 @@ impl<'a> Tree<'a> {
|
|||||||
let superblock = Superblock::parse(&image[0x10000..])?;
|
let superblock = Superblock::parse(&image[0x10000..])?;
|
||||||
|
|
||||||
let root_tree = Tree {
|
let root_tree = Tree {
|
||||||
image: image,
|
image,
|
||||||
addr_map: Rc::clone(&addr_map),
|
addr_map: Rc::clone(&addr_map),
|
||||||
root_addr_log: superblock.root
|
root_addr_log: superblock.root
|
||||||
};
|
};
|
||||||
@ -94,7 +96,7 @@ impl Tree<'_> {
|
|||||||
|
|
||||||
/***** iterator *****/
|
/***** iterator *****/
|
||||||
|
|
||||||
pub struct Iter<'a> {
|
pub struct RangeIter<'a, R: RangeBounds<Key>, F: Fn(Key) -> Key = fn(Key) -> Key> {
|
||||||
tree: &'a Tree<'a>,
|
tree: &'a Tree<'a>,
|
||||||
|
|
||||||
// path to the last returned item
|
// path to the last returned item
|
||||||
@ -102,27 +104,28 @@ pub struct Iter<'a> {
|
|||||||
leaf: Option<Box<Leaf>>,
|
leaf: Option<Box<Leaf>>,
|
||||||
indices: Vec<usize>,
|
indices: Vec<usize>,
|
||||||
|
|
||||||
lower_limit: Option<Key>,
|
bounds: R,
|
||||||
upper_limit: Option<Key>,
|
skip_fn: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tree<'_> {
|
impl Tree<'_> {
|
||||||
pub fn iter<'a>(&'a self) -> Iter<'a> {
|
pub fn iter<'a>(&'a self) -> RangeIter<'a> {
|
||||||
self.range(None, None)
|
self.range(None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range<'a>(&'a self, lower: Option<Key>, upper: Option<Key>) -> Iter<'a> {
|
pub fn range<'a>(&'a self, lower: Option<Key>, upper: Option<Key>) -> RangeIter<'a> {
|
||||||
Iter {
|
RangeIter {
|
||||||
tree: self,
|
tree: self,
|
||||||
nodes: Vec::new(),
|
nodes: Vec::new(),
|
||||||
leaf: None,
|
leaf: None,
|
||||||
indices: Vec::new(), // in nodes and leaf
|
indices: Vec::new(), // in nodes and leaf
|
||||||
lower_limit: lower,
|
lower_limit: lower,
|
||||||
upper_limit: upper,
|
upper_limit: upper,
|
||||||
|
skip_fn: |x|x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range_id<'a>(&'a self, id: u64) -> Iter<'a> {
|
pub fn range_id<'a>(&'a self, id: u64) -> RangeIter<'a> {
|
||||||
if id == u64::MAX {
|
if id == u64::MAX {
|
||||||
self.range(
|
self.range(
|
||||||
Some(Key::new(id, ItemType::Invalid, 0)),
|
Some(Key::new(id, ItemType::Invalid, 0)),
|
||||||
@ -135,9 +138,29 @@ impl Tree<'_> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// given a tree, a range of indices, and two "skip functions", produces a double
|
||||||
|
/// ended iterator which iterates through the keys contained in the range, in ascending
|
||||||
|
/// or descending order.
|
||||||
|
|
||||||
|
/// the skip functions are ignored for now, but are intended as an optimization:
|
||||||
|
/// after a key `k` was returned by the iterator (or the reverse iterator), all keys
|
||||||
|
/// strictly lower than `forward_skip_fn(k)` are skipped (resp. all keys strictly above
|
||||||
|
/// `backward_skip_fn` are skipped.
|
||||||
|
pub fn range_with_skip<'a, R, F>(&'a self, range: R, forward_skip_fn: F, backward_skip_fn: F) -> RangeIter<'a, F>
|
||||||
|
where
|
||||||
|
R: RangeBounds<Key>,
|
||||||
|
F: Fn(Key) -> Key {
|
||||||
|
RangeIter {
|
||||||
|
tree: self,
|
||||||
|
nodes: Vec::new(),
|
||||||
|
leaf: None,
|
||||||
|
indices: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iter<'_> {
|
impl<F: Fn(Key) -> Key> RangeIter<'_, F> {
|
||||||
fn move_down_and_get_first_item(&mut self, mut node_addr: u64) -> Option<Item> {
|
fn move_down_and_get_first_item(&mut self, mut node_addr: u64) -> Option<Item> {
|
||||||
loop {
|
loop {
|
||||||
let node = Node::parse(node_at_log(self.tree.image, self.tree.addr_map.deref(), node_addr).ok()?).ok()?;
|
let node = Node::parse(node_at_log(self.tree.image, self.tree.addr_map.deref(), node_addr).ok()?).ok()?;
|
||||||
@ -190,10 +213,9 @@ impl Iter<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for Iter<'_> {
|
impl<F: Fn(Key) -> Key> Iterator for RangeIter<'_, F> {
|
||||||
type Item = Item;
|
type Item = Item;
|
||||||
|
|
||||||
// for now we just silently stop when we encounter an error, maybe that isn't the best solution
|
// for now we just silently stop when we encounter an error, maybe that isn't the best solution
|
||||||
@ -267,7 +289,7 @@ impl Iterator for Iter<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// first first item under this node
|
// first first item under this node
|
||||||
self.move_down_and_get_first_item(node_addr)
|
return self.move_down_and_get_first_item(node_addr)
|
||||||
.filter(|item|self.upper_limit.is_none() || item.key < self.upper_limit.unwrap())
|
.filter(|item|self.upper_limit.is_none() || item.key < self.upper_limit.unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,16 @@ pub struct Key {
|
|||||||
pub key_offset: u64,
|
pub key_offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
pub fn new(key_id: u64, key_type: ItemType, key_offset: u64) -> Key {
|
||||||
|
Key { key_id, key_type, key_offset }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(key_id: u64) -> Key {
|
||||||
|
Key { key_id, key_type: ItemType::Invalid, key_offset: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug,Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
@ -709,8 +719,16 @@ impl fmt::Debug for Checksum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Key {
|
macro_rules! key {
|
||||||
pub fn new(key_id: u64, key_type: ItemType, key_offset: u64) -> Key {
|
($arg1:expr) => {
|
||||||
Key { key_id, key_type, key_offset }
|
btrfs_structs::Key { key_id: $arg1, key_type: btrfs_structs::ItemType::Invalid, key_offset: 0 }
|
||||||
}
|
};
|
||||||
|
($arg1:expr, $arg2:expr) => {
|
||||||
|
btrfs_structs::Key { key_id: $arg1, key_type: $arg2, key_offset: 0 }
|
||||||
|
};
|
||||||
|
($arg1:expr, $arg2:expr, $arg3:expr) => {
|
||||||
|
btrfs_structs::Key { key_id: $arg1, key_type: $arg2, key_offset: $arg3 }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) use key;
|
||||||
|
26
src/main.rs
26
src/main.rs
@ -1,17 +1,15 @@
|
|||||||
use memmap2::{Mmap, MmapOptions};
|
use std::{
|
||||||
use std::fs::File;
|
iter,
|
||||||
use rouille::Request;
|
env,
|
||||||
use rouille::Response;
|
fs::OpenOptions,
|
||||||
use rouille::router;
|
collections::HashMap,
|
||||||
use std::iter;
|
};
|
||||||
use std::env;
|
use memmap2::Mmap;
|
||||||
use std::{fs::OpenOptions, os::unix::fs::OpenOptionsExt};
|
use rouille::{Request, Response, router};
|
||||||
use std::collections::HashMap;
|
use parsebtrfs::{
|
||||||
|
btrfs_structs::{TreeID, Value::Extent, Value::BlockGroup, ParseError, NODE_SIZE, ItemType},
|
||||||
use parsebtrfs::btrfs_structs::{TreeID, Value::Extent, Value::BlockGroup, ParseError, NODE_SIZE, ItemType, Node, ParseBin};
|
btrfs_lookup::Tree,
|
||||||
use parsebtrfs::btrfs_lookup::Tree;
|
};
|
||||||
|
|
||||||
use parsebtrfs::addrmap::{AddressMap, LogToPhys};
|
|
||||||
|
|
||||||
const COLORS: &[&str] = &["#e6194b", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#46f0f0", "#f032e6", "#bcf60c", "#fabebe", "#008080", "#e6beff", "#9a6324", "#fffac8", "#800000", "#aaffc3", "#808000", "#ffd8b1", "#000075", "#808080", "#000000"];
|
const COLORS: &[&str] = &["#e6194b", "#3cb44b", "#ffe119", "#4363d8", "#f58231", "#911eb4", "#46f0f0", "#f032e6", "#bcf60c", "#fabebe", "#008080", "#e6beff", "#9a6324", "#fffac8", "#800000", "#aaffc3", "#808000", "#ffd8b1", "#000075", "#808080", "#000000"];
|
||||||
|
|
||||||
|
20
src/test.rs
20
src/test.rs
@ -1,5 +1,9 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use btrfs_structs::{Key, ItemType};
|
use btrfs_structs::key;
|
||||||
|
use btrfs_structs::{Key, ItemType, TreeID};
|
||||||
|
use btrfs_lookup::Tree;
|
||||||
|
use memmap2::Mmap;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_new() {
|
fn test_key_new() {
|
||||||
@ -8,3 +12,17 @@ fn test_key_new() {
|
|||||||
Key { key_id: 1, key_type: ItemType::Root, key_offset: 2 }
|
Key { key_id: 1, key_type: ItemType::Root, key_offset: 2 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn image_test() {
|
||||||
|
let file = OpenOptions::new().read(true).open("../image").unwrap();
|
||||||
|
let image = unsafe { Mmap::map(&file).unwrap() };
|
||||||
|
|
||||||
|
let extent_tree = Tree::new(&image, TreeID::Extent).unwrap();
|
||||||
|
|
||||||
|
let skip = |Key { key_id, key_type, key_offset }| Key { key_id: key_id + 0x10000, key_type, key_offset };
|
||||||
|
|
||||||
|
for item in extent_tree.range_with_skip(Some(key!(0x1d70000)), Some(key!(0x2000000)), skip) {
|
||||||
|
println!("{item:x?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user