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

View File

@@ -1,105 +0,0 @@
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::check::{check, CacheCheckOptions};
use thinp::file_utils;
use thinp::report::*;
//------------------------------------------
fn main() {
let parser = App::new("cache_check")
.version(thinp::version::tools_version())
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("AUTO_REPAIR")
.help("Auto repair trivial issues.")
.long("auto-repair"),
)
.arg(
Arg::with_name("IGNORE_NON_FATAL")
.help("Only return a non-zero exit code if a fatal error is found.")
.long("ignore-non-fatal-errors"),
)
.arg(
Arg::with_name("QUIET")
.help("Suppress output messages, return only exit code.")
.short("q")
.long("quiet"),
)
.arg(
Arg::with_name("SB_ONLY")
.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")
.long("skip-hints"),
)
.arg(
Arg::with_name("SKIP_DISCARDS")
.help("Don't check the discard bitset")
.long("skip-discards"),
)
// arguments
.arg(
Arg::with_name("INPUT")
.help("Specify the input device to check")
.required(true)
.index(1),
);
let matches = parser.get_matches();
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 opts = CacheCheckOptions {
dev: &input_file,
async_io: matches.is_present("ASYNC_IO"),
sb_only: matches.is_present("SB_ONLY"),
skip_mappings: matches.is_present("SKIP_MAPPINGS"),
skip_hints: matches.is_present("SKIP_HINTS"),
skip_discards: matches.is_present("SKIP_DISCARDS"),
ignore_non_fatal: matches.is_present("IGNORE_NON_FATAL"),
auto_repair: matches.is_present("AUTO_REPAIR"),
report: report.clone(),
};
if let Err(reason) = check(opts) {
report.fatal(&format!("{}", reason));
process::exit(1);
}
}
//------------------------------------------

View File

@@ -1,71 +0,0 @@
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;
//------------------------------------------
fn main() {
let parser = App::new("cache_dump")
.version(thinp::version::tools_version())
.about("Dump the cache metadata to stdout in XML format")
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("REPAIR")
.help("Repair the metadata whilst dumping it")
.short("r")
.long("repair"),
)
// options
.arg(
Arg::with_name("OUTPUT")
.help("Specify the output file rather than stdout")
.short("o")
.long("output")
.value_name("FILE"),
)
// arguments
.arg(
Arg::with_name("INPUT")
.help("Specify the input device to dump")
.required(true)
.index(1),
);
let matches = parser.get_matches();
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()))
} else {
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 opts = CacheDumpOptions {
input: input_file,
output: output_file,
async_io: matches.is_present("ASYNC_IO"),
repair: matches.is_present("REPAIR"),
};
if let Err(reason) = dump(opts) {
eprintln!("{}", reason);
process::exit(1);
}
}
//------------------------------------------

View File

@@ -1,78 +0,0 @@
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() {
let parser = App::new("cache_repair")
.version(thinp::version::tools_version())
.about("Repair binary cache metadata, and write it to a different device or file")
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("QUIET")
.help("Suppress output messages, return only exit code.")
.short("q")
.long("quiet"),
)
// options
.arg(
Arg::with_name("INPUT")
.help("Specify the input device")
.short("i")
.long("input")
.value_name("FILE")
.required(true),
)
.arg(
Arg::with_name("OUTPUT")
.help("Specify the output device")
.short("o")
.long("output")
.value_name("FILE")
.required(true),
);
let matches = parser.get_matches();
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());
} else if atty::is(Stream::Stdout) {
report = std::sync::Arc::new(mk_progress_bar_report());
} else {
report = Arc::new(mk_simple_report());
}
let opts = CacheRepairOptions {
input: &input_file,
output: &output_file,
async_io: matches.is_present("ASYNC_IO"),
report: report.clone(),
};
if let Err(reason) = repair(opts) {
report.fatal(&format!("{}", reason));
process::exit(1);
}
}

View File

