[thin_repair (rust)] Add searching for missing roots
Based on the method of commit 9e20465
This commit is contained in:
parent
b7132440d0
commit
5dad1097c3
@ -1,4 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use threadpool::ThreadPool;
|
||||
|
||||
@ -565,6 +565,58 @@ pub fn btree_to_map_with_path<V: Unpack + Copy>(
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
struct KeyCollector {
|
||||
keys: Mutex<BTreeSet<u64>>,
|
||||
}
|
||||
|
||||
impl KeyCollector {
|
||||
fn new() -> KeyCollector {
|
||||
KeyCollector {
|
||||
keys: Mutex::new(BTreeSet::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Unpack + Copy> NodeVisitor<V> for KeyCollector {
|
||||
fn visit(
|
||||
&self,
|
||||
_path: &[u64],
|
||||
_kr: &KeyRange,
|
||||
_h: &NodeHeader,
|
||||
keys: &[u64],
|
||||
_values: &[V],
|
||||
) -> Result<()> {
|
||||
let mut keyset = self.keys.lock().unwrap();
|
||||
for k in keys {
|
||||
keyset.insert(*k);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_again(&self, _path: &[u64], _b: u64) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end_walk(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn btree_to_key_set<V: Unpack + Copy>(
|
||||
path: &mut Vec<u64>,
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
ignore_non_fatal: bool,
|
||||
root: u64,
|
||||
) -> Result<BTreeSet<u64>> {
|
||||
let walker = BTreeWalker::new(engine, ignore_non_fatal);
|
||||
let visitor = KeyCollector::new();
|
||||
walker.walk::<_, V>(path, &visitor, root)?;
|
||||
Ok(visitor.keys.into_inner().unwrap())
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
struct NoopVisitor<V> {
|
||||
dummy: std::marker::PhantomData<V>,
|
||||
}
|
||||
|
718
src/thin/metadata_repair.rs
Normal file
718
src/thin/metadata_repair.rs
Normal file
@ -0,0 +1,718 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use fixedbitset::FixedBitSet;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::checksum;
|
||||
use crate::io_engine::IoEngine;
|
||||
use crate::pdata::btree::*;
|
||||
use crate::pdata::btree_walker::*;
|
||||
use crate::pdata::space_map_common::*;
|
||||
use crate::pdata::unpack::Unpack;
|
||||
use crate::thin::block_time::*;
|
||||
use crate::thin::device_detail::*;
|
||||
use crate::thin::superblock::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
pub struct SuperblockOverrides {
|
||||
pub transaction_id: Option<u64>,
|
||||
pub data_block_size: Option<u32>,
|
||||
pub nr_data_blocks: Option<u64>,
|
||||
}
|
||||
|
||||
pub struct FoundRoots {
|
||||
mapping_root: u64,
|
||||
details_root: u64,
|
||||
time: u32,
|
||||
}
|
||||
|
||||
fn merge_time_counts(lhs: &mut BTreeMap<u32, u32>, rhs: &BTreeMap<u32, u32>) -> Result<()> {
|
||||
for (t, c) in rhs.iter() {
|
||||
*lhs.entry(*t).or_insert(0) += c;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct DevInfo {
|
||||
b: u64,
|
||||
nr_devices: u64,
|
||||
nr_mappings: u64,
|
||||
key_low: u64, // min dev_id
|
||||
key_high: u64, // max dev_id, inclusive
|
||||
time_counts: BTreeMap<u32, u32>,
|
||||
age: u32,
|
||||
pushed: bool,
|
||||
}
|
||||
|
||||
impl DevInfo {
|
||||
fn new(b: u64) -> DevInfo {
|
||||
DevInfo {
|
||||
b,
|
||||
nr_devices: 0,
|
||||
nr_mappings: 0,
|
||||
key_low: 0,
|
||||
key_high: 0,
|
||||
time_counts: BTreeMap::<u32, u32>::new(),
|
||||
age: 0,
|
||||
pushed: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn push_child(&mut self, child: &DevInfo) -> Result<()> {
|
||||
if self.key_high > 0 && child.key_low <= self.key_high {
|
||||
return Err(anyhow!("incompatible child"));
|
||||
}
|
||||
if !self.pushed {
|
||||
self.key_low = child.key_low;
|
||||
self.pushed = true;
|
||||
}
|
||||
self.key_high = child.key_high;
|
||||
self.nr_devices += child.nr_devices;
|
||||
self.nr_mappings += child.nr_mappings;
|
||||
merge_time_counts(&mut self.time_counts, &child.time_counts)?;
|
||||
self.age = std::cmp::max(self.age, child.age);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct MappingsInfo {
|
||||
_b: u64,
|
||||
nr_mappings: u64,
|
||||
key_low: u64, // min mapped block
|
||||
key_high: u64, // max mapped block, inclusive
|
||||
time_counts: BTreeMap<u32, u32>,
|
||||
age: u32,
|
||||
pushed: bool,
|
||||
}
|
||||
|
||||
impl MappingsInfo {
|
||||
fn new(b: u64) -> MappingsInfo {
|
||||
MappingsInfo {
|
||||
_b: b,
|
||||
nr_mappings: 0,
|
||||
key_low: 0,
|
||||
key_high: 0,
|
||||
time_counts: BTreeMap::<u32, u32>::new(),
|
||||
age: 0,
|
||||
pushed: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn push_child(&mut self, child: &MappingsInfo) -> Result<()> {
|
||||
if self.key_high > 0 && child.key_low <= self.key_high {
|
||||
return Err(anyhow!("incompatible child"));
|
||||
}
|
||||
if !self.pushed {
|
||||
self.key_low = child.key_low;
|
||||
self.pushed = true;
|
||||
}
|
||||
self.key_high = child.key_high;
|
||||
self.nr_mappings += child.nr_mappings;
|
||||
merge_time_counts(&mut self.time_counts, &child.time_counts)?;
|
||||
self.age = std::cmp::max(self.age, child.age);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DetailsInfo {
|
||||
_b: u64,
|
||||
nr_devices: u64,
|
||||
nr_mappings: u64,
|
||||
key_low: u64,
|
||||
key_high: u64, // inclusive
|
||||
age: u32,
|
||||
pushed: bool,
|
||||
}
|
||||
|
||||
impl DetailsInfo {
|
||||
fn new(b: u64) -> DetailsInfo {
|
||||
DetailsInfo {
|
||||
_b: b,
|
||||
nr_devices: 0,
|
||||
nr_mappings: 0,
|
||||
key_low: 0,
|
||||
key_high: 0,
|
||||
age: 0,
|
||||
pushed: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn push_child(&mut self, child: &DetailsInfo) -> Result<()> {
|
||||
if self.key_high > 0 && child.key_low <= self.key_high {
|
||||
return Err(anyhow!("incompatible child"));
|
||||
}
|
||||
if !self.pushed {
|
||||
self.key_low = child.key_low;
|
||||
self.pushed = true;
|
||||
}
|
||||
self.key_high = child.key_high;
|
||||
self.nr_devices += child.nr_devices;
|
||||
self.nr_mappings += child.nr_mappings;
|
||||
self.age = std::cmp::max(self.age, child.age);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
enum NodeInfo {
|
||||
Dev(DevInfo),
|
||||
Mappings(MappingsInfo),
|
||||
Details(DetailsInfo),
|
||||
}
|
||||
|
||||
struct NodeCollector {
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
nr_blocks: u64,
|
||||
examined: FixedBitSet,
|
||||
referenced: FixedBitSet,
|
||||
infos: BTreeMap<u64, NodeInfo>,
|
||||
}
|
||||
|
||||
impl NodeCollector {
|
||||
pub fn new(engine: Arc<dyn IoEngine + Send + Sync>) -> NodeCollector {
|
||||
let nr_blocks = engine.get_nr_blocks();
|
||||
NodeCollector {
|
||||
engine,
|
||||
nr_blocks,
|
||||
examined: FixedBitSet::with_capacity(nr_blocks as usize),
|
||||
referenced: FixedBitSet::with_capacity(nr_blocks as usize),
|
||||
infos: BTreeMap::<u64, NodeInfo>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_dev_subtree_info(
|
||||
&mut self,
|
||||
header: &NodeHeader,
|
||||
_keys: &[u64],
|
||||
values: &[u64],
|
||||
) -> Result<NodeInfo> {
|
||||
let mut info = DevInfo::new(header.block);
|
||||
|
||||
for b in values {
|
||||
let child = self.get_info(*b)?;
|
||||
if let NodeInfo::Dev(ref child) = child {
|
||||
info.push_child(child)?;
|
||||
} else {
|
||||
return Err(anyhow!("incompatible child type"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NodeInfo::Dev(info))
|
||||
}
|
||||
|
||||
fn gather_mappings_subtree_info(
|
||||
&mut self,
|
||||
header: &NodeHeader,
|
||||
_keys: &[u64],
|
||||
values: &[u64],
|
||||
) -> Result<NodeInfo> {
|
||||
let mut info = MappingsInfo::new(header.block);
|
||||
|
||||
for b in values {
|
||||
let child = self.get_info(*b)?;
|
||||
if let NodeInfo::Mappings(ref child) = child {
|
||||
info.push_child(child)?;
|
||||
} else {
|
||||
return Err(anyhow!("incompatible child type"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NodeInfo::Mappings(info))
|
||||
}
|
||||
|
||||
fn gather_details_subtree_info(
|
||||
&mut self,
|
||||
header: &NodeHeader,
|
||||
_keys: &[u64],
|
||||
values: &[u64],
|
||||
) -> Result<NodeInfo> {
|
||||
let mut info = DetailsInfo::new(header.block);
|
||||
|
||||
for b in values {
|
||||
let child = self.get_info(*b)?;
|
||||
if let NodeInfo::Details(ref child) = child {
|
||||
info.push_child(child)?;
|
||||
} else {
|
||||
return Err(anyhow!("incompatible child type"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NodeInfo::Details(info))
|
||||
}
|
||||
|
||||
fn gather_subtree_info(
|
||||
&mut self,
|
||||
header: &NodeHeader,
|
||||
keys: &[u64],
|
||||
values: &[u64],
|
||||
) -> Result<NodeInfo> {
|
||||
// An internal node should have at least one entry
|
||||
if header.nr_entries == 0 {
|
||||
return Err(anyhow!("unexpected nr_entries"));
|
||||
}
|
||||
|
||||
let first_child = self.get_info(values[0])?;
|
||||
let info = match first_child {
|
||||
NodeInfo::Dev { .. } => self.gather_dev_subtree_info(header, keys, values),
|
||||
NodeInfo::Mappings { .. } => self.gather_mappings_subtree_info(header, keys, values),
|
||||
NodeInfo::Details { .. } => self.gather_details_subtree_info(header, keys, values),
|
||||
}?;
|
||||
|
||||
// the node is a valid parent, so mark the children as non-orphan
|
||||
for b in values {
|
||||
self.referenced.set(*b as usize, true);
|
||||
}
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
fn gather_dev_leaf_info(
|
||||
&mut self,
|
||||
header: &NodeHeader,
|
||||
keys: &[u64],
|
||||
values: &[u64],
|
||||
) -> Result<NodeInfo> {
|
||||
let mut info = DevInfo::new(header.block);
|
||||
info.nr_devices = header.nr_entries as u64;
|
||||
|
||||
// min & max device id
|
||||
if header.nr_entries > 0 {
|
||||
info.key_low = keys[0];
|
||||
info.key_high = keys[keys.len() - 1];
|
||||
}
|
||||
|
||||
for b in values {
|
||||
let child = self.get_info(*b)?; // subtree root
|
||||
if let NodeInfo::Mappings(ref child) = child {
|
||||
info.nr_mappings += child.nr_mappings;
|
||||
merge_time_counts(&mut info.time_counts, &child.time_counts)?;
|
||||
info.age = std::cmp::max(info.age, child.age);
|
||||
} else {
|
||||
return Err(anyhow!("not a data mapping subtree root"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NodeInfo::Dev(info))
|
||||
}
|
||||
|
||||
fn gather_mapping_leaf_info(
|
||||
&self,
|
||||
header: &NodeHeader,
|
||||
keys: &[u64],
|
||||
values: &[BlockTime],
|
||||
) -> Result<NodeInfo> {
|
||||
let mut info = MappingsInfo::new(header.block);
|
||||
info.nr_mappings = header.nr_entries as u64;
|
||||
|
||||
// min & max logical block address
|
||||
if header.nr_entries > 0 {
|
||||
info.key_low = keys[0];
|
||||
info.key_high = keys[keys.len() - 1];
|
||||
}
|
||||
|
||||
for bt in values {
|
||||
*info.time_counts.entry(bt.time).or_insert(0) += 1;
|
||||
info.age = std::cmp::max(info.age, bt.time);
|
||||
}
|
||||
|
||||
// TODO: propagate the low & high data block address upward,
|
||||
// for rebuilding the nr_data_blocks in superblock?
|
||||
Ok(NodeInfo::Mappings(info))
|
||||
}
|
||||
|
||||
fn gather_details_leaf_info(
|
||||
&self,
|
||||
header: &NodeHeader,
|
||||
keys: &[u64],
|
||||
values: &[DeviceDetail],
|
||||
) -> Result<NodeInfo> {
|
||||
let mut info = DetailsInfo::new(header.block);
|
||||
info.nr_devices = header.nr_entries as u64;
|
||||
|
||||
// min & max device id
|
||||
if header.nr_entries > 0 {
|
||||
info.key_low = keys[0];
|
||||
info.key_high = keys[keys.len() - 1];
|
||||
}
|
||||
|
||||
for details in values {
|
||||
info.nr_mappings += details.mapped_blocks;
|
||||
info.age = std::cmp::max(info.age, details.creation_time);
|
||||
info.age = std::cmp::max(info.age, details.snapshotted_time);
|
||||
}
|
||||
|
||||
Ok(NodeInfo::Details(info))
|
||||
}
|
||||
|
||||
fn is_top_level(&self, values: &[u64]) -> bool {
|
||||
if values.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for v in values {
|
||||
if *v > self.nr_blocks {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn gather_info(&mut self, b: u64) -> Result<NodeInfo> {
|
||||
let blk = self.engine.read(b)?;
|
||||
|
||||
let bt = checksum::metadata_block_type(blk.get_data());
|
||||
if bt != checksum::BT::NODE {
|
||||
return Err(anyhow!("not a btree node"));
|
||||
}
|
||||
|
||||
let (_, hdr) = NodeHeader::unpack(blk.get_data())?;
|
||||
if hdr.value_size as usize == std::mem::size_of::<u64>() {
|
||||
let node = unpack_node::<u64>(&[0], blk.get_data(), true, true)?;
|
||||
match node {
|
||||
Node::Internal {
|
||||
ref header,
|
||||
ref keys,
|
||||
ref values,
|
||||
} => self.gather_subtree_info(header, keys, values),
|
||||
Node::Leaf {
|
||||
ref header,
|
||||
ref keys,
|
||||
ref values,
|
||||
} => {
|
||||
if self.is_top_level(values) {
|
||||
self.gather_dev_leaf_info(header, keys, values)
|
||||
} else {
|
||||
// FIXME: convert the values only, to avoid unpacking the node twice
|
||||
let node = unpack_node::<BlockTime>(&[0], blk.get_data(), true, true)?;
|
||||
if let Node::Leaf {
|
||||
ref header,
|
||||
ref keys,
|
||||
ref values,
|
||||
} = node
|
||||
{
|
||||
self.gather_mapping_leaf_info(header, keys, values)
|
||||
} else {
|
||||
Err(anyhow!("unexpected internal node"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if hdr.value_size == DeviceDetail::disk_size() {
|
||||
let node = unpack_node::<DeviceDetail>(&[0], blk.get_data(), true, true)?;
|
||||
if let Node::Leaf {
|
||||
ref header,
|
||||
ref keys,
|
||||
ref values,
|
||||
} = node
|
||||
{
|
||||
self.gather_details_leaf_info(header, keys, values)
|
||||
} else {
|
||||
Err(anyhow!("unexpected value size within an internal node"))
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!("not the value size of interest"))
|
||||
}
|
||||
}
|
||||
|
||||
fn read_info(&self, b: u64) -> Result<&NodeInfo> {
|
||||
if self.examined.contains(b as usize) {
|
||||
// TODO: use an extra 'valid' bitset for faster lookup?
|
||||
self.infos
|
||||
.get(&b)
|
||||
.ok_or_else(|| anyhow!("block {} was examined as invalid", b))
|
||||
} else {
|
||||
Err(anyhow!("not examined"))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_info(&mut self, b: u64) -> Result<&NodeInfo> {
|
||||
if self.examined.contains(b as usize) {
|
||||
// TODO: use an extra 'valid' bitset for faster lookup?
|
||||
self.infos
|
||||
.get(&b)
|
||||
.ok_or_else(|| anyhow!("block {} was examined as invalid", b))
|
||||
} else {
|
||||
self.examined.set(b as usize, true);
|
||||
let info = self.gather_info(b)?;
|
||||
Ok(self.infos.entry(b).or_insert(info))
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_infos(&mut self) -> Result<()> {
|
||||
for b in 0..self.nr_blocks {
|
||||
let _ret = self.get_info(b);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gather_roots(&self) -> Result<(Vec<u64>, Vec<u64>)> {
|
||||
let mut dev_roots = Vec::<u64>::new();
|
||||
let mut details_roots = Vec::<u64>::new();
|
||||
|
||||
for b in 0..self.nr_blocks {
|
||||
// skip non-root blocks
|
||||
if self.referenced.contains(b as usize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip blocks we're not interested in
|
||||
let info = self.read_info(b as u64);
|
||||
if info.is_err() {
|
||||
continue;
|
||||
}
|
||||
let info = info.unwrap();
|
||||
|
||||
match info {
|
||||
NodeInfo::Dev(_) => dev_roots.push(b),
|
||||
NodeInfo::Details(_) => details_roots.push(b),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
Ok((dev_roots, details_roots))
|
||||
}
|
||||
|
||||
fn find_roots_with_compatible_ids(
|
||||
&mut self,
|
||||
dev_roots: &[u64],
|
||||
details_roots: &[u64],
|
||||
) -> Result<Vec<(u64, u64)>> {
|
||||
let mut root_pairs = Vec::<(u64, u64)>::new();
|
||||
|
||||
for dev_root in dev_roots {
|
||||
let mut path = vec![0];
|
||||
let ids1 = btree_to_key_set::<u64>(&mut path, self.engine.clone(), true, *dev_root)?;
|
||||
|
||||
for details_root in details_roots {
|
||||
let mut path = vec![0];
|
||||
let ids2 = btree_to_key_set::<DeviceDetail>(
|
||||
&mut path,
|
||||
self.engine.clone(),
|
||||
true,
|
||||
*details_root,
|
||||
)?;
|
||||
|
||||
if ids1 != ids2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
root_pairs.push((*dev_root, *details_root));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(root_pairs)
|
||||
}
|
||||
|
||||
fn filter_roots_with_incompatible_mapped_blocks(
|
||||
&self,
|
||||
root_pairs: &[(u64, u64)],
|
||||
) -> Result<Vec<(u64, u64)>> {
|
||||
let mut filtered = Vec::<(u64, u64)>::new();
|
||||
|
||||
for (dev_root, details_root) in root_pairs {
|
||||
let dev_info;
|
||||
if let NodeInfo::Dev(i) = self.read_info(*dev_root)? {
|
||||
dev_info = i;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let details_info;
|
||||
if let NodeInfo::Details(i) = self.read_info(*details_root)? {
|
||||
details_info = i;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: compare the ages
|
||||
if dev_info.nr_devices != details_info.nr_devices
|
||||
|| dev_info.nr_mappings != details_info.nr_mappings
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
filtered.push((*dev_root, *details_root));
|
||||
}
|
||||
|
||||
Ok(filtered)
|
||||
}
|
||||
|
||||
fn compare_time_counts(lhs: &BTreeMap<u32, u32>, rhs: &BTreeMap<u32, u32>) -> Ordering {
|
||||
let mut lhs_it = lhs.iter().rev();
|
||||
let mut rhs_it = rhs.iter().rev();
|
||||
let mut lhs_end = false;
|
||||
let mut rhs_end = false;
|
||||
|
||||
while !lhs_end && !rhs_end {
|
||||
if let Some((lhs_time, lhs_count)) = lhs_it.next() {
|
||||
if let Some((rhs_time, rhs_count)) = rhs_it.next() {
|
||||
if lhs_time > rhs_time {
|
||||
return Ordering::Less;
|
||||
} else if rhs_time > lhs_time {
|
||||
return Ordering::Greater;
|
||||
} else if lhs_count > rhs_count {
|
||||
return Ordering::Less;
|
||||
} else if rhs_count > lhs_count {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
} else {
|
||||
rhs_end = true;
|
||||
}
|
||||
} else {
|
||||
lhs_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
if lhs_end && !rhs_end {
|
||||
Ordering::Less
|
||||
} else if !lhs_end && rhs_end {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_roots(lhs: &(&DevInfo, u64), rhs: &(&DevInfo, u64)) -> Ordering {
|
||||
// TODO: sort by other criteria?
|
||||
Self::compare_time_counts(&lhs.0.time_counts, &rhs.0.time_counts)
|
||||
}
|
||||
|
||||
fn sort_roots(&self, root_pairs: &[(u64, u64)]) -> Result<Vec<(u64, u64)>> {
|
||||
let mut infos = Vec::<(&DevInfo, u64)>::new();
|
||||
|
||||
for (dev_root, details_root) in root_pairs {
|
||||
let dev_info;
|
||||
if let NodeInfo::Dev(i) = self.read_info(*dev_root)? {
|
||||
dev_info = i;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
infos.push((dev_info, *details_root));
|
||||
}
|
||||
|
||||
infos.sort_by(Self::compare_roots);
|
||||
|
||||
let mut sorted = Vec::<(u64, u64)>::new();
|
||||
for (dev_info, details_root) in infos {
|
||||
sorted.push((dev_info.b, details_root));
|
||||
}
|
||||
|
||||
Ok(sorted)
|
||||
}
|
||||
|
||||
fn find_root_pairs(
|
||||
&mut self,
|
||||
dev_roots: &[u64],
|
||||
details_roots: &[u64],
|
||||
) -> Result<Vec<(u64, u64)>> {
|
||||
let pairs = self.find_roots_with_compatible_ids(dev_roots, details_roots)?;
|
||||
let pairs = self.filter_roots_with_incompatible_mapped_blocks(&pairs)?;
|
||||
self.sort_roots(&pairs)
|
||||
}
|
||||
|
||||
fn to_found_roots(&self, dev_root: u64, details_root: u64) -> Result<FoundRoots> {
|
||||
let dev_info;
|
||||
if let NodeInfo::Dev(i) = self.read_info(dev_root)? {
|
||||
dev_info = i;
|
||||
} else {
|
||||
return Err(anyhow!("not a top-level root"));
|
||||
}
|
||||
|
||||
let details_info;
|
||||
if let NodeInfo::Details(i) = self.read_info(details_root)? {
|
||||
details_info = i;
|
||||
} else {
|
||||
return Err(anyhow!("not a details root"));
|
||||
}
|
||||
|
||||
Ok(FoundRoots {
|
||||
mapping_root: dev_root,
|
||||
details_root,
|
||||
time: std::cmp::max(dev_info.age, details_info.age),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_roots(mut self) -> Result<FoundRoots> {
|
||||
self.collect_infos()?;
|
||||
let (dev_roots, details_roots) = self.gather_roots()?;
|
||||
let pairs = self.find_root_pairs(&dev_roots, &details_roots)?;
|
||||
|
||||
if pairs.is_empty() {
|
||||
return Err(anyhow!("no compatible roots found"));
|
||||
}
|
||||
|
||||
self.to_found_roots(pairs[0].0, pairs[0].1)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
pub fn is_superblock_consistent(
|
||||
sb: Superblock,
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
) -> Result<Superblock> {
|
||||
let mut path = vec![0];
|
||||
let ids1 = btree_to_key_set::<u64>(&mut path, engine.clone(), true, sb.mapping_root)?;
|
||||
|
||||
path = vec![0];
|
||||
let ids2 = btree_to_key_set::<DeviceDetail>(&mut path, engine.clone(), true, sb.details_root)?;
|
||||
|
||||
if ids1 != ids2 {
|
||||
return Err(anyhow!("inconsistent device ids"));
|
||||
}
|
||||
|
||||
Ok(sb)
|
||||
}
|
||||
|
||||
pub fn rebuild_superblock(
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
opts: &SuperblockOverrides,
|
||||
) -> Result<Superblock> {
|
||||
// Check parameters
|
||||
let nr_data_blocks = opts.nr_data_blocks.ok_or_else(|| anyhow!(""))?;
|
||||
let transaction_id = opts.transaction_id.ok_or_else(|| anyhow!(""))?;
|
||||
let data_block_size = opts.data_block_size.ok_or_else(|| anyhow!(""))?;
|
||||
|
||||
let c = NodeCollector::new(engine.clone());
|
||||
let roots = c.find_roots()?;
|
||||
let sm_root = SMRoot {
|
||||
nr_blocks: nr_data_blocks,
|
||||
nr_allocated: 0,
|
||||
bitmap_root: 0,
|
||||
ref_count_root: 0,
|
||||
};
|
||||
let data_sm_root = pack_root(&sm_root, SPACE_MAP_ROOT_SIZE)?;
|
||||
|
||||
Ok(Superblock {
|
||||
flags: SuperblockFlags { needs_check: false },
|
||||
block: SUPERBLOCK_LOCATION,
|
||||
version: 2,
|
||||
time: roots.time,
|
||||
transaction_id,
|
||||
metadata_snap: 0,
|
||||
data_sm_root,
|
||||
metadata_sm_root: vec![0u8; SPACE_MAP_ROOT_SIZE],
|
||||
mapping_root: roots.mapping_root,
|
||||
details_root: roots.details_root,
|
||||
data_block_size,
|
||||
nr_metadata_blocks: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_or_rebuild_superblock(
|
||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||
loc: u64,
|
||||
opts: &SuperblockOverrides,
|
||||
) -> Result<Superblock> {
|
||||
read_superblock(engine.as_ref(), loc)
|
||||
.and_then(|sb| is_superblock_consistent(sb, engine.clone()))
|
||||
.or_else(|_| rebuild_superblock(engine, &opts))
|
||||
}
|
||||
|
||||
//------------------------------------------
|
@ -4,6 +4,7 @@ pub mod device_detail;
|
||||
pub mod dump;
|
||||
pub mod ir;
|
||||
pub mod metadata;
|
||||
pub mod metadata_repair;
|
||||
pub mod repair;
|
||||
pub mod restore;
|
||||
pub mod runs;
|
||||
|
Loading…
Reference in New Issue
Block a user