from __future__ import annotations import logging from typing import Optional, Dict, Any, List, TYPE_CHECKING # https://stackoverflow.com/a/39757388/929999 if TYPE_CHECKING: from .blockdevice import BlockDevice from .helpers import sort_block_devices_based_on_performance, select_largest_device, select_disk_larger_than_or_close_to from ..hardware import has_uefi from ..output import log def suggest_single_disk_layout(block_device :BlockDevice, default_filesystem :Optional[str] = None, advanced_options :bool = False) -> Dict[str, Any]: if not default_filesystem: from ..user_interaction import ask_for_main_filesystem_format default_filesystem = ask_for_main_filesystem_format(advanced_options) MIN_SIZE_TO_ALLOW_HOME_PART = 40 # GiB using_subvolumes = False using_home_partition = False if default_filesystem == 'btrfs': using_subvolumes = input('Would you like to use BTRFS subvolumes with a default structure? (Y/n): ').strip().lower() in ('', 'y', 'yes') layout = { block_device.path : { "wipe" : True, "partitions" : [] } } # Used for reference: https://wiki.archlinux.org/title/partitioning # 2 MiB is unallocated for GRUB on BIOS. Potentially unneeded for # other bootloaders? # TODO: On BIOS, /boot partition is only needed if the drive will # be encrypted, otherwise it is not recommended. We should probably # add a check for whether the drive will be encrypted or not. layout[block_device.path]['partitions'].append({ # Boot "type" : "primary", "start" : "3MiB", "size" : "203MiB", "boot" : True, "encrypted" : False, "wipe" : True, "mountpoint" : "/boot", "filesystem" : { "format" : "fat32" } }) # Increase the UEFI partition if UEFI is detected. # Also re-align the start to 1MiB since we don't need the first sectors # like we do in MBR layouts where the boot loader is installed traditionally. if has_uefi(): layout[block_device.path]['partitions'][-1]['start'] = '1MiB' layout[block_device.path]['partitions'][-1]['size'] = '512MiB' layout[block_device.path]['partitions'].append({ # Root "type" : "primary", "start" : "206MiB", "encrypted" : False, "wipe" : True, "mountpoint" : "/" if not using_subvolumes else None, "filesystem" : { "format" : default_filesystem } }) if has_uefi(): layout[block_device.path]['partitions'][-1]['start'] = '513MiB' if not using_subvolumes and block_device.size >= MIN_SIZE_TO_ALLOW_HOME_PART: using_home_partition = input('Would you like to create a separate partition for /home? (Y/n): ').strip().lower() in ('', 'y', 'yes') # Set a size for / (/root) if using_subvolumes or block_device.size < MIN_SIZE_TO_ALLOW_HOME_PART or not using_home_partition: # We'll use subvolumes # Or the disk size is too small to allow for a separate /home # Or the user doesn't want to create a separate partition for /home layout[block_device.path]['partitions'][-1]['size'] = '100%' else: layout[block_device.path]['partitions'][-1]['size'] = f"{min(block_device.size, 20)}GiB" if default_filesystem == 'btrfs' and using_subvolumes: # if input('Do you want to use a recommended structure? (Y/n): ').strip().lower() in ('', 'y', 'yes'): # https://btrfs.wiki.kernel.org/index.php/FAQ # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh layout[block_device.path]['partitions'][1]['btrfs'] = { "subvolumes" : { "@":"/", "@home": "/home", "@log": "/var/log", "@pkg": "/var/cache/pacman/pkg", "@.snapshots": "/.snapshots" } } # else: # pass # ... implement a guided setup elif using_home_partition: # If we don't want to use subvolumes, # But we want to be able to re-use data between re-installs.. # A second partition for /home would be nice if we have the space for it layout[block_device.path]['partitions'].append({ # Home "type" : "primary", "start" : f"{min(block_device.size, 20)}GiB", "size" : "100%", "encrypted" : False, "wipe" : True, "mountpoint" : "/home", "filesystem" : { "format" : default_filesystem } }) return layout def suggest_multi_disk_layout(block_devices :List[BlockDevice], default_filesystem :Optional[str] = None, advanced_options :bool = False) -> Dict[str, Any]: if not default_filesystem: from ..user_interaction import ask_for_main_filesystem_format default_filesystem = ask_for_main_filesystem_format(advanced_options) # Not really a rock solid foundation of information to stand on, but it's a start: # https://www.reddit.com/r/btrfs/comments/m287gp/partition_strategy_for_two_physical_disks/ # https://www.reddit.com/r/btrfs/comments/9us4hr/what_is_your_btrfs_partitionsubvolumes_scheme/ MIN_SIZE_TO_ALLOW_HOME_PART = 40 # GiB ARCH_LINUX_INSTALLED_SIZE = 20 # GiB, rough estimate taking in to account user desktops etc. TODO: Catch user packages to detect size? block_devices = sort_block_devices_based_on_performance(block_devices).keys() home_device = select_largest_device(block_devices, gigabytes=MIN_SIZE_TO_ALLOW_HOME_PART) root_device = select_disk_larger_than_or_close_to(block_devices, gigabytes=ARCH_LINUX_INSTALLED_SIZE, filter_out=[home_device]) log(f"Suggesting multi-disk-layout using {len(block_devices)} disks, where {root_device} will be /root and {home_device} will be /home", level=logging.DEBUG) layout = { root_device.path : { "wipe" : True, "partitions" : [] }, home_device.path : { "wipe" : True, "partitions" : [] }, } # TODO: Same deal as with the single disk layout, we should # probably check if the drive will be encrypted. layout[root_device.path]['partitions'].append({ # Boot "type" : "primary", "start" : "3MiB", "size" : "203MiB", "boot" : True, "encrypted" : False, "wipe" : True, "mountpoint" : "/boot", "filesystem" : { "format" : "fat32" } }) if has_uefi(): layout[root_device.path]['partitions'][-1]['start'] = '1MiB' layout[root_device.path]['partitions'][-1]['size'] = '512MiB' layout[root_device.path]['partitions'].append({ # Root "type" : "primary", "start" : "206MiB", "size" : "100%", "encrypted" : False, "wipe" : True, "mountpoint" : "/", "filesystem" : { "format" : default_filesystem } }) if has_uefi(): layout[root_device.path]['partitions'][-1]['start'] = '513MiB' layout[home_device.path]['partitions'].append({ # Home "type" : "primary", "start" : "1MiB", "size" : "100%", "encrypted" : False, "wipe" : True, "mountpoint" : "/home", "filesystem" : { "format" : default_filesystem } }) return layout