Merge pull request #116 from nix-community/installation-order

Make File Installation Order Deterministic
This commit is contained in:
Julian Stecklina 2023-02-25 21:25:23 +01:00 committed by GitHub
commit 195e29f935
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 50 additions and 21 deletions

View File

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::collections::BTreeMap;
use std::fs; use std::fs;
use std::os::unix::prelude::PermissionsExt; use std::os::unix::prelude::PermissionsExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -259,15 +259,17 @@ impl Installer {
.write_secure_file(os_release.to_string().as_bytes()) .write_secure_file(os_release.to_string().as_bytes())
.context("Failed to write os-release file.")?; .context("Failed to write os-release file.")?;
let kernel_path = generation_artifacts let kernel_path: &Path = generation_artifacts
.unsigned_files .files
.get(&esp_gen_paths.kernel) .get(&esp_gen_paths.kernel)
.context("Failed to retrieve kernel path from GenerationArtifacts.")?; .context("Failed to retrieve kernel path from GenerationArtifacts.")?
.into();
let initrd_path = generation_artifacts let initrd_path = generation_artifacts
.unsigned_files .files
.get(&esp_gen_paths.initrd) .get(&esp_gen_paths.initrd)
.context("Failed to retrieve initrd path from GenerationArtifacts.")?; .context("Failed to retrieve initrd path from GenerationArtifacts.")?
.into();
let lanzaboote_image = pe::lanzaboote_image( let lanzaboote_image = pe::lanzaboote_image(
tempdir, tempdir,
@ -326,6 +328,22 @@ impl Installer {
} }
} }
/// A location in the ESP together with information whether the file
/// needs to be signed.
#[derive(Debug, Clone, PartialEq, Eq)]
enum FileSource {
SignedFile(PathBuf),
UnsignedFile(PathBuf),
}
impl<'a> From<&'a FileSource> for &'a Path {
fn from(value: &'a FileSource) -> Self {
match value {
FileSource::SignedFile(p) | FileSource::UnsignedFile(p) => p,
}
}
}
/// Stores the source and destination of all artifacts needed to install all generations. /// Stores the source and destination of all artifacts needed to install all generations.
/// ///
/// The key feature of this data structure is that the mappings are automatically deduplicated /// The key feature of this data structure is that the mappings are automatically deduplicated
@ -342,45 +360,56 @@ struct GenerationArtifacts {
/// Temporary directory that stores all temporary files that are created when building the /// Temporary directory that stores all temporary files that are created when building the
/// GenerationArtifacts. /// GenerationArtifacts.
tempdir: TempDir, tempdir: TempDir,
/// Mapping of signed files from their destinations to their source.
signed_files: HashMap<PathBuf, PathBuf>, /// A mapping from target location to source.
/// Mapping of unsigned files from their destinations to their source. files: BTreeMap<PathBuf, FileSource>,
unsigned_files: HashMap<PathBuf, PathBuf>,
} }
impl GenerationArtifacts { impl GenerationArtifacts {
fn new() -> Result<Self> { fn new() -> Result<Self> {
Ok(Self { Ok(Self {
tempdir: TempDir::new().context("Failed to create temporary directory.")?, tempdir: TempDir::new().context("Failed to create temporary directory.")?,
signed_files: HashMap::new(), files: Default::default(),
unsigned_files: HashMap::new(),
}) })
} }
/// Add a file to be installed.
///
/// Adding the same file multiple times with the same source is ok
/// and will drop the old source.
fn add_file(&mut self, from: FileSource, to: &Path) {
if let Some(_prev_from) = self.files.insert(to.to_path_buf(), from) {
// Should we log something here?
}
}
/// Add source and destination of a PE file to be signed. /// Add source and destination of a PE file to be signed.
/// ///
/// Files are stored in the HashMap using their destination path as the key to ensure that the /// Files are stored in the HashMap using their destination path as the key to ensure that the
/// destination paths are unique. /// destination paths are unique.
fn add_signed(&mut self, from: &Path, to: &Path) { fn add_signed(&mut self, from: &Path, to: &Path) {
self.signed_files.insert(to.into(), from.into()); self.add_file(FileSource::SignedFile(from.to_path_buf()), to);
} }
/// Add source and destination of an arbitrary file. /// Add source and destination of an arbitrary file.
fn add_unsigned(&mut self, from: &Path, to: &Path) { fn add_unsigned(&mut self, from: &Path, to: &Path) {
self.unsigned_files.insert(to.into(), from.into()); self.add_file(FileSource::UnsignedFile(from.to_path_buf()), to);
} }
/// Install all files to the ESP. /// Install all files to the ESP.
fn install(&self, key_pair: &KeyPair) -> Result<()> { fn install(&self, key_pair: &KeyPair) -> Result<()> {
for (to, from) in &self.signed_files { for (to, from) in &self.files {
install_signed(key_pair, from, to) match from {
.with_context(|| format!("Failed to sign and install from {from:?} to {to:?}"))?; FileSource::SignedFile(from) => {
install_signed(key_pair, from, to).with_context(|| {
format!("Failed to sign and install from {from:?} to {to:?}")
})?
}
FileSource::UnsignedFile(from) => install(from, to)
.with_context(|| format!("Failed to install from {from:?} to {to:?}"))?,
}
} }
for (to, from) in &self.unsigned_files {
install(from, to)
.with_context(|| format!("Failed to install from {from:?} to {to:?}"))?;
}
Ok(()) Ok(())
} }
} }