@@ -1,83 +0,0 @@
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() {
let parser = App::new("cache_restore")
.version(thinp::version::tools_version())
.about("Convert XML format metadata to binary.")
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("QUIET")
.help("Suppress output messages, return only exit code.")
.short("q")
.long("quiet"),
)
// options
.arg(
Arg::with_name("INPUT")
.help("Specify the input xml")
.short("i")
.long("input")
.value_name("FILE")
.required(true),
)
.arg(
Arg::with_name("OUTPUT")
.help("Specify the output device to check")
.short("o")
.long("output")
.value_name("FILE")
.required(true),
);
let matches = parser.get_matches();
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 opts = CacheRestoreOptions {
input: &input_file,
output: &output_file,
async_io: matches.is_present("ASYNC_IO"),
report: report.clone(),
};
if let Err(reason) = restore(opts) {
report.fatal(&format!("{}", reason));
process::exit(1);
}
}

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)
}

View File

@@ -1,141 +0,0 @@
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() {
let parser = App::new("thin_check")
.version(thinp::version::tools_version())
.about("Validates thin provisioning metadata on a device or file.")
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("AUTO_REPAIR")
.help("Auto repair trivial issues.")
.long("auto-repair")
.conflicts_with_all(&[
"IGNORE_NON_FATAL",
"METADATA_SNAPSHOT",
"OVERRIDE_MAPPING_ROOT",
"SB_ONLY",
"SKIP_MAPPINGS",
]),
)
.arg(
// Using --clear-needs-check along with --skip-mappings is allowed
// (but not recommended) for backward compatibility (commit 1fe8a0d)
Arg::with_name("CLEAR_NEEDS_CHECK")
.help("Clears the 'needs_check' flag in the superblock")
.long("clear-needs-check-flag")
.conflicts_with_all(&[
"IGNORE_NON_FATAL",
"METADATA_SNAPSHOT",
"OVERRIDE_MAPPING_ROOT",
"SB_ONLY",
]),
)
.arg(
Arg::with_name("IGNORE_NON_FATAL")
.help("Only return a non-zero exit code if a fatal error is found.")
.long("ignore-non-fatal-errors"),
)
.arg(
Arg::with_name("METADATA_SNAPSHOT")
.help("Check the metadata snapshot on a live pool")
.short("m")
.long("metadata-snapshot"),
)
.arg(
Arg::with_name("QUIET")
.help("Suppress output messages, return only exit code.")
.short("q")
.long("quiet"),
)
.arg(
Arg::with_name("SB_ONLY")
.help("Only check the superblock.")
.long("super-block-only"),
)
.arg(
Arg::with_name("SKIP_MAPPINGS")
.help("Don't check the mapping tree")
.long("skip-mappings"),
)
// options
.arg(
Arg::with_name("OVERRIDE_MAPPING_ROOT")
.help("Specify a mapping root to use")
.long("override-mapping-root")
.value_name("OVERRIDE_MAPPING_ROOT")
.takes_value(true),
)
// arguments
.arg(
Arg::with_name("INPUT")
.help("Specify the input device to check")
.required(true)
.index(1),
);
let matches = parser.get_matches();
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 engine: Arc<dyn IoEngine + Send + Sync>;
let writable = matches.is_present("AUTO_REPAIR") || matches.is_present("CLEAR_NEEDS_CHECK");
if matches.is_present("ASYNC_IO") {
engine = Arc::new(
AsyncIoEngine::new(&input_file, MAX_CONCURRENT_IO, writable)
.expect("unable to open input file"),
);
} else {
let nr_threads = std::cmp::max(8, num_cpus::get() * 2);
engine = Arc::new(
SyncIoEngine::new(&input_file, nr_threads, writable)
.expect("unable to open input file"),
);
}
let opts = ThinCheckOptions {
engine,
sb_only: matches.is_present("SB_ONLY"),
skip_mappings: matches.is_present("SKIP_MAPPINGS"),
ignore_non_fatal: matches.is_present("IGNORE_NON_FATAL"),
auto_repair: matches.is_present("AUTO_REPAIR"),
clear_needs_check: matches.is_present("CLEAR_NEEDS_CHECK"),
report: report.clone(),
};
if let Err(reason) = check(opts) {
report.fatal(&format!("{}", reason));
process::exit(1);
}
}

View File

