]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
efi: rework find_esp() error propagation/logging a bit
authorLennart Poettering <lennart@poettering.net>
Mon, 11 Dec 2017 21:04:46 +0000 (22:04 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 11 Dec 2017 22:18:56 +0000 (23:18 +0100)
This renames find_esp() to find_esp_and_warn() and tries to normalize its
behaviour:

1. Change the error that is returned when we can't find the ESP to
   ENOKEY (from ENOENT). This way the error code can only mean one
   thing: that our search loop didn't find a good candidate.
2. Really log about all errors, except for ENOKEY and EACCES, and
   document the letter cases.
3. Normalize parameters to the call: separate out the path parameter in
   two: an input path and an output path. That way the memory management
   is clear: we will access the input parameter only for reading, and
   only write out the output parameter, using malloc() memory.
   Before the calling convention were quire surprising for internal API
   code, as the path parameter had to be malloc() memory and might and
   might not have changed.
4. Rename bootctl's find_esp_warn() to acquire_esp(), and make it a
   simple wrapper around find_esp_warn(), that basically just adds the
   friendly logging for the ENOKEY case. This rework removes double
   logging in a number of error cases, as we no longer log here in
   anything but ENOKEY, and leave that entirely to find_esp_warn().
5. find_esp_and_warn() now takes a bool flag parameter
   "unprivileged_mode", which disables logging in the EACCES case, and
   skips privileged validation of the path. This makes the function less
   magic, and doesn't hide this internal silencing automatism from the
   caller anymore.

With all that in place "bootctl list" and "bootctl status" work properly
(or as good as they can) when I invoke the tools whithout privileges on
my system where /boot is not world-readable

src/boot/bootctl.c
src/shared/bootspec.c
src/shared/bootspec.h
src/systemctl/systemctl.c

index e733e206e40cb76a035e72b2f8eb0d595029f379..52207661cf61f7b92618b92d4d0c7ccf901adb2d 100644 (file)
@@ -61,19 +61,33 @@ static char *arg_path = NULL;
 static bool arg_print_path = false;
 static bool arg_touch_variables = true;
 
-static int find_esp_and_warn(uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
+static int acquire_esp(
+                bool unprivileged_mode,
+                uint32_t *ret_part,
+                uint64_t *ret_pstart,
+                uint64_t *ret_psize,
+                sd_id128_t *ret_uuid) {
+
+        char *np;
         int r;
 
-        r = find_esp(&arg_path, part, pstart, psize, uuid);
-        if (r == -ENOENT)
+        /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on its own,
+         * except for ENOKEY (which is good, we want to show our own message in that case, suggesting use of --path=)
+         * and EACCESS (only when we request unprivileged mode; in this case we simply eat up the error here, so that
+         * --list and --status work too, without noise about this). */
+
+        r = find_esp_and_warn(arg_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid);
+        if (r == -ENOKEY)
                 return log_error_errno(r,
-                                       "Couldn't find EFI system partition. It is recommended to mount it to /boot.\n"
+                                       "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
                                        "Alternatively, use --path= to specify path to mount point.");
-        else if (r < 0)
-                return log_error_errno(r,
-                                       "Couldn't find EFI system partition: %m");
+        if (r < 0)
+                return r;
+
+        free_and_replace(arg_path, np);
 
         log_debug("Using EFI System Partition at %s.", arg_path);
+
         return 0;
 }
 
@@ -925,9 +939,12 @@ static int verb_status(int argc, char *argv[], void *userdata) {
         sd_id128_t uuid = SD_ID128_NULL;
         int r, k;
 
-        r = find_esp_and_warn(NULL, NULL, NULL, &uuid);
+        r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
 
         if (arg_print_path) {
+                if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
+                                   * error the find_esp_and_warn() won't log on its own) */
+                        return log_error_errno(r, "Failed to determine ESP: %m");
                 if (r < 0)
                         return r;
 
@@ -935,6 +952,9 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 return 0;
         }
 
+        r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
+                * can show */
+
         if (is_efi_boot()) {
                 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL;
                 sd_id128_t loader_part_uuid = SD_ID128_NULL;
@@ -997,13 +1017,18 @@ static int verb_status(int argc, char *argv[], void *userdata) {
 }
 
 static int verb_list(int argc, char *argv[], void *userdata) {
+        _cleanup_(boot_config_free) BootConfig config = {};
         sd_id128_t uuid = SD_ID128_NULL;
-        int r;
         unsigned n;
+        int r;
 
-        _cleanup_(boot_config_free) BootConfig config = {};
+        /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
+         * off logging about access errors and turn off potentially privileged device probing. Here we're interested in
+         * the latter but not the former, hence request the mode, and log about EACCES. */
 
-        r = find_esp_and_warn(NULL, NULL, NULL, &uuid);
+        r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
+        if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
+                return log_error_errno(r, "Failed to determine ESP: %m");
         if (r < 0)
                 return r;
 
@@ -1071,7 +1096,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = find_esp_and_warn(&part, &pstart, &psize, &uuid);
+        r = acquire_esp(false, &part, &pstart, &psize, &uuid);
         if (r < 0)
                 return r;
 
@@ -1106,7 +1131,7 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = find_esp_and_warn(NULL, NULL, NULL, &uuid);
+        r = acquire_esp(false, NULL, NULL, NULL, &uuid);
         if (r < 0)
                 return r;
 
index dea3c5212ad93a0d28656fc960534fd973a5e9c5..acd9711839b9faf5c260d0d03bb413fd4cc98214 100644 (file)
@@ -413,8 +413,9 @@ int boot_entries_load_config(const char *esp_path, BootConfig *config) {
 /********************************************************************************/
 
 static int verify_esp(
-                bool searching,
                 const char *p,
+                bool searching,
+                bool unprivileged_mode,
                 uint32_t *ret_part,
                 uint64_t *ret_pstart,
                 uint64_t *ret_psize,
@@ -430,21 +431,19 @@ static int verify_esp(
         struct statfs sfs;
         sd_id128_t uuid = SD_ID128_NULL;
         uint32_t part = 0;
-        bool quiet;
         int r;
 
         assert(p);
 
-        /* Non-root user can only check the status, so if an error occured in the following,
-         * it does not cause any issues. Let's silence the error messages. */
-        quiet = geteuid() != 0;
+        /* Non-root user can only check the status, so if an error occured in the following, it does not cause any
+         * issues. Let's also, silence the error messages. */
 
         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 */
                 if (errno == ENOENT && searching)
                         return -ENOENT;
 
-                return log_full_errno(quiet && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
+                return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
                                       "Failed to check file system type of \"%s\": %m", p);
         }
 
@@ -457,7 +456,7 @@ static int verify_esp(
         }
 
         if (stat(p, &st) < 0)
-                return log_full_errno(quiet && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
+                return log_full_errno(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) {
@@ -468,7 +467,7 @@ static int verify_esp(
         t2 = strjoina(p, "/..");
         r = stat(t2, &st2);
         if (r < 0)
-                return log_full_errno(quiet && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
+                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) {
@@ -478,7 +477,7 @@ static int verify_esp(
 
         /* 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. Also skip the following verification for non-root user. */
-        if (detect_container() > 0 || geteuid() != 0)
+        if (detect_container() > 0 || unprivileged_mode)
                 goto finish;
 
 #if HAVE_BLKID
@@ -579,29 +578,53 @@ finish:
         return 0;
 }
 
-int find_esp(char **path,
-             uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
+int find_esp_and_warn(
+                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) {
 
-        const char *p;
         int r;
 
-        if (*path)
-                return verify_esp(false, *path, part, pstart, psize, uuid);
-
-        FOREACH_STRING(p, "/efi", "/boot", "/boot/efi") {
+        /* This logs about all errors except:
+         *
+         *    -ENOKEY → when we can't find the partition
+         *   -EACCESS → when unprivileged_mode is true, and we can't access something
+         */
 
-                r = verify_esp(true, p, part, pstart, psize, uuid);
-                if (IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
-                        continue;
+        if (path) {
+                r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
                 if (r < 0)
                         return r;
 
-                *path = strdup(p);
-                if (!*path)
+                goto found;
+        }
+
+        FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
+
+                r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
+                if (r >= 0)
+                        goto found;
+                if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
+                        return r;
+        }
+
+        /* No logging here */
+        return -ENOKEY;
+
+found:
+        if (ret_path) {
+                char *c;
+
+                c = strdup(path);
+                if (!c)
                         return log_oom();
 
-                return 0;
+                *ret_path = c;
         }
 
-        return -ENOENT;
+        return 0;
 }
index fb8c10a69aed736644bac4a3b73d303fde3858e4..d9c641bf081bb740e30d6ccbd91e66331bf5fb32 100644 (file)
@@ -61,5 +61,4 @@ static inline const char* boot_entry_title(const BootEntry *entry) {
         return entry->show_title ?: entry->title ?: entry->filename;
 }
 
-int find_esp(char **path,
-             uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid);
+int find_esp_and_warn(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);
index 33fbaf995011ef5fbcfc73fc66a67fdc5244fd97..0a39a7e26c79c78cf77e9e6ac8a8a5b1e5fbaf01 100644 (file)
@@ -3497,7 +3497,7 @@ static int prepare_firmware_setup(void) {
 
 static int load_kexec_kernel(void) {
         _cleanup_(boot_config_free) BootConfig config = {};
-        _cleanup_free_ char *kernel = NULL, *initrd = NULL, *options = NULL;
+        _cleanup_free_ char *where = NULL, *kernel = NULL, *initrd = NULL, *options = NULL;
         const BootEntry *e;
         pid_t pid;
         int r;
@@ -3507,14 +3507,15 @@ static int load_kexec_kernel(void) {
                 return 0;
         }
 
-        r = find_esp(&arg_esp_path, NULL, NULL, NULL, NULL);
-        if (r < 0)
-                return log_error_errno(r, "Cannot find the ESP partition mount point: %m");
+        r = find_esp_and_warn(arg_esp_path, false, &where, NULL, NULL, NULL, NULL);
+        if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */
+                return log_error_errno(r, "Cannot find the ESP partition mount point.");
+        if (r < 0) /* But it logs about all these cases, hence don't log here again */
+                return r;
 
-        r = boot_entries_load_config(arg_esp_path, &config);
+        r = boot_entries_load_config(where, &config);
         if (r < 0)
-                return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
-                                       arg_esp_path);
+                return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where);
 
         if (config.default_entry < 0) {
                 log_error("No entry suitable as default, refusing to guess.");
@@ -3527,9 +3528,9 @@ static int load_kexec_kernel(void) {
                 return -EINVAL;
         }
 
-        kernel = path_join(NULL, arg_esp_path, e->kernel);
+        kernel = path_join(NULL, where, e->kernel);
         if (!strv_isempty(e->initrd))
-                initrd = path_join(NULL, arg_esp_path, *e->initrd);
+                initrd = path_join(NULL, where, *e->initrd);
         options = strv_join(e->options, " ");
         if (!options)
                 return log_oom();