From 29d56f62a5046186469eb6011c4f6ced93fa4043 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Sun, 14 Jun 2020 08:17:46 +0100 Subject: [PATCH] wip --- src/bin/thin_check.cc | 55 ++++++++++++++++++++++++++++++++++++++ src/block_manager.rs | 4 +-- src/checksum.rs | 45 +++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/pack/toplevel.rs | 39 +-------------------------- src/thin/mod.rs | 1 + src/thin/superblock.rs | 60 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 166 insertions(+), 40 deletions(-) create mode 100644 src/bin/thin_check.cc create mode 100644 src/checksum.rs create mode 100644 src/thin/mod.rs create mode 100644 src/thin/superblock.rs diff --git a/src/bin/thin_check.cc b/src/bin/thin_check.cc new file mode 100644 index 0000000..3ad7c01 --- /dev/null +++ b/src/bin/thin_check.cc @@ -0,0 +1,55 @@ +extern crate clap; +extern crate thinp; + +use clap::{App, Arg}; +use std::process; +use thinp::file_utils; + +use std::process::exit; + +fn main() { + let parser = App::new("thin_check") + .version(thinp::version::TOOLS_VERSION) + .about("Validates thin provisioning metadata on a device or file.") + .arg(Arg::with_name("QUIET") + .help("Suppress output messages, return only exit code.") + .short("q") + .long("quiet") + .value_name("QUIET")) + .arg(Arg::with_name("SB_ONLY") + .help("Only check the superblock.") + .long("super-block-only") + .value_name("SB_ONLY")) + .arg(Arg::with_name("ignore-non-fatal-errors") + .help("Only return a non-zero exit code if a fatal error is found.") + .long("ignore-non-fatal-errors") + .value_name("IGNORE_NON_FATAL")) + .arg(Arg::with_name("clear-needs-check-flag") + .help("Clears the 'needs_check' flag in the superblock") + .long("clear-needs-check") + .value_name("CLEAR_NEEDS_CHECK")) + .arg(Arg::with_name("OVERRIDE_MAPPING_ROOT") + .help("Specify a mapping root to use") + .long("override-mapping-root") + .value_name("OVERRIDE_MAPPING_ROOT") + .takes_value(true)) + .arg(Arg::with_name("METADATA_SNAPSHOT") + .help("Check the metadata snapshot on a live pool") + .short("m") + .long("metadata-snapshot") + .value_name("METADATA_SNAPSHOT")) + + let matches = parser.get_matches(); + let input_file = matches.value_of("INPUT").unwrap(); + let output_file = matches.value_of("OUTPUT").unwrap(); + + if !file_utils::file_exists(input_file) { + eprintln!("Couldn't find input file '{}'.", &input_file); + exit(1); + } + + if let Err(reason) = thinp::pack::toplevel::unpack(&input_file, &output_file) { + println!("Application error: {}", reason); + process::exit(1); + } +} diff --git a/src/block_manager.rs b/src/block_manager.rs index 78dd069..606eb30 100644 --- a/src/block_manager.rs +++ b/src/block_manager.rs @@ -6,8 +6,8 @@ use std::fs::File; pub const BLOCK_SIZE: usize = 4096; -#[repr(align(4096))] pub struct Block { + pub loc: u64, pub data: [u8; BLOCK_SIZE as usize], } @@ -41,7 +41,7 @@ impl BlockManager { fn read_block(&mut self, b: u64) -> io::Result { - let mut buf = Block {data: [0; BLOCK_SIZE]}; + let mut buf = Block {loc: b, data: [0; BLOCK_SIZE]}; self.input.seek(io::SeekFrom::Start(b * (BLOCK_SIZE as u64)))?; self.input.read_exact(&mut buf.data)?; diff --git a/src/checksum.rs b/src/checksum.rs new file mode 100644 index 0000000..1706532 --- /dev/null +++ b/src/checksum.rs @@ -0,0 +1,45 @@ +use byteorder::{LittleEndian, ReadBytesExt}; +use crc32c::crc32c; + +use std::io::Cursor; + +const BLOCK_SIZE: u64 = 4096; +const MAGIC: u64 = 0xa537a0aa6309ef77; +const SUPERBLOCK_CSUM_XOR: u32 = 160774; +const BITMAP_CSUM_XOR: u32 = 240779; +const INDEX_CSUM_XOR: u32 = 160478; +const BTREE_CSUM_XOR: u32 = 121107; + +fn checksum(buf: &[u8]) -> u32 { + crc32c(&buf[4..]) ^ 0xffffffff +} + +#[derive(PartialEq)] +pub enum BT { + SUPERBLOCK, + NODE, + INDEX, + BITMAP, + UNKNOWN, +} + +pub fn metadata_block_type(buf: &[u8]) -> BT { + if buf.len() != BLOCK_SIZE as usize { + return BT::UNKNOWN; + } + + // The checksum is always stored in the first u32 of the buffer. + let mut rdr = Cursor::new(buf); + let sum_on_disk = rdr.read_u32::().unwrap(); + let csum = checksum(buf); + let btype = csum ^ sum_on_disk; + + match btype { + SUPERBLOCK_CSUM_XOR => BT::SUPERBLOCK, + BTREE_CSUM_XOR => BT::NODE, + BITMAP_CSUM_XOR => BT::BITMAP, + INDEX_CSUM_XOR => BT::INDEX, + _ => BT::UNKNOWN, + } +} + diff --git a/src/lib.rs b/src/lib.rs index cf410c5..a2d5755 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,3 +19,5 @@ pub mod check; pub mod file_utils; pub mod pack; pub mod version; +pub mod thin; +pub mod checksum; diff --git a/src/pack/toplevel.rs b/src/pack/toplevel.rs index bd1757d..305a351 100644 --- a/src/pack/toplevel.rs +++ b/src/pack/toplevel.rs @@ -8,7 +8,6 @@ use std::{ fs::OpenOptions, io, io::prelude::*, - io::Cursor, io::Write, ops::DerefMut, sync::{Arc, Mutex}, @@ -20,14 +19,11 @@ use std::sync::mpsc::{sync_channel, Receiver}; use crate::file_utils; use crate::pack::node_encode::*; +use crate::checksum::*; const BLOCK_SIZE: u64 = 4096; const MAGIC: u64 = 0xa537a0aa6309ef77; const PACK_VERSION: u64 = 3; -const SUPERBLOCK_CSUM_XOR: u32 = 160774; -const BITMAP_CSUM_XOR: u32 = 240779; -const INDEX_CSUM_XOR: u32 = 160478; -const BTREE_CSUM_XOR: u32 = 121107; fn shuffle(v: &mut Vec) { let mut rng = rand::thread_rng(); @@ -209,39 +205,6 @@ where Ok(buf) } -fn checksum(buf: &[u8]) -> u32 { - crc32c::crc32c(&buf[4..]) ^ 0xffffffff -} - -#[derive(PartialEq)] -enum BT { - SUPERBLOCK, - NODE, - INDEX, - BITMAP, - UNKNOWN, -} - -fn metadata_block_type(buf: &[u8]) -> BT { - if buf.len() != BLOCK_SIZE as usize { - return BT::UNKNOWN; - } - - // The checksum is always stored in the first u32 of the buffer. - let mut rdr = Cursor::new(buf); - let sum_on_disk = rdr.read_u32::().unwrap(); - let csum = checksum(buf); - let btype = csum ^ sum_on_disk; - - match btype { - SUPERBLOCK_CSUM_XOR => BT::SUPERBLOCK, - BTREE_CSUM_XOR => BT::NODE, - BITMAP_CSUM_XOR => BT::BITMAP, - INDEX_CSUM_XOR => BT::INDEX, - _ => BT::UNKNOWN, - } -} - fn check(r: &PResult) { match r { Ok(_) => {} diff --git a/src/thin/mod.rs b/src/thin/mod.rs new file mode 100644 index 0000000..ec05bf6 --- /dev/null +++ b/src/thin/mod.rs @@ -0,0 +1 @@ +mod superblock; diff --git a/src/thin/superblock.rs b/src/thin/superblock.rs new file mode 100644 index 0000000..722ea7f --- /dev/null +++ b/src/thin/superblock.rs @@ -0,0 +1,60 @@ +use crate::block_manager::*; +use crate::checksum::*; + +const SPACE_MAP_ROOT_SIZE: usize = 128; + +pub struct Superblock { + block: u64, + uuid: String, + version: u32, + time: u32, + transaction_id: u64, + metadata_snap: u64, + data_sm_root: [u8; SPACE_MAP_ROOT_SIZE], + metadata_sn_root: [u8; SPACE_MAP_ROOT_SIZE], + mapping_root: u64, + details_root: u64, + data_block_size: u32, +} + +pub enum CheckSeverity { + Fatal, + NonFatal, +} + +pub trait CheckError { + fn severity(&self) -> CheckSeverity; + fn block(&self) -> u64; + fn sub_errors(&self) -> Vec>; +} + +enum ErrorType { + BadChecksum, + BadBlockType(&'static str), + BadBlock(u64), + BadVersion(u32), + MetadataSnapOutOfBounds(u64), + MappingRootOutOfBounds(u64), + DetailsRootOutOfBounds(u64), +} + +struct SuperblockError { + severity: CheckSeverity, + kind: ErrorType, +} + +use SuperblockDamage::*; + +//------------------------------ + +pub fn check_type(b: &Block) -> Result<(), Box> { + match metadata_block_type(&b.data[0..]) { + SUPERBLOCK => Ok(()), + NODE => Err(Box::new(BadBlockType("BTree Node"))), + INDEX => Err(Box::new(BadBlockType("Space Map Index"))), + BITMAP => Err(Box::new(BadBlockType("Space Map Bitmap"))), + UNKNOWN => Err(Box::new(BadChecksum)), + } +} + +//------------------------------