[thin_check (Rust)] work in progress

This commit is contained in:
Joe Thornber 2020-07-27 15:53:42 +01:00
parent 3cf6307762
commit 1398cf31d1
5 changed files with 187 additions and 64 deletions

View File

@ -2,6 +2,7 @@ extern crate clap;
extern crate thinp; extern crate thinp;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path;
use std::process; use std::process;
use thinp::file_utils; use thinp::file_utils;
@ -9,46 +10,63 @@ use std::process::exit;
fn main() { fn main() {
let parser = App::new("thin_check") let parser = App::new("thin_check")
.version(thinp::version::TOOLS_VERSION) .version(thinp::version::TOOLS_VERSION)
.about("Validates thin provisioning metadata on a device or file.") .about("Validates thin provisioning metadata on a device or file.")
.arg(Arg::with_name("QUIET") .arg(
.help("Suppress output messages, return only exit code.") Arg::with_name("QUIET")
.short("q") .help("Suppress output messages, return only exit code.")
.long("quiet") .short("q")
.value_name("QUIET")) .long("quiet")
.arg(Arg::with_name("SB_ONLY") .value_name("QUIET"),
.help("Only check the superblock.") )
.long("super-block-only") .arg(
.value_name("SB_ONLY")) Arg::with_name("SB_ONLY")
.arg(Arg::with_name("ignore-non-fatal-errors") .help("Only check the superblock.")
.help("Only return a non-zero exit code if a fatal error is found.") .long("super-block-only")
.long("ignore-non-fatal-errors") .value_name("SB_ONLY"),
.value_name("IGNORE_NON_FATAL")) )
.arg(Arg::with_name("clear-needs-check-flag") .arg(
.help("Clears the 'needs_check' flag in the superblock") Arg::with_name("ignore-non-fatal-errors")
.long("clear-needs-check") .help("Only return a non-zero exit code if a fatal error is found.")
.value_name("CLEAR_NEEDS_CHECK")) .long("ignore-non-fatal-errors")
.arg(Arg::with_name("OVERRIDE_MAPPING_ROOT") .value_name("IGNORE_NON_FATAL"),
.help("Specify a mapping root to use") )
.long("override-mapping-root") .arg(
.value_name("OVERRIDE_MAPPING_ROOT") Arg::with_name("clear-needs-check-flag")
.takes_value(true)) .help("Clears the 'needs_check' flag in the superblock")
.arg(Arg::with_name("METADATA_SNAPSHOT") .long("clear-needs-check")
.help("Check the metadata snapshot on a live pool") .value_name("CLEAR_NEEDS_CHECK"),
.short("m") )
.long("metadata-snapshot") .arg(
.value_name("METADATA_SNAPSHOT")) 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"),
)
.arg(
Arg::with_name("INPUT")
.help("Specify the input device to check")
.required(true)
.index(1),
);
let matches = parser.get_matches(); let matches = parser.get_matches();
let input_file = matches.value_of("INPUT").unwrap(); let input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = matches.value_of("OUTPUT").unwrap();
if !file_utils::file_exists(input_file) { if !file_utils::file_exists(input_file) {
eprintln!("Couldn't find input file '{}'.", &input_file); eprintln!("Couldn't find input file '{:?}'.", &input_file);
exit(1); exit(1);
} }
if let Err(reason) = thinp::pack::toplevel::unpack(&input_file, &output_file) { if let Err(reason) = thinp::thin::check::check(&input_file) {
println!("Application error: {}", reason); println!("Application error: {}", reason);
process::exit(1); process::exit(1);
} }

View File

@ -1,51 +1,148 @@
use anyhow::{anyhow, Result};
use rio::{self, Completion, Rio};
use std::alloc::{alloc, dealloc, Layout};
use std::collections::HashMap;
use std::fs::File;
use std::fs::OpenOptions;
use std::io; use std::io;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::OpenOptionsExt;
use std::fs::File; use std::path::Path;
use std::sync::{Arc, Mutex};
pub const BLOCK_SIZE: usize = 4096; pub const BLOCK_SIZE: usize = 4096;
const ALIGN: usize = 4096;
// FIXME: introduce a cache
// FIXME: use O_DIRECT
#[derive(Debug)]
pub struct Block { pub struct Block {
pub loc: u64, pub loc: u64,
pub data: [u8; BLOCK_SIZE as usize], data: *mut u8,
}
pub struct BlockManager {
pub nr_blocks: u64,
input: File,
} }
fn get_nr_blocks(path: &str) -> io::Result<u64> { impl Block {
pub fn new(loc: u64) -> Block {
let layout = Layout::from_size_align(BLOCK_SIZE, ALIGN).unwrap();
let ptr = unsafe { alloc(layout) };
assert!(!ptr.is_null(), "out of memory");
Block { loc, data: ptr }
}
fn get_data(&self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut::<'static>(self.data, BLOCK_SIZE) }
}
}
impl Drop for Block {
fn drop(&mut self) {
let layout = Layout::from_size_align(BLOCK_SIZE, ALIGN).unwrap();
unsafe {
dealloc(self.data, layout);
}
}
}
//------------------------------------------
pub trait IoEngine {
fn get_nr_blocks(&self) -> u64;
fn read(&mut self, blocks: &mut Vec<Block>) -> Result<()>;
}
fn get_nr_blocks(path: &Path) -> io::Result<u64> {
let metadata = std::fs::metadata(path)?; let metadata = std::fs::metadata(path)?;
Ok(metadata.len() / (BLOCK_SIZE as u64)) Ok(metadata.len() / (BLOCK_SIZE as u64))
} }
impl BlockManager { //------------------------------------------
pub fn new(path: &str, _cache_size: usize) -> io::Result<BlockManager> {
pub struct SyncIoEngine {
nr_blocks: u64,
input: File,
}
impl SyncIoEngine {
pub fn new(path: &Path) -> Result<SyncIoEngine> {
let input = OpenOptions::new() let input = OpenOptions::new()
.read(true) .read(true)
.write(false) .write(false)
.custom_flags(libc::O_DIRECT) .custom_flags(libc::O_DIRECT)
.open(path)?; .open(path)?;
Ok(BlockManager { let ring = rio::new()?;
Ok(SyncIoEngine {
nr_blocks: get_nr_blocks(path)?,
input,
})
}
}
impl IoEngine for SyncIoEngine {
fn get_nr_blocks(&self) -> u64 {
self.nr_blocks
}
fn read(&mut self, blocks: &mut Vec<Block>) -> Result<()> {
for b in blocks.into_iter() {
self.input.seek(io::SeekFrom::Start(0))?;
self.input.read_exact(&mut b.get_data())?;
}
Ok(())
}
}
//------------------------------------------
/*
pub struct AsyncIoEngine {
ring: Rio,
nr_blocks: u64,
input: File,
}
impl AsyncIoEngine {
pub fn new(path: &Path) -> Result<IoEngine> {
let input = OpenOptions::new()
.read(true)
.write(false)
.custom_flags(libc::O_DIRECT)
.open(path)?;
let ring = rio::new()?;
Ok(IoEngine {
ring,
nr_blocks: get_nr_blocks(path)?, nr_blocks: get_nr_blocks(path)?,
input, input,
}) })
} }
pub fn get(&mut self, b: u64) -> io::Result<Block> { pub fn read(&self, blocks: &mut Vec<Block>) -> Result<()> {
self.read_block(b) // FIXME: using a bounce buffer as a hack, since b.get_data() will not have
} // a big enough lifetime.
let mut bounce_buffer = vec![0; blocks.len() * BLOCK_SIZE];
let mut completions = Vec::new();
fn read_block(&mut self, b: u64) -> io::Result<Block> for n in 0..blocks.len() {
{ let b = &blocks[n];
let mut buf = Block {loc: b, data: [0; BLOCK_SIZE]}; let at = b.loc * BLOCK_SIZE as u64;
let completion = self.ring.read_at(&self.input, &slice, at);
completions.push(completion);
}
self.input.seek(io::SeekFrom::Start(b * (BLOCK_SIZE as u64)))?; for c in completions {
self.input.read_exact(&mut buf.data)?; let n = c.wait()?;
if n != BLOCK_SIZE {
return Err(anyhow!("short read"));
}
}
Ok(buf) // copy out of the bounce buffer
Ok(())
} }
} }
*/

View File

@ -16,11 +16,9 @@ extern crate quickcheck;
extern crate quickcheck_macros; extern crate quickcheck_macros;
pub mod block_manager; pub mod block_manager;
pub mod check;
pub mod file_utils; pub mod file_utils;
pub mod pack; pub mod pack;
pub mod shrink; pub mod shrink;
pub mod thin; pub mod thin;
pub mod version; pub mod version;
pub mod thin;
pub mod checksum; pub mod checksum;

View File

@ -1,13 +1,23 @@
use std::error::Error; use std::error::Error;
use std::path::Path;
use std::time::{Duration, Instant};
use std::thread;
use std::sync::{Arc, Mutex};
use crate::block_manager::BlockManager; use crate::block_manager::{Block, IoEngine, SyncIoEngine, BLOCK_SIZE};
pub fn check(dev: &str) -> Result<(), Box<dyn Error>> { pub fn check(dev: &Path) -> Result<(), Box<dyn Error>> {
let mut bm = BlockManager::new(dev, 1024)?; let mut engine = SyncIoEngine::new(dev)?;
let count = 4096;
for b in 0..100 { let mut blocks = Vec::new();
let _block = bm.get(b)?; for n in 0..count {
blocks.push(Block::new(n));
} }
let now = Instant::now();
engine.read(&mut blocks)?;
println!("read {} blocks in {} ms", count, now.elapsed().as_millis());
Ok(()) Ok(())
} }

View File

@ -1,4 +1,3 @@
use anyhow::Result;
use crate::block_manager::*; use crate::block_manager::*;
use crate::checksum::*; use crate::checksum::*;
@ -12,7 +11,7 @@ pub struct Superblock {
transaction_id: u64, transaction_id: u64,
metadata_snap: u64, metadata_snap: u64,
data_sm_root: [u8; SPACE_MAP_ROOT_SIZE], data_sm_root: [u8; SPACE_MAP_ROOT_SIZE],
metadata_sn_root: [u8; SPACE_MAP_ROOT_SIZE], metadata_sm_root: [u8; SPACE_MAP_ROOT_SIZE],
mapping_root: u64, mapping_root: u64,
details_root: u64, details_root: u64,
data_block_size: u32, data_block_size: u32,
@ -44,6 +43,7 @@ struct SuperblockError {
kind: ErrorType, kind: ErrorType,
} }
/*
use SuperblockDamage::*; use SuperblockDamage::*;
//------------------------------ //------------------------------
@ -57,5 +57,5 @@ pub fn check_type(b: &Block) -> Result<()> {
UNKNOWN => Err(Box::new(BadChecksum)), UNKNOWN => Err(Box::new(BadChecksum)),
} }
} }
*/
//------------------------------ //------------------------------