Reworked the way partition formatting works. As well as added some flags to the partition if it's locked/unlocked for partitioning. By defaults partitions will now be in a locked state - prohibiting formatting unless set or overridden in the formatting call. This allows us to selectively format partitions individually later on. There's also a target_mountpoint that is the desired relative mount point inside a installation. This can be pre-pended with the installation base directory during mount. These changes also function as indicators for the installation (and guided installation) for which partitions to use and/or wipe. If an entire drive is selected for wiping, these changes will have no affect in the decision making as all partitions will be new and have formatable set to true.

This commit is contained in:
Anton Hvornum 2021-02-11 14:11:21 +01:00
parent 03c46cce2b
commit e2cd617d05
No known key found for this signature in database
GPG Key ID: F1234C5BA67C59DF
4 changed files with 69 additions and 23 deletions

View File

@ -125,35 +125,57 @@ class Partition():
self.path = path
self.part_id = part_id
self.mountpoint = mountpoint
self.target_mountpoint = mountpoint
self.filesystem = filesystem
self.size = size # TODO: Refresh?
self.encrypted = encrypted
self.allow_formatting = False # A fail-safe for unconfigured partitions, such as windows NTFS partitions.
if mountpoint:
self.mount(mountpoint)
mount_information = get_mount_info(self.path)
actual_fstype = get_filesystem_type(self.path) # blkid -o value -s TYPE self.path
fstype = get_filesystem_type(self.path) # blkid -o value -s TYPE self.path
if self.mountpoint != mount_information.get('target', None) and mountpoint:
raise DiskError(f"{self} was given a mountpoint but the actual mountpoint differs: {mount_information.get('target', None)}")
if mount_information.get('fstype', None) != self.filesystem and filesystem:
raise DiskError(f"{self} was given a filesystem format, but a existing format was detected: {mount_information.get('fstype', None)}")
if (target := mount_information.get('target', None)):
self.mountpoint = target
if (fstype := mount_information.get('fstype', None)):
if (fstype := mount_information.get('fstype', fstype)):
self.filesystem = fstype
def __repr__(self, *args, **kwargs):
if self.encrypted:
return f'Partition(path={self.path}, real_device={self.real_device}, fs={self.filesystem}, mounted={self.mountpoint})'
def __lt__(self, left_comparitor):
if type(left_comparitor) == Partition:
left_comparitor = left_comparitor.path
else:
return f'Partition(path={self.path}, fs={self.filesystem}, mounted={self.mountpoint})'
left_comparitor = str(left_comparitor)
return self.path < left_comparitor # Not quite sure the order here is correct. But /dev/nvme0n1p1 comes before /dev/nvme0n1p5 so seems correct.
def format(self, filesystem, path=None, log_formating=True):
if not path:
def __repr__(self, *args, **kwargs):
mount_repr = ''
if self.mountpoint:
mount_repr = f", mounted={self.mountpoint}"
elif self.target_mountpoint:
mount_repr = f", rel_mountpoint={self.target_mountpoint}"
if self.encrypted:
return f'Partition(path={self.path}, real_device={self.real_device}, fs={self.filesystem}{mount_repr})'
else:
return f'Partition(path={self.path}, fs={self.filesystem}{mount_repr})'
def format(self, filesystem, path=None, allow_formatting=None, log_formating=True):
"""
Format can be given an overriding path, for instance /dev/null to test
the formating functionality and in essence the support for the given filesystem.
"""
if path is None:
path = self.path
if allow_formatting is None:
allow_formatting = self.allow_formatting
if not allow_formatting:
raise PermissionError(f"{self} is not formatable either because instance is locked ({self.allow_formatting}) or a blocking flag was given ({allow_formatting})")
if log_formating:
log(f'Formatting {path} -> {filesystem}', level=LOG_LEVELS.Info)
@ -173,13 +195,18 @@ class Partition():
raise DiskError(f'Could not format {path} with {filesystem} because: {b"".join(handle)}')
self.filesystem = 'ext4'
elif filesystem == 'xfs':
if (handle:= sys_command(f'/usr/bin/mkfs.xfs -f {path}')).exit_code != 0:
if (handle := sys_command(f'/usr/bin/mkfs.xfs -f {path}')).exit_code != 0:
raise DiskError(f'Could not format {path} with {filesystem} because: {b"".join(handle)}')
self.filesystem = 'xfs'
elif filesystem == 'f2fs':
if (handle:= sys_command(f'/usr/bin/mkfs.f2fs -f {path}')).exit_code != 0:
if (handle := sys_command(f'/usr/bin/mkfs.f2fs -f {path}')).exit_code != 0:
raise DiskError(f'Could not format {path} with {filesystem} because: {b"".join(handle)}')
self.filesystem = 'f2fs'
elif filesystem == 'crypto_LUKS':
from .luks import luks2
encrypted_partition = luks2(self, None, None)
encrypted_partition.format(path)
self.filesystem = 'crypto_LUKS'
else:
raise UnknownFilesystemFormat(f"Fileformat '{filesystem}' is not yet implemented.")
return True
@ -218,10 +245,14 @@ class Partition():
return True
def filesystem_supported(self):
# We perform a dummy format on /dev/null with the given filesystem-type
# in order to determain if we support it or not.
"""
The support for a filesystem (this partition) is tested by calling
partition.format() with a path set to '/dev/null' which returns two exceptions:
1. SysCallError saying that /dev/null is not formattable - but the filesystem is supported
2. UnknownFilesystemFormat that indicates that we don't support the given filesystem type
"""
try:
self.format(self.filesystem, '/dev/null', log_formating=False)
self.format(self.filesystem, '/dev/null', log_formating=False, allow_formatting=True)
except SysCallError:
pass # We supported it, but /dev/null is not formatable as expected so the mkfs call exited with an error code
except UnknownFilesystemFormat as err:
@ -364,4 +395,8 @@ def get_mount_info(path):
if len(output['filesystems']) > 1:
raise DiskError(f"Path '{path}' contains multiple mountpoints: {output['filesystems']}")
return output['filesystems'][0]
return output['filesystems'][0]
def get_filesystem_type(path):
output = b''.join(sys_command(f"blkid -o value -s TYPE {path}"))
return output.strip().decode('UTF-8')

