Skip to content

Commit 70a9797

Browse files
committed
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.
1 parent d7f6b7f commit 70a9797

4 files changed

Lines changed: 67 additions & 46 deletions

File tree

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,65 @@
1+
from dataclasses import dataclass
2+
from enum import Enum, auto
13
from pathlib import Path
24

35
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
4-
from archinstall.lib.models.device import DiskLayoutConfiguration
6+
from archinstall.lib.models.device import DiskLayoutConfiguration, FilesystemType
7+
8+
_FAT_FILESYSTEMS = (FilesystemType.FAT12, FilesystemType.FAT16, FilesystemType.FAT32)
9+
10+
11+
class BootloaderValidationFailureKind(Enum):
12+
LimineNonFatBoot = auto()
13+
LimineLayout = auto()
14+
15+
16+
@dataclass(frozen=True)
17+
class BootloaderValidationFailure:
18+
kind: BootloaderValidationFailureKind
19+
description: str
520

621

722
def validate_bootloader_layout(
823
bootloader_config: BootloaderConfiguration | None,
924
disk_config: DiskLayoutConfiguration | None,
10-
) -> str | None:
25+
) -> BootloaderValidationFailure | None:
1126
"""Validate bootloader configuration against disk layout.
1227
13-
Returns an error message if the configuration would produce an
14-
unbootable system, or None if it is valid.
28+
Returns a failure with a human-readable description if the configuration
29+
would produce an unbootable system, or None if it is valid.
1530
"""
16-
# Limine can only read FAT. When the ESP is the boot partition but
17-
# mounted outside /boot and UKI is disabled, the kernel ends up on the
18-
# root filesystem which Limine cannot access.
19-
if not (bootloader_config and bootloader_config.bootloader == Bootloader.Limine and not bootloader_config.uki and disk_config):
31+
if not (bootloader_config and disk_config):
2032
return None
2133

22-
efi_part = next(
23-
(p for m in disk_config.device_modifications if (p := m.get_efi_partition())),
24-
None,
25-
)
26-
boot_part = next(
27-
(p for m in disk_config.device_modifications if (p := m.get_boot_partition())),
28-
None,
29-
)
30-
31-
if efi_part and boot_part == efi_part and efi_part.mountpoint != Path('/boot'):
32-
return (
33-
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_part.mountpoint}, '
34-
'enable UKI or add a separate /boot partition to install Limine.'
34+
if bootloader_config.bootloader == Bootloader.Limine:
35+
boot_part = next(
36+
(p for m in disk_config.device_modifications if (p := m.get_boot_partition())),
37+
None,
3538
)
39+
40+
# Limine reads its config and kernels from the boot partition, which
41+
# must be FAT.
42+
if boot_part and boot_part.fs_type not in _FAT_FILESYSTEMS:
43+
return BootloaderValidationFailure(
44+
kind=BootloaderValidationFailureKind.LimineNonFatBoot,
45+
description='Limine does not support booting with a non-FAT boot partition.',
46+
)
47+
48+
# When the ESP is the boot partition but mounted outside /boot and
49+
# UKI is disabled, kernels end up on the root filesystem which
50+
# Limine cannot access.
51+
if not bootloader_config.uki:
52+
efi_part = next(
53+
(p for m in disk_config.device_modifications if (p := m.get_efi_partition())),
54+
None,
55+
)
56+
if efi_part and efi_part == boot_part and efi_part.mountpoint != Path('/boot'):
57+
return BootloaderValidationFailure(
58+
kind=BootloaderValidationFailureKind.LimineLayout,
59+
description=(
60+
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_part.mountpoint}, '
61+
'enable UKI or add a separate /boot partition to install Limine.'
62+
),
63+
)
64+
3665
return None

archinstall/lib/global_menu.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from pathlib import Path
21
from typing import override
32

