Consolidate Limine layout validation in bootloader utils
Move the boot-partition FAT check from GlobalMenu into validate_bootloader_layout so all three call sites (GlobalMenu, guided.py, Installer._add_limine_bootloader) share one function. Return a BootloaderValidationFailure dataclass (kind + description) instead of str | None, so callers can match on the failure kind and the description is built where partition context is in scope.
This commit is contained in:
parent
d7f6b7f871
commit
70a9797774
|
|
@ -1,36 +1,65 @@
|
|||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
from pathlib import Path
|
||||
|
||||
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
|
||||
from archinstall.lib.models.device import DiskLayoutConfiguration
|
||||
from archinstall.lib.models.device import DiskLayoutConfiguration, FilesystemType
|
||||
|
||||
_FAT_FILESYSTEMS = (FilesystemType.FAT12, FilesystemType.FAT16, FilesystemType.FAT32)
|
||||
|
||||
|
||||
class BootloaderValidationFailureKind(Enum):
|
||||
LimineNonFatBoot = auto()
|
||||
LimineLayout = auto()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BootloaderValidationFailure:
|
||||
kind: BootloaderValidationFailureKind
|
||||
description: str
|
||||
|
||||
|
||||
def validate_bootloader_layout(
|
||||
bootloader_config: BootloaderConfiguration | None,
|
||||
disk_config: DiskLayoutConfiguration | None,
|
||||
) -> str | None:
|
||||
) -> BootloaderValidationFailure | None:
|
||||
"""Validate bootloader configuration against disk layout.
|
||||
|
||||
Returns an error message if the configuration would produce an
|
||||
unbootable system, or None if it is valid.
|
||||
Returns a failure with a human-readable description if the configuration
|
||||
would produce an unbootable system, or None if it is valid.
|
||||
"""
|
||||
# Limine can only read FAT. When the ESP is the boot partition but
|
||||
# mounted outside /boot and UKI is disabled, the kernel ends up on the
|
||||
# root filesystem which Limine cannot access.
|
||||
if not (bootloader_config and bootloader_config.bootloader == Bootloader.Limine and not bootloader_config.uki and disk_config):
|
||||
if not (bootloader_config and disk_config):
|
||||
return None
|
||||
|
||||
efi_part = next(
|
||||
(p for m in disk_config.device_modifications if (p := m.get_efi_partition())),
|
||||
None,
|
||||
)
|
||||
boot_part = next(
|
||||
(p for m in disk_config.device_modifications if (p := m.get_boot_partition())),
|
||||
None,
|
||||
)
|
||||
|
||||
if efi_part and boot_part == efi_part and efi_part.mountpoint != Path('/boot'):
|
||||
return (
|
||||
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_part.mountpoint}, '
|
||||
'enable UKI or add a separate /boot partition to install Limine.'
|
||||
if bootloader_config.bootloader == Bootloader.Limine:
|
||||
boot_part = next(
|
||||
(p for m in disk_config.device_modifications if (p := m.get_boot_partition())),
|
||||
None,
|
||||
)
|
||||
|
||||
# Limine reads its config and kernels from the boot partition, which
|
||||
# must be FAT.
|
||||
if boot_part and boot_part.fs_type not in _FAT_FILESYSTEMS:
|
||||
return BootloaderValidationFailure(
|
||||
kind=BootloaderValidationFailureKind.LimineNonFatBoot,
|
||||
description='Limine does not support booting with a non-FAT boot partition.',
|
||||
)
|
||||
|
||||
# When the ESP is the boot partition but mounted outside /boot and
|
||||
# UKI is disabled, kernels end up on the root filesystem which
|
||||
# Limine cannot access.
|
||||
if not bootloader_config.uki:
|
||||
efi_part = next(
|
||||
(p for m in disk_config.device_modifications if (p := m.get_efi_partition())),
|
||||
None,
|
||||
)
|
||||
if efi_part and efi_part == boot_part and efi_part.mountpoint != Path('/boot'):
|
||||
return BootloaderValidationFailure(
|
||||
kind=BootloaderValidationFailureKind.LimineLayout,
|
||||
description=(
|
||||
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_part.mountpoint}, '
|
||||
'enable UKI or add a separate /boot partition to install Limine.'
|
||||
),
|
||||
)
|
||||
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
from pathlib import Path
|
||||
from typing import override
|
||||
|
||||
from archinstall.default_profiles.profile import GreeterType
|
||||
|
|
@ -6,6 +5,7 @@ from archinstall.lib.applications.application_menu import ApplicationMenu
|
|||
from archinstall.lib.args import ArchConfig
|
||||
from archinstall.lib.authentication.authentication_menu import AuthenticationMenu
|
||||
from archinstall.lib.bootloader.bootloader_menu import BootloaderMenu
|
||||
from archinstall.lib.bootloader.utils import validate_bootloader_layout
|
||||
from archinstall.lib.configuration import save_config
|
||||
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
|
||||
from archinstall.lib.general.general_menu import select_hostname, select_ntp, select_timezone
|
||||
|
|
@ -490,18 +490,11 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
if efi_partition.fs_type not in [FilesystemType.FAT12, FilesystemType.FAT16, FilesystemType.FAT32]:
|
||||
return 'ESP must be formatted as a FAT filesystem'
|
||||
|
||||
if bootloader == Bootloader.Limine:
|
||||
if boot_partition.fs_type not in [FilesystemType.FAT12, FilesystemType.FAT16, FilesystemType.FAT32]:
|
||||
return 'Limine does not support booting with a non-FAT boot partition'
|
||||
if self._uefi and efi_partition and boot_partition == efi_partition and efi_partition.mountpoint != Path('/boot') and not bootloader_config.uki:
|
||||
return (
|
||||
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_partition.mountpoint}, '
|
||||
'enable UKI or add a separate /boot partition'
|
||||
)
|
||||
if bootloader == Bootloader.Refind and not self._uefi:
|
||||
return 'rEFInd can only be used on UEFI systems'
|
||||
|
||||
elif bootloader == Bootloader.Refind:
|
||||
if not self._uefi:
|
||||
return 'rEFInd can only be used on UEFI systems'
|
||||
if failure := validate_bootloader_layout(bootloader_config, disk_config):
|
||||
return failure.description
|
||||
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from types import TracebackType
|
|||
from typing import Any, Self
|
||||
|
||||
from archinstall.lib.boot import Boot
|
||||
from archinstall.lib.bootloader.utils import validate_bootloader_layout
|
||||
from archinstall.lib.command import SysCommand, run
|
||||
from archinstall.lib.disk.fido import Fido2
|
||||
from archinstall.lib.disk.luks import Luks2, unlock_luks2_dev
|
||||
|
|
@ -30,7 +31,7 @@ from archinstall.lib.linux_path import LPath
|
|||
from archinstall.lib.locale.utils import verify_keyboard_layout, verify_x11_keyboard_layout
|
||||
from archinstall.lib.mirror.mirror_handler import MirrorListHandler
|
||||
from archinstall.lib.models.application import ZramAlgorithm
|
||||
from archinstall.lib.models.bootloader import Bootloader
|
||||
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
|
||||
from archinstall.lib.models.device import (
|
||||
DiskEncryption,
|
||||
DiskLayoutConfiguration,
|
||||
|
|
@ -1466,15 +1467,13 @@ class Installer:
|
|||
elif not efi_partition.mountpoint:
|
||||
raise ValueError('EFI partition is not mounted')
|
||||
|
||||
# Limine can only read FAT filesystems. When the ESP doubles as
|
||||
# the boot partition but is mounted outside /boot (e.g. /efi,
|
||||
# /boot/efi) and UKI is disabled, kernels end up on the root
|
||||
# filesystem under /boot/ which Limine cannot access.
|
||||
if boot_partition == efi_partition and efi_partition.mountpoint != Path('/boot') and not uki_enabled:
|
||||
raise DiskError(
|
||||
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_partition.mountpoint}, '
|
||||
'so enable UKI or add a separate /boot partition to install Limine.',
|
||||
)
|
||||
# Safety net for programmatic callers that bypass GlobalMenu and
|
||||
# guided.py validation.
|
||||
if failure := validate_bootloader_layout(
|
||||
BootloaderConfiguration(bootloader=Bootloader.Limine, uki=uki_enabled),
|
||||
self._disk_config,
|
||||
):
|
||||
raise DiskError(failure.description)
|
||||
|
||||
info(f'Limine EFI partition: {efi_partition.dev_path}')
|
||||
|
||||
|
|
|
|||
|
|
@ -214,11 +214,11 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
|||
|
||||
# Safety net for silent/config-file flow. The TUI menu blocks Install via
|
||||
# GlobalMenu._validate_bootloader() before reaching this point.
|
||||
if err_msg := validate_bootloader_layout(
|
||||
if failure := validate_bootloader_layout(
|
||||
arch_config_handler.config.bootloader_config,
|
||||
arch_config_handler.config.disk_config,
|
||||
):
|
||||
error(err_msg)
|
||||
error(failure.description)
|
||||
return
|
||||
|
||||
if arch_config_handler.args.dry_run:
|
||||
|
|
|
|||
Loading…
Reference in New Issue