* ~2023: remove support for TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT
+* H2 2023: remove support for unmerged-usr
+
Features:
* add ability to path_is_valid() to classify paths that refer to a dir from
* sd-boot: include domain specific hash string in hash function for random seed
plus sizes of everything. also include DMI/SMBIOS blob
-* accept a random seed via DMI/SMBIOS vendor string that is credited to the
- kernel RNG, as cheap alternative to virtio-rng (problem: when credited it
- must also be invalidated, question is if we can safely do that for SMBIOS
- data structures)
-
* sd-stub: invoke random seed logic the same way as in sd-boot, except if
random seed EFI variable is already set. That way, the variable set will be
set in all cases: if you just use sd-stub, or just sd-boot, or both.
* systemd-repart: make it a static checker during early boot for existence and
absence of other partitions for trusted boot environments
- * systemd-repart: add support for GPT_FLAG_GROWFS also on real systems, i.e.
+ * systemd-repart: add support for SD_GPT_FLAG_GROWFS also on real systems, i.e.
generate some unit to actually enlarge the fs after growing the partition
during boot.
- in particular an example how to do the equivalent of switching runlevels
- man: maybe sort directives in man pages, and take sections from --help and apply them to man too
- document root=gpt-auto properly
- - GPT_FLAG_GROWFS is not documented at all. GPT_FLAG_* should be documented in Discoverable Partitions.
* systemctl:
- add systemctl switch to dump transaction without executing it
<refnamediv>
<refname>kernel-install</refname>
- <refpurpose>Add and remove kernel and initramfs images to and from /boot</refpurpose>
+ <refpurpose>Add and remove kernel and initrd images to and from /boot</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>kernel-install</command>
- <arg choice="plain">COMMAND</arg>
<arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">COMMAND</arg>
<arg choice="plain"><replaceable>KERNEL-VERSION</replaceable></arg>
<arg choice="plain"><replaceable>KERNEL-IMAGE</replaceable></arg>
<arg choice="opt" rep="repeat"><replaceable>INITRD-FILE</replaceable></arg>
<refsect1>
<title>Description</title>
- <para><command>kernel-install</command> is used to install and remove kernel and initramfs images to and
- from the boot loader partition, referred to as <varname>$BOOT</varname> here. It will usually be one of
- <filename>/boot/</filename>, <filename>/efi/</filename>, or <filename>/boot/efi/</filename>, see below.
- </para>
+ <para><command>kernel-install</command> is used to install and remove kernel and initrd images
+ <footnote>
+ <para>Nowadays actually CPIO archives used as an "initramfs", rather than "initrd". See
+ <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry> for an
+ explanation.</para>
+ </footnote>
+ to and from the boot loader partition, referred to as <varname>$BOOT</varname> here. It will usually be
+ one of <filename>/boot/</filename>, <filename>/efi/</filename>, or <filename>/boot/efi/</filename>, see
+ below.</para>
<para><command>kernel-install</command> will run the executable files ("plugins") located in the
directory <filename>/usr/lib/kernel/install.d/</filename> and the local administration directory
<varlistentry>
<term><command>add <replaceable>KERNEL-VERSION</replaceable> <replaceable>KERNEL-IMAGE</replaceable> [<replaceable>INITRD-FILE</replaceable> ...]</command></term>
<listitem>
- <para>This command expects a kernel version string and a path to a kernel image file as
- arguments. Optionally, one or more initial RAM disk images may be specified as well (note that
- plugins might generate additional ones). <command>kernel-install</command> calls the executable
- files from <filename>/usr/lib/kernel/install.d/*.install</filename> and
+ <para>This command expects a kernel version string and a path to a kernel image file as arguments.
+ Optionally, one or more initrd images may be specified as well (note that plugins might generate
+ additional ones). <command>kernel-install</command> calls the executable files from
+ <filename>/usr/lib/kernel/install.d/*.install</filename> and
<filename>/etc/kernel/install.d/*.install</filename> (i.e. the plugins) with the following
arguments:</para>
<programlisting>add <replaceable>KERNEL-VERSION</replaceable> <filename>$BOOT/<replaceable>ENTRY-TOKEN</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename> <replaceable>KERNEL-IMAGE</replaceable> [<replaceable>INITRD-FILE</replaceable> ...]</programlisting>
- <para>The third argument directly refers to the path where to place kernel images, initial RAM disk
+ <para>The third argument directly refers to the path where to place kernel images, initrd
images and other resources for <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
Loader Specification</ulink> Type #1 entries (the "entry directory"). If other boot loader schemes
are used the parameter may be ignored. The <replaceable>ENTRY-TOKEN</replaceable> string is
readonly s DefaultStandardOutput = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s DefaultStandardError = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s WatchdogDevice = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t WatchdogLastPingTimestamp = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t WatchdogLastPingTimestampMonotonic = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
@org.freedesktop.systemd1.Privileged("true")
readwrite t RuntimeWatchdogUSec = ...;
readonly i DefaultOOMScoreAdjust = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s CtrlAltDelBurstAction = '...';
- @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
- readonly s WatchdogDevice = '...';
- @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
- readonly t WatchdogTimeoutUsec = ...;
- @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
- readonly t WatchdogPreTimeoutUsec = ...;
- @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
- readonly t WatchdogLastPingTimestamp = ...;
- @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
- readonly t WatchdogLastPingTimestampMonotonic = ...;
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<!--property DefaultStandardError is not documented!-->
+ <!--property WatchdogDevice is not documented!-->
+
+ <!--property WatchdogLastPingTimestamp is not documented!-->
+
+ <!--property WatchdogLastPingTimestampMonotonic is not documented!-->
+
<!--property RuntimeWatchdogUSec is not documented!-->
<!--property RuntimeWatchdogPreUSec is not documented!-->
<!--property CtrlAltDelBurstAction is not documented!-->
- <!--property WatchdogDevice is not documented!-->
-
- <!--property WatchdogTimeoutUsec is not documented!-->
-
- <!--property WatchdogPreTimeoutUsec is not documented!-->
-
- <!--property WatchdogLastPingTimestamp is not documented!-->
-
- <!--property WatchdogLastPingTimestampMonotonic is not documented!-->
-
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Manager"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStandardError"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="WatchdogDevice"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="WatchdogLastPingTimestamp"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="WatchdogLastPingTimestampMonotonic"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeWatchdogUSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="RuntimeWatchdogPreUSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="CtrlAltDelBurstAction"/>
- <variablelist class="dbus-property" generated="True" extra-ref="WatchdogDevice"/>
-
- <variablelist class="dbus-property" generated="True" extra-ref="WatchdogTimeoutUsec"/>
-
- <variablelist class="dbus-property" generated="True" extra-ref="WatchdogPreTimeoutUsec"/>
-
- <variablelist class="dbus-property" generated="True" extra-ref="WatchdogLastPingTimestamp"/>
-
- <variablelist class="dbus-property" generated="True" extra-ref="WatchdogLastPingTimestampMonotonic"/>
-
<!--End of Autogenerated section-->
<refsect2>
for more information.</para>
<para><function>SwitchRoot()</function> may be used to transition to a new root directory. This is
- intended to be used by initial RAM disks. The method takes two arguments: the new root directory (which
- needs to be specified) and an init binary path (which may be left empty, in which case it is
- automatically searched for). The state of the system manager will be serialized before the
- transition. After the transition, the manager binary on the main system is invoked and replaces the old
- PID 1. All state will then be deserialized.</para>
+ intended to be used in the initrd, and also to transition from the host system into a shutdown initrd.
+ The method takes two arguments: the new root directory (which needs to be specified) and an init binary
+ path (which may be left empty, in which case it is automatically searched for). The state of the system
+ manager will be serialized before the transition. After the transition, the manager binary on the main
+ system is invoked and replaces the old PID 1. All state will then be deserialized.</para>
<para><function>SetEnvironment()</function> may be used to alter the environment block that is passed
to all spawned processes. It takes a string array of environment variable assignments. Any previously set
Produces output similar to
<programlisting>
NEXT LEFT LAST PASSED UNIT ACTIVATES
-n/a n/a Thu 2017-02-23 13:40:29 EST 3 days ago ureadahead-stop.timer ureadahead-stop.service
+- - Thu 2017-02-23 13:40:29 EST 3 days ago ureadahead-stop.timer ureadahead-stop.service
Sun 2017-02-26 18:55:42 EST 1min 14s left Thu 2017-02-23 13:54:44 EST 3 days ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Sun 2017-02-26 20:37:16 EST 1h 42min left Sun 2017-02-26 11:56:36 EST 6h ago apt-daily.timer apt-daily.service
Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago snapd.refresh.timer snapd.refresh.service
the new target, possibly including the graphical environment or terminal you are currently using.
</para>
- <para>Note that this is allowed only on units where
+ <para>Note that this operation is allowed only on units where
<option>AllowIsolate=</option> is enabled. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para>
<term><command>switch-root</command> <replaceable>ROOT</replaceable> <optional><replaceable>INIT</replaceable></optional></term>
<listitem>
- <para>Switches to a different root directory and executes a new system manager process below it. This is
- intended for usage in initial RAM disks ("initrd"), and will transition from the initrd's system manager
- process (a.k.a. "init" process) to the main system manager process which is loaded from the actual host
- volume. This call takes two arguments: the directory that is to become the new root directory, and the path
- to the new system manager binary below it to execute as PID 1. If the latter is omitted or the empty
- string, a systemd binary will automatically be searched for and used as init. If the system manager path is
- omitted, equal to the empty string or identical to the path to the systemd binary, the state of the
- initrd's system manager process is passed to the main system manager, which allows later introspection of
- the state of the services involved in the initrd boot phase.</para>
+ <para>Switches to a different root directory and executes a new system manager process below it.
+ This is intended for use in the initrd, and will transition from the initrd's system manager
+ process (a.k.a. "init" process) to the main system manager process which is loaded from the
+ actual host root files system. This call takes two arguments: the directory that is to become the
+ new root directory, and the path to the new system manager binary below it to execute as PID 1.
+ If the latter is omitted or the empty string, a systemd binary will automatically be searched for
+ and used as init. If the system manager path is omitted, equal to the empty string or identical
+ to the path to the systemd binary, the state of the initrd's system manager process is passed to
+ the main system manager, which allows later introspection of the state of the services involved
+ in the initrd boot phase.</para>
</listitem>
</varlistentry>
if (!pp)
return errno > 0 ? -errno : -EIO;
- if (sd_id128_string_equal(blkid_partition_get_type_string(pp), GPT_USER_HOME) <= 0)
+ if (sd_id128_string_equal(blkid_partition_get_type_string(pp), SD_GPT_USER_HOME) <= 0)
continue;
if (!streq_ptr(blkid_partition_get_name(pp), label))
if (!t)
return log_oom();
- r = fdisk_parttype_set_typestr(t, GPT_USER_HOME_STR);
+ r = fdisk_parttype_set_typestr(t, SD_GPT_USER_HOME_STR);
if (r < 0)
return log_error_errno(r, "Failed to initialize partition type: %m");
log_info("Setting up LUKS device %s completed.", setup->dm_node);
- r = make_filesystem(setup->dm_node, fstype, user_record_user_name_and_realm(h), fs_uuid, user_record_luks_discard(h));
+ r = make_filesystem(setup->dm_node, fstype, user_record_user_name_and_realm(h), NULL, fs_uuid, user_record_luks_discard(h));
if (r < 0)
return r;
#include "process-util.h"
#include "random-util.h"
#include "resize-fs.h"
+#include "rm-rf.h"
#include "sort-util.h"
#include "specifier.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "sync-util.h"
+#include "tmpfile-util.h"
#include "terminal-util.h"
#include "tpm-pcr.h"
#include "tpm2-util.h"
return 0;
}
-static int do_copy_files(Partition *p, const char *fs) {
+static int do_copy_files(Partition *p, const char *root) {
int r;
assert(p);
- assert(fs);
+ assert(root);
STRV_FOREACH_PAIR(source, target, p->copy_files) {
_cleanup_close_ int sfd = -1, pfd = -1, tfd = -1;
return log_error_errno(r, "Failed to check type of source file '%s': %m", *source);
/* We are looking at a directory */
- tfd = chase_symlinks_and_open(*target, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
+ tfd = chase_symlinks_and_open(*target, root, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
if (tfd < 0) {
_cleanup_free_ char *dn = NULL, *fn = NULL;
if (r < 0)
return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
- r = mkdir_p_root(fs, dn, UID_INVALID, GID_INVALID, 0755);
+ r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create parent directory '%s': %m", dn);
- pfd = chase_symlinks_and_open(dn, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
+ pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
if (pfd < 0)
return log_error_errno(pfd, "Failed to open parent directory of target: %m");
if (r < 0)
return log_error_errno(r, "Failed to extract directory from '%s': %m", *target);
- r = mkdir_p_root(fs, dn, UID_INVALID, GID_INVALID, 0755);
+ r = mkdir_p_root(root, dn, UID_INVALID, GID_INVALID, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create parent directory: %m");
- pfd = chase_symlinks_and_open(dn, fs, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
+ pfd = chase_symlinks_and_open(dn, root, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_DIRECTORY|O_CLOEXEC, NULL);
if (pfd < 0)
return log_error_errno(pfd, "Failed to open parent directory of target: %m");
return 0;
}
-static int do_make_directories(Partition *p, const char *fs) {
+static int do_make_directories(Partition *p, const char *root) {
int r;
assert(p);
- assert(fs);
+ assert(root);
STRV_FOREACH(d, p->make_directories) {
- r = mkdir_p_root(fs, *d, UID_INVALID, GID_INVALID, 0755);
+ r = mkdir_p_root(root, *d, UID_INVALID, GID_INVALID, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
}
return 0;
}
-static int partition_populate(Partition *p, const char *node) {
+static int partition_populate_directory(Partition *p, char **ret_root, char **ret_tmp_root) {
+ _cleanup_(rm_rf_physical_and_freep) char *root = NULL;
+ int r;
+
+ assert(ret_root);
+ assert(ret_tmp_root);
+
+ /* When generating squashfs, we need the source tree to be available when we generate the squashfs
+ * filesystem. Because we might have multiple source trees, we build a temporary source tree
+ * beforehand where we merge all our inputs. We then use this merged source tree to create the
+ * squashfs filesystem. */
+
+ if (!streq(p->format, "squashfs")) {
+ *ret_root = NULL;
+ *ret_tmp_root = NULL;
+ return 0;
+ }
+
+ /* If we only have a single directory that's meant to become the root directory of the filesystem,
+ * we can shortcut this function and just use that directory as the root directory instead. If we
+ * allocate a temporary directory, it's stored in "ret_tmp_root" to indicate it should be removed.
+ * Otherwise, we return the directory to use in "root" to indicate it should not be removed. */
+
+ if (strv_length(p->copy_files) == 2 && strv_length(p->make_directories) == 0 && streq(p->copy_files[1], "/")) {
+ _cleanup_free_ char *s = NULL;
+
+ s = strdup(p->copy_files[0]);
+ if (!s)
+ return log_oom();
+
+ *ret_root = TAKE_PTR(s);
+ *ret_tmp_root = NULL;
+ return 0;
+ }
+
+ r = mkdtemp_malloc("/var/tmp/repart-XXXXXX", &root);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create temporary directory: %m");
+
+ r = do_copy_files(p, root);
+ if (r < 0)
+ return r;
+
+ r = do_make_directories(p, root);
+ if (r < 0)
+ return r;
+
+ *ret_root = NULL;
+ *ret_tmp_root = TAKE_PTR(root);
+ return 0;
+}
+
+static int partition_populate_filesystem(Partition *p, const char *node) {
int r;
assert(p);
assert(node);
+ if (streq(p->format, "squashfs"))
+ return 0;
+
if (strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
return 0;
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- _cleanup_free_ char *encrypted = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *tmp_root = NULL;
+ _cleanup_free_ char *encrypted = NULL, *root = NULL;
_cleanup_close_ int encrypted_dev_fd = -1;
const char *fsdev;
sd_id128_t fs_uuid;
if (r < 0)
return r;
- r = make_filesystem(fsdev, p->format, strempty(p->new_label), fs_uuid, arg_discard);
+ /* Ideally, we populate filesystems using our own code after creating the filesystem to
+ * ensure consistent handling of chattrs, xattrs and other similar things. However, when
+ * using squashfs, we can't populate after creating the filesystem because it's read-only, so
+ * instead we create a temporary root to use as the source tree when generating the squashfs
+ * filesystem. */
+ r = partition_populate_directory(p, &root, &tmp_root);
+ if (r < 0)
+ return r;
+
+ r = make_filesystem(fsdev, p->format, strempty(p->new_label), root ?: tmp_root, fs_uuid, arg_discard);
if (r < 0) {
encrypted_dev_fd = safe_close(encrypted_dev_fd);
(void) deactivate_luks(cd, encrypted);
if (flock(encrypted_dev_fd, LOCK_UN) < 0)
return log_error_errno(errno, "Failed to unlock LUKS device: %m");
- r = partition_populate(p, fsdev);
+ /* Now, we can populate all the other filesystems that aren't squashfs. */
+ r = partition_populate_filesystem(p, fsdev);
if (r < 0) {
encrypted_dev_fd = safe_close(encrypted_dev_fd);
(void) deactivate_luks(cd, encrypted);
if (p->no_auto >= 0) {
if (gpt_partition_type_knows_no_auto(p->type_uuid))
- SET_FLAG(f, GPT_FLAG_NO_AUTO, p->no_auto);
+ SET_FLAG(f, SD_GPT_FLAG_NO_AUTO, p->no_auto);
else {
char buffer[SD_ID128_UUID_STRING_MAX];
log_warning("Configured NoAuto=%s for partition type '%s' that doesn't support it, ignoring.",
if (p->read_only >= 0) {
if (gpt_partition_type_knows_read_only(p->type_uuid))
- SET_FLAG(f, GPT_FLAG_READ_ONLY, p->read_only);
+ SET_FLAG(f, SD_GPT_FLAG_READ_ONLY, p->read_only);
else {
char buffer[SD_ID128_UUID_STRING_MAX];
log_warning("Configured ReadOnly=%s for partition type '%s' that doesn't support it, ignoring.",
if (p->growfs >= 0) {
if (gpt_partition_type_knows_growfs(p->type_uuid))
- SET_FLAG(f, GPT_FLAG_GROWFS, p->growfs);
+ SET_FLAG(f, SD_GPT_FLAG_GROWFS, p->growfs);
else {
char buffer[SD_ID128_UUID_STRING_MAX];
log_warning("Configured GrowFileSystem=%s for partition type '%s' that doesn't support it, ignoring.",
try1 = "/";
else if (gpt_partition_type_is_usr_verity(type_uuid))
try1 = "/usr/";
- else if (sd_id128_equal(type_uuid, GPT_ESP)) {
+ else if (sd_id128_equal(type_uuid, SD_GPT_ESP)) {
try1 = "/efi/";
try2 = "/boot/";
- } else if (sd_id128_equal(type_uuid, GPT_XBOOTLDR))
+ } else if (sd_id128_equal(type_uuid, SD_GPT_XBOOTLDR))
try1 = "/boot/";
else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
static int run(int argc, char *argv[]) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
- _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
_cleanup_(context_freep) Context* context = NULL;
_cleanup_free_ char *node = NULL;
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_REQUIRE_ROOT,
&mounted_dir,
- &loop_device,
- &decrypted_image);
+ &loop_device);
if (r < 0)
return r;
assert(node);
/* Mask away all flags supported by this partition's type and the three flags the UEFI spec defines generically */
- pflags &= ~(supported | GPT_FLAG_REQUIRED_PARTITION | GPT_FLAG_NO_BLOCK_IO_PROTOCOL | GPT_FLAG_LEGACY_BIOS_BOOTABLE);
+ pflags &= ~(supported |
+ SD_GPT_FLAG_REQUIRED_PARTITION |
+ SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL |
+ SD_GPT_FLAG_LEGACY_BIOS_BOOTABLE);
if (pflags == 0)
return;
}
#endif
+#if HAVE_BLKID
+static int dissected_image_new(const char *path, DissectedImage **ret) {
+ _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+ _cleanup_free_ char *name = NULL;
+ int r;
+
+ assert(ret);
+
+ if (path) {
+ _cleanup_free_ char *filename = NULL;
+
+ r = path_extract_filename(path, &filename);
+ if (r < 0)
+ return r;
+
+ r = raw_strip_suffixes(filename, &name);
+ if (r < 0)
+ return r;
+
+ if (!image_name_is_valid(name)) {
+ log_debug("Image name %s is not valid, ignoring.", strna(name));
+ name = mfree(name);
+ }
+ }
+
+ m = new(DissectedImage, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (DissectedImage) {
+ .has_init_system = -1,
+ .image_name = TAKE_PTR(name),
+ };
+
+ for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
+ m->partitions[i] = DISSECTED_PARTITION_NULL;
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+#endif
+
static void dissected_partition_done(DissectedPartition *p) {
assert(p);
free(p->decrypted_node);
free(p->mount_options);
- *p = (DissectedPartition) {
- .partno = -1,
- .architecture = _ARCHITECTURE_INVALID,
- };
+ *p = DISSECTED_PARTITION_NULL;
}
#if HAVE_BLKID
if (r != 0)
return errno_or_else(EIO);
- m = new(DissectedImage, 1);
- if (!m)
- return -ENOMEM;
-
- *m = (DissectedImage) {
- .has_init_system = -1,
- };
-
- if (image_path) {
- _cleanup_free_ char *extracted_filename = NULL, *name_stripped = NULL;
-
- r = path_extract_filename(image_path, &extracted_filename);
- if (r < 0)
- return r;
-
- r = raw_strip_suffixes(extracted_filename, &name_stripped);
- if (r < 0)
- return r;
-
- if (!image_name_is_valid(name_stripped))
- log_debug("Image name %s is not valid, ignoring.", strna(name_stripped));
- else
- m->image_name = TAKE_PTR(name_stripped);
- }
+ r = dissected_image_new(image_path, &m);
+ if (r < 0)
+ return r;
if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
label = blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
- if (sd_id128_equal(type_id, GPT_HOME)) {
+ if (sd_id128_equal(type_id, SD_GPT_HOME)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_HOME;
- rw = !(pflags & GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
- } else if (sd_id128_equal(type_id, GPT_SRV)) {
+ } else if (sd_id128_equal(type_id, SD_GPT_SRV)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_SRV;
- rw = !(pflags & GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
- } else if (sd_id128_equal(type_id, GPT_ESP)) {
+ } else if (sd_id128_equal(type_id, SD_GPT_ESP)) {
- /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is
- * not defined there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as
+ /* Note that we don't check the SD_GPT_FLAG_NO_AUTO flag for the ESP, as it is
+ * not defined there. We instead check the SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as
* recommended by the UEFI spec (See "12.3.3 Number and Location of System
* Partitions"). */
- if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
+ if (pflags & SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
continue;
designator = PARTITION_ESP;
fstype = "vfat";
- } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) {
+ } else if (sd_id128_equal(type_id, SD_GPT_XBOOTLDR)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_XBOOTLDR;
- rw = !(pflags & GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
} else if (gpt_partition_type_is_root(type_id)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
/* If a root ID is specified, ignore everything but the root id */
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
designator = PARTITION_ROOT_OF_ARCH(architecture);
- rw = !(pflags & GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
} else if (gpt_partition_type_is_root_verity(type_id)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
m->has_verity = true;
} else if (gpt_partition_type_is_root_verity_sig(type_id)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
m->has_verity_sig = true;
} else if (gpt_partition_type_is_usr(type_id)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
/* If a usr ID is specified, ignore everything but the usr id */
assert_se((architecture = gpt_partition_type_uuid_to_arch(type_id)) >= 0);
designator = PARTITION_USR_OF_ARCH(architecture);
- rw = !(pflags & GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
} else if (gpt_partition_type_is_usr_verity(type_id)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
m->has_verity = true;
} else if (gpt_partition_type_is_usr_verity_sig(type_id)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
m->has_verity_sig = true;
fstype = "verity_hash_signature";
rw = false;
- } else if (sd_id128_equal(type_id, GPT_SWAP)) {
+ } else if (sd_id128_equal(type_id, SD_GPT_SWAP)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO);
+ check_partition_flags(node, pflags, SD_GPT_FLAG_NO_AUTO);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_SWAP;
- } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
+ } else if (sd_id128_equal(type_id, SD_GPT_LINUX_GENERIC)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
if (generic_node)
multiple_generic = true;
else {
generic_nr = nr;
- generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
- generic_growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ generic_rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ generic_growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
generic_uuid = id;
generic_node = strdup(node);
if (!generic_node)
return -ENOMEM;
}
- } else if (sd_id128_equal(type_id, GPT_TMP)) {
+ } else if (sd_id128_equal(type_id, SD_GPT_TMP)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_TMP;
- rw = !(pflags & GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
- } else if (sd_id128_equal(type_id, GPT_VAR)) {
+ } else if (sd_id128_equal(type_id, SD_GPT_VAR)) {
- check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
+ check_partition_flags(node, pflags,
+ SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
- if (pflags & GPT_FLAG_NO_AUTO)
+ if (pflags & SD_GPT_FLAG_NO_AUTO)
continue;
if (!FLAGS_SET(flags, DISSECT_IMAGE_RELAX_VAR_CHECK)) {
* /etc/machine-id we can securely bind the partition to the
* installation. */
- r = sd_id128_get_machine_app_specific(GPT_VAR, &var_uuid);
+ r = sd_id128_get_machine_app_specific(SD_GPT_VAR, &var_uuid);
if (r < 0)
return r;
}
designator = PARTITION_VAR;
- rw = !(pflags & GPT_FLAG_READ_ONLY);
- growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
+ rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
+ growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
}
if (designator != _PARTITION_DESIGNATOR_INVALID) {
if (!m)
return NULL;
+ /* First, clear dissected partitions. */
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
dissected_partition_done(m->partitions + i);
+ /* Second, free decrypted images. This must be after dissected_partition_done(), as freeing
+ * DecryptedImage may try to deactivate partitions. */
+ decrypted_image_unref(m->decrypted_image);
+
+ /* Third, unref LoopDevice. This must be called after the above two, as freeing LoopDevice may try to
+ * remove existing partitions on the loopback block device. */
+ loop_device_unref(m->loop);
+
free(m->image_name);
free(m->hostname);
strv_free(m->machine_info);
}
#if HAVE_LIBCRYPTSETUP
-typedef struct DecryptedPartition {
+struct DecryptedPartition {
struct crypt_device *device;
char *name;
bool relinquished;
-} DecryptedPartition;
+};
+#endif
+
+typedef struct DecryptedPartition DecryptedPartition;
struct DecryptedImage {
+ unsigned n_ref;
DecryptedPartition *decrypted;
size_t n_decrypted;
};
-#endif
-DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
+static DecryptedImage* decrypted_image_free(DecryptedImage *d) {
#if HAVE_LIBCRYPTSETUP
int r;
DecryptedPartition *p = d->decrypted + i;
if (p->device && p->name && !p->relinquished) {
- r = sym_crypt_deactivate_by_name(p->device, p->name, 0);
+ /* Let's deactivate lazily, as the dm volume may be already/still used by other processes. */
+ r = sym_crypt_deactivate_by_name(p->device, p->name, CRYPT_DEACTIVATE_DEFERRED);
if (r < 0)
log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
}
return NULL;
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(DecryptedImage, decrypted_image, decrypted_image_free);
+
#if HAVE_LIBCRYPTSETUP
+static int decrypted_image_new(DecryptedImage **ret) {
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
+
+ assert(ret);
+
+ d = new(DecryptedImage, 1);
+ if (!d)
+ return -ENOMEM;
+
+ *d = (DecryptedImage) {
+ .n_ref = 1,
+ };
+
+ *ret = TAKE_PTR(d);
+ return 0;
+}
static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
_cleanup_free_ char *name = NULL, *node = NULL;
CRYPT_ACTIVATE_READONLY);
}
+static usec_t verity_timeout(void) {
+ usec_t t = 100 * USEC_PER_MSEC;
+ const char *e;
+ int r;
+
+ /* On slower machines, like non-KVM vm, setting up device may take a long time.
+ * Let's make the timeout configurable. */
+
+ e = getenv("SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC");
+ if (!e)
+ return t;
+
+ r = parse_sec(e, &t);
+ if (r < 0)
+ log_debug_errno(r,
+ "Failed to parse timeout specified in $SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC, "
+ "using the default timeout (%s).",
+ FORMAT_TIMESPAN(t, USEC_PER_MSEC));
+
+ return t;
+}
+
static int verity_partition(
PartitionDesignator designator,
DissectedPartition *m,
* Improvements in libcrypsetup can ensure this never happens:
* https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
- return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
- if (!IN_SET(r,
- 0, /* Success */
- -EEXIST, /* Volume is already open and ready to be used */
- -EBUSY, /* Volume is being opened but not ready, crypt_init_by_name can fetch details */
- -ENODEV /* Volume is being opened but not ready, crypt_init_by_name would fail, try to open again */))
+ break;
+ if (r < 0 && !IN_SET(r,
+ -EEXIST, /* Volume is already open and ready to be used */
+ -EBUSY, /* Volume is being opened but not ready, crypt_init_by_name can fetch details */
+ -ENODEV /* Volume is being opened but not ready, crypt_init_by_name would fail, try to open again */))
return r;
if (IN_SET(r, -EEXIST, -EBUSY)) {
- struct crypt_device *existing_cd = NULL;
+ _cleanup_(sym_crypt_freep) struct crypt_device *existing_cd = NULL;
if (!restore_deferred_remove){
/* To avoid races, disable automatic removal on umount while setting up the new device. Restore it on failure. */
/* If activation returns EBUSY there might be no deferred removal to cancel, that's fine */
if (r < 0 && r != -ENXIO)
return log_debug_errno(r, "Disabling automated deferred removal for verity device %s failed: %m", node);
- if (r == 0) {
+ if (r >= 0) {
restore_deferred_remove = strdup(name);
if (!restore_deferred_remove)
return -ENOMEM;
r = verity_can_reuse(verity, name, &existing_cd);
/* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */
if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
- return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
- if (!IN_SET(r, 0, -ENODEV, -ENOENT, -EBUSY))
+ break;
+ if (r < 0 && !IN_SET(r, -ENODEV, -ENOENT, -EBUSY))
return log_debug_errno(r, "Checking whether existing verity device %s can be reused failed: %m", node);
- if (r == 0) {
- usec_t timeout_usec = 100 * USEC_PER_MSEC;
- const char *e;
-
- /* On slower machines, like non-KVM vm, setting up device may take a long time.
- * Let's make the timeout configurable. */
- e = getenv("SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC");
- if (e) {
- usec_t t;
-
- r = parse_sec(e, &t);
- if (r < 0)
- log_debug_errno(r,
- "Failed to parse timeout specified in $SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC, "
- "using the default timeout (%s).",
- FORMAT_TIMESPAN(timeout_usec, USEC_PER_MSEC));
- else
- timeout_usec = t;
- }
-
+ if (r >= 0) {
/* devmapper might say that the device exists, but the devlink might not yet have been
* created. Check and wait for the udev event in that case. */
- r = device_wait_for_devlink(node, "block", timeout_usec, NULL);
+ r = device_wait_for_devlink(node, "block", verity_timeout(), NULL);
/* Fallback to activation with a unique device if it's taking too long */
- if (r == -ETIMEDOUT)
+ if (r == -ETIMEDOUT && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
break;
if (r < 0)
return r;
- if (cd)
- sym_crypt_free(cd);
- cd = existing_cd;
+ crypt_free_and_replace(cd, existing_cd);
}
}
- if (r == 0)
- break;
+ if (r >= 0)
+ goto success;
/* Device is being opened by another process, but it has not finished yet, yield for 2ms */
(void) usleep(2 * USEC_PER_MSEC);
}
- /* An existing verity device was reported by libcryptsetup/libdevmapper, but we can't use it at this time.
- * Fall back to activating it with a unique device name. */
- if (r != 0 && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
+ /* All trials failed or a conflicting verity device exists. Let's try to activate with a unique name. */
+ if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
+ /* Before trying to activate with unique name, we need to free crypt_device object.
+ * Otherwise, we get error from libcryptsetup like the following:
+ * ------
+ * systemd[1234]: Cannot use device /dev/loop5 which is in use (already mapped or mounted).
+ * ------
+ */
+ sym_crypt_free(cd);
+ cd = NULL;
return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "All attempts to activate verity device %s failed.", name);
+success:
/* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */
restore_deferred_remove = mfree(restore_deferred_remove);
DissectedImage *m,
const char *passphrase,
const VeritySettings *verity,
- DissectImageFlags flags,
- DecryptedImage **ret) {
+ DissectImageFlags flags) {
#if HAVE_LIBCRYPTSETUP
_cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
if (verity && verity->root_hash && verity->root_hash_size < sizeof(sd_id128_t))
return -EINVAL;
- if (!m->encrypted && !m->verity_ready) {
- *ret = NULL;
+ if (!m->encrypted && !m->verity_ready)
return 0;
- }
#if HAVE_LIBCRYPTSETUP
- d = new0(DecryptedImage, 1);
- if (!d)
- return -ENOMEM;
+ r = decrypted_image_new(&d);
+ if (r < 0)
+ return r;
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
DissectedPartition *p = m->partitions + i;
}
}
- *ret = TAKE_PTR(d);
+ m->decrypted_image = TAKE_PTR(d);
return 1;
#else
DissectedImage *m,
const char *passphrase,
const VeritySettings *verity,
- DissectImageFlags flags,
- DecryptedImage **ret) {
+ DissectImageFlags flags) {
_cleanup_strv_free_erase_ char **z = NULL;
int n = 3, r;
n--;
for (;;) {
- r = dissected_image_decrypt(m, passphrase, verity, flags, ret);
+ r = dissected_image_decrypt(m, passphrase, verity, flags);
if (r >= 0)
return r;
if (r == -EKEYREJECTED)
}
}
-int decrypted_image_relinquish(DecryptedImage *d) {
+static int decrypted_image_relinquish(DecryptedImage *d) {
assert(d);
/* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a
return 0;
}
+int dissected_image_relinquish(DissectedImage *m) {
+ int r;
+
+ assert(m);
+
+ if (m->decrypted_image) {
+ r = decrypted_image_relinquish(m->decrypted_image);
+ if (r < 0)
+ return r;
+ }
+
+ if (m->loop)
+ loop_device_relinquish(m->loop);
+
+ return 0;
+}
+
static char *build_auxiliary_path(const char *image, const char *suffix) {
const char *e;
char *n;
return r;
}
+int dissect_loop_device(
+ LoopDevice *loop,
+ const VeritySettings *verity,
+ const MountOptions *mount_options,
+ DissectImageFlags flags,
+ DissectedImage **ret) {
+
+ _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
+ int r;
+
+ assert(loop);
+ assert(ret);
+
+ r = dissect_image(loop->fd, loop->node, loop->backing_file ?: loop->node, verity, mount_options, flags, &m);
+ if (r < 0)
+ return r;
+
+ m->loop = loop_device_ref(loop);
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
int dissect_loop_device_and_warn(
- const LoopDevice *loop,
+ LoopDevice *loop,
const VeritySettings *verity,
const MountOptions *mount_options,
DissectImageFlags flags,
const char *image,
DissectImageFlags flags,
char **ret_directory,
- LoopDevice **ret_loop_device,
- DecryptedImage **ret_decrypted_image) {
+ LoopDevice **ret_loop_device) {
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
- _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_(rmdir_and_freep) char *created_dir = NULL;
_cleanup_free_ char *temp = NULL;
assert(image);
assert(ret_directory);
assert(ret_loop_device);
- assert(ret_decrypted_image);
r = verity_settings_load(&verity, image, NULL, NULL);
if (r < 0)
if (r < 0)
return r;
- r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags, &decrypted_image);
+ r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags);
if (r < 0)
return r;
if (r < 0)
return r;
- if (decrypted_image) {
- r = decrypted_image_relinquish(decrypted_image);
- if (r < 0)
- return log_error_errno(r, "Failed to relinquish DM devices: %m");
- }
-
- loop_device_relinquish(d);
+ r = dissected_image_relinquish(dissected_image);
+ if (r < 0)
+ return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
*ret_directory = TAKE_PTR(created_dir);
*ret_loop_device = TAKE_PTR(d);
- *ret_decrypted_image = TAKE_PTR(decrypted_image);
return 0;
}
const char *required_sysext_scope) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
- _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
DissectImageFlags dissect_image_flags;
dissected_image,
NULL,
&verity,
- dissect_image_flags,
- &decrypted_image);
+ dissect_image_flags);
if (r < 0)
return log_debug_errno(r, "Failed to decrypt dissected image: %m");
return log_debug_errno(r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", dissected_image->image_name);
}
- if (decrypted_image) {
- r = decrypted_image_relinquish(decrypted_image);
- if (r < 0)
- return log_debug_errno(r, "Failed to relinquish decrypted image: %m");
- }
-
- loop_device_relinquish(loop_device);
+ r = dissected_image_relinquish(dissected_image);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to relinquish dissected image: %m");
return 0;
}
#include "alloc-util.h"
#include "blkid-util.h"
+#include "btrfs-util.h"
#include "chase-symlinks.h"
#include "device-util.h"
#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
+#include "fd-util.h"
#include "find-esp.h"
#include "gpt.h"
#include "parse-util.h"
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
- if (sd_id128_string_equal(v, GPT_ESP) <= 0)
+ if (sd_id128_string_equal(v, SD_GPT_ESP) <= 0)
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
- if (sd_id128_string_equal(v, GPT_ESP) <= 0)
+ if (sd_id128_string_equal(v, SD_GPT_ESP) <= 0)
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
bool unprivileged_mode,
dev_t *ret_dev) {
- struct stat st, st2;
- const char *t2, *trigger;
+ _cleanup_close_ int fd = -1;
+ STRUCT_NEW_STATX_DEFINE(sxa);
+ STRUCT_NEW_STATX_DEFINE(sxb);
int r;
+ /* Checks if the specified directory is at the root of its file system, and returns device
+ * major/minor of the device, if it is. */
+
assert(path);
- assert(ret_dev);
+
+ /* We are using O_PATH here, since that way we can operate on directory inodes we cannot look into,
+ * which is quite likely if we run unprivileged */
+ fd = open(path, O_CLOEXEC|O_DIRECTORY|O_PATH);
+ if (fd < 0)
+ return log_full_errno((searching && errno == ENOENT) ||
+ (unprivileged_mode && ERRNO_IS_PRIVILEGE(errno)) ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to open directory \"%s\": %m", path);
/* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
* directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
* before stat()ing */
- trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
- (void) access(trigger, F_OK);
+ (void) faccessat(fd, "trigger", F_OK, AT_SYMLINK_NOFOLLOW); /* Filename doesn't matter... */
- if (stat(path, &st) < 0)
- return log_full_errno((searching && errno == ENOENT) ||
- (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
+ r = statx_fallback(fd, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxa.sx);
+ if (r < 0)
+ return log_full_errno((unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, r,
"Failed to determine block device node of \"%s\": %m", path);
- if (major(st.st_dev) == 0)
- return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
- SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
- "Block device node of \"%s\" is invalid.", path);
-
- if (path_equal(path, "/")) {
- /* Let's assume that the root directory of the OS is always the root of its file system
- * (which technically doesn't have to be the case, but it's close enough, and it's not easy
- * to be fully correct for it, since we can't look further up than the root dir easily.) */
- if (ret_dev)
- *ret_dev = st.st_dev;
-
- return 0;
- }
+ assert(S_ISDIR(sxa.sx.stx_mode)); /* We used O_DIRECTORY above, when opening, so this must hold */
- t2 = strjoina(path, "/..");
- if (stat(t2, &st2) < 0) {
- if (errno != EACCES)
- r = -errno;
- else {
- _cleanup_free_ char *parent = NULL;
+ if (FLAGS_SET(sxa.sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) {
- /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
- * directly instead. It's not as good, due to symlinks and such, but we can't do
- * anything better here. */
+ /* If we have STATX_ATTR_MOUNT_ROOT, we are happy, that's all we need. We operate under the
+ * assumption that a top of a mount point is also the top of the file system. (Which of
+ * course is strictly speaking not always true...) */
- r = path_extract_directory(path, &parent);
- if (r < 0)
- return log_error_errno(r, "Failed to extract parent path from '%s': %m", path);
+ if (!FLAGS_SET(sxa.sx.stx_attributes, STATX_ATTR_MOUNT_ROOT))
+ return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+ SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+ "Directory \"%s\" is not the root of the file system.", path);
- r = RET_NERRNO(stat(parent, &st2));
- }
+ goto success;
+ }
+ /* Now let's look at the parent */
+ r = statx_fallback(fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxb.sx);
+ if (r < 0 && ERRNO_IS_PRIVILEGE(r)) {
+ _cleanup_free_ char *parent = NULL;
+
+ /* If going via ".." didn't work due to EACCESS, then let's determine the parent path
+ * directly instead. It's not as good, due to symlinks and such, but we can't do anything
+ * better here.
+ *
+ * (In case you wonder where this fallback is useful: consider a classic Fedora setup with
+ * /boot/ being an ext4 partition and /boot/efi/ being the VFAT ESP. The latter is mounted
+ * inaccessible for regular users via the dmask= mount option. In that case as unprivileged
+ * user we can stat() /boot/efi/, and we can stat()/enumerate /boot/. But we cannot look into
+ * /boot/efi/, and in particular not use /boot/efi/../ – hence this work-around.) */
+
+ if (path_equal(path, "/"))
+ goto success;
+
+ r = path_extract_directory(path, &parent);
if (r < 0)
- return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
- "Failed to determine block device node of parent of \"%s\": %m", path);
+ return log_error_errno(r, "Failed to extract parent path from '%s': %m", path);
+
+ r = statx_fallback(AT_FDCWD, parent, AT_SYMLINK_NOFOLLOW, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxb.sx);
}
+ if (r < 0)
+ return log_full_errno(unprivileged_mode && ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_ERR, r,
+ "Failed to determine block device node of parent of \"%s\": %m", path);
+
+ if (statx_inode_same(&sxa.sx, &sxb.sx)) /* for the root dir inode nr for both inodes will be the same */
+ goto success;
- if (st.st_dev == st2.st_dev)
+ if (statx_mount_same(&sxa.nsx, &sxb.nsx))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"Directory \"%s\" is not the root of the file system.", path);
- if (ret_dev)
- *ret_dev = st.st_dev;
+success:
+ if (!ret_dev)
+ return 0;
+
+ if (sxa.sx.stx_dev_major == 0) { /* Hmm, maybe a btrfs device, and the caller asked for the backing device? Then let's try to get it. */
+ _cleanup_close_ int real_fd = -1;
+
+ /* The statx() above we can execute on an O_PATH fd. But the btrfs ioctl we cannot. Hence
+ * acquire a "real" fd first, without the O_PATH flag. */
+
+ real_fd = fd_reopen(fd, O_DIRECTORY|O_CLOEXEC);
+ if (real_fd < 0)
+ return real_fd;
+ return btrfs_get_block_device_fd(real_fd, ret_dev);
+ }
+
+ *ret_dev = makedev(sxa.sx.stx_dev_major, sxa.sx.stx_dev_minor);
return 0;
}
bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
- dev_t devid;
+ dev_t devid = 0;
int r;
assert(p);
* -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
*/
- relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0 || FLAGS_SET(flags, VERIFY_ESP_RELAX_CHECKS);
+ relax_checks =
+ getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0 ||
+ FLAGS_SET(flags, VERIFY_ESP_RELAX_CHECKS);
/* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
* issues. Let's also, silence the error messages. */
"File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
}
- r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
+ relax_checks =
+ relax_checks ||
+ detect_container() > 0;
+
+ r = verify_fsroot_dir(p, searching, unprivileged_mode, relax_checks ? NULL : &devid);
if (r < 0)
return r;
/* In a container we don't have access to block devices, skip this part of the verification, we trust
* the container manager set everything up correctly on its own. */
- if (detect_container() > 0 || relax_checks)
+ if (relax_checks)
goto finish;
/* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
flags | VERIFY_ESP_SEARCHING);
if (r >= 0)
goto found;
- if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
+ if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL, -ENOTDIR)) /* This one is not it */
return r;
p = mfree(p);
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
- if (sd_id128_string_equal(v, GPT_XBOOTLDR) <= 0)
+ if (sd_id128_string_equal(v, SD_GPT_XBOOTLDR) <= 0)
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"%s: Partitition has wrong PART_ENTRY_TYPE=%s for XBOOTLDR partition.", node, v);
if (r < 0)
return log_device_error_errno(d, r, "Failed to query ID_PART_ENTRY_TYPE: %m");
- r = sd_id128_string_equal(v, GPT_XBOOTLDR);
+ r = sd_id128_string_equal(v, SD_GPT_XBOOTLDR);
if (r < 0)
return log_device_error_errno(d, r, "Failed to parse ID_PART_ENTRY_TYPE=%s: %m", v);
if (r == 0)
dev_t *ret_devid) {
bool relax_checks;
- dev_t devid;
+ dev_t devid = 0;
int r;
assert(p);
- relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
+ relax_checks =
+ getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0 ||
+ detect_container() > 0;
- r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
+ r = verify_fsroot_dir(p, searching, unprivileged_mode, relax_checks ? NULL : &devid);
if (r < 0)
return r;
- if (detect_container() > 0 || relax_checks)
+ if (relax_checks)
goto finish;
if (unprivileged_mode)
root ? " under directory " : "",
strempty(root));
- r = verify_xbootldr(p, true, unprivileged_mode, ret_uuid, ret_devid);
+ r = verify_xbootldr(p, /* searching= */ true, unprivileged_mode, ret_uuid, ret_devid);
if (r >= 0)
goto found;
- if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
+ if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL, -ENOTDIR)) /* This one is not it */
return r;
return -ENOKEY;
'sd-daemon.h',
'sd-device.h',
'sd-event.h',
+ 'sd-gpt.h',
'sd-hwdb.h',
'sd-id128.h',
'sd-journal.h',
'sd-login.h',
'sd-messages.h',
- 'sd-netlink.h',
'sd-path.h',
]
'sd-lldp-tx.h',
'sd-lldp.h',
'sd-ndisc.h',
+ 'sd-netlink.h',
'sd-network.h',
'sd-radv.h',
'sd-resolve.h',
#endif
static bool have_root_gpt_type(void) {
- #ifdef GPT_ROOT_NATIVE
+ #ifdef SD_GPT_ROOT_NATIVE
return true;
#else
return false;
"size=32M, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F\n"
"size=32M, type=", sfdisk);
- #ifdef GPT_ROOT_NATIVE
- fprintf(sfdisk, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(GPT_ROOT_NATIVE));
+ #ifdef SD_GPT_ROOT_NATIVE
+ fprintf(sfdisk, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_GPT_ROOT_NATIVE));
#else
- fprintf(sfdisk, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(GPT_ROOT_X86_64));
+ fprintf(sfdisk, SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_GPT_ROOT_X86_64));
#endif
fputs("\n"
assert_se(dissected->partitions[PARTITION_HOME].node);
assert_se(sd_id128_randomize(&id) >= 0);
- assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", id, true) >= 0);
+ assert_se(make_filesystem(dissected->partitions[PARTITION_ESP].node, "vfat", "EFI", NULL, id, true) >= 0);
assert_se(sd_id128_randomize(&id) >= 0);
- assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", id, true) >= 0);
+ assert_se(make_filesystem(dissected->partitions[PARTITION_XBOOTLDR].node, "vfat", "xbootldr", NULL, id, true) >= 0);
assert_se(sd_id128_randomize(&id) >= 0);
- assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", id, true) >= 0);
+ assert_se(make_filesystem(dissected->partitions[PARTITION_ROOT].node, "ext4", "root", NULL, id, true) >= 0);
assert_se(sd_id128_randomize(&id) >= 0);
- assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", id, true) >= 0);
+ assert_se(make_filesystem(dissected->partitions[PARTITION_HOME].node, "ext4", "home", NULL, id, true) >= 0);
dissected = dissected_image_unref(dissected);
assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0);
static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
- #if defined(GPT_ROOT_NATIVE) && ENABLE_EFI
+ #if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI
_cleanup_free_ char *root_id = NULL, *root_label = NULL;
bool found_esp = false;
if (sd_id128_from_string(stype, &type) < 0)
continue;
- if (sd_id128_equal(type, GPT_ESP)) {
+ if (sd_id128_equal(type, SD_GPT_ESP)) {
sd_id128_t id, esp;
/* We found an ESP, let's see if it matches
if (sd_id128_equal(id, esp))
found_esp = true;
- } else if (sd_id128_equal(type, GPT_ROOT_NATIVE)) {
+ } else if (sd_id128_equal(type, SD_GPT_ROOT_NATIVE)) {
unsigned long long flags;
flags = blkid_partition_get_flags(pp);
- if (flags & GPT_FLAG_NO_AUTO)
+ if (flags & SD_GPT_FLAG_NO_AUTO)
continue;
/* We found a suitable root partition, let's remember the first one, or the one with
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device name: %m");
- fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
log_device_debug_errno(dev, fd, "Failed to open block device %s%s: %m",