Merge rust tools into a single pdata_tools exe
This commit is contained in:
69
src/bin/pdata_tools.rs
Normal file
69
src/bin/pdata_tools.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use anyhow::{anyhow, ensure, Result};
|
||||
use std::ffi::OsString;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use thinp::commands::*;
|
||||
|
||||
fn name_eq(name: &Path, cmd: &str) -> bool {
|
||||
name == Path::new(cmd)
|
||||
}
|
||||
|
||||
fn main_() -> Result<()> {
|
||||
let mut args = std::env::args_os();
|
||||
ensure!(args.len() > 0);
|
||||
|
||||
let mut os_name = args.next().unwrap();
|
||||
let mut name = Path::new(&os_name);
|
||||
name = Path::new(name.file_name().unwrap());
|
||||
|
||||
if name == Path::new("pdata_tools") {
|
||||
os_name = args.next().unwrap();
|
||||
name = Path::new(&os_name);
|
||||
}
|
||||
|
||||
let mut new_args = vec![OsString::from(&name)];
|
||||
for a in args.into_iter() {
|
||||
new_args.push(OsString::from(a));
|
||||
}
|
||||
|
||||
if name_eq(name, "cache_check") {
|
||||
cache_check::run(&new_args);
|
||||
} else if name_eq(name, "cache_dump") {
|
||||
cache_dump::run(&new_args);
|
||||
} else if name_eq(name, "cache_repair") {
|
||||
cache_repair::run(&new_args);
|
||||
} else if name_eq(name, "cache_restore") {
|
||||
cache_restore::run(&new_args);
|
||||
} else if name_eq(name, "thin_check") {
|
||||
thin_check::run(&new_args);
|
||||
} else if name_eq(name, "thin_dump") {
|
||||
thin_dump::run(&new_args);
|
||||
} else if name_eq(name, "thin_metadata_pack") {
|
||||
thin_metadata_pack::run(&new_args);
|
||||
} else if name_eq(name, "thin_metadata_unpack") {
|
||||
thin_metadata_unpack::run(&new_args);
|
||||
} else if name_eq(name, "thin_repair") {
|
||||
thin_repair::run(&new_args);
|
||||
} else if name_eq(name, "thin_restore") {
|
||||
thin_restore::run(&new_args);
|
||||
} else if name_eq(name, "thin_shrink") {
|
||||
thin_shrink::run(&new_args);
|
||||
} else {
|
||||
return Err(anyhow!("unrecognised command"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let code = match main_() {
|
||||
Ok(()) => 0,
|
||||
Err(_) => {
|
||||
// We don't print out the error since -q may be set
|
||||
// eprintln!("{}", e);
|
||||
1
|
||||
}
|
||||
};
|
||||
|
||||
exit(code)
|
||||
}
|
||||
10
src/cache/check.rs
vendored
10
src/cache/check.rs
vendored
@@ -6,6 +6,7 @@ use std::sync::{Arc, Mutex};
|
||||
use crate::cache::hint::*;
|
||||
use crate::cache::mapping::*;
|
||||
use crate::cache::superblock::*;
|
||||
use crate::commands::utils::*;
|
||||
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
|
||||
use crate::pdata::array::{self, ArrayBlock, ArrayError};
|
||||
use crate::pdata::array_walker::*;
|
||||
@@ -267,7 +268,14 @@ pub fn check(opts: CacheCheckOptions) -> anyhow::Result<()> {
|
||||
let metadata_sm = core_sm(engine.get_nr_blocks(), u8::MAX as u32);
|
||||
inc_superblock(&metadata_sm)?;
|
||||
|
||||
let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?;
|
||||
let sb = match read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION) {
|
||||
Ok(sb) => sb,
|
||||
Err(e) => {
|
||||
check_not_xml(&opts.dev, &opts.report);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
check_superblock(&sb)?;
|
||||
|
||||
if opts.sb_only {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
@@ -7,15 +6,15 @@ use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
use thinp::cache::check::{check, CacheCheckOptions};
|
||||
use thinp::file_utils;
|
||||
use thinp::report::*;
|
||||
use crate::cache::check::{check, CacheCheckOptions};
|
||||
use crate::report::*;
|
||||
use crate::commands::utils::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
fn main() {
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("cache_check")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
// flags
|
||||
.arg(
|
||||
Arg::with_name("ASYNC_IO")
|
||||
@@ -44,11 +43,6 @@ fn main() {
|
||||
.help("Only check the superblock.")
|
||||
.long("super-block-only"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("SKIP_MAPPINGS")
|
||||
.help("Don't check the mapping array")
|
||||
.long("skip-mappings"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("SKIP_HINTS")
|
||||
.help("Don't check the hint array")
|
||||
@@ -67,22 +61,19 @@ fn main() {
|
||||
.index(1),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let report;
|
||||
if matches.is_present("QUIET") {
|
||||
report = std::sync::Arc::new(mk_quiet_report());
|
||||
let report = if matches.is_present("QUIET") {
|
||||
std::sync::Arc::new(mk_quiet_report())
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
report = std::sync::Arc::new(mk_progress_bar_report());
|
||||
std::sync::Arc::new(mk_progress_bar_report())
|
||||
} else {
|
||||
report = Arc::new(mk_simple_report());
|
||||
}
|
||||
Arc::new(mk_simple_report())
|
||||
};
|
||||
|
||||
check_input_file(input_file, &report);
|
||||
check_file_not_tiny(input_file, &report);
|
||||
|
||||
let opts = CacheCheckOptions {
|
||||
dev: &input_file,
|
||||
@@ -1,17 +1,17 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use thinp::cache::dump::{dump, CacheDumpOptions};
|
||||
use thinp::file_utils;
|
||||
|
||||
use crate::cache::dump::{dump, CacheDumpOptions};
|
||||
use crate::commands::utils::*;
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
fn main() {
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("cache_dump")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Dump the cache metadata to stdout in XML format")
|
||||
// flags
|
||||
.arg(
|
||||
@@ -42,7 +42,7 @@ fn main() {
|
||||
.index(1),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = if matches.is_present("OUTPUT") {
|
||||
Some(Path::new(matches.value_of("OUTPUT").unwrap()))
|
||||
@@ -50,10 +50,12 @@ fn main() {
|
||||
None
|
||||
};
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
// Create a temporary report just in case these checks
|
||||
// need to report anything.
|
||||
let report = std::sync::Arc::new(crate::report::mk_simple_report());
|
||||
check_input_file(input_file, &report);
|
||||
check_file_not_tiny(input_file, &report);
|
||||
drop(report);
|
||||
|
||||
let opts = CacheDumpOptions {
|
||||
input: input_file,
|
||||
@@ -1,18 +1,18 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use thinp::cache::repair::{repair, CacheRepairOptions};
|
||||
use thinp::file_utils;
|
||||
use thinp::report::*;
|
||||
|
||||
fn main() {
|
||||
use crate::cache::repair::{repair, CacheRepairOptions};
|
||||
use crate::commands::utils::*;
|
||||
use crate::report::*;
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("cache_repair")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Repair binary cache metadata, and write it to a different device or file")
|
||||
// flags
|
||||
.arg(
|
||||
@@ -45,24 +45,19 @@ fn main() {
|
||||
.required(true),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let report;
|
||||
|
||||
if matches.is_present("QUIET") {
|
||||
report = std::sync::Arc::new(mk_quiet_report());
|
||||
let report = if matches.is_present("QUIET") {
|
||||
std::sync::Arc::new(mk_quiet_report())
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
report = std::sync::Arc::new(mk_progress_bar_report());
|
||||
std::sync::Arc::new(mk_progress_bar_report())
|
||||
} else {
|
||||
report = Arc::new(mk_simple_report());
|
||||
}
|
||||
Arc::new(mk_simple_report())
|
||||
};
|
||||
|
||||
check_input_file(input_file, &report);
|
||||
|
||||
let opts = CacheRepairOptions {
|
||||
input: &input_file,
|
||||
@@ -1,18 +1,15 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use thinp::cache::restore::{restore, CacheRestoreOptions};
|
||||
use thinp::file_utils;
|
||||
use thinp::report::*;
|
||||
|
||||
fn main() {
|
||||
use crate::cache::restore::{restore, CacheRestoreOptions};
|
||||
use crate::commands::utils::*;
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("cache_restore")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Convert XML format metadata to binary.")
|
||||
// flags
|
||||
.arg(
|
||||
@@ -45,29 +42,13 @@ fn main() {
|
||||
.required(true),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
if let Err(e) = file_utils::check_output_file_requirements(output_file) {
|
||||
eprintln!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let report;
|
||||
|
||||
if matches.is_present("QUIET") {
|
||||
report = std::sync::Arc::new(mk_quiet_report());
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
report = std::sync::Arc::new(mk_progress_bar_report());
|
||||
} else {
|
||||
report = Arc::new(mk_simple_report());
|
||||
}
|
||||
let report = mk_report(matches.is_present("QUIET"));
|
||||
check_input_file(input_file, &report);
|
||||
check_output_file(output_file, &report);
|
||||
|
||||
let opts = CacheRestoreOptions {
|
||||
input: &input_file,
|
||||
12
src/commands/mod.rs
Normal file
12
src/commands/mod.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
pub mod cache_check;
|
||||
pub mod cache_dump;
|
||||
pub mod cache_repair;
|
||||
pub mod cache_restore;
|
||||
pub mod thin_check;
|
||||
pub mod thin_dump;
|
||||
pub mod thin_metadata_pack;
|
||||
pub mod thin_metadata_unpack;
|
||||
pub mod thin_repair;
|
||||
pub mod thin_restore;
|
||||
pub mod thin_shrink;
|
||||
pub mod utils;
|
||||
@@ -1,19 +1,17 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use thinp::file_utils;
|
||||
use thinp::io_engine::*;
|
||||
use thinp::report::*;
|
||||
use thinp::thin::check::{check, ThinCheckOptions, MAX_CONCURRENT_IO};
|
||||
|
||||
fn main() {
|
||||
use crate::io_engine::*;
|
||||
use crate::thin::check::{check, ThinCheckOptions, MAX_CONCURRENT_IO};
|
||||
use crate::commands::utils::*;
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("thin_check")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Validates thin provisioning metadata on a device or file.")
|
||||
// flags
|
||||
.arg(
|
||||
@@ -90,23 +88,13 @@ fn main() {
|
||||
.index(1),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args.into_iter());
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let report;
|
||||
|
||||
if matches.is_present("QUIET") {
|
||||
report = std::sync::Arc::new(mk_quiet_report());
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
report = std::sync::Arc::new(mk_progress_bar_report());
|
||||
} else {
|
||||
report = Arc::new(mk_simple_report());
|
||||
}
|
||||
let report = mk_report(matches.is_present("QUIET"));
|
||||
check_input_file(input_file, &report);
|
||||
check_file_not_tiny(input_file, &report);
|
||||
check_not_xml(input_file, &report);
|
||||
|
||||
let engine: Arc<dyn IoEngine + Send + Sync>;
|
||||
let writable = matches.is_present("AUTO_REPAIR") || matches.is_present("CLEAR_NEEDS_CHECK");
|
||||
@@ -1,19 +1,19 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use thinp::file_utils;
|
||||
use thinp::report::*;
|
||||
use thinp::thin::dump::{dump, ThinDumpOptions};
|
||||
use thinp::thin::metadata_repair::SuperblockOverrides;
|
||||
|
||||
fn main() {
|
||||
use crate::commands::utils::*;
|
||||
use crate::report::*;
|
||||
use crate::thin::dump::{dump, ThinDumpOptions};
|
||||
use crate::thin::metadata_repair::SuperblockOverrides;
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("thin_dump")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Dump thin-provisioning metadata to stdout in XML format")
|
||||
// flags
|
||||
.arg(
|
||||
@@ -80,7 +80,7 @@ fn main() {
|
||||
.index(1),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = if matches.is_present("OUTPUT") {
|
||||
Some(Path::new(matches.value_of("OUTPUT").unwrap()))
|
||||
@@ -88,10 +88,8 @@ fn main() {
|
||||
None
|
||||
};
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
let report = std::sync::Arc::new(mk_simple_report());
|
||||
check_input_file(input_file, &report);
|
||||
|
||||
let transaction_id = matches.value_of("TRANSACTION_ID").map(|s| {
|
||||
s.parse::<u64>().unwrap_or_else(|_| {
|
||||
@@ -1,14 +1,15 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use thinp::file_utils;
|
||||
|
||||
fn main() {
|
||||
use crate::commands::utils::*;
|
||||
use crate::report::*;
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("thin_metadata_pack")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Produces a compressed file of thin metadata. Only packs metadata blocks that are actually used.")
|
||||
.arg(Arg::with_name("INPUT")
|
||||
.help("Specify thinp metadata binary device/file")
|
||||
@@ -23,17 +24,15 @@ fn main() {
|
||||
.value_name("FILE")
|
||||
.takes_value(true));
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
exit(1);
|
||||
}
|
||||
let report = std::sync::Arc::new(mk_simple_report());
|
||||
check_input_file(input_file, &report);
|
||||
|
||||
if let Err(reason) = thinp::pack::toplevel::pack(&input_file, &output_file) {
|
||||
println!("Application error: {}\n", reason);
|
||||
if let Err(reason) = crate::pack::toplevel::pack(&input_file, &output_file) {
|
||||
report.fatal(&format!("Application error: {}\n", reason));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use thinp::file_utils;
|
||||
use crate::file_utils;
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
fn main() {
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("thin_metadata_unpack")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Unpack a compressed file of thin metadata.")
|
||||
.arg(
|
||||
Arg::with_name("INPUT")
|
||||
@@ -29,16 +28,16 @@ fn main() {
|
||||
.takes_value(true),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
if !file_utils::is_file(input_file) {
|
||||
eprintln!("Invalid input file '{}'.", input_file.display());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if let Err(reason) = thinp::pack::toplevel::unpack(&input_file, &output_file) {
|
||||
if let Err(reason) = crate::pack::toplevel::unpack(&input_file, &output_file) {
|
||||
eprintln!("Application error: {}", reason);
|
||||
process::exit(1);
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use thinp::file_utils;
|
||||
use thinp::report::*;
|
||||
use thinp::thin::metadata_repair::SuperblockOverrides;
|
||||
use thinp::thin::repair::{repair, ThinRepairOptions};
|
||||
|
||||
fn main() {
|
||||
use crate::commands::utils::*;
|
||||
use crate::thin::metadata_repair::SuperblockOverrides;
|
||||
use crate::thin::repair::{repair, ThinRepairOptions};
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("thin_repair")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Repair thin-provisioning metadata, and write it to different device or file")
|
||||
// flags
|
||||
.arg(
|
||||
@@ -64,46 +61,35 @@ fn main() {
|
||||
.value_name("NUM"),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
let report = mk_report(matches.is_present("QUIET"));
|
||||
check_input_file(input_file, &report);
|
||||
check_output_file(output_file, &report);
|
||||
|
||||
let transaction_id = matches.value_of("TRANSACTION_ID").map(|s| {
|
||||
s.parse::<u64>().unwrap_or_else(|_| {
|
||||
eprintln!("Couldn't parse transaction_id");
|
||||
report.fatal("Couldn't parse transaction_id");
|
||||
process::exit(1);
|
||||
})
|
||||
});
|
||||
|
||||
let data_block_size = matches.value_of("DATA_BLOCK_SIZE").map(|s| {
|
||||
s.parse::<u32>().unwrap_or_else(|_| {
|
||||
eprintln!("Couldn't parse data_block_size");
|
||||
report.fatal("Couldn't parse data_block_size");
|
||||
process::exit(1);
|
||||
})
|
||||
});
|
||||
|
||||
let nr_data_blocks = matches.value_of("NR_DATA_BLOCKS").map(|s| {
|
||||
s.parse::<u64>().unwrap_or_else(|_| {
|
||||
eprintln!("Couldn't parse nr_data_blocks");
|
||||
report.fatal("Couldn't parse nr_data_blocks");
|
||||
process::exit(1);
|
||||
})
|
||||
});
|
||||
|
||||
let report;
|
||||
|
||||
if matches.is_present("QUIET") {
|
||||
report = std::sync::Arc::new(mk_quiet_report());
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
report = std::sync::Arc::new(mk_progress_bar_report());
|
||||
} else {
|
||||
report = Arc::new(mk_simple_report());
|
||||
}
|
||||
|
||||
let opts = ThinRepairOptions {
|
||||
input: &input_file,
|
||||
output: &output_file,
|
||||
@@ -1,18 +1,15 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use atty::Stream;
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
use thinp::file_utils;
|
||||
use thinp::report::*;
|
||||
use thinp::thin::restore::{restore, ThinRestoreOptions};
|
||||
|
||||
fn main() {
|
||||
use crate::commands::utils::*;
|
||||
use crate::thin::restore::{restore, ThinRestoreOptions};
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("thin_restore")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Convert XML format metadata to binary.")
|
||||
// flags
|
||||
.arg(
|
||||
@@ -45,29 +42,13 @@ fn main() {
|
||||
.required(true),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if let Err(e) = file_utils::is_file(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
if let Err(e) = file_utils::check_output_file_requirements(output_file) {
|
||||
eprintln!("{}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let report;
|
||||
|
||||
if matches.is_present("QUIET") {
|
||||
report = std::sync::Arc::new(mk_quiet_report());
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
report = std::sync::Arc::new(mk_progress_bar_report());
|
||||
} else {
|
||||
report = Arc::new(mk_simple_report());
|
||||
}
|
||||
let report = mk_report(matches.is_present("QUIET"));
|
||||
check_input_file(input_file, &report);
|
||||
check_output_file(output_file, &report);
|
||||
|
||||
let opts = ThinRestoreOptions {
|
||||
input: &input_file,
|
||||
@@ -3,16 +3,16 @@
|
||||
// https://github.com/nkshirsagar/thinpool_shrink/blob/split_ranges/thin_shrink.py
|
||||
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use thinp::file_utils;
|
||||
|
||||
fn main() {
|
||||
use crate::commands::utils::*;
|
||||
|
||||
pub fn run(args: &[std::ffi::OsString]) {
|
||||
let parser = App::new("thin_shrink")
|
||||
.version(thinp::version::tools_version())
|
||||
.version(crate::version::tools_version())
|
||||
.about("Rewrite xml metadata and move data in an inactive pool.")
|
||||
.arg(
|
||||
Arg::with_name("INPUT")
|
||||
@@ -57,7 +57,7 @@ fn main() {
|
||||
.takes_value(true),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let matches = parser.get_matches_from(args);
|
||||
|
||||
// FIXME: check these look like xml
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
@@ -66,13 +66,11 @@ fn main() {
|
||||
let data_file = Path::new(matches.value_of("DATA").unwrap());
|
||||
let do_copy = !matches.is_present("NOCOPY");
|
||||
|
||||
if let Err(e) = file_utils::is_file_or_blk(input_file) {
|
||||
eprintln!("Invalid input file '{}': {}.", input_file.display(), e);
|
||||
exit(1);
|
||||
}
|
||||
let report = mk_report(false);
|
||||
check_input_file(input_file, &report);
|
||||
|
||||
if let Err(reason) =
|
||||
thinp::shrink::toplevel::shrink(&input_file, &output_file, &data_file, size, do_copy)
|
||||
crate::shrink::toplevel::shrink(&input_file, &output_file, &data_file, size, do_copy)
|
||||
{
|
||||
eprintln!("Application error: {}\n", reason);
|
||||
exit(1);
|
||||
91
src/commands/utils.rs
Normal file
91
src/commands/utils.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use anyhow::Result;
|
||||
use atty::Stream;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
use crate::file_utils;
|
||||
use crate::report::*;
|
||||
|
||||
pub fn check_input_file(input_file: &Path, report: &Report) {
|
||||
if !file_utils::file_exists(input_file) {
|
||||
report.fatal(&format!(
|
||||
"Couldn't find input file '{:?}'.",
|
||||
&input_file
|
||||
));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if !file_utils::is_file_or_blk(input_file) {
|
||||
report.fatal(&format!(
|
||||
"Not a block device or regular file '{:?}'.",
|
||||
&input_file
|
||||
));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_file_not_tiny(input_file: &Path, report: &Report) {
|
||||
if file_utils::file_size(input_file).expect("couldn't get input size") < 4096 {
|
||||
report.fatal("Metadata device/file too small. Is this binary metadata?");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_output_file(path: &Path, report: &Report) {
|
||||
// minimal thin metadata size is 10 blocks, with one device
|
||||
match file_utils::file_size(path) {
|
||||
Ok(size) => {
|
||||
if size < 40960 {
|
||||
report.fatal("Output file too small.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
report.fatal(&format!("{}", e));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mk_report(quiet: bool) -> std::sync::Arc<Report> {
|
||||
use std::sync::Arc;
|
||||
|
||||
if quiet {
|
||||
Arc::new(mk_quiet_report())
|
||||
} else if atty::is(Stream::Stdout) {
|
||||
Arc::new(mk_progress_bar_report())
|
||||
} else {
|
||||
Arc::new(mk_simple_report())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_xml(line: &[u8]) -> bool {
|
||||
line.starts_with(b"<superblock") ||
|
||||
line.starts_with(b"?xml") ||
|
||||
line.starts_with(b"<!DOCTYPE")
|
||||
}
|
||||
|
||||
pub fn check_not_xml_(input_file: &Path, report: &Report) -> Result<()> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(input_file)?;
|
||||
let mut data = vec![0; 16];
|
||||
file.read_exact(&mut data)?;
|
||||
|
||||
if is_xml(&data) {
|
||||
report.fatal("This looks like XML. This tool only checks the binary metadata format.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This trys to read the start of input_path to see
|
||||
/// if it's xml. If there are any problems reading the file
|
||||
/// then it fails silently.
|
||||
pub fn check_not_xml(input_file: &Path, report: &Report) {
|
||||
let _ = check_not_xml_(input_file, report);
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
@@ -1,5 +1,5 @@
|
||||
use nix::sys::stat;
|
||||
use nix::sys::stat::FileStat;
|
||||
use nix::sys::stat::{FileStat, SFlag};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::{Seek, Write};
|
||||
@@ -9,47 +9,38 @@ use tempfile::tempfile;
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
#[inline(always)]
|
||||
pub fn s_isreg(info: &FileStat) -> bool {
|
||||
(info.st_mode & stat::SFlag::S_IFMT.bits()) == stat::SFlag::S_IFREG.bits()
|
||||
fn test_bit(mode: u32, flag: SFlag) -> bool {
|
||||
SFlag::from_bits_truncate(mode).contains(flag)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn s_isblk(info: &FileStat) -> bool {
|
||||
(info.st_mode & stat::SFlag::S_IFMT.bits()) == stat::SFlag::S_IFBLK.bits()
|
||||
pub fn is_file_or_blk_(info: FileStat) -> bool {
|
||||
test_bit(info.st_mode, SFlag::S_IFBLK) || test_bit(info.st_mode, SFlag::S_IFREG)
|
||||
}
|
||||
|
||||
pub fn is_file(path: &Path) -> io::Result<()> {
|
||||
pub fn file_exists(path: &Path) -> bool {
|
||||
match stat::stat(path) {
|
||||
Ok(info) => {
|
||||
if s_isreg(&info) {
|
||||
Ok(())
|
||||
} else {
|
||||
fail("Not a regular file")
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// FIXME: assuming all errors indicate the file doesn't
|
||||
// exist.
|
||||
fail("No such file or directory")
|
||||
}
|
||||
Ok(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_file_or_blk(path: &Path) -> io::Result<()> {
|
||||
pub fn is_file_or_blk(path: &Path) -> bool {
|
||||
match stat::stat(path) {
|
||||
Ok(info) =>is_file_or_blk_(info),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_file(path: &Path) -> bool {
|
||||
match stat::stat(path) {
|
||||
Ok(info) => {
|
||||
if s_isreg(&info) || s_isblk(&info) {
|
||||
Ok(())
|
||||
if test_bit(info.st_mode, SFlag::S_IFREG) {
|
||||
true
|
||||
} else {
|
||||
fail("Not a block device or regular file")
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// FIXME: assuming all errors indicate the file doesn't
|
||||
// exist.
|
||||
fail("No such file or directory")
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +70,9 @@ fn get_device_size(path: &Path) -> io::Result<u64> {
|
||||
pub fn file_size(path: &Path) -> io::Result<u64> {
|
||||
match stat::stat(path) {
|
||||
Ok(info) => {
|
||||
if s_isreg(&info) {
|
||||
if test_bit(info.st_mode, SFlag::S_IFREG) {
|
||||
Ok(info.st_size as u64)
|
||||
} else if s_isblk(&info) {
|
||||
} else if test_bit(info.st_mode, SFlag::S_IFBLK) {
|
||||
get_device_size(path)
|
||||
} else {
|
||||
fail("Not a block device or regular file")
|
||||
@@ -123,12 +114,4 @@ pub fn create_sized_file(path: &Path, nr_bytes: u64) -> io::Result<std::fs::File
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
pub fn check_output_file_requirements(path: &Path) -> io::Result<()> {
|
||||
// minimal thin metadata size is 10 blocks, with one device
|
||||
if file_size(path)? < 40960 {
|
||||
return fail("Output file too small.");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
@@ -17,6 +17,7 @@ extern crate quickcheck_macros;
|
||||
|
||||
pub mod cache;
|
||||
pub mod checksum;
|
||||
pub mod commands;
|
||||
pub mod file_utils;
|
||||
pub mod io_engine;
|
||||
pub mod math;
|
||||
|
||||
@@ -34,6 +34,7 @@ pub trait ReportInner {
|
||||
fn set_sub_title(&mut self, txt: &str);
|
||||
fn progress(&mut self, percent: u8);
|
||||
fn log(&mut self, txt: &str);
|
||||
fn to_stdout(&mut self, txt: &str);
|
||||
fn complete(&mut self);
|
||||
}
|
||||
|
||||
@@ -91,6 +92,13 @@ impl Report {
|
||||
let outcome = self.outcome.lock().unwrap();
|
||||
outcome.clone()
|
||||
}
|
||||
|
||||
// Force a message to be printed to stdout. eg,
|
||||
// TRANSACTION_ID = <blah>
|
||||
pub fn to_stdout(&self, txt: &str) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.to_stdout(txt)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
@@ -126,6 +134,10 @@ impl ReportInner for PBInner {
|
||||
self.bar.println(txt);
|
||||
}
|
||||
|
||||
fn to_stdout(&mut self, txt: &str) {
|
||||
println!("{}", txt);
|
||||
}
|
||||
|
||||
fn complete(&mut self) {
|
||||
self.bar.finish();
|
||||
}
|
||||
@@ -173,6 +185,10 @@ impl ReportInner for SimpleInner {
|
||||
eprintln!("{}", txt);
|
||||
}
|
||||
|
||||
fn to_stdout(&mut self, txt: &str) {
|
||||
println!("{}", txt);
|
||||
}
|
||||
|
||||
fn complete(&mut self) {}
|
||||
}
|
||||
|
||||
@@ -192,6 +208,7 @@ impl ReportInner for QuietInner {
|
||||
fn progress(&mut self, _percent: u8) {}
|
||||
|
||||
fn log(&mut self, _txt: &str) {}
|
||||
fn to_stdout(&mut self, _txt: &str) {}
|
||||
|
||||
fn complete(&mut self) {}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
|
||||
// superblock
|
||||
let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?;
|
||||
|
||||
report.info(&format!("TRANSACTION_ID={}", sb.transaction_id));
|
||||
report.to_stdout(&format!("TRANSACTION_ID={}", sb.transaction_id));
|
||||
|
||||
if opts.sb_only {
|
||||
return Ok(());
|
||||
@@ -295,7 +295,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
|
||||
|
||||
report.set_sub_title("metadata space map");
|
||||
let root = unpack::<SMRoot>(&sb.metadata_sm_root[0..])?;
|
||||
report.info(&format!(
|
||||
report.to_stdout(&format!(
|
||||
"METADATA_FREE_BLOCKS={}",
|
||||
root.nr_blocks - root.nr_allocated
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user