[thin_restore] first pass at btree_builder.
No tests yet
This commit is contained in:
parent
f60ae770c2
commit
37ea0280df
@ -301,7 +301,7 @@ impl Adjacent for BlockTime {
|
||||
}
|
||||
|
||||
impl Adjacent for DeviceDetail {
|
||||
fn adjacent(&self, rhs: &Self) -> bool {
|
||||
fn adjacent(&self, _rhs: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -561,7 +561,7 @@ impl Panel for DeviceDetailPanel {
|
||||
btree::Node::Internal { values, .. } => {
|
||||
Some(PushDeviceDetail(values[self.state.selected().unwrap()]))
|
||||
}
|
||||
btree::Node::Leaf { values, keys, .. } => None,
|
||||
btree::Node::Leaf { .. } => None,
|
||||
},
|
||||
Key::Char('h') | Key::Left => Some(PopPanel),
|
||||
_ => None,
|
||||
|
75
src/bin/thin_restore.rs
Normal file
75
src/bin/thin_restore.rs
Normal file
@ -0,0 +1,75 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
use thinp::file_utils;
|
||||
use thinp::report::*;
|
||||
use thinp::thin::restore::{restore, ThinRestoreOptions};
|
||||
|
||||
fn main() {
|
||||
let parser = App::new("thin_restore")
|
||||
.version(thinp::version::TOOLS_VERSION)
|
||||
.about("Convert XML format metadata to binary.")
|
||||
.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("INPUT")
|
||||
.help("Specify the input xml")
|
||||
.short("i")
|
||||
.long("input")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("OUTPUT")
|
||||
.help("Specify the output device to check")
|
||||
.short("o")
|
||||
.long("output")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("SYNC_IO")
|
||||
.help("Force use of synchronous io")
|
||||
.long("sync-io"),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if !file_utils::file_exists(input_file) {
|
||||
eprintln!("Couldn't find input file '{:?}'.", &input_file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let report;
|
||||
|
||||
if matches.is_present("QUIET") {
|
||||
report = std::sync::Arc::new(mk_quiet_report());
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
report = std::sync::Arc::new(mk_progress_bar_report());
|
||||
} else {
|
||||
report = Arc::new(mk_simple_report());
|
||||
}
|
||||
|
||||
let opts = ThinRestoreOptions {
|
||||
input: &input_file,
|
||||
output: &output_file,
|
||||
async_io: !matches.is_present("SYNC_IO"),
|
||||
report,
|
||||
};
|
||||
|
||||
if let Err(reason) = restore(opts) {
|
||||
println!("{}", reason);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
@ -461,6 +461,26 @@ impl<V: Unpack> Node<V> {
|
||||
Leaf { header, .. } => header,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mut_header(&mut self) -> &mut NodeHeader {
|
||||
use Node::*;
|
||||
match self {
|
||||
Internal { header, .. } => header,
|
||||
Leaf { header, .. } => header,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_keys(&self) -> &[u64] {
|
||||
use Node::*;
|
||||
match self {
|
||||
Internal { keys, .. } => &keys[0..],
|
||||
Leaf { keys, .. } => &keys[0..],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_block(&mut self, b: u64) {
|
||||
self.get_mut_header().block = b;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_result<'a, V>(path: &Vec<u64>, r: IResult<&'a [u8], V>) -> Result<(&'a [u8], V)> {
|
||||
|
364
src/pdata/btree_builder.rs
Normal file
364
src/pdata/btree_builder.rs
Normal file
@ -0,0 +1,364 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Cursor;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::checksum;
|
||||
use crate::io_engine::*;
|
||||
use crate::pdata::btree::*;
|
||||
use crate::pdata::space_map::*;
|
||||
use crate::pdata::unpack::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
fn pack_node<W: WriteBytesExt, V: Pack + Unpack>(node: &Node<V>, w: &mut W) -> Result<()> {
|
||||
match node {
|
||||
Node::Internal {
|
||||
header,
|
||||
keys,
|
||||
values,
|
||||
} => {
|
||||
header.pack(w)?;
|
||||
for k in keys {
|
||||
w.write_u64::<LittleEndian>(*k)?;
|
||||
}
|
||||
|
||||
// pad with zeroes
|
||||
for _i in keys.len()..header.max_entries as usize {
|
||||
w.write_u64::<LittleEndian>(0)?;
|
||||
}
|
||||
|
||||
for v in values {
|
||||
v.pack(w)?;
|
||||
}
|
||||
}
|
||||
Node::Leaf {
|
||||
header,
|
||||
keys,
|
||||
values,
|
||||
} => {
|
||||
header.pack(w)?;
|
||||
for k in keys {
|
||||
w.write_u64::<LittleEndian>(*k)?;
|
||||
}
|
||||
|
||||
// pad with zeroes
|
||||
for _i in keys.len()..header.max_entries as usize {
|
||||
w.write_u64::<LittleEndian>(0)?;
|
||||
}
|
||||
|
||||
for v in values {
|
||||
v.pack(w)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
fn calc_max_entries<V: Unpack>() -> usize {
|
||||
let elt_size = 8 + V::disk_size() as usize;
|
||||
((BLOCK_SIZE - NodeHeader::disk_size() as usize) / elt_size) as usize
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
struct Entries<V> {
|
||||
max_entries: usize,
|
||||
entries: VecDeque<(u64, V)>,
|
||||
}
|
||||
|
||||
enum Action<V> {
|
||||
Noop,
|
||||
WriteSingle {
|
||||
keys: Vec<u64>,
|
||||
values: Vec<V>,
|
||||
},
|
||||
WritePair {
|
||||
keys1: Vec<u64>,
|
||||
values1: Vec<V>,
|
||||
keys2: Vec<u64>,
|
||||
values2: Vec<V>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<V> Entries<V> {
|
||||
pub fn new(max_entries: usize) -> Entries<V> {
|
||||
Entries {
|
||||
max_entries,
|
||||
entries: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entry(&mut self, k: u64, v: V) -> Action<V> {
|
||||
let result = if self.full() {
|
||||
let (keys, values) = self.pop(self.max_entries);
|
||||
Action::WriteSingle { keys, values }
|
||||
} else {
|
||||
Action::Noop
|
||||
};
|
||||
|
||||
self.entries.push_back((k, v));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn complete(&mut self) -> Action<V> {
|
||||
let n = self.entries.len();
|
||||
|
||||
if n >= self.max_entries {
|
||||
let n1 = n / 2;
|
||||
let n2 = n - n1;
|
||||
let (keys1, values1) = self.pop(n1);
|
||||
let (keys2, values2) = self.pop(n2);
|
||||
|
||||
Action::WritePair {
|
||||
keys1,
|
||||
values1,
|
||||
keys2,
|
||||
values2,
|
||||
}
|
||||
} else if n > 0 {
|
||||
let (keys, values) = self.pop(n);
|
||||
Action::WriteSingle { keys, values }
|
||||
} else {
|
||||
Action::Noop
|
||||
}
|
||||
}
|
||||
|
||||
fn full(&self) -> bool {
|
||||
self.entries.len() >= 2 * self.max_entries
|
||||
}
|
||||
|
||||
fn pop(&mut self, count: usize) -> (Vec<u64>, Vec<V>) {
|
||||
let mut keys = Vec::new();
|
||||
let mut values = Vec::new();
|
||||
|
||||
for _i in 0..count {
|
||||
let (k, v) = self.entries.pop_front().unwrap();
|
||||
keys.push(k);
|
||||
values.push(v);
|
||||
}
|
||||
|
||||
(keys, values)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
struct WriteBatcher {
|
||||
engine: Arc<Box<dyn IoEngine>>,
|
||||
sm: Arc<Mutex<dyn SpaceMap>>,
|
||||
|
||||
batch_size: usize,
|
||||
queue: Vec<Block>,
|
||||
}
|
||||
|
||||
impl WriteBatcher {
|
||||
fn new(
|
||||
engine: Arc<Box<dyn IoEngine>>,
|
||||
sm: Arc<Mutex<dyn SpaceMap>>,
|
||||
batch_size: usize,
|
||||
) -> WriteBatcher {
|
||||
WriteBatcher {
|
||||
engine,
|
||||
sm,
|
||||
batch_size,
|
||||
queue: Vec::with_capacity(batch_size),
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc(&mut self) -> Result<u64> {
|
||||
let mut sm = self.sm.lock().unwrap();
|
||||
let b = sm.alloc()?;
|
||||
|
||||
if b.is_none() {
|
||||
return Err(anyhow!("out of metadata space"));
|
||||
}
|
||||
|
||||
Ok(b.unwrap())
|
||||
}
|
||||
|
||||
fn write(&mut self, b: Block) -> Result<()> {
|
||||
checksum::write_checksum(&mut b.get_data(), checksum::BT::NODE)?;
|
||||
|
||||
if self.queue.len() == self.batch_size {
|
||||
self.flush()?;
|
||||
}
|
||||
|
||||
self.queue.push(b);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.engine.write_many(&self.queue)?;
|
||||
self.queue.clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
fn write_node_<V: Unpack + Pack>(w: &mut WriteBatcher, mut node: Node<V>) -> Result<(u64, u64)> {
|
||||
let keys = node.get_keys();
|
||||
let first_key = keys.first().unwrap_or(&0u64).clone();
|
||||
|
||||
let loc = w.alloc()?;
|
||||
node.set_block(loc);
|
||||
|
||||
let b = Block::new(loc);
|
||||
let mut cursor = Cursor::new(b.get_data());
|
||||
pack_node(&node, &mut cursor)?;
|
||||
w.write(b)?;
|
||||
|
||||
Ok((first_key, loc))
|
||||
}
|
||||
|
||||
fn write_leaf<V: Unpack + Pack>(
|
||||
w: &mut WriteBatcher,
|
||||
keys: Vec<u64>,
|
||||
values: Vec<V>,
|
||||
) -> Result<(u64, u64)> {
|
||||
let header = NodeHeader {
|
||||
block: 0,
|
||||
is_leaf: true,
|
||||
nr_entries: keys.len() as u32,
|
||||
max_entries: calc_max_entries::<V>() as u32,
|
||||
value_size: V::disk_size(),
|
||||
};
|
||||
|
||||
let node = Node::Leaf {
|
||||
header,
|
||||
keys,
|
||||
values,
|
||||
};
|
||||
|
||||
write_node_(w, node)
|
||||
}
|
||||
|
||||
fn write_internal(w: &mut WriteBatcher, keys: Vec<u64>, values: Vec<u64>) -> Result<(u64, u64)> {
|
||||
let header = NodeHeader {
|
||||
block: 0,
|
||||
is_leaf: false,
|
||||
nr_entries: keys.len() as u32,
|
||||
max_entries: calc_max_entries::<u64>() as u32,
|
||||
value_size: u64::disk_size(),
|
||||
};
|
||||
|
||||
let node: Node<u64> = Node::Internal {
|
||||
header,
|
||||
keys,
|
||||
values,
|
||||
};
|
||||
|
||||
write_node_(w, node)
|
||||
}
|
||||
|
||||
pub struct Builder<V: Unpack + Pack> {
|
||||
w: WriteBatcher,
|
||||
entries: Entries<V>,
|
||||
|
||||
max_internal_entries: usize,
|
||||
internal_entries: Vec<Entries<u64>>,
|
||||
|
||||
root: u64,
|
||||
}
|
||||
|
||||
impl<V: Unpack + Pack> Builder<V> {
|
||||
pub fn new(engine: Arc<Box<dyn IoEngine>>, sm: Arc<Mutex<dyn SpaceMap>>) -> Builder<V> {
|
||||
let max_entries = calc_max_entries::<V>();
|
||||
let max_internal_entries = calc_max_entries::<u64>();
|
||||
|
||||
Builder {
|
||||
w: WriteBatcher::new(engine, sm, 256),
|
||||
entries: Entries::new(max_entries),
|
||||
max_internal_entries,
|
||||
internal_entries: Vec::new(),
|
||||
root: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entry(&mut self, k: u64, v: V) -> Result<()> {
|
||||
let action = self.entries.add_entry(k, v);
|
||||
self.perform_action(action)
|
||||
}
|
||||
|
||||
pub fn complete(mut self) -> Result<u64> {
|
||||
let action = self.entries.complete();
|
||||
self.perform_action(action)?;
|
||||
self.w.flush()?;
|
||||
Ok(self.root)
|
||||
}
|
||||
|
||||
//--------------------
|
||||
|
||||
fn add_internal_entry(&mut self, level: usize, k: u64, v: u64) -> Result<()> {
|
||||
if self.internal_entries.len() == level {
|
||||
self.internal_entries.push(Entries::new(self.max_internal_entries));
|
||||
}
|
||||
|
||||
let action = self.internal_entries[level].add_entry(k, v);
|
||||
self.perform_internal_action(level, action)
|
||||
}
|
||||
|
||||
fn perform_internal_action(&mut self, level: usize, action: Action<u64>) -> Result<()> {
|
||||
match action {
|
||||
Action::Noop => {}
|
||||
Action::WriteSingle { keys, values } => {
|
||||
let (k, loc) = write_internal(&mut self.w, keys, values)?;
|
||||
self.add_internal_entry(level + 1, k, loc)?;
|
||||
self.root = loc;
|
||||
}
|
||||
Action::WritePair {
|
||||
keys1,
|
||||
values1,
|
||||
keys2,
|
||||
values2,
|
||||
} => {
|
||||
let (k, loc) = write_leaf(&mut self.w, keys1, values1)?;
|
||||
self.add_internal_entry(level + 1, k, loc)?;
|
||||
|
||||
let (k, loc) = write_leaf(&mut self.w, keys2, values2)?;
|
||||
self.add_internal_entry(level + 1, k, loc)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn perform_action<V2: Unpack + Pack>(&mut self, action: Action<V2>) -> Result<()> {
|
||||
match action {
|
||||
Action::Noop => {}
|
||||
Action::WriteSingle { keys, values } => {
|
||||
let (k, loc) = write_leaf(&mut self.w, keys, values)?;
|
||||
self.add_internal_entry(0, k, loc)?;
|
||||
}
|
||||
Action::WritePair {
|
||||
keys1,
|
||||
values1,
|
||||
keys2,
|
||||
values2,
|
||||
} => {
|
||||
let (k, loc) = write_leaf(&mut self.w, keys1, values1)?;
|
||||
self.add_internal_entry(0, k, loc)?;
|
||||
|
||||
let (k, loc) = write_leaf(&mut self.w, keys2, values2)?;
|
||||
self.add_internal_entry(0, k, loc)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn fail() {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
//------------------------------------------
|
@ -1,4 +1,5 @@
|
||||
pub mod btree;
|
||||
pub mod btree_builder;
|
||||
pub mod space_map;
|
||||
pub mod unpack;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use fixedbitset::FixedBitSet;
|
||||
use nom::{multi::count, number::complete::*, IResult};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
|
||||
use crate::io_engine::*;
|
||||
use crate::pdata::unpack::{Pack, Unpack};
|
||||
@ -96,7 +96,7 @@ impl Unpack for MetadataIndex {
|
||||
let (i, _blocknr) = le_u64(i)?;
|
||||
let (i, indexes) = count(IndexEntry::unpack, MAX_METADATA_BITMAPS)(i)?;
|
||||
|
||||
Ok((i, MetadataIndex {indexes}))
|
||||
Ok((i, MetadataIndex { indexes }))
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,17 +195,16 @@ impl Pack for Bitmap {
|
||||
for e in chunk {
|
||||
w >>= 2;
|
||||
match e {
|
||||
Small(0) => {
|
||||
},
|
||||
Small(0) => {}
|
||||
Small(1) => {
|
||||
w |= 0x2 << 62;
|
||||
},
|
||||
}
|
||||
Small(2) => {
|
||||
w |= 0x1 << 62;
|
||||
},
|
||||
}
|
||||
Small(_) => {
|
||||
return Err(anyhow!("Bad small value in bitmap entry"));
|
||||
},
|
||||
}
|
||||
Overflow => {
|
||||
w |= 0x3 << 62;
|
||||
}
|
||||
@ -228,8 +227,12 @@ pub trait SpaceMap {
|
||||
|
||||
// Returns the old ref count
|
||||
fn set(&mut self, b: u64, v: u32) -> Result<u32>;
|
||||
|
||||
|
||||
fn inc(&mut self, begin: u64, len: u64) -> Result<()>;
|
||||
|
||||
// Finds a block with a zero reference count. Increments the
|
||||
// count.
|
||||
fn alloc(&mut self) -> Result<Option<u64>>;
|
||||
}
|
||||
|
||||
pub type ASpaceMap = Arc<Mutex<dyn SpaceMap + Sync + Send>>;
|
||||
@ -238,6 +241,7 @@ pub type ASpaceMap = Arc<Mutex<dyn SpaceMap + Sync + Send>>;
|
||||
|
||||
pub struct CoreSpaceMap<T> {
|
||||
nr_allocated: u64,
|
||||
first_free: u64,
|
||||
counts: Vec<T>,
|
||||
}
|
||||
|
||||
@ -248,6 +252,7 @@ where
|
||||
pub fn new(nr_entries: u64) -> CoreSpaceMap<V> {
|
||||
CoreSpaceMap {
|
||||
nr_allocated: 0,
|
||||
first_free: 0,
|
||||
counts: vec![V::default(); nr_entries as usize],
|
||||
}
|
||||
}
|
||||
@ -271,13 +276,16 @@ where
|
||||
|
||||
fn set(&mut self, b: u64, v: u32) -> Result<u32> {
|
||||
let old = self.counts[b as usize];
|
||||
assert!(v < 0xff); // FIXME: we can't assume this
|
||||
assert!(v < 0xff); // FIXME: we can't assume this
|
||||
self.counts[b as usize] = V::from(v as u8);
|
||||
|
||||
if old == V::from(0u8) && v != 0 {
|
||||
self.nr_allocated += 1;
|
||||
} else if old != V::from(0u8) && v == 0 {
|
||||
self.nr_allocated -= 1;
|
||||
if b < self.first_free {
|
||||
self.first_free = b;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(old.into())
|
||||
@ -295,6 +303,19 @@ where
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloc(&mut self) -> Result<Option<u64>> {
|
||||
for b in self.first_free..(self.counts.len() as u64) {
|
||||
if self.counts[b as usize] == V::from(0u8) {
|
||||
self.counts[b as usize] = V::from(1u8);
|
||||
self.first_free = b + 1;
|
||||
return Ok(Some(b));
|
||||
}
|
||||
}
|
||||
|
||||
self.first_free = self.counts.len() as u64;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn core_sm(nr_entries: u64, max_count: u32) -> Arc<Mutex<dyn SpaceMap + Send + Sync>> {
|
||||
@ -314,6 +335,7 @@ pub fn core_sm(nr_entries: u64, max_count: u32) -> Arc<Mutex<dyn SpaceMap + Send
|
||||
// aren't interested in counting how many times we've visited.
|
||||
pub struct RestrictedSpaceMap {
|
||||
nr_allocated: u64,
|
||||
first_free: usize,
|
||||
counts: FixedBitSet,
|
||||
}
|
||||
|
||||
@ -322,6 +344,7 @@ impl RestrictedSpaceMap {
|
||||
RestrictedSpaceMap {
|
||||
nr_allocated: 0,
|
||||
counts: FixedBitSet::with_capacity(nr_entries as usize),
|
||||
first_free: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,11 +377,14 @@ impl SpaceMap for RestrictedSpaceMap {
|
||||
} else {
|
||||
if old {
|
||||
self.nr_allocated -= 1;
|
||||
if b < self.first_free as u64 {
|
||||
self.first_free = b as usize;
|
||||
}
|
||||
}
|
||||
self.counts.set(b as usize, false);
|
||||
}
|
||||
|
||||
Ok(if old {1} else {0})
|
||||
Ok(if old { 1 } else { 0 })
|
||||
}
|
||||
|
||||
fn inc(&mut self, begin: u64, len: u64) -> Result<()> {
|
||||
@ -370,6 +396,19 @@ impl SpaceMap for RestrictedSpaceMap {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloc(&mut self) -> Result<Option<u64>> {
|
||||
for b in self.first_free..self.counts.len() {
|
||||
if !self.counts.contains(b) {
|
||||
self.counts.insert(b);
|
||||
self.first_free = b + 1;
|
||||
return Ok(Some(b as u64));
|
||||
}
|
||||
}
|
||||
|
||||
self.first_free = self.counts.len();
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
@ -19,7 +19,7 @@ impl fmt::Display for DeviceDetail {
|
||||
self.mapped_blocks,
|
||||
self.transaction_id,
|
||||
self.creation_time,
|
||||
self.snapshotted_time);
|
||||
self.snapshotted_time)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,5 @@ pub mod device_detail;
|
||||
pub mod superblock;
|
||||
pub mod check;
|
||||
pub mod dump;
|
||||
pub mod restore;
|
||||
pub mod xml;
|
||||
|
31
src/thin/restore.rs
Normal file
31
src/thin/restore.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use anyhow::Result;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
|
||||
use crate::pdata::btree::{self, *};
|
||||
use crate::pdata::space_map::*;
|
||||
use crate::pdata::unpack::*;
|
||||
use crate::report::*;
|
||||
use crate::thin::block_time::*;
|
||||
use crate::thin::device_detail::*;
|
||||
use crate::thin::superblock::*;
|
||||
use crate::thin::xml::{self, MetadataVisitor};
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
pub struct ThinRestoreOptions<'a> {
|
||||
pub input: &'a Path,
|
||||
pub output: &'a Path,
|
||||
pub async_io: bool,
|
||||
pub report: Arc<Report>,
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
pub fn restore(opts: ThinRestoreOptions) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
//------------------------------------------
|
Loading…
Reference in New Issue
Block a user