diff --git a/src/commands/thin_dump.rs b/src/commands/thin_dump.rs index 60b03511b..ce5c20f76 100644 --- a/src/commands/thin_dump.rs +++ b/src/commands/thin_dump.rs @@ -8,6 +8,7 @@ use std::sync::Arc; use crate::commands::utils::*; use crate::commands::Command; +use crate::dump_utils::OutputError; use crate::report::*; use crate::thin::dump::{dump, ThinDumpOptions}; use crate::thin::metadata_repair::SuperblockOverrides; @@ -128,9 +129,17 @@ impl<'a> Command<'a> for ThinDumpCommand { }, }; - dump(opts).map_err(|reason| { - report.fatal(&format!("{}", reason)); - std::io::Error::from_raw_os_error(libc::EPERM) - }) + if let Err(e) = dump(opts) { + if !e.is::() { + report.fatal(&format!("{:?}", e)); + report.fatal( + "metadata contains errors (run thin_check for details).\n\ + perhaps you wanted to run with --repair ?", + ); + } + return Err(std::io::Error::from_raw_os_error(libc::EPERM)); + } + + Ok(()) } } diff --git a/src/dump_utils.rs b/src/dump_utils.rs new file mode 100644 index 000000000..aab921990 --- /dev/null +++ b/src/dump_utils.rs @@ -0,0 +1,13 @@ +//------------------------------------------ + +// A wrapper for callers to determine the error type +#[derive(Debug)] +pub struct OutputError; + +impl std::fmt::Display for OutputError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "output error") + } +} + +//------------------------------------------ diff --git a/src/lib.rs b/src/lib.rs index 8b6ef32cb..b68ed0633 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ pub mod cache; pub mod checksum; pub mod commands; pub mod copier; +pub mod dump_utils; pub mod era; pub mod file_utils; pub mod grid_layout; diff --git a/src/thin/dump.rs b/src/thin/dump.rs index 6a573d7db..8988add94 100644 --- a/src/thin/dump.rs +++ b/src/thin/dump.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; use std::fs::File; use std::io::BufWriter; use std::io::Write; @@ -6,6 +6,7 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use crate::checksum; +use crate::dump_utils::*; use crate::io_engine::{AsyncIoEngine, Block, IoEngine, SyncIoEngine}; use crate::pdata::btree::{self, *}; use crate::pdata::btree_walker::*; @@ -153,12 +154,12 @@ pub struct ThinDumpOptions<'a> { pub overrides: SuperblockOverrides, } -struct Context { +struct ThinDumpContext { report: Arc, engine: Arc, } -fn mk_context(opts: &ThinDumpOptions) -> Result { +fn mk_context(opts: &ThinDumpOptions) -> Result { let engine: Arc = if opts.async_io { Arc::new(AsyncIoEngine::new(opts.input, MAX_CONCURRENT_IO, false)?) } else { @@ -166,7 +167,7 @@ fn mk_context(opts: &ThinDumpOptions) -> Result { Arc::new(SyncIoEngine::new(opts.input, nr_threads, false)?) }; - Ok(Context { + Ok(ThinDumpContext { report: opts.report.clone(), engine, }) @@ -174,35 +175,28 @@ fn mk_context(opts: &ThinDumpOptions) -> Result { //------------------------------------------ -fn emit_leaf(v: &mut MappingVisitor, b: &Block) -> Result<()> { +fn emit_leaf(v: &MappingVisitor, b: &Block) -> Result<()> { use Node::*; let path = Vec::new(); let kr = KeyRange::new(); let bt = checksum::metadata_block_type(b.get_data()); if bt != checksum::BT::NODE { - return Err(anyhow!(format!( - "checksum failed for node {}, {:?}", - b.loc, bt - ))); + return Err(anyhow!("checksum failed for node {}, {:?}", b.loc, bt)); } let node = unpack_node::(&path, b.get_data(), true, true)?; match node { - Internal { .. } => { - return Err(anyhow!("not a leaf")); - } + Internal { .. } => Err(anyhow!("block {} is not a leaf", b.loc)), Leaf { header, keys, values, - } => { - v.visit(&path, &kr, &header, &keys, &values)?; - } + } => v + .visit(&path, &kr, &header, &keys, &values) + .context(OutputError), } - - Ok(()) } fn read_for(engine: Arc, blocks: &[u64], mut t: T) -> Result<()> @@ -214,7 +208,8 @@ where .read_many(cs) .map_err(|_e| anyhow!("read_many failed"))? { - t(b.map_err(|_e| anyhow!("read of individual block failed"))?)?; + let blk = b.map_err(|_e| anyhow!("read of individual block failed"))?; + t(blk)?; } } @@ -226,15 +221,14 @@ fn emit_leaves( out: &mut dyn MetadataVisitor, leaves: &[u64], ) -> Result<()> { - let mut v = MappingVisitor::new(out); + let v = MappingVisitor::new(out); let proc = |b| { - emit_leaf(&mut v, &b)?; + emit_leaf(&v, &b)?; Ok(()) }; read_for(engine, leaves, proc)?; - v.end_walk() - .map_err(|e| anyhow!("failed to emit leaves: {}", e)) + v.end_walk().context(OutputError) } fn emit_entries( @@ -255,7 +249,7 @@ fn emit_entries( leaves.clear(); } let str = format!("{}", id); - out.ref_shared(&str)?; + out.ref_shared(&str).context(OutputError)?; } } } @@ -284,12 +278,13 @@ pub fn dump_metadata( nr_data_blocks: data_root.nr_blocks, metadata_snap: None, }; - out.superblock_b(&out_sb)?; + out.superblock_b(&out_sb).context(OutputError)?; for d in &md.defs { - out.def_shared_b(&format!("{}", d.def_id))?; + out.def_shared_b(&format!("{}", d.def_id)) + .context(OutputError)?; emit_entries(engine.clone(), out, &d.map.entries)?; - out.def_shared_e()?; + out.def_shared_e().context(OutputError)?; } for dev in &md.devs { @@ -300,12 +295,12 @@ pub fn dump_metadata( creation_time: dev.detail.creation_time, snap_time: dev.detail.snapshotted_time, }; - out.device_b(&device)?; + out.device_b(&device).context(OutputError)?; emit_entries(engine.clone(), out, &dev.map.entries)?; - out.device_e()?; + out.device_e().context(OutputError)?; } - out.superblock_e()?; - out.eof()?; + out.superblock_e().context(OutputError)?; + out.eof().context(OutputError)?; Ok(()) } @@ -327,11 +322,13 @@ pub fn dump(opts: ThinDumpOptions) -> Result<()> { read_superblock(ctx.engine.as_ref(), SUPERBLOCK_LOCATION) .and_then(|sb| sb.overrides(&opts.overrides))? }; + let md = build_metadata(ctx.engine.clone(), &sb)?; let md = optimise_metadata(md)?; let writer: Box = if opts.output.is_some() { - Box::new(BufWriter::new(File::create(opts.output.unwrap())?)) + let f = File::create(opts.output.unwrap()).context(OutputError)?; + Box::new(BufWriter::new(f)) } else { Box::new(BufWriter::new(std::io::stdout())) };