From c706b27f40c0fc1b433a8742b2273cc453de2e1d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 30 Mar 2023 10:39:53 +0200 Subject: [PATCH 1/4] btrfs-util: Add btrfs_get_block_device_at() Let's make btrfs_get_block_device_fd() more generic by renaming it to btrfs_get_block_device_at() so it can operate on only paths, dir_fd and path, or only on fd by using xopenat(). --- src/shared/btrfs-util.c | 28 ++++++++-------------------- src/shared/btrfs-util.h | 9 +++++++-- src/shared/find-esp.c | 14 ++------------ 3 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index c684057541..2789364c98 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -247,16 +247,17 @@ int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offs return RET_NERRNO(ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args)); } -int btrfs_get_block_device_fd(int fd, dev_t *dev) { +int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret) { struct btrfs_ioctl_fs_info_args fsi = {}; - _cleanup_close_ int regfd = -EBADF; + _cleanup_close_ int fd = -EBADF; uint64_t id; int r; - assert(fd >= 0); - assert(dev); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(ret); - fd = fd_reopen_condition(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, O_PATH, ®fd); + fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY, 0); if (fd < 0) return fd; @@ -271,7 +272,7 @@ int btrfs_get_block_device_fd(int fd, dev_t *dev) { /* We won't do this for btrfs RAID */ if (fsi.num_devices != 1) { - *dev = 0; + *ret = 0; return 0; } @@ -306,26 +307,13 @@ int btrfs_get_block_device_fd(int fd, dev_t *dev) { if (major(st.st_rdev) == 0) return -ENODEV; - *dev = st.st_rdev; + *ret = st.st_rdev; return 1; } return -ENODEV; } -int btrfs_get_block_device(const char *path, dev_t *dev) { - _cleanup_close_ int fd = -EBADF; - - assert(path); - assert(dev); - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - return btrfs_get_block_device_fd(fd, dev); -} - int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) { struct btrfs_ioctl_ino_lookup_args args = { .objectid = BTRFS_FIRST_FREE_OBJECTID diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index 080d5a607d..75a8ed8549 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -49,8 +49,13 @@ int btrfs_is_subvol(const char *path); int btrfs_reflink(int infd, int outfd); int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz); -int btrfs_get_block_device_fd(int fd, dev_t *dev); -int btrfs_get_block_device(const char *path, dev_t *dev); +int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret); +static inline int btrfs_get_block_device(const char *path, dev_t *ret) { + return btrfs_get_block_device_at(AT_FDCWD, path, ret); +} +static inline int btrfs_get_block_device_fd(int fd, dev_t *ret) { + return btrfs_get_block_device_at(fd, "", ret); +} int btrfs_defrag_fd(int fd); int btrfs_defrag(const char *p); diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index f6abe51de5..c6ef4ec87a 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -327,18 +327,8 @@ 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_fd(fd, ret_dev); *ret_dev = makedev(sxa.sx.stx_dev_major, sxa.sx.stx_dev_minor); return 0; From 05c423adc944b571e1b068b84b55bd42fb3ca08f Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 30 Mar 2023 10:21:45 +0200 Subject: [PATCH 2/4] find-esp: Drop explicit automount trigger We call statx_fallback() which calls either statx() or fstatat(), both of which automount by default, so we don't need the explicit trigger. --- src/shared/find-esp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index c6ef4ec87a..1dbd1e8217 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -261,11 +261,6 @@ static int verify_fsroot_dir( (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 */ - (void) faccessat(fd, "trigger", F_OK, AT_SYMLINK_NOFOLLOW); /* Filename doesn't matter... */ - 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, From 6eec59f9cc325a5491309a32aadc8fa0722992a1 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 31 Mar 2023 10:39:55 +0200 Subject: [PATCH 3/4] stat-util: Add xstatfsat() --- src/basic/stat-util.c | 14 ++++++++++++++ src/basic/stat-util.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 2b09ab63b2..e97770707f 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -455,6 +455,20 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s return 0; } +int xstatfsat(int dir_fd, const char *path, struct statfs *ret) { + _cleanup_close_ int fd = -EBADF; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(ret); + + fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY, 0); + if (fd < 0) + return fd; + + return RET_NERRNO(fstatfs(fd, ret)); +} + void inode_hash_func(const struct stat *q, struct siphash *state) { siphash24_compress(&q->st_dev, sizeof(q->st_dev), state); siphash24_compress(&q->st_ino, sizeof(q->st_ino), state); diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 726d0644aa..e6b84d215e 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -79,6 +79,8 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b); int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); +int xstatfsat(int dir_fd, const char *path, struct statfs *ret); + #if HAS_FEATURE_MEMORY_SANITIZER # warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this." # define STRUCT_STATX_DEFINE(var) \ From 63105f33edad423691e2d53bf7071f99c83799ba Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 22 Dec 2022 14:59:56 +0100 Subject: [PATCH 4/4] find-esp: Add openat() like helpers that operate on fds We also rework the internals of find-esp to work on directory file descriptors instead of absolute paths and do a lot of general cleanups. By passing the parent directory file descriptor to verify_fsroot_dir() along with the name of the directory we're operating on, we can get rid of the fallback that goes via path to open the parent directory if '..' fails due to permission errors. --- src/shared/find-esp.c | 337 +++++++++++++++++++++++++----------------- src/shared/find-esp.h | 3 + 2 files changed, 204 insertions(+), 136 deletions(-) diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c index 1dbd1e8217..60f80387f8 100644 --- a/src/shared/find-esp.c +++ b/src/shared/find-esp.c @@ -238,12 +238,13 @@ static int verify_esp_udev( } static int verify_fsroot_dir( + int dir_fd, const char *path, bool searching, bool unprivileged_mode, dev_t *ret_dev) { - _cleanup_close_ int fd = -EBADF; + _cleanup_free_ char *f = NULL; STRUCT_NEW_STATX_DEFINE(sxa); STRUCT_NEW_STATX_DEFINE(sxb); int r; @@ -251,19 +252,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. */ - r = statx_fallback(fd, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sxa.sx); + 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(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 */ @@ -283,29 +287,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); @@ -323,14 +305,16 @@ success: 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. */ - return btrfs_get_block_device_fd(fd, ret_dev); + 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, @@ -340,10 +324,13 @@ static int verify_esp( bool relax_checks, 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: * @@ -359,13 +346,25 @@ static int verify_esp( /* 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. */ + 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 (!relax_checks) { + _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); + + 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)) @@ -378,7 +377,7 @@ static int verify_esp( relax_checks || detect_container() > 0; - r = verify_fsroot_dir(p, searching, unprivileged_mode, relax_checks ? NULL : &devid); + r = verify_fsroot_dir(pfd, p, searching, unprivileged_mode, relax_checks ? NULL : &devid); if (r < 0) return r; @@ -399,12 +398,16 @@ static int verify_esp( 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) @@ -419,8 +422,8 @@ 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, char **ret_path, @@ -430,9 +433,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 = (unprivileged_mode ? VERIFY_ESP_UNPRIVILEGED_MODE : 0); int r; /* This logs about all errors except: @@ -441,48 +442,46 @@ int find_esp_and_warn( * -EACCESS → when unprivileged_mode is true, and we can't access something */ - if (path) { - r = chase(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; + if (rfd >= 0) + r = dir_fd_is_root(rfd); + else + r = true; + if (r < 0) + return log_error_errno(r, "Failed to check if directory file descriptor is root: %m"); + if (r == 0) + flags |= VERIFY_ESP_RELAX_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(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) @@ -494,36 +493,72 @@ 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(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, + bool 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) { + char *q = path_join(empty_to_root(root), p); + if (!q) + return -ENOMEM; + + *ret_path = TAKE_PTR(q); + } + 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; } @@ -686,23 +721,34 @@ static int verify_xbootldr_udev( } static int verify_xbootldr( - const char *p, + int rfd, + const char *path, bool searching, bool unprivileged_mode, + char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid) { + _cleanup_free_ char *p = NULL; + _cleanup_close_ int pfd = -EBADF; bool relax_checks; dev_t devid = 0; int r; - assert(p); + assert(rfd >= 0 || rfd == AT_FDCWD); + assert(path); + + 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); relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0 || detect_container() > 0; - r = verify_fsroot_dir(p, searching, unprivileged_mode, relax_checks ? NULL : &devid); + r = verify_fsroot_dir(pfd, p, searching, unprivileged_mode, relax_checks ? NULL : &devid); if (r < 0) return r; @@ -716,12 +762,16 @@ static int verify_xbootldr( 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) @@ -730,85 +780,100 @@ 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, 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 = chase(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; - - goto found; - } + if (path) + return verify_xbootldr(rfd, path, /* searching= */ false, unprivileged_mode, 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(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); - if (stat(p, &st) < 0) + 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 (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("/boot", root, CHASE_PREFIX_ROOT, &p, NULL); - if (r == -ENOENT) + r = verify_xbootldr(rfd, "/boot", /* searching= */ true, unprivileged_mode, ret_path, ret_uuid, ret_devid); + if (r < 0) { + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL, -ENOTDIR)) /* This one is not it */ + return r; + return -ENOKEY; + } + + return 0; +} + +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_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 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 -ENOKEY; + if (ret_path) { + char *q = path_join(empty_to_root(root), p); + if (!q) + return -ENOMEM; -found: - if (ret_path) - *ret_path = TAKE_PTR(p); + *ret_path = TAKE_PTR(q); + } + if (ret_uuid) + *ret_uuid = uuid; + if (ret_devid) + *ret_devid = devid; return 0; } diff --git a/src/shared/find-esp.h b/src/shared/find-esp.h index 78d7f4551e..94f320195b 100644 --- a/src/shared/find-esp.h +++ b/src/shared/find-esp.h @@ -8,5 +8,8 @@ #include "sd-id128.h" +int find_esp_and_warn_at(int rfd, const char *path, bool 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); int find_esp_and_warn(const char *root, const char *path, bool 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); + +int find_xbootldr_and_warn_at(int rfd, const char *path, bool unprivileged_mode, char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid); 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);