@@ -1,144 +0,0 @@
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() {
let parser = App::new("thin_dump")
.version(thinp::version::tools_version())
.about("Dump thin-provisioning metadata to stdout in XML format")
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("QUIET")
.help("Suppress output messages, return only exit code.")
.short("q")
.long("quiet"),
)
.arg(
Arg::with_name("REPAIR")
.help("Repair the metadata whilst dumping it")
.short("r")
.long("repair"),
)
.arg(
Arg::with_name("SKIP_MAPPINGS")
.help("Do not dump the mappings")
.long("skip-mappings"),
)
// options
.arg(
Arg::with_name("DATA_BLOCK_SIZE")
.help("Provide the data block size for repairing")
.long("data-block-size")
.value_name("SECTORS"),
)
.arg(
Arg::with_name("METADATA_SNAPSHOT")
.help("Access the metadata snapshot on a live pool")
.short("m")
.long("metadata-snapshot")
.value_name("METADATA_SNAPSHOT"),
)
.arg(
Arg::with_name("NR_DATA_BLOCKS")
.help("Override the number of data blocks if needed")
.long("nr-data-blocks")
.value_name("NUM"),
)
.arg(
Arg::with_name("OUTPUT")
.help("Specify the output file rather than stdout")
.short("o")
.long("output")
.value_name("FILE"),
)
.arg(
Arg::with_name("TRANSACTION_ID")
.help("Override the transaction id if needed")
.long("transaction-id")
.value_name("NUM"),
)
// arguments
.arg(
Arg::with_name("INPUT")
.help("Specify the input device to dump")
.required(true)
.index(1),
);
let matches = parser.get_matches();
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()))
} else {
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 transaction_id = matches.value_of("TRANSACTION_ID").map(|s| {
s.parse::<u64>().unwrap_or_else(|_| {
eprintln!("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");
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");
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 = ThinDumpOptions {
input: input_file,
output: output_file,
async_io: matches.is_present("ASYNC_IO"),
report: report.clone(),
repair: matches.is_present("REPAIR"),
overrides: SuperblockOverrides {
transaction_id,
data_block_size,
nr_data_blocks,
},
};
if let Err(reason) = dump(opts) {
report.fatal(&format!("{}", reason));
process::exit(1);
}
}

View File

@@ -1,39 +0,0 @@
extern crate clap;
extern crate thinp;
use clap::{App, Arg};
use std::path::Path;
use std::process::exit;
use thinp::file_utils;
fn main() {
let parser = App::new("thin_metadata_pack")
.version(thinp::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")
.required(true)
.short("i")
.value_name("DEV")
.takes_value(true))
.arg(Arg::with_name("OUTPUT")
.help("Specify packed output file")
.required(true)
.short("o")
.value_name("FILE")
.takes_value(true));
let matches = parser.get_matches();
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);
}
if let Err(reason) = thinp::pack::toplevel::pack(&input_file, &output_file) {
println!("Application error: {}\n", reason);
exit(1);
}
}

View File

@@ -1,45 +0,0 @@
extern crate clap;
extern crate thinp;
use clap::{App, Arg};
use std::path::Path;
use std::process;
use thinp::file_utils;
use std::process::exit;
fn main() {
let parser = App::new("thin_metadata_unpack")
.version(thinp::version::tools_version())
.about("Unpack a compressed file of thin metadata.")
.arg(
Arg::with_name("INPUT")
.help("Specify thinp metadata binary device/file")
.required(true)
.short("i")
.value_name("DEV")
.takes_value(true),
)
.arg(
Arg::with_name("OUTPUT")
.help("Specify packed output file")
.required(true)
.short("o")
.value_name("FILE")
.takes_value(true),
);
let matches = parser.get_matches();
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);
exit(1);
}
if let Err(reason) = thinp::pack::toplevel::unpack(&input_file, &output_file) {
eprintln!("Application error: {}", reason);
process::exit(1);
}
}

View File

