From 9d46e91c49a7c161e3fab4002caba67a9571d7bf Mon Sep 17 00:00:00 2001 From: r-vdp Date: Sun, 31 May 2026 22:40:50 +0300 Subject: [PATCH] nixos/systemd-boot-builder: track critical paths separately from BootFile Whether a write failure must be fatal is a property of the destination path (is it needed by the configuration we are switching to?), not of the particular BootFile instance that happened to survive deduplication. Compute the set of critical paths up front and look it up in write_boot_files, so the dedup loop no longer needs to pick the "right" instance and becomes a plain order-preserving seen-set walk. This leaves BootFile.current unused. Suggested-by: Will Fancher --- .../systemd-boot/systemd-boot-builder.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index 61791019338d..f223caac9984 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py @@ -532,6 +532,7 @@ def install_bootloader(args: argparse.Namespace) -> None: gens += get_generations(profile) boot_files: BootFileList = [] + critical_paths: set[Path] = set() default_config = Path(args.default_config) default_entry_id: str | None = None @@ -545,6 +546,7 @@ def install_bootloader(args: argparse.Namespace) -> None: boot_files.extend(new_boot_files) if is_default: default_entry_id = new_bootctl_id + critical_paths.update(bf.path for bf in new_boot_files) for specialisation_name, specialisation in bootspec.specialisations.items(): is_default = Path(specialisation.init).parent == default_config new_boot_files, new_bootctl_id = boot_file( @@ -558,13 +560,14 @@ def install_bootloader(args: argparse.Namespace) -> None: boot_files.extend(new_boot_files) if is_default: default_entry_id = new_bootctl_id + critical_paths.update(bf.path for bf in new_boot_files) # Garbage-collect stale kernels/initrds/entries before re-populating extra # files, so that user-supplied extraEntries (which may also live under # loader/entries and start with `nixos-`) are not removed again. garbage_collect(boot_files) - write_boot_files(boot_files) + write_boot_files(boot_files, critical_paths) write_loader_conf(default_entry_id) @@ -612,20 +615,18 @@ def garbage_collect(gc_roots: BootFileList) -> None: delete_path(e) -def write_boot_files(boot_files: BootFileList) -> None: +def write_boot_files(boot_files: BootFileList, critical_paths: set[Path]) -> None: # Deduplicate by destination path so shared files are written once. - # Prefer the current configuration's entry so its failures are fatal. - unique: dict[Path, BootFile] = {} - for bf in boot_files: - if bf.path not in unique or bf.current: - unique[bf.path] = bf - - for boot_file in unique.values(): + seen: set[Path] = set() + for boot_file in boot_files: + if boot_file.path in seen: + continue + seen.add(boot_file.path) boot_path = BOOT_MOUNT_POINT / boot_file.path try: boot_file.writer.write_boot_file(boot_path) except subprocess.CalledProcessError: - if boot_file.current: + if boot_file.path in critical_paths: print("failed to create initrd secrets!", file=sys.stderr) sys.exit(1) # Keep the entry bootable by leaving at least a pristine initrd