2014-07-22 17:38:20 +02:00
|
|
|
#!/usr/bin/env python3
|
2015-02-18 15:06:10 +01:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
2020-08-25 16:05:56 +02:00
|
|
|
# === This file is part of Calamares - <https://calamares.io> ===
|
2014-07-22 17:38:20 +02:00
|
|
|
#
|
2020-08-24 16:36:47 +02:00
|
|
|
# SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
|
|
|
# SPDX-FileCopyrightText: 2017 Alf Gaida <agaida@siduction.org>
|
|
|
|
# SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
|
|
|
# SPDX-FileCopyrightText: 2019 Kevin Kofler <kevin.kofler@chello.at>
|
[mount] Print a warning if mount failure
The return of the call to libcalamares.utils.mount is never tested and
it may fail silently; this causes some mounpoints to be missing.
This adds a warning if mountpoint cannot be mounted.
chcon: failed to get security context of '/tmp/verity': Operation not supported
06:44:23 [6]: static CalamaresUtils::ProcessResult CalamaresUtils::System::runCommand(CalamaresUtils::System::RunLocation, const QStringList&, const QString&, const QString&, std::chrono::seconds)
Running "env" ("mount", "-t", "unformatted", "/dev/sdb2", "/tmp/calamares-root-kv8dqgb5/tmp/verity")
.. Finished. Exit code: 32
.. Target cmd: ("mount", "-t", "unformatted", "/dev/sdb7", "/tmp/calamares-root-kv8dqgb5/tmp/verity") output:
mount: /tmp/calamares-root-kv8dqgb5/tmp/verity: unknown filesystem type 'unformatted'.
2020-10-31 09:44:49 +01:00
|
|
|
# SPDX-FileCopyrightText: 2019-2020 Collabora Ltd
|
2020-08-24 16:36:47 +02:00
|
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
2014-07-22 17:38:20 +02:00
|
|
|
#
|
2020-08-25 16:05:56 +02:00
|
|
|
# Calamares is Free Software: see the License-Identifier above.
|
2014-07-22 17:38:20 +02:00
|
|
|
#
|
|
|
|
|
2014-07-24 10:17:46 +02:00
|
|
|
import tempfile
|
2016-11-10 15:58:05 +01:00
|
|
|
import subprocess
|
mount: copy the SELinux context of the host directory to the mountpoint
On systems with SELinux enabled, we have to create the directories on
top of which we mount another partition or virtual file system (e.g.,
/dev) with the correct SELinux context, BEFORE we mount the other
partition. Otherwise, SELinux will get really confused when systemd
tries to recreate the mount tree for a private file system namespace for
a service. And unfortunately, even an autorelabel does not fix it
because it runs when /dev etc. are already mounted.
Without this fix, on Fedora >= 30, the system installed with Calamares
would fail to start the dbus-broker system bus, leading to several
important pieces of functionality not working (e.g., shutdown as
non-root).
On systems without SELinux enabled, chcon (which is part of coreutils)
will just print a warning and do nothing, so this should always be safe.
2019-05-09 13:50:31 +02:00
|
|
|
import os
|
2014-07-22 17:38:20 +02:00
|
|
|
|
2014-07-24 10:17:46 +02:00
|
|
|
import libcalamares
|
2014-07-22 17:38:20 +02:00
|
|
|
|
2019-04-19 17:02:03 +02:00
|
|
|
import gettext
|
|
|
|
_ = gettext.translation("calamares-python",
|
|
|
|
localedir=libcalamares.utils.gettext_path(),
|
|
|
|
languages=libcalamares.utils.gettext_languages(),
|
|
|
|
fallback=True).gettext
|
|
|
|
|
|
|
|
|
|
|
|
def pretty_name():
|
|
|
|
return _("Mounting partitions.")
|
|
|
|
|
2021-07-06 15:31:00 +02:00
|
|
|
|
2021-07-06 16:43:56 +02:00
|
|
|
def get_btrfs_subvolumes(partitions):
|
2021-07-06 15:31:00 +02:00
|
|
|
"""
|
|
|
|
Gets the job-configuration for btrfs subvolumes, or if there is
|
|
|
|
none given, returns a default configuration that matches
|
|
|
|
the setup (/ and /home) from before configurability was introduced.
|
2021-07-06 16:43:56 +02:00
|
|
|
|
|
|
|
@param partitions
|
|
|
|
The partitions (from the partitioning module) that will exist on disk.
|
|
|
|
This is used to filter out subvolumes that don't need to be created
|
|
|
|
because they get a dedicated partition instead.
|
2021-07-06 15:31:00 +02:00
|
|
|
"""
|
|
|
|
btrfs_subvolumes = libcalamares.job.configuration.get("btrfsSubvolumes", None)
|
|
|
|
# Warn if there's no configuration at all, and empty configurations are
|
|
|
|
# replaced by a simple root-only layout.
|
|
|
|
if btrfs_subvolumes is None:
|
|
|
|
libcalamares.utils.warning("No configuration for btrfsSubvolumes")
|
|
|
|
if not btrfs_subvolumes:
|
2021-07-06 15:34:13 +02:00
|
|
|
btrfs_subvolumes = [ dict(mountPoint="/", subvolume="/@"), dict(mountPoint="/home", subvolume="/@home") ]
|
2021-07-06 15:31:00 +02:00
|
|
|
|
2021-07-06 16:43:56 +02:00
|
|
|
# Filter out the subvolumes which have a dedicated partition
|
|
|
|
non_root_partition_mounts = [ m for m in [ p.get("mountPoint", None) for p in partitions ] if m is not None and m != '/' ]
|
2021-07-07 12:57:08 +02:00
|
|
|
btrfs_subvolumes = list(filter(lambda s : s["mountPoint"] not in non_root_partition_mounts, btrfs_subvolumes))
|
2021-07-06 16:43:56 +02:00
|
|
|
|
2021-07-06 16:48:46 +02:00
|
|
|
# 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'})
|
|
|
|
|
2021-07-06 15:31:00 +02:00
|
|
|
return btrfs_subvolumes
|
|
|
|
|
|
|
|
|
2021-11-09 21:53:44 +01:00
|
|
|
def parse_global_storage(gs_value):
|
|
|
|
"""
|
|
|
|
Something in the chain is converting on and off to true and false. This converts it back.
|
|
|
|
|
|
|
|
:param gs_value: The value from global storage
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
if gs_value is True:
|
|
|
|
return "on"
|
|
|
|
elif gs_value is False:
|
|
|
|
return "off"
|
|
|
|
else:
|
|
|
|
return gs_value
|
|
|
|
|
|
|
|
|
2019-08-14 09:58:40 +02:00
|
|
|
def mount_partition(root_mount_point, partition, partitions):
|
2019-08-19 12:31:37 +02:00
|
|
|
"""
|
|
|
|
Do a single mount of @p partition inside @p root_mount_point.
|
|
|
|
"""
|
2019-07-17 12:15:22 +02:00
|
|
|
# Create mount point with `+` rather than `os.path.join()` because
|
|
|
|
# `partition["mountPoint"]` starts with a '/'.
|
|
|
|
raw_mount_point = partition["mountPoint"]
|
2020-10-31 12:18:30 +01:00
|
|
|
if not raw_mount_point:
|
|
|
|
return
|
|
|
|
|
2019-07-17 12:15:22 +02:00
|
|
|
mount_point = root_mount_point + raw_mount_point
|
|
|
|
|
|
|
|
# Ensure that the created directory has the correct SELinux context on
|
|
|
|
# SELinux-enabled systems.
|
|
|
|
os.makedirs(mount_point, exist_ok=True)
|
2020-06-09 13:24:12 +02:00
|
|
|
try:
|
|
|
|
subprocess.call(['chcon', '--reference=' + raw_mount_point, mount_point])
|
|
|
|
except FileNotFoundError as e:
|
|
|
|
libcalamares.utils.warning(str(e))
|
|
|
|
except OSError:
|
|
|
|
libcalamares.utils.error("Cannot run 'chcon' normally.")
|
|
|
|
raise
|
2019-07-17 12:15:22 +02:00
|
|
|
|
|
|
|
fstype = partition.get("fs", "").lower()
|
2020-11-06 11:56:29 +01:00
|
|
|
if fstype == "unformatted":
|
2020-10-31 12:19:53 +01:00
|
|
|
return
|
2019-07-17 12:15:22 +02:00
|
|
|
|
|
|
|
if fstype == "fat16" or fstype == "fat32":
|
|
|
|
fstype = "vfat"
|
|
|
|
|
[mount] Print a warning if mount failure
The return of the call to libcalamares.utils.mount is never tested and
it may fail silently; this causes some mounpoints to be missing.
This adds a warning if mountpoint cannot be mounted.
chcon: failed to get security context of '/tmp/verity': Operation not supported
06:44:23 [6]: static CalamaresUtils::ProcessResult CalamaresUtils::System::runCommand(CalamaresUtils::System::RunLocation, const QStringList&, const QString&, const QString&, std::chrono::seconds)
Running "env" ("mount", "-t", "unformatted", "/dev/sdb2", "/tmp/calamares-root-kv8dqgb5/tmp/verity")
.. Finished. Exit code: 32
.. Target cmd: ("mount", "-t", "unformatted", "/dev/sdb7", "/tmp/calamares-root-kv8dqgb5/tmp/verity") output:
mount: /tmp/calamares-root-kv8dqgb5/tmp/verity: unknown filesystem type 'unformatted'.
2020-10-31 09:44:49 +01:00
|
|
|
device = partition["device"]
|
|
|
|
|
2019-07-17 12:15:22 +02:00
|
|
|
if "luksMapperName" in partition:
|
[mount] Print a warning if mount failure
The return of the call to libcalamares.utils.mount is never tested and
it may fail silently; this causes some mounpoints to be missing.
This adds a warning if mountpoint cannot be mounted.
chcon: failed to get security context of '/tmp/verity': Operation not supported
06:44:23 [6]: static CalamaresUtils::ProcessResult CalamaresUtils::System::runCommand(CalamaresUtils::System::RunLocation, const QStringList&, const QString&, const QString&, std::chrono::seconds)
Running "env" ("mount", "-t", "unformatted", "/dev/sdb2", "/tmp/calamares-root-kv8dqgb5/tmp/verity")
.. Finished. Exit code: 32
.. Target cmd: ("mount", "-t", "unformatted", "/dev/sdb7", "/tmp/calamares-root-kv8dqgb5/tmp/verity") output:
mount: /tmp/calamares-root-kv8dqgb5/tmp/verity: unknown filesystem type 'unformatted'.
2020-10-31 09:44:49 +01:00
|
|
|
device = os.path.join("/dev/mapper", partition["luksMapperName"])
|
|
|
|
|
|
|
|
if libcalamares.utils.mount(device,
|
|
|
|
mount_point,
|
|
|
|
fstype,
|
|
|
|
partition.get("options", "")) != 0:
|
|
|
|
libcalamares.utils.warning("Cannot mount {}".format(device))
|
2019-07-17 12:15:22 +02:00
|
|
|
|
2021-02-06 18:48:09 +01:00
|
|
|
# Special handling for btrfs subvolumes. Create the subvolumes listed in mount.conf
|
2019-07-17 12:15:22 +02:00
|
|
|
if fstype == "btrfs" and partition["mountPoint"] == '/':
|
2021-01-26 21:24:50 +01:00
|
|
|
# Root has been mounted to btrfs volume -> create subvolumes from configuration
|
2021-07-06 16:43:56 +02:00
|
|
|
btrfs_subvolumes = get_btrfs_subvolumes(partitions)
|
|
|
|
|
2021-01-26 21:48:02 +01:00
|
|
|
# Store created list in global storage so it can be used in the fstab module
|
|
|
|
libcalamares.globalstorage.insert("btrfsSubvolumes", btrfs_subvolumes)
|
2021-01-26 21:35:42 +01:00
|
|
|
# Create the subvolumes that are in the completed list
|
|
|
|
for s in btrfs_subvolumes:
|
2020-12-07 21:59:29 +01:00
|
|
|
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
2021-01-26 21:35:42 +01:00
|
|
|
root_mount_point + s['subvolume']])
|
2020-12-07 21:52:39 +01:00
|
|
|
|
2019-07-17 12:15:22 +02:00
|
|
|
subprocess.check_call(["umount", "-v", root_mount_point])
|
2014-09-24 18:13:05 +02:00
|
|
|
|
[mount] Print a warning if mount failure
The return of the call to libcalamares.utils.mount is never tested and
it may fail silently; this causes some mounpoints to be missing.
This adds a warning if mountpoint cannot be mounted.
chcon: failed to get security context of '/tmp/verity': Operation not supported
06:44:23 [6]: static CalamaresUtils::ProcessResult CalamaresUtils::System::runCommand(CalamaresUtils::System::RunLocation, const QStringList&, const QString&, const QString&, std::chrono::seconds)
Running "env" ("mount", "-t", "unformatted", "/dev/sdb2", "/tmp/calamares-root-kv8dqgb5/tmp/verity")
.. Finished. Exit code: 32
.. Target cmd: ("mount", "-t", "unformatted", "/dev/sdb7", "/tmp/calamares-root-kv8dqgb5/tmp/verity") output:
mount: /tmp/calamares-root-kv8dqgb5/tmp/verity: unknown filesystem type 'unformatted'.
2020-10-31 09:44:49 +01:00
|
|
|
device = partition["device"]
|
|
|
|
|
2016-05-05 13:54:15 +02:00
|
|
|
if "luksMapperName" in partition:
|
[mount] Print a warning if mount failure
The return of the call to libcalamares.utils.mount is never tested and
it may fail silently; this causes some mounpoints to be missing.
This adds a warning if mountpoint cannot be mounted.
chcon: failed to get security context of '/tmp/verity': Operation not supported
06:44:23 [6]: static CalamaresUtils::ProcessResult CalamaresUtils::System::runCommand(CalamaresUtils::System::RunLocation, const QStringList&, const QString&, const QString&, std::chrono::seconds)
Running "env" ("mount", "-t", "unformatted", "/dev/sdb2", "/tmp/calamares-root-kv8dqgb5/tmp/verity")
.. Finished. Exit code: 32
.. Target cmd: ("mount", "-t", "unformatted", "/dev/sdb7", "/tmp/calamares-root-kv8dqgb5/tmp/verity") output:
mount: /tmp/calamares-root-kv8dqgb5/tmp/verity: unknown filesystem type 'unformatted'.
2020-10-31 09:44:49 +01:00
|
|
|
device = os.path.join("/dev/mapper", partition["luksMapperName"])
|
|
|
|
|
2021-01-26 21:24:50 +01:00
|
|
|
# Mount the subvolumes
|
|
|
|
for s in btrfs_subvolumes:
|
2021-02-07 14:39:38 +01:00
|
|
|
mount_option = "subvol={}".format(s['subvolume'])
|
2021-02-06 23:16:26 +01:00
|
|
|
subvolume_mountpoint = mount_point[:-1] + s['mountPoint']
|
2021-01-26 21:24:50 +01:00
|
|
|
if libcalamares.utils.mount(device,
|
2021-02-06 23:16:26 +01:00
|
|
|
subvolume_mountpoint,
|
[mount] Print a warning if mount failure
The return of the call to libcalamares.utils.mount is never tested and
it may fail silently; this causes some mounpoints to be missing.
This adds a warning if mountpoint cannot be mounted.
chcon: failed to get security context of '/tmp/verity': Operation not supported
06:44:23 [6]: static CalamaresUtils::ProcessResult CalamaresUtils::System::runCommand(CalamaresUtils::System::RunLocation, const QStringList&, const QString&, const QString&, std::chrono::seconds)
Running "env" ("mount", "-t", "unformatted", "/dev/sdb2", "/tmp/calamares-root-kv8dqgb5/tmp/verity")
.. Finished. Exit code: 32
.. Target cmd: ("mount", "-t", "unformatted", "/dev/sdb7", "/tmp/calamares-root-kv8dqgb5/tmp/verity") output:
mount: /tmp/calamares-root-kv8dqgb5/tmp/verity: unknown filesystem type 'unformatted'.
2020-10-31 09:44:49 +01:00
|
|
|
fstype,
|
2021-01-26 21:24:50 +01:00
|
|
|
",".join([mount_option, partition.get("options", "")])) != 0:
|
2021-02-07 14:39:38 +01:00
|
|
|
libcalamares.utils.warning("Cannot mount {}".format(device))
|
2019-07-17 12:15:22 +02:00
|
|
|
|
2021-11-06 20:33:43 +01:00
|
|
|
if fstype == "zfs" and partition["mountPoint"] == '/':
|
|
|
|
# Get the zfs dataset list from global storage
|
|
|
|
zfs = libcalamares.globalstorage.value("zfs")
|
|
|
|
|
|
|
|
if not zfs:
|
2021-11-09 21:53:44 +01:00
|
|
|
libcalamares.utils.warning("Failed to locate zfs dataset list")
|
|
|
|
raise Exception("Internal error mounting zfs datasets")
|
|
|
|
|
|
|
|
# import the zpool
|
|
|
|
import_result = subprocess.run(['zpool', 'import', '-R', root_mount_point, zfs[0]['zpool']])
|
|
|
|
if import_result.returncode != 0:
|
|
|
|
raise Exception("Failed to import zpool")
|
|
|
|
|
|
|
|
passphrase = libcalamares.globalstorage.value("encryptphrase")
|
|
|
|
if passphrase:
|
|
|
|
# The zpool is encrypted, we need to unlock it
|
|
|
|
loadkey_result = subprocess.run(['sh', '-c', 'echo "' + passphrase + '" | zfs load-key ' + zfs[0]['zpool']])
|
|
|
|
if loadkey_result.returncode != 0:
|
|
|
|
raise Exception("Failed to unlock zpool")
|
|
|
|
|
|
|
|
# first we handle the / dataset if there is one
|
|
|
|
for dataset in zfs:
|
|
|
|
if dataset['mountpoint'] == '/':
|
|
|
|
# Properly set the canmount field from global storage
|
|
|
|
can_mount = parse_global_storage(dataset['canMount'])
|
|
|
|
set_result = subprocess.run(['zfs', 'set', 'canmount=' + can_mount,
|
|
|
|
dataset['zpool'] + '/' + dataset['dsName']])
|
|
|
|
if set_result.returncode != 0:
|
|
|
|
raise Exception("Failed to set zfs mountpoint")
|
|
|
|
if dataset['canMount'] == 'noauto':
|
|
|
|
mount_result = subprocess.run(['zfs', 'mount', dataset['zpool'] + '/' + dataset['dsName']])
|
|
|
|
if mount_result.returncode != 0:
|
|
|
|
raise Exception("Failed to mount root dataset")
|
2021-11-06 20:33:43 +01:00
|
|
|
|
|
|
|
# Set the canmount property for each dataset. This will effectively mount the dataset
|
|
|
|
for dataset in zfs:
|
2021-11-09 21:53:44 +01:00
|
|
|
# We already handled the / mountpoint above
|
|
|
|
if dataset['mountpoint'] != '/':
|
|
|
|
can_mount = parse_global_storage(dataset['canMount'])
|
|
|
|
|
|
|
|
set_result = subprocess.run(['zfs', 'set', 'canmount=' + can_mount,
|
|
|
|
dataset['zpool'] + '/' + dataset['dsName']])
|
|
|
|
if set_result.returncode != 0:
|
|
|
|
raise Exception("Failed to set zfs mountpoint")
|
2021-11-06 20:33:43 +01:00
|
|
|
|
2014-07-22 17:38:20 +02:00
|
|
|
|
2014-07-25 16:41:21 +02:00
|
|
|
def run():
|
2017-03-29 21:09:25 +02:00
|
|
|
"""
|
2019-08-19 12:31:37 +02:00
|
|
|
Mount all the partitions from GlobalStorage and from the job configuration.
|
|
|
|
Partitions are mounted in-lexical-order of their mountPoint.
|
2015-02-20 20:54:25 +01:00
|
|
|
"""
|
2014-07-29 14:45:58 +02:00
|
|
|
partitions = libcalamares.globalstorage.value("partitions")
|
2018-11-28 13:26:40 +01:00
|
|
|
|
2019-04-28 20:32:27 +02:00
|
|
|
if not partitions:
|
|
|
|
libcalamares.utils.warning("partitions is empty, {!s}".format(partitions))
|
|
|
|
return (_("Configuration Error"),
|
|
|
|
_("No partitions are defined for <pre>{!s}</pre> to use." ).format("mount"))
|
|
|
|
|
|
|
|
root_mount_point = tempfile.mkdtemp(prefix="calamares-root-")
|
|
|
|
|
2018-11-28 13:26:40 +01:00
|
|
|
# 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:
|
|
|
|
libcalamares.utils.warning("No extra mounts defined. Does mount.conf exist?")
|
2014-07-24 17:14:56 +02:00
|
|
|
|
2018-11-28 13:26:40 +01:00
|
|
|
if libcalamares.globalstorage.value("firmwareType") == "efi":
|
2019-08-14 09:58:40 +02:00
|
|
|
extra_mounts.extend(extra_mounts_efi)
|
|
|
|
|
|
|
|
# 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
|
|
|
|
# is created on the right partition (e.g. if a partition is to be mounted
|
|
|
|
# under /tmp, we make sure /tmp is mounted before the partition)
|
2019-08-19 12:28:32 +02:00
|
|
|
mountable_partitions = [ p for p in partitions + extra_mounts if "mountPoint" in p and p["mountPoint"] ]
|
|
|
|
mountable_partitions.sort(key=lambda x: x["mountPoint"])
|
|
|
|
for partition in mountable_partitions:
|
2019-08-14 09:58:40 +02:00
|
|
|
mount_partition(root_mount_point, partition, partitions)
|
2014-07-24 17:14:56 +02:00
|
|
|
|
2014-07-29 14:45:58 +02:00
|
|
|
libcalamares.globalstorage.insert("rootMountPoint", root_mount_point)
|
2015-06-14 13:04:52 +02:00
|
|
|
|
2014-12-05 00:17:33 +01:00
|
|
|
# Remember the extra mounts for the unpackfs module
|
2019-08-14 09:58:40 +02:00
|
|
|
libcalamares.globalstorage.insert("extraMounts", extra_mounts)
|