]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #24709 from keszybz/partition-table-constants
authorLennart Poettering <lennart@poettering.net>
Wed, 21 Sep 2022 18:07:21 +0000 (20:07 +0200)
committerGitHub <noreply@github.com>
Wed, 21 Sep 2022 18:07:21 +0000 (20:07 +0200)
Expose various GPT UUIDs as public contants and link them up in docs

1  2 
TODO
man/kernel-install.xml
man/org.freedesktop.systemd1.xml
man/systemctl.xml
src/home/homework-luks.c
src/partition/repart.c
src/shared/dissect-image.c
src/shared/find-esp.c
src/systemd/meson.build
src/test/test-loop-block.c
src/udev/udev-builtin-blkid.c

diff --combined TODO
index 3ad402ae3a44a1e4672485fd55c7e3a2744fcd5f,2a9a700568b9be8c51b70ea73fb222d244f73215..d9c7c6df6ac5ed460aaf024789562e54828df535
--- 1/TODO
--- 2/TODO
+++ b/TODO
@@@ -115,8 -115,6 +115,8 @@@ Deprecations and removals
  
  * ~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
diff --combined man/kernel-install.xml
index ce2f901b60ac58ed90267d3a853e24ce4d658493,475dd325a5234f454303eb2be3d20f34e74739f3..b8ea2b16b26e0e08793fa7e859a0a0dc7df63ebb
  
    <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
index 364fb9de03623ba98bf0d7679bd64a4618cbaf77,945c24a3a76719a14a4e50bd5ea0384cc03ef6a9..741a72d2649f7dce255967ed959aa8bcf7eae7d4
@@@ -393,12 -393,6 +393,12 @@@ node /org/freedesktop/systemd1 
        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
diff --combined man/systemctl.xml
index a7cd6398afa8f3832f795a2cc7709b003405f336,4f70cd0c6396d2e0a810f4065146ab92c9530ca1..56bd4b84b93e6dec3becd412fe24b9dfb5e09a74
@@@ -159,7 -159,7 +159,7 @@@ kobject-uevent 1 systemd-udevd-kernel.s
              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
@@@ -487,7 -487,7 +487,7 @@@ Jan 12 10:46:45 example.com bluetoothd[
              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>
  
diff --combined src/home/homework-luks.c
index c57621a6b1c5b9c29609a4fb1c97c4b308eb0510,bf0c2abb8c3dd769cf360ea2605040eea39aca85..0369e285a70289bfe14d65003428bea0fb4c5c81
@@@ -704,7 -704,7 +704,7 @@@ static int luks_validate
                  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))
@@@ -1851,7 -1851,7 +1851,7 @@@ static int make_partition_table
          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");
  
@@@ -2333,7 -2333,7 +2333,7 @@@ int home_create_luks
  
          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;
  
diff --combined src/partition/repart.c
index c35183861f78fec9ea0bf01166c6f7286843119c,301a91d9378f1b61337fd0b99b517134d31c48f3..413d136fe9cc4c380265f34d85c11ff93c0ed395
@@@ -55,7 -55,6 +55,7 @@@
  #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"
@@@ -63,7 -62,6 +63,7 @@@
  #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"
@@@ -3170,11 -3168,11 +3170,11 @@@ static int context_copy_blocks(Context 
          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;
  
@@@ -3397,8 -3340,7 +3397,8 @@@ static int context_mkfs(Context *contex
          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);
@@@ -3823,7 -3755,7 +3823,7 @@@ static uint64_t partition_merge_flags(P
  
          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.",
@@@ -4360,10 -4292,10 +4360,10 @@@ static int resolve_copy_blocks_auto
                  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),
@@@ -5325,6 -5257,7 +5325,6 @@@ static int determine_auto_size(Context 
  
  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;
  
index f9124ba09a0ee461b8e7e2fdd7d75ef0c4efbd19,241b191c876afe3399ddb2ffe0f99859d52c925b..a7eec9fa11d42dfd18c460f5e5d882f77269cbd2
@@@ -134,7 -134,10 +134,10 @@@ static void check_partition_flags
          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
@@@ -323,9 -287,30 +326,9 @@@ int dissect_image
          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) {
@@@ -1130,18 -1127,9 +1145,18 @@@ DissectedImage* dissected_image_unref(D
          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);
@@@ -1571,22 -1559,19 +1586,22 @@@ int dissected_image_mount_and_warn
  }
  
  #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;
@@@ -1926,28 -1892,6 +1941,28 @@@ static int do_crypt_activate_verity
                          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);
  
