diff --git a/src/bin/thin_explore.rs b/src/bin/thin_explore.rs index 058c065..2ff1362 100644 --- a/src/bin/thin_explore.rs +++ b/src/bin/thin_explore.rs @@ -265,7 +265,8 @@ fn read_node_header(engine: &dyn IoEngine, loc: u64) -> Result(engine: &dyn IoEngine, loc: u64) -> Result> { let b = engine.read(loc)?; - btree::unpack_node(&b.get_data(), true, false) + let path = Vec::new(); + btree::unpack_node(&path, &b.get_data(), true, false) .map_err(|_| anyhow!("couldn't unpack btree node")) } diff --git a/src/pdata/btree.rs b/src/pdata/btree.rs index 11fde20..048f88c 100644 --- a/src/pdata/btree.rs +++ b/src/pdata/btree.rs @@ -176,10 +176,10 @@ fn test_split_range() { } } -fn split_one(kr: &KeyRange, k: u64) -> Result<(KeyRange, KeyRange)> { +fn split_one(path: &Vec, kr: &KeyRange, k: u64) -> Result<(KeyRange, KeyRange)> { match kr.split(k) { None => { - return Err(node_err(&format!( + return Err(node_err(path, &format!( "couldn't split key range {} at {}", kr, k ))); @@ -188,11 +188,11 @@ fn split_one(kr: &KeyRange, k: u64) -> Result<(KeyRange, KeyRange)> { } } -fn split_key_ranges_(kr: &KeyRange, keys: &[u64]) -> Result> { +fn split_key_ranges_(path: &Vec, kr: &KeyRange, keys: &[u64]) -> Result> { let mut krs = Vec::with_capacity(keys.len()); if keys.len() == 0 { - return Err(node_err("split_key_ranges: no keys present")); + return Err(node_err(path, "split_key_ranges: no keys present")); } // The first key gives the lower bound @@ -202,7 +202,7 @@ fn split_key_ranges_(kr: &KeyRange, keys: &[u64]) -> Result> { }; for i in 1..keys.len() { - let (first, rest) = split_one(&kr, keys[i])?; + let (first, rest) = split_one(path, &kr, keys[i])?; krs.push(first); kr = rest; } @@ -212,9 +212,9 @@ fn split_key_ranges_(kr: &KeyRange, keys: &[u64]) -> Result> { Ok(krs) } -fn split_key_ranges(kr: &KeyRange, keys: &[u64]) -> Result> { +fn split_key_ranges(path: &Vec, kr: &KeyRange, keys: &[u64]) -> Result> { let msg = format!("split: {:?} at {:?}", &kr, &keys); - let r = split_key_ranges_(kr, keys); + let r = split_key_ranges_(path, kr, keys); if r.is_err() { eprintln!("{} -> {:?}", msg, &r); } @@ -244,55 +244,36 @@ pub enum BTreeError { Aggregate(Vec), // #[error("{0:?}, {1}")] - Path(u64, Box), -} - -fn extract_path(e: &BTreeError) -> (Vec, BTreeError) { - let mut path = Vec::new(); - let mut e = e; - - loop { - match e { - BTreeError::Path(b, next) => { - path.push(*b); - e = next.as_ref(); - } - _ => return (path, e.clone()), - } - } + Path(Vec, Box), } impl fmt::Display for BTreeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (path, e) = extract_path(self); - match e { - BTreeError::IoError => write!(f, "io error, path{:?}", path), - BTreeError::NodeError(msg) => write!(f, "node error: {}, path{:?}", msg, path), - BTreeError::ValueError(msg) => write!(f, "value error: {}, path{:?}", msg, path), - BTreeError::KeyContext(kr, be) => { - write!(f, "{}, effecting keys {}, path{:?}", be, kr, path) - } + match self { + BTreeError::IoError => write!(f, "io error"), + BTreeError::NodeError(msg) => write!(f, "node error: {}", msg), + BTreeError::ValueError(msg) => write!(f, "value error: {}", msg), + BTreeError::KeyContext(kr, be) => write!(f, "{}, effecting keys {}", be, kr), BTreeError::Aggregate(errs) => { for e in errs { write!(f, "{}", e)? } Ok(()) } - // Can't happen - BTreeError::Path(_, e) => write!(f, "{}", e), + BTreeError::Path(path, e) => write!(f, "{} @{:?}", e, path), } } } -pub fn node_err(msg: &str) -> BTreeError { - BTreeError::NodeError(msg.to_string()) +pub fn node_err(path: &Vec, msg: &str) -> BTreeError { + BTreeError::Path(path.clone(), Box::new(BTreeError::NodeError(msg.to_string()))) } -fn node_err_s(msg: String) -> BTreeError { - BTreeError::NodeError(msg) +fn node_err_s(path: &Vec, msg: String) -> BTreeError { + BTreeError::Path(path.clone(), Box::new(BTreeError::NodeError(msg))) } -pub fn io_err() -> BTreeError { - BTreeError::IoError +pub fn io_err(path: &Vec) -> BTreeError { + BTreeError::Path(path.clone(), Box::new(BTreeError::IoError)) } pub fn value_err(msg: String) -> BTreeError { @@ -377,15 +358,16 @@ impl Node { } } -pub fn convert_result<'a, V>(r: IResult<&'a [u8], V>) -> Result<(&'a [u8], V)> { - r.map_err(|_e| node_err("parse error")) +pub fn convert_result<'a, V>(path: &Vec, r: IResult<&'a [u8], V>) -> Result<(&'a [u8], V)> { + r.map_err(|_e| node_err(path, "parse error")) } -pub fn convert_io_err(r: std::io::Result) -> Result { - r.map_err(|_| io_err()) +pub fn convert_io_err(path: &Vec, r: std::io::Result) -> Result { + r.map_err(|_| io_err(path)) } pub fn unpack_node( + path: &Vec, data: &[u8], ignore_non_fatal: bool, is_root: bool, @@ -393,10 +375,10 @@ pub fn unpack_node( use nom::multi::count; let (i, header) = - NodeHeader::unpack(data).map_err(|_e| node_err("couldn't parse node header"))?; + NodeHeader::unpack(data).map_err(|_e| node_err(path, "couldn't parse node header"))?; if header.is_leaf && header.value_size != V::disk_size() { - return Err(node_err_s(format!( + return Err(node_err_s(path, format!( "value_size mismatch: expected {}, was {}", V::disk_size(), header.value_size @@ -405,25 +387,25 @@ pub fn unpack_node( let elt_size = header.value_size + 8; if elt_size as usize * header.max_entries as usize + NODE_HEADER_SIZE > BLOCK_SIZE { - return Err(node_err_s(format!( + return Err(node_err_s(path, format!( "max_entries is too large ({})", header.max_entries ))); } if header.nr_entries > header.max_entries { - return Err(node_err("nr_entries > max_entries")); + return Err(node_err(path, "nr_entries > max_entries")); } if !ignore_non_fatal { if header.max_entries % 3 != 0 { - return Err(node_err("max_entries is not divisible by 3")); + return Err(node_err(path, "max_entries is not divisible by 3")); } if !is_root { let min = header.max_entries / 3; if header.nr_entries < min { - return Err(node_err_s(format!( + return Err(node_err_s(path, format!( "too few entries {}, expected at least {}", header.nr_entries, min ))); @@ -431,13 +413,13 @@ pub fn unpack_node( } } - let (i, keys) = convert_result(count(le_u64, header.nr_entries as usize)(i))?; + let (i, keys) = convert_result(path, count(le_u64, header.nr_entries as usize)(i))?; let mut last = None; for k in &keys { if let Some(l) = last { if k <= l { - return Err(node_err("keys out of order")); + return Err(node_err(path, "keys out of order")); } } @@ -445,10 +427,10 @@ pub fn unpack_node( } let nr_free = header.max_entries - header.nr_entries; - let (i, _padding) = convert_result(count(le_u64, nr_free as usize)(i))?; + let (i, _padding) = convert_result(path, count(le_u64, nr_free as usize)(i))?; if header.is_leaf { - let (_i, values) = convert_result(count(V::unpack, header.nr_entries as usize)(i))?; + let (_i, values) = convert_result(path, count(V::unpack, header.nr_entries as usize)(i))?; Ok(Node::Leaf { header, @@ -456,7 +438,7 @@ pub fn unpack_node( values, }) } else { - let (_i, values) = convert_result(count(le_u64, header.nr_entries as usize)(i))?; + let (_i, values) = convert_result(path, count(le_u64, header.nr_entries as usize)(i))?; Ok(Node::Internal { header, keys, @@ -469,7 +451,7 @@ pub fn unpack_node( pub trait NodeVisitor { // &self is deliberately non mut to allow the walker to use multiple threads. - fn visit(&self, keys: &KeyRange, header: &NodeHeader, keys: &[u64], values: &[V]) + fn visit(&self, path: &Vec, keys: &KeyRange, header: &NodeHeader, keys: &[u64], values: &[V]) -> Result<()>; } @@ -549,7 +531,13 @@ impl BTreeWalker { } } - fn walk_nodes(&self, visitor: &NV, kr: &[KeyRange], bs: &[u64]) -> Vec + fn walk_nodes( + &self, + path: &mut Vec, + visitor: &NV, + kr: &[KeyRange], + bs: &[u64], + ) -> Vec where NV: NodeVisitor, V: Unpack, @@ -579,7 +567,7 @@ impl BTreeWalker { Err(_) => { // IO completely failed, error every block for (i, b) in blocks.iter().enumerate() { - let e = io_err().keys_context(&kr[i]); + let e = io_err(path).keys_context(&kr[i]); errs.push(e.clone()); self.set_fail(*b, e); } @@ -589,11 +577,11 @@ impl BTreeWalker { for rb in rblocks { match rb { Err(_) => { - let e = io_err().keys_context(&kr[i]); + let e = io_err(path).keys_context(&kr[i]); errs.push(e.clone()); self.set_fail(blocks[i], e); } - Ok(b) => match self.walk_node(visitor, &kr[i], &b, false) { + Ok(b) => match self.walk_node(path, visitor, &kr[i], &b, false) { Err(e) => { errs.push(e); } @@ -609,7 +597,14 @@ impl BTreeWalker { errs } - fn walk_node_(&self, visitor: &NV, kr: &KeyRange, b: &Block, is_root: bool) -> Result<()> + fn walk_node_( + &self, + path: &mut Vec, + visitor: &NV, + kr: &KeyRange, + b: &Block, + is_root: bool, + ) -> Result<()> where NV: NodeVisitor, V: Unpack, @@ -619,17 +614,17 @@ impl BTreeWalker { let bt = checksum::metadata_block_type(b.get_data()); if bt != checksum::BT::NODE { return Err( - node_err_s(format!("checksum failed for node {}, {:?}", b.loc, bt)) + node_err_s(path, format!("checksum failed for node {}, {:?}", b.loc, bt)) .keys_context(kr), ); } - let node = unpack_node::(&b.get_data(), self.ignore_non_fatal, is_root)?; + let node = unpack_node::(path, &b.get_data(), self.ignore_non_fatal, is_root)?; match node { Internal { keys, values, .. } => { - let krs = split_key_ranges(&kr, &keys)?; - let errs = self.walk_nodes(visitor, &krs, &values); + let krs = split_key_ranges(path, &kr, &keys)?; + let errs = self.walk_nodes(path, visitor, &krs, &values); return self.build_aggregate(b.loc, errs); } Leaf { @@ -637,7 +632,8 @@ impl BTreeWalker { keys, values, } => { - if let Err(e) = visitor.visit(&kr, &header, &keys, &values) { + if let Err(e) = visitor.visit(path, &kr, &header, &keys, &values) { + let e = BTreeError::Path(path.clone(), Box::new(e.clone())); self.set_fail(b.loc, e.clone()); return Err(e); } @@ -647,20 +643,25 @@ impl BTreeWalker { Ok(()) } - fn walk_node(&self, visitor: &NV, kr: &KeyRange, b: &Block, is_root: bool) -> Result<()> + fn walk_node( + &self, + path: &mut Vec, + visitor: &NV, + kr: &KeyRange, + b: &Block, + is_root: bool, + ) -> Result<()> where NV: NodeVisitor, V: Unpack, { - let r = self.walk_node_(visitor, kr, b, is_root); - let r = match r { - Err(e) => Err(BTreeError::Path(b.loc, Box::new(e))), - Ok(v) => Ok(v), - }; + path.push(b.loc); + let r = self.walk_node_(path, visitor, kr, b, is_root); + path.pop(); r } - pub fn walk(&self, visitor: &NV, root: u64) -> Result<()> + pub fn walk(&self, path: &mut Vec, visitor: &NV, root: u64) -> Result<()> where NV: NodeVisitor, V: Unpack, @@ -672,12 +673,12 @@ impl BTreeWalker { Ok(()) } } else { - let root = self.engine.read(root).map_err(|_| io_err())?; + let root = self.engine.read(root).map_err(|_| io_err(path))?; let kr = KeyRange { start: None, end: None, }; - self.walk_node(visitor, &kr, &root, true) + self.walk_node(path, visitor, &kr, &root, true) } } } @@ -798,6 +799,7 @@ where */ pub fn walk_threaded( + path: &mut Vec, w: Arc, pool: &ThreadPool, visitor: Arc, @@ -807,7 +809,7 @@ where NV: NodeVisitor + Send + Sync + 'static, V: Unpack, { - w.walk(visitor.as_ref(), root) + w.walk(path, visitor.as_ref(), root) } //------------------------------------------ @@ -826,7 +828,7 @@ impl ValueCollector { // FIXME: should we be using Copy rather than clone? (Yes) impl NodeVisitor for ValueCollector { - fn visit(&self, _kr: &KeyRange, _h: &NodeHeader, keys: &[u64], values: &[V]) -> Result<()> { + fn visit(&self, _path: &Vec, _kr: &KeyRange, _h: &NodeHeader, keys: &[u64], values: &[V]) -> Result<()> { let mut vals = self.values.lock().unwrap(); for n in 0..keys.len() { vals.insert(keys[n], values[n].clone()); @@ -837,17 +839,20 @@ impl NodeVisitor for ValueCollector { } pub fn btree_to_map( + path: &mut Vec, engine: Arc, ignore_non_fatal: bool, root: u64, ) -> Result> { let walker = BTreeWalker::new(engine, ignore_non_fatal); let visitor = ValueCollector::::new(); - walker.walk(&visitor, root)?; + let mut path = Vec::new(); + walker.walk(&mut path, &visitor, root)?; Ok(visitor.values.into_inner().unwrap()) } pub fn btree_to_map_with_sm( + path: &mut Vec, engine: Arc, sm: Arc>, ignore_non_fatal: bool, @@ -856,13 +861,12 @@ pub fn btree_to_map_with_sm( let walker = BTreeWalker::new_with_sm(engine, sm, ignore_non_fatal)?; let visitor = ValueCollector::::new(); - walker.walk(&visitor, root)?; + walker.walk(path, &visitor, root)?; Ok(visitor.values.into_inner().unwrap()) } //------------------------------------------ -/* struct ValuePathCollector { values: Mutex, V)>> } @@ -875,9 +879,29 @@ impl ValuePathCollector { } } -impl NodeVisitor for ValueCollector { +impl NodeVisitor for ValuePathCollector { + fn visit(&self, path: &Vec, _kr: &KeyRange, _h: &NodeHeader, keys: &[u64], values: &[V]) -> Result<()> { + let mut vals = self.values.lock().unwrap(); + for n in 0..keys.len() { + vals.insert(keys[n], (path.clone(), values[n].clone())); + } + Ok(()) + } +} + +pub fn btree_to_map_with_path( + path: &mut Vec, + engine: Arc, + sm: Arc>, + ignore_non_fatal: bool, + root: u64, +) -> Result, V)>> { + let walker = BTreeWalker::new_with_sm(engine, sm, ignore_non_fatal)?; + let visitor = ValuePathCollector::::new(); + + walker.walk(path, &visitor, root)?; + Ok(visitor.values.into_inner().unwrap()) } -*/ //------------------------------------------ diff --git a/src/thin/check.rs b/src/thin/check.rs index 2cac626..e9f64fd 100644 --- a/src/thin/check.rs +++ b/src/thin/check.rs @@ -25,7 +25,7 @@ struct BottomLevelVisitor { //------------------------------------------ impl NodeVisitor for BottomLevelVisitor { - fn visit(&self, _kr: &KeyRange, _h: &NodeHeader, _k: &[u64], values: &[BlockTime]) -> btree::Result<()> { + fn visit(&self, _path: &Vec, _kr: &KeyRange, _h: &NodeHeader, _k: &[u64], values: &[BlockTime]) -> btree::Result<()> { // FIXME: do other checks if values.len() == 0 { @@ -99,7 +99,7 @@ impl<'a> OverflowChecker<'a> { } impl<'a> NodeVisitor for OverflowChecker<'a> { - fn visit(&self, _kr: &KeyRange, _h: &NodeHeader, keys: &[u64], values: &[u32]) -> btree::Result<()> { + fn visit(&self, _path: &Vec, _kr: &KeyRange, _h: &NodeHeader, keys: &[u64], values: &[u32]) -> btree::Result<()> { for n in 0..keys.len() { let k = keys[n]; let v = values[n]; @@ -123,6 +123,7 @@ struct BitmapLeak { // This checks the space map and returns any leak blocks for auto-repair to process. fn check_space_map( + path: &mut Vec, ctx: &Context, kind: &str, entries: Vec, @@ -144,7 +145,7 @@ fn check_space_map( } else { w = BTreeWalker::new_with_sm(engine.clone(), metadata_sm.unwrap().clone(), false)?; } - w.walk(&v, root.ref_count_root)?; + w.walk(path, &v, root.ref_count_root)?; } let mut blocks = Vec::with_capacity(entries.len()); @@ -347,7 +348,7 @@ fn check_mapping_bottom_level( ctx: &Context, metadata_sm: &Arc>, data_sm: &Arc>, - roots: &BTreeMap, + roots: &BTreeMap, u64)>, ) -> Result<()> { ctx.report.set_sub_title("mapping tree"); @@ -359,26 +360,28 @@ fn check_mapping_bottom_level( if roots.len() > 64000 { ctx.report.info("spreading load across devices"); - for (_thin_id, root) in roots { + for (_thin_id, (path, root)) in roots { let data_sm = data_sm.clone(); let root = *root; let v = BottomLevelVisitor { data_sm }; let w = w.clone(); + let mut path = path.clone(); ctx.pool.execute(move || { // FIXME: propogate errors + share fails. - let _r = w.walk(&v, root); + let _r = w.walk(&mut path, &v, root); }); } ctx.pool.join(); } else { ctx.report.info("spreading load within device"); - for (_thin_id, root) in roots { + for (_thin_id, (path, root)) in roots { let w = w.clone(); let data_sm = data_sm.clone(); let root = *root; let v = Arc::new(BottomLevelVisitor { data_sm }); + let mut path = path.clone(); // FIXME: propogate errors + share fails. - walk_threaded(w, &ctx.pool, v, root)? + walk_threaded(&mut path, w, &ctx.pool, v, root)? } } @@ -433,17 +436,20 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> { // superblock let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?; let metadata_root = unpack::(&sb.metadata_sm_root[0..])?; + let mut path = Vec::new(); + path.push(0); // Device details. We read this once to get the number of thin devices, and hence the // maximum metadata ref count. Then create metadata space map, and reread to increment // the ref counts for that metadata. - let devs = btree_to_map::(engine.clone(), false, sb.details_root)?; + let devs = btree_to_map::(&mut path, engine.clone(), false, sb.details_root)?; let nr_devs = devs.len(); let metadata_sm = core_sm(engine.get_nr_blocks(), nr_devs as u32); inc_superblock(&metadata_sm)?; report.set_sub_title("device details tree"); let _devs = btree_to_map_with_sm::( + &mut path, engine.clone(), metadata_sm.clone(), false, @@ -459,7 +465,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> { // mapping top level report.set_sub_title("mapping tree"); let roots = - btree_to_map_with_sm::(engine.clone(), metadata_sm.clone(), false, sb.mapping_root)?; + btree_to_map_with_path::(&mut path, engine.clone(), metadata_sm.clone(), false, sb.mapping_root)?; // mapping bottom level let root = unpack::(&sb.data_sm_root[0..])?; @@ -472,6 +478,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> { let root = unpack::(&sb.data_sm_root[0..])?; let entries = btree_to_map_with_sm::( + &mut path, engine.clone(), metadata_sm.clone(), false, @@ -481,6 +488,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> { inc_entries(&metadata_sm, &entries[0..])?; let data_leaks = check_space_map( + &mut path, &ctx, "data", entries, @@ -507,6 +515,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> { // We call this for the side effect of incrementing the ref counts // for the metadata that holds the tree. let _counts = btree_to_map_with_sm::( + &mut path, engine.clone(), metadata_sm.clone(), false, @@ -515,7 +524,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> { // Now the counts should be correct and we can check it. let metadata_leaks = - check_space_map(&ctx, "metadata", entries, None, metadata_sm.clone(), root)?; + check_space_map(&mut path, &ctx, "metadata", entries, None, metadata_sm.clone(), root)?; bail_out(&ctx, "metadata space map")?; if opts.auto_repair {