Merge rust tools into a single pdata_tools exe

This commit is contained in:
Joe Thornber
2021-10-11 12:07:26 +01:00
parent c9b47437f2
commit 024554c987
41 changed files with 840 additions and 567 deletions

8
Cargo.lock generated
View File

@ -381,9 +381,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.97" version = "0.2.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
[[package]] [[package]]
name = "log" name = "log"
@ -421,9 +421,9 @@ dependencies = [
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.21.0" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c3728fec49d363a50a8828a190b379a446cc5cf085c06259bbbeb34447e4ec7" checksum = "e7555d6c7164cc913be1ce7f95cbecdabda61eb2ccd89008524af306fb7f5031"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cc", "cc",

View File

@ -20,7 +20,7 @@ flate2 = "1.0"
io-uring = "0.4" io-uring = "0.4"
indicatif = "0.16" indicatif = "0.16"
libc = "0.2" libc = "0.2"
nix = "0.21" nix = "0.22"
nom = "6.2" nom = "6.2"
num_cpus = "1.13" num_cpus = "1.13"
num-derive = "0.3" num-derive = "0.3"

69
src/bin/pdata_tools.rs Normal file
View 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
View File

@ -6,6 +6,7 @@ use std::sync::{Arc, Mutex};
use crate::cache::hint::*; use crate::cache::hint::*;
use crate::cache::mapping::*; use crate::cache::mapping::*;
use crate::cache::superblock::*; use crate::cache::superblock::*;
use crate::commands::utils::*;
use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine}; use crate::io_engine::{AsyncIoEngine, IoEngine, SyncIoEngine};
use crate::pdata::array::{self, ArrayBlock, ArrayError}; use crate::pdata::array::{self, ArrayBlock, ArrayError};
use crate::pdata::array_walker::*; 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); let metadata_sm = core_sm(engine.get_nr_blocks(), u8::MAX as u32);
inc_superblock(&metadata_sm)?; 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)?; check_superblock(&sb)?;
if opts.sb_only { if opts.sb_only {

View File

@ -1,5 +1,4 @@
extern crate clap; extern crate clap;
extern crate thinp;
use atty::Stream; use atty::Stream;
use clap::{App, Arg}; use clap::{App, Arg};
@ -7,15 +6,15 @@ use std::path::Path;
use std::process; use std::process;
use std::sync::Arc; use std::sync::Arc;
use thinp::cache::check::{check, CacheCheckOptions}; use crate::cache::check::{check, CacheCheckOptions};
use thinp::file_utils; use crate::report::*;
use thinp::report::*; use crate::commands::utils::*;
//------------------------------------------ //------------------------------------------
fn main() { pub fn run(args: &[std::ffi::OsString]) {
let parser = App::new("cache_check") let parser = App::new("cache_check")
.version(thinp::version::tools_version()) .version(crate::version::tools_version())
// flags // flags
.arg( .arg(
Arg::with_name("ASYNC_IO") Arg::with_name("ASYNC_IO")
@ -44,11 +43,6 @@ fn main() {
.help("Only check the superblock.") .help("Only check the superblock.")
.long("super-block-only"), .long("super-block-only"),
) )
.arg(
Arg::with_name("SKIP_MAPPINGS")
.help("Don't check the mapping array")
.long("skip-mappings"),
)
.arg( .arg(
Arg::with_name("SKIP_HINTS") Arg::with_name("SKIP_HINTS")
.help("Don't check the hint array") .help("Don't check the hint array")
@ -67,22 +61,19 @@ fn main() {
.index(1), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
if let Err(e) = file_utils::is_file_or_blk(input_file) { let report = if matches.is_present("QUIET") {
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); std::sync::Arc::new(mk_quiet_report())
process::exit(1);
}
let report;
if matches.is_present("QUIET") {
report = std::sync::Arc::new(mk_quiet_report());
} else if atty::is(Stream::Stdout) { } else if atty::is(Stream::Stdout) {
report = std::sync::Arc::new(mk_progress_bar_report()); std::sync::Arc::new(mk_progress_bar_report())
} else { } 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 { let opts = CacheCheckOptions {
dev: &input_file, dev: &input_file,

View File

@ -1,17 +1,17 @@
extern crate clap; extern crate clap;
extern crate thinp;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; 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") 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") .about("Dump the cache metadata to stdout in XML format")
// flags // flags
.arg( .arg(
@ -42,7 +42,7 @@ fn main() {
.index(1), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = if matches.is_present("OUTPUT") { let output_file = if matches.is_present("OUTPUT") {
Some(Path::new(matches.value_of("OUTPUT").unwrap())) Some(Path::new(matches.value_of("OUTPUT").unwrap()))
@ -50,10 +50,12 @@ fn main() {
None None
}; };
if let Err(e) = file_utils::is_file_or_blk(input_file) { // Create a temporary report just in case these checks
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); // need to report anything.
process::exit(1); 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 { let opts = CacheDumpOptions {
input: input_file, input: input_file,

View File

@ -1,18 +1,18 @@
extern crate clap; extern crate clap;
extern crate thinp;
use atty::Stream; use atty::Stream;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; use std::process;
use std::sync::Arc; 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") 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") .about("Repair binary cache metadata, and write it to a different device or file")
// flags // flags
.arg( .arg(
@ -45,24 +45,19 @@ fn main() {
.required(true), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = Path::new(matches.value_of("OUTPUT").unwrap()); let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
if let Err(e) = file_utils::is_file_or_blk(input_file) { let report = if matches.is_present("QUIET") {
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); std::sync::Arc::new(mk_quiet_report())
process::exit(1);
}
let report;
if matches.is_present("QUIET") {
report = std::sync::Arc::new(mk_quiet_report());
} else if atty::is(Stream::Stdout) { } else if atty::is(Stream::Stdout) {
report = std::sync::Arc::new(mk_progress_bar_report()); std::sync::Arc::new(mk_progress_bar_report())
} else { } else {
report = Arc::new(mk_simple_report()); Arc::new(mk_simple_report())
} };
check_input_file(input_file, &report);
let opts = CacheRepairOptions { let opts = CacheRepairOptions {
input: &input_file, input: &input_file,

View File

@ -1,18 +1,15 @@
extern crate clap; extern crate clap;
extern crate thinp;
use atty::Stream;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; 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") let parser = App::new("cache_restore")
.version(thinp::version::tools_version()) .version(crate::version::tools_version())
.about("Convert XML format metadata to binary.") .about("Convert XML format metadata to binary.")
// flags // flags
.arg( .arg(
@ -45,29 +42,13 @@ fn main() {
.required(true), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = Path::new(matches.value_of("OUTPUT").unwrap()); let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
if let Err(e) = file_utils::is_file(input_file) { let report = mk_report(matches.is_present("QUIET"));
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); check_input_file(input_file, &report);
process::exit(1); check_output_file(output_file, &report);
}
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 opts = CacheRestoreOptions { let opts = CacheRestoreOptions {
input: &input_file, input: &input_file,

12
src/commands/mod.rs Normal file
View 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;

View File

@ -1,19 +1,17 @@
extern crate clap; extern crate clap;
extern crate thinp;
use atty::Stream;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; use std::process;
use std::sync::Arc; 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") 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.") .about("Validates thin provisioning metadata on a device or file.")
// flags // flags
.arg( .arg(
@ -90,23 +88,13 @@ fn main() {
.index(1), .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()); let input_file = Path::new(matches.value_of("INPUT").unwrap());
if let Err(e) = file_utils::is_file_or_blk(input_file) { let report = mk_report(matches.is_present("QUIET"));
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); check_input_file(input_file, &report);
process::exit(1); check_file_not_tiny(input_file, &report);
} check_not_xml(input_file, &report);
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 engine: Arc<dyn IoEngine + Send + Sync>; let engine: Arc<dyn IoEngine + Send + Sync>;
let writable = matches.is_present("AUTO_REPAIR") || matches.is_present("CLEAR_NEEDS_CHECK"); let writable = matches.is_present("AUTO_REPAIR") || matches.is_present("CLEAR_NEEDS_CHECK");

View File

@ -1,19 +1,19 @@
extern crate clap; extern crate clap;
extern crate thinp;
use atty::Stream; use atty::Stream;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; use std::process;
use std::sync::Arc; 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") 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") .about("Dump thin-provisioning metadata to stdout in XML format")
// flags // flags
.arg( .arg(
@ -80,7 +80,7 @@ fn main() {
.index(1), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = if matches.is_present("OUTPUT") { let output_file = if matches.is_present("OUTPUT") {
Some(Path::new(matches.value_of("OUTPUT").unwrap())) Some(Path::new(matches.value_of("OUTPUT").unwrap()))
@ -88,10 +88,8 @@ fn main() {
None None
}; };
if let Err(e) = file_utils::is_file_or_blk(input_file) { let report = std::sync::Arc::new(mk_simple_report());
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); check_input_file(input_file, &report);
process::exit(1);
}
let transaction_id = matches.value_of("TRANSACTION_ID").map(|s| { let transaction_id = matches.value_of("TRANSACTION_ID").map(|s| {
s.parse::<u64>().unwrap_or_else(|_| { s.parse::<u64>().unwrap_or_else(|_| {

View File

@ -1,14 +1,15 @@
extern crate clap; extern crate clap;
extern crate thinp;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process::exit; 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") 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.") .about("Produces a compressed file of thin metadata. Only packs metadata blocks that are actually used.")
.arg(Arg::with_name("INPUT") .arg(Arg::with_name("INPUT")
.help("Specify thinp metadata binary device/file") .help("Specify thinp metadata binary device/file")
@ -23,17 +24,15 @@ fn main() {
.value_name("FILE") .value_name("FILE")
.takes_value(true)); .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = Path::new(matches.value_of("OUTPUT").unwrap()); let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
if let Err(e) = file_utils::is_file_or_blk(input_file) { let report = std::sync::Arc::new(mk_simple_report());
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); check_input_file(input_file, &report);
exit(1);
}
if let Err(reason) = thinp::pack::toplevel::pack(&input_file, &output_file) { if let Err(reason) = crate::pack::toplevel::pack(&input_file, &output_file) {
println!("Application error: {}\n", reason); report.fatal(&format!("Application error: {}\n", reason));
exit(1); exit(1);
} }
} }

View File

@ -1,16 +1,15 @@
extern crate clap; extern crate clap;
extern crate thinp;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; use std::process;
use thinp::file_utils; use crate::file_utils;
use std::process::exit; use std::process::exit;
fn main() { pub fn run(args: &[std::ffi::OsString]) {
let parser = App::new("thin_metadata_unpack") 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.") .about("Unpack a compressed file of thin metadata.")
.arg( .arg(
Arg::with_name("INPUT") Arg::with_name("INPUT")
@ -29,16 +28,16 @@ fn main() {
.takes_value(true), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = Path::new(matches.value_of("OUTPUT").unwrap()); let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
if let Err(e) = file_utils::is_file(input_file) { if !file_utils::is_file(input_file) {
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); eprintln!("Invalid input file '{}'.", input_file.display());
exit(1); 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); eprintln!("Application error: {}", reason);
process::exit(1); process::exit(1);
} }

View File

@ -1,19 +1,16 @@
extern crate clap; extern crate clap;
extern crate thinp;
use atty::Stream;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; 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") 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") .about("Repair thin-provisioning metadata, and write it to different device or file")
// flags // flags
.arg( .arg(
@ -64,46 +61,35 @@ fn main() {
.value_name("NUM"), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = Path::new(matches.value_of("OUTPUT").unwrap()); let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
if let Err(e) = file_utils::is_file_or_blk(input_file) { let report = mk_report(matches.is_present("QUIET"));
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); check_input_file(input_file, &report);
process::exit(1); check_output_file(output_file, &report);
}
let transaction_id = matches.value_of("TRANSACTION_ID").map(|s| { let transaction_id = matches.value_of("TRANSACTION_ID").map(|s| {
s.parse::<u64>().unwrap_or_else(|_| { s.parse::<u64>().unwrap_or_else(|_| {
eprintln!("Couldn't parse transaction_id"); report.fatal("Couldn't parse transaction_id");
process::exit(1); process::exit(1);
}) })
}); });
let data_block_size = matches.value_of("DATA_BLOCK_SIZE").map(|s| { let data_block_size = matches.value_of("DATA_BLOCK_SIZE").map(|s| {
s.parse::<u32>().unwrap_or_else(|_| { s.parse::<u32>().unwrap_or_else(|_| {
eprintln!("Couldn't parse data_block_size"); report.fatal("Couldn't parse data_block_size");
process::exit(1); process::exit(1);
}) })
}); });
let nr_data_blocks = matches.value_of("NR_DATA_BLOCKS").map(|s| { let nr_data_blocks = matches.value_of("NR_DATA_BLOCKS").map(|s| {
s.parse::<u64>().unwrap_or_else(|_| { s.parse::<u64>().unwrap_or_else(|_| {
eprintln!("Couldn't parse nr_data_blocks"); report.fatal("Couldn't parse nr_data_blocks");
process::exit(1); 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 { let opts = ThinRepairOptions {
input: &input_file, input: &input_file,
output: &output_file, output: &output_file,

View File

@ -1,18 +1,15 @@
extern crate clap; extern crate clap;
extern crate thinp;
use atty::Stream;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process; 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") let parser = App::new("thin_restore")
.version(thinp::version::tools_version()) .version(crate::version::tools_version())
.about("Convert XML format metadata to binary.") .about("Convert XML format metadata to binary.")
// flags // flags
.arg( .arg(
@ -45,29 +42,13 @@ fn main() {
.required(true), .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 input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = Path::new(matches.value_of("OUTPUT").unwrap()); let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
if let Err(e) = file_utils::is_file(input_file) { let report = mk_report(matches.is_present("QUIET"));
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); check_input_file(input_file, &report);
process::exit(1); check_output_file(output_file, &report);
}
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 opts = ThinRestoreOptions { let opts = ThinRestoreOptions {
input: &input_file, input: &input_file,

View File

@ -3,16 +3,16 @@
// https://github.com/nkshirsagar/thinpool_shrink/blob/split_ranges/thin_shrink.py // https://github.com/nkshirsagar/thinpool_shrink/blob/split_ranges/thin_shrink.py
extern crate clap; extern crate clap;
extern crate thinp;
use clap::{App, Arg}; use clap::{App, Arg};
use std::path::Path; use std::path::Path;
use std::process::exit; 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") 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.") .about("Rewrite xml metadata and move data in an inactive pool.")
.arg( .arg(
Arg::with_name("INPUT") Arg::with_name("INPUT")
@ -57,7 +57,7 @@ fn main() {
.takes_value(true), .takes_value(true),
); );
let matches = parser.get_matches(); let matches = parser.get_matches_from(args);
// FIXME: check these look like xml // FIXME: check these look like xml
let input_file = Path::new(matches.value_of("INPUT").unwrap()); 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 data_file = Path::new(matches.value_of("DATA").unwrap());
let do_copy = !matches.is_present("NOCOPY"); let do_copy = !matches.is_present("NOCOPY");
if let Err(e) = file_utils::is_file_or_blk(input_file) { let report = mk_report(false);
eprintln!("Invalid input file '{}': {}.", input_file.display(), e); check_input_file(input_file, &report);
exit(1);
}
if let Err(reason) = 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); eprintln!("Application error: {}\n", reason);
exit(1); exit(1);

91
src/commands/utils.rs Normal file
View 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);
}
//---------------------------------------

View File

@ -1,5 +1,5 @@
use nix::sys::stat; use nix::sys::stat;
use nix::sys::stat::FileStat; use nix::sys::stat::{FileStat, SFlag};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io; use std::io;
use std::io::{Seek, Write}; use std::io::{Seek, Write};
@ -9,47 +9,38 @@ use tempfile::tempfile;
//--------------------------------------- //---------------------------------------
#[inline(always)] fn test_bit(mode: u32, flag: SFlag) -> bool {
pub fn s_isreg(info: &FileStat) -> bool { SFlag::from_bits_truncate(mode).contains(flag)
(info.st_mode & stat::SFlag::S_IFMT.bits()) == stat::SFlag::S_IFREG.bits()
} }
#[inline(always)] pub fn is_file_or_blk_(info: FileStat) -> bool {
pub fn s_isblk(info: &FileStat) -> bool { test_bit(info.st_mode, SFlag::S_IFBLK) || test_bit(info.st_mode, SFlag::S_IFREG)
(info.st_mode & stat::SFlag::S_IFMT.bits()) == stat::SFlag::S_IFBLK.bits()
} }
pub fn is_file(path: &Path) -> io::Result<()> { pub fn file_exists(path: &Path) -> bool {
match stat::stat(path) {
Ok(_) => true,
_ => false,
}
}
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) { match stat::stat(path) {
Ok(info) => { Ok(info) => {
if s_isreg(&info) { if test_bit(info.st_mode, SFlag::S_IFREG) {
Ok(()) true
} else { } else {
fail("Not a regular file") false
} }
} }
_ => { _ => false,
// FIXME: assuming all errors indicate the file doesn't
// exist.
fail("No such file or directory")
}
}
}
pub fn is_file_or_blk(path: &Path) -> io::Result<()> {
match stat::stat(path) {
Ok(info) => {
if s_isreg(&info) || s_isblk(&info) {
Ok(())
} else {
fail("Not a block device or regular file")
}
}
_ => {
// FIXME: assuming all errors indicate the file doesn't
// exist.
fail("No such file or directory")
}
} }
} }
@ -79,9 +70,9 @@ fn get_device_size(path: &Path) -> io::Result<u64> {
pub fn file_size(path: &Path) -> io::Result<u64> { pub fn file_size(path: &Path) -> io::Result<u64> {
match stat::stat(path) { match stat::stat(path) {
Ok(info) => { Ok(info) => {
if s_isreg(&info) { if test_bit(info.st_mode, SFlag::S_IFREG) {
Ok(info.st_size as u64) 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) get_device_size(path)
} else { } else {
fail("Not a block device or regular file") 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(())
}
//---------------------------------------

View File

@ -17,6 +17,7 @@ extern crate quickcheck_macros;
pub mod cache; pub mod cache;
pub mod checksum; pub mod checksum;
pub mod commands;
pub mod file_utils; pub mod file_utils;
pub mod io_engine; pub mod io_engine;
pub mod math; pub mod math;

View File

@ -34,6 +34,7 @@ pub trait ReportInner {
fn set_sub_title(&mut self, txt: &str); fn set_sub_title(&mut self, txt: &str);
fn progress(&mut self, percent: u8); fn progress(&mut self, percent: u8);
fn log(&mut self, txt: &str); fn log(&mut self, txt: &str);
fn to_stdout(&mut self, txt: &str);
fn complete(&mut self); fn complete(&mut self);
} }
@ -91,6 +92,13 @@ impl Report {
let outcome = self.outcome.lock().unwrap(); let outcome = self.outcome.lock().unwrap();
outcome.clone() 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); self.bar.println(txt);
} }
fn to_stdout(&mut self, txt: &str) {
println!("{}", txt);
}
fn complete(&mut self) { fn complete(&mut self) {
self.bar.finish(); self.bar.finish();
} }
@ -173,6 +185,10 @@ impl ReportInner for SimpleInner {
eprintln!("{}", txt); eprintln!("{}", txt);
} }
fn to_stdout(&mut self, txt: &str) {
println!("{}", txt);
}
fn complete(&mut self) {} fn complete(&mut self) {}
} }
@ -192,6 +208,7 @@ impl ReportInner for QuietInner {
fn progress(&mut self, _percent: u8) {} fn progress(&mut self, _percent: u8) {}
fn log(&mut self, _txt: &str) {} fn log(&mut self, _txt: &str) {}
fn to_stdout(&mut self, _txt: &str) {}
fn complete(&mut self) {} fn complete(&mut self) {}
} }

View File

@ -218,7 +218,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
// superblock // superblock
let sb = read_superblock(engine.as_ref(), SUPERBLOCK_LOCATION)?; 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 { if opts.sb_only {
return Ok(()); return Ok(());
@ -295,7 +295,7 @@ pub fn check(opts: ThinCheckOptions) -> Result<()> {
report.set_sub_title("metadata space map"); report.set_sub_title("metadata space map");
let root = unpack::<SMRoot>(&sb.metadata_sm_root[0..])?; let root = unpack::<SMRoot>(&sb.metadata_sm_root[0..])?;
report.info(&format!( report.to_stdout(&format!(
"METADATA_FREE_BLOCKS={}", "METADATA_FREE_BLOCKS={}",
root.nr_blocks - root.nr_allocated root.nr_blocks - root.nr_allocated
)); ));

View File

@ -13,16 +13,24 @@ use common::test_dir::*;
//------------------------------------------ //------------------------------------------
const USAGE: &str = "Usage: cache_check [options] {device|file}\n\ const USAGE: &str =
Options:\n \ "cache_check 0.9.0
{-q|--quiet}\n \
{-h|--help}\n \ USAGE:
{-V|--version}\n \ cache_check [FLAGS] <INPUT>
{--clear-needs-check-flag}\n \
{--super-block-only}\n \ FLAGS:
{--skip-mappings}\n \ --auto-repair Auto repair trivial issues.
{--skip-hints}\n \ --ignore-non-fatal-errors Only return a non-zero exit code if a fatal error is found.
{--skip-discards}"; -q, --quiet Suppress output messages, return only exit code.
--super-block-only Only check the superblock.
--skip-discards Don't check the discard bitset
--skip-hints Don't check the hint array
-h, --help Prints help information
-V, --version Prints version information
ARGS:
<INPUT> Specify the input device to check";
//------------------------------------------ //------------------------------------------
@ -33,8 +41,12 @@ impl<'a> Program<'a> for CacheCheck {
"cache_check" "cache_check"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
CACHE_CHECK.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
cache_check_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -91,8 +103,9 @@ test_corrupted_input_data!(CacheCheck);
fn failing_q() -> Result<()> { fn failing_q() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
let output = run_fail_raw(CACHE_CHECK, args!["-q", &md])?; let output = run_fail_raw(cache_check_cmd(args!["-q", &md]))?;
assert_eq!(output.stdout.len(), 0); assert_eq!(output.stdout.len(), 0);
eprintln!("stderr = '{}'", std::str::from_utf8(&output.stderr).unwrap());
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
Ok(()) Ok(())
} }
@ -101,7 +114,7 @@ fn failing_q() -> Result<()> {
fn failing_quiet() -> Result<()> { fn failing_quiet() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
let output = run_fail_raw(CACHE_CHECK, args!["--quiet", &md])?; let output = run_fail_raw(cache_check_cmd(args!["--quiet", &md]))?;
assert_eq!(output.stdout.len(), 0); assert_eq!(output.stdout.len(), 0);
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
Ok(()) Ok(())
@ -111,17 +124,23 @@ fn failing_quiet() -> Result<()> {
fn valid_metadata_passes() -> Result<()> { fn valid_metadata_passes() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
run_ok(CACHE_CHECK, args![&md])?; run_ok(cache_check_cmd(args![&md]))?;
Ok(()) Ok(())
} }
// FIXME: put back in, I don't want to add the --debug- arg to the
// tool again, so we should have a little library function for tweaking
// metadata version.
/*
#[test] #[test]
fn bad_metadata_version() -> Result<()> { fn bad_metadata_version() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
run_ok( run_ok(
CACHE_RESTORE, cache_restore_cmd(
args![ args![
"-i", "-i",
&xml, &xml,
@ -130,7 +149,8 @@ fn bad_metadata_version() -> Result<()> {
"--debug-override-metadata-version", "--debug-override-metadata-version",
"12345" "12345"
], ],
)?; ))?;
run_fail(CACHE_CHECK, args![&md])?; run_fail(cache_check_cmd(args![&md]))?;
Ok(()) Ok(())
} }
*/

View File

@ -15,12 +15,22 @@ use common::test_dir::*;
//------------------------------------------ //------------------------------------------
const USAGE: &str = "Usage: cache_dump [options] {device|file}\n\ const USAGE: &str = "cache_dump 0.9.0
Options:\n \ Dump the cache metadata to stdout in XML format
{-h|--help}\n \
{-o <xml file>}\n \ USAGE:
{-V|--version}\n \ cache_dump [FLAGS] [OPTIONS] <INPUT>
{--repair}";
FLAGS:
-r, --repair Repair the metadata whilst dumping it
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-o, --output <FILE> Specify the output file rather than stdout
ARGS:
<INPUT> Specify the input device to dump";
//------------------------------------------ //------------------------------------------
@ -31,8 +41,12 @@ impl<'a> Program<'a> for CacheDump {
"cache_dump" "cache_dump"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
CACHE_DUMP.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
cache_dump_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -84,7 +98,7 @@ test_unreadable_input_file!(CacheDump);
fn dump_restore_cycle() -> Result<()> { fn dump_restore_cycle() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let output = run_ok_raw(CACHE_DUMP, args![&md])?; let output = run_ok_raw(cache_dump_cmd(args![&md]))?;
let xml = td.mk_path("meta.xml"); let xml = td.mk_path("meta.xml");
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
@ -96,9 +110,9 @@ fn dump_restore_cycle() -> Result<()> {
drop(file); drop(file);
let md2 = mk_zeroed_md(&mut td)?; let md2 = mk_zeroed_md(&mut td)?;
run_ok(CACHE_RESTORE, args!["-i", &xml, "-o", &md2])?; run_ok(cache_restore_cmd(args!["-i", &xml, "-o", &md2]))?;
let output2 = run_ok_raw(CACHE_DUMP, args![&md2])?; let output2 = run_ok_raw(cache_dump_cmd(args![&md2]))?;
assert_eq!(output.stdout, output2.stdout); assert_eq!(output.stdout, output2.stdout);
Ok(()) Ok(())

View File

@ -12,12 +12,21 @@ use common::test_dir::*;
//------------------------------------------ //------------------------------------------
const USAGE: &str = "Usage: cache_repair [options] {device|file}\n\ const USAGE: &str =
Options:\n \ "cache_repair 0.9.0
{-h|--help}\n \ Repair binary cache metadata, and write it to a different device or file
{-i|--input} <input metadata (binary format)>\n \
{-o|--output} <output metadata (binary format)>\n \ USAGE:
{-V|--version}"; cache_repair [FLAGS] --input <FILE> --output <FILE>
FLAGS:
-q, --quiet Suppress output messages, return only exit code.
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-i, --input <FILE> Specify the input device
-o, --output <FILE> Specify the output device";
//----------------------------------------- //-----------------------------------------
@ -28,8 +37,12 @@ impl<'a> Program<'a> for CacheRepair {
"cache_repair" "cache_repair"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
CACHE_REPAIR.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
cache_repair_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {

View File

@ -14,17 +14,21 @@ use common::test_dir::*;
//------------------------------------------ //------------------------------------------
const USAGE: &str = "Usage: cache_restore [options]\n\ const USAGE: &str =
Options:\n \ "cache_restore 0.9.0
{-h|--help}\n \ Convert XML format metadata to binary.
{-i|--input} <input xml file>\n \
{-o|--output} <output device or file>\n \ USAGE:
{-q|--quiet}\n \ cache_restore [FLAGS] --input <FILE> --output <FILE>
{--metadata-version} <1 or 2>\n \
{-V|--version}\n\ FLAGS:
\n \ -q, --quiet Suppress output messages, return only exit code.
{--debug-override-metadata-version} <integer>\n \ -h, --help Prints help information
{--omit-clean-shutdown}"; -V, --version Prints version information
OPTIONS:
-i, --input <FILE> Specify the input xml
-o, --output <FILE> Specify the output device to check";
//------------------------------------------ //------------------------------------------
@ -35,8 +39,12 @@ impl<'a> Program<'a> for CacheRestore {
"thin_restore" "thin_restore"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
CACHE_RESTORE.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
cache_restore_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -105,7 +113,7 @@ fn quiet_flag(flag: &str) -> Result<()> {
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
let output = run_ok_raw(CACHE_RESTORE, args!["-i", &xml, "-o", &md, flag])?; let output = run_ok_raw(cache_restore_cmd(args!["-i", &xml, "-o", &md, flag]))?;
assert_eq!(output.stdout.len(), 0); assert_eq!(output.stdout.len(), 0);
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
@ -129,17 +137,19 @@ fn successfully_restores() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
run_ok(CACHE_RESTORE, args!["-i", &xml, "-o", &md])?; run_ok(cache_restore_cmd(args!["-i", &xml, "-o", &md]))?;
Ok(()) Ok(())
} }
// FIXME: finish
/*
#[test] #[test]
fn override_metadata_version() -> Result<()> { fn override_metadata_version() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
run_ok( run_ok(
CACHE_RESTORE, cache_restore_cmd(
args![ args![
"-i", "-i",
&xml, &xml,
@ -148,20 +158,24 @@ fn override_metadata_version() -> Result<()> {
"--debug-override-metadata-version", "--debug-override-metadata-version",
"10298" "10298"
], ],
)?; ))?;
Ok(()) Ok(())
} }
*/
// FIXME: finish
/*
#[test] #[test]
fn accepts_omit_clean_shutdown() -> Result<()> { fn accepts_omit_clean_shutdown() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
run_ok( run_ok(
CACHE_RESTORE, cache_restore_cmd(
args!["-i", &xml, "-o", &md, "--omit-clean-shutdown"], args!["-i", &xml, "-o", &md, "--omit-clean-shutdown"],
)?; ))?;
Ok(()) Ok(())
} }
*/
//----------------------------------------- //-----------------------------------------

View File

@ -27,7 +27,7 @@ pub fn mk_valid_md(td: &mut TestDir) -> Result<PathBuf> {
write_xml(&xml, &mut gen)?; write_xml(&xml, &mut gen)?;
let _file = file_utils::create_sized_file(&md, 4096 * 4096); let _file = file_utils::create_sized_file(&md, 4096 * 4096);
run_ok(CACHE_RESTORE, args!["-i", &xml, "-o", &md])?; run_ok(cache_restore_cmd(args!["-i", &xml, "-o", &md]))?;
Ok(md) Ok(md)
} }

View File

@ -13,7 +13,7 @@ pub fn test_help_short<'a, P>() -> Result<()>
where where
P: Program<'a>, P: Program<'a>,
{ {
let stdout = run_ok(P::path(), args!["-h"])?; let stdout = run_ok(P::cmd(args!["-h"]))?;
assert_eq!(stdout, P::usage()); assert_eq!(stdout, P::usage());
Ok(()) Ok(())
} }
@ -22,7 +22,7 @@ pub fn test_help_long<'a, P>() -> Result<()>
where where
P: Program<'a>, P: Program<'a>,
{ {
let stdout = run_ok(P::path(), args!["--help"])?; let stdout = run_ok(P::cmd(vec!["--help"]))?;
assert_eq!(stdout, P::usage()); assert_eq!(stdout, P::usage());
Ok(()) Ok(())
} }
@ -49,7 +49,7 @@ pub fn test_version_short<'a, P>() -> Result<()>
where where
P: Program<'a>, P: Program<'a>,
{ {
let stdout = run_ok(P::path(), args!["-V"])?; let stdout = run_ok(P::cmd(args!["-V"]))?;
assert!(stdout.contains(tools_version())); assert!(stdout.contains(tools_version()));
Ok(()) Ok(())
} }
@ -58,7 +58,7 @@ pub fn test_version_long<'a, P>() -> Result<()>
where where
P: Program<'a>, P: Program<'a>,
{ {
let stdout = run_ok(P::path(), args!["--version"])?; let stdout = run_ok(P::cmd(args!["--version"]))?;
assert!(stdout.contains(tools_version())); assert!(stdout.contains(tools_version()));
Ok(()) Ok(())
} }
@ -85,7 +85,7 @@ where
P: Program<'a>, P: Program<'a>,
{ {
let option = "--hedgehogs-only"; let option = "--hedgehogs-only";
let stderr = run_fail(P::path(), args![option])?; let stderr = run_fail(P::cmd(args![option]))?;
assert!(stderr.contains(&P::bad_option_hint(option))); assert!(stderr.contains(&P::bad_option_hint(option)));
Ok(()) Ok(())
} }

View File

@ -63,7 +63,7 @@ where
P: InputProgram<'a>, P: InputProgram<'a>,
{ {
let args: [&str; 0] = []; let args: [&str; 0] = [];
let stderr = run_fail(P::path(), args)?; let stderr = run_fail(P::cmd(args))?;
assert!(stderr.contains(P::missing_input_arg())); assert!(stderr.contains(P::missing_input_arg()));
Ok(()) Ok(())
} }
@ -85,7 +85,7 @@ where
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let output = mk_zeroed_md(&mut td)?; let output = mk_zeroed_md(&mut td)?;
ensure_untouched(&output, || { ensure_untouched(&output, || {
let stderr = run_fail(P::path(), args!["-o", &output])?; let stderr = run_fail(P::cmd(args!["-o", &output]))?;
assert!(stderr.contains(P::missing_input_arg())); assert!(stderr.contains(P::missing_input_arg()));
Ok(()) Ok(())
}) })
@ -109,7 +109,7 @@ where
let wrapper = build_args_fn(P::arg_type())?; let wrapper = build_args_fn(P::arg_type())?;
wrapper(&mut td, "no-such-file".as_ref(), &|args: &[&OsStr]| { wrapper(&mut td, "no-such-file".as_ref(), &|args: &[&OsStr]| {
let stderr = run_fail(P::path(), args)?; let stderr = run_fail(P::cmd(args))?;
assert!(stderr.contains(P::file_not_found())); assert!(stderr.contains(P::file_not_found()));
Ok(()) Ok(())
}) })
@ -133,7 +133,7 @@ where
let wrapper = build_args_fn(P::arg_type())?; let wrapper = build_args_fn(P::arg_type())?;
wrapper(&mut td, "/tmp".as_ref(), &|args: &[&OsStr]| { wrapper(&mut td, "/tmp".as_ref(), &|args: &[&OsStr]| {
let stderr = run_fail(P::path(), args)?; let stderr = run_fail(P::cmd(args))?;
assert!(stderr.contains("Not a block device or regular file")); assert!(stderr.contains("Not a block device or regular file"));
Ok(()) Ok(())
}) })
@ -161,7 +161,7 @@ where
let wrapper = build_args_fn(P::arg_type())?; let wrapper = build_args_fn(P::arg_type())?;
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| { wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
let stderr = run_fail(P::path(), args)?; let stderr = run_fail(P::cmd(args))?;
assert!(stderr.contains("Permission denied")); assert!(stderr.contains("Permission denied"));
Ok(()) Ok(())
}) })
@ -191,7 +191,8 @@ where
let wrapper = build_args_fn(P::arg_type())?; let wrapper = build_args_fn(P::arg_type())?;
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| { wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
let stderr = run_fail(P::path(), args)?; let stderr = run_fail(P::cmd(args))?;
eprintln!("actual: {:?}", stderr);
assert!(stderr.contains("Metadata device/file too small. Is this binary metadata?")); assert!(stderr.contains("Metadata device/file too small. Is this binary metadata?"));
Ok(()) Ok(())
}) })
@ -220,11 +221,9 @@ where
let wrapper = build_args_fn(P::arg_type())?; let wrapper = build_args_fn(P::arg_type())?;
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| { wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
let stderr = run_fail(P::path(), args)?; let stderr = run_fail(P::cmd(args))?;
eprintln!("{}", stderr);
let msg = format!( let msg = format!(
"This looks like XML. {} only checks the binary metadata format.", "This looks like XML. This tool only checks the binary metadata format.",
P::name()
); );
assert!(stderr.contains(&msg)); assert!(stderr.contains(&msg));
Ok(()) Ok(())
@ -253,7 +252,7 @@ where
ArgType::IoOptions => with_output_superblock_zeroed, ArgType::IoOptions => with_output_superblock_zeroed,
}; };
wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| { wrapper(&mut td, input.as_ref(), &|args: &[&OsStr]| {
let stderr = run_fail(P::path(), args)?; let stderr = run_fail(P::cmd(args))?;
assert!(stderr.contains(P::corrupted_input())); assert!(stderr.contains(P::corrupted_input()));
Ok(()) Ok(())
}) })

View File

@ -16,7 +16,7 @@ where
{ {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let input = P::mk_valid_input(&mut td)?; let input = P::mk_valid_input(&mut td)?;
let stderr = run_fail(P::path(), args!["-i", &input])?; let stderr = run_fail(P::cmd(args!["-i", &input]))?;
assert!(stderr.contains(P::missing_output_arg())); assert!(stderr.contains(P::missing_output_arg()));
Ok(()) Ok(())
} }
@ -37,7 +37,9 @@ where
{ {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let input = P::mk_valid_input(&mut td)?; let input = P::mk_valid_input(&mut td)?;
let stderr = run_fail(P::path(), args!["-i", &input, "-o", "no-such-file"])?; let cmd = P::cmd(args!["-i", &input, "-o", "no-such-file"]);
let stderr = run_fail(cmd)?;
assert!(stderr.contains(<P as MetadataWriter>::file_not_found())); assert!(stderr.contains(<P as MetadataWriter>::file_not_found()));
Ok(()) Ok(())
} }
@ -58,7 +60,7 @@ where
{ {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let input = P::mk_valid_input(&mut td)?; let input = P::mk_valid_input(&mut td)?;
let stderr = run_fail(P::path(), args!["-i", &input, "-o", "/tmp"])?; let stderr = run_fail(P::cmd(args!["-i", &input, "-o", "/tmp"]))?;
assert!(stderr.contains("Not a block device or regular file")); assert!(stderr.contains("Not a block device or regular file"));
Ok(()) Ok(())
} }
@ -84,7 +86,7 @@ where
let _file = file_utils::create_sized_file(&output, 4_194_304); let _file = file_utils::create_sized_file(&output, 4_194_304);
duct::cmd!("chmod", "-w", &output).run()?; duct::cmd!("chmod", "-w", &output).run()?;
let stderr = run_fail(P::path(), args!["-i", &input, "-o", &output])?; let stderr = run_fail(P::cmd(args!["-i", &input, "-o", &output]))?;
assert!(stderr.contains("Permission denied")); assert!(stderr.contains("Permission denied"));
Ok(()) Ok(())
} }
@ -113,7 +115,7 @@ where
let output = td.mk_path("meta.bin"); let output = td.mk_path("meta.bin");
let _file = file_utils::create_sized_file(&output, 4096); let _file = file_utils::create_sized_file(&output, 4096);
let stderr = run_fail(P::path(), args!["-i", &input, "-o", &output])?; let stderr = run_fail(P::cmd(args!["-i", &input, "-o", &output]))?;
assert!(stderr.contains("Output file too small")); assert!(stderr.contains("Output file too small"));
Ok(()) Ok(())
} }

View File

@ -1,5 +1,4 @@
use anyhow::Result; use anyhow::Result;
use std::ffi::{OsStr, OsString};
//------------------------------------------ //------------------------------------------
@ -15,49 +14,29 @@ macro_rules! args {
} }
// Returns stdout. The command must return zero. // Returns stdout. The command must return zero.
pub fn run_ok<S, I>(program: S, args: I) -> Result<String> pub fn run_ok(command: duct::Expression) -> Result<String> {
where let command = command.stdout_capture().stderr_capture();
S: AsRef<OsStr>,
I: IntoIterator,
I::Item: Into<OsString>,
{
let command = duct::cmd(program.as_ref(), args)
.stdout_capture()
.stderr_capture();
let output = command.run()?; let output = command.run()?;
assert!(output.status.success()); assert!(output.status.success());
let stdout = std::str::from_utf8(&output.stdout[..]) let stdout = std::str::from_utf8(&output.stdout[..])
.unwrap() .unwrap()
.trim_end_matches(|c| c == '\n' || c == '\r') .trim_end_matches(|c| c == '\n' || c == '\r')
.to_string(); .to_string();
Ok(stdout) Ok(stdout)
} }
// Returns the entire output. The command must return zero. // Returns the entire output. The command must return zero.
pub fn run_ok_raw<S, I>(program: S, args: I) -> Result<std::process::Output> pub fn run_ok_raw(command: duct::Expression) -> Result<std::process::Output> {
where let command = command.stdout_capture().stderr_capture();
S: AsRef<OsStr>,
I: IntoIterator,
I::Item: Into<OsString>,
{
let command = duct::cmd(program.as_ref(), args)
.stdout_capture()
.stderr_capture();
let output = command.run()?; let output = command.run()?;
assert!(output.status.success()); assert!(output.status.success());
Ok(output) Ok(output)
} }
// Returns stderr, a non zero status must be returned // Returns stderr, a non zero status must be returned
pub fn run_fail<S, I>(program: S, args: I) -> Result<String> pub fn run_fail(command: duct::Expression) -> Result<String> {
where let command = command.stdout_capture().stderr_capture();
S: AsRef<OsStr>,
I: IntoIterator,
I::Item: Into<OsString>,
{
let command = duct::cmd(program.as_ref(), args)
.stdout_capture()
.stderr_capture();
let output = command.unchecked().run()?; let output = command.unchecked().run()?;
assert!(!output.status.success()); assert!(!output.status.success());
let stderr = std::str::from_utf8(&output.stderr[..]).unwrap().to_string(); let stderr = std::str::from_utf8(&output.stderr[..]).unwrap().to_string();
@ -65,15 +44,8 @@ where
} }
// Returns the entire output, a non zero status must be returned // Returns the entire output, a non zero status must be returned
pub fn run_fail_raw<S, I>(program: S, args: I) -> Result<std::process::Output> pub fn run_fail_raw(command: duct::Expression) -> Result<std::process::Output> {
where let command = command.stdout_capture().stderr_capture();
S: AsRef<OsStr>,
I: IntoIterator,
I::Item: Into<OsString>,
{
let command = duct::cmd(program.as_ref(), args)
.stdout_capture()
.stderr_capture();
let output = command.unchecked().run()?; let output = command.unchecked().run()?;
assert!(!output.status.success()); assert!(!output.status.success());
Ok(output) Ok(output)

View File

@ -1,5 +1,4 @@
use anyhow::Result; use anyhow::Result;
use std::ffi::OsStr;
use std::path::PathBuf; use std::path::PathBuf;
use crate::common::test_dir::TestDir; use crate::common::test_dir::TestDir;
@ -13,7 +12,10 @@ pub enum ArgType {
pub trait Program<'a> { pub trait Program<'a> {
fn name() -> &'a str; fn name() -> &'a str;
fn path() -> &'a OsStr; fn cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>;
fn usage() -> &'a str; fn usage() -> &'a str;
fn arg_type() -> ArgType; fn arg_type() -> ArgType;

View File

@ -1,69 +1,159 @@
//------------------------------------------ use std::ffi::OsString;
use std::path::PathBuf;
#[macro_export]
macro_rules! path_to_cpp {
($name: literal) => {
concat!("bin/", $name)
};
}
#[macro_export]
macro_rules! path_to_rust {
($name: literal) => {
env!(concat!("CARGO_BIN_EXE_", $name))
};
}
#[cfg(not(feature = "rust_tests"))]
#[macro_export]
macro_rules! path_to {
($name: literal) => {
path_to_cpp!($name)
};
}
#[cfg(feature = "rust_tests")]
#[macro_export]
macro_rules! path_to {
($name: literal) => {
path_to_rust!($name)
};
}
//------------------------------------------ //------------------------------------------
pub const CACHE_CHECK: &str = path_to!("cache_check"); pub fn cpp_cmd<S, I>(cmd: S, args: I) -> duct::Expression
pub const CACHE_DUMP: &str = path_to!("cache_dump"); where
pub const CACHE_REPAIR: &str = path_to!("cache_repair"); S: Into<OsString>,
pub const CACHE_RESTORE: &str = path_to!("cache_restore"); I: IntoIterator,
I::Item: Into<OsString>,
{
let mut bin = PathBuf::from("./bin");
bin.push(Into::<OsString>::into(cmd));
duct::cmd(bin.as_path(), args)
}
pub const THIN_CHECK: &str = path_to!("thin_check"); pub fn rust_cmd<S, I>(cmd: S, args: I) -> duct::Expression
pub const THIN_DELTA: &str = path_to_cpp!("thin_delta"); // TODO: rust version where
pub const THIN_DUMP: &str = path_to!("thin_dump"); S: Into<OsString>,
pub const THIN_METADATA_PACK: &str = path_to_rust!("thin_metadata_pack"); // rust-only I: IntoIterator,
pub const THIN_METADATA_UNPACK: &str = path_to_rust!("thin_metadata_unpack"); // rust-only I::Item: Into<OsString>,
pub const THIN_REPAIR: &str = path_to!("thin_repair"); {
pub const THIN_RESTORE: &str = path_to!("thin_restore"); const RUST_PATH: &str = env!(concat!("CARGO_BIN_EXE_", "pdata_tools"));
pub const THIN_RMAP: &str = path_to_cpp!("thin_rmap"); // TODO: rust version
pub const THIN_GENERATE_METADATA: &str = path_to_cpp!("thin_generate_metadata"); // cpp-only
pub const THIN_GENERATE_MAPPINGS: &str = path_to_cpp!("thin_generate_mappings"); // cpp-only
pub const THIN_GENERATE_DAMAGE: &str = path_to_cpp!("thin_generate_damage"); // cpp-only
//------------------------------------------ let mut all_args = vec![Into::<OsString>::into(cmd)];
for a in args {
pub mod cpp_msg { all_args.push(Into::<OsString>::into(a));
pub const FILE_NOT_FOUND: &str = "No such file or directory";
pub const MISSING_INPUT_ARG: &str = "No input file provided";
pub const MISSING_OUTPUT_ARG: &str = "No output file provided";
pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
pub fn bad_option_hint(option: &str) -> String {
format!("unrecognized option '{}'", option)
} }
duct::cmd(RUST_PATH, &all_args)
} }
pub mod rust_msg { pub fn thin_check_cmd<I>(args: I) -> duct::Expression
pub const FILE_NOT_FOUND: &str = "No such file or directory"; where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("thin_check", args)
}
pub fn thin_rmap_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
cpp_cmd("thin_rmap", args)
}
pub fn thin_generate_metadata_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
cpp_cmd("thin_generate_metadata", args)
}
pub fn thin_generate_mappings_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
cpp_cmd("thin_generate_mappings", args)
}
pub fn thin_generate_damage_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
cpp_cmd("thin_generate_damage", args)
}
pub fn thin_restore_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("thin_restore", args)
}
pub fn thin_repair_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("thin_restore", args)
}
pub fn thin_dump_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("thin_dump", args)
}
pub fn thin_delta_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
cpp_cmd("thin_delta", args)
}
pub fn thin_metadata_pack_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("thin_metadata_pack", args)
}
pub fn thin_metadata_unpack_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("thin_metadata_unpack", args)
}
pub fn cache_check_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("cache_check", args)
}
pub fn cache_dump_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("cache_dump", args)
}
pub fn cache_restore_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("cache_restore", args)
}
pub fn cache_repair_cmd<I>(args: I) -> duct::Expression
where
I: IntoIterator,
I::Item: Into<OsString>,
{
rust_cmd("cache_repair", args)
}
//------------------------------------------
pub mod msg {
pub const FILE_NOT_FOUND: &str = "Couldn't find input file";
pub const MISSING_INPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific pub const MISSING_INPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
pub const MISSING_OUTPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific pub const MISSING_OUTPUT_ARG: &str = "The following required arguments were not provided"; // TODO: be specific
pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock"; pub const BAD_SUPERBLOCK: &str = "bad checksum in superblock";
@ -73,9 +163,4 @@ pub mod rust_msg {
} }
} }
#[cfg(not(feature = "rust_tests"))]
pub use cpp_msg as msg;
#[cfg(feature = "rust_tests")]
pub use rust_msg as msg;
//------------------------------------------ //------------------------------------------

View File

@ -28,7 +28,7 @@ pub fn mk_valid_md(td: &mut TestDir) -> Result<PathBuf> {
write_xml(&xml, &mut gen)?; write_xml(&xml, &mut gen)?;
let _file = file_utils::create_sized_file(&md, 4096 * 4096); let _file = file_utils::create_sized_file(&md, 4096 * 4096);
run_ok(THIN_RESTORE, args!["-i", &xml, "-o", &md])?; run_ok(thin_restore_cmd(args!["-i", &xml, "-o", &md]))?;
Ok(md) Ok(md)
} }
@ -39,11 +39,11 @@ pub fn mk_valid_md(td: &mut TestDir) -> Result<PathBuf> {
pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> { pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> {
let md = mk_zeroed_md(td)?; let md = mk_zeroed_md(td)?;
let args = args!["-o", &md, "--format", "--nr-data-blocks", "102400"]; let args = args!["-o", &md, "--format", "--nr-data-blocks", "102400"];
run_ok(THIN_GENERATE_METADATA, args)?; run_ok(thin_generate_metadata_cmd(args))?;
// Create a 2GB device // Create a 2GB device
let args = args!["-o", &md, "--create-thin", "1"]; let args = args!["-o", &md, "--create-thin", "1"];
run_ok(THIN_GENERATE_METADATA, args)?; run_ok(thin_generate_metadata_cmd(args))?;
let args = args![ let args = args![
"-o", "-o",
&md, &md,
@ -54,7 +54,7 @@ pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> {
"--rw=randwrite", "--rw=randwrite",
"--seq-nr=16" "--seq-nr=16"
]; ];
run_ok(THIN_GENERATE_MAPPINGS, args)?; run_ok(thin_generate_mappings_cmd(args))?;
// Take a few snapshots. // Take a few snapshots.
let mut snap_id = 2; let mut snap_id = 2;
@ -62,7 +62,7 @@ pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> {
// take a snapshot // take a snapshot
let snap_id_str = snap_id.to_string(); let snap_id_str = snap_id.to_string();
let args = args!["-o", &md, "--create-snap", &snap_id_str, "--origin", "1"]; let args = args!["-o", &md, "--create-snap", &snap_id_str, "--origin", "1"];
run_ok(THIN_GENERATE_METADATA, args)?; run_ok(thin_generate_metadata_cmd(args))?;
// partially overwrite the origin (64MB) // partially overwrite the origin (64MB)
let args = args![ let args = args![
@ -77,7 +77,7 @@ pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> {
"--rw=randwrite", "--rw=randwrite",
"--seq-nr=16" "--seq-nr=16"
]; ];
run_ok(THIN_GENERATE_MAPPINGS, args)?; run_ok(thin_generate_mappings_cmd(args))?;
snap_id += 1; snap_id += 1;
} }
@ -86,7 +86,7 @@ pub fn prep_metadata(td: &mut TestDir) -> Result<PathBuf> {
pub fn set_needs_check(md: &PathBuf) -> Result<()> { pub fn set_needs_check(md: &PathBuf) -> Result<()> {
let args = args!["-o", &md, "--set-needs-check"]; let args = args!["-o", &md, "--set-needs-check"];
run_ok(THIN_GENERATE_METADATA, args)?; run_ok(thin_generate_metadata_cmd(args))?;
Ok(()) Ok(())
} }
@ -110,7 +110,7 @@ pub fn generate_metadata_leaks(
"--actual", "--actual",
&actual_str &actual_str
]; ];
run_ok(THIN_GENERATE_DAMAGE, args)?; run_ok(thin_generate_damage_cmd(args))?;
Ok(()) Ok(())
} }

View File

@ -13,18 +13,28 @@ use common::thin::*;
//------------------------------------------ //------------------------------------------
const USAGE: &str = "Usage: thin_check [options] {device|file}\n\ const USAGE: &str = "thin_check 0.9.0
Options:\n \ Validates thin provisioning metadata on a device or file.
{-q|--quiet}\n \
{-h|--help}\n \ USAGE:
{-V|--version}\n \ thin_check [FLAGS] [OPTIONS] <INPUT>
{-m|--metadata-snap}\n \
{--auto-repair}\n \ FLAGS:
{--override-mapping-root}\n \ --auto-repair Auto repair trivial issues.
{--clear-needs-check-flag}\n \ --clear-needs-check-flag Clears the 'needs_check' flag in the superblock
{--ignore-non-fatal-errors}\n \ --ignore-non-fatal-errors Only return a non-zero exit code if a fatal error is found.
{--skip-mappings}\n \ -m, --metadata-snapshot Check the metadata snapshot on a live pool
{--super-block-only}"; -q, --quiet Suppress output messages, return only exit code.
--super-block-only Only check the superblock.
--skip-mappings Don't check the mapping tree
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
--override-mapping-root <OVERRIDE_MAPPING_ROOT> Specify a mapping root to use
ARGS:
<INPUT> Specify the input device to check";
//----------------------------------------- //-----------------------------------------
@ -35,8 +45,12 @@ impl<'a> Program<'a> for ThinCheck {
"thin_check" "thin_check"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_CHECK.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
thin_check_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -93,7 +107,7 @@ test_corrupted_input_data!(ThinCheck);
fn accepts_flag(flag: &str) -> Result<()> { fn accepts_flag(flag: &str) -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
run_ok(THIN_CHECK, args![flag, &md])?; run_ok(thin_check_cmd(args![flag, &md]))?;
Ok(()) Ok(())
} }
@ -130,7 +144,14 @@ fn accepts_quiet() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let output = run_ok_raw(THIN_CHECK, args!["--quiet", &md])?; let output = run_ok_raw(thin_check_cmd(args!["--quiet", &md]))?;
if output.stdout.len() > 0 {
eprintln!("stdout: {:?}", &std::str::from_utf8(&output.stdout));
}
if output.stderr.len() > 0 {
eprintln!("stderr: {:?}", &std::str::from_utf8(&output.stderr));
}
assert_eq!(output.stdout.len(), 0); assert_eq!(output.stdout.len(), 0);
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
Ok(()) Ok(())
@ -143,7 +164,7 @@ fn accepts_quiet() -> Result<()> {
fn detects_corrupt_superblock_with_superblock_only() -> Result<()> { fn detects_corrupt_superblock_with_superblock_only() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
let _stderr = run_fail(THIN_CHECK, args!["--super-block-only", &md])?; let _stderr = run_fail(thin_check_cmd(args!["--super-block-only", &md]))?;
Ok(()) Ok(())
} }
@ -154,7 +175,8 @@ fn detects_corrupt_superblock_with_superblock_only() -> Result<()> {
fn prints_info_fields() -> Result<()> { fn prints_info_fields() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let stdout = run_ok(THIN_CHECK, args![&md])?; let stdout = run_ok(thin_check_cmd(args![&md]))?;
eprintln!("info: {:?}", stdout);
assert!(stdout.contains("TRANSACTION_ID=")); assert!(stdout.contains("TRANSACTION_ID="));
assert!(stdout.contains("METADATA_FREE_BLOCKS=")); assert!(stdout.contains("METADATA_FREE_BLOCKS="));
Ok(()) Ok(())
@ -167,20 +189,28 @@ fn prints_info_fields() -> Result<()> {
fn auto_repair_incompatible_opts() -> Result<()> { fn auto_repair_incompatible_opts() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
run_fail(THIN_CHECK, args!["--auto-repair", "-m", &md])?; run_fail(thin_check_cmd(args!["--auto-repair", "-m", &md]))?;
run_fail( run_fail(thin_check_cmd(args![
THIN_CHECK, "--auto-repair",
args!["--auto-repair", "--override-mapping-root", "123", &md], "--override-mapping-root",
)?; "123",
run_fail( &md
THIN_CHECK, ]))?;
args!["--auto-repair", "--super-block-only", &md], run_fail(thin_check_cmd(args![
)?; "--auto-repair",
run_fail(THIN_CHECK, args!["--auto-repair", "--skip-mappings", &md])?; "--super-block-only",
run_fail( &md
THIN_CHECK, ]))?;
args!["--auto-repair", "--ignore-non-fatal-errors", &md], run_fail(thin_check_cmd(args![
)?; "--auto-repair",
"--skip-mappings",
&md
]))?;
run_fail(thin_check_cmd(args![
"--auto-repair",
"--ignore-non-fatal-errors",
&md
]))?;
Ok(()) Ok(())
} }
@ -188,24 +218,23 @@ fn auto_repair_incompatible_opts() -> Result<()> {
fn clear_needs_check_incompatible_opts() -> Result<()> { fn clear_needs_check_incompatible_opts() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
run_fail(THIN_CHECK, args!["--clear-needs-check-flag", "-m", &md])?; run_fail(thin_check_cmd(args!["--clear-needs-check-flag", "-m", &md]))?;
run_fail( run_fail(thin_check_cmd(args![
THIN_CHECK,
args![
"--clear-needs-check-flag", "--clear-needs-check-flag",
"--override-mapping-root", "--override-mapping-root",
"123", "123",
&md &md
], ]))?;
)?; run_fail(thin_check_cmd(args![
run_fail( "--clear-needs-check-flag",
THIN_CHECK, "--super-block-only",
args!["--clear-needs-check-flag", "--super-block-only", &md], &md
)?; ]))?;
run_fail( run_fail(thin_check_cmd(args![
THIN_CHECK, "--clear-needs-check-flag",
args!["--clear-needs-check-flag", "--ignore-non-fatal-errors", &md], "--ignore-non-fatal-errors",
)?; &md
]))?;
Ok(()) Ok(())
} }
@ -220,7 +249,7 @@ fn clear_needs_check() -> Result<()> {
set_needs_check(&md)?; set_needs_check(&md)?;
assert!(get_needs_check(&md)?); assert!(get_needs_check(&md)?);
run_ok(THIN_CHECK, args!["--clear-needs-check-flag", &md])?; run_ok(thin_check_cmd(args!["--clear-needs-check-flag", &md]))?;
assert!(!get_needs_check(&md)?); assert!(!get_needs_check(&md)?);
Ok(()) Ok(())
} }
@ -232,7 +261,7 @@ fn no_clear_needs_check_if_error() -> Result<()> {
set_needs_check(&md)?; set_needs_check(&md)?;
generate_metadata_leaks(&md, 1, 0, 1)?; generate_metadata_leaks(&md, 1, 0, 1)?;
run_fail(THIN_CHECK, args!["--clear-needs-check-flag", &md])?; run_fail(thin_check_cmd(args!["--clear-needs-check-flag", &md]))?;
assert!(get_needs_check(&md)?); assert!(get_needs_check(&md)?);
Ok(()) Ok(())
} }
@ -246,10 +275,11 @@ fn clear_needs_check_if_skip_mappings() -> Result<()> {
generate_metadata_leaks(&md, 1, 0, 1)?; generate_metadata_leaks(&md, 1, 0, 1)?;
assert!(get_needs_check(&md)?); assert!(get_needs_check(&md)?);
run_ok( run_ok(thin_check_cmd(args![
THIN_CHECK, "--clear-needs-check-flag",
args!["--clear-needs-check-flag", "--skip-mappings", &md], "--skip-mappings",
)?; &md
]))?;
assert!(!get_needs_check(&md)?); assert!(!get_needs_check(&md)?);
Ok(()) Ok(())
} }
@ -262,8 +292,8 @@ fn metadata_leaks_are_non_fatal() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = prep_metadata(&mut td)?; let md = prep_metadata(&mut td)?;
generate_metadata_leaks(&md, 1, 0, 1)?; generate_metadata_leaks(&md, 1, 0, 1)?;
run_fail(THIN_CHECK, args![&md])?; run_fail(thin_check_cmd(args![&md]))?;
run_ok(THIN_CHECK, args!["--ignore-non-fatal-errors", &md])?; run_ok(thin_check_cmd(args!["--ignore-non-fatal-errors", &md]))?;
Ok(()) Ok(())
} }
@ -274,7 +304,7 @@ fn fatal_errors_cant_be_ignored() -> Result<()> {
generate_metadata_leaks(&md, 1, 1, 0)?; generate_metadata_leaks(&md, 1, 1, 0)?;
ensure_untouched(&md, || { ensure_untouched(&md, || {
run_fail(THIN_CHECK, args!["--ignore-non-fatal-errors", &md])?; run_fail(thin_check_cmd(args!["--ignore-non-fatal-errors", &md]))?;
Ok(()) Ok(())
}) })
} }
@ -289,14 +319,14 @@ fn auto_repair() -> Result<()> {
// auto-repair should have no effect on good metadata. // auto-repair should have no effect on good metadata.
ensure_untouched(&md, || { ensure_untouched(&md, || {
run_ok(THIN_CHECK, args!["--auto-repair", &md])?; run_ok(thin_check_cmd(args!["--auto-repair", &md]))?;
Ok(()) Ok(())
})?; })?;
generate_metadata_leaks(&md, 16, 0, 1)?; generate_metadata_leaks(&md, 16, 0, 1)?;
run_fail(THIN_CHECK, args![&md])?; run_fail(thin_check_cmd(args![&md]))?;
run_ok(THIN_CHECK, args!["--auto-repair", &md])?; run_ok(thin_check_cmd(args!["--auto-repair", &md]))?;
run_ok(THIN_CHECK, args![&md])?; run_ok(thin_check_cmd(args![&md]))?;
Ok(()) Ok(())
} }
@ -307,7 +337,7 @@ fn auto_repair_has_limits() -> Result<()> {
generate_metadata_leaks(&md, 16, 1, 0)?; generate_metadata_leaks(&md, 16, 1, 0)?;
ensure_untouched(&md, || { ensure_untouched(&md, || {
run_fail(THIN_CHECK, args!["--auto-repair", &md])?; run_fail(thin_check_cmd(args!["--auto-repair", &md]))?;
Ok(()) Ok(())
})?; })?;
Ok(()) Ok(())
@ -319,7 +349,7 @@ fn auto_repair_clears_needs_check() -> Result<()> {
let md = prep_metadata(&mut td)?; let md = prep_metadata(&mut td)?;
set_needs_check(&md)?; set_needs_check(&md)?;
run_ok(THIN_CHECK, args!["--auto-repair", &md])?; run_ok(thin_check_cmd(args!["--auto-repair", &md]))?;
assert!(!get_needs_check(&md)?); assert!(!get_needs_check(&md)?);
Ok(()) Ok(())
} }

View File

@ -29,8 +29,12 @@ impl<'a> Program<'a> for ThinDelta {
"thin_delta" "thin_delta"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_DELTA.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
rust_cmd("thin_delta", args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -42,7 +46,7 @@ impl<'a> Program<'a> for ThinDelta {
} }
fn bad_option_hint(option: &str) -> String { fn bad_option_hint(option: &str) -> String {
cpp_msg::bad_option_hint(option) msg::bad_option_hint(option)
} }
} }
@ -58,7 +62,7 @@ test_rejects_bad_option!(ThinDelta);
fn snap1_unspecified() -> Result<()> { fn snap1_unspecified() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let stderr = run_fail(THIN_DELTA, args!["--snap2", "45", &md])?; let stderr = run_fail(thin_delta_cmd(args!["--snap2", "45", &md]))?;
assert!(stderr.contains("--snap1 or --root1 not specified")); assert!(stderr.contains("--snap1 or --root1 not specified"));
Ok(()) Ok(())
} }
@ -67,14 +71,14 @@ fn snap1_unspecified() -> Result<()> {
fn snap2_unspecified() -> Result<()> { fn snap2_unspecified() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let stderr = run_fail(THIN_DELTA, args!["--snap1", "45", &md])?; let stderr = run_fail(thin_delta_cmd(args!["--snap1", "45", &md]))?;
assert!(stderr.contains("--snap2 or --root2 not specified")); assert!(stderr.contains("--snap2 or --root2 not specified"));
Ok(()) Ok(())
} }
#[test] #[test]
fn dev_unspecified() -> Result<()> { fn dev_unspecified() -> Result<()> {
let stderr = run_fail(THIN_DELTA, args!["--snap1", "45", "--snap2", "46"])?; let stderr = run_fail(thin_delta_cmd(args!["--snap1", "45", "--snap2", "46"]))?;
// TODO: replace with msg::MISSING_INPUT_ARG once the rust version is ready // TODO: replace with msg::MISSING_INPUT_ARG once the rust version is ready
assert!(stderr.contains("No input file provided")); assert!(stderr.contains("No input file provided"));
Ok(()) Ok(())

View File

@ -35,8 +35,12 @@ impl<'a> Program<'a> for ThinDump {
"thin_dump" "thin_dump"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_DUMP.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
thin_dump_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -89,7 +93,7 @@ fn dump_restore_cycle() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let output = run_ok_raw(THIN_DUMP, args![&md])?; let output = run_ok_raw(thin_dump_cmd(args![&md]))?;
let xml = td.mk_path("meta.xml"); let xml = td.mk_path("meta.xml");
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
@ -101,9 +105,9 @@ fn dump_restore_cycle() -> Result<()> {
drop(file); drop(file);
let md2 = mk_zeroed_md(&mut td)?; let md2 = mk_zeroed_md(&mut td)?;
run_ok(THIN_RESTORE, args!["-i", &xml, "-o", &md2])?; run_ok(thin_restore_cmd(args!["-i", &xml, "-o", &md2]))?;
let output2 = run_ok_raw(THIN_DUMP, args![&md2])?; let output2 = run_ok_raw(thin_dump_cmd(args![&md2]))?;
assert_eq!(output.stdout, output2.stdout); assert_eq!(output.stdout, output2.stdout);
Ok(()) Ok(())
@ -118,7 +122,7 @@ fn no_stderr() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let output = run_ok_raw(THIN_DUMP, args![&md])?; let output = run_ok_raw(thin_dump_cmd(args![&md]))?;
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
Ok(()) Ok(())
@ -132,7 +136,7 @@ fn no_stderr() -> Result<()> {
fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> { fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let output = run_ok_raw(THIN_DUMP, args![&md, flag, value])?; let output = run_ok_raw(thin_dump_cmd(args![&md, flag, value]))?;
if !cfg!(feature = "rust_tests") { if !cfg!(feature = "rust_tests") {
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
@ -164,11 +168,10 @@ fn override_nr_data_blocks() -> Result<()> {
fn repair_superblock() -> Result<()> { fn repair_superblock() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
let before = run_ok_raw(THIN_DUMP, args![&md])?; let before = run_ok_raw(thin_dump_cmd(args![&md]))?;
damage_superblock(&md)?; damage_superblock(&md)?;
let after = run_ok_raw( let after = run_ok_raw(thin_dump_cmd(
THIN_DUMP,
args![ args![
"--repair", "--repair",
"--transaction-id=1", "--transaction-id=1",
@ -176,7 +179,7 @@ fn repair_superblock() -> Result<()> {
"--nr-data-blocks=20480", "--nr-data-blocks=20480",
&md &md
], ],
)?; ))?;
if !cfg!(feature = "rust_tests") { if !cfg!(feature = "rust_tests") {
assert_eq!(after.stderr.len(), 0); assert_eq!(after.stderr.len(), 0);
} }
@ -196,14 +199,14 @@ fn missing_transaction_id() -> Result<()> {
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
damage_superblock(&md)?; damage_superblock(&md)?;
let stderr = run_fail( let stderr = run_fail(
THIN_DUMP, thin_dump_cmd(
args![ args![
"--repair", "--repair",
"--data-block-size=128", "--data-block-size=128",
"--nr-data-blocks=20480", "--nr-data-blocks=20480",
&md &md
], ],
)?; ))?;
assert!(stderr.contains("transaction id")); assert!(stderr.contains("transaction id"));
Ok(()) Ok(())
} }
@ -214,14 +217,14 @@ fn missing_data_block_size() -> Result<()> {
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
damage_superblock(&md)?; damage_superblock(&md)?;
let stderr = run_fail( let stderr = run_fail(
THIN_DUMP, thin_dump_cmd(
args![ args![
"--repair", "--repair",
"--transaction-id=1", "--transaction-id=1",
"--nr-data-blocks=20480", "--nr-data-blocks=20480",
&md &md
], ],
)?; ))?;
assert!(stderr.contains("data block size")); assert!(stderr.contains("data block size"));
Ok(()) Ok(())
} }
@ -233,14 +236,14 @@ fn missing_nr_data_blocks() -> Result<()> {
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
damage_superblock(&md)?; damage_superblock(&md)?;
let stderr = run_fail( let stderr = run_fail(
THIN_DUMP, thin_dump_cmd(
args![ args![
"--repair", "--repair",
"--transaction-id=1", "--transaction-id=1",
"--data-block-size=128", "--data-block-size=128",
&md &md
], ],
)?; ))?;
assert!(stderr.contains("nr data blocks")); assert!(stderr.contains("nr data blocks"));
Ok(()) Ok(())
} }

View File

@ -38,8 +38,12 @@ impl<'a> Program<'a> for ThinMetadataPack {
"thin_metadata_pack" "thin_metadata_pack"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_METADATA_PACK.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
thin_metadata_pack_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -51,7 +55,7 @@ impl<'a> Program<'a> for ThinMetadataPack {
} }
fn bad_option_hint(option: &str) -> String { fn bad_option_hint(option: &str) -> String {
rust_msg::bad_option_hint(option) msg::bad_option_hint(option)
} }
} }
@ -61,21 +65,21 @@ impl<'a> InputProgram<'a> for ThinMetadataPack {
} }
fn file_not_found() -> &'a str { fn file_not_found() -> &'a str {
rust_msg::FILE_NOT_FOUND msg::FILE_NOT_FOUND
} }
fn missing_input_arg() -> &'a str { fn missing_input_arg() -> &'a str {
rust_msg::MISSING_INPUT_ARG msg::MISSING_INPUT_ARG
} }
fn corrupted_input() -> &'a str { fn corrupted_input() -> &'a str {
rust_msg::BAD_SUPERBLOCK msg::BAD_SUPERBLOCK
} }
} }
impl<'a> OutputProgram<'a> for ThinMetadataPack { impl<'a> OutputProgram<'a> for ThinMetadataPack {
fn missing_output_arg() -> &'a str { fn missing_output_arg() -> &'a str {
rust_msg::MISSING_OUTPUT_ARG msg::MISSING_OUTPUT_ARG
} }
} }

View File

@ -40,8 +40,12 @@ impl<'a> Program<'a> for ThinMetadataUnpack {
"thin_metadata_pack" "thin_metadata_pack"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_METADATA_UNPACK.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
thin_metadata_unpack_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -53,7 +57,7 @@ impl<'a> Program<'a> for ThinMetadataUnpack {
} }
fn bad_option_hint(option: &str) -> String { fn bad_option_hint(option: &str) -> String {
rust_msg::bad_option_hint(option) msg::bad_option_hint(option)
} }
} }
@ -63,11 +67,11 @@ impl<'a> InputProgram<'a> for ThinMetadataUnpack {
} }
fn file_not_found() -> &'a str { fn file_not_found() -> &'a str {
rust_msg::FILE_NOT_FOUND msg::FILE_NOT_FOUND
} }
fn missing_input_arg() -> &'a str { fn missing_input_arg() -> &'a str {
rust_msg::MISSING_INPUT_ARG msg::MISSING_INPUT_ARG
} }
fn corrupted_input() -> &'a str { fn corrupted_input() -> &'a str {
@ -77,7 +81,7 @@ impl<'a> InputProgram<'a> for ThinMetadataUnpack {
impl<'a> OutputProgram<'a> for ThinMetadataUnpack { impl<'a> OutputProgram<'a> for ThinMetadataUnpack {
fn missing_output_arg() -> &'a str { fn missing_output_arg() -> &'a str {
rust_msg::MISSING_OUTPUT_ARG msg::MISSING_OUTPUT_ARG
} }
} }
@ -102,14 +106,11 @@ fn end_to_end() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md_in = mk_valid_md(&mut td)?; let md_in = mk_valid_md(&mut td)?;
let md_out = mk_zeroed_md(&mut td)?; let md_out = mk_zeroed_md(&mut td)?;
run_ok(THIN_METADATA_PACK, args!["-i", &md_in, "-o", "meta.pack"])?; run_ok(thin_metadata_pack_cmd(args!["-i", &md_in, "-o", "meta.pack"]))?;
run_ok( run_ok(thin_metadata_unpack_cmd(args!["-i", "meta.pack", "-o", &md_out]))?;
THIN_METADATA_UNPACK,
args!["-i", "meta.pack", "-o", &md_out],
)?;
let dump1 = run_ok(THIN_DUMP, args![&md_in])?; let dump1 = run_ok(thin_dump_cmd(args![&md_in]))?;
let dump2 = run_ok(THIN_DUMP, args![&md_out])?; let dump2 = run_ok(thin_dump_cmd(args![&md_out]))?;
assert_eq!(dump1, dump2); assert_eq!(dump1, dump2);
Ok(()) Ok(())
} }

View File

@ -33,8 +33,12 @@ impl<'a> Program<'a> for ThinRepair {
"thin_repair" "thin_repair"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_REPAIR.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
thin_repair_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -108,7 +112,7 @@ fn dont_repair_xml() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
run_fail(THIN_REPAIR, args!["-i", &xml, "-o", &md])?; run_fail(thin_repair_cmd(args!["-i", &xml, "-o", &md]))?;
Ok(()) Ok(())
} }
@ -121,9 +125,9 @@ fn override_thing(flag: &str, val: &str, pattern: &str) -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md1 = mk_valid_md(&mut td)?; let md1 = mk_valid_md(&mut td)?;
let md2 = mk_zeroed_md(&mut td)?; let md2 = mk_zeroed_md(&mut td)?;
let output = run_ok_raw(THIN_REPAIR, args![flag, val, "-i", &md1, "-o", &md2])?; let output = run_ok_raw(thin_repair_cmd(args![flag, val, "-i", &md1, "-o", &md2]))?;
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
let output = run_ok(THIN_DUMP, args![&md2])?; let output = run_ok(thin_dump_cmd(args![&md2]))?;
assert!(output.contains(pattern)); assert!(output.contains(pattern));
Ok(()) Ok(())
} }
@ -151,14 +155,14 @@ fn override_nr_data_blocks() -> Result<()> {
fn superblock_succeeds() -> Result<()> { fn superblock_succeeds() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md1 = mk_valid_md(&mut td)?; let md1 = mk_valid_md(&mut td)?;
let original = run_ok_raw(THIN_DUMP, args![&md1])?; let original = run_ok_raw(thin_dump_cmd(args![&md1]))?;
if !cfg!(feature = "rust_tests") { if !cfg!(feature = "rust_tests") {
assert_eq!(original.stderr.len(), 0); assert_eq!(original.stderr.len(), 0);
} }
damage_superblock(&md1)?; damage_superblock(&md1)?;
let md2 = mk_zeroed_md(&mut td)?; let md2 = mk_zeroed_md(&mut td)?;
run_ok( run_ok(
THIN_REPAIR, thin_repair_cmd(
args![ args![
"--transaction-id=1", "--transaction-id=1",
"--data-block-size=128", "--data-block-size=128",
@ -168,8 +172,8 @@ fn superblock_succeeds() -> Result<()> {
"-o", "-o",
&md2 &md2
], ],
)?; ))?;
let repaired = run_ok_raw(THIN_DUMP, args![&md2])?; let repaired = run_ok_raw(thin_dump_cmd(args![&md2]))?;
if !cfg!(feature = "rust_tests") { if !cfg!(feature = "rust_tests") {
assert_eq!(repaired.stderr.len(), 0); assert_eq!(repaired.stderr.len(), 0);
} }
@ -186,7 +190,7 @@ fn missing_thing(flag1: &str, flag2: &str, pattern: &str) -> Result<()> {
let md1 = mk_valid_md(&mut td)?; let md1 = mk_valid_md(&mut td)?;
damage_superblock(&md1)?; damage_superblock(&md1)?;
let md2 = mk_zeroed_md(&mut td)?; let md2 = mk_zeroed_md(&mut td)?;
let stderr = run_fail(THIN_REPAIR, args![flag1, flag2, "-i", &md1, "-o", &md2])?; let stderr = run_fail(thin_repair_cmd(args![flag1, flag2, "-i", &md1, "-o", &md2]))?;
assert!(stderr.contains(pattern)); assert!(stderr.contains(pattern));
Ok(()) Ok(())
} }

View File

@ -34,8 +34,12 @@ impl<'a> Program<'a> for ThinRestore {
"thin_restore" "thin_restore"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_RESTORE.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
thin_restore_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -104,7 +108,7 @@ fn quiet_flag(flag: &str) -> Result<()> {
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
let output = run_ok_raw(THIN_RESTORE, args!["-i", &xml, "-o", &md, flag])?; let output = run_ok_raw(thin_restore_cmd(args!["-i", &xml, "-o", &md, flag]))?;
assert_eq!(output.stdout.len(), 0); assert_eq!(output.stdout.len(), 0);
assert_eq!(output.stderr.len(), 0); assert_eq!(output.stderr.len(), 0);
@ -130,9 +134,9 @@ fn override_something(flag: &str, value: &str, pattern: &str) -> Result<()> {
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
let md = mk_zeroed_md(&mut td)?; let md = mk_zeroed_md(&mut td)?;
run_ok(THIN_RESTORE, args!["-i", &xml, "-o", &md, flag, value])?; run_ok(thin_restore_cmd(args!["-i", &xml, "-o", &md, flag, value]))?;
let output = run_ok(THIN_DUMP, args![&md])?; let output = run_ok(thin_dump_cmd(args![&md]))?;
assert!(output.contains(pattern)); assert!(output.contains(pattern));
Ok(()) Ok(())
} }

View File

@ -29,8 +29,12 @@ impl<'a> Program<'a> for ThinRmap {
"thin_rmap" "thin_rmap"
} }
fn path() -> &'a std::ffi::OsStr { fn cmd<I>(args: I) -> duct::Expression
THIN_RMAP.as_ref() where
I: IntoIterator,
I::Item: Into<std::ffi::OsString>,
{
thin_rmap_cmd(args)
} }
fn usage() -> &'a str { fn usage() -> &'a str {
@ -42,7 +46,7 @@ impl<'a> Program<'a> for ThinRmap {
} }
fn bad_option_hint(option: &str) -> String { fn bad_option_hint(option: &str) -> String {
cpp_msg::bad_option_hint(option) msg::bad_option_hint(option)
} }
} }
@ -58,7 +62,7 @@ test_rejects_bad_option!(ThinRmap);
fn valid_region_format_should_pass() -> Result<()> { fn valid_region_format_should_pass() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
run_ok(THIN_RMAP, args!["--region", "23..7890", &md])?; run_ok(thin_rmap_cmd(args!["--region", "23..7890", &md]))?;
Ok(()) Ok(())
} }
@ -77,7 +81,7 @@ fn invalid_regions_should_fail() -> Result<()> {
for r in &invalid_regions { for r in &invalid_regions {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
run_fail(THIN_RMAP, args![r, &md])?; run_fail(thin_rmap_cmd(args![r, &md]))?;
} }
Ok(()) Ok(())
} }
@ -86,10 +90,9 @@ fn invalid_regions_should_fail() -> Result<()> {
fn multiple_regions_should_pass() -> Result<()> { fn multiple_regions_should_pass() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?; let md = mk_valid_md(&mut td)?;
run_ok( run_ok(thin_rmap_cmd(args![
THIN_RMAP, "--region", "1..23", "--region", "45..78", &md
args!["--region", "1..23", "--region", "45..78", &md], ]))?;
)?;
Ok(()) Ok(())
} }
@ -97,7 +100,7 @@ fn multiple_regions_should_pass() -> Result<()> {
fn junk_input() -> Result<()> { fn junk_input() -> Result<()> {
let mut td = TestDir::new()?; let mut td = TestDir::new()?;
let xml = mk_valid_xml(&mut td)?; let xml = mk_valid_xml(&mut td)?;
run_fail(THIN_RMAP, args!["--region", "0..-1", &xml])?; run_fail(thin_rmap_cmd(args!["--region", "0..-1", &xml]))?;
Ok(()) Ok(())
} }