diff --git a/src/pdata/btree.rs b/src/pdata/btree.rs index 0e2f8df..21b6ce2 100644 --- a/src/pdata/btree.rs +++ b/src/pdata/btree.rs @@ -2,6 +2,7 @@ use anyhow::{anyhow, Result}; use fixedbitset::FixedBitSet; use nom::{number::complete::*, IResult}; use std::sync::{Arc, Mutex}; +use std::collections::BTreeMap; use crate::checksum; use crate::io_engine::*; @@ -307,3 +308,44 @@ impl BTreeWalker { } //------------------------------------------ + +struct ValueCollector { + values: BTreeMap, +} + +impl ValueCollector { + fn new() -> ValueCollector { + ValueCollector { values: BTreeMap::new() } + } +} + +impl NodeVisitor for ValueCollector { + fn visit(&mut self, _w: &BTreeWalker, _b: &Block, node: &Node) -> Result<()> { + if let Node::Leaf { + header: _h, + keys, + values, + } = node + { + for n in 0..keys.len() { + let k = keys[n]; + let v = values[n].clone(); + self.values.insert(k, v); + } + } + + Ok(()) + } +} + +pub fn btree_to_map(engine: Arc, + ignore_non_fatal: bool, + root: u64) -> Result> { + let mut walker = BTreeWalker::new(engine, ignore_non_fatal); + let mut visitor = ValueCollector::::new(); + + walker.walk(&mut visitor, root)?; + Ok(visitor.values) +} + +//------------------------------------------ diff --git a/src/thin/check.rs b/src/thin/check.rs index 07a63eb..9502270 100644 --- a/src/thin/check.rs +++ b/src/thin/check.rs @@ -8,7 +8,7 @@ use threadpool::ThreadPool; use crate::checksum; use crate::io_engine::{AsyncIoEngine, Block, IoEngine, SyncIoEngine}; -use crate::pdata::btree::{unpack, BTreeWalker, Node, NodeVisitor, Unpack}; +use crate::pdata::btree::{btree_to_map, unpack, BTreeWalker, Node, NodeVisitor, Unpack}; use crate::pdata::space_map::*; use crate::thin::superblock::*; @@ -139,37 +139,6 @@ impl Unpack for DeviceDetail { } } -struct DeviceVisitor { - devs: BTreeMap, -} - -impl DeviceVisitor { - pub fn new() -> DeviceVisitor { - DeviceVisitor { - devs: BTreeMap::new(), - } - } -} - -impl NodeVisitor for DeviceVisitor { - fn visit(&mut self, _w: &BTreeWalker, _b: &Block, node: &Node) -> Result<()> { - if let Node::Leaf { - header: _h, - keys, - values, - } = node - { - for n in 0..keys.len() { - let k = keys[n] as u32; - let v = values[n].clone(); - self.devs.insert(k, v); - } - } - - Ok(()) - } -} - //------------------------------------------ struct IndexVisitor { @@ -197,38 +166,6 @@ impl NodeVisitor for IndexVisitor { //------------------------------------------ -// FIXME: move to btree -struct ValueCollector { - values: Vec<(u64, V)>, -} - -impl ValueCollector { - fn new() -> ValueCollector { - ValueCollector { values: Vec::new() } - } -} - -impl NodeVisitor for ValueCollector { - fn visit(&mut self, _w: &BTreeWalker, _b: &Block, node: &Node) -> Result<()> { - if let Node::Leaf { - header: _h, - keys, - values, - } = node - { - for n in 0..keys.len() { - let k = keys[n]; - let v = values[n].clone(); - self.values.push((k, v)); - } - } - - Ok(()) - } -} - -//------------------------------------------ - struct OverflowChecker<'a> { data_sm: &'a dyn SpaceMap, } @@ -288,22 +225,12 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?; // device details - let nr_devs; - { - let mut visitor = DeviceVisitor::new(); - let mut w = BTreeWalker::new(engine.clone(), false); - w.walk(&mut visitor, sb.details_root)?; - nr_devs = visitor.devs.len(); - println!("found {} devices", visitor.devs.len()); - } + let devs = btree_to_map::(engine.clone(), false, sb.details_root)?; + let nr_devs = devs.len(); + println!("found {} devices", nr_devs); // mapping top level - let mut roots = BTreeMap::new(); - { - let mut visitor = TopLevelVisitor { roots: &mut roots }; - let mut w = BTreeWalker::new(engine.clone(), false); - let _result = w.walk(&mut visitor, sb.mapping_root)?; - } + let roots = btree_to_map::(engine.clone(), false, sb.mapping_root)?; // mapping bottom level let data_sm; @@ -342,7 +269,7 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { // data space map { - let mut data_sm = data_sm.lock().unwrap(); + let data_sm = data_sm.lock().unwrap(); let root = unpack::(&sb.data_sm_root[0..])?; let nr_data_blocks = root.nr_blocks; eprintln!("data root: {:?}", root); @@ -369,6 +296,7 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { engine.read_many(&mut blocks)?; + let mut leaks = 0; let mut fail = false; let mut blocknr = 0; for (n, _i) in v.entries.iter().enumerate() { @@ -389,7 +317,10 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { match e { BitmapEntry::Small(actual) => { let expected = data_sm.get(blocknr)?; - if actual != expected as u8 { + if actual == 1 && expected == 0 { + eprintln!("Data block {} leaked.", blocknr); + leaks += 1; + } else if actual != expected as u8 { eprintln!("Bad reference count for data block {}. Expected {}, but space map contains {}.", blocknr, expected, actual); fail = true; @@ -408,6 +339,13 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> { } } + if leaks > 0 { + eprintln!( + "{} data blocks have leaked. Use --auto-repair to fix.", + leaks + ); + } + if fail { return Err(anyhow!("Inconsistent data space map")); }