Merge pull request #27067 from DaanDeMeyer/find-esp-at
find-esp: Add openat() like helpers that operate on fds
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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) \
|
||||
|
||||
+8
-20
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+201
-151
@@ -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,24 +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. */
|
||||
|
||||
/* 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 +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);
|
||||
@@ -327,25 +304,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,
|
||||
@@ -355,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:
|
||||
*
|
||||
@@ -374,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))
|
||||
@@ -393,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;
|
||||
|
||||
@@ -414,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)
|
||||
@@ -434,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,
|
||||
@@ -445,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:
|
||||
@@ -456,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)
|
||||
@@ -509,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;
|
||||
}
|
||||
@@ -701,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;
|
||||
|
||||
@@ -731,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)
|
||||
@@ -745,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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user