This commit is contained in:
zj
2026-02-12 01:16:16 +08:00
parent 85d0f00a48
commit 6a08d19fd8
3 changed files with 217 additions and 1 deletions

View File

@@ -49,10 +49,27 @@ BootRepairTool/
- **NEW**: Gentoo
- **NEW**: NixOS
### 4. GRUB 修复 (`backend.py:chroot_and_repair_grub`)
### 4. 内核恢复功能 (`backend.py:check_and_restore_kernel`)
**NEW v2.1**: 自动检测和恢复缺失的内核文件
#### 检测逻辑
- 检查 `/boot` 目录下的 `vmlinuz-*``initramfs-*.img` 文件
- 如果缺失,自动尝试恢复
#### 恢复方法
1. **从 /usr/lib/modules 复制** - 如果根分区包含内核模块目录
2. **重新安装内核包** - 使用包管理器重新安装内核:
- CentOS/RHEL/Rocky/Alma: `yum reinstall kernel-core kernel-modules`
- Debian/Ubuntu: `apt-get install --reinstall linux-image-generic`
- Arch/Manjaro: `pacman -S linux`
3. **重新生成 initramfs** - 使用 dracut/mkinitcpio/update-initramfs
### 5. GRUB 修复 (`backend.py:chroot_and_repair_grub`)
#### BIOS 模式
- `grub-install --target=i386-pc --recheck --force /dev/sdX`
- **NEW**: 独立 `/boot` 分区支持(添加 `--boot-directory=/boot`
#### UEFI 模式 (参考 Calamares 实现)
- 自动检测系统架构 (x86_64/i386/arm64/loongarch64)

Binary file not shown.

View File

@@ -1228,6 +1228,199 @@ def install_efi_fallback(mount_point: str, efi_target: str, efi_grub_file: str,
return False
def check_and_restore_kernel(mount_point: str, distro_type: str, has_separate_boot: bool) -> Tuple[bool, str]:
"""
检查并恢复 /boot 目录下的内核文件。
如果内核文件缺失,尝试从 /usr/lib/modules 复制或重新安装内核包。
返回: (是否成功, 错误信息)
"""
log_step("检查内核文件", f"独立 /boot 分区: {has_separate_boot}")
boot_dir = os.path.join(mount_point, "boot")
# 检查现有的内核文件
vmlinuz_files = []
initramfs_files = []
try:
if os.path.exists(boot_dir):
for f in os.listdir(boot_dir):
if f.startswith("vmlinuz-"):
vmlinuz_files.append(f)
elif f.startswith("initramfs-") and f.endswith(".img"):
initramfs_files.append(f)
except Exception as e:
log_warning(f"检查 /boot 目录失败: {e}")
log_info(f"发现 {len(vmlinuz_files)} 个内核文件, {len(initramfs_files)} 个 initramfs 文件")
# 如果内核文件存在,检查是否完整
if vmlinuz_files and initramfs_files:
log_success("✓ 内核文件已存在")
return True, ""
log_warning("内核文件缺失,尝试恢复...")
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
# 方法1: 从 /usr/lib/modules 复制内核(如果存在)
usr_lib_modules = os.path.join(mount_point, "usr/lib/modules")
if os.path.exists(usr_lib_modules):
log_info("检查 /usr/lib/modules 中的内核...")
try:
kernel_versions = []
for d in os.listdir(usr_lib_modules):
# 跳过 rescue 和普通目录,查找版本号格式的目录
if os.path.isdir(os.path.join(usr_lib_modules, d)) and not d.endswith("kdump"):
kernel_versions.append(d)
if kernel_versions:
log_info(f"找到内核版本: {kernel_versions}")
for kernel_ver in kernel_versions:
# 检查该版本的内核文件是否存在
kernel_source = os.path.join(usr_lib_modules, kernel_ver, "vmlinuz")
if not os.path.exists(kernel_source):
# 某些系统使用不同的路径
kernel_source = os.path.join(mount_point, f"usr/lib/modules/{kernel_ver}/vmlinuz")
if os.path.exists(kernel_source):
kernel_target = os.path.join(boot_dir, f"vmlinuz-{kernel_ver}")
log_info(f"复制内核: {kernel_source} -> {kernel_target}")
try:
shutil.copy2(kernel_source, kernel_target)
log_success(f"✓ 复制内核成功: vmlinuz-{kernel_ver}")
except Exception as e:
log_error(f"复制内核失败: {e}")
else:
log_warning("/usr/lib/modules 中没有找到内核")
except Exception as e:
log_warning(f"检查 /usr/lib/modules 失败: {e}")
# 重新检查内核文件
vmlinuz_files = []
try:
for f in os.listdir(boot_dir):
if f.startswith("vmlinuz-"):
vmlinuz_files.append(f)
except:
pass
# 方法2: 重新安装内核包如果方法1失败
if not vmlinuz_files:
log_info("尝试重新安装内核包...")
# 根据发行版选择正确的包名
kernel_packages = {
"centos": ["kernel-core", "kernel-modules"],
"rhel": ["kernel-core", "kernel-modules"],
"fedora": ["kernel-core", "kernel-modules"],
"rocky": ["kernel-core", "kernel-modules"],
"almalinux": ["kernel-core", "kernel-modules"],
"debian": ["linux-image-generic"],
"ubuntu": ["linux-image-generic"],
"arch": ["linux"],
"manjaro": ["linux"],
}
packages = kernel_packages.get(distro_type, ["kernel"])
if distro_type in ["centos", "rhel", "fedora", "rocky", "almalinux"]:
success, _, stderr = run_command(
chroot_cmd_prefix + ["yum", "reinstall", "-y"] + packages,
f"重新安装内核包",
timeout=300
)
elif distro_type in ["debian", "ubuntu"]:
success, _, stderr = run_command(
chroot_cmd_prefix + ["apt-get", "install", "--reinstall", "-y"] + packages,
f"重新安装内核包",
timeout=300
)
elif distro_type in ["arch", "manjaro"]:
success, _, stderr = run_command(
chroot_cmd_prefix + ["pacman", "-S", "--noconfirm"] + packages,
f"重新安装内核包",
timeout=300
)
else:
log_warning(f"不支持的发行版 '{distro_type}',无法自动安装内核")
return False, "无法自动恢复内核文件"
if not success:
log_error("内核包安装失败")
return False, "内核包安装失败"
# 方法3: 重新生成 initramfs
log_info("重新生成 initramfs...")
# 获取当前存在的内核版本
vmlinuz_files = []
try:
for f in os.listdir(boot_dir):
if f.startswith("vmlinuz-"):
vmlinuz_files.append(f)
except:
pass
if not vmlinuz_files:
log_error("无法找到内核文件,无法生成 initramfs")
return False, "内核文件缺失"
for vmlinuz in vmlinuz_files:
kernel_ver = vmlinuz.replace("vmlinuz-", "")
log_info(f"为内核 {kernel_ver} 生成 initramfs...")
if distro_type in ["centos", "rhel", "fedora", "rocky", "almalinux"]:
# CentOS/RHEL 使用 dracut
success, _, stderr = run_command(
chroot_cmd_prefix + ["dracut", "-f", f"/boot/initramfs-{kernel_ver}.img", kernel_ver],
f"生成 initramfs for {kernel_ver}",
timeout=120
)
elif distro_type in ["debian", "ubuntu"]:
# Debian/Ubuntu 使用 update-initramfs
success, _, stderr = run_command(
chroot_cmd_prefix + ["update-initramfs", "-c", "-k", kernel_ver],
f"生成 initramfs for {kernel_ver}",
timeout=120
)
elif distro_type in ["arch", "manjaro"]:
# Arch 使用 mkinitcpio
success, _, stderr = run_command(
chroot_cmd_prefix + ["mkinitcpio", "-p", kernel_ver],
f"生成 initramfs for {kernel_ver}",
timeout=120
)
else:
# 通用方法:尝试 dracut
success, _, stderr = run_command(
chroot_cmd_prefix + ["dracut", "-f", f"/boot/initramfs-{kernel_ver}.img", kernel_ver],
f"生成 initramfs for {kernel_ver}",
timeout=120
)
if success:
log_success(f"✓ initramfs 生成成功: {kernel_ver}")
else:
log_warning(f"initramfs 生成失败: {stderr}")
# 最终检查
try:
vmlinuz_count = sum(1 for f in os.listdir(boot_dir) if f.startswith("vmlinuz-"))
initramfs_count = sum(1 for f in os.listdir(boot_dir) if f.startswith("initramfs-") and f.endswith(".img"))
log_info(f"恢复完成: {vmlinuz_count} 个内核, {initramfs_count} 个 initramfs")
if vmlinuz_count > 0:
return True, ""
else:
return False, "内核恢复失败"
except Exception as e:
return False, f"检查 /boot 目录失败: {e}"
def chroot_and_repair_grub(mount_point: str, target_disk: str,
is_uefi: bool = False, distro_type: str = "unknown",
install_hybrid: bool = False,
@@ -1274,6 +1467,12 @@ def chroot_and_repair_grub(mount_point: str, target_disk: str,
has_separate_boot = True
log_info("检测到独立的 /boot 分区")
# 检查并恢复内核文件
kernel_ok, kernel_err = check_and_restore_kernel(mount_point, distro_type, has_separate_boot)
if not kernel_ok:
log_error(f"内核恢复失败: {kernel_err}")
return False, f"内核恢复失败: {kernel_err}"
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
# 检测 Live 环境