From 9b3db344aba22644ccf1fd09ad163395fd5e40be Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Fri, 27 May 2022 12:24:01 +0200 Subject: Fix dual-booting (#1250) # Fixes * Optimized partition lookups * Fixed re-use of partition UUID's * `BlockDevice().get_partition()` now supports looking up both `PARTUUID` and `UUID` for a partition under itself * Partitions listed in `--disk-layout` that doesn't have a PARTUUID/UUID should no longer cause an exception, but instead logs a warning and they will simply be ignored * `Filesystem().add_partition()` now handles `DiskError` raised by `partition.part_uuid` * Fixed issue on normal partitions where the device was not properly frozen in `lambda` calls, meaning two or more mount-points shared the same `device_instance`. * Lowered global `DISK_RETRY_ATTEMPTS` to 5, as the timeouts are linear *(`range(DISK_RETRY_ATTEMPTS) * DISK_TIMEOUTS`)* --- archinstall/lib/disk/blockdevice.py | 40 +++++++++++++++++++------------- archinstall/lib/disk/filesystem.py | 46 +++++++++++++++++++++++-------------- archinstall/lib/disk/partition.py | 4 ++-- archinstall/lib/installer.py | 6 +++-- archinstall/lib/storage.py | 2 +- 5 files changed, 60 insertions(+), 38 deletions(-) (limited to 'archinstall/lib') diff --git a/archinstall/lib/disk/blockdevice.py b/archinstall/lib/disk/blockdevice.py index 15f03789..c4707abd 100644 --- a/archinstall/lib/disk/blockdevice.py +++ b/archinstall/lib/disk/blockdevice.py @@ -289,19 +289,27 @@ class BlockDevice: def flush_cache(self) -> None: self.part_cache = {} - def get_partition(self, uuid :str) -> Partition: - count = 0 - while count < 5: - for partition_uuid, partition in self.partitions.items(): - if partition.part_uuid.lower() == uuid.lower(): - return partition - else: - log(f"uuid {uuid} not found. Waiting for {count +1} time",level=logging.DEBUG) - time.sleep(float(storage['arguments'].get('disk-sleep', 0.2))) - count += 1 - else: - log(f"Could not find {uuid} in disk after 5 retries",level=logging.INFO) - print(f"Cache: {self.part_cache}") - print(f"Partitions: {self.partitions.items()}") - print(f"UUID: {[uuid]}") - raise DiskError(f"New partition {uuid} never showed up after adding new partition on {self}") + def get_partition(self, uuid :Optional[str] = None, partuuid :Optional[str] = None) -> Partition: + if not uuid and not partuuid: + raise ValueError(f"BlockDevice.get_partition() requires either a UUID or a PARTUUID for lookups.") + + for count in range(storage.get('DISK_RETRY_ATTEMPTS', 5)): + for partition_index, partition in self.partitions.items(): + try: + if uuid and partition.uuid.lower() == uuid.lower(): + return partition + elif partuuid and partition.part_uuid.lower() == partuuid.lower(): + return partition + except DiskError as error: + # Most likely a blockdevice that doesn't support or use UUID's + # (like Microsoft recovery partition) + log(f"Could not get UUID/PARTUUID of {partition}: {error}", level=logging.DEBUG, fg="gray") + pass + + log(f"uuid {uuid} or {partuuid} not found. Waiting {storage.get('DISK_TIMEOUTS', 1) * count}s for next attempt",level=logging.DEBUG) + time.sleep(storage.get('DISK_TIMEOUTS', 1) * count) + + log(f"Could not find {uuid}/{partuuid} in disk after 5 retries", level=logging.INFO) + log(f"Cache: {self.part_cache}") + log(f"Partitions: {self.partitions.items()}") + raise DiskError(f"New partition {uuid}/{partuuid} never showed up after adding new partition on {self}") diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py index 8d8f596e..66112ccc 100644 --- a/archinstall/lib/disk/filesystem.py +++ b/archinstall/lib/disk/filesystem.py @@ -97,7 +97,9 @@ class Filesystem: print(_("Re-using partition instance: {}").format(partition_instance)) partition['device_instance'] = partition_instance else: - raise ValueError(f"{self}.load_layout() doesn't know how to continue without a new partition definition or a UUID ({partition.get('PARTUUID')}) on the device ({self.blockdevice.get_partition(uuid=partition.get('PARTUUID'))}).") + log(f"{self}.load_layout() doesn't know how to work without 'wipe' being set or UUID ({partition.get('PARTUUID')}) was given and found.", fg="yellow", level=logging.WARNING) + continue + # raise ValueError(f"{self}.load_layout() doesn't know how to continue without a new partition definition or a UUID ({partition.get('PARTUUID')}) on the device ({self.blockdevice.get_partition(uuid=partition.get('PARTUUID'))}).") if partition.get('filesystem', {}).get('format', False): @@ -206,7 +208,12 @@ class Filesystem: def add_partition(self, partition_type :str, start :str, end :str, partition_format :Optional[str] = None) -> Partition: log(f'Adding partition to {self.blockdevice}, {start}->{end}', level=logging.INFO) - previous_partition_uuids = {partition.part_uuid for partition in self.blockdevice.partitions.values()} + previous_partuuids = [] + for partition in self.blockdevice.partitions.values(): + try: + previous_partuuids.append(partition.part_uuid) + except DiskError: + pass if self.mode == MBR: if len(self.blockdevice.partitions) > 3: @@ -220,36 +227,41 @@ class Filesystem: log(f"Adding partition using the following parted command: {parted_string}", level=logging.DEBUG) if self.parted(parted_string): - count = 0 - while count < 10: - new_uuid = None - new_uuid_set = (previous_partition_uuids ^ {partition.part_uuid for partition in self.blockdevice.partitions.values()}) + for count in range(storage.get('DISK_RETRY_ATTEMPTS', 3)): + self.partprobe() - if len(new_uuid_set) > 0: - new_uuid = new_uuid_set.pop() + new_partition_uuids = [] + for partition in self.blockdevice.partitions.values(): + try: + new_partition_uuids.append(partition.part_uuid) + except DiskError: + pass + + new_partuuid_set = (set(previous_partuuids) ^ set(new_partition_uuids)) + + print(previous_partuuids, new_partition_uuids, new_partuuid_set) - if new_uuid: + if len(new_partuuid_set) and (new_partuuid := new_partuuid_set.pop()): try: - return self.blockdevice.get_partition(new_uuid) + return self.blockdevice.get_partition(partuuid=new_partuuid) except Exception as err: log(f'Blockdevice: {self.blockdevice}', level=logging.ERROR, fg="red") log(f'Partitions: {self.blockdevice.partitions}', level=logging.ERROR, fg="red") - log(f'Partition set: {new_uuid_set}', level=logging.ERROR, fg="red") - log(f'New UUID: {[new_uuid]}', level=logging.ERROR, fg="red") + log(f'Partition set: {new_partuuid_set}', level=logging.ERROR, fg="red") + log(f'New UUID: {[new_partuuid]}', level=logging.ERROR, fg="red") log(f'get_partition(): {self.blockdevice.get_partition}', level=logging.ERROR, fg="red") raise err else: - count += 1 - log(f"Could not get UUID for partition. Waiting before retry attempt {count} of 10 ...",level=logging.DEBUG) - time.sleep(float(storage['arguments'].get('disk-sleep', 0.2))) + log(f"Could not get UUID for partition. Waiting {storage.get('DISK_TIMEOUTS', 1) * count}s before retrying.",level=logging.DEBUG) + time.sleep(storage.get('DISK_TIMEOUTS', 1) * count) else: log("Add partition is exiting due to excessive wait time", level=logging.ERROR, fg="red") raise DiskError(f"New partition never showed up after adding new partition on {self}.") # TODO: This should never be able to happen log(f"Could not find the new PARTUUID after adding the partition.", level=logging.ERROR, fg="red") - log(f"Previous partitions: {previous_partition_uuids}", level=logging.ERROR, fg="red") - log(f"New partitions: {(previous_partition_uuids ^ {partition.part_uuid for partition in self.blockdevice.partitions.values()})}", level=logging.ERROR, fg="red") + log(f"Previous partitions: {previous_partuuids}", level=logging.ERROR, fg="red") + log(f"New partitions: {(previous_partuuids ^ {partition.part_uuid for partition in self.blockdevice.partitions.values()})}", level=logging.ERROR, fg="red") raise DiskError(f"Could not add partition using: {parted_string}") def set_name(self, partition: int, name: str) -> bool: diff --git a/archinstall/lib/disk/partition.py b/archinstall/lib/disk/partition.py index e33c600c..bb1ffeb6 100644 --- a/archinstall/lib/disk/partition.py +++ b/archinstall/lib/disk/partition.py @@ -101,7 +101,7 @@ class Partition: except SysCallError as error: # Not mounted anywhere most likely - log(f"Could not locate mount information for {self.path}: {error}", level=logging.DEBUG) + log(f"Could not locate mount information for {self.path}: {error}", level=logging.DEBUG, fg="grey") pass return None @@ -216,7 +216,7 @@ class Partition: if not self.partprobe(): raise DiskError(f"Could not perform partprobe on {self.device_path}") - time.sleep(max(0.1, storage['DISK_TIMEOUTS'] * i)) + time.sleep(storage.get('DISK_TIMEOUTS', 1) * i) partuuid = self._safe_uuid if partuuid: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 903d33af..3b53ec50 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -314,9 +314,11 @@ class Installer: if partition.get('filesystem',{}).get('mount_options',[]): mount_options = ','.join(partition['filesystem']['mount_options']) - mount_queue[mountpoint] = lambda target=f"{self.target}{mountpoint}", options=mount_options: partition['device_instance'].mount(target, options) + mount_queue[mountpoint] = lambda instance=partition['device_instance'], target=f"{self.target}{mountpoint}", options=mount_options: instance.mount(target, options) else: - mount_queue[mountpoint] = lambda target=f"{self.target}{mountpoint}": partition['device_instance'].mount(target) + mount_queue[mountpoint] = lambda instance=partition['device_instance'], target=f"{self.target}{mountpoint}": instance.mount(target) + + log(f"Using mount order: {list(sorted(mount_queue.items(), key=lambda item: item[0]))}", level=logging.INFO, fg="white") # We mount everything by sorting on the mountpoint itself. for mountpoint, frozen_func in sorted(mount_queue.items(), key=lambda item: item[0]): diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py index 650b9c0e..dd7ddc88 100644 --- a/archinstall/lib/storage.py +++ b/archinstall/lib/storage.py @@ -23,7 +23,7 @@ storage: Dict[str, Any] = { 'MOUNT_POINT': '/mnt/archinstall', 'ENC_IDENTIFIER': 'ainst', 'DISK_TIMEOUTS' : 1, # seconds - 'DISK_RETRY_ATTEMPTS' : 20, # RETRY_ATTEMPTS * DISK_TIMEOUTS is used in disk operations + 'DISK_RETRY_ATTEMPTS' : 5, # RETRY_ATTEMPTS * DISK_TIMEOUTS is used in disk operations 'CMD_LOCALE':{'LC_ALL':'C'}, # default locale for execution commands. Can be overriden with set_cmd_locale() 'CMD_LOCALE_DEFAULT':{'LC_ALL':'C'}, # should be the same as the former. Not be used except in reset_cmd_locale() } -- cgit v1.2.3-54-g00ecf