From 09e12eb559be33101fa743a3f5f4c0cd113aadd3 Mon Sep 17 00:00:00 2001 From: nikstur Date: Mon, 24 Apr 2023 21:54:14 +0200 Subject: [PATCH] tool: disable gc in the presence of malformed gens Disable GC if there are any malformed gens to avoid catastrophic failure when there are upstream changes to NixOS that are not handled in lzbt. --- rust/tool/Cargo.lock | 7 +++++ rust/tool/Cargo.toml | 1 + rust/tool/src/install.rs | 61 ++++++++++++++++++++++++---------------- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/rust/tool/Cargo.lock b/rust/tool/Cargo.lock index b314f32..38687b2 100644 --- a/rust/tool/Cargo.lock +++ b/rust/tool/Cargo.lock @@ -473,6 +473,12 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "indoc" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690" + [[package]] name = "instant" version = "0.1.12" @@ -541,6 +547,7 @@ dependencies = [ "fastrand", "filetime", "goblin", + "indoc", "log", "nix", "rand", diff --git a/rust/tool/Cargo.toml b/rust/tool/Cargo.toml index 1a534fc..83a6427 100644 --- a/rust/tool/Cargo.toml +++ b/rust/tool/Cargo.toml @@ -27,6 +27,7 @@ sha2 = "0.10.6" fastrand = "1.9.0" log = { version = "0.4.17", features = ["std"] } stderrlog = "0.5.4" +indoc = "2.0.1" [dev-dependencies] assert_cmd = "2.0.11" diff --git a/rust/tool/src/install.rs b/rust/tool/src/install.rs index 078a82c..f6af99e 100644 --- a/rust/tool/src/install.rs +++ b/rust/tool/src/install.rs @@ -18,6 +18,7 @@ use crate::systemd::SystemdVersion; use crate::utils::{file_hash, SecureTempDirExt}; pub struct Installer { + enable_gc: bool, gc_roots: Roots, lanzaboote_stub: PathBuf, systemd: PathBuf, @@ -43,6 +44,7 @@ impl Installer { gc_roots.extend(esp_paths.to_iter()); Self { + enable_gc: true, gc_roots, lanzaboote_stub, systemd, @@ -85,21 +87,34 @@ impl Installer { self.install_systemd_boot()?; - log::info!("Collecting garbage..."); - // Only collect garbage in these two directories. This way, no files that do not belong to - // the NixOS installation are deleted. Lanzatool takes full control over the esp/EFI/nixos - // directory and deletes ALL files that it doesn't know about. Dual- or multiboot setups - // that need files in this directory will NOT work. - self.gc_roots.collect_garbage(&self.esp_paths.nixos)?; - // The esp/EFI/Linux directory is assumed to be potentially shared with other distros. - // Thus, only files that start with "nixos-" are garbage collected (i.e. potentially - // deleted). - self.gc_roots - .collect_garbage_with_filter(&self.esp_paths.linux, |p| { - p.file_name() - .and_then(|n| n.to_str()) - .map_or(false, |n| n.starts_with("nixos-")) - })?; + if self.enable_gc { + log::info!("Collecting garbage..."); + // Only collect garbage in these two directories. This way, no files that do not belong to + // the NixOS installation are deleted. Lanzatool takes full control over the esp/EFI/nixos + // directory and deletes ALL files that it doesn't know about. Dual- or multiboot setups + // that need files in this directory will NOT work. + self.gc_roots.collect_garbage(&self.esp_paths.nixos)?; + // The esp/EFI/Linux directory is assumed to be potentially shared with other distros. + // Thus, only files that start with "nixos-" are garbage collected (i.e. potentially + // deleted). + self.gc_roots + .collect_garbage_with_filter(&self.esp_paths.linux, |p| { + p.file_name() + .and_then(|n| n.to_str()) + .map_or(false, |n| n.starts_with("nixos-")) + })?; + } else { + // Garbage collection can only be disabled if there are malformed generations. If + // garbage collection can ever be disabled differently, the below log message most + // likely does not make sense anymore. + log::warn!(indoc::indoc! {" + Garbage collection is disabled because you have malformed NixOS generations that do + not contain a readable bootspec document. + + Remove the malformed generations with `nix-env --delete-generations` to re-enable + garbage collection. + "}) + }; log::info!("Successfully installed Lanzaboote."); Ok(()) @@ -171,15 +186,13 @@ impl Installer { // Ignore failing to read a generation so that old malformed generations do not stop // lzbt from working. - if let Err(e) = &generation_result { - // Semantically, this message should be a warning. However, since users might - // have hundreds of old and thus malformed generations and can do little about - // it, this should remain a debug message. This way the user is not spammed - // with no-op warnings while still enabling debugging. - log::debug!( - "Ignoring generation {} because it's malformed: {e:#}", - link.version - ); + if generation_result.is_err() { + // If there is ANY malformed generation present, completely disable all garbage + // collection to protect the old generations from being deleted. The user has + // to manually intervene by getting rid of the old generations to re-enable + // garbage collection. This safeguard against catastrophic failure in case of + // unhandled upstream changes to NixOS. + self.enable_gc = false; } generation_result.ok()