View File

@ -11,4 +11,6 @@ class SysCallError(BaseException):
class ProfileNotFound(BaseException):
pass
class HardwareIncompatibilityError(BaseException):
pass
class PermissionError(BaseException):
pass

View File

@ -12,6 +12,7 @@ class luks2():
self.mountpoint = mountpoint
self.args = args
self.kwargs = kwargs
self.filesystem = 'crypto_LUKS'
def __enter__(self):
key_file = self.encrypt(self.partition, self.password, *self.args, **self.kwargs)
@ -57,4 +58,8 @@ class luks2():
def close(self, mountpoint):
sys_command(f'cryptsetup close /dev/mapper/{mountpoint}')
return os.path.islink(f'/dev/mapper/{mountpoint}') is False
return os.path.islink(f'/dev/mapper/{mountpoint}') is False
def format(self, path):
if (handle := sys_command(f"/usr/bin/cryptsetup -q -v luksErase {path}")).exit_code != 0:
raise DiskError(f'Could not format {path} with {self.filesystem} because: {b"".join(handle)}')

View File

@ -105,18 +105,22 @@ else:
if archinstall.arguments['harddrive'].has_partitions():
archinstall.log(f" ! {archinstall.arguments['harddrive']} contains existing partitions", fg='red')
try:
partition_mountpoints = {}
for partition in archinstall.arguments['harddrive']:
if partition.filesystem_supported():
archinstall.log(f" {partition}")
partition_mountpoints[partition] = None
if (option := input('Do you wish to keep existing disk setup or format entire drive? (k/f): ')).lower() in ('k', 'keep'):
# If we want to keep the existing partitioning table
# Make sure that it's the selected drive mounted under /mnt
# That way, we can rely on genfstab and some manual post-installation steps.
if archinstall.arguments['harddrive'].has_mount_point(archinstall.storage['MOUNT_POINT']) is False:
raise archinstall.DiskError(f"The selected drive {archinstall.arguments['harddrive']} is not pre-mounted to {archinstall.storage['MOUNT_POINT']}. This is required when keeping a existing partitioning scheme.")
if (option := input('Do you wish to keep one/more existing partitions or format entire drive? (k/f): ')).lower() in ('k', 'keep'):
archinstall.arguments['harddrive'].keep_partitions = True
while True:
partition = archinstall.generic_select(partition_mountpoints.values(), "Select a partition to assign mount-point to")
archinstall.arguments['harddrive'].allocate_partitions(selections)
archinstall.log('Using existing partition table reported above.')
else:
archinstall.arguments['harddrive'].keep_partitions = False
except archinstall.UnknownFilesystemFormat as err:
archinstall.log(f"Current filesystem is not supported: {err}", fg='red')
input(f"Do you wish to erase all data? (y/n):")