This commit is contained in:
zj
2026-02-12 01:37:27 +08:00
parent 6a08d19fd8
commit add8a9643a
3 changed files with 242 additions and 1 deletions

View File

@@ -1421,6 +1421,215 @@ def check_and_restore_kernel(mount_point: str, distro_type: str, has_separate_bo
return False, f"检查 /boot 目录失败: {e}"
def restore_bls_entries(mount_point: str, distro_type: str) -> Tuple[bool, str]:
"""
恢复 BLS (Boot Loader Specification) 配置条目。
CentOS/RHEL/Fedora 等使用 BLS 格式,启动项在 /boot/loader/entries/*.conf
返回: (是否成功, 错误信息)
"""
# 检查是否使用 BLS
loader_dir = os.path.join(mount_point, "boot/loader/entries")
grub_cfg_path = os.path.join(mount_point, "boot/grub2/grub.cfg")
# 检查 grub.cfg 是否包含 blscfg
uses_bls = False
try:
if os.path.exists(grub_cfg_path):
with open(grub_cfg_path, 'r') as f:
content = f.read()
if 'blscfg' in content or 'BootLoaderSpec' in content:
uses_bls = True
log_info("检测到系统使用 BLS (Boot Loader Specification)")
except Exception as e:
log_debug(f"检查 BLS 失败: {e}")
if not uses_bls:
log_debug("系统不使用 BLS跳过 BLS 恢复")
return True, ""
log_step("恢复 BLS 启动条目")
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
# 方法1: 使用 kernel-install 重新生成 BLS 条目(推荐)
log_info("尝试使用 kernel-install 重新生成 BLS 条目...")
# 获取已安装的内核版本
boot_dir = os.path.join(mount_point, "boot")
kernel_versions = []
try:
for f in os.listdir(boot_dir):
if f.startswith("vmlinuz-"):
kernel_ver = f.replace("vmlinuz-", "")
kernel_versions.append(kernel_ver)
except Exception as e:
log_warning(f"读取 /boot 目录失败: {e}")
if not kernel_versions:
log_error("没有找到内核版本,无法生成 BLS 条目")
return False, "没有内核版本"
log_info(f"发现内核版本: {kernel_versions}")
# 创建 loader 目录
os.makedirs(loader_dir, exist_ok=True)
for kernel_ver in kernel_versions:
# 检查是否已有 BLS 条目
entry_file = os.path.join(loader_dir, f"{kernel_ver}.conf")
if os.path.exists(entry_file):
log_info(f"BLS 条目已存在: {entry_file}")
continue
log_info(f"为内核 {kernel_ver} 生成 BLS 条目...")
# 尝试使用 kernel-install
success, _, stderr = run_command(
chroot_cmd_prefix + ["kernel-install", "add", kernel_ver, f"/boot/vmlinuz-{kernel_ver}"],
f"生成 BLS 条目 for {kernel_ver}",
timeout=30
)
if success:
log_success(f"✓ kernel-install 成功: {kernel_ver}")
else:
log_warning(f"kernel-install 失败,尝试手动创建 BLS 条目...")
# 方法2: 手动创建 BLS 条目
machine_id = ""
try:
machine_id_path = os.path.join(mount_point, "etc/machine-id")
if os.path.exists(machine_id_path):
with open(machine_id_path, 'r') as f:
machine_id = f.read().strip()
except:
pass
if not machine_id:
machine_id = "unknown"
# 读取 /etc/os-release 获取标题
os_name = "Linux"
os_version = ""
try:
os_release_path = os.path.join(mount_point, "etc/os-release")
if os.path.exists(os_release_path):
with open(os_release_path, 'r') as f:
for line in f:
if line.startswith('NAME='):
os_name = line.split('=')[1].strip().strip('"')
elif line.startswith('VERSION_ID='):
os_version = line.split('=')[1].strip().strip('"')
except:
pass
title = f"{os_name}"
if os_version:
title += f" {os_version}"
# 获取根分区 UUID 或路径
root_device = "/dev/mapper/cl-root" # 默认,应该根据实际检测
# 尝试从 /etc/fstab 获取根分区
try:
fstab_path = os.path.join(mount_point, "etc/fstab")
if os.path.exists(fstab_path):
with open(fstab_path, 'r') as f:
for line in f:
if line.startswith('/') and ' / ' in line:
parts = line.split()
if len(parts) >= 2 and parts[1] == '/':
root_device = parts[0]
break
except:
pass
# 创建 BLS 条目文件
bls_content = f"""title {title} ({kernel_ver})
version {kernel_ver}
linux /vmlinuz-{kernel_ver}
initrd /initramfs-{kernel_ver}.img
options root={root_device} ro
"""
# 对于 CentOS/RHEL添加特定的 LVM 和 resume 参数
if distro_type in ["centos", "rhel", "fedora", "rocky", "almalinux"]:
bls_content = f"""title {title} ({kernel_ver})
version {kernel_ver}
linux /vmlinuz-{kernel_ver}
initrd /initramfs-{kernel_ver}.img
options root={root_device} ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet
"""
entry_filename = f"{machine_id}-{kernel_ver}.conf"
entry_path = os.path.join(loader_dir, entry_filename)
try:
with open(entry_path, 'w') as f:
f.write(bls_content)
log_success(f"✓ 手动创建 BLS 条目: {entry_path}")
except Exception as e:
log_error(f"创建 BLS 条目失败: {e}")
return False, f"创建 BLS 条目失败: {e}"
# 方法3: 重新运行 grub2-mkconfig 确保 BLS 支持正确
log_info("重新生成 grub.cfg 以确保 BLS 支持...")
# 找到 grub2-mkconfig 或 grub-mkconfig
mkconfig_cmd = None
for cmd in ["/usr/sbin/grub2-mkconfig", "/sbin/grub2-mkconfig", "/usr/bin/grub2-mkconfig"]:
if os.path.exists(os.path.join(mount_point, cmd.lstrip('/'))):
mkconfig_cmd = cmd
break
if not mkconfig_cmd:
for cmd in ["/usr/sbin/grub-mkconfig", "/sbin/grub-mkconfig", "/usr/bin/grub-mkconfig"]:
if os.path.exists(os.path.join(mount_point, cmd.lstrip('/'))):
mkconfig_cmd = cmd
break
if mkconfig_cmd:
# 找到 grub.cfg 路径
grub_cfg = "/boot/grub2/grub.cfg"
for cfg_path in ["/boot/grub2/grub.cfg", "/boot/grub/grub.cfg"]:
if os.path.exists(os.path.join(mount_point, cfg_path.lstrip('/'))):
grub_cfg = cfg_path
break
success, _, stderr = run_command(
chroot_cmd_prefix + [mkconfig_cmd, "-o", grub_cfg],
"重新生成 grub.cfg",
timeout=60
)
if success:
log_success("✓ grub.cfg 重新生成成功")
else:
log_warning(f"grub.cfg 重新生成失败: {stderr}")
# 最终检查
try:
if os.path.exists(loader_dir):
entries = [f for f in os.listdir(loader_dir) if f.endswith('.conf')]
log_info(f"BLS 条目数量: {len(entries)}")
for entry in entries:
log_info(f" - {entry}")
if len(entries) > 0:
log_success("✓ BLS 配置恢复完成")
return True, ""
else:
log_warning("没有 BLS 条目生成")
return False, "BLS 条目生成失败"
else:
log_error("loader/entries 目录不存在")
return False, "BLS 目录不存在"
except Exception as e:
return False, f"检查 BLS 条目失败: {e}"
def chroot_and_repair_grub(mount_point: str, target_disk: str,
is_uefi: bool = False, distro_type: str = "unknown",
install_hybrid: bool = False,
@@ -1473,6 +1682,12 @@ def chroot_and_repair_grub(mount_point: str, target_disk: str,
log_error(f"内核恢复失败: {kernel_err}")
return False, f"内核恢复失败: {kernel_err}"
# 恢复 BLS 启动条目CentOS/RHEL/Fedora 使用 BLS
bls_ok, bls_err = restore_bls_entries(mount_point, distro_type)
if not bls_ok:
log_warning(f"BLS 恢复失败: {bls_err}")
# BLS 失败不终止,继续尝试传统菜单
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
# 检测 Live 环境