device-mapper-persistent-data/SOURCES/0009-commands-Fix-version-s...

1293 lines
47 KiB
Diff

From 255429f9323f68006dc0233a0668a39200342b2c Mon Sep 17 00:00:00 2001
From: Ming-Hung Tsai <mtsai@redhat.com>
Date: Wed, 21 Feb 2024 15:25:53 +0800
Subject: [PATCH 09/10] [commands] Fix version string compatibility issue with
LVM
Make the displayed version string backward-compatible to LVM's
_check_tool_version() parser, which is used to check cache_check's
version while activating a cache device (issue #294).
(cherry picked from commit 3cb749b91e5ed0dbd284ddd15b08998898f6d802)
---
src/commands/cache_check.rs | 5 ++-
src/commands/cache_dump.rs | 5 ++-
src/commands/cache_generate_metadata.rs | 5 ++-
src/commands/cache_metadata_size.rs | 9 ++++-
src/commands/cache_repair.rs | 5 ++-
src/commands/cache_restore.rs | 5 ++-
src/commands/cache_writeback.rs | 5 ++-
src/commands/era_check.rs | 5 ++-
src/commands/era_dump.rs | 5 ++-
src/commands/era_generate_metadata.rs | 5 ++-
src/commands/era_invalidate.rs | 5 ++-
src/commands/era_repair.rs | 5 ++-
src/commands/era_restore.rs | 5 ++-
src/commands/thin_check.rs | 5 ++-
src/commands/thin_delta.rs | 49 +++++++++++++++++--------
src/commands/thin_dump.rs | 5 ++-
src/commands/thin_explore.rs | 3 ++
src/commands/thin_generate_damage.rs | 5 ++-
src/commands/thin_generate_metadata.rs | 5 ++-
src/commands/thin_ls.rs | 5 ++-
src/commands/thin_metadata_pack.rs | 9 ++++-
src/commands/thin_metadata_size.rs | 9 ++++-
src/commands/thin_metadata_unpack.rs | 5 ++-
src/commands/thin_repair.rs | 5 ++-
src/commands/thin_restore.rs | 5 ++-
src/commands/thin_rmap.rs | 5 ++-
src/commands/thin_shrink.rs | 9 ++++-
src/commands/thin_stat.rs | 5 ++-
src/commands/thin_trim.rs | 5 ++-
src/version.rs | 32 ++++++++++++++++
tests/common/common_args.rs | 4 +-
tests/thin_delta.rs | 12 ++----
32 files changed, 194 insertions(+), 57 deletions(-)
diff --git a/src/commands/cache_check.rs b/src/commands/cache_check.rs
index dd86d492..3af29b8c 100644
--- a/src/commands/cache_check.rs
+++ b/src/commands/cache_check.rs
@@ -8,6 +8,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::*;
+use crate::version::*;
//------------------------------------------
@@ -18,6 +19,7 @@ impl CacheCheckCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Validates cache metadata on a device or file.")
// flags
.arg(
@@ -76,7 +78,7 @@ impl CacheCheckCommand {
.required(true)
.index(1),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -87,6 +89,7 @@ impl<'a> Command<'a> for CacheCheckCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
diff --git a/src/commands/cache_dump.rs b/src/commands/cache_dump.rs
index f087536d..43126495 100644
--- a/src/commands/cache_dump.rs
+++ b/src/commands/cache_dump.rs
@@ -7,6 +7,7 @@ use crate::cache::dump::{dump, CacheDumpOptions};
use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
+use crate::version::*;
//------------------------------------------
@@ -17,6 +18,7 @@ impl CacheDumpCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Dump the cache metadata to stdout in XML format")
.arg(
Arg::new("REPAIR")
@@ -40,7 +42,7 @@ impl CacheDumpCommand {
.required(true)
.index(1),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -51,6 +53,7 @@ impl<'a> Command<'a> for CacheDumpCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = matches.get_one::<String>("OUTPUT").map(Path::new);
diff --git a/src/commands/cache_generate_metadata.rs b/src/commands/cache_generate_metadata.rs
index 449dfad4..0818e46b 100644
--- a/src/commands/cache_generate_metadata.rs
+++ b/src/commands/cache_generate_metadata.rs
@@ -8,6 +8,7 @@ use crate::cache::metadata_generator::*;
use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
+use crate::version::*;
//------------------------------------------
@@ -18,6 +19,7 @@ impl CacheGenerateMetadataCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("A tool for creating synthetic cache metadata.")
// flags
.arg(
@@ -128,7 +130,7 @@ impl CacheGenerateMetadataCommand {
])
.required(true),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -139,6 +141,7 @@ impl<'a> Command<'a> for CacheGenerateMetadataCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/cache_metadata_size.rs b/src/commands/cache_metadata_size.rs
index 8189e8cf..0083f44b 100644
--- a/src/commands/cache_metadata_size.rs
+++ b/src/commands/cache_metadata_size.rs
@@ -11,6 +11,7 @@ use crate::commands::Command;
use crate::math::div_up;
use crate::report::mk_simple_report;
use crate::units::*;
+use crate::version::*;
//------------------------------------------
@@ -40,9 +41,10 @@ pub struct CacheMetadataSizeCommand;
impl CacheMetadataSizeCommand {
fn cli(&self) -> clap::Command {
- clap::Command::new(self.name())
+ let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Estimate the size of the metadata device needed for a given configuration.")
.override_usage("cache_metadata_size [OPTIONS] <--device-size <SIZE> --block-size <SIZE> | --nr-blocks <NUM>>")
// flags
@@ -98,7 +100,9 @@ impl CacheMetadataSizeCommand {
.value_name("UNIT")
.value_parser(value_parser!(Units))
.default_value("sector"),
- )
+ );
+
+ version_args(cmd)
}
fn parse_args<I, T>(&self, args: I) -> Result<(CacheMetadataSizeOptions, Units, OutputFormat)>
@@ -107,6 +111,7 @@ impl CacheMetadataSizeCommand {
T: Into<OsString> + Clone,
{
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let nr_blocks = matches.get_one::<u64>("NR_BLOCKS");
let device_size = matches.get_one::<StorageSize>("DEVICE_SIZE");
diff --git a/src/commands/cache_repair.rs b/src/commands/cache_repair.rs
index 2098548e..658e096c 100644
--- a/src/commands/cache_repair.rs
+++ b/src/commands/cache_repair.rs
@@ -8,6 +8,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::*;
+use crate::version::*;
pub struct CacheRepairCommand;
@@ -16,6 +17,7 @@ impl CacheRepairCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Repair binary cache metadata, and write it to a different device or file")
.arg(
Arg::new("QUIET")
@@ -44,7 +46,7 @@ impl CacheRepairCommand {
// a dummy argument for compatibility with lvconvert
.arg(Arg::new("DUMMY").required(false).hide(true).index(1));
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -55,6 +57,7 @@ impl<'a> Command<'a> for CacheRepairCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let report = mk_report(matches.get_flag("QUIET"));
let log_level = match parse_log_level(&matches) {
diff --git a/src/commands/cache_restore.rs b/src/commands/cache_restore.rs
index 609d2a10..b88d9c92 100644
--- a/src/commands/cache_restore.rs
+++ b/src/commands/cache_restore.rs
@@ -9,6 +9,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::{parse_log_level, verbose_args};
+use crate::version::*;
pub struct CacheRestoreCommand;
@@ -17,6 +18,7 @@ impl CacheRestoreCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Convert XML format metadata to binary.")
.arg(
Arg::new("QUIET")
@@ -58,7 +60,7 @@ impl CacheRestoreCommand {
.value_name("FILE")
.required(true),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -69,6 +71,7 @@ impl<'a> Command<'a> for CacheRestoreCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/cache_writeback.rs b/src/commands/cache_writeback.rs
index 3e7be9a2..875188a3 100644
--- a/src/commands/cache_writeback.rs
+++ b/src/commands/cache_writeback.rs
@@ -7,6 +7,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::{parse_log_level, verbose_args};
+use crate::version::*;
pub struct CacheWritebackCommand;
@@ -15,6 +16,7 @@ impl CacheWritebackCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Repair binary cache metadata, and write it to a different device or file")
.arg(
Arg::new("QUIET")
@@ -88,7 +90,7 @@ impl CacheWritebackCommand {
.value_parser(value_parser!(u32))
.default_value("0"),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -99,6 +101,7 @@ impl<'a> Command<'a> for CacheWritebackCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let metadata_dev = Path::new(matches.get_one::<String>("METADATA_DEV").unwrap());
let origin_dev = Path::new(matches.get_one::<String>("ORIGIN_DEV").unwrap());
diff --git a/src/commands/era_check.rs b/src/commands/era_check.rs
index 7ee03ded..fefd772b 100644
--- a/src/commands/era_check.rs
+++ b/src/commands/era_check.rs
@@ -8,6 +8,7 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::era::check::{check, EraCheckOptions};
use crate::report::*;
+use crate::version::*;
//------------------------------------------
@@ -18,6 +19,7 @@ impl EraCheckCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Validate era metadata on device or file.")
// flags
.arg(
@@ -46,7 +48,7 @@ impl EraCheckCommand {
.required(true)
.index(1),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -57,6 +59,7 @@ impl<'a> Command<'a> for EraCheckCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
diff --git a/src/commands/era_dump.rs b/src/commands/era_dump.rs
index 35100b05..e0f9919e 100644
--- a/src/commands/era_dump.rs
+++ b/src/commands/era_dump.rs
@@ -7,6 +7,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::era::dump::{dump, EraDumpOptions};
+use crate::version::*;
//------------------------------------------
@@ -17,6 +18,7 @@ impl EraDumpCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Dump the era metadata to stdout in XML format")
.arg(
Arg::new("LOGICAL")
@@ -46,7 +48,7 @@ impl EraDumpCommand {
.required(true)
.index(1),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -57,6 +59,7 @@ impl<'a> Command<'a> for EraDumpCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = matches.get_one::<String>("OUTPUT").map(Path::new);
diff --git a/src/commands/era_generate_metadata.rs b/src/commands/era_generate_metadata.rs
index 58c46f25..9028c5da 100644
--- a/src/commands/era_generate_metadata.rs
+++ b/src/commands/era_generate_metadata.rs
@@ -7,6 +7,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::era::metadata_generator::*;
+use crate::version::*;
//------------------------------------------
@@ -17,6 +18,7 @@ impl EraGenerateMetadataCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("A tool for creating synthetic era metadata.")
// flags
.arg(
@@ -67,7 +69,7 @@ impl EraGenerateMetadataCommand {
.required(true),
)
.group(ArgGroup::new("commands").args(["FORMAT"]).required(true));
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -78,6 +80,7 @@ impl<'a> Command<'a> for EraGenerateMetadataCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/era_invalidate.rs b/src/commands/era_invalidate.rs
index cf5c14f9..58032112 100644
--- a/src/commands/era_invalidate.rs
+++ b/src/commands/era_invalidate.rs
@@ -5,6 +5,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::era::invalidate::{invalidate, EraInvalidateOptions};
+use crate::version::*;
//------------------------------------------
@@ -15,6 +16,7 @@ impl EraInvalidateCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("List blocks that may have changed since a given era")
.arg(
Arg::new("METADATA_SNAPSHOT")
@@ -44,7 +46,7 @@ impl EraInvalidateCommand {
.required(true)
.index(1),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -55,6 +57,7 @@ impl<'a> Command<'a> for EraInvalidateCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = matches.get_one::<String>("OUTPUT").map(Path::new);
diff --git a/src/commands/era_repair.rs b/src/commands/era_repair.rs
index e4f095c8..825c37d8 100644
--- a/src/commands/era_repair.rs
+++ b/src/commands/era_repair.rs
@@ -8,6 +8,7 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::era::repair::{repair, EraRepairOptions};
use crate::report::*;
+use crate::version::*;
pub struct EraRepairCommand;
@@ -16,6 +17,7 @@ impl EraRepairCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Repair binary era metadata, and write it to a different device or file")
.arg(
Arg::new("QUIET")
@@ -41,7 +43,7 @@ impl EraRepairCommand {
.value_name("FILE")
.required(true),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -52,6 +54,7 @@ impl<'a> Command<'a> for EraRepairCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/era_restore.rs b/src/commands/era_restore.rs
index 9c1184be..b779ac98 100644
--- a/src/commands/era_restore.rs
+++ b/src/commands/era_restore.rs
@@ -8,6 +8,7 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::era::restore::{restore, EraRestoreOptions};
use crate::report::{parse_log_level, verbose_args};
+use crate::version::*;
pub struct EraRestoreCommand;
@@ -16,6 +17,7 @@ impl EraRestoreCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Convert XML format metadata to binary.")
// flags
.arg(
@@ -42,7 +44,7 @@ impl EraRestoreCommand {
.value_name("FILE")
.required(true),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -53,6 +55,7 @@ impl<'a> Command<'a> for EraRestoreCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/thin_check.rs b/src/commands/thin_check.rs
index c505c821..f8eeafc4 100644
--- a/src/commands/thin_check.rs
+++ b/src/commands/thin_check.rs
@@ -6,6 +6,7 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::{parse_log_level, verbose_args};
use crate::thin::check::{check, ThinCheckOptions};
+use crate::version::*;
pub struct ThinCheckCommand;
@@ -14,6 +15,7 @@ impl ThinCheckCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Validates thin provisioning metadata on a device or file.")
// flags
.arg(
@@ -85,7 +87,7 @@ impl ThinCheckCommand {
.required(true)
.index(1),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -96,6 +98,7 @@ impl<'a> Command<'a> for ThinCheckCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
diff --git a/src/commands/thin_delta.rs b/src/commands/thin_delta.rs
index 0d4371ac..9d50d641 100644
--- a/src/commands/thin_delta.rs
+++ b/src/commands/thin_delta.rs
@@ -9,6 +9,7 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::thin::delta::*;
use crate::thin::delta_visitor::Snap;
+use crate::version::*;
//------------------------------------------
@@ -19,6 +20,7 @@ impl ThinDeltaCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Print the differences in the mappings between two thin devices")
.arg(
Arg::new("METADATA_SNAPSHOT")
@@ -72,17 +74,15 @@ impl ThinDeltaCommand {
.index(1),
)
// groups
- .group(
- ArgGroup::new("SNAP1")
- .args(["ROOT1", "THIN1"])
- .required(true),
- )
- .group(
- ArgGroup::new("SNAP2")
- .args(["ROOT2", "THIN2"])
- .required(true),
- );
- engine_args(cmd)
+ //
+ // Due to a bug in clap 4.4 that doesn't handle exclusive Args
+ // and ArgGroup::required(true) properly, we avoid using the
+ // 'required(true)' flag on ArgGroup, and perform manual checks
+ // instead. (see github clap-rs/clap#5041)
+ .group(ArgGroup::new("SNAP1").args(["ROOT1", "THIN1"]))
+ .group(ArgGroup::new("SNAP2").args(["ROOT2", "THIN2"]));
+
+ engine_args(version_args(cmd))
}
}
@@ -93,6 +93,7 @@ impl<'a> Command<'a> for ThinDeltaCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
@@ -102,16 +103,34 @@ impl<'a> Command<'a> for ThinDeltaCommand {
return to_exit_code::<()>(&report, Err(e));
}
- let snap1 = match matches.get_one::<clap::Id>("SNAP1").unwrap().as_str() {
+ let snap1 = match matches
+ .get_one::<clap::Id>("SNAP1")
+ .unwrap_or(&clap::Id::default())
+ .as_str()
+ {
"THIN1" => Snap::DeviceId(*matches.get_one::<u64>("THIN1").unwrap()),
"ROOT1" => Snap::RootBlock(*matches.get_one::<u64>("ROOT1").unwrap()),
- _ => return to_exit_code::<()>(&report, Err(anyhow!("unknown option"))),
+ _ => {
+ return to_exit_code::<()>(
+ &report,
+ Err(anyhow!("--thin1 or --root1 not specified")),
+ )
+ }
};
- let snap2 = match matches.get_one::<clap::Id>("SNAP2").unwrap().as_str() {
+ let snap2 = match matches
+ .get_one::<clap::Id>("SNAP2")
+ .unwrap_or(&clap::Id::default())
+ .as_str()
+ {
"THIN2" => Snap::DeviceId(*matches.get_one::<u64>("THIN2").unwrap()),
"ROOT2" => Snap::RootBlock(*matches.get_one::<u64>("ROOT2").unwrap()),
- _ => return to_exit_code::<()>(&report, Err(anyhow!("unknown option"))),
+ _ => {
+ return to_exit_code::<()>(
+ &report,
+ Err(anyhow!("--thin2 or --root2 not specified")),
+ )
+ }
};
let engine_opts = parse_engine_opts(ToolType::Thin, &matches);
diff --git a/src/commands/thin_dump.rs b/src/commands/thin_dump.rs
index 96cd8e87..d876066f 100644
--- a/src/commands/thin_dump.rs
+++ b/src/commands/thin_dump.rs
@@ -10,6 +10,7 @@ use crate::commands::Command;
use crate::report::*;
use crate::thin::dump::{dump, OutputFormat, ThinDumpOptions};
use crate::thin::metadata_repair::SuperblockOverrides;
+use crate::version::*;
pub struct ThinDumpCommand;
@@ -18,6 +19,7 @@ impl ThinDumpCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Dump thin-provisioning metadata to stdout in XML format")
.arg(
Arg::new("QUIET")
@@ -108,7 +110,7 @@ impl ThinDumpCommand {
.required(true)
.index(1),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -119,6 +121,7 @@ impl<'a> Command<'a> for ThinDumpCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = matches.get_one::<String>("OUTPUT").map(Path::new);
diff --git a/src/commands/thin_explore.rs b/src/commands/thin_explore.rs
index 79ca6ff2..5b6c7fac 100644
--- a/src/commands/thin_explore.rs
+++ b/src/commands/thin_explore.rs
@@ -39,6 +39,7 @@ use crate::pdata::unpack::*;
use crate::thin::block_time::*;
use crate::thin::device_detail::*;
use crate::thin::superblock::*;
+use crate::version::*;
//------------------------------------
@@ -879,6 +880,7 @@ impl ThinExploreCommand {
clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("A text user interface for examining thin metadata.")
.arg(
Arg::new("NODE_PATH")
@@ -903,6 +905,7 @@ impl<'a> Command<'a> for ThinExploreCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let node_path = matches
.get_one::<String>("NODE_PATH")
diff --git a/src/commands/thin_generate_damage.rs b/src/commands/thin_generate_damage.rs
index f6d9f47c..080a0325 100644
--- a/src/commands/thin_generate_damage.rs
+++ b/src/commands/thin_generate_damage.rs
@@ -5,6 +5,7 @@ use std::process;
use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::thin::damage_generator::*;
+use crate::version::*;
//------------------------------------------
use crate::commands::Command;
@@ -16,6 +17,7 @@ impl ThinGenerateDamageCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("A tool for creating synthetic thin metadata.")
.arg(
Arg::new("CREATE_METADATA_LEAKS")
@@ -92,7 +94,7 @@ impl ThinGenerateDamageCommand {
.args(["MAPPING_ROOT", "DETAILS_ROOT", "METADATA_SNAPSHOT"])
.multiple(true),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -103,6 +105,7 @@ impl<'a> Command<'a> for ThinGenerateDamageCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let report = mk_report(false);
diff --git a/src/commands/thin_generate_metadata.rs b/src/commands/thin_generate_metadata.rs
index 14a59a01..7e445d28 100644
--- a/src/commands/thin_generate_metadata.rs
+++ b/src/commands/thin_generate_metadata.rs
@@ -5,6 +5,7 @@ use std::process;
use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::thin::metadata_generator::*;
+use crate::version::*;
//------------------------------------------
use crate::commands::Command;
@@ -16,6 +17,7 @@ impl ThinGenerateMetadataCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("A tool for creating synthetic thin metadata.")
// flags
.arg(
@@ -64,7 +66,7 @@ impl ThinGenerateMetadataCommand {
.args(["FORMAT", "SET_NEEDS_CHECK"])
.required(true),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -75,6 +77,7 @@ impl<'a> Command<'a> for ThinGenerateMetadataCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let report = mk_report(false);
diff --git a/src/commands/thin_ls.rs b/src/commands/thin_ls.rs
index 7d778d31..20dcd582 100644
--- a/src/commands/thin_ls.rs
+++ b/src/commands/thin_ls.rs
@@ -8,6 +8,7 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::{parse_log_level, verbose_args};
use crate::thin::ls::*;
+use crate::version::*;
pub struct ThinLsCommand;
@@ -16,6 +17,7 @@ impl ThinLsCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("List thin volumes within a pool")
.arg(
Arg::new("NO_HEADERS")
@@ -47,7 +49,7 @@ impl ThinLsCommand {
.required(true)
.index(1),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -60,6 +62,7 @@ impl<'a> Command<'a> for ThinLsCommand {
use OutputField::*;
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
diff --git a/src/commands/thin_metadata_pack.rs b/src/commands/thin_metadata_pack.rs
index 265439c9..be0c7585 100644
--- a/src/commands/thin_metadata_pack.rs
+++ b/src/commands/thin_metadata_pack.rs
@@ -6,14 +6,16 @@ use std::path::Path;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::*;
+use crate::version::*;
pub struct ThinMetadataPackCommand;
impl ThinMetadataPackCommand {
fn cli(&self) -> clap::Command {
- clap::Command::new(self.name())
+ let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Produces a compressed file of thin metadata. Only packs metadata blocks that are actually used.")
// flags
.arg(Arg::new("FORCE")
@@ -33,7 +35,9 @@ impl ThinMetadataPackCommand {
.required(true)
.short('o')
.long("output")
- .value_name("FILE"))
+ .value_name("FILE"));
+
+ version_args(cmd)
}
}
@@ -44,6 +48,7 @@ impl<'a> Command<'a> for ThinMetadataPackCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/thin_metadata_size.rs b/src/commands/thin_metadata_size.rs
index 86f04b3a..b1c18678 100644
--- a/src/commands/thin_metadata_size.rs
+++ b/src/commands/thin_metadata_size.rs
@@ -10,6 +10,7 @@ use crate::commands::Command;
use crate::report::mk_simple_report;
use crate::thin::metadata_size::*;
use crate::units::*;
+use crate::version::*;
//------------------------------------------
@@ -39,9 +40,10 @@ pub struct ThinMetadataSizeCommand;
impl ThinMetadataSizeCommand {
fn cli(&self) -> clap::Command {
- clap::Command::new(self.name())
+ let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Estimate the size of the metadata device needed for a given configuration.")
// flags
.arg(
@@ -94,7 +96,9 @@ impl ThinMetadataSizeCommand {
.value_name("UNIT")
.value_parser(value_parser!(Units))
.default_value("sector"),
- )
+ );
+
+ version_args(cmd)
}
fn parse_args<I, T>(&self, args: I) -> Result<(ThinMetadataSizeOptions, Units, OutputFormat)>
@@ -103,6 +107,7 @@ impl ThinMetadataSizeCommand {
T: Into<OsString> + Clone,
{
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let pool_size = matches
.get_one::<StorageSize>("POOL_SIZE")
diff --git a/src/commands/thin_metadata_unpack.rs b/src/commands/thin_metadata_unpack.rs
index 7e383ce3..6fbb8437 100644
--- a/src/commands/thin_metadata_unpack.rs
+++ b/src/commands/thin_metadata_unpack.rs
@@ -8,6 +8,7 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::pack::toplevel::unpack;
use crate::report::mk_simple_report;
+use crate::version::*;
pub struct ThinMetadataUnpackCommand;
@@ -16,6 +17,7 @@ impl ThinMetadataUnpackCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Unpack a compressed file of thin metadata.")
// flags
.arg(
@@ -40,7 +42,7 @@ impl ThinMetadataUnpackCommand {
.short('o')
.value_name("DEV"),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -51,6 +53,7 @@ impl<'a> Command<'a> for ThinMetadataUnpackCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/thin_repair.rs b/src/commands/thin_repair.rs
index 6f4384cc..5abc5e36 100644
--- a/src/commands/thin_repair.rs
+++ b/src/commands/thin_repair.rs
@@ -9,6 +9,7 @@ use crate::commands::Command;
use crate::report::{parse_log_level, verbose_args};
use crate::thin::metadata_repair::SuperblockOverrides;
use crate::thin::repair::{repair, ThinRepairOptions};
+use crate::version::*;
pub struct ThinRepairCommand;
@@ -17,6 +18,7 @@ impl ThinRepairCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Repair thin-provisioning metadata, and write it to different device or file")
.arg(
Arg::new("QUIET")
@@ -66,7 +68,7 @@ impl ThinRepairCommand {
// a dummy argument for compatibility with lvconvert
.arg(Arg::new("DUMMY").required(false).hide(true).index(1));
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -77,6 +79,7 @@ impl<'a> Command<'a> for ThinRepairCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/thin_restore.rs b/src/commands/thin_restore.rs
index feab577a..43becfcb 100644
--- a/src/commands/thin_restore.rs
+++ b/src/commands/thin_restore.rs
@@ -9,6 +9,7 @@ use crate::commands::Command;
use crate::report::{parse_log_level, verbose_args};
use crate::thin::metadata_repair::SuperblockOverrides;
use crate::thin::restore::{restore, ThinRestoreOptions};
+use crate::version::*;
pub struct ThinRestoreCommand;
@@ -17,6 +18,7 @@ impl ThinRestoreCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Convert XML format metadata to binary.")
.arg(
Arg::new("QUIET")
@@ -63,7 +65,7 @@ impl ThinRestoreCommand {
.value_name("NUM")
.value_parser(value_parser!(u64)),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -74,6 +76,7 @@ impl<'a> Command<'a> for ThinRestoreCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/thin_rmap.rs b/src/commands/thin_rmap.rs
index fef2e36a..109a8a0f 100644
--- a/src/commands/thin_rmap.rs
+++ b/src/commands/thin_rmap.rs
@@ -8,6 +8,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::thin::rmap::*;
+use crate::version::*;
//------------------------------------------
@@ -18,6 +19,7 @@ impl ThinRmapCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Output reverse map of a thin provisioned region of blocks")
// options
.arg(
@@ -39,7 +41,7 @@ impl ThinRmapCommand {
.required(true)
.index(1),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -50,6 +52,7 @@ impl<'a> Command<'a> for ThinRmapCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
let report = mk_report(false);
diff --git a/src/commands/thin_shrink.rs b/src/commands/thin_shrink.rs
index 38783d41..772024cd 100644
--- a/src/commands/thin_shrink.rs
+++ b/src/commands/thin_shrink.rs
@@ -13,14 +13,16 @@ use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::*;
use crate::thin::shrink::{shrink, ThinShrinkOptions};
+use crate::version::*;
pub struct ThinShrinkCommand;
impl ThinShrinkCommand {
fn cli(&self) -> clap::Command {
- clap::Command::new(self.name())
+ let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Rewrite xml metadata and move data in an inactive pool.")
.arg(
Arg::new("INPUT")
@@ -64,7 +66,9 @@ impl ThinShrinkCommand {
.help("Perform binary metadata rebuild rather than XML rewrite")
.long("binary")
.action(ArgAction::SetTrue),
- )
+ );
+
+ version_args(cmd)
}
fn parse_args<I, T>(&self, args: I) -> io::Result<ThinShrinkOptions>
@@ -73,6 +77,7 @@ impl ThinShrinkCommand {
T: Into<ffi::OsString> + Clone,
{
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let input = Path::new(matches.get_one::<String>("INPUT").unwrap());
let output = Path::new(matches.get_one::<String>("OUTPUT").unwrap());
diff --git a/src/commands/thin_stat.rs b/src/commands/thin_stat.rs
index e62e7e8f..62ecb9c6 100644
--- a/src/commands/thin_stat.rs
+++ b/src/commands/thin_stat.rs
@@ -5,6 +5,7 @@ use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::report::mk_simple_report;
use crate::thin::stat::*;
+use crate::version::*;
//------------------------------------------
use crate::commands::Command;
@@ -16,6 +17,7 @@ impl ThinStatCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Tool to show metadata statistics")
.arg(
Arg::new("OP")
@@ -30,7 +32,7 @@ impl ThinStatCommand {
.index(1),
);
- engine_args(cmd)
+ engine_args(version_args(cmd))
}
}
@@ -41,6 +43,7 @@ impl<'a> Command<'a> for ThinStatCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let report = std::sync::Arc::new(mk_simple_report());
let engine_opts = parse_engine_opts(ToolType::Thin, &matches);
diff --git a/src/commands/thin_trim.rs b/src/commands/thin_trim.rs
index 9e742e15..79df88dc 100644
--- a/src/commands/thin_trim.rs
+++ b/src/commands/thin_trim.rs
@@ -8,6 +8,7 @@ use crate::commands::utils::*;
use crate::report::{parse_log_level, verbose_args};
use crate::thin::check::{check, ThinCheckOptions};
use crate::thin::trim::{trim, ThinTrimOptions};
+use crate::version::*;
//------------------------------------------
use crate::commands::Command;
@@ -19,6 +20,7 @@ impl ThinTrimCommand {
let cmd = clap::Command::new(self.name())
.next_display_order(None)
.version(crate::tools_version!())
+ .disable_version_flag(true)
.about("Issue discard requests for free pool space (offline tool).")
.arg(
Arg::new("QUIET")
@@ -42,7 +44,7 @@ impl ThinTrimCommand {
.value_name("FILE")
.required(true),
);
- verbose_args(engine_args(cmd))
+ verbose_args(engine_args(version_args(cmd)))
}
}
@@ -53,6 +55,7 @@ impl<'a> Command<'a> for ThinTrimCommand {
fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
let matches = self.cli().get_matches_from(args);
+ display_version(&matches);
let metadata_dev = Path::new(matches.get_one::<String>("METADATA_DEV").unwrap());
let data_dev = Path::new(matches.get_one::<String>("DATA_DEV").unwrap());
diff --git a/src/version.rs b/src/version.rs
index af094ef4..bf4e8dda 100644
--- a/src/version.rs
+++ b/src/version.rs
@@ -1,6 +1,38 @@
+use clap::ArgMatches;
+use std::io::Write;
+
+//------------------------------------------
+
#[macro_export]
macro_rules! tools_version {
() => {
env!("CARGO_PKG_VERSION")
};
}
+
+pub fn version_args(cmd: clap::Command) -> clap::Command {
+ use clap::Arg;
+
+ cmd.arg(
+ Arg::new("VERSION")
+ .help("Print version")
+ .short('V')
+ .long("version")
+ .exclusive(true)
+ .action(clap::ArgAction::SetTrue),
+ )
+}
+
+pub fn display_version(matches: &ArgMatches) {
+ if matches.get_flag("VERSION") {
+ let mut stdout = std::io::stdout();
+ // ignore broken pipe errors
+ let _ = stdout.write_all(tools_version!().as_bytes());
+ let _ = stdout.write_all(b"\n");
+ let _ = stdout.flush();
+
+ std::process::exit(0);
+ }
+}
+
+//------------------------------------------
diff --git a/tests/common/common_args.rs b/tests/common/common_args.rs
index b2da8ead..c5286749 100644
--- a/tests/common/common_args.rs
+++ b/tests/common/common_args.rs
@@ -50,7 +50,7 @@ where
P: Program<'a>,
{
let stdout = run_ok(P::cmd(args!["-V"]))?;
- assert!(stdout.contains(tools_version!()));
+ assert!(stdout.starts_with(tools_version!()));
Ok(())
}
@@ -59,7 +59,7 @@ where
P: Program<'a>,
{
let stdout = run_ok(P::cmd(args!["--version"]))?;
- assert!(stdout.contains(tools_version!()));
+ assert!(stdout.starts_with(tools_version!()));
Ok(())
}
diff --git a/tests/thin_delta.rs b/tests/thin_delta.rs
index 0732038a..13556bf4 100644
--- a/tests/thin_delta.rs
+++ b/tests/thin_delta.rs
@@ -13,7 +13,7 @@ use common::thin::*;
const USAGE: &str = "Print the differences in the mappings between two thin devices
-Usage: thin_delta [OPTIONS] <--root1 <BLOCKNR>|--thin1 <DEV_ID>> <--root2 <BLOCKNR>|--thin2 <DEV_ID>> <INPUT>
+Usage: thin_delta [OPTIONS] <INPUT>
Arguments:
<INPUT> Specify the input device
@@ -71,10 +71,7 @@ fn snap1_unspecified() -> Result<()> {
let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?;
let stderr = run_fail(thin_delta_cmd(args!["--snap2", "45", &md]))?;
- assert!(stderr.contains(
- "the following required arguments were not provided:
- <--root1 <BLOCKNR>|--thin1 <DEV_ID>>"
- ));
+ assert!(stderr.contains("--thin1 or --root1 not specified"));
Ok(())
}
@@ -83,10 +80,7 @@ fn snap2_unspecified() -> Result<()> {
let mut td = TestDir::new()?;
let md = mk_valid_md(&mut td)?;
let stderr = run_fail(thin_delta_cmd(args!["--snap1", "45", &md]))?;
- assert!(stderr.contains(
- "the following required arguments were not provided:
- <--root2 <BLOCKNR>|--thin2 <DEV_ID>>"
- ));
+ assert!(stderr.contains("--thin2 or --root2 not specified"));
Ok(())
}
--
2.43.0