@@@ -2109,7 -2064,8 +2124,7 @@@ int dissected_image_decrypt
                  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
@@@ -2175,7 -2133,8 +2190,7 @@@ int dissected_image_decrypt_interactive
                  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;
@@@ -2835,31 -2777,8 +2850,31 @@@ finish
          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,
@@@ -2989,10 -2908,12 +3004,10 @@@ int mount_image_privately_interactively
                  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;
  }
@@@ -3102,6 -3029,7 +3117,6 @@@ int verity_dissect_and_mount
                  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;
  }
diff --combined src/shared/find-esp.c
index 22e7f7472d7b13ca65d2fa9f9b95a3e068419c56,889ba8c46b8f9f768bb432bf6e946eae1177b490..dfe0574aba6b16010662ade9fc0842b61320e43f
@@@ -8,13 -8,11 +8,13 @@@
  
  #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"
@@@ -94,7 -92,7 +94,7 @@@ static int verify_esp_blkid
          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);
@@@ -186,7 -184,7 +186,7 @@@ static int verify_esp_udev
          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);
@@@ -237,104 -235,70 +237,104 @@@ static int verify_fsroot_dir
                  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;
  }
  
@@@ -349,7 -313,7 +349,7 @@@ static int verify_esp
  
          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
@@@ -521,7 -479,7 +521,7 @@@ int find_esp_and_warn
                                 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);
@@@ -582,7 -540,7 +582,7 @@@ static int verify_xbootldr_blkid
                  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);
@@@ -646,7 -604,7 +646,7 @@@ static int verify_xbootldr_udev
                  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)
@@@ -696,20 -654,18 +696,20 @@@ static int verify_xbootldr
                  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)
@@@ -801,10 -757,10 +801,10 @@@ int find_xbootldr_and_warn
                                         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;
diff --combined src/systemd/meson.build
index c9e005b38064b10ff222abe9971ca21a8c18fe33,d75097de27aeefe3288ce2f2d89fb3fd7ac3637d..efe1036ce34ab314c6109c34bc02cca313fc0fac
@@@ -7,11 -7,13 +7,12 @@@ _systemd_headers = 
          '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',
  ]
  
@@@ -32,7 -34,6 +33,7 @@@ _not_installed_headers = 
          'sd-lldp-tx.h',
          'sd-lldp.h',
          'sd-ndisc.h',
 +        'sd-netlink.h',
          'sd-network.h',
          'sd-radv.h',
          'sd-resolve.h',
index 83d158f470be4e271c9c1d97d1e43a710f348131,bba8ff91bf62e7a9f437a8dd0611b6e8774227f2..4adae5b786bc351e7fc320555e94e102141e1bba
@@@ -111,7 -111,7 +111,7 @@@ static void* thread_func(void *ptr) 
  #endif
  
  static bool have_root_gpt_type(void) {
- #ifdef GPT_ROOT_NATIVE
+ #ifdef SD_GPT_ROOT_NATIVE
          return true;
  #else
          return false;
@@@ -202,10 -202,10 +202,10 @@@ static int run(int argc, char *argv[]) 
                "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);
index 6de470c71dda1ebd00cba1494affbf19fa7748b0,435cac44ed585ae69e2722ace7614f67acda1e32..92ea43eef0590201d463fe27920916bb4d2f1a05
@@@ -117,7 -117,7 +117,7 @@@ static void print_property(sd_device *d
  
  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
@@@ -310,7 -310,7 +310,7 @@@ static int builtin_blkid(sd_device *dev
          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",