Skip to content

Commit 29473be

Browse files
committed
[thin_dump] Prompt for repair only on input errors
thin_dump should display the reparing hint only if there's any error in the input metadata rather than the output process. A context therefore is added to the returned error for callers to determine the error type. Note that a broken pipe error (EPIPE) is treated as an output error since the Rust std library ignores SIGPIPE by default [1][2]. [1] rust-lang/rust#13158 [2] rust-lang/rust#62569
1 parent fa3b3c6 commit 29473be

File tree

2 files changed

+48
-30
lines changed

2 files changed

+48
-30
lines changed

src/commands/thin_dump.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::sync::Arc;
99
use crate::commands::utils::*;
1010
use crate::commands::Command;
1111
use crate::report::*;
12-
use crate::thin::dump::{dump, ThinDumpOptions};
12+
use crate::thin::dump::{dump, OutputError, ThinDumpOptions};
1313
use crate::thin::metadata_repair::SuperblockOverrides;
1414

1515
pub struct ThinDumpCommand;
@@ -128,13 +128,17 @@ impl<'a> Command<'a> for ThinDumpCommand {
128128
},
129129
};
130130

131-
dump(opts).map_err(|reason| {
132-
report.fatal(&format!("{}", reason));
133-
report.fatal(
134-
"metadata contains errors (run thin_check for details).\n\
135-
perhaps you wanted to run with --repair ?",
136-
);
137-
std::io::Error::from_raw_os_error(libc::EPERM)
138-
})
131+
if let Err(e) = dump(opts) {
132+
if !e.is::<OutputError>() {
133+
report.fatal(&format!("{:?}", e));
134+
report.fatal(
135+
"metadata contains errors (run thin_check for details).\n\
136+
perhaps you wanted to run with --repair ?",
137+
);
138+
}
139+
return Err(std::io::Error::from_raw_os_error(libc::EPERM));
140+
}
141+
142+
Ok(())
139143
}
140144
}

src/thin/dump.rs

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::{anyhow, Result};
1+
use anyhow::{anyhow, Context, Result};
22
use std::fs::File;
33
use std::io::BufWriter;
44
use std::io::Write;
@@ -153,52 +153,60 @@ pub struct ThinDumpOptions<'a> {
153153
pub overrides: SuperblockOverrides,
154154
}
155155

