4
This commit is contained in:
19
AGENTS.md
19
AGENTS.md
@@ -49,10 +49,27 @@ BootRepairTool/
|
|||||||
- **NEW**: Gentoo
|
- **NEW**: Gentoo
|
||||||
- **NEW**: NixOS
|
- **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 模式
|
#### BIOS 模式
|
||||||
- `grub-install --target=i386-pc --recheck --force /dev/sdX`
|
- `grub-install --target=i386-pc --recheck --force /dev/sdX`
|
||||||
|
- **NEW**: 独立 `/boot` 分区支持(添加 `--boot-directory=/boot`)
|
||||||
|
|
||||||
#### UEFI 模式 (参考 Calamares 实现)
|
#### UEFI 模式 (参考 Calamares 实现)
|
||||||
- 自动检测系统架构 (x86_64/i386/arm64/loongarch64)
|
- 自动检测系统架构 (x86_64/i386/arm64/loongarch64)
|
||||||
|
|||||||
Binary file not shown.
199
backend.py
199
backend.py
@@ -1228,6 +1228,199 @@ def install_efi_fallback(mount_point: str, efi_target: str, efi_grub_file: str,
|
|||||||
return False
|
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,
|
def chroot_and_repair_grub(mount_point: str, target_disk: str,
|
||||||
is_uefi: bool = False, distro_type: str = "unknown",
|
is_uefi: bool = False, distro_type: str = "unknown",
|
||||||
install_hybrid: bool = False,
|
install_hybrid: bool = False,
|
||||||
@@ -1274,6 +1467,12 @@ def chroot_and_repair_grub(mount_point: str, target_disk: str,
|
|||||||
has_separate_boot = True
|
has_separate_boot = True
|
||||||
log_info("检测到独立的 /boot 分区")
|
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]
|
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
|
||||||
|
|
||||||
# 检测 Live 环境
|
# 检测 Live 环境
|
||||||
|
|||||||
Reference in New Issue
Block a user