[thin_explore] Explore devices tree, including path support.
This commit is contained in:
parent
c42b623e39
commit
f60ae770c2
@ -18,12 +18,12 @@ use termion::input::TermRead;
|
|||||||
use termion::raw::IntoRawMode;
|
use termion::raw::IntoRawMode;
|
||||||
|
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::{TermionBackend},
|
backend::TermionBackend,
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
terminal::Frame,
|
terminal::Frame,
|
||||||
text::{Span},
|
text::Span,
|
||||||
widgets::{Block, Borders, List, ListItem, ListState, Row, StatefulWidget, Table, Widget},
|
widgets::{Block, Borders, List, ListItem, ListState, Row, StatefulWidget, Table, Widget},
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
@ -32,6 +32,7 @@ use thinp::io_engine::*;
|
|||||||
use thinp::pdata::btree;
|
use thinp::pdata::btree;
|
||||||
use thinp::pdata::unpack::*;
|
use thinp::pdata::unpack::*;
|
||||||
use thinp::thin::block_time::*;
|
use thinp::thin::block_time::*;
|
||||||
|
use thinp::thin::device_detail::*;
|
||||||
use thinp::thin::superblock::*;
|
use thinp::thin::superblock::*;
|
||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
@ -141,13 +142,20 @@ fn ls_previous(ls: &mut ListState) {
|
|||||||
|
|
||||||
//------------------------------------
|
//------------------------------------
|
||||||
|
|
||||||
struct SBWidget {
|
struct SBWidget<'a> {
|
||||||
sb: Superblock,
|
sb: &'a Superblock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for SBWidget {
|
impl<'a> StatefulWidget for SBWidget<'a> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
type State = ListState;
|
||||||
let sb = &self.sb;
|
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut ListState) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Min(10), Constraint::Percentage(80)].as_ref())
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
let sb = self.sb;
|
||||||
let flags = ["flags".to_string(), format!("{}", sb.flags)];
|
let flags = ["flags".to_string(), format!("{}", sb.flags)];
|
||||||
let block = ["block".to_string(), format!("{}", sb.block)];
|
let block = ["block".to_string(), format!("{}", sb.block)];
|
||||||
let uuid = ["uuid".to_string(), format!("-")];
|
let uuid = ["uuid".to_string(), format!("-")];
|
||||||
@ -198,7 +206,22 @@ impl Widget for SBWidget {
|
|||||||
.style(Style::default().fg(Color::White))
|
.style(Style::default().fg(Color::White))
|
||||||
.column_spacing(1);
|
.column_spacing(1);
|
||||||
|
|
||||||
Widget::render(table, area, buf);
|
Widget::render(table, chunks[0], buf);
|
||||||
|
|
||||||
|
let items = vec![
|
||||||
|
ListItem::new(Span::raw(format!("Device tree"))),
|
||||||
|
ListItem::new(Span::raw(format!("Mapping tree")))
|
||||||
|
];
|
||||||
|
|
||||||
|
let items = List::new(items)
|
||||||
|
.block(Block::default().borders(Borders::ALL).title("Entries"))
|
||||||
|
.highlight_style(
|
||||||
|
Style::default()
|
||||||
|
.bg(Color::LightGreen)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
);
|
||||||
|
|
||||||
|
StatefulWidget::render(items, chunks[1], buf, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,13 +268,6 @@ impl<'a> Widget for HeaderWidget<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
fn read_node_header(engine: &dyn IoEngine, loc: u64) -> Result<btree::NodeHeader> {
|
|
||||||
let b = engine.read(loc)?;
|
|
||||||
unpack(&b.get_data()).map_err(|_| anyhow!("couldn't unpack btree header"))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fn read_node<V: Unpack>(engine: &dyn IoEngine, loc: u64) -> Result<btree::Node<V>> {
|
fn read_node<V: Unpack>(engine: &dyn IoEngine, loc: u64) -> Result<btree::Node<V>> {
|
||||||
let b = engine.read(loc)?;
|
let b = engine.read(loc)?;
|
||||||
let path = Vec::new();
|
let path = Vec::new();
|
||||||
@ -284,6 +300,12 @@ impl Adjacent for BlockTime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Adjacent for DeviceDetail {
|
||||||
|
fn adjacent(&self, rhs: &Self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<X: Adjacent, Y: Adjacent> Adjacent for (X, Y) {
|
impl<X: Adjacent, Y: Adjacent> Adjacent for (X, Y) {
|
||||||
fn adjacent(&self, rhs: &Self) -> bool {
|
fn adjacent(&self, rhs: &Self) -> bool {
|
||||||
self.0.adjacent(&rhs.0) && self.1.adjacent(&rhs.1)
|
self.0.adjacent(&rhs.0) && self.1.adjacent(&rhs.1)
|
||||||
@ -328,6 +350,8 @@ fn mk_runs<V: Adjacent + Sized + Copy>(keys: &[u64], values: &[V]) -> Vec<((u64,
|
|||||||
adjacent_runs(pairs)
|
adjacent_runs(pairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
struct NodeWidget<'a, V: Unpack + Adjacent + Clone> {
|
struct NodeWidget<'a, V: Unpack + Adjacent + Clone> {
|
||||||
title: String,
|
title: String,
|
||||||
node: &'a btree::Node<V>,
|
node: &'a btree::Node<V>,
|
||||||
@ -420,6 +444,7 @@ impl<'a, V: Unpack + fmt::Display + Adjacent + Copy> StatefulWidget for NodeWidg
|
|||||||
//------------------------------------
|
//------------------------------------
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
|
PushDeviceDetail(u64),
|
||||||
PushTopLevel(u64),
|
PushTopLevel(u64),
|
||||||
PushBottomLevel(u32, u64),
|
PushBottomLevel(u32, u64),
|
||||||
PopPanel,
|
PopPanel,
|
||||||
@ -435,32 +460,133 @@ trait Panel {
|
|||||||
fn path_action(&mut self, child: u64) -> Option<Action>;
|
fn path_action(&mut self, child: u64) -> Option<Action>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
struct SBPanel {
|
struct SBPanel {
|
||||||
sb: Superblock,
|
sb: Superblock,
|
||||||
|
state: ListState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SBPanel {
|
||||||
|
fn new(sb: Superblock) -> SBPanel {
|
||||||
|
let mut state = ListState::default();
|
||||||
|
state.select(Some(0));
|
||||||
|
|
||||||
|
SBPanel { sb, state }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Panel for SBPanel {
|
impl Panel for SBPanel {
|
||||||
fn render(&mut self, area: Rect, f: &mut Frame_) {
|
fn render(&mut self, area: Rect, f: &mut Frame_) {
|
||||||
// FIXME: get rid of clone
|
let w = SBWidget { sb: &self.sb };
|
||||||
let w = SBWidget {
|
f.render_stateful_widget(w, area, &mut self.state);
|
||||||
sb: self.sb.clone(),
|
|
||||||
};
|
|
||||||
f.render_widget(w, area);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, _k: Key) -> Option<Action> {
|
fn input(&mut self, k: Key) -> Option<Action> {
|
||||||
None
|
match k {
|
||||||
|
Key::Char('j') | Key::Down => {
|
||||||
|
ls_next(&mut self.state, 2);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Key::Char('k') | Key::Up => {
|
||||||
|
ls_previous(&mut self.state);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Key::Char('l') | Key::Right => {
|
||||||
|
if self.state.selected().unwrap() == 0 {
|
||||||
|
Some(PushDeviceDetail(self.sb.details_root))
|
||||||
|
} else {
|
||||||
|
Some(PushTopLevel(self.sb.mapping_root))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Key::Char('h') | Key::Left => Some(PopPanel),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_action(&mut self, child: u64) -> Option<Action> {
|
fn path_action(&mut self, child: u64) -> Option<Action> {
|
||||||
if child == self.sb.mapping_root {
|
if child == self.sb.mapping_root {
|
||||||
Some(PushTopLevel(child))
|
Some(PushTopLevel(child))
|
||||||
|
} else if child == self.sb.details_root {
|
||||||
|
Some(PushDeviceDetail(child))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
|
struct DeviceDetailPanel {
|
||||||
|
node: btree::Node<DeviceDetail>,
|
||||||
|
nr_entries: usize,
|
||||||
|
state: ListState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceDetailPanel {
|
||||||
|
fn new(node: btree::Node<DeviceDetail>) -> DeviceDetailPanel {
|
||||||
|
let nr_entries = node.get_header().nr_entries as usize;
|
||||||
|
let mut state = ListState::default();
|
||||||
|
state.select(Some(0));
|
||||||
|
|
||||||
|
DeviceDetailPanel {
|
||||||
|
node,
|
||||||
|
nr_entries,
|
||||||
|
state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Panel for DeviceDetailPanel {
|
||||||
|
fn render(&mut self, area: Rect, f: &mut Frame_) {
|
||||||
|
let w = NodeWidget {
|
||||||
|
title: "Device Details".to_string(),
|
||||||
|
node: &self.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
f.render_stateful_widget(w, area, &mut self.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input(&mut self, k: Key) -> Option<Action> {
|
||||||
|
match k {
|
||||||
|
Key::Char('j') | Key::Down => {
|
||||||
|
ls_next(&mut self.state, self.nr_entries);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Key::Char('k') | Key::Up => {
|
||||||
|
ls_previous(&mut self.state);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Key::Char('l') | Key::Right => match &self.node {
|
||||||
|
btree::Node::Internal { values, .. } => {
|
||||||
|
Some(PushDeviceDetail(values[self.state.selected().unwrap()]))
|
||||||
|
}
|
||||||
|
btree::Node::Leaf { values, keys, .. } => None,
|
||||||
|
},
|
||||||
|
Key::Char('h') | Key::Left => Some(PopPanel),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_action(&mut self, child: u64) -> Option<Action> {
|
||||||
|
match &self.node {
|
||||||
|
btree::Node::Internal { values, .. } => {
|
||||||
|
for i in 0..values.len() {
|
||||||
|
if values[i] == child {
|
||||||
|
self.state.select(Some(i));
|
||||||
|
return Some(PushDeviceDetail(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
btree::Node::Leaf { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------
|
||||||
|
|
||||||
struct TopLevelPanel {
|
struct TopLevelPanel {
|
||||||
node: btree::Node<u64>,
|
node: btree::Node<u64>,
|
||||||
nr_entries: usize,
|
nr_entries: usize,
|
||||||
@ -485,7 +611,7 @@ impl Panel for TopLevelPanel {
|
|||||||
fn render(&mut self, area: Rect, f: &mut Frame_) {
|
fn render(&mut self, area: Rect, f: &mut Frame_) {
|
||||||
let w = NodeWidget {
|
let w = NodeWidget {
|
||||||
title: "Top Level".to_string(),
|
title: "Top Level".to_string(),
|
||||||
node: &self.node, // FIXME: get rid of clone
|
node: &self.node,
|
||||||
};
|
};
|
||||||
|
|
||||||
f.render_stateful_widget(w, area, &mut self.state);
|
f.render_stateful_widget(w, area, &mut self.state);
|
||||||
@ -622,6 +748,10 @@ fn perform_action(
|
|||||||
action: Action,
|
action: Action,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match action {
|
match action {
|
||||||
|
PushDeviceDetail(b) => {
|
||||||
|
let node = read_node::<DeviceDetail>(engine, b)?;
|
||||||
|
panels.push(Box::new(DeviceDetailPanel::new(node)));
|
||||||
|
}
|
||||||
PushTopLevel(b) => {
|
PushTopLevel(b) => {
|
||||||
let node = read_node::<u64>(engine, b)?;
|
let node = read_node::<u64>(engine, b)?;
|
||||||
panels.push(Box::new(TopLevelPanel::new(node)));
|
panels.push(Box::new(TopLevelPanel::new(node)));
|
||||||
@ -631,7 +761,7 @@ fn perform_action(
|
|||||||
panels.push(Box::new(BottomLevelPanel::new(thin_id, node)));
|
panels.push(Box::new(BottomLevelPanel::new(thin_id, node)));
|
||||||
}
|
}
|
||||||
PopPanel => {
|
PopPanel => {
|
||||||
if panels.len() > 2 {
|
if panels.len() > 1 {
|
||||||
panels.pop();
|
panels.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -640,21 +770,15 @@ fn perform_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn explore(path: &Path, node_path: Option<Vec<u64>>) -> Result<()> {
|
fn explore(path: &Path, node_path: Option<Vec<u64>>) -> Result<()> {
|
||||||
let stdout = io::stdout();
|
|
||||||
let mut stdout = stdout.lock().into_raw_mode()?;
|
|
||||||
write!(stdout, "{}", termion::clear::All)?;
|
|
||||||
|
|
||||||
let backend = TermionBackend::new(stdout);
|
|
||||||
let mut terminal = Terminal::new(backend)?;
|
|
||||||
|
|
||||||
let engine = SyncIoEngine::new(&path, 1, false)?;
|
let engine = SyncIoEngine::new(&path, 1, false)?;
|
||||||
|
|
||||||
let mut panels: Vec<Box<dyn Panel>> = Vec::new();
|
let mut panels: Vec<Box<dyn Panel>> = Vec::new();
|
||||||
|
|
||||||
if let Some(path) = node_path {
|
if let Some(path) = node_path {
|
||||||
|
eprintln!("using path: {:?}", path);
|
||||||
assert_eq!(path[0], 0);
|
assert_eq!(path[0], 0);
|
||||||
let sb = read_superblock(&engine, path[0])?;
|
let sb = read_superblock(&engine, path[0])?;
|
||||||
panels.push(Box::new(SBPanel { sb }));
|
panels.push(Box::new(SBPanel::new(sb)));
|
||||||
for b in &path[1..] {
|
for b in &path[1..] {
|
||||||
let action = panels.last_mut().unwrap().path_action(*b);
|
let action = panels.last_mut().unwrap().path_action(*b);
|
||||||
if let Some(action) = action {
|
if let Some(action) = action {
|
||||||
@ -665,14 +789,17 @@ fn explore(path: &Path, node_path: Option<Vec<u64>>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let sb = read_superblock(&engine, 0)?;
|
let sb = read_superblock(&engine, 0)?;
|
||||||
panels.push(Box::new(SBPanel { sb: sb.clone() }));
|
panels.push(Box::new(SBPanel::new(sb.clone())));
|
||||||
|
|
||||||
let node = read_node::<u64>(&engine, sb.mapping_root)?;
|
|
||||||
panels.push(Box::new(TopLevelPanel::new(node)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let events = Events::new();
|
let events = Events::new();
|
||||||
|
|
||||||
|
let stdout = io::stdout();
|
||||||
|
let mut stdout = stdout.lock().into_raw_mode()?;
|
||||||
|
write!(stdout, "{}", termion::clear::All)?;
|
||||||
|
let backend = TermionBackend::new(stdout);
|
||||||
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
'main: loop {
|
'main: loop {
|
||||||
let render_panels = |f: &mut Frame_| {
|
let render_panels = |f: &mut Frame_| {
|
||||||
let chunks = Layout::default()
|
let chunks = Layout::default()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use data_encoding::BASE64;
|
use data_encoding::BASE64;
|
||||||
use nom::{number::complete::*, IResult};
|
use nom::{number::complete::*, IResult};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -418,6 +418,27 @@ impl Unpack for NodeHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pack for NodeHeader {
|
||||||
|
fn pack<W: WriteBytesExt>(&self, w: &mut W) -> anyhow::Result<()> {
|
||||||
|
// csum needs to be calculated right for the whole metadata block.
|
||||||
|
w.write_u32::<LittleEndian>(0)?;
|
||||||
|
|
||||||
|
let flags;
|
||||||
|
if self.is_leaf {
|
||||||
|
flags = LEAF_NODE;
|
||||||
|
} else {
|
||||||
|
flags = INTERNAL_NODE;
|
||||||
|
}
|
||||||
|
w.write_u32::<LittleEndian>(flags)?;
|
||||||
|
w.write_u64::<LittleEndian>(self.block)?;
|
||||||
|
w.write_u32::<LittleEndian>(self.nr_entries)?;
|
||||||
|
w.write_u32::<LittleEndian>(self.max_entries)?;
|
||||||
|
w.write_u32::<LittleEndian>(self.value_size)?;
|
||||||
|
w.write_u32::<LittleEndian>(0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Node<V: Unpack> {
|
pub enum Node<V: Unpack> {
|
||||||
Internal {
|
Internal {
|
||||||
@ -1020,15 +1041,14 @@ impl<V: Unpack + Copy> NodeVisitor<V> for ValueCollector<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn btree_to_map<V: Unpack + Copy>(
|
pub fn btree_to_map<V: Unpack + Copy>(
|
||||||
_path: &mut Vec<u64>,
|
path: &mut Vec<u64>,
|
||||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
ignore_non_fatal: bool,
|
ignore_non_fatal: bool,
|
||||||
root: u64,
|
root: u64,
|
||||||
) -> Result<BTreeMap<u64, V>> {
|
) -> Result<BTreeMap<u64, V>> {
|
||||||
let walker = BTreeWalker::new(engine, ignore_non_fatal);
|
let walker = BTreeWalker::new(engine, ignore_non_fatal);
|
||||||
let visitor = ValueCollector::<V>::new();
|
let visitor = ValueCollector::<V>::new();
|
||||||
let mut path = Vec::new();
|
walker.walk(path, &visitor, root)?;
|
||||||
walker.walk(&mut path, &visitor, root)?;
|
|
||||||
Ok(visitor.values.into_inner().unwrap())
|
Ok(visitor.values.into_inner().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +467,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
|
|||||||
// Device details. We read this once to get the number of thin devices, and hence the
|
// Device details. We read this once to get the number of thin devices, and hence the
|
||||||
// maximum metadata ref count. Then create metadata space map, and reread to increment
|
// maximum metadata ref count. Then create metadata space map, and reread to increment
|
||||||
// the ref counts for that metadata.
|
// the ref counts for that metadata.
|
||||||
let devs = btree_to_map::<DeviceDetail>(&mut path, engine.clone(), false, sb.details_root)?;
|
let devs = btree_to_map::<DeviceDetail>(&mut path, engine.clone(), opts.ignore_non_fatal, sb.details_root)?;
|
||||||
let nr_devs = devs.len();
|
let nr_devs = devs.len();
|
||||||
let metadata_sm = core_sm(engine.get_nr_blocks(), nr_devs as u32);
|
let metadata_sm = core_sm(engine.get_nr_blocks(), nr_devs as u32);
|
||||||
inc_superblock(&metadata_sm)?;
|
inc_superblock(&metadata_sm)?;
|
||||||
@ -477,7 +477,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
|
|||||||
&mut path,
|
&mut path,
|
||||||
engine.clone(),
|
engine.clone(),
|
||||||
metadata_sm.clone(),
|
metadata_sm.clone(),
|
||||||
false,
|
opts.ignore_non_fatal,
|
||||||
sb.details_root,
|
sb.details_root,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -493,7 +493,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
|
|||||||
&mut path,
|
&mut path,
|
||||||
engine.clone(),
|
engine.clone(),
|
||||||
metadata_sm.clone(),
|
metadata_sm.clone(),
|
||||||
false,
|
opts.ignore_non_fatal,
|
||||||
sb.mapping_root,
|
sb.mapping_root,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -514,7 +514,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
|
|||||||
&mut path,
|
&mut path,
|
||||||
engine.clone(),
|
engine.clone(),
|
||||||
metadata_sm.clone(),
|
metadata_sm.clone(),
|
||||||
false,
|
opts.ignore_non_fatal,
|
||||||
root.bitmap_root,
|
root.bitmap_root,
|
||||||
)?;
|
)?;
|
||||||
let entries: Vec<IndexEntry> = entries.values().cloned().collect();
|
let entries: Vec<IndexEntry> = entries.values().cloned().collect();
|
||||||
@ -556,7 +556,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
|
|||||||
&mut path,
|
&mut path,
|
||||||
engine.clone(),
|
engine.clone(),
|
||||||
metadata_sm.clone(),
|
metadata_sm.clone(),
|
||||||
false,
|
opts.ignore_non_fatal,
|
||||||
root.ref_count_root,
|
root.ref_count_root,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
use crate::pdata::unpack::*;
|
use crate::pdata::unpack::*;
|
||||||
use nom::{number::complete::*, IResult};
|
use nom::{number::complete::*, IResult};
|
||||||
|
|
||||||
@ -11,6 +13,17 @@ pub struct DeviceDetail {
|
|||||||
pub snapshotted_time: u32,
|
pub snapshotted_time: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DeviceDetail {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "mapped = {}, trans = {}, create = {}, snap = {}",
|
||||||
|
self.mapped_blocks,
|
||||||
|
self.transaction_id,
|
||||||
|
self.creation_time,
|
||||||
|
self.snapshotted_time);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Unpack for DeviceDetail {
|
impl Unpack for DeviceDetail {
|
||||||
fn disk_size() -> u32 {
|
fn disk_size() -> u32 {
|
||||||
24
|
24
|
||||||
|
Loading…
Reference in New Issue
Block a user