[thin_check (Rust)] work in progress
This commit is contained in:
parent
3cf6307762
commit
1398cf31d1
@ -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);
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
//------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user