/
githubmirror
/
thin-provisioning-tools
Обзор
Документация
Войти
/
githubmirror
/
thin-provisioning-tools
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/thin/shrink.rs
326 строк
9 KB
Ming-Hung Tsai
[thin_shrink] Fix incorrect number of data blocks in output superblock
08 июл 2025, 12:21
08 июл 2025, 12:21
4a70a04
Код
Авторство
О чём код?
use anyhow::Result; use rangemap::RangeSet; use std::fs::File; use std::fs::OpenOptions; use std::io::{BufWriter, Read, SeekFrom}; use std::ops::Range; use std::os::unix::fs::OpenOptionsExt; use std::path::{Path, PathBuf}; use std::sync::mpsc; use std::sync::Arc; use std::vec::Vec; use crate::copier::batcher::CopyOpBatcher; use crate::copier::wrapper::ThreadedCopier; use crate::copier::*; use crate::io_engine::utils::VectoredBlockIo; use crate::io_engine::{IoEngine, SyncIoEngine, SECTOR_SHIFT}; use crate::pdata::space_map::metadata::core_metadata_sm; use crate::report::Report; use crate::shrink::toplevel::*; use crate::thin::dump::dump_metadata; use crate::thin::ir::{self, MetadataVisitor, Visit}; use crate::thin::metadata::*; use crate::thin::restore::Restorer; use crate::thin::superblock::{read_superblock, Superblock, SUPERBLOCK_LOCATION}; use crate::thin::xml; use crate::write_batcher::WriteBatcher; //--------------------------------------- fn copy_regions( data_dev: &Path, remaps: &[(BlockRange, u64)], block_size: usize, progress: Arc<dyn CopyProgress + Send + Sync>, ) -> Result<()> { let file = OpenOptions::new().read(true).write(true).open(data_dev)?; let vio: VectoredBlockIo<File> = file.into(); let buffer_size = std::cmp::max(block_size, 64 * 1024 * 1024); let copier = SyncCopier::in_file(buffer_size, block_size, vio)?; let (tx, rx) = mpsc::sync_channel::<Vec<CopyOp>>(1); let mut batcher = CopyOpBatcher::new(1_000_000, tx); let copier = ThreadedCopier::new(copier); let handle = copier.run(rx, progress); for (from, to) in remaps { let len = range_len(from); let dst_range = Range { start: *to, end: *to + len, }; for (src, dst) in from.clone().zip(dst_range) { batcher.push(CopyOp { src, dst })?; } } batcher.complete()?; handle.join().unwrap()?; Ok(()) } //--------------------------------------- struct MappingCollector { nr_blocks: u64, below: RangeSet<u64>, above: RangeSet<u64>, } impl MappingCollector { fn new(nr_blocks: u64) -> MappingCollector { MappingCollector { nr_blocks, below: RangeSet::new(), above: RangeSet::new(), } } fn get_remaps(self) -> Result<Vec<(BlockRange, u64)>> { let new_range = 0..self.nr_blocks; let free = self.below.gaps(&new_range); build_remaps(self.above, free) } } impl MetadataVisitor for MappingCollector { fn superblock_b(&mut self, _sb: &ir::Superblock) -> Result<Visit> { Ok(Visit::Continue) } fn superblock_e(&mut self) -> Result<Visit> { Ok(Visit::Continue) } fn def_shared_b(&mut self, _name: &str) -> Result<Visit> { Ok(Visit::Continue) } fn def_shared_e(&mut self) -> Result<Visit> { Ok(Visit::Continue) } fn device_b(&mut self, _d: &ir::Device) -> Result<Visit> { Ok(Visit::Continue) } fn device_e(&mut self) -> Result<Visit> { Ok(Visit::Continue) } fn map(&mut self, m: &ir::Map) -> Result<Visit> { if m.data_begin >= self.nr_blocks { self.above.insert(std::ops::Range { start: m.data_begin, end: m.data_begin + m.len, }); } else if m.data_begin + m.len <= self.nr_blocks { self.below.insert(std::ops::Range { start: m.data_begin, end: m.data_begin + m.len, }); } else { self.below.insert(std::ops::Range { start: m.data_begin, end: self.nr_blocks, }); self.above.insert(std::ops::Range { start: self.nr_blocks, end: m.data_begin + m.len, }); } Ok(Visit::Continue) } fn ref_shared(&mut self, _name: &str) -> Result<Visit> { Ok(Visit::Continue) } fn eof(&mut self) -> Result<Visit> { Ok(Visit::Continue) } } //--------------------------------------- struct DataRemapper<'a> { writer: &'a mut dyn MetadataVisitor, nr_blocks: u64, remaps: Vec<(BlockRange, u64)>, } impl<'a> DataRemapper<'a> { fn new( writer: &'a mut dyn MetadataVisitor, nr_blocks: u64, remaps: Vec<(BlockRange, u64)>, ) -> DataRemapper<'a> { DataRemapper { writer, nr_blocks, remaps, } } } impl MetadataVisitor for DataRemapper<'_> { fn superblock_b(&mut self, sb: &ir::Superblock) -> Result<Visit> { let mut sb = sb.clone(); sb.nr_data_blocks = self.nr_blocks; self.writer.superblock_b(&sb) } fn superblock_e(&mut self) -> Result<Visit> { self.writer.superblock_e() } fn def_shared_b(&mut self, name: &str) -> Result<Visit> { self.writer.def_shared_b(name) } fn def_shared_e(&mut self) -> Result<Visit> { self.writer.def_shared_e() } fn device_b(&mut self, d: &ir::Device) -> Result<Visit> { self.writer.device_b(d) } fn device_e(&mut self) -> Result<Visit> { self.writer.device_e() } fn map(&mut self, m: &ir::Map) -> Result<Visit> { if m.data_begin + m.len < self.nr_blocks { // no remapping needed. self.writer.map(m)?; } else { let r = m.data_begin..(m.data_begin + m.len); let remaps = remap(&r, &self.remaps); let mut written = 0; for r in remaps { self.writer.map(&ir::Map { thin_begin: m.thin_begin + written, data_begin: r.start, time: m.time, len: range_len(&r), })?; written += range_len(&r); } } Ok(Visit::Continue) } fn ref_shared(&mut self, name: &str) -> Result<Visit> { self.writer.ref_shared(name) } fn eof(&mut self) -> Result<Visit> { self.writer.eof() } } //--------------------------------------- fn build_remaps_from_metadata( engine: Arc<dyn IoEngine>, sb: &Superblock, md: &Metadata, nr_blocks: u64, ) -> Result<Vec<(BlockRange, u64)>> { let mut collector = MappingCollector::new(nr_blocks); dump_metadata( engine, &mut collector, &ThinSuperblock::OnDisk(sb.clone()), md, )?; collector.get_remaps() } fn build_remaps_from_xml<R: Read>(input: R, nr_blocks: u64) -> Result<Vec<(BlockRange, u64)>> { let mut collector = MappingCollector::new(nr_blocks); xml::read(input, &mut collector)?; collector.get_remaps() } pub struct ThinShrinkOptions { pub input: PathBuf, pub output: PathBuf, pub data_device: PathBuf, pub nr_blocks: u64, pub do_copy: bool, pub binary_mode: bool, pub report: Arc<Report>, } fn rewrite_xml(opts: ThinShrinkOptions) -> Result<()> { use std::io::Seek; // 1st pass let mut input = OpenOptions::new() .read(true) .write(false) .custom_flags(libc::O_EXCL) .open(&opts.input)?; let sb = xml::read_superblock(input.try_clone()?)?; input.seek(SeekFrom::Start(0))?; let remaps = build_remaps_from_xml(input.try_clone()?, opts.nr_blocks)?; let progress = Arc::new(IgnoreProgress {}); if opts.do_copy { let bs = (sb.data_block_size as usize) << SECTOR_SHIFT; copy_regions(&opts.data_device, &remaps, bs, progress)?; } // 2nd pass let writer = BufWriter::new(File::create(&opts.output)?); let mut xml_writer = xml::XmlWriter::new(writer); let mut remapper = DataRemapper::new(&mut xml_writer, opts.nr_blocks, remaps); input.seek(SeekFrom::Start(0))?; xml::read(input, &mut remapper) } fn rebuild_metadata(opts: ThinShrinkOptions) -> Result<()> { let input = Arc::new(SyncIoEngine::new(&opts.input, false)?); let sb = read_superblock(input.as_ref(), SUPERBLOCK_LOCATION)?; let md = build_metadata(input.clone(), &ThinSuperblock::OnDisk(sb.clone()))?; let md = optimise_metadata(md)?; // 1st pass let remaps = build_remaps_from_metadata(input.clone(), &sb, &md, opts.nr_blocks)?; let progress = Arc::new(IgnoreProgress {}); if opts.do_copy { let bs = (sb.data_block_size as usize) << SECTOR_SHIFT; copy_regions(&opts.data_device, &remaps, bs, progress)?; } // 2nd pass let output = Arc::new(SyncIoEngine::new(&opts.output, true)?); let sm = core_metadata_sm(output.get_nr_blocks(), u32::MAX); let mut w = WriteBatcher::new(output.clone(), sm, output.get_batch_size()); let mut restorer = Restorer::new(&mut w, opts.report); let mut remapper = DataRemapper::new(&mut restorer, opts.nr_blocks, remaps); dump_metadata( input, &mut remapper, &ThinSuperblock::OnDisk(sb.clone()), &md, ) } pub fn shrink(opts: ThinShrinkOptions) -> Result<()> { if opts.binary_mode { rebuild_metadata(opts) } else { rewrite_xml(opts) } } //---------------------------------------