156-
struct Context {
156+
struct ThinDumpContext {
157157
report: Arc<Report>,
158158
engine: Arc<dyn IoEngine + Send + Sync>,
159159
}
160160

161-
fn mk_context(opts: &ThinDumpOptions) -> Result<Context> {
161+
fn mk_context(opts: &ThinDumpOptions) -> Result<ThinDumpContext> {
162162
let engine: Arc<dyn IoEngine + Send + Sync> = if opts.async_io {
163163
Arc::new(AsyncIoEngine::new(opts.input, MAX_CONCURRENT_IO, false)?)
164164
} else {
165165
let nr_threads = std::cmp::max(8, num_cpus::get() * 2);
166166
Arc::new(SyncIoEngine::new(opts.input, nr_threads, false)?)
167167
};
168168

169-
Ok(Context {
169+
Ok(ThinDumpContext {
170170
report: opts.report.clone(),
171171
engine,
172172
})
173173
}
174174

175175
//------------------------------------------
176176

177+
// a wrapper for callers to indicate the error type
178+
#[derive(Debug)]
179+
pub struct OutputError;
180+
181+
impl std::fmt::Display for OutputError {
182+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183+
write!(f, "output error")
184+
}
185+
}
186+
177187
fn emit_leaf(v: &mut MappingVisitor, b: &Block) -> Result<()> {
178188
use Node::*;
179189
let path = Vec::new();
180190
let kr = KeyRange::new();
181191

182192
let bt = checksum::metadata_block_type(b.get_data());
183193
if bt != checksum::BT::NODE {
184-
return Err(anyhow!(format!(
185-
"checksum failed for node {}, {:?}",
186-
b.loc, bt
187-
)));
194+
return Err(anyhow!("checksum failed for node {}, {:?}", b.loc, bt));
188195
}
189196

190197
let node = unpack_node::<BlockTime>(&path, b.get_data(), true, true)?;
191198

192199
match node {
193200
Internal { .. } => {
194-
return Err(anyhow!("not a leaf"));
201+
return Err(anyhow!("block {} is not a leaf", b.loc));
195202
}
196203
Leaf {
197204
header,
198205
keys,
199206
values,
200207
} => {
201-
v.visit(&path, &kr, &header, &keys, &values)?;
208+
v.visit(&path, &kr, &header, &keys, &values)
209+
.map_err(|e| anyhow::Error::from(e).context(OutputError))?;
202210
}
203211
}
204212

@@ -214,7 +222,8 @@ where
214222
.read_many(cs)
215223
.map_err(|_e| anyhow!("read_many failed"))?
216224
{
217-
t(b.map_err(|_e| anyhow!("read of individual block failed"))?)?;
225+
let blk = b.map_err(|_e| anyhow!("read of individual block failed"))?;
226+
t(blk)?;
218227
}
219228
}
220229

@@ -234,7 +243,7 @@ fn emit_leaves(
234243

235244
read_for(engine, leaves, proc)?;
236245
v.end_walk()
237-
.map_err(|e| anyhow!("failed to emit leaves: {}", e))
246+
.map_err(|e| anyhow::Error::from(e).context(OutputError))
238247
}
239248

240249
fn emit_entries(
@@ -255,7 +264,7 @@ fn emit_entries(
255264
leaves.clear();
256265
}
257266
let str = format!("{}", id);
258-
out.ref_shared(&str)?;
267+
out.ref_shared(&str).context(OutputError)?;
259268
}
260269
}
261270
}
@@ -284,12 +293,13 @@ pub fn dump_metadata(
284293
nr_data_blocks: data_root.nr_blocks,
285294
metadata_snap: None,
286295
};
287-
out.superblock_b(&out_sb)?;
296+
out.superblock_b(&out_sb).context(OutputError)?;
288297

289298
for d in &md.defs {
290-
out.def_shared_b(&format!("{}", d.def_id))?;
299+
out.def_shared_b(&format!("{}", d.def_id))
300+
.context(OutputError)?;
291301
emit_entries(engine.clone(), out, &d.map.entries)?;
292-
out.def_shared_e()?;
302+
out.def_shared_e().context(OutputError)?;
293303
}
294304

295305
for dev in &md.devs {
@@ -300,12 +310,12 @@ pub fn dump_metadata(
300310
creation_time: dev.detail.creation_time,
301311
snap_time: dev.detail.snapshotted_time,
302312
};
303-
out.device_b(&device)?;
313+
out.device_b(&device).context(OutputError)?;
304314
emit_entries(engine.clone(), out, &dev.map.entries)?;
305-
out.device_e()?;
315+
out.device_e().context(OutputError)?;
306316
}
307-
out.superblock_e()?;
308-
out.eof()?;
317+
out.superblock_e().context(OutputError)?;
318+
out.eof().context(OutputError)?;
309319

310320
Ok(())
311321
}
@@ -327,11 +337,15 @@ pub fn dump(opts: ThinDumpOptions) -> Result<()> {
327337
read_superblock(ctx.engine.as_ref(), SUPERBLOCK_LOCATION)
328338
.and_then(|sb| sb.overrides(&opts.overrides))?
329339
};
340+
330341
let md = build_metadata(ctx.engine.clone(), &sb)?;
331342
let md = optimise_metadata(md)?;
332343

333344
let writer: Box<dyn Write> = if opts.output.is_some() {
334-
Box::new(BufWriter::new(File::create(opts.output.unwrap())?))
345+
Box::new(BufWriter::new(
346+
File::create(opts.output.unwrap())
347+
.map_err(|e| anyhow::Error::from(e).context(OutputError))?,
348+
))
335349
} else {
336350
Box::new(BufWriter::new(std::io::stdout()))
337351
};

0 commit comments

Comments
 (0)