lanzatool: keep unrelated files when running gc

This commit is contained in:
nikstur 2023-01-04 01:20:50 +01:00
parent 8cd7452194
commit b6eb6c1e52
3 changed files with 91 additions and 2 deletions

View File

@ -35,10 +35,30 @@ impl Roots {
} }
pub fn collect_garbage(&self, directory: impl AsRef<Path>) -> Result<()> { pub fn collect_garbage(&self, directory: impl AsRef<Path>) -> Result<()> {
self.collect_garbage_with_filter(directory, |_| true)
}
/// Collect garbage with an additional filter.
///
/// The filter function takes a &Path and returns a bool. The paths for which the filter
/// function returns true are considered for garbage collection. This means that _only_ files
/// that are unused AND for which the filter function returns true are deleted.
pub fn collect_garbage_with_filter<P>(
&self,
directory: impl AsRef<Path>,
mut predicate: P,
) -> Result<()>
where
P: FnMut(&Path) -> bool,
{
// Find all the paths not used anymore. // Find all the paths not used anymore.
let entries_not_in_use = WalkDir::new(directory.as_ref()) let entries_not_in_use = WalkDir::new(directory.as_ref())
.into_iter() .into_iter()
.filter(|e| !self.in_use(e.as_ref().ok())); .filter(|e| !self.in_use(e.as_ref().ok()))
.filter(|e| match e.as_ref().ok() {
Some(e) => predicate(e.path()),
None => false,
});
// Remove all entries not in use. // Remove all entries not in use.
for e in entries_not_in_use { for e in entries_not_in_use {
@ -148,6 +168,27 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn only_delete_filtered_unused_files() -> Result<()> {
let tmpdir = tempfile::tempdir()?;
let rootdir = create_dir(tmpdir.path().join("root"))?;
let unused_file = create_file(rootdir.join("unused_file"))?;
let unused_file_with_prefix = create_file(rootdir.join("prefix_unused_file"))?;
let mut roots = Roots::new();
roots.extend(vec![&rootdir]);
roots.collect_garbage_with_filter(&rootdir, |p| {
p.file_name()
.and_then(|n| n.to_str())
.map_or(false, |n| n.starts_with("prefix_"))
})?;
assert!(unused_file.exists());
assert!(!unused_file_with_prefix.exists());
Ok(())
}
fn create_file(path: PathBuf) -> Result<PathBuf> { fn create_file(path: PathBuf) -> Result<PathBuf> {
fs::File::create(&path)?; fs::File::create(&path)?;
Ok(path) Ok(path)

View File

@ -61,7 +61,20 @@ impl Installer {
}; };
self.install_links(links)?; self.install_links(links)?;
self.gc_roots.collect_garbage(&self.esp)?; // 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.join("EFI/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.join("EFI/Linux"), |p| {
p.file_name()
.and_then(|n| n.to_str())
.map_or(false, |n| n.starts_with("nixos-"))
})?;
Ok(()) Ok(())
} }

View File

@ -44,6 +44,41 @@ fn keep_only_configured_number_of_generations() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn keep_unrelated_files_on_esp() -> Result<()> {
let esp_mountpoint = tempdir()?;
let tmpdir = tempdir()?;
let profiles = tempdir()?;
let generation_links: Vec<PathBuf> = [1, 2, 3]
.into_iter()
.map(|v| {
common::setup_generation_link(tmpdir.path(), profiles.path(), v)
.expect("Failed to setup generation link")
})
.collect();
// Install all 3 generations.
let output0 = common::lanzaboote_install(0, esp_mountpoint.path(), generation_links.clone())?;
assert!(output0.status.success());
let unrelated_uki = esp_mountpoint.path().join("EFI/Linux/ubuntu.efi");
let unrelated_os = esp_mountpoint.path().join("EFI/windows");
let unrelated_firmware = esp_mountpoint.path().join("dell");
fs::File::create(&unrelated_uki)?;
fs::create_dir(&unrelated_os)?;
fs::create_dir(&unrelated_firmware)?;
// Call `lanzatool install` again with a config limit of 2.
let output1 = common::lanzaboote_install(2, esp_mountpoint.path(), generation_links)?;
assert!(output1.status.success());
assert!(unrelated_uki.exists());
assert!(unrelated_os.exists());
assert!(unrelated_firmware.exists());
Ok(())
}
fn count_files(path: &Path) -> Result<usize> { fn count_files(path: &Path) -> Result<usize> {
Ok(fs::read_dir(path)?.count()) Ok(fs::read_dir(path)?.count())
} }