From 44b32045ce9fea4396b7b14950a552e9aeb65c51 Mon Sep 17 00:00:00 2001 From: zj <1052308357@qq.com> Date: Thu, 12 Feb 2026 02:32:57 +0800 Subject: [PATCH] 8 --- AGENTS.md | 28 +++++- __pycache__/backend.cpython-39.pyc | Bin 52841 -> 55350 bytes backend.py | 138 +++++++++++++++++++++++++++-- 3 files changed, 153 insertions(+), 13 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e22fe2b..05bc938 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -110,11 +110,29 @@ options root=/dev/mapper/cl-root ro crashkernel=auto rd.lvm.lv=cl/root rhgb quie 检测到缺少 EFI 模块时,自动安装相应的包: | 发行版 | 包名 | |--------|------| -| CentOS/RHEL/Rocky/Alma/Fedora | `grub2-efi-x64`, `grub2-efi-x64-modules` | -| Debian/Ubuntu | `grub-efi-amd64` | -| Arch/Manjaro | `grub` (已包含 EFI 支持) | +| CentOS/RHEL/Rocky/Alma/Fedora | `grub2-efi-x64`, `grub2-efi-x64-modules`, `shim-x64` | +| Debian/Ubuntu | `grub-efi-amd64`, `shim-signed` | +| Arch/Manjaro | `grub`, `efibootmgr` | -### 7. GRUB 修复 (`backend.py:chroot_and_repair_grub`) +### 7. 手动 EFI 安装 (`backend.py:_manual_install_efi_files`) + +**NEW v2.4**: 当 `grub2-install` 因 Secure Boot 不支持时,使用手动方式安装 EFI 文件 + +#### 问题场景 +某些系统(特别是 CentOS/RHEL 8)的 `grub2-install` 会报错: +``` +this utility cannot be used for EFI platforms because it does not support UEFI Secure Boot. +``` + +这是因为标准 `grub2-install` 不支持 Secure Boot,需要使用 `shim` 链式加载。 + +#### 手动安装方法 +当 `grub2-install` 失败时,自动尝试: +1. **查找现有 EFI 文件** - 从 `/boot/efi/EFI//` 复制已有的 `grubx64.efi` 或 `shimx64.efi` +2. **使用 shim 链式加载** - 复制 `shimx64.efi` 并配置它加载 `grubx64.efi` +3. **使用 grub-mkimage 生成** - 从模块生成新的 EFI 文件 + +### 8. GRUB 修复 (`backend.py:chroot_and_repair_grub`) #### BIOS 模式 - `grub-install --target=i386-pc --recheck --force /dev/sdX` @@ -123,11 +141,13 @@ options root=/dev/mapper/cl-root ro crashkernel=auto rd.lvm.lv=cl/root rhgb quie #### UEFI 模式 (参考 Calamares 实现) - 自动检测系统架构 (x86_64/i386/arm64/loongarch64) - **NEW**: 自动检测并安装 UEFI GRUB 模块 +- **NEW**: 当 grub2-install 因 Secure Boot 失败时,自动使用手动 EFI 安装 - 获取正确的 EFI 参数 (target, grub_file, boot_file) - **多重安装策略**(自动回退): 1. 标准安装(带 NVRAM) 2. Live 环境/可移动模式(`--removable`) 3. 无 NVRAM 模式(`--no-nvram`) + 4. **NEW**: 手动 EFI 文件复制(针对 Secure Boot 限制) - **NEW**: EFI Fallback 安装(复制到 `/EFI/Boot/bootx64.efi`) - **NEW**: 支持自定义 EFI 启动项名称 diff --git a/__pycache__/backend.cpython-39.pyc b/__pycache__/backend.cpython-39.pyc index 2345c4b3fbaeda74dc75fcc21f5252d8ee2c08cb..367ec55a12b33cf0c3b3cf86a54e9acb08b22246 100644 GIT binary patch delta 12242 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 delta 10011 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 bool: + """ + 手动安装 EFI 文件(当 grub2-install 因为 Secure Boot 不支持时使用)。 + 直接从 /boot/efi/EFI// 复制已有的 EFI 文件,或使用 shim 链式加载。 + + 返回: 是否成功 + """ + log_step("手动安装 EFI 文件", f"目标: {efi_grub_file}") + + efi_firmware_dir = os.path.join(mount_point, "boot/efi/EFI") + + # 创建目标目录 + target_dir = os.path.join(efi_firmware_dir, bootloader_id) + try: + os.makedirs(target_dir, exist_ok=True) + log_info(f"创建/确认 EFI 目录: {target_dir}") + except Exception as e: + log_error(f"创建 EFI 目录失败: {e}") + return False + + # 查找源 EFI 文件 + # 可能的源路径: + source_paths = [] + + # 1. 从 /boot/efi/EFI/centos 或 /boot/efi/EFI/redhat 复制 + for subdir in ["centos", "redhat", "fedora", "rocky", "almalinux", "boot"]: + source_paths.append(os.path.join(efi_firmware_dir, subdir, efi_grub_file)) + source_paths.append(os.path.join(efi_firmware_dir, subdir, "shimx64.efi")) + source_paths.append(os.path.join(efi_firmware_dir, subdir, "shim.efi")) + + # 2. 从 /boot/efi/ 直接查找 + source_paths.append(os.path.join(mount_point, "boot/efi", efi_grub_file)) + source_paths.append(os.path.join(mount_point, "boot/efi", "shimx64.efi")) + + # 3. 从 /usr/lib/grub/x86_64-efi/ 复制 grubx64.efi + grub_modules_dir = os.path.join(mount_point, f"usr/lib/grub/{efi_target}") + source_paths.append(os.path.join(grub_modules_dir, "grubx64.efi")) + + # 查找 shim 和 grub + shim_source = None + grub_source = None + + for path in source_paths: + if os.path.exists(path): + log_info(f"找到 EFI 文件: {path}") + if "shim" in path.lower(): + shim_source = path + elif "grub" in path.lower(): + grub_source = path + + # 复制文件 + try: + if grub_source: + grub_target = os.path.join(target_dir, efi_grub_file) + shutil.copy2(grub_source, grub_target) + log_success(f"✓ 复制 GRUB: {grub_source} -> {grub_target}") + + if shim_source: + # 如果使用 shim,复制 shim 并重命名为 grubx64.efi + # 或者保留 shim 文件名并在 NVRAM 中注册 + shim_target = os.path.join(target_dir, "shimx64.efi") + shutil.copy2(shim_source, shim_target) + log_success(f"✓ 复制 Shim: {shim_source} -> {shim_target}") + + # 同时复制为 bootloader 名称,便于启动 + if efi_grub_file != "shimx64.efi": + shutil.copy2(shim_source, os.path.join(target_dir, efi_grub_file)) + + # 如果没有找到任何文件,尝试从 grub 模块生成 + if not grub_source and not shim_source: + log_warning("未找到现有 EFI 文件,尝试使用 grub-mkimage 生成...") + + # 构建 grub.efi 路径 + grub_efi_output = os.path.join(target_dir, efi_grub_file) + + # 获取模块列表(必需模块) + modules = "part_gpt part_msdos fat ext2 xfs btrfs normal boot linux configfile search search_fs_uuid search_fs_file" + + # 尝试使用 grub-mkimage 生成 EFI 文件 + chroot_cmd_prefix = ["sudo", "chroot", mount_point] + mkimage_cmd = chroot_cmd_prefix + [ + "grub2-mkimage", + "-o", grub_efi_output, + "-O", efi_target, + "-p", "/boot/grub2", + ] + modules.split() + + success, _, stderr = run_command( + mkimage_cmd, + "使用 grub-mkimage 生成 EFI 文件", + timeout=60 + ) + + if success and os.path.exists(grub_efi_output): + log_success(f"✓ 成功生成 EFI 文件: {grub_efi_output}") + else: + log_error(f"生成 EFI 文件失败: {stderr}") + return False + + return True + + except Exception as e: + log_error(f"手动安装 EFI 文件失败: {e}") + return False + + def install_efi_fallback(mount_point: str, efi_target: str, efi_grub_file: str, efi_boot_file: str, bootloader_id: str = "GRUB") -> bool: """ @@ -1920,6 +2030,16 @@ def chroot_and_repair_grub(mount_point: str, target_disk: str, else: install_errors.append(f"no-nvram 模式: {stderr}") + # 如果所有 grub2-install 方法都失败,尝试手动复制 EFI 文件(针对 Secure Boot 问题) + if not grub_install_success and "Secure Boot" in str(install_errors): + log_info("grub2-install 不支持 Secure Boot,尝试手动安装 EFI 文件...") + success = _manual_install_efi_files(mount_point, efi_target, efi_grub_file, efi_boot_file, bootloader_id) + if success: + grub_install_success = True + log_success("✓ 手动 EFI 文件安装成功") + else: + log_error("手动 EFI 文件安装失败") + # 安装 EFI fallback(如果启用) if grub_install_success and use_fallback: log_info("安装 EFI fallback 文件...")