X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fshared%2Ffind-esp.c;h=f830d6dfe3258594860d325271aa519ad864dfcd;hb=HEAD;hp=d474bb41a0785c7604508747421a4cac3f5f2778;hpb=f1710073c7d9de58ba28bf7a9fdb15ec34fec444;p=thirdparty%2Fsystemd.git diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index d474bb41a07..f830d6dfe32 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -9,7 +9,7 @@ #include "alloc-util.h" #include "blkid-util.h" #include "btrfs-util.h" -#include "chase-symlinks.h" +#include "chase.h" #include "device-util.h" #include "devnum-util.h" #include "env-util.h" @@ -17,6 +17,7 @@ #include "fd-util.h" #include "find-esp.h" #include "gpt.h" +#include "mount-util.h" #include "parse-util.h" #include "path-util.h" #include "stat-util.h" @@ -26,12 +27,36 @@ 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 */ + VERIFY_ESP_SKIP_FSTYPE_CHECK = 1 << 2, /* Skip filesystem check */ + VERIFY_ESP_SKIP_DEVICE_CHECK = 1 << 3, /* Skip device node check */ } VerifyESPFlags; +static VerifyESPFlags verify_esp_flags_init(int unprivileged_mode, const char *env_name_for_relaxing) { + VerifyESPFlags flags = 0; + int r; + + assert(env_name_for_relaxing); + + if (unprivileged_mode < 0) + unprivileged_mode = geteuid() != 0; + if (unprivileged_mode) + flags |= VERIFY_ESP_UNPRIVILEGED_MODE; + + r = getenv_bool(env_name_for_relaxing); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $%s environment variable, assuming false.", env_name_for_relaxing); + else if (r > 0) + flags |= VERIFY_ESP_SKIP_FSTYPE_CHECK | VERIFY_ESP_SKIP_DEVICE_CHECK; + + if (detect_container() > 0) + flags |= VERIFY_ESP_SKIP_DEVICE_CHECK; + + return flags; +} + static int verify_esp_blkid( dev_t devid, - bool searching, + VerifyESPFlags flags, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, @@ -44,6 +69,7 @@ static int verify_esp_blkid( #if HAVE_BLKID _cleanup_(blkid_free_probep) blkid_probe b = NULL; _cleanup_free_ char *node = NULL; + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING); const char *v; int r; @@ -65,9 +91,9 @@ static int verify_esp_blkid( r = blkid_do_safeprobe(b); if (r == -2) return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); - else if (r == 1) + if (r == 1) return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); - else if (r != 0) + if (r != 0) return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); @@ -146,12 +172,13 @@ static int verify_esp_blkid( static int verify_esp_udev( dev_t devid, - bool searching, + VerifyESPFlags flags, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid) { + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING); _cleanup_(sd_device_unrefp) sd_device *d = NULL; sd_id128_t uuid = SD_ID128_NULL; uint64_t pstart = 0, psize = 0; @@ -238,12 +265,14 @@ static int verify_esp_udev( } static int verify_fsroot_dir( + int dir_fd, const char *path, - bool searching, - bool unprivileged_mode, + VerifyESPFlags flags, dev_t *ret_dev) { - _cleanup_close_ int fd = -EBADF; + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING), + unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE); + _cleanup_free_ char *f = NULL; STRUCT_NEW_STATX_DEFINE(sxa); STRUCT_NEW_STATX_DEFINE(sxb); int r; @@ -251,24 +280,22 @@ static int verify_fsroot_dir( /* 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(dir_fd >= 0); assert(path); - /* 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); + /* We pass the full path from the root directory file descriptor so we can use it for logging, but + * dir_fd points to the parent directory of the final component of the given path, so we extract the + * filename and operate on that. */ - /* 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 */ - (void) faccessat(fd, "trigger", F_OK, AT_SYMLINK_NOFOLLOW); /* Filename doesn't matter... */ + r = path_extract_filename(path, &f); + if (r < 0 && r != -EADDRNOTAVAIL) + return log_error_errno(r, "Failed to extract filename of %s: %m", path); - r = statx_fallback(fd, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxa.sx); + r = statx_fallback(dir_fd, strempty(f), AT_SYMLINK_NOFOLLOW|(isempty(f) ? AT_EMPTY_PATH : 0), + 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, + return log_full_errno((searching && r == -ENOENT) || + (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, r, "Failed to determine block device node of \"%s\": %m", path); assert(S_ISDIR(sxa.sx.stx_mode)); /* We used O_DIRECTORY above, when opening, so this must hold */ @@ -288,29 +315,7 @@ static int verify_fsroot_dir( } /* 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_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); - } + r = statx_fallback(dir_fd, "", AT_EMPTY_PATH, 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); @@ -327,25 +332,17 @@ 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); - } + 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. */ + return btrfs_get_block_device_at(dir_fd, strempty(f), ret_dev); *ret_dev = makedev(sxa.sx.stx_dev_major, sxa.sx.stx_dev_minor); return 0; } static int verify_esp( - const char *p, + int rfd, + const char *path, + char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, @@ -353,12 +350,15 @@ static int verify_esp( dev_t *ret_devid, VerifyESPFlags flags) { - bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING), - unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE); + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING), + unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE); + _cleanup_free_ char *p = NULL; + _cleanup_close_ int pfd = -EBADF; dev_t devid = 0; int r; - assert(p); + assert(rfd >= 0 || rfd == AT_FDCWD); + assert(path); /* This logs about all errors, except: * @@ -367,20 +367,34 @@ 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 || - 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. */ - if (!relax_checks) { + r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT, &p, &pfd); + if (r < 0) + return log_full_errno((searching && r == -ENOENT) || + (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, + r, "Failed to open parent directory of \"%s\": %m", path); + + if (!FLAGS_SET(flags, VERIFY_ESP_SKIP_FSTYPE_CHECK)) { + _cleanup_free_ char *f = NULL; struct statfs sfs; - if (statfs(p, &sfs) < 0) + r = path_extract_filename(p, &f); + if (r < 0 && r != -EADDRNOTAVAIL) + return log_error_errno(r, "Failed to extract filename of %s: %m", p); + + /* Trigger any automounts so that xstatfsat() operates on the mount instead of the mountpoint + * directory. */ + r = trigger_automount_at(pfd, f); + if (r < 0) + return log_error_errno(r, "Failed to trigger automount at %s: %m", p); + + r = xstatfsat(pfd, strempty(f), &sfs); + if (r < 0) /* If we are searching for the mount point, don't generate a log message if we can't find the path */ - return log_full_errno((searching && errno == ENOENT) || - (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, + return log_full_errno((searching && r == -ENOENT) || + (unprivileged_mode && r == -EACCES) ? LOG_DEBUG : LOG_ERR, r, "Failed to check file system type of \"%s\": %m", p); if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) @@ -389,37 +403,42 @@ static int verify_esp( "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); } - relax_checks = - relax_checks || - detect_container() > 0; - - r = verify_fsroot_dir(p, searching, unprivileged_mode, relax_checks ? NULL : &devid); + r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? 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 (relax_checks) + if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK)) goto finish; + if (devnum_is_zero(devid)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Could not determine backing block device of directory \"%s\" (btrfs RAID?).", p); + /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell), * however blkid can't work if we have no privileges to access block devices directly, which is why * we use udev in that case. */ if (unprivileged_mode) - r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + r = verify_esp_udev(devid, flags, ret_part, ret_pstart, ret_psize, ret_uuid); else - r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + r = verify_esp_blkid(devid, flags, ret_part, ret_pstart, ret_psize, ret_uuid); if (r < 0) return r; + if (ret_path) + *ret_path = TAKE_PTR(p); if (ret_devid) *ret_devid = devid; return 0; finish: + if (ret_path) + *ret_path = TAKE_PTR(p); if (ret_part) *ret_part = 0; if (ret_pstart) @@ -434,10 +453,10 @@ finish: return 0; } -int find_esp_and_warn( - const char *root, +int find_esp_and_warn_at( + int rfd, const char *path, - bool unprivileged_mode, + int unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, @@ -445,9 +464,7 @@ 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; + VerifyESPFlags flags; int r; /* This logs about all errors except: @@ -456,48 +473,39 @@ int find_esp_and_warn( * -EACCESS → when unprivileged_mode is true, and we can't access something */ - if (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)); + assert(rfd >= 0 || rfd == AT_FDCWD); - r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags); - if (r < 0) - return r; + flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_ESP_CHECKS"); - goto found; - } + if (path) + return verify_esp(rfd, path, ret_path, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags); path = getenv("SYSTEMD_ESP_PATH"); if (path) { + _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -EBADF; struct stat st; - 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)) + if (!path_is_valid(path) || !path_is_absolute(path)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s", - p); + "$SYSTEMD_ESP_PATH does not refer to an absolute path, refusing to use it: %s", + path); + + r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT, &p, &fd); + if (r < 0) + return log_error_errno(r, "Failed to resolve path %s: %m", path); /* 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(p, &st) < 0) + if (fstat(fd, &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.", p); + if (ret_path) + *ret_path = TAKE_PTR(p); if (ret_part) *ret_part = 0; if (ret_pstart) @@ -509,48 +517,86 @@ int find_esp_and_warn( if (ret_devid) *ret_devid = st.st_dev; - goto found; + return 0; } 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, + r = verify_esp(rfd, dir, ret_path, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags | VERIFY_ESP_SEARCHING); if (r >= 0) - goto found; + return 0; if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL, -ENOTDIR, -ENOTTY)) /* This one is not it */ return r; - - p = mfree(p); } /* No logging here */ return -ENOKEY; +} -found: - if (ret_path) - *ret_path = TAKE_PTR(p); +int find_esp_and_warn( + const char *root, + const char *path, + int unprivileged_mode, + char **ret_path, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + _cleanup_close_ int rfd = -EBADF; + _cleanup_free_ char *p = NULL; + uint32_t part; + uint64_t pstart, psize; + sd_id128_t uuid; + dev_t devid; + int r; + + rfd = open(empty_to_root(root), O_PATH|O_DIRECTORY|O_CLOEXEC); + if (rfd < 0) + return -errno; + + r = find_esp_and_warn_at( + rfd, + path, + unprivileged_mode, + ret_path ? &p : NULL, + ret_part ? &part : NULL, + ret_pstart ? &pstart : NULL, + ret_psize ? &psize : NULL, + ret_uuid ? &uuid : NULL, + ret_devid ? &devid : NULL); + if (r < 0) + return r; + + if (ret_path) { + r = chaseat_prefix_root(p, root, ret_path); + if (r < 0) + return r; + } + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + if (ret_devid) + *ret_devid = devid; return 0; } static int verify_xbootldr_blkid( dev_t devid, - bool searching, + VerifyESPFlags flags, sd_id128_t *ret_uuid) { sd_id128_t uuid = SD_ID128_NULL; #if HAVE_BLKID + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING); _cleanup_(blkid_free_probep) blkid_probe b = NULL; _cleanup_free_ char *node = NULL; const char *type, *v; @@ -594,7 +640,7 @@ static int verify_xbootldr_blkid( 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); + "%s: Partition has wrong PART_ENTRY_TYPE=%s for XBOOTLDR partition.", node, v); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); @@ -629,9 +675,10 @@ static int verify_xbootldr_blkid( static int verify_xbootldr_udev( dev_t devid, - bool searching, + VerifyESPFlags flags, sd_id128_t *ret_uuid) { + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING); _cleanup_(sd_device_unrefp) sd_device *d = NULL; sd_id128_t uuid = SD_ID128_NULL; const char *node, *type, *v; @@ -701,42 +748,62 @@ static int verify_xbootldr_udev( } static int verify_xbootldr( - const char *p, - bool searching, - bool unprivileged_mode, + int rfd, + const char *path, + VerifyESPFlags flags, + char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid) { - bool relax_checks; + _cleanup_free_ char *p = NULL; + _cleanup_close_ int pfd = -EBADF; + bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING), + unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE); dev_t devid = 0; int r; - assert(p); + assert(rfd >= 0 || rfd == AT_FDCWD); + assert(path); - relax_checks = - getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0 || - detect_container() > 0; + r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT, &p, &pfd); + if (r < 0) + return log_full_errno((searching && r == -ENOENT) || + (unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR, + r, "Failed to open parent directory of \"%s\": %m", path); - r = verify_fsroot_dir(p, searching, unprivileged_mode, relax_checks ? NULL : &devid); + r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? NULL : &devid); if (r < 0) return r; - if (relax_checks) + if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK)) goto finish; + if (devnum_is_zero(devid)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Could not determine backing block device of directory \"%s\" (btrfs RAID?).%s", + p, + searching ? "" : + "\nHint: set $SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes environment variable " + "to bypass this and further verifications for the directory."); + if (unprivileged_mode) - r = verify_xbootldr_udev(devid, searching, ret_uuid); + r = verify_xbootldr_udev(devid, flags, ret_uuid); else - r = verify_xbootldr_blkid(devid, searching, ret_uuid); + r = verify_xbootldr_blkid(devid, flags, ret_uuid); if (r < 0) return r; + if (ret_path) + *ret_path = TAKE_PTR(p); if (ret_devid) *ret_devid = devid; return 0; finish: + if (ret_path) + *ret_path = TAKE_PTR(p); if (ret_uuid) *ret_uuid = SD_ID128_NULL; if (ret_devid) @@ -745,85 +812,104 @@ finish: return 0; } -int find_xbootldr_and_warn( - const char *root, +int find_xbootldr_and_warn_at( + int rfd, const char *path, - bool unprivileged_mode, + int unprivileged_mode, char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid) { - _cleanup_free_ char *p = NULL; + VerifyESPFlags flags; int r; /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ - if (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)); + assert(rfd >= 0 || rfd == AT_FDCWD); - r = verify_xbootldr(p, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid); - if (r < 0) - return r; + flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_XBOOTLDR_CHECKS"); - goto found; - } + if (path) + return verify_xbootldr(rfd, path, flags, ret_path, ret_uuid, ret_devid); path = getenv("SYSTEMD_XBOOTLDR_PATH"); if (path) { + _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -EBADF; struct stat st; - 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)) + if (!path_is_valid(path) || !path_is_absolute(path)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s", - p); + "$SYSTEMD_XBOOTLDR_PATH does not refer to an absolute path, refusing to use it: %s", + path); + + r = chaseat(rfd, path, CHASE_AT_RESOLVE_IN_ROOT, &p, &fd); + if (r < 0) + return log_error_errno(r, "Failed to resolve path %s: %m", p); - if (stat(p, &st) < 0) + if (fstat(fd, &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.", p); + if (ret_path) + *ret_path = TAKE_PTR(p); if (ret_uuid) *ret_uuid = SD_ID128_NULL; if (ret_devid) *ret_devid = st.st_dev; - goto found; + return 0; } - r = chase_symlinks("/boot", root, CHASE_PREFIX_ROOT, &p, NULL); - if (r == -ENOENT) + r = verify_xbootldr(rfd, "/boot", flags | VERIFY_ESP_SEARCHING, ret_path, ret_uuid, ret_devid); + if (r < 0) { + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL, -ENOTDIR, -ENOTTY)) /* This one is not it */ + return r; + 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, -ENOTDIR)) /* This one is not it */ - return r; + return 0; +} - return -ENOKEY; +int find_xbootldr_and_warn( + const char *root, + const char *path, + int unprivileged_mode, + char **ret_path, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { -found: - if (ret_path) - *ret_path = TAKE_PTR(p); + _cleanup_close_ int rfd = -EBADF; + _cleanup_free_ char *p = NULL; + sd_id128_t uuid; + dev_t devid; + int r; + + rfd = open(empty_to_root(root), O_PATH|O_DIRECTORY|O_CLOEXEC); + if (rfd < 0) + return -errno; + + r = find_xbootldr_and_warn_at( + rfd, + path, + unprivileged_mode, + ret_path ? &p : NULL, + ret_uuid ? &uuid : NULL, + ret_devid ? &devid : NULL); + if (r < 0) + return r; + + if (ret_path) { + r = chaseat_prefix_root(p, root, ret_path); + if (r < 0) + return r; + } + if (ret_uuid) + *ret_uuid = uuid; + if (ret_devid) + *ret_devid = devid; return 0; }