diff --git a/dstack/crates/dstack-volume/src/bin/dstack-volume.rs b/dstack/crates/dstack-volume/src/bin/dstack-volume.rs index 367fc33fc..0e2d8f251 100644 --- a/dstack/crates/dstack-volume/src/bin/dstack-volume.rs +++ b/dstack/crates/dstack-volume/src/bin/dstack-volume.rs @@ -431,15 +431,7 @@ fn unescape_mountinfo(value: &[u8]) -> Vec { fn mapping_root(mapper_name: &str) -> Result { let status = run_fun!(veritysetup status $mapper_name)?; - status - .lines() - .find_map(|line| { - let line = line.trim(); - line.strip_prefix("root hash:") - .or_else(|| line.strip_prefix("Root hash:")) - }) - .map(|root| root.trim().to_string()) - .context("verity mapping status has no root hash") + dstack_volume::parse_verity_root_hash(&status).context("verity mapping status has no root hash") } #[cfg(test)] diff --git a/dstack/crates/dstack-volume/src/lib.rs b/dstack/crates/dstack-volume/src/lib.rs index 5a8aa5f28..295415b86 100644 --- a/dstack/crates/dstack-volume/src/lib.rs +++ b/dstack/crates/dstack-volume/src/lib.rs @@ -23,6 +23,21 @@ pub mod volume_format; pub use volume::Compression; +/// Read the root hash from `veritysetup format` (`Root hash:`) or +/// `veritysetup status` (indented `root hash:`) output. +pub fn parse_verity_root_hash(output: &str) -> Option { + output + .lines() + .filter_map(|line| { + let line = line.trim(); + line.strip_prefix("Root hash:") + .or_else(|| line.strip_prefix("root hash:")) + }) + .map(str::trim) + .find(|root| !root.is_empty() && root.bytes().all(|byte| byte.is_ascii_hexdigit())) + .map(str::to_string) +} + /// A fixed dm-verity salt. /// /// The root is a function of the squashfs bytes and this salt, so keeping the @@ -93,3 +108,21 @@ async fn verity_fs_image(fs_image: PathBuf, output: PathBuf) -> Result Result<()> { Ok(()) } -fn parse_root_hash(output: &str) -> Option { - output - .lines() - .find_map(|l| l.strip_prefix("Root hash:")) - .map(|v| v.trim().to_string()) -} - fn require_tool(name: &str) -> Result<()> { let present = run_cmd!(which $name >/dev/null 2>&1).is_ok(); if !present { @@ -402,13 +395,6 @@ fn require_tool(name: &str) -> Result<()> { mod tests { use super::*; - #[test] - fn parses_veritysetup_root() { - let sample = "VERITY header information for x\nUUID: \nHash type: 1\n\ - Data blocks: 10\nRoot hash: abc123def\n"; - assert_eq!(parse_root_hash(sample).as_deref(), Some("abc123def")); - } - #[test] fn uuid_is_deterministic_and_content_specific() { use std::io::Write;