diff --git a/crates/lib/src/cfsctl.rs b/crates/lib/src/cfsctl.rs index 6daae5b23..63a005d58 100644 --- a/crates/lib/src/cfsctl.rs +++ b/crates/lib/src/cfsctl.rs @@ -1,11 +1,13 @@ use std::{ ffi::OsString, - fs::create_dir_all, + fs::{create_dir_all, File}, + io::BufWriter, path::{Path, PathBuf}, sync::Arc, }; -use anyhow::Result; +use anyhow::{Context, Result}; +use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; use rustix::fs::CWD; @@ -13,6 +15,7 @@ use rustix::fs::CWD; use composefs_boot::{write_boot, BootOps}; use composefs::{ + dumpfile, fsverity::{FsVerityHashValue, Sha512HashValue}, repository::Repository, }; @@ -130,6 +133,9 @@ enum Command { }, ComputeId { path: PathBuf, + /// Write the dumpfile to the provided target + #[clap(long)] + write_dumpfile_to: Option, #[clap(long)] bootable: bool, #[clap(long)] @@ -308,6 +314,7 @@ where }, Command::ComputeId { ref path, + write_dumpfile_to, bootable, stat_root, } => { @@ -317,6 +324,12 @@ where } let id = fs.compute_image_id(); println!("{}", id.to_hex()); + if let Some(path) = write_dumpfile_to.as_deref() { + let mut w = File::create(path) + .with_context(|| format!("Opening {path}")) + .map(BufWriter::new)?; + dumpfile::write_dumpfile(&mut w, &fs).context("Writing dumpfile")?; + } } Command::CreateImage { ref path, diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs index 75bc7871f..380309cef 100644 --- a/crates/lib/src/cli.rs +++ b/crates/lib/src/cli.rs @@ -3,7 +3,8 @@ //! Command line tool to manage bootable ostree-based containers. use std::ffi::{CString, OsStr, OsString}; -use std::io::Seek; +use std::fs::File; +use std::io::{BufWriter, Seek}; use std::os::unix::process::CommandExt; use std::process::Command; use std::sync::Arc; @@ -14,6 +15,7 @@ use cap_std_ext::cap_std; use cap_std_ext::cap_std::fs::Dir; use clap::Parser; use clap::ValueEnum; +use composefs::dumpfile; use composefs_boot::BootOps as _; use etc_merge::{compute_diff, print_diff}; use fn_error_context::context; @@ -329,6 +331,10 @@ pub(crate) enum ContainerOpts { /// Output the bootable composefs digest. #[clap(hide = true)] ComputeComposefsDigest { + /// Additionally generate a dumpfile written to the target path + #[clap(long)] + write_dumpfile_to: Option, + /// Identifier for image; if not provided, the running image will be used. image: Option, }, @@ -1365,7 +1371,10 @@ async fn run_from_opt(opt: Opt) -> Result<()> { )?; Ok(()) } - ContainerOpts::ComputeComposefsDigest { image } => { + ContainerOpts::ComputeComposefsDigest { + write_dumpfile_to, + image, + } => { // Allocate a tempdir let td = tempdir_in("/var/tmp")?; let td = td.path(); @@ -1412,6 +1421,13 @@ async fn run_from_opt(opt: Opt) -> Result<()> { let id = fs.compute_image_id(); println!("{}", id.to_hex()); + if let Some(path) = write_dumpfile_to.as_deref() { + let mut w = File::create(path) + .with_context(|| format!("Opening {path}")) + .map(BufWriter::new)?; + dumpfile::write_dumpfile(&mut w, &fs).context("Writing dumpfile")?; + } + Ok(()) } },