From 2fdf50dfd2eefcd1fe3d2c877056477ab93e6f0b Mon Sep 17 00:00:00 2001 From: Rudra Saraswat Date: Sat, 4 Apr 2026 00:01:45 +0100 Subject: [PATCH] fix: bootloader update logic --- usr/lib/akshara/utils/update.py | 109 ++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/usr/lib/akshara/utils/update.py b/usr/lib/akshara/utils/update.py index 75864e4..3737ae4 100644 --- a/usr/lib/akshara/utils/update.py +++ b/usr/lib/akshara/utils/update.py @@ -35,6 +35,7 @@ def update_cleanup() -> None: "-rf", "/var/cache/akshara/rootfs", "/.update_rootfs", + "/.boot.bkp", ] ) @@ -143,86 +144,116 @@ def merge_var(new_rootfs: RootFS, overrides_keep_new: dict) -> None: ) -def handle_boot(new_rootfs, boot_config) -> None: +def merge_into_tree(tree_root: str, tmp_tree_root: str) -> None: + """Merge a new tree into an existing tree. + + Args: + tree_root: String containing path to existing tree. + tmp_tree_root: String containing path to new tree. + """ + + new_paths = [] + for tmp_root, _, files in os.walk(tmp_tree_root): + root = tmp_root.replace(tmp_tree_root, tree_root, 1) + subprocess.run(["mkdir", "-p", root]) + new_paths.append(root) + for path in files: + subprocess.run(["rm", "-rf", "--", os.path.join(root, path)]) + subprocess.run( + [ + "cp", + "-ax", + os.path.join(tmp_root, path), + os.path.join(root, path), + ] + ) + new_paths.append(os.path.join(root, path)) + + for root, _, files in os.walk(tree_root): + for path in files: + if os.path.join(root, path) not in new_paths: + subprocess.run(["rm", "-rf", "--", os.path.join(root, path)]) + + +def handle_boot(new_rootfs: RootFS, boot_config: dict) -> None: """Handles /boot partition.""" - subprocess.run(["rm", "-rf", "/.tmp.boot"]) - subprocess.run(["cp", "-ax", os.path.join(str(new_rootfs), "boot"), "/.tmp.boot"]) + subprocess.run(["rm", "-rf", "/.boot.bkp"]) + subprocess.run(["cp", "-ax", "/boot", "/.boot.bkp"]) + + # Replace contents of /boot with those from new rootfs + # FIXME: should be atomic + merge_into_tree("/boot", os.path.join(str(new_rootfs), "boot")) + + subprocess.run(["mount", "--bind", "/boot", f"{new_rootfs}/boot"]) + + def restore_old_boot(): + subprocess.run( + ["umount", "-l", f"{new_rootfs}/boot"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + merge_into_tree("/boot", "/.boot.bkp") if boot_config["type"] == "bios": if boot_config["loader"] == "grub": if ( - subprocess.run( + new_rootfs.exec_chroot( [ "grub-install", - f"--directory={os.path.join(str(new_rootfs), 'usr/lib/grub/i386-pc')}", - "--boot-directory=/.tmp.boot", "--target=i386-pc", boot_config["device"], ] ).returncode != 0 ): - output.error("aborting update...") - subprocess.run(["rm", "-rf", "/.tmp.boot"]) + restore_old_boot() output.error("failed to install GRUB") + output.error("aborted update") exit(1) else: - subprocess.run(["umount", "-l", "/boot"]) - subprocess.run(["rm", "-rf", "/.tmp.boot"]) + restore_old_boot() output.error("unsupported bootloader for BIOS configuration") + output.error("aborted update") exit(1) elif boot_config["type"] == "uefi": if boot_config["loader"] == "grub": if ( - subprocess.run( + new_rootfs.exec_chroot( [ "grub-install", - f"--directory={os.path.join(str(new_rootfs), 'usr/lib/grub/x86_64-efi')}", - "--efi-directory=/.tmp.boot", - "--boot-directory=/.tmp.boot", + "--efi-directory=/boot", "--target=x86_64-efi", "--removable", "--bootloader-id=blendOS", ] - ) + ).returncode != 0 ): - output.error("aborting update...") - subprocess.run(["rm", "-rf", "/.tmp.boot"]) + restore_old_boot() output.error("failed to install GRUB") + output.error("aborted update") exit(1) else: - subprocess.run(["rm", "-rf", "/.tmp.boot"]) + restore_old_boot() output.error("unsupported bootloader for BIOS configuration") + output.error("aborted update") exit(1) else: - subprocess.run(["umount", "-l", "/boot"]) - subprocess.run(["rm", "-rf", "/.tmp.boot"]) + restore_old_boot() output.error(f"unsupported system type - {boot_config['type']}") + output.error("aborted update") exit(1) if boot_config["loader"] == "grub": - new_rootfs.exec_chroot(["grub-mkconfig", "-o", "/grub.cfg"]) - subprocess.run(["cp", f"{new_rootfs}/grub.cfg", "/.tmp.boot/grub/grub.cfg"]) + new_rootfs.exec_chroot(["grub-mkconfig", "-o", "/boot/grub/grub.cfg"]) - # Replace /boot with /.tmp.boot - # FIXME: should be atomic - for tmp_boot, dirs, files in os.walk("/.tmp.boot"): - boot = tmp_boot.replace("/.tmp.boot", "/boot", 1) - subprocess.run(["mkdir", "-p", boot]) - for path in dirs + files: - subprocess.run(["rm", "-rf", "--", os.path.join(boot, path)]) - subprocess.run( - [ - "cp", - "-ax", - os.path.join(tmp_boot, path), - os.path.join(boot, path), - ] - ) - - subprocess.run(["rm", "-rf", "/.tmp.boot"]) + subprocess.run( + ["umount", "-l", f"{new_rootfs}/boot"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + subprocess.run(["rm", "-rf", "/.boot.bkp"]) def update() -> None: