pvimg: info: Provide two JSON output variants: pretty and minify

Add two JSON output variants: pretty and minify. The desired variant can
be selected via '--format json:pretty' and '--format json:minify'. Using
'--format json' without a variant defaults to pretty.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Marc Hartmayer
2026-02-09 16:30:07 +01:00
committed by Jan Höppner
parent 48c8fc8321
commit 012025595f
6 changed files with 173 additions and 27 deletions
+135 -14
View File
@@ -2,9 +2,15 @@
//
// Copyright IBM Corp. 2024
use std::{env, fmt::Display, path::PathBuf};
use std::fmt::Display;
use std::str::FromStr;
use std::string::ToString;
use std::{env, ffi::OsStr, path::PathBuf};
use clap::{ArgGroup, Args, Command, CommandFactory, Parser, ValueEnum, ValueHint};
use clap::{
builder::PossibleValue, Arg, ArgGroup, Args, Command, CommandFactory, Parser, ValueEnum,
ValueHint,
};
use log::warn;
use utils::{CertificateOptions, DeprecatedVerbosityOptions};
@@ -206,21 +212,112 @@ pub struct CreateBootImageLegacyFlags {
}
#[non_exhaustive]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum OutputFormat {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum OutputFormatKind {
/// JSON format.
Json,
}
impl Display for OutputFormat {
impl Display for OutputFormatKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Json => "JSON",
match self {
Self::Json => write!(f, "JSON"),
}
}
}
impl FromStr for OutputFormatKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"json" => Ok(Self::Json),
_ => Err(format!("Invalid output format: {s}")),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormatVariant {
/// Default
Default,
/// Minified
Minify,
/// Pretty
Pretty,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OutputFormatSpec {
pub kind: OutputFormatKind,
pub variant: OutputFormatVariant,
}
#[derive(Clone, Default)]
struct OutputFormatSpecParser;
impl clap::builder::TypedValueParser for OutputFormatSpecParser {
type Value = OutputFormatSpec;
fn parse_ref(
&self,
cmd: &Command,
arg: Option<&Arg>,
value: &OsStr,
) -> Result<Self::Value, clap::error::Error> {
let s = value.to_string_lossy();
let mut parts = s.splitn(2, ':');
let arg_name = arg.unwrap().get_id().to_string();
let kind_s = parts.next().unwrap().to_ascii_lowercase();
let variant_s = parts.next().map(|x| x.to_ascii_lowercase());
let kind = kind_s.as_str().parse().map_err(|_| {
let mut err =
clap::error::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd);
err.insert(
clap::error::ContextKind::InvalidArg,
clap::error::ContextValue::String(arg_name.clone()),
);
err.insert(
clap::error::ContextKind::InvalidValue,
clap::error::ContextValue::String(kind_s.to_string()),
);
err
})?;
let variant = match (kind, variant_s.as_deref()) {
(_, None) => OutputFormatVariant::Default,
(_, Some("default")) => OutputFormatVariant::Default,
(OutputFormatKind::Json, Some("pretty")) => OutputFormatVariant::Pretty,
(OutputFormatKind::Json, Some("minify")) => OutputFormatVariant::Minify,
(OutputFormatKind::Json, Some(other)) => {
let mut err =
clap::error::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd);
err.insert(
clap::error::ContextKind::InvalidArg,
clap::error::ContextValue::String(arg_name),
);
err.insert(
clap::error::ContextKind::InvalidValue,
clap::error::ContextValue::String(format!("{kind_s}:{other}")),
);
Err(err)?
}
)
};
Ok(OutputFormatSpec { kind, variant })
}
// This is used for shell completion suggestions for `--format`
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
use clap::builder::PossibleValue as PV;
let vals: Vec<PV> = vec![
PV::new("json").help("Pretty-printed machine-readable JSON"),
PV::new("json:pretty").help("Pretty-printed machine-readable JSON"),
PV::new("json:minify").help("Minified machine-readable JSON"),
];
Some(Box::new(vals.into_iter()))
}
}
@@ -236,9 +333,9 @@ pub struct InfoArgs {
#[clap(flatten)]
pub input: SeImgInputArgs,
/// The output format
#[arg(long, value_enum)]
pub format: OutputFormat,
/// Output format
#[arg(long, value_parser=OutputFormatSpecParser::default())]
pub format: OutputFormatSpec,
/// Use the key in FILE to decrypt the Secure Execution header.
///
@@ -793,6 +890,30 @@ mod test {
CliOption::new("image", ["/dev/null"]),
],
)),
flat_map_collect(insert(
args.clone(),
vec![
CliOption::new("hdr-key", ["--key", "/dev/null"]),
CliOption::new("format", ["--format=json:default"]),
CliOption::new("image", ["/dev/null"]),
],
)),
flat_map_collect(insert(
args.clone(),
vec![
CliOption::new("hdr-key", ["--key", "/dev/null"]),
CliOption::new("format", ["--format=json:minify"]),
CliOption::new("image", ["/dev/null"]),
],
)),
flat_map_collect(insert(
args.clone(),
vec![
CliOption::new("hdr-key", ["--key", "/dev/null"]),
CliOption::new("format", ["--format=json:pretty"]),
CliOption::new("image", ["/dev/null"]),
],
)),
// separation between keyword and positional args works
flat_map_collect(insert(
args.clone(),
+28 -9
View File
@@ -12,30 +12,49 @@ use pv::{
};
use pvimg::{
error::OwnExitCode,
uvdata::{KeyExchangeTrait, SeHdr, UvDataTrait},
uvdata::{KeyExchangeTrait, SeH, SeHdr, UvDataTrait},
};
use crate::cli::InfoArgs;
use crate::cli::{InfoArgs, OutputFormatKind, OutputFormatSpec, OutputFormatVariant};
pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
info!(
"Reading Secure Execution header {}",
opt.input.path.display()
);
let mut input = open_file(&opt.input.path)?;
let mut img = open_file(&opt.input.path)?;
let mut output = std::io::stdout();
SeHdr::seek_sehdr(&mut input, None)?;
let hdr = SeHdr::try_from_io(input)?;
if let Some(key_path) = &opt.hdr_key {
SeHdr::seek_sehdr(&mut img, None)?;
let hdr = SeHdr::try_from_io(&mut img)?;
let se_hdr = if let Some(key_path) = &opt.hdr_key {
let key =
SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?;
serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?;
let decrypted_hdr = hdr.decrypt(&key)?;
SeH::Decrypted(decrypted_hdr)
} else {
warn!("WARNING: The Secure Execution header integrity and authenticity was not verified. Specify '--hdr-key' to authenticate it. Do not trust the data without verification.");
serde_json::to_writer_pretty(&mut output, &hdr)?;
SeH::Encrypted(hdr)
};
match opt.format {
OutputFormatSpec {
kind: OutputFormatKind::Json,
variant,
} => {
match variant {
OutputFormatVariant::Minify => {
serde_json::to_writer(&mut output, &se_hdr)?;
}
OutputFormatVariant::Default | OutputFormatVariant::Pretty => {
serde_json::to_writer_pretty(&mut output, &se_hdr)?
}
}
// Make sure the output ends with a new line
writeln!(&mut output)?
}
}
writeln!(output)?;
output.flush()?;
Ok(OwnExitCode::Success)
}
+1 -1
View File
@@ -37,7 +37,7 @@ pub mod misc {
pub mod uvdata {
pub use crate::pv_utils::{
AeadPlainDataTrait, BuilderTrait, ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait,
FlagData, KeyExchangeTrait, PcfV1, PlaintextControlFlagsV1, ScfV1, SeHdr, SeHdrAadV1,
FlagData, KeyExchangeTrait, PcfV1, PlaintextControlFlagsV1, ScfV1, SeH, SeHdr, SeHdrAadV1,
SeHdrBinV1, SeHdrBuilder, SeHdrData, SeHdrDataV1, SeHdrPlain, SeHdrVersion, SeHdrVersioned,
SecretControlFlagsV1, UvDataPlainTrait, UvDataTrait, UvKeyHashesV1,
};
+1 -1
View File
@@ -19,7 +19,7 @@ pub use misc::{round_up, try_copy_slice_to_array};
pub use psw::{ShortPsw, PSW, PSW_MASK_BA, PSW_MASK_EA};
pub use se_hdr::{
ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1,
PlaintextControlFlagsV1, ScfV1, SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, SeHdrData,
PlaintextControlFlagsV1, ScfV1, SeH, SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, SeHdrData,
SeHdrDataV1, SeHdrPlain, SeHdrVersion, SeHdrVersioned, SecretControlFlagsV1,
};
pub use secured_comp::{ComponentTrait, SecuredComponent, SecuredComponentBuilder};
+2 -2
View File
@@ -9,9 +9,9 @@ mod hdr_v1;
mod keys;
pub use brb::{
ComponentMetadata, ComponentMetadataV1, SeHdr, SeHdrDataV1, SeHdrPlain, SeHdrVersion,
ComponentMetadata, ComponentMetadataV1, SeH, SeHdr, SeHdrBinV1, SeHdrData, SeHdrDataV1,
SeHdrPlain, SeHdrVersion, SeHdrVersioned,
};
pub use brb::{SeHdrBinV1, SeHdrData, SeHdrVersioned};
pub use builder::SeHdrBuilder;
pub use flags::{
ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1, PlaintextControlFlagsV1, ScfV1,
+6
View File
@@ -47,6 +47,12 @@ pub enum SeHdrVersion {
V1 = 0x100,
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
pub enum SeH {
Decrypted(SeHdrPlain),
Encrypted(SeHdr),
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]