[thin_check (rust)] check metadata space map
This commit is contained in:
parent
e65d2dec6f
commit
3757e1d947
@ -1,6 +1,6 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use fixedbitset::FixedBitSet;
|
use fixedbitset::FixedBitSet;
|
||||||
use nom::{number::complete::*, IResult};
|
use nom::{multi::count, number::complete::*, IResult};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::io_engine::*;
|
use crate::io_engine::*;
|
||||||
@ -78,6 +78,29 @@ impl Unpack for IndexEntry {
|
|||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
const MAX_METADATA_BITMAPS: usize = 255;
|
||||||
|
|
||||||
|
pub struct MetadataIndex {
|
||||||
|
pub indexes: Vec<IndexEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unpack for MetadataIndex {
|
||||||
|
fn disk_size() -> u32 {
|
||||||
|
BLOCK_SIZE as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack(data: &[u8]) -> IResult<&[u8], Self> {
|
||||||
|
let (i, _csum) = le_u32(data)?;
|
||||||
|
let (i, _padding) = le_u32(i)?;
|
||||||
|
let (i, _blocknr) = le_u64(i)?;
|
||||||
|
let (i, indexes) = count(IndexEntry::unpack, MAX_METADATA_BITMAPS)(i)?;
|
||||||
|
|
||||||
|
Ok((i, MetadataIndex {indexes}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BitmapHeader {
|
pub struct BitmapHeader {
|
||||||
pub csum: u32,
|
pub csum: u32,
|
||||||
|
@ -232,7 +232,7 @@ fn spinner_thread(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(SpinnerCmd::Title(txt)) => {
|
Ok(SpinnerCmd::Title(txt)) => {
|
||||||
let mut fmt = "Checking thin metadata [{bar:40.cyan/blue}] Remaining {eta}, ".to_string();
|
let mut fmt = "Checking thin metadata [{bar:40}] Remaining {eta}, ".to_string();
|
||||||
fmt.push_str(&txt);
|
fmt.push_str(&txt);
|
||||||
bar.set_style(
|
bar.set_style(
|
||||||
ProgressStyle::default_bar()
|
ProgressStyle::default_bar()
|
||||||
@ -259,6 +259,87 @@ fn spinner_thread(
|
|||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
|
fn check_space_map(
|
||||||
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
|
entries: Vec<IndexEntry>,
|
||||||
|
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
||||||
|
root: SMRoot,
|
||||||
|
) -> Result<()> {
|
||||||
|
let sm = sm.lock().unwrap();
|
||||||
|
|
||||||
|
// overflow btree
|
||||||
|
{
|
||||||
|
let mut v = OverflowChecker::new(&*sm);
|
||||||
|
let mut w = BTreeWalker::new(engine.clone(), false);
|
||||||
|
w.walk(&mut v, root.ref_count_root)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut blocks = Vec::new();
|
||||||
|
for i in &entries {
|
||||||
|
blocks.push(Block::new(i.blocknr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: we should do this in batches
|
||||||
|
engine.read_many(&mut blocks)?;
|
||||||
|
|
||||||
|
let mut leaks = 0;
|
||||||
|
let mut fail = false;
|
||||||
|
let mut blocknr = 0;
|
||||||
|
for n in 0..entries.len() {
|
||||||
|
let b = &blocks[n];
|
||||||
|
if checksum::metadata_block_type(&b.get_data()) != checksum::BT::BITMAP {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Index entry points to block ({}) that isn't a bitmap",
|
||||||
|
b.loc
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let bitmap = unpack::<Bitmap>(b.get_data())?;
|
||||||
|
for e in bitmap.entries {
|
||||||
|
if blocknr >= root.nr_blocks {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match e {
|
||||||
|
BitmapEntry::Small(actual) => {
|
||||||
|
let expected = sm.get(blocknr)?;
|
||||||
|
if actual == 1 && expected == 0 {
|
||||||
|
// eprintln!("Data block {} leaked.", blocknr);
|
||||||
|
leaks += 1;
|
||||||
|
} else if actual != expected as u8 {
|
||||||
|
eprintln!("Bad reference count for data block {}. Expected {}, but space map contains {}.",
|
||||||
|
blocknr, expected, actual);
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BitmapEntry::Overflow => {
|
||||||
|
let expected = sm.get(blocknr)?;
|
||||||
|
if expected < 3 {
|
||||||
|
eprintln!("Bad reference count for data block {}. Expected {}, but space map says it's >= 3.",
|
||||||
|
blocknr, expected);
|
||||||
|
fail = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blocknr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if leaks > 0 {
|
||||||
|
eprintln!(
|
||||||
|
"{} data blocks have leaked. Use --auto-repair to fix.",
|
||||||
|
leaks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if fail {
|
||||||
|
return Err(anyhow!("Inconsistent data space map"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
const MAX_CONCURRENT_IO: u32 = 1024;
|
const MAX_CONCURRENT_IO: u32 = 1024;
|
||||||
|
|
||||||
pub struct ThinCheckOptions<'a> {
|
pub struct ThinCheckOptions<'a> {
|
||||||
@ -346,89 +427,40 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> {
|
|||||||
pool.join();
|
pool.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the data space map.
|
spinner.set_title("data space map")?;
|
||||||
|
let root = unpack::<SMRoot>(&sb.data_sm_root[0..])?;
|
||||||
|
let entries = btree_to_map::<IndexEntry>(engine.clone(), false, root.bitmap_root)?;
|
||||||
|
let entries: Vec<IndexEntry> = entries.values().cloned().collect();
|
||||||
|
check_space_map(engine.clone(), entries, data_sm.clone(), root)?;
|
||||||
|
|
||||||
|
spinner.set_title("metadata space map")?;
|
||||||
|
let root = unpack::<SMRoot>(&sb.metadata_sm_root[0..])?;
|
||||||
|
let mut b = Block::new(root.bitmap_root);
|
||||||
|
engine.read(&mut b)?;
|
||||||
|
let entries = unpack::<MetadataIndex>(b.get_data())?.indexes;
|
||||||
|
|
||||||
|
// Unused entries will point to block 0
|
||||||
|
let entries: Vec<IndexEntry> = entries
|
||||||
|
.iter()
|
||||||
|
.take_while(|e| e.blocknr != 0)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// We need to increment the ref counts for all the bitmaps, then walk the overflow
|
||||||
|
// tree to inc the ref counts for those.
|
||||||
{
|
{
|
||||||
spinner.set_title("data space map")?;
|
let mut sm = metadata_sm.lock().unwrap();
|
||||||
let data_sm = data_sm.lock().unwrap();
|
for ie in &entries {
|
||||||
let root = unpack::<SMRoot>(&sb.data_sm_root[0..])?;
|
sm.inc(ie.blocknr, 1)?;
|
||||||
let nr_data_blocks = root.nr_blocks;
|
|
||||||
|
|
||||||
// overflow btree
|
|
||||||
{
|
|
||||||
let mut v = OverflowChecker::new(&*data_sm);
|
|
||||||
let mut w = BTreeWalker::new(engine.clone(), false);
|
|
||||||
w.walk(&mut v, root.ref_count_root)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bitmaps
|
|
||||||
let entries = btree_to_map::<IndexEntry>(engine.clone(), false, root.bitmap_root)?;
|
|
||||||
|
|
||||||
let mut blocks = Vec::new();
|
|
||||||
for (_k, i) in &entries {
|
|
||||||
blocks.push(Block::new(i.blocknr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: we should do this in batches
|
|
||||||
engine.read_many(&mut blocks)?;
|
|
||||||
|
|
||||||
let mut leaks = 0;
|
|
||||||
let mut fail = false;
|
|
||||||
let mut blocknr = 0;
|
|
||||||
for n in 0..entries.len() {
|
|
||||||
let b = &blocks[n];
|
|
||||||
if checksum::metadata_block_type(&b.get_data()) != checksum::BT::BITMAP {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Index entry points to block ({}) that isn't a bitmap",
|
|
||||||
b.loc
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let bitmap = unpack::<Bitmap>(b.get_data())?;
|
|
||||||
for e in bitmap.entries {
|
|
||||||
if blocknr >= nr_data_blocks {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
match e {
|
|
||||||
BitmapEntry::Small(actual) => {
|
|
||||||
let expected = data_sm.get(blocknr)?;
|
|
||||||
if actual == 1 && expected == 0 {
|
|
||||||
// eprintln!("Data block {} leaked.", blocknr);
|
|
||||||
leaks += 1;
|
|
||||||
} else if actual != expected as u8 {
|
|
||||||
eprintln!("Bad reference count for data block {}. Expected {}, but space map contains {}.",
|
|
||||||
blocknr, expected, actual);
|
|
||||||
fail = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BitmapEntry::Overflow => {
|
|
||||||
let expected = data_sm.get(blocknr)?;
|
|
||||||
if expected < 3 {
|
|
||||||
eprintln!("Bad reference count for data block {}. Expected {}, but space map says it's >= 3.",
|
|
||||||
blocknr, expected);
|
|
||||||
fail = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
blocknr += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if leaks > 0 {
|
|
||||||
eprintln!(
|
|
||||||
"{} data blocks have leaked. Use --auto-repair to fix.",
|
|
||||||
leaks
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if fail {
|
|
||||||
spinner.abort()?;
|
|
||||||
return Err(anyhow!("Inconsistent data space map"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let _counts = btree_to_map_with_sm::<u32>(
|
||||||
// Check the metadata space map.
|
engine.clone(),
|
||||||
spinner.set_title("metadata space map")?;
|
metadata_sm.clone(),
|
||||||
|
false,
|
||||||
|
root.ref_count_root,
|
||||||
|
)?;
|
||||||
|
check_space_map(engine.clone(), entries, metadata_sm.clone(), root)?;
|
||||||
|
|
||||||
spinner.complete()?;
|
spinner.complete()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user