/
githubmirror
/
thin-provisioning-tools
Обзор
Документация
Войти
/
githubmirror
/
thin-provisioning-tools
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v1.0.7
src/thin/dump.rs
379 строк
10 KB
Joe Thornber
[examples] Add a custom emitter examples
02 окт 2023, 14:53
02 окт 2023, 14:53
e5a1a7a
Код
Авторство
О чём код?
use anyhow::{anyhow, Context, Result}; use std::fs::File; use std::io::BufWriter; use std::io::Write; use std::path::Path; use std::str::FromStr; use std::sync::{Arc, Mutex}; use crate::checksum; use crate::commands::engine::*; use crate::dump_utils::*; use crate::io_engine::*; use crate::pdata::btree::{self, *}; use crate::pdata::btree_walker::*; use crate::pdata::space_map::common::*; use crate::pdata::unpack::*; use crate::report::*; use crate::thin::block_time::*; use crate::thin::human_readable_format::HumanReadableWriter; use crate::thin::ir::{self, MetadataVisitor}; use crate::thin::metadata::*; use crate::thin::metadata_repair::*; use crate::thin::superblock::*; use crate::thin::xml; //------------------------------------------ pub struct RunBuilder { run: Option<ir::Map>, } impl RunBuilder { pub fn new() -> RunBuilder { RunBuilder { run: None } } pub fn next(&mut self, thin_block: u64, data_block: u64, time: u32) -> Option<ir::Map> { use ir::Map; match self.run { None => { self.run = Some(ir::Map { thin_begin: thin_block, data_begin: data_block, time, len: 1, }); None } Some(ir::Map { thin_begin, data_begin, time: mtime, len, }) => { if thin_block == (thin_begin + len) && data_block == (data_begin + len) && mtime == time { self.run.as_mut().unwrap().len += 1; None } else { self.run.replace(Map { thin_begin: thin_block, data_begin: data_block, time, len: 1, }) } } } } pub fn complete(&mut self) -> Option<ir::Map> { self.run.take() } } impl Default for RunBuilder { fn default() -> Self { Self::new() } } //------------------------------------------ struct MVInner<'a> { md_out: &'a mut dyn MetadataVisitor, builder: RunBuilder, } struct MappingVisitor<'a> { inner: Mutex<MVInner<'a>>, } //------------------------------------------ impl<'a> MappingVisitor<'a> { fn new(md_out: &'a mut dyn MetadataVisitor) -> MappingVisitor<'a> { MappingVisitor { inner: Mutex::new(MVInner { md_out, builder: RunBuilder::new(), }), } } } impl<'a> NodeVisitor<BlockTime> for MappingVisitor<'a> { fn visit( &self, _path: &[u64], _kr: &KeyRange, _h: &NodeHeader, keys: &[u64], values: &[BlockTime], ) -> btree::Result<()> { let mut inner = self.inner.lock().unwrap(); for (k, v) in keys.iter().zip(values.iter()) { if let Some(run) = inner.builder.next(*k, v.block, v.time) { // FIXME: BTreeError should carry more information than a string // so the caller could identify the actual root cause, // e.g., a broken pipe error or something. inner .md_out .map(&run) .map_err(|e| btree::value_err(format!("{}", e)))?; } } Ok(()) } fn visit_again(&self, _path: &[u64], b: u64) -> btree::Result<()> { let mut inner = self.inner.lock().unwrap(); inner .md_out .ref_shared(&format!("{}", b)) .map_err(|e| btree::value_err(format!("{}", e)))?; Ok(()) } fn end_walk(&self) -> btree::Result<()> { let mut inner = self.inner.lock().unwrap(); if let Some(run) = inner.builder.complete() { inner .md_out .map(&run) .map_err(|e| btree::value_err(format!("{}", e)))?; } Ok(()) } } //------------------------------------------ #[derive(Clone)] pub enum OutputFormat { XML, HumanReadable, } impl FromStr for OutputFormat { type Err = anyhow::Error; fn from_str(s: &str) -> Result<Self, Self::Err> { match s { "xml" => Ok(OutputFormat::XML), "human_readable" => Ok(OutputFormat::HumanReadable), _ => Err(anyhow!("unknown format")), } } } pub struct ThinDumpOptions<'a> { pub input: &'a Path, pub output: Option<&'a Path>, pub engine_opts: EngineOptions, pub report: Arc<Report>, pub repair: bool, pub skip_mappings: bool, pub overrides: SuperblockOverrides, pub selected_devs: Option<Vec<u64>>, pub format: OutputFormat, } struct ThinDumpContext { report: Arc<Report>, engine: Arc<dyn IoEngine + Send + Sync>, } fn mk_context(opts: &ThinDumpOptions) -> Result<ThinDumpContext> { let engine = EngineBuilder::new(opts.input, &opts.engine_opts) .exclusive(!opts.engine_opts.use_metadata_snap) .build()?; Ok(ThinDumpContext { report: opts.report.clone(), engine, }) } //------------------------------------------ fn emit_leaf(v: &MappingVisitor, b: &Block) -> Result<()> { use Node::*; let path = Vec::new(); let kr = KeyRange::new(); let bt = checksum::metadata_block_type(b.get_data()); if bt != checksum::BT::NODE { return Err(anyhow!("checksum failed for node {}, {:?}", b.loc, bt)); } let node = unpack_node::<BlockTime>(&path, b.get_data(), true, true)?; match node { Internal { .. } => Err(anyhow!("block {} is not a leaf", b.loc)), Leaf { header, keys, values, } => v .visit(&path, &kr, &header, &keys, &values) .context(OutputError), } } fn read_for<T>(engine: Arc<dyn IoEngine>, blocks: &[u64], mut t: T) -> Result<()> where T: FnMut(Block) -> Result<()>, { for cs in blocks.chunks(engine.get_batch_size()) { for b in engine .read_many(cs) .map_err(|_e| anyhow!("read_many failed"))? { let blk = b.map_err(|_e| anyhow!("read of individual block failed"))?; t(blk)?; } } Ok(()) } fn emit_leaves( engine: Arc<dyn IoEngine>, out: &mut dyn MetadataVisitor, leaves: &[u64], ) -> Result<()> { let v = MappingVisitor::new(out); let proc = |b| { emit_leaf(&v, &b)?; Ok(()) }; read_for(engine, leaves, proc)?; v.end_walk().context(OutputError) } fn emit_entries( engine: Arc<dyn IoEngine>, out: &mut dyn MetadataVisitor, entries: &[Entry], ) -> Result<()> { let mut leaves = Vec::new(); for e in entries { match e { Entry::Leaf(b) => { leaves.push(*b); } Entry::Ref(id) => { if !leaves.is_empty() { emit_leaves(engine.clone(), out, &leaves[0..])?; leaves.clear(); } let str = format!("{}", id); out.ref_shared(&str).context(OutputError)?; } } } if !leaves.is_empty() { emit_leaves(engine, out, &leaves[0..])?; } Ok(()) } pub fn dump_metadata( engine: Arc<dyn IoEngine>, out: &mut dyn MetadataVisitor, sb: &Superblock, md: &Metadata, ) -> Result<()> { let data_root = unpack::<SMRoot>(&sb.data_sm_root[0..])?; let out_sb = ir::Superblock { uuid: "".to_string(), time: sb.time, transaction: sb.transaction_id, flags: if sb.flags.needs_check { Some(1) } else { None }, version: Some(sb.version), data_block_size: sb.data_block_size, nr_data_blocks: data_root.nr_blocks, metadata_snap: None, }; out.superblock_b(&out_sb).context(OutputError)?; for d in &md.defs { out.def_shared_b(&format!("{}", d.def_id)) .context(OutputError)?; emit_entries(engine.clone(), out, &d.map.entries)?; out.def_shared_e().context(OutputError)?; } for dev in &md.devs { let device = ir::Device { dev_id: dev.thin_id, mapped_blocks: dev.detail.mapped_blocks, transaction: dev.detail.transaction_id, creation_time: dev.detail.creation_time, snap_time: dev.detail.snapshotted_time, }; out.device_b(&device).context(OutputError)?; emit_entries(engine.clone(), out, &dev.map.entries)?; out.device_e().context(OutputError)?; } out.superblock_e().context(OutputError)?; out.eof().context(OutputError)?; Ok(()) } //------------------------------------------ pub fn dump_with_formatter(opts: ThinDumpOptions, mut out: Box<dyn MetadataVisitor>) -> Result<()> { let ctx = mk_context(&opts)?; let sb = if opts.repair { read_or_rebuild_superblock( ctx.engine.clone(), ctx.report.clone(), SUPERBLOCK_LOCATION, &opts.overrides, )? } else if opts.engine_opts.use_metadata_snap { read_superblock_snap(ctx.engine.as_ref())? } else { read_superblock(ctx.engine.as_ref(), SUPERBLOCK_LOCATION) .and_then(|sb| sb.overrides(&opts.overrides))? }; let md = if opts.skip_mappings { build_metadata_without_mappings(ctx.engine.clone(), &sb)? } else { let m = build_metadata_with_dev(ctx.engine.clone(), &sb, opts.selected_devs)?; optimise_metadata(m)? }; dump_metadata(ctx.engine, out.as_mut(), &sb, &md) } pub fn dump(opts: ThinDumpOptions) -> Result<()> { let writer: Box<dyn Write> = if opts.output.is_some() { let f = File::create(opts.output.unwrap()).context(OutputError)?; Box::new(BufWriter::new(f)) } else { Box::new(BufWriter::new(std::io::stdout())) }; let out: Box<dyn MetadataVisitor> = match opts.format { OutputFormat::XML => Box::new(xml::XmlWriter::new(writer)), OutputFormat::HumanReadable => Box::new(HumanReadableWriter::new(writer)), }; dump_with_formatter(opts, out) } //------------------------------------------