[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;
use clap::{App, Arg};
use std::path::Path;
use std::process;
use thinp::file_utils;
@ -9,46 +10,63 @@ use std::process::exit;
fn main() {
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.")
.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"))
.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"),
)
.arg(
Arg::with_name("INPUT")
.help("Specify the input device to check")
.required(true)
.index(1),
);
let matches = parser.get_matches();
let input_file = matches.value_of("INPUT").unwrap();
let output_file = matches.value_of("OUTPUT").unwrap();
let input_file = Path::new(matches.value_of("INPUT").unwrap());
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);
}
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);
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::{Read, Seek};
use std::fs::OpenOptions;
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;
const ALIGN: usize = 4096;
// FIXME: introduce a cache
// FIXME: use O_DIRECT
#[derive(Debug)]
pub struct Block {
pub loc: u64,
pub data: [u8; BLOCK_SIZE as usize],
}
pub struct BlockManager {
pub nr_blocks: u64,
input: File,
data: *mut u8,
}
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)?;
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()
.read(true)
.write(false)
.custom_flags(libc::O_DIRECT)
.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)?,
input,
})
}
pub fn get(&mut self, b: u64) -> io::Result<Block> {
self.read_block(b)
}
pub fn read(&self, blocks: &mut Vec<Block>) -> Result<()> {
// 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>
{
let mut buf = Block {loc: b, data: [0; BLOCK_SIZE]};
for n in 0..blocks.len() {
let b = &blocks[n];
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)))?;
self.input.read_exact(&mut buf.data)?;
for c in completions {
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;
pub mod block_manager;
pub mod check;
pub mod file_utils;
pub mod pack;
pub mod shrink;
pub mod thin;
pub mod version;
pub mod thin;
pub mod checksum;

View File

@ -1,13 +1,23 @@
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>> {
let mut bm = BlockManager::new(dev, 1024)?;
pub fn check(dev: &Path) -> Result<(), Box<dyn Error>> {
let mut engine = SyncIoEngine::new(dev)?;
let count = 4096;
for b in 0..100 {
let _block = bm.get(b)?;
let mut blocks = Vec::new();
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(())
}

View File

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