diff --git a/src/modules/fstab/fstab.conf b/src/modules/fstab/fstab.conf index 560aa0073..79d2de54d 100644 --- a/src/modules/fstab/fstab.conf +++ b/src/modules/fstab/fstab.conf @@ -5,57 +5,8 @@ # Also creates mount points for all the filesystems. # # When creating fstab entries for a filesystem, this module -# uses the options for the filesystem type to write to the -# options field of the file. +# uses the options previously defined in the mount module --- -# Mount options to use for all filesystems. If a specific filesystem -# is listed here, use those options, otherwise use the *default* -# options from this mapping. -# -# With kernels 5.15 and newer be cautious of adding the option space_cache -# to the btrfs mount options. The default in 5.15 changed to space_cache=v2. -# If space_cache or space_cache=v1 are specified, it may fail to remount. -# -# btrfs_swap options are used when a swapfile is chosen with a btrfs root -# the options are applied to the subvolume which holds the swap partition -# -# The settings shown here apply only the btrfs defaults; these -# are generally the right ones. Commented-out lines show other -# options wich **might** be applicable for specific situations. -mountOptions: - default: defaults,noatime - # btrfs: defaults,noatime,autodefrag,compress=zstd - btrfs: defaults - # btrfs_swap: defaults,noatime - btrfs_swap: defaults - -# Mount options to use for the EFI System Partition. If not defined, the -# *mountOptions* for *vfat* are used, or if that is not set either, -# *default* from *mountOptions*. -efiMountOptions: umask=0077 - -# If a filesystem is on an SSD, add the following options. If a specific -# filesystem is listed here, use those options, otherwise no additional -# options are set (i.e. there is no *default* like in *mountOptions*). -# -# This example configuration applies the *discard* option to most -# common filesystems on an SSD. This may not be the right option -# for your distribution. If you use a systemd timer to trim the -# SSD, it may interfere with the *discard* option. Opinions vary -# as to whether *discard* is worth the effort -- it depends on -# the usage pattern of the disk as well. -# -# ssdExtraMountOptions: -# ext4: discard -# jfs: discard -# xfs: discard -# swap: discard -# btrfs: discard,compress=lzo -# -# The standard configuration applies asynchronous discard support and ssd optimizations to btrfs -# and does nothing for other filesystems. -ssdExtraMountOptions: - btrfs: discard=async,ssd # Additional options added to each line in /etc/crypttab crypttabOptions: luks diff --git a/src/modules/fstab/fstab.schema.yaml b/src/modules/fstab/fstab.schema.yaml index 087e82cac..d2b4177ac 100644 --- a/src/modules/fstab/fstab.schema.yaml +++ b/src/modules/fstab/fstab.schema.yaml @@ -6,23 +6,4 @@ $id: https://calamares.io/schemas/fstab additionalProperties: false type: object properties: - mountOptions: - type: object - additionalProperties: true # we don't know which FS exist - properties: - default: { type: string } - btrfs: { type: string } - required: [ default ] - ssdExtraMountOptions: - type: object - additionalProperties: true # we don't know which FS exist - properties: - ext4: { type: string } - jfs: { type: string } - xfs: { type: string } - swap: { type: string } - btrfs: { type: string } - btrfs_swap: { type: string } - efiMountOptions: { type: string } crypttabOptions: { type: string } -required: [ mountOptions ] diff --git a/src/modules/fstab/main.py b/src/modules/fstab/main.py index 6a771a24b..8c768b5dd 100644 --- a/src/modules/fstab/main.py +++ b/src/modules/fstab/main.py @@ -103,15 +103,13 @@ class FstabGenerator(object): :param partitions: :param root_mount_point: - :param mount_options: - :param ssd_extra_mount_options: + :param mount_options_list: """ - def __init__(self, partitions, root_mount_point, mount_options, - ssd_extra_mount_options, crypttab_options): + def __init__(self, partitions, root_mount_point, mount_options_list, + crypttab_options): self.partitions = partitions self.root_mount_point = root_mount_point - self.mount_options = mount_options - self.ssd_extra_mount_options = ssd_extra_mount_options + self.mount_options_list = mount_options_list self.crypttab_options = crypttab_options self.ssd_disks = set() self.root_is_ssd = False @@ -236,17 +234,7 @@ class FstabGenerator(object): libcalamares.utils.debug("Ignoring foreign swap {!s} {!s}".format(disk_name, partition.get("uuid", None))) return None - # If this is btrfs subvol a dedicated to a swapfile, use different options than a normal btrfs subvol - if filesystem == "btrfs" and partition.get("subvol", None) == "/@swap": - options = self.get_mount_options("btrfs_swap", mount_point) - else: - options = self.get_mount_options(filesystem, mount_point) - - if is_ssd: - extra = self.ssd_extra_mount_options.get(filesystem) - - if extra: - options += "," + extra + options = self.get_mount_options(filesystem, mount_point) if mount_point == "/" and filesystem != "btrfs": check = 1 @@ -258,10 +246,6 @@ class FstabGenerator(object): if mount_point == "/": self.root_is_ssd = is_ssd - # If there's a set-and-not-empty subvolume set, add it - if filesystem == "btrfs" and partition.get("subvol",None): - options = "subvol={},".format(partition["subvol"]) + options - if has_luks: device = "/dev/mapper/" + partition["luksMapperName"] elif partition["uuid"] is not None: @@ -292,15 +276,8 @@ class FstabGenerator(object): if partition["mountPoint"]: mkdir_p(self.root_mount_point + partition["mountPoint"]) - def get_mount_options(self, filesystem, mount_point): - efiMountPoint = libcalamares.globalstorage.value("efiSystemPartition") - job_config = libcalamares.job.configuration - - if (mount_point == efiMountPoint and "efiMountOptions" in job_config): - return job_config["efiMountOptions"] - - return self.mount_options.get(filesystem, - self.mount_options["default"]) + def get_mount_options(self, filesystem, mountpoint): + return next((x for x in self.mount_options_list if x["mountpoint"] == mountpoint), "defaults") def create_swapfile(root_mount_point, root_btrfs): @@ -383,21 +360,19 @@ def run(): swap_choice = None libcalamares.job.setprogress(0.1) - mount_options = conf.get("mountOptions", {}) - ssd_extra_mount_options = conf.get("ssdExtraMountOptions", {}) + mount_options_list = global_storage.value("mountOptionsList") crypttab_options = conf.get("crypttabOptions", "luks") # We rely on mount_options having a default; if there wasn't one, # bail out with a meaningful error. - if not mount_options: + if not mount_options_list: return (_("Configuration Error"), _("No
{!s}
configuration is given for
{!s}
to use.") .format("mountOptions", "fstab")) generator = FstabGenerator(partitions, root_mount_point, - mount_options, - ssd_extra_mount_options, + mount_options_list, crypttab_options) if swap_choice is not None: diff --git a/src/modules/mount/main.py b/src/modules/mount/main.py index a3318d1a0..90609cb56 100644 --- a/src/modules/mount/main.py +++ b/src/modules/mount/main.py @@ -16,6 +16,7 @@ import tempfile import subprocess import os +import re import libcalamares @@ -42,6 +43,72 @@ def pretty_name(): return _("Mounting partitions.") +def disk_name_for_partition(partition): + """ Returns disk name for each found partition. + + :param partition: + :return: + """ + name = os.path.basename(partition["device"]) + + if name.startswith("/dev/mmcblk") or name.startswith("/dev/nvme"): + return re.sub("p[0-9]+$", "", name) + + return re.sub("[0-9]+$", "", name) + + +def is_ssd_disk(partition): + """ Checks if given disk is actually a ssd disk. + + :param partition: + :return: + """ + + disk_name = disk_name_for_partition(partition) + filename = os.path.join("/sys/block", disk_name, "queue/rotational") + + if not os.path.exists(filename): + # Should not happen unless sysfs changes, but better safe than sorry + return False + + with open(filename) as sysfile: + return sysfile.read() == "0\n" + + +def get_mount_options(filesystem, mount_options, partition): + """ + + :param filesystem: + :param mount_options: + :param partition: + :return: + """ + # If there are no mount options defined then we use the defaults + if mount_options is None: + return "defaults" + + options = next((x for x in mount_options if x["filesystem"] == filesystem), None) + + # If there is no match then check for default options + if options is None: + options = next((x for x in mount_options if x["filesystem"] == "default"), None) + + # If it is still None, then fallback to returning defaults + if options is None: + return "defaults" + + option_items = options.get("options", []) + if is_ssd_disk(partition): + option_items.extend(options.get("ssdOptions", [])) + else: + option_items.extend(options.get("hddOptions", [])) + + if option_items: + return ",".join(option_items) + else: + return "defaults" + + def get_btrfs_subvolumes(partitions): """ Gets the job-configuration for btrfs subvolumes, or if there is @@ -69,7 +136,9 @@ def get_btrfs_subvolumes(partitions): # If we have a swap **file**, give it a separate subvolume. swap_choice = libcalamares.globalstorage.value("partitionChoices") if swap_choice and swap_choice.get("swap", None) == "file": - btrfs_subvolumes.append({'mountPoint': '/swap', 'subvolume': '/@swap'}) + swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap") + btrfs_subvolumes.append({'mountPoint': '/swap', 'subvolume': swap_subvol}) + libcalamares.globalstorage.insert("btrfsSwapSubvol", swap_subvol) return btrfs_subvolumes @@ -138,7 +207,7 @@ def mount_zfs(root_mount_point, partition): raise ZfsException(_("Failed to set zfs mountpoint")) -def mount_partition(root_mount_point, partition, partitions): +def mount_partition(root_mount_point, partition, partitions, mount_options, mount_options_list): """ Do a single mount of @p partition inside @p root_mount_point. """ @@ -176,11 +245,13 @@ def mount_partition(root_mount_point, partition, partitions): if fstype == "zfs": mount_zfs(root_mount_point, partition) else: # fstype == "zfs" + mount_options_string = get_mount_options(fstype, mount_options, partition) if libcalamares.utils.mount(device, mount_point, fstype, - partition.get("options", "")) != 0: + mount_options_string) != 0: libcalamares.utils.warning("Cannot mount {}".format(device)) + mount_options_list.append({"mountpoint": mount_point, "option_string": mount_options_string}) # Special handling for btrfs subvolumes. Create the subvolumes listed in mount.conf if fstype == "btrfs" and partition["mountPoint"] == '/': @@ -207,13 +278,19 @@ def mount_partition(root_mount_point, partition, partitions): device = os.path.join("/dev/mapper", partition["luksMapperName"]) # Mount the subvolumes + swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap") for s in btrfs_subvolumes: mount_option = "subvol={}".format(s['subvolume']) + if s['subvolume'] == swap_subvol: + mount_option += "," + get_mount_options("btrfs_swap", mount_options, partition) + else: + mount_option += "," + get_mount_options(fstype, mount_options, partition) subvolume_mountpoint = mount_point[:-1] + s['mountPoint'] + mount_options_list.append({"mountpoint": subvolume_mountpoint, "option_string": mount_option}) if libcalamares.utils.mount(device, subvolume_mountpoint, fstype, - ",".join([mount_option, partition.get("options", "")])) != 0: + mount_option) != 0: libcalamares.utils.warning("Cannot mount {}".format(device)) @@ -222,6 +299,7 @@ def run(): Mount all the partitions from GlobalStorage and from the job configuration. Partitions are mounted in-lexical-order of their mountPoint. """ + partitions = libcalamares.globalstorage.value("partitions") if not partitions: @@ -231,14 +309,18 @@ def run(): root_mount_point = tempfile.mkdtemp(prefix="calamares-root-") + # Get the mountOptions, if this is None, that is OK and will be handled later + mount_options = libcalamares.job.configuration.get("mountOptions") + # Guard against missing keys (generally a sign that the config file is bad) extra_mounts = libcalamares.job.configuration.get("extraMounts") or [] - extra_mounts_efi = libcalamares.job.configuration.get("extraMountsEfi") or [] - if not extra_mounts and not extra_mounts_efi: + if not extra_mounts: libcalamares.utils.warning("No extra mounts defined. Does mount.conf exist?") - if libcalamares.globalstorage.value("firmwareType") == "efi": - extra_mounts.extend(extra_mounts_efi) + if libcalamares.globalstorage.value("firmwareType") != "efi": + for mount in extra_mounts: + if mount.get("efi", None) is True: + extra_mounts.remove(mount) # Add extra mounts to the partitions list and sort by mount points. # This way, we ensure / is mounted before the rest, and every mount point @@ -246,13 +328,17 @@ def run(): # under /tmp, we make sure /tmp is mounted before the partition) mountable_partitions = [p for p in partitions + extra_mounts if "mountPoint" in p and p["mountPoint"]] mountable_partitions.sort(key=lambda x: x["mountPoint"]) + + # mount_options_list will be inserted into global storage for use in fstab later + mount_options_list = [] try: for partition in mountable_partitions: - mount_partition(root_mount_point, partition, partitions) + mount_partition(root_mount_point, partition, partitions, mount_options, mount_options_list) except ZfsException as ze: return _("zfs mounting error"), ze.message libcalamares.globalstorage.insert("rootMountPoint", root_mount_point) + libcalamares.globalstorage.insert("mountOptionsList", mount_options_list) # Remember the extra mounts for the unpackfs module libcalamares.globalstorage.insert("extraMounts", extra_mounts) diff --git a/src/modules/mount/mount.conf b/src/modules/mount/mount.conf index 84dca05a7..56313b188 100644 --- a/src/modules/mount/mount.conf +++ b/src/modules/mount/mount.conf @@ -35,11 +35,10 @@ extraMounts: - device: /run/udev mountPoint: /run/udev options: bind - -extraMountsEfi: - device: efivarfs fs: efivarfs mountPoint: /sys/firmware/efi/efivars + efi: true # Btrfs subvolumes to create if root filesystem is on btrfs volume. # If *mountpoint* is mounted already to another partition, it is ignored. @@ -47,9 +46,7 @@ extraMountsEfi: # # It is possible to prevent subvolume creation -- this is likely only relevant # for the root (/) subvolume -- by giving an empty string as a subvolume -# name. In this case no subvolume will be created. When using snapper as -# a rollback mechanism, it is recommended to **not** create a subvolume -# for root. +# name. In this case no subvolume will be created. btrfsSubvolumes: - mountPoint: / @@ -63,3 +60,21 @@ btrfsSubvolumes: subvolume: /@cache - mountPoint: /var/log subvolume: /@log + +btrfsSwapSubvol: /@swap + +mountOptions: + - filesystem: default + options: [ defaults, noatime ] + - filesystem: efi + options: [ defaults, umask=0077 ] + - filesystem: btrfs + options: [ defaults, noatime, compress ] + ssdOptions: [ discard=async ] + hddOptions: [ autodefrag ] + - filesystem: btrfs_swap + options: [ defaults, noatime ] + + + + diff --git a/src/modules/mount/mount.schema.yaml b/src/modules/mount/mount.schema.yaml index fb5dfb69c..0392c1120 100644 --- a/src/modules/mount/mount.schema.yaml +++ b/src/modules/mount/mount.schema.yaml @@ -6,7 +6,6 @@ $id: https://calamares.io/schemas/mount additionalProperties: false type: object properties: - # TODO: share the schema definition, since these are identical extraMounts: type: array items: @@ -17,17 +16,7 @@ properties: fs: { type: string } mountPoint: { type: string } options: { type: string } - required: [ device, mountPoint ] - extraMountsEfi: - type: array - items: - type: object - additionalProperties: false - properties: - device: { type: string } - fs: { type: string } - mountPoint: { type: string } - options: { type: string } + efi: { type: boolean, default: false } required: [ device, mountPoint ] btrfsSubvolumes: type: array @@ -38,3 +27,17 @@ properties: mountPoint: { type: string } subvolume: { type: string } required: [ subvolume, mountPoint ] + btrfsSwapSubvol: { type: string } + mountOptions: + type: array + items: + type: object + additionalProperties: false + properties: + filesystem: { type: string } + options: { type: array } + ssdOptions: { type: array } + hddOptions: { type: array } + required: [ filesystem ] + + diff --git a/src/modules/partition/partition.conf b/src/modules/partition/partition.conf index b03c855db..2cd7fb8ea 100644 --- a/src/modules/partition/partition.conf +++ b/src/modules/partition/partition.conf @@ -156,7 +156,7 @@ defaultFileSystemType: "ext4" # # If not specified at all, uses *defaultFileSystemType* without a # warning (this matches traditional no-choice-available behavior best). -# availableFileSystemTypes: ["ext4","f2fs"] +availableFileSystemTypes: ["ext4","f2fs","btrfs"] # Show/hide LUKS related functionality in automated partitioning modes. # Disable this if you choose not to deploy early unlocking support in GRUB2