[thin_shrink] calculate remaps
This commit is contained in:
parent
3f1b776359
commit
259eef9eee
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -105,6 +105,11 @@ dependencies = [
|
|||||||
"regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedbitset"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
@ -397,6 +402,7 @@ dependencies = [
|
|||||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crc32c 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crc32c 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fixedbitset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -483,6 +489,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum crc32c 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77ba37ef26c12988c1cee882d522d65e1d5d2ad8c3864665b88ee92767ed84c5"
|
"checksum crc32c 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77ba37ef26c12988c1cee882d522d65e1d5d2ad8c3864665b88ee92767ed84c5"
|
||||||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||||
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||||
|
"checksum fixedbitset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4fcacf5cd3681968f6524ea159383132937739c6c40dabab9e37ed515911b"
|
||||||
"checksum flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
|
"checksum flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
|
||||||
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||||
"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
|
"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
|
||||||
|
@ -11,6 +11,7 @@ byteorder = "1.3"
|
|||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
crc32c = "0.4"
|
crc32c = "0.4"
|
||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
|
fixedbitset = "0.3"
|
||||||
libc = "0.2.71"
|
libc = "0.2.71"
|
||||||
quick-xml = "0.18"
|
quick-xml = "0.18"
|
||||||
nix = "0.17"
|
nix = "0.17"
|
||||||
|
@ -9,31 +9,45 @@ fn main() {
|
|||||||
let parser = App::new("thin_shrink")
|
let parser = App::new("thin_shrink")
|
||||||
.version(thinp::version::TOOLS_VERSION)
|
.version(thinp::version::TOOLS_VERSION)
|
||||||
.about("Rewrite xml metadata and move data in an inactive pool.")
|
.about("Rewrite xml metadata and move data in an inactive pool.")
|
||||||
.arg(Arg::with_name("INPUT")
|
.arg(
|
||||||
|
Arg::with_name("INPUT")
|
||||||
.help("Specify thinp metadata xml file")
|
.help("Specify thinp metadata xml file")
|
||||||
.required(true)
|
.required(true)
|
||||||
.long("input")
|
.long("input")
|
||||||
.value_name("INPUT")
|
.value_name("INPUT")
|
||||||
.takes_value(true))
|
.takes_value(true),
|
||||||
.arg(Arg::with_name("OUTPUT")
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("OUTPUT")
|
||||||
.help("Specify output xml file")
|
.help("Specify output xml file")
|
||||||
.required(true)
|
.required(true)
|
||||||
.long("output")
|
.long("output")
|
||||||
.value_name("OUTPUT")
|
.value_name("OUTPUT")
|
||||||
.takes_value(true));
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
// FIXME: support various disk units
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("SIZE")
|
||||||
|
.help("Specify new size for the pool (in data blocks)")
|
||||||
|
.required(true)
|
||||||
|
.long("nr-blocks")
|
||||||
|
.value_name("SIZE")
|
||||||
|
.takes_value(true),
|
||||||
|
);
|
||||||
|
|
||||||
let matches = parser.get_matches();
|
let matches = parser.get_matches();
|
||||||
|
|
||||||
// FIXME: check these look like xml
|
// FIXME: check these look like xml
|
||||||
let input_file = matches.value_of("INPUT").unwrap();
|
let input_file = matches.value_of("INPUT").unwrap();
|
||||||
let output_file = matches.value_of("OUTPUT").unwrap();
|
let output_file = matches.value_of("OUTPUT").unwrap();
|
||||||
|
let size = matches.value_of("SIZE").unwrap().parse::<u64>().unwrap();
|
||||||
|
|
||||||
if !file_utils::file_exists(input_file) {
|
if !file_utils::file_exists(input_file) {
|
||||||
eprintln!("Couldn't find input file '{}'.", &input_file);
|
eprintln!("Couldn't find input file '{}'.", &input_file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(reason) = thinp::shrink::toplevel::shrink(&input_file, &output_file) {
|
if let Err(reason) = thinp::shrink::toplevel::shrink(&input_file, &output_file, size) {
|
||||||
println!("Application error: {}\n", reason);
|
println!("Application error: {}\n", reason);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use fixedbitset::{FixedBitSet, IndexRange};
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
|
||||||
@ -6,7 +7,178 @@ use crate::shrink::xml;
|
|||||||
|
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
|
|
||||||
pub fn shrink(input_file: &str, _output_file: &str) -> Result<()> {
|
#[derive(Debug)]
|
||||||
|
struct Pass1 {
|
||||||
|
// FIXME: Inefficient, use a range_set of some description
|
||||||
|
allocated_blocks: FixedBitSet,
|
||||||
|
|
||||||
|
nr_blocks: u64,
|
||||||
|
|
||||||
|
/// High blocks are beyond the new, reduced end of the pool. These
|
||||||
|
/// will need to be moved.
|
||||||
|
nr_high_blocks: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pass1 {
|
||||||
|
fn new(nr_blocks: u64) -> Pass1 {
|
||||||
|
Pass1 {
|
||||||
|
allocated_blocks: FixedBitSet::with_capacity(0),
|
||||||
|
nr_blocks,
|
||||||
|
nr_high_blocks: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl xml::MetadataVisitor for Pass1 {
|
||||||
|
fn superblock_b(&mut self, sb: &xml::Superblock) -> Result<()> {
|
||||||
|
self.allocated_blocks.grow(sb.nr_data_blocks as usize);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn superblock_e(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_b(&mut self, _d: &xml::Device) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_e(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map(&mut self, m: xml::Map) -> Result<()> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eof(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockRange = std::ops::Range<u64>;
|
||||||
|
|
||||||
|
fn bits_to_ranges(bits: &FixedBitSet) -> Vec<BlockRange> {
|
||||||
|
let mut ranges = Vec::new();
|
||||||
|
let mut start = None;
|
||||||
|
|
||||||
|
for i in 0..bits.len() {
|
||||||
|
match (bits[i], start) {
|
||||||
|
(false, None) => {}
|
||||||
|
(true, None) => {
|
||||||
|
start = Some((i as u64, 1));
|
||||||
|
}
|
||||||
|
(false, Some((b, len))) => {
|
||||||
|
ranges.push(b..(b + len));
|
||||||
|
start = None;
|
||||||
|
}
|
||||||
|
(true, Some((b, len))) => {
|
||||||
|
start = Some((b, len + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((b, len)) = start {
|
||||||
|
ranges.push(b..(b + len));
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splits the ranges into those below threshold, and those equal or
|
||||||
|
// above threshold below threshold, and those equal or above threshold
|
||||||
|
fn ranges_split(ranges: &Vec<BlockRange>, threshold: u64) -> (Vec<BlockRange>, Vec<BlockRange>) {
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
let mut below = Vec::new();
|
||||||
|
let mut above = Vec::new();
|
||||||
|
for r in ranges {
|
||||||
|
match r {
|
||||||
|
Range { start, end } if *end <= threshold => below.push(*start..*end),
|
||||||
|
Range { start, end } if *start < threshold => {
|
||||||
|
below.push(*start..threshold);
|
||||||
|
above.push(threshold..*end);
|
||||||
|
}
|
||||||
|
Range { start, end } => above.push(*start..*end),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(below, above)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negate_ranges(ranges: &Vec<BlockRange>) -> Vec<BlockRange> {
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let mut cursor = 0;
|
||||||
|
|
||||||
|
for r in ranges {
|
||||||
|
match r {
|
||||||
|
Range { start, end } if cursor < *start => {
|
||||||
|
result.push(cursor..*start);
|
||||||
|
cursor = *end;
|
||||||
|
}
|
||||||
|
Range { start: _, end } => {
|
||||||
|
cursor = *end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range_len(r: &BlockRange) -> u64 {
|
||||||
|
r.end - r.start
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ranges_total(rs: &Vec<BlockRange>) -> u64 {
|
||||||
|
rs.into_iter().fold(0, |sum, r| sum + range_len(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes there is enough space to remap.
|
||||||
|
fn remap_ranges(ranges: Vec<BlockRange>, free: Vec<BlockRange>) -> Vec<(BlockRange, BlockRange)> {
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
let mut remap = Vec::new();
|
||||||
|
let mut range_iter = ranges.into_iter();
|
||||||
|
let mut free_iter = free.into_iter();
|
||||||
|
|
||||||
|
let mut r_ = range_iter.next();
|
||||||
|
let mut f_ = free_iter.next();
|
||||||
|
|
||||||
|
while let (Some(r), Some(f)) = (r_, f_) {
|
||||||
|
let rlen = range_len(&r);
|
||||||
|
let flen = range_len(&f);
|
||||||
|
|
||||||
|
match rlen.cmp(&flen) {
|
||||||
|
Ordering::Less => {
|
||||||
|
// range fits into the free chunk
|
||||||
|
remap.push((r, f.start..(f.start + rlen)));
|
||||||
|
f_ = Some((f.start + rlen)..f.end);
|
||||||
|
r_ = range_iter.next();
|
||||||
|
},
|
||||||
|
Ordering::Equal => {
|
||||||
|
remap.push((r, f));
|
||||||
|
r_ = range_iter.next();
|
||||||
|
f_ = free_iter.next();
|
||||||
|
},
|
||||||
|
Ordering::Greater => {
|
||||||
|
remap.push((r.start..(r.start + flen), f));
|
||||||
|
r_ = Some((r.start + flen)..r.end);
|
||||||
|
f_ = free_iter.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remap
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrink(input_file: &str, _output_file: &str, nr_blocks: u64) -> Result<()> {
|
||||||
let input = OpenOptions::new()
|
let input = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(false)
|
.write(false)
|
||||||
@ -14,8 +186,42 @@ pub fn shrink(input_file: &str, _output_file: &str) -> Result<()> {
|
|||||||
.open(input_file)?;
|
.open(input_file)?;
|
||||||
|
|
||||||
// let mut visitor = xml::XmlWriter::new(std::io::stdout());
|
// let mut visitor = xml::XmlWriter::new(std::io::stdout());
|
||||||
let mut visitor = xml::NoopVisitor::new();
|
// let mut visitor = xml::NoopVisitor::new();
|
||||||
xml::read(input, &mut visitor)?;
|
let mut pass1 = Pass1::new(nr_blocks);
|
||||||
|
xml::read(input, &mut pass1)?;
|
||||||
|
eprintln!("{} blocks need moving", pass1.nr_high_blocks);
|
||||||
|
|
||||||
|
let mut free_blocks = 0u64;
|
||||||
|
for i in 0..pass1.allocated_blocks.len() {
|
||||||
|
if !pass1.allocated_blocks[i] {
|
||||||
|
free_blocks += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!("{} free blocks below new end.", free_blocks);
|
||||||
|
|
||||||
|
let ranges = bits_to_ranges(&pass1.allocated_blocks);
|
||||||
|
eprintln!("{} allocated ranges:", ranges.len());
|
||||||
|
|
||||||
|
eprintln!("{:?}", &ranges);
|
||||||
|
|
||||||
|
let (below, above) = ranges_split(&ranges, nr_blocks);
|
||||||
|
eprintln!("ranges split at {}: ({:?}, {:?})", nr_blocks, below, above);
|
||||||
|
|
||||||
|
let free = negate_ranges(&below);
|
||||||
|
eprintln!("free {:?}.", free);
|
||||||
|
|
||||||
|
let nr_moving = ranges_total(&above);
|
||||||
|
eprintln!("{} blocks need to be remapped.", nr_moving);
|
||||||
|
|
||||||
|
let free_blocks = ranges_total(&free);
|
||||||
|
eprintln!("{} free blocks.", free_blocks);
|
||||||
|
|
||||||
|
if free_blocks < nr_moving {
|
||||||
|
panic!("Insufficient space");
|
||||||
|
}
|
||||||
|
|
||||||
|
let remaps = remap_ranges(above, free);
|
||||||
|
eprintln!("remappings {:?}.", remaps);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -14,29 +14,29 @@ use quick_xml::{Reader, Writer};
|
|||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
|
|
||||||
pub struct Superblock {
|
pub struct Superblock {
|
||||||
uuid: String,
|
pub uuid: String,
|
||||||
time: u64,
|
pub time: u64,
|
||||||
transaction: u64,
|
pub transaction: u64,
|
||||||
flags: Option<u32>,
|
pub flags: Option<u32>,
|
||||||
version: Option<u32>,
|
pub version: Option<u32>,
|
||||||
data_block_size: u32,
|
pub data_block_size: u32,
|
||||||
nr_data_blocks: u64,
|
pub nr_data_blocks: u64,
|
||||||
metadata_snap: Option<u64>,
|
pub metadata_snap: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
dev_id: u32,
|
pub dev_id: u32,
|
||||||
mapped_blocks: u64,
|
pub mapped_blocks: u64,
|
||||||
transaction: u64,
|
pub transaction: u64,
|
||||||
creation_time: u64,
|
pub creation_time: u64,
|
||||||
snap_time: u64,
|
pub snap_time: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Map {
|
pub struct Map {
|
||||||
thin_begin: u64,
|
pub thin_begin: u64,
|
||||||
data_begin: u64,
|
pub data_begin: u64,
|
||||||
time: u32,
|
pub time: u32,
|
||||||
len: u64,
|
pub len: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MetadataVisitor {
|
pub trait MetadataVisitor {
|
||||||
@ -238,11 +238,11 @@ fn parse_superblock(e: &BytesStart) -> Result<Superblock> {
|
|||||||
uuid: check_attr(tag, "uuid", uuid)?,
|
uuid: check_attr(tag, "uuid", uuid)?,
|
||||||
time: check_attr(tag, "time", time)?,
|
time: check_attr(tag, "time", time)?,
|
||||||
transaction: check_attr(tag, "transaction", transaction)?,
|
transaction: check_attr(tag, "transaction", transaction)?,
|
||||||
flags: flags,
|
flags,
|
||||||
version: version,
|
version,
|
||||||
data_block_size: check_attr(tag, "data_block_size", data_block_size)?,
|
data_block_size: check_attr(tag, "data_block_size", data_block_size)?,
|
||||||
nr_data_blocks: check_attr(tag, "nr_data_blocks", nr_data_blocks)?,
|
nr_data_blocks: check_attr(tag, "nr_data_blocks", nr_data_blocks)?,
|
||||||
metadata_snap: metadata_snap,
|
metadata_snap,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user