Improve partition type detection, assignment, and misconfig handling (#3336)

This commit is contained in:
mintsuki 2025-04-04 01:03:02 +02:00 committed by GitHub
parent e6b0ebb5f3
commit fa581021eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 53 deletions

View File

@ -95,13 +95,19 @@ class PartitioningList(ListManager):
'assign_mountpoint': str(_('Assign mountpoint')),
'mark_formatting': str(_('Mark/Unmark to be formatted (wipes data)')),
'mark_bootable': str(_('Mark/Unmark as bootable')),
'mark_xbootldr': str(_('Mark/Unmark as XBOOTLDR')),
}
if self._using_gpt:
self._actions.update({
'mark_esp': str(_('Mark/Unmark as ESP')),
'mark_xbootldr': str(_('Mark/Unmark as XBOOTLDR'))
})
self._actions.update({
'set_filesystem': str(_('Change filesystem')),
'btrfs_mark_compressed': str(_('Mark/Unmark as compressed')), # btrfs only
'btrfs_mark_nodatacow': str(_('Mark/Unmark as nodatacow')), # btrfs only
'btrfs_set_subvolumes': str(_('Set subvolumes')), # btrfs only
'delete_partition': str(_('Delete partition'))
}
})
device_partitions = []
@ -239,8 +245,14 @@ class PartitioningList(ListManager):
# how do we know it was the original one?
not_filter += [
self._actions['set_filesystem'],
self._actions['mark_bootable'],
self._actions['mark_xbootldr'],
self._actions['mark_bootable']
]
if self._using_gpt:
not_filter += [
self._actions['mark_esp'],
self._actions['mark_xbootldr']
]
not_filter += [
self._actions['btrfs_mark_compressed'],
self._actions['btrfs_mark_nodatacow'],
self._actions['btrfs_set_subvolumes']
@ -287,23 +299,46 @@ class PartitioningList(ListManager):
action_key = [k for k, v in self._actions.items() if v == action][0]
match action_key:
case 'assign_mountpoint':
partition.mountpoint = self._prompt_mountpoint()
if partition.mountpoint == Path('/boot'):
partition.set_flag(PartitionFlag.BOOT)
if self._using_gpt:
partition.set_flag(PartitionFlag.ESP)
new_mountpoint = self._prompt_mountpoint()
if not partition.is_swap():
if partition.is_home():
partition.invert_flag(PartitionFlag.LINUX_HOME)
partition.mountpoint = new_mountpoint
if partition.is_root():
partition.flags = []
if partition.is_boot():
partition.flags = []
partition.set_flag(PartitionFlag.BOOT)
if self._using_gpt:
partition.set_flag(PartitionFlag.ESP)
if partition.is_home():
partition.flags = []
partition.set_flag(PartitionFlag.LINUX_HOME)
case 'mark_formatting':
self._prompt_formatting(partition)
case 'mark_bootable':
partition.invert_flag(PartitionFlag.BOOT)
if self._using_gpt:
if not partition.is_swap():
partition.invert_flag(PartitionFlag.BOOT)
case 'mark_esp':
if not partition.is_root() and not partition.is_home() and not partition.is_swap():
if PartitionFlag.XBOOTLDR in partition.flags:
partition.invert_flag(PartitionFlag.XBOOTLDR)
partition.invert_flag(PartitionFlag.ESP)
case 'mark_xbootldr':
partition.invert_flag(PartitionFlag.XBOOTLDR)
if not partition.is_root() and not partition.is_home() and not partition.is_swap():
if PartitionFlag.ESP in partition.flags:
partition.invert_flag(PartitionFlag.ESP)
partition.invert_flag(PartitionFlag.XBOOTLDR)
case 'set_filesystem':
fs_type = self._prompt_partition_fs_type()
if fs_type:
if partition.is_swap():
partition.invert_flag(PartitionFlag.SWAP)
partition.fs_type = fs_type
if partition.is_swap():
partition.mountpoint = None
partition.flags = []
partition.set_flag(PartitionFlag.SWAP)
# btrfs subvolumes will define mountpoints
if fs_type == FilesystemType.Btrfs:
partition.mountpoint = None
@ -390,7 +425,6 @@ class PartitioningList(ListManager):
def _prompt_mountpoint(self) -> Path:
header = str(_('Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) + '\n'
header += str(_('If mountpoint /boot is set, then the partition will also be marked as bootable.')) + '\n'
prompt = str(_('Mountpoint'))
mountpoint = prompt_dir(prompt, header, validate=False, allow_skip=False)
@ -520,6 +554,8 @@ class PartitioningList(ListManager):
if self._using_gpt:
partition.set_flag(PartitionFlag.ESP)
elif partition.is_swap():
partition.mountpoint = None
partition.flags = []
partition.set_flag(PartitionFlag.SWAP)
return partition

View File

@ -425,21 +425,40 @@ class GlobalMenu(AbstractMenu):
shim if necessary.
"""
bootloader = self._item_group.find_by_key('bootloader').value
root_partition: PartitionModification | None = None
boot_partition: PartitionModification | None = None
efi_partition: PartitionModification | None = None
if disk_config := self._item_group.find_by_key('disk_config').value:
for layout in disk_config.device_modifications:
if root_partition := layout.get_root_partition():
break
for layout in disk_config.device_modifications:
if boot_partition := layout.get_boot_partition():
break
if SysInfo.has_uefi():
for layout in disk_config.device_modifications:
if efi_partition := layout.get_efi_partition():
break
else:
return "No disk layout selected"
if root_partition is None:
return "Root partition not found"
if boot_partition is None:
return "Boot partition not found"
if SysInfo.has_uefi():
if efi_partition is None:
return "EFI system partition (ESP) not found"
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 != FilesystemType.Fat32:
return "Limine does not support booting without a FAT boot partition"
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"
return None

View File

@ -871,9 +871,6 @@ class PartitionModification:
partuuid: str | None = None
uuid: str | None = None
_efi_indicator_flags = (PartitionFlag.BOOT, PartitionFlag.ESP)
_boot_indicator_flags = (PartitionFlag.BOOT, PartitionFlag.XBOOTLDR)
def __post_init__(self) -> None:
# needed to use the object as a dictionary key due to hash func
if not hasattr(self, '_obj_id'):
@ -951,26 +948,16 @@ class PartitionModification:
raise ValueError('Mountpoint is not specified')
def is_efi(self) -> bool:
return (
any(set(self.flags) & set(self._efi_indicator_flags))
and (
self.fs_type == FilesystemType.Fat12
or self.fs_type == FilesystemType.Fat16
or self.fs_type == FilesystemType.Fat32
)
and PartitionFlag.XBOOTLDR not in self.flags
)
return PartitionFlag.ESP in self.flags
def is_boot(self) -> bool:
"""
Returns True if any of the boot indicator flags are found in self.flags
or if the partition is mounted on /boot
"""
return any(set(self.flags) & set(self._boot_indicator_flags)) or Path('/boot') == self.mountpoint
if self.mountpoint is not None:
return self.mountpoint == Path('/boot')
return False
def is_root(self) -> bool:
if self.mountpoint is not None:
return Path('/') == self.mountpoint
return self.mountpoint == Path('/')
else:
for subvol in self.btrfs_subvols:
if subvol.is_root():
@ -979,10 +966,9 @@ class PartitionModification:
return False
def is_home(self) -> bool:
return (
self.mountpoint == Path('/home')
or PartitionFlag.LINUX_HOME in self.flags
)
if self.mountpoint is not None:
return self.mountpoint == Path('/home')
return False
def is_swap(self) -> bool:
return self.fs_type == FilesystemType.LinuxSwap
@ -1377,26 +1363,12 @@ class DeviceModification:
self.partitions.append(partition)
def get_efi_partition(self) -> PartitionModification | None:
"""
Similar to get_boot_partition() but excludes XBOOTLDR partitions from it's candidates.
"""
filtered = filter(lambda x: x.is_efi() and x.mountpoint, self.partitions)
return next(filtered, None)
def get_boot_partition(self) -> PartitionModification | None:
"""
Returns the first partition marked as XBOOTLDR (PARTTYPE id of bc13c2ff-...) or Boot and has a mountpoint.
Only returns XBOOTLDR if separate EFI is detected using self.get_efi_partition()
Will return None if no suitable partition is found.
"""
if efi_partition := self.get_efi_partition():
filtered = filter(lambda x: x.is_boot() and x != efi_partition and x.mountpoint, self.partitions)
if boot_partition := next(filtered, None):
return boot_partition
return efi_partition
else:
filtered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions)
return next(filtered, None)
filtered = filter(lambda x: x.is_boot() and x.mountpoint, self.partitions)
return next(filtered, None)
def get_root_partition(self) -> PartitionModification | None:
filtered = filter(lambda x: x.is_root(), self.partitions)