diff options
| -rw-r--r-- | src/build.rs | 1 | ||||
| -rw-r--r-- | src/initramfs.rs | 20 | ||||
| -rw-r--r-- | tests/build.rs | 49 |
3 files changed, 70 insertions, 0 deletions
diff --git a/src/build.rs b/src/build.rs index 8b6ebb0..cb6a436 100644 --- a/src/build.rs +++ b/src/build.rs @@ -110,6 +110,7 @@ impl<I: PackageInstaller, B: InitramfsBuilder> BuildExecutor<I, B> { Err(error) => return Self::fail_and_remove_workspace(workspace, error), }; let result = (|| { + initrd.verify_in_rootfs(&rootfs)?; std::fs::create_dir_all(parent) .with_context(|| format!("create artifact directory {}", parent.display()))?; self.tar_writer.write(&rootfs, artifact)?; diff --git a/src/initramfs.rs b/src/initramfs.rs index a108b85..4ece24c 100644 --- a/src/initramfs.rs +++ b/src/initramfs.rs @@ -249,4 +249,24 @@ impl InitramfsResult { pub fn sha256(&self) -> &str { &self.sha256 } + + /// Ensure the initrd recorded by the platform adapter still exists in the + /// assembled rootfs and has not changed before native packaging begins. + pub fn verify_in_rootfs(&self, rootfs: &Path) -> Result<()> { + let contents = fs::read(rootfs.join(&self.initrd_path)).with_context(|| { + format!( + "read generated initrd {}", + rootfs.join(&self.initrd_path).display() + ) + })?; + let digest = format!("{:x}", Sha256::digest(contents)); + if digest != self.sha256 { + bail!( + "generated initrd digest changed for {}: expected {}, found {digest}", + self.initrd_path.display(), + self.sha256 + ); + } + Ok(()) + } } diff --git a/tests/build.rs b/tests/build.rs index 2fcd1bf..c9cc59f 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -73,6 +73,20 @@ impl PackageInstaller for ArchiveFailingInstaller { } } +struct MutatingInitrdBuilder; + +impl InitramfsBuilder for MutatingInitrdBuilder { + fn build(&mut self, request: &InitramfsRequest) -> anyhow::Result<InitramfsResult> { + let initrd = request + .rootfs() + .join(format!("boot/initrd-{}.img", request.kernel().as_str())); + fs::write(&initrd, "primary initrd")?; + let result = InitramfsResult::from_rootfs(request.rootfs(), request.kernel())?; + fs::write(initrd, "mutated initrd")?; + Ok(result) + } +} + #[derive(Default)] struct FixtureInitramfsBuilder; @@ -355,3 +369,38 @@ fn removes_the_new_workspace_and_partial_artifact_when_packaging_fails() { "failed packaging must not leave a companion manifest" ); } + +#[test] +fn removes_the_new_workspace_and_artifact_when_manifest_collection_fails() { + let fixture = tempdir().expect("fixture directory"); + let spec = ImageSpec::load(std::path::Path::new("profiles/alt-controller.toml")) + .expect("load controller spec"); + let plan = BuildPlan::compile(spec).expect("compile build plan"); + let mut executor = BuildExecutor::new(FixtureInstaller::default(), MutatingInitrdBuilder); + let workspace = fixture.path().join("work"); + let artifact = fixture.path().join("out/image.tar"); + + let error = executor + .execute(&plan, &workspace, "profiles/apt.conf", &artifact) + .expect_err("mutated initrd must fail before packaging"); + + assert!( + error + .to_string() + .contains("generated initrd digest changed") + ); + assert!( + !workspace.exists(), + "failed build must not leave a workspace" + ); + assert!( + !artifact.exists(), + "failed manifest collection must not leave an artifact" + ); + assert!( + !ArtifactManifest::path_beside(&artifact) + .expect("manifest path") + .exists(), + "failed manifest collection must not leave a companion manifest" + ); +} |