bootspec: split out code that validates whether directory is top-level dir of fs

Let's add a new function that checks whether some directory is the
top-level directory inside an fs, splitting out the code for this from
verify_esp().

While we are at it, let's slightly improve the code, so that we can
correctly work if we have no priviliges but the ESP is mounted
unaccessible: if we can't stat() the path "$ESP/.." then manually remove
the last component of $ESP and check that instead. Which is very similar
in behaviour, and hopefully good enough in the unprivileged case.
This commit is contained in:
Lennart Poettering
2019-03-01 12:41:32 +01:00
parent cedb9eec76
commit 18ae9ef1a5
+67 -24
View File
@@ -622,6 +622,66 @@ static int verify_esp_udev(
return 0;
}
static int verify_fsroot_dir(
const char *path,
bool searching,
bool unprivileged_mode,
dev_t *ret_dev) {
struct stat st, st2;
const char *t2;
int r;
assert(path);
assert(ret_dev);
if (stat(path, &st) < 0)
return log_full_errno((searching && errno == ENOENT) ||
(unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
"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);
t2 = strjoina(path, "/..");
if (stat(t2, &st2) < 0) {
if (errno != EACCES)
r = -errno;
else {
_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. */
parent = dirname_malloc(path);
if (!parent)
return log_oom();
if (stat(parent, &st2) < 0)
r = -errno;
else
r = 0;
}
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);
}
if (st.st_dev == st2.st_dev)
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;
return 0;
}
static int verify_esp(
const char *p,
bool searching,
@@ -631,10 +691,8 @@ static int verify_esp(
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
struct stat st, st2;
struct statfs sfs;
bool relax_checks;
const char *t2;
dev_t devid;
int r;
assert(p);
@@ -652,6 +710,8 @@ static int verify_esp(
* issues. Let's also, silence the error messages. */
if (!relax_checks) {
struct statfs sfs;
if (statfs(p, &sfs) < 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) ||
@@ -664,26 +724,9 @@ static int verify_esp(
"File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
}
if (stat(p, &st) < 0)
return log_full_errno((searching && errno == ENOENT) ||
(unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
"Failed to determine block device node of \"%s\": %m", p);
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.", p);
t2 = strjoina(p, "/..");
r = stat(t2, &st2);
r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
if (r < 0)
return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
"Failed to determine block device node of parent of \"%s\": %m", p);
if (st.st_dev == st2.st_dev)
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
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. */
@@ -696,9 +739,9 @@ static int verify_esp(
* 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)
return verify_esp_udev(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
else
return verify_esp_blkid(st.st_dev, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
finish:
if (ret_part)