[era_check (rust)] First code drop
This commit is contained in:
parent
c8a1da1df9
commit
3d36456a36
@ -34,6 +34,8 @@ fn main_() -> Result<()> {
|
|||||||
cache_repair::run(&new_args);
|
cache_repair::run(&new_args);
|
||||||
} else if name_eq(name, "cache_restore") {
|
} else if name_eq(name, "cache_restore") {
|
||||||
cache_restore::run(&new_args);
|
cache_restore::run(&new_args);
|
||||||
|
} else if name_eq(name, "era_check") {
|
||||||
|
era_check::run(&new_args);
|
||||||
} else if name_eq(name, "thin_check") {
|
} else if name_eq(name, "thin_check") {
|
||||||
thin_check::run(&new_args);
|
thin_check::run(&new_args);
|
||||||
} else if name_eq(name, "thin_dump") {
|
} else if name_eq(name, "thin_dump") {
|
||||||
|
84
src/commands/era_check.rs
Normal file
84
src/commands/era_check.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use atty::Stream;
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::commands::utils::*;
|
||||||
|
use crate::era::check::{check, EraCheckOptions};
|
||||||
|
use crate::report::*;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub fn run(args: &[std::ffi::OsString]) {
|
||||||
|
let parser = App::new("era_check")
|
||||||
|
.version(crate::version::tools_version())
|
||||||
|
// flags
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("ASYNC_IO")
|
||||||
|
.help("Force use of io_uring for synchronous io")
|
||||||
|
.long("async-io")
|
||||||
|
.hidden(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("AUTO_REPAIR")
|
||||||
|
.help("Auto repair trivial issues.")
|
||||||
|
.long("auto-repair"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("IGNORE_NON_FATAL")
|
||||||
|
.help("Only return a non-zero exit code if a fatal error is found.")
|
||||||
|
.long("ignore-non-fatal-errors"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("QUIET")
|
||||||
|
.help("Suppress output messages, return only exit code.")
|
||||||
|
.short("q")
|
||||||
|
.long("quiet"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("SB_ONLY")
|
||||||
|
.help("Only check the superblock.")
|
||||||
|
.long("super-block-only"),
|
||||||
|
)
|
||||||
|
// arguments
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("INPUT")
|
||||||
|
.help("Specify the input device to check")
|
||||||
|
.required(true)
|
||||||
|
.index(1),
|
||||||
|
);
|
||||||
|
|
||||||
|
let matches = parser.get_matches_from(args);
|
||||||
|
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||||
|
|
||||||
|
let report = if matches.is_present("QUIET") {
|
||||||
|
std::sync::Arc::new(mk_quiet_report())
|
||||||
|
} else if atty::is(Stream::Stdout) {
|
||||||
|
std::sync::Arc::new(mk_progress_bar_report())
|
||||||
|
} else {
|
||||||
|
Arc::new(mk_simple_report())
|
||||||
|
};
|
||||||
|
|
||||||
|
check_input_file(input_file, &report);
|
||||||
|
check_file_not_tiny(input_file, &report);
|
||||||
|
check_not_xml(input_file, &report);
|
||||||
|
|
||||||
|
let opts = EraCheckOptions {
|
||||||
|
dev: input_file,
|
||||||
|
async_io: matches.is_present("ASYNC_IO"),
|
||||||
|
sb_only: matches.is_present("SB_ONLY"),
|
||||||
|
ignore_non_fatal: matches.is_present("IGNORE_NON_FATAL"),
|
||||||
|
auto_repair: matches.is_present("AUTO_REPAIR"),
|
||||||
|
report: report.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(reason) = check(&opts) {
|
||||||
|
report.fatal(&format!("{}", reason));
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
@ -2,6 +2,7 @@ pub mod cache_check;
|
|||||||
pub mod cache_dump;
|
pub mod cache_dump;
|
||||||
pub mod cache_repair;
|
pub mod cache_repair;
|
||||||
pub mod cache_restore;
|
pub mod cache_restore;
|
||||||
|
pub mod era_check;
|
||||||
pub mod thin_check;
|
pub mod thin_check;
|
||||||
pub mod thin_dump;
|
pub mod thin_dump;
|
||||||
pub mod thin_metadata_pack;
|
pub mod thin_metadata_pack;
|
||||||
|
153
src/era/check.rs
Normal file
153
src/era/check.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::era::superblock::*;
|
||||||
|
use crate::era::writeset::*;
|
||||||
|
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
|
||||||
|
use crate::pdata::array::{self, ArrayBlock, ArrayError};
|
||||||
|
use crate::pdata::array_walker::*;
|
||||||
|
use crate::pdata::bitset::*;
|
||||||
|
use crate::pdata::btree_walker::*;
|
||||||
|
use crate::pdata::space_map::*;
|
||||||
|
use crate::report::*;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
const MAX_CONCURRENT_IO: u32 = 1024;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
fn inc_superblock(sm: &ASpaceMap) -> anyhow::Result<()> {
|
||||||
|
let mut sm = sm.lock().unwrap();
|
||||||
|
sm.inc(SUPERBLOCK_LOCATION, 1)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
struct EraChecker {
|
||||||
|
current_era: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EraChecker {
|
||||||
|
pub fn new(current_era: u32) -> EraChecker {
|
||||||
|
EraChecker { current_era }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrayVisitor<u32> for EraChecker {
|
||||||
|
fn visit(&self, index: u64, b: ArrayBlock<u32>) -> array::Result<()> {
|
||||||
|
let mut errs: Vec<ArrayError> = Vec::new();
|
||||||
|
|
||||||
|
let dbegin = index as u32 * b.header.max_entries;
|
||||||
|
let dend = dbegin + b.header.max_entries;
|
||||||
|
for (era, dblock) in b.values.iter().zip(dbegin..dend) {
|
||||||
|
if era > &self.current_era {
|
||||||
|
errs.push(array::value_err(format!(
|
||||||
|
"invalid era value at data block {}: {}",
|
||||||
|
dblock, era
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match errs.len() {
|
||||||
|
0 => Ok(()),
|
||||||
|
1 => Err(errs[0].clone()),
|
||||||
|
_ => Err(array::aggregate_error(errs)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub struct EraCheckOptions<'a> {
|
||||||
|
pub dev: &'a Path,
|
||||||
|
pub async_io: bool,
|
||||||
|
pub sb_only: bool,
|
||||||
|
pub ignore_non_fatal: bool,
|
||||||
|
pub auto_repair: bool,
|
||||||
|
pub report: Arc<Report>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
report: Arc<Report>,
|
||||||
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_context(opts: &EraCheckOptions) -> anyhow::Result<Context> {
|
||||||
|
let engine: Arc<dyn IoEngine + Send + Sync>;
|
||||||
|
|
||||||
|
if opts.async_io {
|
||||||
|
engine = Arc::new(AsyncIoEngine::new(opts.dev, MAX_CONCURRENT_IO, false)?);
|
||||||
|
} else {
|
||||||
|
let nr_threads = std::cmp::max(8, num_cpus::get() * 2);
|
||||||
|
engine = Arc::new(SyncIoEngine::new(opts.dev, nr_threads, false)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Context {
|
||||||
|
report: opts.report.clone(),
|
||||||
|
engine,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_superblock(sb: &Superblock) -> anyhow::Result<()> {
|
||||||
|
if sb.version > 1 {
|
||||||
|
return Err(anyhow!("unknown superblock version"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(opts: &EraCheckOptions) -> Result<()> {
|
||||||
|
let ctx = mk_context(opts)?;
|
||||||
|
let engine = &ctx.engine;
|
||||||
|
let report = &ctx.report;
|
||||||
|
let mut fatal = false;
|
||||||
|
|
||||||
|
report.set_title("Checking era metadata");
|
||||||
|
|
||||||
|
let metadata_sm = core_sm(engine.get_nr_blocks(), u8::MAX as u32);
|
||||||
|
inc_superblock(&metadata_sm)?;
|
||||||
|
|
||||||
|
let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?;
|
||||||
|
check_superblock(&sb)?;
|
||||||
|
|
||||||
|
if opts.sb_only {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut path = vec![0];
|
||||||
|
let writesets = btree_to_map::<Writeset>(
|
||||||
|
&mut path,
|
||||||
|
engine.clone(),
|
||||||
|
opts.ignore_non_fatal,
|
||||||
|
sb.writeset_tree_root,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for ws in writesets.values() {
|
||||||
|
let (_bs, err) = read_bitset_with_sm(
|
||||||
|
engine.clone(),
|
||||||
|
ws.root,
|
||||||
|
ws.nr_bits as usize,
|
||||||
|
metadata_sm.clone(),
|
||||||
|
opts.ignore_non_fatal,
|
||||||
|
)?;
|
||||||
|
if err.is_some() {
|
||||||
|
ctx.report.fatal(&format!("{}", err.unwrap()));
|
||||||
|
fatal = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let w = ArrayWalker::new_with_sm(engine.clone(), metadata_sm.clone(), opts.ignore_non_fatal)?;
|
||||||
|
let mut c = EraChecker::new(sb.current_era);
|
||||||
|
if let Err(e) = w.walk(&mut c, sb.era_array_root) {
|
||||||
|
ctx.report.fatal(&format!("{}", e));
|
||||||
|
fatal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if fatal {
|
||||||
|
Err(anyhow!("fatal errors in metadata"))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
3
src/era/mod.rs
Normal file
3
src/era/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod check;
|
||||||
|
pub mod superblock;
|
||||||
|
pub mod writeset;
|
153
src/era/superblock.rs
Normal file
153
src/era/superblock.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
use nom::{bytes::complete::*, number::complete::*, IResult};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use crate::checksum::*;
|
||||||
|
use crate::era::writeset::Writeset;
|
||||||
|
use crate::io_engine::*;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
pub const SPACE_MAP_ROOT_SIZE: usize = 128;
|
||||||
|
pub const SUPERBLOCK_LOCATION: u64 = 0;
|
||||||
|
|
||||||
|
const MAGIC: u64 = 0o17660203573; // 0x7EC1077B in hex
|
||||||
|
const UUID_SIZE: usize = 16;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SuperblockFlags {
|
||||||
|
pub clean_shutdown: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Superblock {
|
||||||
|
pub flags: SuperblockFlags,
|
||||||
|
pub block: u64,
|
||||||
|
pub version: u32,
|
||||||
|
|
||||||
|
pub metadata_sm_root: Vec<u8>,
|
||||||
|
|
||||||
|
pub data_block_size: u32,
|
||||||
|
pub nr_blocks: u32,
|
||||||
|
|
||||||
|
pub current_era: u32,
|
||||||
|
pub current_writeset: Writeset,
|
||||||
|
|
||||||
|
pub writeset_tree_root: u64,
|
||||||
|
pub era_array_root: u64,
|
||||||
|
|
||||||
|
pub metadata_snap: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack(data: &[u8]) -> IResult<&[u8], Superblock> {
|
||||||
|
let (i, _csum) = le_u32(data)?;
|
||||||
|
let (i, flags) = le_u32(i)?;
|
||||||
|
let (i, block) = le_u64(i)?;
|
||||||
|
let (i, _uuid) = take(16usize)(i)?;
|
||||||
|
let (i, _magic) = le_u64(i)?;
|
||||||
|
let (i, version) = le_u32(i)?;
|
||||||
|
|
||||||
|
let (i, metadata_sm_root) = take(SPACE_MAP_ROOT_SIZE)(i)?;
|
||||||
|
let (i, data_block_size) = le_u32(i)?;
|
||||||
|
let (i, _metadata_block_size) = le_u32(i)?;
|
||||||
|
let (i, nr_blocks) = le_u32(i)?;
|
||||||
|
|
||||||
|
let (i, current_era) = le_u32(i)?;
|
||||||
|
let (i, nr_bits) = le_u32(i)?;
|
||||||
|
let (i, root) = le_u64(i)?;
|
||||||
|
|
||||||
|
let (i, writeset_tree_root) = le_u64(i)?;
|
||||||
|
let (i, era_array_root) = le_u64(i)?;
|
||||||
|
let (i, metadata_snap) = le_u64(i)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
i,
|
||||||
|
Superblock {
|
||||||
|
flags: SuperblockFlags {
|
||||||
|
clean_shutdown: (flags & 0x1) != 0,
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
version,
|
||||||
|
metadata_sm_root: metadata_sm_root.to_vec(),
|
||||||
|
data_block_size,
|
||||||
|
nr_blocks,
|
||||||
|
current_era,
|
||||||
|
current_writeset: Writeset { nr_bits, root },
|
||||||
|
writeset_tree_root,
|
||||||
|
era_array_root,
|
||||||
|
metadata_snap,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_superblock(engine: &dyn IoEngine, loc: u64) -> Result<Superblock> {
|
||||||
|
let b = engine.read(loc)?;
|
||||||
|
|
||||||
|
if metadata_block_type(b.get_data()) != BT::ERA_SUPERBLOCK {
|
||||||
|
return Err(anyhow!("bad checksum in superblock"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok((_, sb)) = unpack(b.get_data()) {
|
||||||
|
Ok(sb)
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("couldn't unpack superblock"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
fn pack_superblock<W: WriteBytesExt>(sb: &Superblock, w: &mut W) -> Result<()> {
|
||||||
|
// checksum, which we don't know yet
|
||||||
|
w.write_u32::<LittleEndian>(0)?;
|
||||||
|
|
||||||
|
// flags
|
||||||
|
let mut flags: u32 = 0;
|
||||||
|
if sb.flags.clean_shutdown {
|
||||||
|
flags |= 0x1;
|
||||||
|
}
|
||||||
|
w.write_u32::<LittleEndian>(flags)?;
|
||||||
|
w.write_u64::<LittleEndian>(sb.block)?;
|
||||||
|
|
||||||
|
w.write_all(&[0; UUID_SIZE])?;
|
||||||
|
w.write_u64::<LittleEndian>(MAGIC)?;
|
||||||
|
w.write_u32::<LittleEndian>(sb.version)?;
|
||||||
|
|
||||||
|
w.write_all(&sb.metadata_sm_root)?;
|
||||||
|
w.write_u32::<LittleEndian>(sb.data_block_size)?;
|
||||||
|
// metadata block size
|
||||||
|
w.write_u32::<LittleEndian>((BLOCK_SIZE >> SECTOR_SHIFT) as u32)?;
|
||||||
|
w.write_u32::<LittleEndian>(sb.nr_blocks)?;
|
||||||
|
|
||||||
|
w.write_u32::<LittleEndian>(sb.current_era)?;
|
||||||
|
w.write_u32::<LittleEndian>(sb.current_writeset.nr_bits)?;
|
||||||
|
w.write_u64::<LittleEndian>(sb.current_writeset.root)?;
|
||||||
|
|
||||||
|
w.write_u64::<LittleEndian>(sb.writeset_tree_root)?;
|
||||||
|
w.write_u64::<LittleEndian>(sb.era_array_root)?;
|
||||||
|
|
||||||
|
w.write_u64::<LittleEndian>(sb.metadata_snap)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_superblock(engine: &dyn IoEngine, _loc: u64, sb: &Superblock) -> Result<()> {
|
||||||
|
let b = Block::zeroed(SUPERBLOCK_LOCATION);
|
||||||
|
|
||||||
|
// pack the superblock
|
||||||
|
{
|
||||||
|
let mut cursor = Cursor::new(b.get_data());
|
||||||
|
pack_superblock(sb, &mut cursor)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the checksum
|
||||||
|
write_checksum(b.get_data(), BT::ERA_SUPERBLOCK)?;
|
||||||
|
|
||||||
|
// write
|
||||||
|
engine.write(&b)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
35
src/era/writeset.rs
Normal file
35
src/era/writeset.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
use nom::{number::complete::*, IResult};
|
||||||
|
|
||||||
|
use crate::pdata::unpack::*;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Writeset {
|
||||||
|
pub nr_bits: u32,
|
||||||
|
pub root: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unpack for Writeset {
|
||||||
|
fn disk_size() -> u32 {
|
||||||
|
12
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack(i: &[u8]) -> IResult<&[u8], Writeset> {
|
||||||
|
let (i, nr_bits) = le_u32(i)?;
|
||||||
|
let (i, root) = le_u64(i)?;
|
||||||
|
Ok((i, Writeset { nr_bits, root }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pack for Writeset {
|
||||||
|
fn pack<W: WriteBytesExt>(&self, w: &mut W) -> Result<()> {
|
||||||
|
w.write_u32::<LittleEndian>(self.nr_bits)?;
|
||||||
|
w.write_u64::<LittleEndian>(self.root)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
@ -18,6 +18,7 @@ extern crate quickcheck_macros;
|
|||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod checksum;
|
pub mod checksum;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
|
pub mod era;
|
||||||
pub mod file_utils;
|
pub mod file_utils;
|
||||||
pub mod io_engine;
|
pub mod io_engine;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
|
Loading…
Reference in New Issue
Block a user