Repository: archlinux/archinstall

Issue:
Cannot create FAT32 boot partition
Recently, I was creating a new Arch install on my Steam Deck (64GB SSD).

I did the following:

Using the manual partition screen, I deleted the partitions to get a fresh start for when Arch installed.  I then used Create A New Partition > Fat32, and got the error as described in the log.
[archinstall-githubissue.log](https://github.com/archlinux/archinstall/files/14830995/archinstall-githubissue.log)

Next I tried using fdisk to wipe the partitions, in order to see if that would help resolve the issue as a workaround. I deleted the partitions using the 'd1,d2,d3,w' commands to get rid of the three partitions.

I did `pacman -Q archinstall`, and my version is 2.7.2-1

When I go into the manual partitioning again, it says I have a fat32 partition that takes up my entire disk. I deleted it, twice, to remove it from the menu. Then I  selected 'Create a new partition' > 'fat32', and it crashed with the  same error.

This is completely preventing me from installing Arch Linux at all. 




Discussion:

Resolving patch:
diff --git a/archinstall/lib/disk/device_model.py b/archinstall/lib/disk/device_model.py
--- a/archinstall/lib/disk/device_model.py
+++ b/archinstall/lib/disk/device_model.py
@@ -696,6 +696,10 @@ def __post_init__(self):
 	def __hash__(self):
 		return hash(self._obj_id)
 
+	@property
+	def end(self) -> Size:
+		return self.start + self.length
+
 	@property
 	def obj_id(self) -> str:
 		if hasattr(self, '_obj_id'):
@@ -827,14 +831,12 @@ def table_data(self) -> Dict[str, Any]:
 		"""
 		Called for displaying data in table format
 		"""
-		end = self.start + self.length
-
 		part_mod = {
 			'Status': self.status.value,
 			'Device': str(self.dev_path) if self.dev_path else '',
 			'Type': self.type.value,
 			'Start': self.start.format_size(Unit.sectors, self.start.sector_size, include_unit=False),
-			'End': end.format_size(Unit.sectors, self.start.sector_size, include_unit=False),
+			'End': self.end.format_size(Unit.sectors, self.start.sector_size, include_unit=False),
 			'Size': self.length.format_highest(),
 			'FS type': self.fs_type.value if self.fs_type else 'Unknown',
 			'Mountpoint': self.mountpoint if self.mountpoint else '',
diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py
--- a/archinstall/lib/disk/partitioning_menu.py
+++ b/archinstall/lib/disk/partitioning_menu.py
@@ -3,9 +3,13 @@
 import re
 from pathlib import Path
 from typing import Any, TYPE_CHECKING, List, Optional, Tuple
+from dataclasses import dataclass
 
-from .device_model import PartitionModification, FilesystemType, BDevice, Size, Unit, PartitionType, PartitionFlag, \
+from .device_model import (
+	PartitionModification, FilesystemType, BDevice,
+	Size, Unit, PartitionType, PartitionFlag,
 	ModificationStatus, DeviceGeometry, SectorSize, BtrfsMountOption
+)
 from ..hardware import SysInfo
 from ..menu import Menu, ListManager, MenuSelection, TextInput
 from ..output import FormattedOutput, warn
@@ -15,6 +19,12 @@
 	_: Any
 
 
+@dataclass
+class DefaultFreeSector:
+	start: Size
+	end: Size
+
+
 class PartitioningList(ListManager):
 	"""
 	subclass of ListManager for the managing of user accounts
@@ -268,21 +278,27 @@ def _prompt_size(self) -> Tuple[Size, Size]:
 		prompt += str(_('If no unit is provided, the value is interpreted as sectors')) + '\n'
 		print(prompt)
 
-		largest_free_area: DeviceGeometry = max(device_info.free_space_regions, key=lambda r: r.get_length())
+		default_free_sector = self._find_default_free_space()
+
+		if not default_free_sector:
+			default_free_sector = DefaultFreeSector(
+				Size(0, Unit.sectors, self._device.device_info.sector_size),
+				Size(0, Unit.sectors, self._device.device_info.sector_size)
+			)
 
 		# prompt until a valid start sector was entered
-		default_start = Size(largest_free_area.start, Unit.sectors, device_info.sector_size)
-		start_prompt = str(_('Enter start (default: sector {}): ')).format(largest_free_area.start)
+		start_prompt = str(_('Enter start (default: sector {}): ')).format(default_free_sector.start.value)
+
 		start_size = self._enter_size(
 			device_info.sector_size,
 			device_info.total_size,
 			start_prompt,
-			default_start,
+			default_free_sector.start,
 			None
 		)
 
-		if start_size.value == largest_free_area.start:
-			end_size = Size(largest_free_area.end, Unit.sectors, device_info.sector_size)
+		if start_size.value == default_free_sector.start.value and default_free_sector.end.value != 0:
+			end_size = default_free_sector.end
 		else:
 			end_size = device_info.total_size
 
@@ -298,6 +314,44 @@ def _prompt_size(self) -> Tuple[Size, Size]:
 
 		return start_size, end_size
 
+	def _find_default_free_space(self) -> Optional[DefaultFreeSector]:
+		device_info = self._device.device_info
+
+		largest_free_area: Optional[DeviceGeometry] = None
+		largest_deleted_area: Optional[PartitionModification] = None
+
+		if len(device_info.free_space_regions) > 0:
+			largest_free_area = max(device_info.free_space_regions, key=lambda r: r.get_length())
+
+		deleted_partitions = list(filter(lambda x: x.status == ModificationStatus.Delete, self._data))
+		if len(deleted_partitions) > 0:
+			largest_deleted_area = max(deleted_partitions, key=lambda p: p.length)
+
+		def _free_space(space: DeviceGeometry) -> DefaultFreeSector:
+			start = Size(space.start, Unit.sectors, device_info.sector_size)
+			end = Size(space.end, Unit.sectors, device_info.sector_size)
+			return DefaultFreeSector(start, end)
+
+		def _free_deleted(space: PartitionModification) -> DefaultFreeSector:
+			start = space.start.convert(Unit.sectors, self._device.device_info.sector_size)
+			end = space.end.convert(Unit.sectors, self._device.device_info.sector_size)
+			return DefaultFreeSector(start, end)
+
+		if not largest_deleted_area and largest_free_area:
+			return _free_space(largest_free_area)
+		elif not largest_free_area and largest_deleted_area:
+			return _free_deleted(largest_deleted_area)
+		elif not largest_deleted_area and not largest_free_area:
+			return None
+		elif largest_free_area and largest_deleted_area:
+			free_space = _free_space(largest_free_area)
+			if free_space.start > largest_deleted_area.start:
+				return free_space
+			else:
+				return _free_deleted(largest_deleted_area)
+
+		return None
+
 	def _create_new_partition(self) -> PartitionModification:
 		fs_type = self._prompt_partition_fs_type()
 


Test patch: