Skip to content

Commit 886b15f

Browse files
committed
[thin_dump] Prompt for repair only on input errors
Separate output errors from input errors for callers to determine the error type. A broken pipe error (EPIPE) is also treated as an output error since the Rust std library ignores SIGPIPE by default [1][2]. thin_dump then displays the hint if there's any error in metadata, or exit gracefully if it is an output error. [1] rust-lang/rust#13158 [2] rust-lang/rust#62569
1 parent fa3b3c6 commit 886b15f

File tree

6 files changed

+85
-42
lines changed

6 files changed

+85
-42
lines changed

src/commands/thin_dump.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::sync::Arc;
88

99
use crate::commands::utils::*;
1010
use crate::commands::Command;
11+
use crate::error::IoError;
1112
use crate::report::*;
1213
use crate::thin::dump::{dump, ThinDumpOptions};
1314
use crate::thin::metadata_repair::SuperblockOverrides;
@@ -128,13 +129,17 @@ impl<'a> Command<'a> for ThinDumpCommand {
128129
},
129130
};
130131

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

src/error.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// An error type for applications need to distinguish input & output errors
2+
pub enum IoError {
3+
Input(anyhow::Error),
4+
Output(anyhow::Error),
5+
}
6+
7+
pub fn to_input_err(e: anyhow::Error) -> IoError {
8+
IoError::Input(e)
9+
}
10+
11+
pub fn to_output_err(e: anyhow::Error) -> IoError {
12+
IoError::Output(e)
13+
}
14+
15+
impl From<IoError> for anyhow::Error {
16+
fn from(e: IoError) -> anyhow::Error {
17+
match e {
18+
IoError::Input(inner) => inner,
19+
IoError::Output(inner) => inner,
20+
}
21+
}
22+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub mod checksum;
2020
pub mod commands;
2121
pub mod copier;
2222
pub mod era;
23+
pub mod error;
2324
pub mod file_utils;
2425
pub mod grid_layout;
2526
pub mod io_engine;

src/shrink/toplevel.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,9 @@ fn rebuild_metadata(opts: ThinShrinkOptions) -> Result<()> {
543543
let mut w = WriteBatcher::new(output.clone(), sm, output.get_batch_size());
544544
let mut restorer = Restorer::new(&mut w, opts.report);
545545
let mut remapper = DataRemapper::new(&mut restorer, opts.nr_blocks, remaps);
546-
dump_metadata(input, &mut remapper, &sb, &md)
546+
dump_metadata(input, &mut remapper, &sb, &md)?;
547+
548+
Ok(())
547549
}
548550

549551
pub fn shrink(opts: ThinShrinkOptions) -> Result<()> {

src/thin/dump.rs

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::path::Path;
66
use std::sync::{Arc, Mutex};
77

88
use crate::checksum;
9+
use crate::error::*;
910
use crate::io_engine::{AsyncIoEngine, Block, IoEngine, SyncIoEngine};
1011
use crate::pdata::btree::{self, *};
1112
use crate::pdata::btree_walker::*;
@@ -174,47 +175,51 @@ fn mk_context(opts: &ThinDumpOptions) -> Result<Context> {
174175

175176
//------------------------------------------
176177

177-
fn emit_leaf(v: &mut MappingVisitor, b: &Block) -> Result<()> {
178+
fn emit_leaf(v: &mut MappingVisitor, b: &Block) -> Result<(), IoError> {
178179
use Node::*;
179180
let path = Vec::new();
180181
let kr = KeyRange::new();
181182

182183
let bt = checksum::metadata_block_type(b.get_data());
183184
if bt != checksum::BT::NODE {
184-
return Err(anyhow!(format!(
185+
return Err(IoError::Input(anyhow!(
185186
"checksum failed for node {}, {:?}",
186-
b.loc, bt
187+
b.loc,
188+
bt
187189
)));
188190
}
189191

190-
let node = unpack_node::<BlockTime>(&path, b.get_data(), true, true)?;
192+
let node = unpack_node::<BlockTime>(&path, b.get_data(), true, true)
193+
.map_err(|e| IoError::Input(e.into()))?;
191194

192195
match node {
193196
Internal { .. } => {
194-
return Err(anyhow!("not a leaf"));
197+
return Err(IoError::Input(anyhow!("node {} is not a leaf", b.loc)));
195198
}
196199
Leaf {
197200
header,
198201
keys,
199202
values,
200203
} => {
201-
v.visit(&path, &kr, &header, &keys, &values)?;
204+
v.visit(&path, &kr, &header, &keys, &values)
205+
.map_err(|e| IoError::Output(e.into()))?;
202206
}
203207
}
204208

205209
Ok(())
206210
}
207211

208-
fn read_for<T>(engine: Arc<dyn IoEngine>, blocks: &[u64], mut t: T) -> Result<()>
212+
fn read_for<T>(engine: Arc<dyn IoEngine>, blocks: &[u64], mut t: T) -> Result<(), IoError>
209213
where
210-
T: FnMut(Block) -> Result<()>,
214+
T: FnMut(Block) -> Result<(), IoError>,
211215
{
212216
for cs in blocks.chunks(engine.get_batch_size()) {
213217
for b in engine
214218
.read_many(cs)
215-
.map_err(|_e| anyhow!("read_many failed"))?
219+
.map_err(|_e| IoError::Input(anyhow!("read_many failed")))?
216220
{
217-
t(b.map_err(|_e| anyhow!("read of individual block failed"))?)?;
221+
let blk = b.map_err(|_e| IoError::Input(anyhow!("read of individual block failed")))?;
222+
t(blk)?;
218223
}
219224
}
220225

@@ -225,7 +230,7 @@ fn emit_leaves(
225230
engine: Arc<dyn IoEngine>,
226231
out: &mut dyn MetadataVisitor,
227232
leaves: &[u64],
228-
) -> Result<()> {
233+
) -> Result<(), IoError> {
229234
let mut v = MappingVisitor::new(out);
230235
let proc = |b| {
231236
emit_leaf(&mut v, &b)?;
@@ -234,14 +239,14 @@ fn emit_leaves(
234239

235240
read_for(engine, leaves, proc)?;
236241
v.end_walk()
237-
.map_err(|e| anyhow!("failed to emit leaves: {}", e))
242+
.map_err(|e| IoError::Output(anyhow!("failed to emit leaves: {}", e)))
238243
}
239244

240245
fn emit_entries(
241246
engine: Arc<dyn IoEngine>,
242247
out: &mut dyn MetadataVisitor,
243248
entries: &[Entry],
244-
) -> Result<()> {
249+
) -> Result<(), IoError> {
245250
let mut leaves = Vec::new();
246251

247252
for e in entries {
@@ -255,7 +260,7 @@ fn emit_entries(
255260
leaves.clear();
256261
}
257262
let str = format!("{}", id);
258-
out.ref_shared(&str)?;
263+
out.ref_shared(&str).map_err(to_output_err)?;
259264
}
260265
}
261266
}
@@ -272,8 +277,9 @@ pub fn dump_metadata(
272277
out: &mut dyn MetadataVisitor,
273278
sb: &Superblock,
274279
md: &Metadata,
275-
) -> Result<()> {
276-
let data_root = unpack::<SMRoot>(&sb.data_sm_root[0..])?;
280+
) -> Result<(), IoError> {
281+
let data_root =
282+
unpack::<SMRoot>(&sb.data_sm_root[0..]).map_err(|e| IoError::Input(e.into()))?;
277283
let out_sb = ir::Superblock {
278284
uuid: "".to_string(),
279285
time: sb.time,
@@ -284,12 +290,13 @@ pub fn dump_metadata(
284290
nr_data_blocks: data_root.nr_blocks,
285291
metadata_snap: None,
286292
};
287-
out.superblock_b(&out_sb)?;
293+
out.superblock_b(&out_sb).map_err(to_output_err)?;
288294

289295
for d in &md.defs {
290-
out.def_shared_b(&format!("{}", d.def_id))?;
296+
out.def_shared_b(&format!("{}", d.def_id))
297+
.map_err(to_output_err)?;
291298
emit_entries(engine.clone(), out, &d.map.entries)?;
292-
out.def_shared_e()?;
299+
out.def_shared_e().map_err(to_output_err)?;
293300
}
294301

295302
for dev in &md.devs {
@@ -300,38 +307,42 @@ pub fn dump_metadata(
300307
creation_time: dev.detail.creation_time,
301308
snap_time: dev.detail.snapshotted_time,
302309
};
303-
out.device_b(&device)?;
310+
out.device_b(&device).map_err(to_output_err)?;
304311
emit_entries(engine.clone(), out, &dev.map.entries)?;
305-
out.device_e()?;
312+
out.device_e().map_err(to_output_err)?;
306313
}
307-
out.superblock_e()?;
308-
out.eof()?;
314+
out.superblock_e().map_err(to_output_err)?;
315+
out.eof().map_err(to_output_err)?;
309316

310317
Ok(())
311318
}
312319

313320
//------------------------------------------
314321

315-
pub fn dump(opts: ThinDumpOptions) -> Result<()> {
316-
let ctx = mk_context(&opts)?;
322+
pub fn dump(opts: ThinDumpOptions) -> Result<(), IoError> {
323+
let ctx = mk_context(&opts).map_err(to_input_err)?;
317324
let sb = if opts.repair {
318325
read_or_rebuild_superblock(
319326
ctx.engine.clone(),
320327
ctx.report.clone(),
321328
SUPERBLOCK_LOCATION,
322329
&opts.overrides,
323-
)?
330+
)
324331
} else if opts.use_metadata_snap {
325-
read_superblock_snap(ctx.engine.as_ref())?
332+
read_superblock_snap(ctx.engine.as_ref())
326333
} else {
327334
read_superblock(ctx.engine.as_ref(), SUPERBLOCK_LOCATION)
328-
.and_then(|sb| sb.overrides(&opts.overrides))?
329-
};
330-
let md = build_metadata(ctx.engine.clone(), &sb)?;
331-
let md = optimise_metadata(md)?;
335+
.and_then(|sb| sb.overrides(&opts.overrides))
336+
}
337+
.map_err(to_input_err)?;
338+
339+
let md = build_metadata(ctx.engine.clone(), &sb).map_err(to_input_err)?;
340+
let md = optimise_metadata(md).map_err(to_input_err)?;
332341

333342
let writer: Box<dyn Write> = if opts.output.is_some() {
334-
Box::new(BufWriter::new(File::create(opts.output.unwrap())?))
343+
Box::new(BufWriter::new(
344+
File::create(opts.output.unwrap()).map_err(|e| IoError::Output(e.into()))?,
345+
))
335346
} else {
336347
Box::new(BufWriter::new(std::io::stdout()))
337348
};

src/thin/repair.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ pub fn repair(opts: ThinRepairOptions) -> Result<()> {
7272
);
7373
let mut restorer = Restorer::new(&mut w, ctx.report);
7474

75-
dump_metadata(ctx.engine_in, &mut restorer, &sb, &md)
75+
dump_metadata(ctx.engine_in, &mut restorer, &sb, &md)?;
76+
77+
Ok(())
7678
}
7779

7880
//------------------------------------------

0 commit comments

Comments
 (0)