@@ -1,123 +0,0 @@
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() {
let parser = App::new("thin_repair")
.version(thinp::version::tools_version())
.about("Repair thin-provisioning metadata, and write it to different device or file")
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("QUIET")
.help("Suppress output messages, return only exit code.")
.short("q")
.long("quiet"),
)
// options
.arg(
Arg::with_name("DATA_BLOCK_SIZE")
.help("Provide the data block size for repairing")
.long("data-block-size")
.value_name("SECTORS"),
)
.arg(
Arg::with_name("INPUT")
.help("Specify the input device")
.short("i")
.long("input")
.value_name("FILE")
.required(true),
)
.arg(
Arg::with_name("NR_DATA_BLOCKS")
.help("Override the number of data blocks if needed")
.long("nr-data-blocks")
.value_name("NUM"),
)
.arg(
Arg::with_name("OUTPUT")
.help("Specify the output device")
.short("o")
.long("output")
.value_name("FILE")
.required(true),
)
.arg(
Arg::with_name("TRANSACTION_ID")
.help("Override the transaction id if needed")
.long("transaction-id")
.value_name("NUM"),
);
let matches = parser.get_matches();
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 transaction_id = matches.value_of("TRANSACTION_ID").map(|s| {
s.parse::<u64>().unwrap_or_else(|_| {
eprintln!("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");
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");
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,
async_io: matches.is_present("ASYNC_IO"),
report: report.clone(),
overrides: SuperblockOverrides {
transaction_id,
data_block_size,
nr_data_blocks,
},
};
if let Err(reason) = repair(opts) {
report.fatal(&format!("{}", reason));
process::exit(1);
}
}

View File

@@ -1,83 +0,0 @@
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() {
let parser = App::new("thin_restore")
.version(thinp::version::tools_version())
.about("Convert XML format metadata to binary.")
// flags
.arg(
Arg::with_name("ASYNC_IO")
.help("Force use of io_uring for synchronous io")
.long("async-io")
.hidden(true),
)
.arg(
Arg::with_name("QUIET")
.help("Suppress output messages, return only exit code.")
.short("q")
.long("quiet"),
)
// options
.arg(
Arg::with_name("INPUT")
.help("Specify the input xml")
.short("i")
.long("input")
.value_name("FILE")
.required(true),
)
.arg(
Arg::with_name("OUTPUT")
.help("Specify the output device")
.short("o")
.long("output")
.value_name("FILE")
.required(true),
);
let matches = parser.get_matches();
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 opts = ThinRestoreOptions {
input: &input_file,
output: &output_file,
async_io: matches.is_present("ASYNC_IO"),
report: report.clone(),
};
if let Err(reason) = restore(opts) {
report.fatal(&format!("{}", reason));
process::exit(1);
}
}

View File

@@ -1,80 +0,0 @@
// This work is based on the implementation by Nikhil Kshirsagar which
// can be found here:
// 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() {
let parser = App::new("thin_shrink")
.version(thinp::version::tools_version())
.about("Rewrite xml metadata and move data in an inactive pool.")
.arg(
Arg::with_name("INPUT")
.help("Specify thinp metadata xml file")
.required(true)
.short("i")
.long("input")
.value_name("FILE")
.takes_value(true),
)
.arg(
Arg::with_name("OUTPUT")
.help("Specify output xml file")
.required(true)
.short("o")
.long("output")
.value_name("FILE")
.takes_value(true),
)
.arg(
Arg::with_name("DATA")
.help("Specify pool data device where data will be moved")
.required(true)
.long("data")
.value_name("DATA")
.takes_value(true),
)
.arg(
Arg::with_name("NOCOPY")
.help("Skip the copying of data, useful for benchmarking")
.required(false)
.long("no-copy")
.value_name("NOCOPY")
.takes_value(false),
)
.arg(
Arg::with_name("SIZE")
.help("Specify new size for the pool (in data blocks)")
.required(true)
.long("nr-blocks")
.value_name("SIZE")
.takes_value(true),
);
let matches = parser.get_matches();
// FIXME: check these look like xml
let input_file = Path::new(matches.value_of("INPUT").unwrap());
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
let size = matches.value_of("SIZE").unwrap().parse::<u64>().unwrap();
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);
}
if let Err(reason) =
thinp::shrink::toplevel::shrink(&input_file, &output_file, &data_file, size, do_copy)
{
eprintln!("Application error: {}\n", reason);
exit(1);
}
}