From 0a8c061ab405e244a187b4615654ecca2e538156 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Thu, 10 Jun 2021 21:00:33 +0200 Subject: Fixed format detection on commands, should be using exit codes instead? --- archinstall/lib/installer.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'archinstall/lib/installer.py') diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index da6f6a9b..b62b9595 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -122,6 +122,14 @@ class Installer: return True + def mount_ordered_layout(self, layout :dict): + mountpoints = {} + for partition in layout['partitions']: + mountpoints[partition['mountpoint']] = partition['device_instance'] + + for mountpoint in sorted(mountpoints.keys()): + mountpoints[mountpoint].mount(f"{self.target}{mountpoint}") + def mount(self, partition, mountpoint, create_mountpoint=True): if create_mountpoint and not os.path.isdir(f'{self.target}{mountpoint}'): os.makedirs(f'{self.target}{mountpoint}') -- cgit v1.2.3-70-g09d2 From 24476ac1f696c882fb2f741cb8c5fa858f786f46 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 11 Jun 2021 17:22:20 +0200 Subject: Made it so that the .partitions property of Install() fetches from live data, rather than storing and caching partitions on initation. Since it now supports mounting a partition layout given by external usage. --- archinstall/lib/disk.py | 11 +++++++---- archinstall/lib/installer.py | 5 ++++- examples/guided.py | 6 ++---- 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'archinstall/lib/installer.py') diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py index 8e9d0d3f..7a7fce5e 100644 --- a/archinstall/lib/disk.py +++ b/archinstall/lib/disk.py @@ -636,7 +636,7 @@ class Filesystem: :param string: A raw string passed to /usr/bin/parted -s :type string: str """ - return self.raw_parted(string).exit_code + return self.raw_parted(string).exit_code == 0 def use_entire_disk(self, root_filesystem_type='ext4') -> Partition: # TODO: Implement this with declarative profiles instead. @@ -652,20 +652,22 @@ class Filesystem: DiskError("Too many partitions on disk, MBR disks can only have 3 parimary partitions") if partition_format: - partitioning = self.parted(f'{self.blockdevice.device} mkpart {partition_type} {partition_format} {start} {end}') == 0 + parted_string = f'{self.blockdevice.device} mkpart {partition_type} {partition_format} {start} {end}' else: - partitioning = self.parted(f'{self.blockdevice.device} mkpart {partition_type} {start} {end}') == 0 + parted_string = f'{self.blockdevice.device} mkpart {partition_type} {start} {end}' - if partitioning: + if self.parted(parted_string): start_wait = time.time() while previous_partition_uuids == {partition.uuid for partition in self.blockdevice.partitions.values()}: if time.time() - start_wait > 10: raise DiskError(f"New partition never showed up after adding new partition on {self} (timeout 10 seconds).") time.sleep(0.025) + time.sleep(0.5) # Let the kernel catch up with quick block devices (nvme for instance) return self.blockdevice.get_partition(uuid=(previous_partition_uuids ^ {partition.uuid for partition in self.blockdevice.partitions.values()}).pop()) + def set_name(self, partition: int, name: str): return self.parted(f'{self.blockdevice.device} name {partition + 1} "{name}"') == 0 @@ -673,6 +675,7 @@ class Filesystem: return self.parted(f'{self.blockdevice.device} set {partition + 1} {string}') == 0 def parted_mklabel(self, device: str, disk_label: str): + log(f"Creating a new partition labling on {device}", level=logging.INFO, fg="yellow") # Try to unmount devices before attempting to run mklabel try: SysCommand(f'bash -c "umount {device}?"') diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index b62b9595..91c55b33 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -57,7 +57,6 @@ class Installer: self.post_base_install = [] storage['session'] = self - self.partitions = get_partitions_in_use(self.target) self.MODULES = [] self.BINARIES = [] @@ -108,6 +107,10 @@ class Installer: self.sync_log_to_install_medium() return False + @property + def partitions(self): + return get_partitions_in_use(self.target) + def sync_log_to_install_medium(self): # Copy over the install log (if there is one) to the install medium if # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to. diff --git a/examples/guided.py b/examples/guided.py index 527fc67c..5a9f2b49 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -224,8 +224,6 @@ def perform_filesystem_operations(): with archinstall.Filesystem(drive, mode) as fs: fs.load_layout(archinstall.storage['disk_layouts'][drive]) - perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt')) - def perform_installation(mountpoint): """ @@ -308,7 +306,7 @@ def perform_installation(mountpoint): # This step must be after profile installs to allow profiles to install language pre-requisits. # After which, this step will set the language both for console and x11 if x11 was installed for instance. - installation.set_keyboard_language(archinstall.arguments['keyboard-language']) + installation.set_keyboard_language(archinstall.arguments['keyboard-layout']) if archinstall.arguments['profile'] and archinstall.arguments['profile'].has_post_install(): with archinstall.arguments['profile'].load_instructions(namespace=f"{archinstall.arguments['profile'].namespace}.py") as imported: @@ -375,4 +373,4 @@ else: archinstall.storage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(archinstall.arguments.get('gfx_driver', None), None) perform_filesystem_operations() -perform_installation(archinstall.arguments.get('target-mountpoint', None)) +perform_installation(archinstall.storage.get('MOUNT_POINT', '/mnt')) \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 9b6d7021a89116f09ad5324f19d7d08b9ec2856b Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 13 Jun 2021 10:37:30 +0200 Subject: This fixes https://github.com/archlinux/archinstall/pull/426#discussion_r650372664 --- archinstall/lib/installer.py | 7 ++++--- examples/guided.py | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'archinstall/lib/installer.py') diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 91c55b33..b1e38dc6 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -125,10 +125,11 @@ class Installer: return True - def mount_ordered_layout(self, layout :dict): + def mount_ordered_layout(self, layouts :dict): mountpoints = {} - for partition in layout['partitions']: - mountpoints[partition['mountpoint']] = partition['device_instance'] + for blockdevice in layouts: + for partition in layouts[blockdevice]['partitions']: + mountpoints[partition['mountpoint']] = partition['device_instance'] for mountpoint in sorted(mountpoints.keys()): mountpoints[mountpoint].mount(f"{self.target}{mountpoint}") diff --git a/examples/guided.py b/examples/guided.py index 5a9f2b49..14cbe0a1 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -236,8 +236,7 @@ def perform_installation(mountpoint): with archinstall.Installer(mountpoint, kernels=archinstall.arguments.get('kernels', 'linux')) as installation: # Mount all the drives to the desired mountpoint # This *can* be done outside of the installation, but the installer can deal with it. - for drive in archinstall.arguments['harddrives']: - installation.mount_ordered_layout(archinstall.storage['disk_layouts'][drive]) + installation.mount_ordered_layout(archinstall.storage['disk_layouts']) # if len(mirrors): # Certain services might be running that affects the system during installation. -- cgit v1.2.3-70-g09d2 From d76760b45fcc085f1d878465e5293de2740a70b4 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Sun, 13 Jun 2021 14:25:07 +0200 Subject: Removed old safety logics for partitions. Partitions will now always be formatted when .format() is called on them. The safety now lay in the code parsing the declerative partition layouts. Also added the encrypt/mount logic for encrypted partitions, which by default will be unencrypted unless a password is specified. --- archinstall/lib/disk.py | 50 ++++++++++++------------------------- archinstall/lib/installer.py | 15 +++++++++-- archinstall/lib/luks.py | 7 ------ archinstall/lib/storage.py | 3 ++- archinstall/lib/user_interaction.py | 23 +++++++++-------- examples/guided.py | 10 ++++---- 6 files changed, 48 insertions(+), 60 deletions(-) (limited to 'archinstall/lib/installer.py') diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py index 97b982d8..7ef65a5c 100644 --- a/archinstall/lib/disk.py +++ b/archinstall/lib/disk.py @@ -76,6 +76,7 @@ def suggest_single_disk_layout(block_device): "start" : "1MiB", "size" : "513MiB", "boot" : True, + "encrypted" : False, "format" : True, "mountpoint" : "/boot", "filesystem" : { @@ -86,7 +87,7 @@ def suggest_single_disk_layout(block_device): # Root "type" : "primary", "start" : "513MiB", - "encrypted" : True, + "encrypted" : False, "format" : True, "size" : "100%" if block_device.size < MIN_SIZE_TO_ALLOW_HOME_PART else f"{min(block_device.size, 20)*1024}MiB", "mountpoint" : "/", @@ -99,7 +100,7 @@ def suggest_single_disk_layout(block_device): layout[block_device]['partitions'].append({ # Home "type" : "primary", - "encrypted" : True, + "encrypted" : False, "format" : True, "start" : f"{min(block_device.size*0.2, 20)*1024}MiB", "size" : "100%", @@ -138,6 +139,7 @@ def suggest_multi_disk_layout(block_devices): "start" : "1MiB", "size" : "513MiB", "boot" : True, + "encrypted" : False, "format" : True, "mountpoint" : "/boot", "filesystem" : { @@ -148,7 +150,7 @@ def suggest_multi_disk_layout(block_devices): # Root "type" : "primary", "start" : "513MiB", - "encrypted" : True, + "encrypted" : False, "format" : True, "size" : "100%", "mountpoint" : "/", @@ -160,7 +162,7 @@ def suggest_multi_disk_layout(block_devices): layout[home_device]['partitions'].append({ # Home "type" : "primary", - "encrypted" : True, + "encrypted" : False, "format" : True, "start" : "4MiB", "size" : "100%", @@ -514,7 +516,7 @@ class Partition: from .luks import luks2 try: - with luks2(self, 'luksloop', password, auto_unmount=True) as unlocked_device: + with luks2(self, storage.get('ENC_IDENTIFIER', 'ai')+'loop', password, auto_unmount=True) as unlocked_device: return unlocked_device.filesystem except SysCallError: return None @@ -540,35 +542,12 @@ class Partition: return True if files > 0 else False - def safe_to_format(self): - if self.allow_formatting is False: - log(f"Partition {self} is not marked for formatting.", level=logging.DEBUG) - return False - elif self.target_mountpoint == '/boot': - try: - if self.has_content(): - log(f"Partition {self} is a boot partition and has content inside.", level=logging.DEBUG) - return False - except SysCallError as err: - log(err.message, logging.DEBUG) - log(f"Partition {self} was identified as /boot but we could not mount to check for content, continuing!", level=logging.DEBUG) - pass - - return True - def encrypt(self, *args, **kwargs): """ A wrapper function for luks2() instances and the .encrypt() method of that instance. """ from .luks import luks2 - if not self._encrypted: - raise DiskError(f"Attempting to encrypt a partition that was not marked for encryption: {self}") - - if not self.safe_to_format(): - log(f"Partition {self} was marked as protected but encrypt() was called on it!", level=logging.ERROR, fg="red") - return False - handle = luks2(self, None, None) return handle.encrypt(self, *args, **kwargs) @@ -745,6 +724,8 @@ class Filesystem: return True def load_layout(self, layout :dict): + from .luks import luks2 + # If the layout tells us to wipe the drive, we do so if layout.get('wipe', False): if self.mode == GPT: @@ -770,11 +751,11 @@ class Filesystem: raise ValueError("BlockDevice().load_layout() doesn't know how to continue without either a UUID or creation of partition.") if partition.get('filesystem', {}).get('format', None): - if partition.get('encrypt', False): + if partition.get('encrypted', False): assert partition.get('password') partition['device_instance'].encrypt(password=partition['password']) - with archinstall.luks2(partition['device_instance'], 'luksloop', partition['password']) as unlocked_device: + with luks2(partition['device_instance'], storage.get('ENC_IDENTIFIER', 'ai')+'loop', partition['password']) as unlocked_device: unlocked_device.format(partition['filesystem']['format'], allow_formatting=partition.get('format', False)) else: partition['device_instance'].format(partition['filesystem']['format'], allow_formatting=partition.get('format', False)) @@ -957,7 +938,8 @@ def encrypted_partitions(blockdevices :dict) -> bool: if partition.get('encrypted', False): yield partition -def find_partition_by_mountpoint(partitions, relative_mountpoint :str): - for partition in partitions: - if partition.get('mountpoint', None) == relative_mountpoint: - return partition \ No newline at end of file +def find_partition_by_mountpoint(block_devices, relative_mountpoint :str): + for device in block_devices: + for partition in block_devices[device]['partitions']: + if partition.get('mountpoint', None) == relative_mountpoint: + return partition \ No newline at end of file diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index b1e38dc6..00d3a001 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -126,13 +126,21 @@ class Installer: return True def mount_ordered_layout(self, layouts :dict): + from .luks import luks2 + mountpoints = {} for blockdevice in layouts: for partition in layouts[blockdevice]['partitions']: - mountpoints[partition['mountpoint']] = partition['device_instance'] + mountpoints[partition['mountpoint']] = partition for mountpoint in sorted(mountpoints.keys()): - mountpoints[mountpoint].mount(f"{self.target}{mountpoint}") + if mountpoints[mountpoint]['encrypted']: + loopdev = storage.get('ENC_IDENTIFIER', 'ai')+'loop' + password = mountpoints[mountpoint]['password'] + with luks2(mountpoints[mountpoint]['device_instance'], loopdev, password, auto_unmount=False) as unlocked_device: + unlocked_device.mount(f"{self.target}{mountpoint}") + else: + mountpoints[mountpoint]['device_instance'].mount(f"{self.target}{mountpoint}") def mount(self, partition, mountpoint, create_mountpoint=True): if create_mountpoint and not os.path.isdir(f'{self.target}{mountpoint}'): @@ -437,6 +445,9 @@ class Installer: elif partition.mountpoint == self.target: root_partition = partition + if boot_partition is None and root_partition is None: + raise ValueError(f"Could not detect root (/) or boot (/boot) in {self.target} based on: {self.partitions}") + self.log(f'Adding bootloader {bootloader} to {boot_partition if boot_partition else root_partition}', level=logging.INFO) if bootloader == 'systemd-bootctl': diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index b910bfb2..781bed43 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -18,9 +18,6 @@ class luks2: self.mapdev = None def __enter__(self): - # if self.partition.allow_formatting: - # self.key_file = self.encrypt(self.partition, *self.args, **self.kwargs) - # else: if not self.key_file: self.key_file = f"/tmp/{os.path.basename(self.partition.path)}.disk_pw" # TODO: Make disk-pw-file randomly unique? @@ -42,9 +39,6 @@ class luks2: return True def encrypt(self, partition, password=None, key_size=512, hash_type='sha512', iter_time=10000, key_file=None): - if not self.partition.allow_formatting: - raise DiskError(f'Could not encrypt volume {partition} due to it having a formatting lock.') - log(f'Encrypting {partition} (This might take a while)', level=logging.INFO) if not key_file: @@ -132,7 +126,6 @@ class luks2: if os.path.islink(f'/dev/mapper/{mountpoint}'): self.mapdev = f'/dev/mapper/{mountpoint}' unlocked_partition = Partition(self.mapdev, None, encrypted=True, filesystem=get_filesystem_type(self.mapdev), autodetect_filesystem=False) - unlocked_partition.allow_formatting = self.partition.allow_formatting return unlocked_partition def close(self, mountpoint=None): diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py index 4e19e4d4..67f8e716 100644 --- a/archinstall/lib/storage.py +++ b/archinstall/lib/storage.py @@ -18,5 +18,6 @@ storage = { 'PROFILE_DB': None, # Used in cases when listing profiles is desired, not mandatory for direct profile grabing. 'LOG_PATH': '/var/log/archinstall', 'LOG_FILE': 'install.log', - 'MOUNT_POINT': '/mnt', + 'MOUNT_POINT': '/mnt/archinstall', + 'ENC_IDENTIFIER': 'ainst' } diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 07f98328..da9f9809 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -9,7 +9,7 @@ import signal import sys import time -from .disk import BlockDevice, valid_fs_type, suggest_single_disk_layout, suggest_multi_disk_layout +from .disk import BlockDevice, valid_fs_type, find_partition_by_mountpoint, suggest_single_disk_layout, suggest_multi_disk_layout from .exceptions import * from .general import SysCommand from .hardware import AVAILABLE_GFX_DRIVERS, has_uefi @@ -190,18 +190,19 @@ def generic_multi_select(options, text="Select one or more of the options above sys.stdout.flush() return selected_options -def select_encrypted_partitions(blockdevices :dict) -> dict: - if len(blockdevices) == 1: - if len(blockdevices[0]['partitions']) == 2: - root = find_partition_by_mountpoint(blockdevices[0]['partitions'], '/') - blockdevices[0]['partitions'][root]['encrypted'] = True - return True +def select_encrypted_partitions(block_devices :dict, password :str) -> dict: + root = find_partition_by_mountpoint(block_devices, '/') + root['encrypted'] = True + root['password'] = password + + return block_devices - options = [] - for partition in blockdevices.values(): - options.append({key: val for key, val in partition.items() if val}) + # TODO: Next version perhaps we can support multiple encrypted partitions + #options = [] + #for partition in block_devices.values(): + # options.append({key: val for key, val in partition.items() if val}) - print(generic_multi_select(options, f"Choose which partitions to encrypt (leave blank when done): ")) + #print(generic_multi_select(options, f"Choose which partitions to encrypt (leave blank when done): ")) class MiniCurses: def __init__(self, width, height): diff --git a/examples/guided.py b/examples/guided.py index 14cbe0a1..4136ca6e 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -87,11 +87,11 @@ def ask_user_questions(): if (passwd := archinstall.get_password(prompt='Enter disk encryption password (leave blank for no encryption): ')): archinstall.arguments['!encryption-password'] = passwd - # If no partitions was marked as encrypted (rare), but a password was supplied - - # then we need to identify which partitions to encrypt. This will default to / (root) if only - # root and boot are detected. - if len(list(archinstall.encrypted_partitions(archinstall.storage['disk_layouts']))) == 0: - archinstall.storage['disk_layouts'] = archinstall.select_encrypted_partitions(archinstall.storage['disk_layouts']) + if archinstall.arguments['harddrives'] and archinstall.arguments.get('!encryption-password', None): + # If no partitions was marked as encrypted, but a password was supplied and we have some disks to format.. + # Then we need to identify which partitions to encrypt. This will default to / (root). + if len(list(archinstall.encrypted_partitions(archinstall.storage['disk_layouts']))) == 0: + archinstall.storage['disk_layouts'] = archinstall.select_encrypted_partitions(archinstall.storage['disk_layouts'], archinstall.arguments['!encryption-password']) # Ask which boot-loader to use (will only ask if we're in BIOS (non-efi) mode) if not archinstall.arguments.get("bootloader", None): -- cgit v1.2.3-70-g09d2