[thin_shrink] write test harness
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
use nix::sys::stat;
|
||||
use nix::sys::stat::{FileStat, SFlag};
|
||||
use std::io;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{Seek, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use tempfile::tempfile;
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
@@ -11,15 +13,13 @@ fn check_bits(mode: u32, flag: &SFlag) -> bool {
|
||||
}
|
||||
|
||||
pub fn is_file_or_blk(info: FileStat) -> bool {
|
||||
check_bits(info.st_mode, &stat::SFlag::S_IFBLK) ||
|
||||
check_bits(info.st_mode, &stat::SFlag::S_IFREG)
|
||||
check_bits(info.st_mode, &stat::SFlag::S_IFBLK)
|
||||
|| check_bits(info.st_mode, &stat::SFlag::S_IFREG)
|
||||
}
|
||||
|
||||
pub fn file_exists(path: &str) -> bool {
|
||||
match stat::stat(path) {
|
||||
Ok(info) => {
|
||||
is_file_or_blk(info)
|
||||
}
|
||||
Ok(info) => is_file_or_blk(info),
|
||||
_ => {
|
||||
// FIXME: assuming all errors indicate the file doesn't
|
||||
// exist.
|
||||
@@ -40,14 +40,14 @@ pub fn fail<T>(msg: &str) -> io::Result<T> {
|
||||
}
|
||||
|
||||
fn get_device_size(path: &str) -> io::Result<u64> {
|
||||
let file = File::open(path)?;
|
||||
let file = File::open(path)?;
|
||||
let fd = file.as_raw_fd();
|
||||
let mut cap = 0u64;
|
||||
unsafe {
|
||||
match ioctl_blkgetsize64(fd, &mut cap) {
|
||||
Ok(_) => {Ok(cap)}
|
||||
_ => {fail("BLKGETSIZE64 ioctl failed")}
|
||||
}
|
||||
match ioctl_blkgetsize64(fd, &mut cap) {
|
||||
Ok(_) => Ok(cap),
|
||||
_ => fail("BLKGETSIZE64 ioctl failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,12 +60,25 @@ pub fn file_size(path: &str) -> io::Result<u64> {
|
||||
get_device_size(path)
|
||||
} else {
|
||||
fail("not a regular file or block device")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
fail("stat failed")
|
||||
}
|
||||
}
|
||||
_ => fail("stat failed"),
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
pub fn temp_file_sized(nr_bytes: u64) -> io::Result<std::fs::File> {
|
||||
let mut file = tempfile()?;
|
||||
|
||||
let zeroes: Vec<u8> = vec![0; 1];
|
||||
|
||||
if nr_bytes > 0 {
|
||||
file.seek(io::SeekFrom::Start(nr_bytes - 1))?;
|
||||
file.write_all(&zeroes)?;
|
||||
}
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
@@ -20,4 +20,5 @@ pub mod check;
|
||||
pub mod file_utils;
|
||||
pub mod pack;
|
||||
pub mod shrink;
|
||||
pub mod thin;
|
||||
pub mod version;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
pub mod toplevel;
|
||||
|
||||
mod copier;
|
||||
mod xml;
|
||||
|
@@ -5,7 +5,7 @@ use std::io::Write;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
use crate::shrink::copier::{self, Region};
|
||||
use crate::shrink::xml;
|
||||
use crate::thin::xml::{self, Visit};
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
@@ -34,36 +34,36 @@ impl Pass1 {
|
||||
}
|
||||
|
||||
impl xml::MetadataVisitor for Pass1 {
|
||||
fn superblock_b(&mut self, sb: &xml::Superblock) -> Result<()> {
|
||||
fn superblock_b(&mut self, sb: &xml::Superblock) -> Result<Visit> {
|
||||
self.allocated_blocks.grow(sb.nr_data_blocks as usize);
|
||||
self.block_size = Some(sb.data_block_size as u64);
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_b(&mut self, _d: &xml::Device) -> Result<()> {
|
||||
Ok(())
|
||||
fn device_b(&mut self, _d: &xml::Device) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_e(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn map(&mut self, m: &xml::Map) -> Result<()> {
|
||||
fn map(&mut self, m: &xml::Map) -> Result<Visit> {
|
||||
for i in m.data_begin..(m.data_begin + m.len) {
|
||||
if i > self.nr_blocks {
|
||||
self.nr_high_blocks += 1;
|
||||
}
|
||||
self.allocated_blocks.insert(i as usize);
|
||||
}
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,23 +87,23 @@ impl<W: Write> Pass2<W> {
|
||||
}
|
||||
|
||||
impl<W: Write> xml::MetadataVisitor for Pass2<W> {
|
||||
fn superblock_b(&mut self, sb: &xml::Superblock) -> Result<()> {
|
||||
fn superblock_b(&mut self, sb: &xml::Superblock) -> Result<Visit> {
|
||||
self.writer.superblock_b(sb)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<()> {
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
self.writer.superblock_e()
|
||||
}
|
||||
|
||||
fn device_b(&mut self, d: &xml::Device) -> Result<()> {
|
||||
fn device_b(&mut self, d: &xml::Device) -> Result<Visit> {
|
||||
self.writer.device_b(d)
|
||||
}
|
||||
|
||||
fn device_e(&mut self) -> Result<()> {
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
self.writer.device_e()
|
||||
}
|
||||
|
||||
fn map(&mut self, m: &xml::Map) -> Result<()> {
|
||||
fn map(&mut self, m: &xml::Map) -> Result<Visit> {
|
||||
if m.data_begin + m.len < self.nr_blocks {
|
||||
// no remapping needed.
|
||||
self.writer.map(m)?;
|
||||
@@ -123,10 +123,10 @@ impl<W: Write> xml::MetadataVisitor for Pass2<W> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<()> {
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
self.writer.eof()
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ fn ranges_split(ranges: &[BlockRange], threshold: u64) -> (Vec<BlockRange>, Vec<
|
||||
(below, above)
|
||||
}
|
||||
|
||||
fn negate_ranges(ranges: &[BlockRange]) -> Vec<BlockRange> {
|
||||
fn negate_ranges(ranges: &[BlockRange], upper_limit: u64) -> Vec<BlockRange> {
|
||||
use std::ops::Range;
|
||||
|
||||
let mut result = Vec::new();
|
||||
@@ -199,6 +199,10 @@ fn negate_ranges(ranges: &[BlockRange]) -> Vec<BlockRange> {
|
||||
}
|
||||
}
|
||||
|
||||
if cursor < upper_limit {
|
||||
result.push(cursor..upper_limit);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
@@ -443,7 +447,7 @@ pub fn shrink(input_path: &str, output_path: &str, data_path: &str, nr_blocks: u
|
||||
let ranges = bits_to_ranges(&pass1.allocated_blocks);
|
||||
let (below, above) = ranges_split(&ranges, nr_blocks);
|
||||
|
||||
let free = negate_ranges(&below);
|
||||
let free = negate_ranges(&below, nr_blocks);
|
||||
let free_blocks = ranges_total(&free);
|
||||
eprintln!("{} free blocks.", free_blocks);
|
||||
|
||||
|
1
src/thin/mod.rs
Normal file
1
src/thin/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod xml;
|
@@ -1,11 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use std::{
|
||||
borrow::{Cow},
|
||||
fmt::Display,
|
||||
io::prelude::*,
|
||||
io::BufReader,
|
||||
io::Write,
|
||||
};
|
||||
use std::{borrow::Cow, fmt::Display, io::prelude::*, io::BufReader, io::Write};
|
||||
|
||||
use quick_xml::events::attributes::Attribute;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, Event};
|
||||
@@ -13,6 +7,7 @@ use quick_xml::{Reader, Writer};
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Superblock {
|
||||
pub uuid: String,
|
||||
pub time: u64,
|
||||
@@ -24,6 +19,7 @@ pub struct Superblock {
|
||||
pub metadata_snap: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Device {
|
||||
pub dev_id: u32,
|
||||
pub mapped_blocks: u64,
|
||||
@@ -32,6 +28,7 @@ pub struct Device {
|
||||
pub snap_time: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Map {
|
||||
pub thin_begin: u64,
|
||||
pub data_begin: u64,
|
||||
@@ -39,16 +36,22 @@ pub struct Map {
|
||||
pub len: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Visit {
|
||||
Continue,
|
||||
Stop,
|
||||
}
|
||||
|
||||
pub trait MetadataVisitor {
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<()>;
|
||||
fn superblock_e(&mut self) -> Result<()>;
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit>;
|
||||
fn superblock_e(&mut self) -> Result<Visit>;
|
||||
|
||||
fn device_b(&mut self, d: &Device) -> Result<()>;
|
||||
fn device_e(&mut self) -> Result<()>;
|
||||
fn device_b(&mut self, d: &Device) -> Result<Visit>;
|
||||
fn device_e(&mut self) -> Result<Visit>;
|
||||
|
||||
fn map(&mut self, m: &Map) -> Result<()>;
|
||||
fn map(&mut self, m: &Map) -> Result<Visit>;
|
||||
|
||||
fn eof(&mut self) -> Result<()>;
|
||||
fn eof(&mut self) -> Result<Visit>;
|
||||
}
|
||||
|
||||
pub struct XmlWriter<W: Write> {
|
||||
@@ -57,7 +60,9 @@ pub struct XmlWriter<W: Write> {
|
||||
|
||||
impl<W: Write> XmlWriter<W> {
|
||||
pub fn new(w: W) -> XmlWriter<W> {
|
||||
XmlWriter { w: Writer::new_with_indent(w, 0x20, 2) }
|
||||
XmlWriter {
|
||||
w: Writer::new_with_indent(w, 0x20, 2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +81,7 @@ fn mk_attr<T: Display>(key: &[u8], value: T) -> Attribute {
|
||||
const XML_VERSION: u32 = 2;
|
||||
|
||||
impl<W: Write> MetadataVisitor for XmlWriter<W> {
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<()> {
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit> {
|
||||
let tag = b"superblock";
|
||||
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||
elem.push_attribute(mk_attr(b"uuid", sb.uuid.clone()));
|
||||
@@ -96,16 +101,16 @@ impl<W: Write> MetadataVisitor for XmlWriter<W> {
|
||||
}
|
||||
|
||||
self.w.write_event(Event::Start(elem))?;
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<()> {
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
self.w
|
||||
.write_event(Event::End(BytesEnd::borrowed(b"superblock")))?;
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_b(&mut self, d: &Device) -> Result<()> {
|
||||
fn device_b(&mut self, d: &Device) -> Result<Visit> {
|
||||
let tag = b"device";
|
||||
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||
elem.push_attribute(mk_attr(b"dev_id", d.dev_id));
|
||||
@@ -114,16 +119,16 @@ impl<W: Write> MetadataVisitor for XmlWriter<W> {
|
||||
elem.push_attribute(mk_attr(b"creation_time", d.creation_time));
|
||||
elem.push_attribute(mk_attr(b"snap_time", d.snap_time));
|
||||
self.w.write_event(Event::Start(elem))?;
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_e(&mut self) -> Result<()> {
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
self.w
|
||||
.write_event(Event::End(BytesEnd::borrowed(b"device")))?;
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn map(&mut self, m: &Map) -> Result<()> {
|
||||
fn map(&mut self, m: &Map) -> Result<Visit> {
|
||||
match m.len {
|
||||
1 => {
|
||||
let tag = b"single_mapping";
|
||||
@@ -143,13 +148,13 @@ impl<W: Write> MetadataVisitor for XmlWriter<W> {
|
||||
self.w.write_event(Event::Empty(elem))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<()> {
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
let w = self.w.inner();
|
||||
w.flush()?;
|
||||
Ok(())
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +189,7 @@ fn missing_attr<T>(_tag: &str, _attr: &str) -> Result<T> {
|
||||
fn check_attr<T>(tag: &str, name: &str, maybe_v: Option<T>) -> Result<T> {
|
||||
match maybe_v {
|
||||
None => missing_attr(tag, name),
|
||||
Some(v) => Ok(v)
|
||||
Some(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +283,7 @@ fn parse_single_map(e: &BytesStart) -> Result<Map> {
|
||||
thin_begin: check_attr(tag, "origin_block", thin_begin)?,
|
||||
data_begin: check_attr(tag, "data_block", data_begin)?,
|
||||
time: check_attr(tag, "time", time)?,
|
||||
len: 1
|
||||
len: 1,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -309,6 +314,40 @@ fn parse_range_map(e: &BytesStart) -> Result<Map> {
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_event<R, M>(reader: &mut Reader<R>, buf: &mut Vec<u8>, visitor: &mut M) -> Result<Visit>
|
||||
where
|
||||
R: Read + BufRead,
|
||||
M: MetadataVisitor,
|
||||
{
|
||||
match reader.read_event(buf) {
|
||||
Ok(Event::Start(ref e)) => match e.name() {
|
||||
b"superblock" => visitor.superblock_b(&parse_superblock(e)?),
|
||||
b"device" => visitor.device_b(&parse_device(e)?),
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::End(ref e)) => match e.name() {
|
||||
b"superblock" => visitor.superblock_e(),
|
||||
b"device" => visitor.device_e(),
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::Empty(ref e)) => match e.name() {
|
||||
b"single_mapping" => visitor.map(&parse_single_map(e)?),
|
||||
b"range_mapping" => visitor.map(&parse_range_map(e)?),
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::Text(_)) => Ok(Visit::Continue),
|
||||
Ok(Event::Comment(_)) => Ok(Visit::Continue),
|
||||
Ok(Event::Eof) => {
|
||||
visitor.eof()?;
|
||||
Ok(Visit::Stop)
|
||||
}
|
||||
Ok(_) => todo!(),
|
||||
|
||||
// FIXME: don't panic!
|
||||
Err(e) => panic!("error parsing xml {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read<R, M>(input: R, visitor: &mut M) -> Result<()>
|
||||
where
|
||||
R: Read,
|
||||
@@ -321,29 +360,9 @@ where
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(Event::Start(ref e)) => match e.name() {
|
||||
b"superblock" => visitor.superblock_b(&parse_superblock(e)?)?,
|
||||
b"device" => visitor.device_b(&parse_device(e)?)?,
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::End(ref e)) => match e.name() {
|
||||
b"superblock" => visitor.superblock_e()?,
|
||||
b"device" => visitor.device_e()?,
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::Empty(ref e)) => match e.name() {
|
||||
b"single_mapping" => visitor.map(&parse_single_map(e)?)?,
|
||||
b"range_mapping" => visitor.map(&parse_range_map(e)?)?,
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::Text(_)) => {}
|
||||
Ok(Event::Comment(_)) => {}
|
||||
Ok(Event::Eof) => break,
|
||||
Ok(_) => todo!(),
|
||||
|
||||
// FIXME: don't panic!
|
||||
Err(e) => panic!("error parsing xml {:?}", e),
|
||||
match handle_event(&mut reader, &mut buf, visitor)? {
|
||||
Visit::Continue => {}
|
||||
Visit::Stop => break,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,3 +370,44 @@ where
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
struct SBVisitor {
|
||||
superblock: Option<Superblock>,
|
||||
}
|
||||
|
||||
impl MetadataVisitor for SBVisitor {
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit> {
|
||||
self.superblock = Some(sb.clone());
|
||||
Ok(Visit::Stop)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_b(&mut self, _d: &Device) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn map(&mut self, _m: &Map) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Stop)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_superblock<R>(input: R) -> Result<Superblock>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
let mut v = SBVisitor {superblock: None};
|
||||
read(input, &mut v)?;
|
||||
Ok(v.superblock.unwrap())
|
||||
}
|
||||
|
||||
//---------------------------------------
|
Reference in New Issue
Block a user