43
from archinstall.default_profiles.profile import GreeterType
54
from archinstall.lib.applications.application_menu import ApplicationMenu
65
from archinstall.lib.args import ArchConfig
76
from archinstall.lib.authentication.authentication_menu import AuthenticationMenu
87
from archinstall.lib.bootloader.bootloader_menu import BootloaderMenu
8+
from archinstall.lib.bootloader.utils import validate_bootloader_layout
99
from archinstall.lib.configuration import save_config
1010
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
1111
from archinstall.lib.general.general_menu import select_hostname, select_ntp, select_timezone
@@ -490,18 +490,11 @@ def _validate_bootloader(self) -> str | None:
490490
if efi_partition.fs_type not in [FilesystemType.FAT12, FilesystemType.FAT16, FilesystemType.FAT32]:
491491
return 'ESP must be formatted as a FAT filesystem'
492492

493-
if bootloader == Bootloader.Limine:
494-
if boot_partition.fs_type not in [FilesystemType.FAT12, FilesystemType.FAT16, FilesystemType.FAT32]:
495-
return 'Limine does not support booting with a non-FAT boot partition'
496-
if self._uefi and efi_partition and boot_partition == efi_partition and efi_partition.mountpoint != Path('/boot') and not bootloader_config.uki:
497-
return (
498-
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_partition.mountpoint}, '
499-
'enable UKI or add a separate /boot partition'
500-
)
501-
502-
elif bootloader == Bootloader.Refind:
503-
if not self._uefi:
504-
return 'rEFInd can only be used on UEFI systems'
493+
if bootloader == Bootloader.Refind and not self._uefi:
494+
return 'rEFInd can only be used on UEFI systems'
495+
496+
if failure := validate_bootloader_layout(bootloader_config, disk_config):
497+
return failure.description
505498

506499
return None
507500

archinstall/lib/installer.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from typing import Any, Self
1313

1414
from archinstall.lib.boot import Boot
15+
from archinstall.lib.bootloader.utils import validate_bootloader_layout
1516
from archinstall.lib.command import SysCommand, run
1617
from archinstall.lib.disk.fido import Fido2
1718
from archinstall.lib.disk.luks import Luks2, unlock_luks2_dev
@@ -30,7 +31,7 @@
3031
from archinstall.lib.locale.utils import verify_keyboard_layout, verify_x11_keyboard_layout
3132
from archinstall.lib.mirror.mirror_handler import MirrorListHandler
3233
from archinstall.lib.models.application import ZramAlgorithm
33-
from archinstall.lib.models.bootloader import Bootloader
34+
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
3435
from archinstall.lib.models.device import (
3536
DiskEncryption,
3637
DiskLayoutConfiguration,
@@ -1466,15 +1467,13 @@ def _add_limine_bootloader(
14661467
elif not efi_partition.mountpoint:
14671468
raise ValueError('EFI partition is not mounted')
14681469

1469-
# Limine can only read FAT filesystems. When the ESP doubles as
1470-
# the boot partition but is mounted outside /boot (e.g. /efi,
1471-
# /boot/efi) and UKI is disabled, kernels end up on the root
1472-
# filesystem under /boot/ which Limine cannot access.
1473-
if boot_partition == efi_partition and efi_partition.mountpoint != Path('/boot') and not uki_enabled:
1474-
raise DiskError(
1475-
f'Limine requires kernels on a FAT partition. The ESP is mounted at {efi_partition.mountpoint}, '
1476-
'so enable UKI or add a separate /boot partition to install Limine.',
1477-
)
1470+
# Safety net for programmatic callers that bypass GlobalMenu and
1471+
# guided.py validation.
1472+
if failure := validate_bootloader_layout(
1473+
BootloaderConfiguration(bootloader=Bootloader.Limine, uki=uki_enabled),
1474+
self._disk_config,
1475+
):
1476+
raise DiskError(failure.description)
14781477

14791478
info(f'Limine EFI partition: {efi_partition.dev_path}')
14801479

archinstall/scripts/guided.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,11 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
214214

215215
# Safety net for silent/config-file flow. The TUI menu blocks Install via
216216
# GlobalMenu._validate_bootloader() before reaching this point.
217-
if err_msg := validate_bootloader_layout(
217+
if failure := validate_bootloader_layout(
218218
arch_config_handler.config.bootloader_config,
219219
arch_config_handler.config.disk_config,
220220
):
221-
error(err_msg)
221+
error(failure.description)
222222
return
223223

224224
if arch_config_handler.args.dry_run:

0 commit comments

Comments
 (0)