]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/find-esp.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / shared / find-esp.c
index 09c09c8e7e88298e85dbebb3b3d5513f93ebbde3..084dd1c1e2a9483a33d2332cfca58667b327ed56 100644 (file)
@@ -4,22 +4,31 @@
 #include <sys/vfs.h>
 
 #include "sd-device.h"
+#include "sd-id128.h"
 
 #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 "id128-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "stat-util.h"
 #include "string-util.h"
 #include "virt.h"
 
+typedef enum VerifyESPFlags {
+        VERIFY_ESP_SEARCHING         = 1 << 0, /* Downgrade various "not found" logs to debug level */
+        VERIFY_ESP_UNPRIVILEGED_MODE = 1 << 1, /* Call into udev rather than blkid */
+        VERIFY_ESP_RELAX_CHECKS      = 1 << 2, /* Do not validate ESP partition */
+} VerifyESPFlags;
+
 static int verify_esp_blkid(
                 dev_t devid,
                 bool searching,
@@ -38,9 +47,9 @@ static int verify_esp_blkid(
         const char *v;
         int r;
 
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
+        r = devname_from_devnum(S_IFBLK, devid, &node);
         if (r < 0)
-                return log_error_errno(r, "Failed to format major/minor device path: %m");
+                return log_error_errno(r, "Failed to get device path for " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(devid));
 
         errno = 0;
         b = blkid_new_probe_from_filename(node);
@@ -85,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 (id128_equal_string(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);
@@ -144,72 +153,77 @@ static int verify_esp_udev(
                 sd_id128_t *ret_uuid) {
 
         _cleanup_(sd_device_unrefp) sd_device *d = NULL;
-        _cleanup_free_ char *node = NULL;
         sd_id128_t uuid = SD_ID128_NULL;
         uint64_t pstart = 0, psize = 0;
         uint32_t part = 0;
-        const char *v;
+        const char *node, *v;
         int r;
 
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
-        if (r < 0)
-                return log_error_errno(r, "Failed to format major/minor device path: %m");
-
         r = sd_device_new_from_devnum(&d, 'b', devid);
         if (r < 0)
                 return log_error_errno(r, "Failed to get device from device number: %m");
 
+        r = sd_device_get_devname(d, &node);
+        if (r < 0)
+                return log_device_error_errno(d, r, "Failed to get device node: %m");
+
         r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         if (!streq(v, "vfat"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not FAT.", node );
+                return log_device_full_errno(d,
+                                             searching ? LOG_DEBUG : LOG_ERR,
+                                             SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                             "File system \"%s\" is not FAT.", node );
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_full_errno(d,
+                                             searching && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
+                                             searching && r == -ENOENT ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : r,
+                                             "Failed to get device property: %m");
         if (!streq(v, "gpt"))
-                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
-                                      SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
-                                      "File system \"%s\" is not on a GPT partition table.", node);
+                return log_device_full_errno(d,
+                                             searching ? LOG_DEBUG : LOG_ERR,
+                                             SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+                                             "File system \"%s\" is not on a GPT partition table.", 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 (id128_equal_string(v, 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);
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
+        if (sd_id128_string_equal(v, SD_GPT_ESP) <= 0)
+                return log_device_full_errno(d,
+                                             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_UUID", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = sd_id128_from_string(v, &uuid);
         if (r < 0)
-                return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
+                return log_device_error_errno(d, r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = safe_atou32(v, &part);
         if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
+                return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_NUMBER field.");
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = safe_atou64(v, &pstart);
         if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
+                return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_OFFSET field.");
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
         if (r < 0)
-                return log_error_errno(r, "Failed to get device property: %m");
+                return log_device_error_errno(d, r, "Failed to get device property: %m");
         r = safe_atou64(v, &psize);
         if (r < 0)
-                return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
+                return log_device_error_errno(d, r, "Failed to parse PART_ENTRY_SIZE field.");
 
         if (ret_part)
                 *ret_part = part;
@@ -229,85 +243,119 @@ static int verify_fsroot_dir(
                 bool unprivileged_mode,
                 dev_t *ret_dev) {
 
-        struct stat st, st2;
-        const char *t2, *trigger;
+        _cleanup_close_ int fd = -EBADF;
+        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...) */
 
-                        parent = dirname_malloc(path);
-                        if (!parent)
-                                return log_oom();
+                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 (st.st_dev == st2.st_dev)
+        if (statx_inode_same(&sxa.sx, &sxb.sx)) /* for the root dir inode nr for both inodes will be the same */
+                goto success;
+
+        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 = -EBADF;
+
+                /* 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;
 }
 
 static int verify_esp(
                 const char *p,
-                bool searching,
-                bool unprivileged_mode,
                 uint32_t *ret_part,
                 uint64_t *ret_pstart,
                 uint64_t *ret_psize,
                 sd_id128_t *ret_uuid,
-                dev_t *ret_devid) {
+                dev_t *ret_devid,
+                VerifyESPFlags flags) {
 
-        bool relax_checks;
-        dev_t devid;
+        bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
+             unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
+        dev_t devid = 0;
         int r;
 
         assert(p);
@@ -319,7 +367,9 @@ static int verify_esp(
          *  -EACESS        → if 'unprivileged_mode' is set, and we have trouble accessing the thing
          */
 
-        relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
+        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. */
@@ -339,13 +389,17 @@ static int verify_esp(
                                               "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
@@ -381,6 +435,7 @@ finish:
 }
 
 int find_esp_and_warn(
+                const char *root,
                 const char *path,
                 bool unprivileged_mode,
                 char **ret_path,
@@ -390,6 +445,9 @@ int find_esp_and_warn(
                 sd_id128_t *ret_uuid,
                 dev_t *ret_devid) {
 
+        VerifyESPFlags flags = (unprivileged_mode ? VERIFY_ESP_UNPRIVILEGED_MODE : 0) |
+                               (root ? VERIFY_ESP_RELAX_CHECKS : 0);
+        _cleanup_free_ char *p = NULL;
         int r;
 
         /* This logs about all errors except:
@@ -399,7 +457,15 @@ int find_esp_and_warn(
          */
 
         if (path) {
-                r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               strempty(root));
+
+                r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags);
                 if (r < 0)
                         return r;
 
@@ -410,19 +476,27 @@ int find_esp_and_warn(
         if (path) {
                 struct stat st;
 
-                if (!path_is_valid(path) || !path_is_absolute(path))
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               strempty(root));
+
+                if (!path_is_valid(p) || !path_is_absolute(p))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
-                                               path);
+                                               p);
 
                 /* Note: when the user explicitly configured things with an env var we won't validate the
                  * path beyond checking it refers to a directory. After all we want this to be useful for
                  * testing. */
 
-                if (stat(path, &st) < 0)
-                        return log_error_errno(errno, "Failed to stat '%s': %m", path);
+                if (stat(p, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", p);
                 if (!S_ISDIR(st.st_mode))
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", p);
 
                 if (ret_part)
                         *ret_part = 0;
@@ -438,29 +512,33 @@ int find_esp_and_warn(
                 goto found;
         }
 
-        FOREACH_STRING(_path, "/efi", "/boot", "/boot/efi") {
-                path = _path;
-
-                r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
+        FOREACH_STRING(dir, "/efi", "/boot", "/boot/efi") {
+                r = chase_symlinks(dir, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               dir,
+                                               root ? " under directory " : "",
+                                               strempty(root));
+
+                r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid,
+                               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);
         }
 
         /* No logging here */
         return -ENOKEY;
 
 found:
-        if (ret_path) {
-                char *c;
-
-                c = strdup(path);
-                if (!c)
-                        return log_oom();
-
-                *ret_path = c;
-        }
+        if (ret_path)
+                *ret_path = TAKE_PTR(p);
 
         return 0;
 }
@@ -478,39 +556,42 @@ static int verify_xbootldr_blkid(
         const char *type, *v;
         int r;
 
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
+        r = devname_from_devnum(S_IFBLK, devid, &node);
         if (r < 0)
-                return log_error_errno(r, "Failed to format block device path for %u:%u: %m",
-                                       major(devid), minor(devid));
+                return log_error_errno(r, "Failed to get block device path for " DEVNUM_FORMAT_STR ": %m",
+                                       DEVNUM_FORMAT_VAL(devid));
 
         errno = 0;
         b = blkid_new_probe_from_filename(node);
         if (!b)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "%s: Failed to create blkid probe: %m", node);
+                return log_error_errno(errno_or_else(ENOMEM), "%s: Failed to create blkid probe: %m", node);
 
         blkid_probe_enable_partitions(b, 1);
         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
 
         errno = 0;
         r = blkid_do_safeprobe(b);
-        if (r == -2)
+        if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system is ambiguous.", node);
-        else if (r == 1)
+        if (r == _BLKID_SAFEPROBE_NOT_FOUND)
                 return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system does not contain a label.", node);
-        else if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe file system: %m", node);
+        if (r == _BLKID_SAFEPROBE_ERROR)
+                return log_error_errno(errno_or_else(EIO), "%s: Failed to probe file system: %m", node);
+
+        assert(r == _BLKID_SAFEPROBE_FOUND);
 
-        errno = 0;
         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &type, NULL);
         if (r != 0)
-                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe PART_ENTRY_SCHEME: %m", node);
+                return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+                                      searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(EIO),
+                                      "%s: Failed to probe PART_ENTRY_SCHEME: %m", node);
         if (streq(type, "gpt")) {
 
                 errno = 0;
                 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 (id128_equal_string(v, GPT_XBOOTLDR) <= 0)
+                        return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
+                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);
@@ -518,7 +599,7 @@ static int verify_xbootldr_blkid(
                 errno = 0;
                 r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
                 if (r != 0)
-                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe PART_ENTRY_UUID: %m", node);
+                        return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_UUID: %m", node);
                 r = sd_id128_from_string(v, &uuid);
                 if (r < 0)
                         return log_error_errno(r, "%s: Partition has invalid UUID PART_ENTRY_TYPE=%s: %m", node, v);
@@ -528,7 +609,7 @@ static int verify_xbootldr_blkid(
                 errno = 0;
                 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);
+                        return log_error_errno(errno_or_else(EIO), "%s: Failed to probe PART_ENTRY_TYPE: %m", node);
                 if (!streq(v, "0xea"))
                         return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
                                               searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
@@ -552,23 +633,24 @@ static int verify_xbootldr_udev(
                 sd_id128_t *ret_uuid) {
 
         _cleanup_(sd_device_unrefp) sd_device *d = NULL;
-        _cleanup_free_ char *node = NULL;
         sd_id128_t uuid = SD_ID128_NULL;
-        const char *type, *v;
+        const char *node, *type, *v;
         int r;
 
-        r = device_path_make_major_minor(S_IFBLK, devid, &node);
+        r = sd_device_new_from_devnum(&d, 'b', devid);
         if (r < 0)
-                return log_error_errno(r, "Failed to format block device path for %u:%u: %m",
-                                       major(devid), minor(devid));
+                return log_error_errno(r, "Failed to get block device for " DEVNUM_FORMAT_STR ": %m", DEVNUM_FORMAT_VAL(devid));
 
-        r = sd_device_new_from_devnum(&d, 'b', devid);
+        r = sd_device_get_devname(d, &node);
         if (r < 0)
-                return log_error_errno(r, "%s: Failed to get block device: %m", node);
+                return log_device_error_errno(d, r, "Failed to get device node: %m");
 
         r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &type);
         if (r < 0)
-                return log_device_error_errno(d, r, "Failed to query ID_PART_ENTRY_SCHEME: %m");
+                return log_device_full_errno(d,
+                                             searching && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
+                                             searching && r == -ENOENT ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : r,
+                                             "Failed to query ID_PART_ENTRY_SCHEME: %m");
 
         if (streq(type, "gpt")) {
 
@@ -576,7 +658,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 = id128_equal_string(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)
@@ -584,7 +666,7 @@ static int verify_xbootldr_udev(
                                         d,
                                         searching ? LOG_DEBUG : LOG_ERR,
                                         searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
-                                        "Parition has wrong ID_PART_ENTRY_TYPE=%s for XBOOTLDR partition.", v);
+                                        "Partition has wrong ID_PART_ENTRY_TYPE=%s for XBOOTLDR partition.", v);
 
                 r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
                 if (r < 0)
@@ -626,18 +708,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)
@@ -662,18 +746,28 @@ finish:
 }
 
 int find_xbootldr_and_warn(
+                const char *root,
                 const char *path,
                 bool unprivileged_mode,
                 char **ret_path,
                 sd_id128_t *ret_uuid,
                 dev_t *ret_devid) {
 
+        _cleanup_free_ char *p = NULL;
         int r;
 
         /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
 
         if (path) {
-                r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               strempty(root));
+
+                r = verify_xbootldr(p, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
                 if (r < 0)
                         return r;
 
@@ -684,15 +778,23 @@ int find_xbootldr_and_warn(
         if (path) {
                 struct stat st;
 
-                if (!path_is_valid(path) || !path_is_absolute(path))
+                r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, &p, NULL);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to resolve path %s%s%s: %m",
+                                               path,
+                                               root ? " under directory " : "",
+                                               strempty(root));
+
+                if (!path_is_valid(p) || !path_is_absolute(p))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
-                                               path);
+                                               p);
 
-                if (stat(path, &st) < 0)
-                        return log_error_errno(errno, "Failed to stat '%s': %m", path);
+                if (stat(p, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat '%s': %m", p);
                 if (!S_ISDIR(st.st_mode))
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", p);
 
                 if (ret_uuid)
                         *ret_uuid = SD_ID128_NULL;
@@ -702,26 +804,26 @@ int find_xbootldr_and_warn(
                 goto found;
         }
 
-        r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
-        if (r >= 0) {
-                path = "/boot";
+        r = chase_symlinks("/boot", root, CHASE_PREFIX_ROOT, &p, NULL);
+        if (r == -ENOENT)
+                return -ENOKEY;
+        if (r < 0)
+                return log_error_errno(r,
+                                       "Failed to resolve path /boot%s%s: %m",
+                                       root ? " under directory " : "",
+                                       strempty(root));
+
+        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;
 
 found:
-        if (ret_path) {
-                char *c;
-
-                c = strdup(path);
-                if (!c)
-                        return log_oom();
-
-                *ret_path = c;
-        }
+        if (ret_path)
+                *ret_path = TAKE_PTR(p);
 
         return 0;
 }