diff --git a/archinstall/lib/disk/helpers.py b/archinstall/lib/disk/helpers.py index 9442f1b..a9f2563 100644 --- a/archinstall/lib/disk/helpers.py +++ b/archinstall/lib/disk/helpers.py @@ -123,9 +123,19 @@ def harddrive(size=None, model=None, fuzzy=False): return collection[drive] +def split_bind_name(path :Union[pathlib.Path, str]) ->list: + # we check for the bind notation. if exist we'll only use the "true" device path + if '[' in str(path) : #is a bind path (btrfs subvolume path) + device_path, bind_path= path.split('[') + bind_path = bind_path[:-1].strip() # remove the ] + else: + device_path = path + bind_path = None + return device_path,bind_path def get_mount_info(path :Union[pathlib.Path, str], traverse=False, return_real_path=False) -> dict: - for traversal in list(map(str, [str(path)] + list(pathlib.Path(str(path)).parents))): + device_path,bind_path = split_bind_name(path) + for traversal in list(map(str, [str(device_path)] + list(pathlib.Path(str(device_path)).parents))): try: log(f"Getting mount information for device path {traversal}", level=logging.INFO) output = SysCommand(f'/usr/bin/findmnt --json {traversal}').decode('UTF-8') @@ -141,6 +151,10 @@ def get_mount_info(path :Union[pathlib.Path, str], traverse=False, return_real_p raise DiskError(f"Could not get mount information for device path {path}") output = json.loads(output) + # for btrfs partitions we redice the filesystem list to the one with the source equals to the parameter + # i.e. the subvolume filesystem we're searching for + if 'filesystems' in output and len(output['filesystems']) > 1 and bind_path is not None: + output['filesystems'] = [ entry for entry in output['filesystems'] if entry['source'] == str(path) ] if 'filesystems' in output: if len(output['filesystems']) > 1: raise DiskError(f"Path '{path}' contains multiple mountpoints: {output['filesystems']}") diff --git a/archinstall/lib/disk/partition.py b/archinstall/lib/disk/partition.py index d3efe5c..0695765 100644 --- a/archinstall/lib/disk/partition.py +++ b/archinstall/lib/disk/partition.py @@ -7,7 +7,7 @@ import os import hashlib from typing import Optional from .blockdevice import BlockDevice -from .helpers import get_mount_info, get_filesystem_type, convert_size_to_gb +from .helpers import get_mount_info, get_filesystem_type, convert_size_to_gb, split_bind_name from ..storage import storage from ..exceptions import DiskError, SysCallError, UnknownFilesystemFormat from ..output import log @@ -155,11 +155,13 @@ class Partition: Returns the PARTUUID as returned by lsblk. This is more reliable than relying on /dev/disk/by-partuuid as it doesn't seam to be able to detect md raid partitions. + For bind mounts all the subvolumes share the same uuid """ + device_path,bind_path = split_bind_name(self.path) for i in range(storage['DISK_RETRY_ATTEMPTS']): self.partprobe() - partuuid_struct = SysCommand(f'lsblk -J -o+PARTUUID {self.path}') + partuuid_struct = SysCommand(f'lsblk -J -o+PARTUUID {device_path}') if partuuid_struct.exit_code == 0: if partition_information := next(iter(json.loads(partuuid_struct.decode('UTF-8'))['blockdevices']), None): if partuuid := partition_information.get('partuuid', None): @@ -177,8 +179,8 @@ class Partition: For instance when you want to get a __repr__ of the class. """ self.partprobe() - - partuuid_struct = SysCommand(f'lsblk -J -o+PARTUUID {self.path}') + device_path,bind_path = split_bind_name(self.path) + partuuid_struct = SysCommand(f'lsblk -J -o+PARTUUID {device_path}') if partuuid_struct.exit_code == 0: if partition_information := next(iter(json.loads(partuuid_struct.decode('UTF-8'))['blockdevices']), None): if partuuid := partition_information.get('partuuid', None): @@ -190,7 +192,6 @@ class Partition: @encrypted.setter def encrypted(self, value: bool): - self._encrypted = value @property diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 4b26d97..941d4fc 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -11,7 +11,7 @@ from .disk import get_partitions_in_use, Partition from .general import SysCommand, generate_password from .hardware import has_uefi, is_vm, cpu_vendor from .locale_helpers import verify_keyboard_layout, verify_x11_keyboard_layout -from .disk.helpers import get_mount_info +from .disk.helpers import get_mount_info, split_bind_name from .mirrors import use_mirrors from .plugins import plugins from .storage import storage @@ -468,11 +468,14 @@ class Installer: for partition in self.partitions: if partition.filesystem == 'btrfs': # if partition.encrypted: - self.base_packages.append('btrfs-progs') + if 'btrfs-progs' not in self.base_packages: + self.base_packages.append('btrfs-progs') if partition.filesystem == 'xfs': - self.base_packages.append('xfsprogs') + if 'xfs' not in self.base_packages: + self.base_packages.append('xfsprogs') if partition.filesystem == 'f2fs': - self.base_packages.append('f2fs-tools') + if 'f2fs' not in self.base_packages: + self.base_packages.append('f2fs-tools') # Configure mkinitcpio to handle some specific use cases. if partition.filesystem == 'btrfs': @@ -480,7 +483,6 @@ class Installer: self.MODULES.append('btrfs') if '/usr/bin/btrfs' not in self.BINARIES: self.BINARIES.append('/usr/bin/btrfs') - # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. if partition.filesystem == 'ntfs3' and partition.mountpoint == self.target: if 'fsck' in self.HOOKS: @@ -634,15 +636,22 @@ class Installer: entry.write(f"initrd /initramfs-{kernel}.img\n") # blkid doesn't trigger on loopback devices really well, # so we'll use the old manual method until we get that sorted out. - + if root_fs_type is not None: + options_entry = f'rw intel_pstate=no_hwp rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}\n' + else: + options_entry = f'rw intel_pstate=no_hwp {" ".join(self.KERNEL_PARAMS)}\n' if real_device := self.detect_encryption(root_partition): # TODO: We need to detect if the encrypted device is a whole disk encryption, # or simply a partition encryption. Right now we assume it's a partition (and we always have) log(f"Identifying root partition by PART-UUID on {real_device}: '{real_device.uuid}'.", level=logging.DEBUG) - entry.write(f'options cryptdevice=PARTUUID={real_device.uuid}:luksdev root=/dev/mapper/luksdev rw intel_pstate=no_hwp rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}\n') + entry.write(f'options cryptdevice=PARTUUID={real_device.uuid}:luksdev root=/dev/mapper/luksdev {options_entry}') else: + # this code might have to be propagated to all boot entries + base_path,bind_path = split_bind_name(str(root_partition.path)) + if bind_path is not None: # and root_fs_type == 'btrfs': + options_entry = f"rootflags=subvol={bind_path[1:]} " + options_entry log(f"Identifying root partition by PART-UUID on {root_partition}, looking for '{root_partition.uuid}'.", level=logging.DEBUG) - entry.write(f'options root=PARTUUID={root_partition.uuid} rw intel_pstate=no_hwp rootfstype={root_fs_type} {" ".join(self.KERNEL_PARAMS)}\n') + entry.write(f'options root=PARTUUID={root_partition.uuid} {options_entry}') self.helper_flags['bootloader'] = bootloader