4
This commit is contained in:
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
|
||||
|
||||
|
||||
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 环境
|
||||
|
||||
Reference in New Issue
Block a user