Systemd/src/shared/bootspec.c

1079 lines
40 KiB
C
Raw Normal View History

2018-06-14 12:50:49 +02:00
/* SPDX-License-Identifier: LGPL-2.1+ */
2017-10-17 18:23:16 +02:00
#include <stdio.h>
#include <linux/magic.h>
2017-10-17 18:23:16 +02:00
#include "sd-device.h"
#include "sd-id128.h"
2017-10-17 18:23:16 +02:00
#include "alloc-util.h"
#include "blkid-util.h"
2017-10-17 18:23:16 +02:00
#include "bootspec.h"
#include "conf-files.h"
#include "def.h"
#include "device-nodes.h"
2017-10-17 18:23:16 +02:00
#include "efivars.h"
#include "env-util.h"
2017-10-17 18:23:16 +02:00
#include "fd-util.h"
#include "fileio.h"
#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
2017-10-17 18:23:16 +02:00
#include "string-util.h"
#include "strv.h"
#include "virt.h"
2017-10-17 18:23:16 +02:00
static void boot_entry_free(BootEntry *entry) {
assert(entry);
2017-10-17 18:23:16 +02:00
free(entry->id);
free(entry->path);
free(entry->root);
2017-10-17 18:23:16 +02:00
free(entry->title);
2017-10-20 17:58:13 +02:00
free(entry->show_title);
2017-10-17 18:23:16 +02:00
free(entry->version);
free(entry->machine_id);
free(entry->architecture);
strv_free(entry->options);
free(entry->kernel);
free(entry->efi);
strv_free(entry->initrd);
free(entry->device_tree);
}
static int boot_entry_load(
const char *root,
const char *path,
BootEntry *entry) {
_cleanup_(boot_entry_free) BootEntry tmp = {};
2017-10-17 18:23:16 +02:00
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
char *b, *c;
2017-10-17 18:23:16 +02:00
int r;
assert(root);
assert(path);
assert(entry);
c = endswith_no_case(path, ".conf");
if (!c)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
2017-10-17 18:23:16 +02:00
b = basename(path);
tmp.id = strndup(b, c - b);
if (!tmp.id)
2017-10-17 18:23:16 +02:00
return log_oom();
tmp.path = strdup(path);
if (!tmp.path)
return log_oom();
tmp.root = strdup(root);
if (!tmp.root)
return log_oom();
f = fopen(path, "re");
if (!f)
return log_error_errno(errno, "Failed to open \"%s\": %m", path);
2017-10-17 18:23:16 +02:00
for (;;) {
_cleanup_free_ char *buf = NULL, *field = NULL;
const char *p;
2017-10-17 18:23:16 +02:00
r = read_line(f, LONG_LINE_MAX, &buf);
if (r == 0)
break;
if (r == -ENOBUFS)
return log_error_errno(r, "%s:%u: Line too long", path, line);
if (r < 0)
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
line++;
if (IN_SET(*strstrip(buf), '#', '\0'))
continue;
p = buf;
r = extract_first_word(&p, &field, " \t", 0);
if (r < 0) {
log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
continue;
}
if (r == 0) {
2017-10-17 18:23:16 +02:00
log_warning("%s:%u: Bad syntax", path, line);
continue;
}
if (streq(field, "title"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&tmp.title, p);
else if (streq(field, "version"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&tmp.version, p);
else if (streq(field, "machine-id"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&tmp.machine_id, p);
else if (streq(field, "architecture"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&tmp.architecture, p);
else if (streq(field, "options"))
2017-10-17 18:23:16 +02:00
r = strv_extend(&tmp.options, p);
else if (streq(field, "linux"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&tmp.kernel, p);
else if (streq(field, "efi"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&tmp.efi, p);
else if (streq(field, "initrd"))
2017-10-17 18:23:16 +02:00
r = strv_extend(&tmp.initrd, p);
else if (streq(field, "devicetree"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&tmp.device_tree, p);
else {
log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
2017-10-17 18:23:16 +02:00
continue;
}
if (r < 0)
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
}
*entry = tmp;
tmp = (BootEntry) {};
return 0;
}
void boot_config_free(BootConfig *config) {
size_t i;
2017-10-17 18:23:16 +02:00
assert(config);
2017-10-17 18:23:16 +02:00
free(config->default_pattern);
free(config->timeout);
free(config->editor);
free(config->auto_entries);
free(config->auto_firmware);
2017-10-17 18:23:16 +02:00
free(config->entry_oneshot);
free(config->entry_default);
for (i = 0; i < config->n_entries; i++)
boot_entry_free(config->entries + i);
free(config->entries);
}
static int boot_loader_read_conf(const char *path, BootConfig *config) {
2017-10-17 18:23:16 +02:00
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
int r;
assert(path);
assert(config);
2017-10-17 18:23:16 +02:00
f = fopen(path, "re");
if (!f) {
if (errno == ENOENT)
return 0;
2017-10-17 18:23:16 +02:00
return log_error_errno(errno, "Failed to open \"%s\": %m", path);
}
2017-10-17 18:23:16 +02:00
for (;;) {
_cleanup_free_ char *buf = NULL, *field = NULL;
const char *p;
2017-10-17 18:23:16 +02:00
r = read_line(f, LONG_LINE_MAX, &buf);
if (r == 0)
break;
if (r == -ENOBUFS)
return log_error_errno(r, "%s:%u: Line too long", path, line);
if (r < 0)
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
line++;
if (IN_SET(*strstrip(buf), '#', '\0'))
continue;
p = buf;
r = extract_first_word(&p, &field, " \t", 0);
if (r < 0) {
log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
continue;
}
if (r == 0) {
2017-10-17 18:23:16 +02:00
log_warning("%s:%u: Bad syntax", path, line);
continue;
}
if (streq(field, "default"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&config->default_pattern, p);
else if (streq(field, "timeout"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&config->timeout, p);
else if (streq(field, "editor"))
2017-10-17 18:23:16 +02:00
r = free_and_strdup(&config->editor, p);
else if (streq(field, "auto-entries"))
r = free_and_strdup(&config->auto_entries, p);
else if (streq(field, "auto-firmware"))
r = free_and_strdup(&config->auto_firmware, p);
else if (streq(field, "console-mode"))
r = free_and_strdup(&config->console_mode, p);
2017-10-17 18:23:16 +02:00
else {
log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
2017-10-17 18:23:16 +02:00
continue;
}
if (r < 0)
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
}
return 1;
2017-10-17 18:23:16 +02:00
}
2018-09-18 01:39:24 +02:00
static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
return str_verscmp(a->id, b->id);
2017-10-17 18:23:16 +02:00
}
static int boot_entries_find(
const char *root,
const char *dir,
BootEntry **entries,
size_t *n_entries) {
2017-10-17 18:23:16 +02:00
_cleanup_strv_free_ char **files = NULL;
size_t n_allocated = *n_entries;
bool added = false;
2017-10-17 18:23:16 +02:00
char **f;
int r;
assert(root);
assert(dir);
assert(entries);
assert(n_entries);
2017-10-17 18:23:16 +02:00
r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL);
if (r < 0)
return log_error_errno(r, "Failed to list files in \"%s\": %m", dir);
STRV_FOREACH(f, files) {
if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
2017-10-17 18:23:16 +02:00
return log_oom();
r = boot_entry_load(root, *f, *entries + *n_entries);
2017-10-17 18:23:16 +02:00
if (r < 0)
continue;
(*n_entries) ++;
added = true;
2017-10-17 18:23:16 +02:00
}
if (added)
typesafe_qsort(*entries, *n_entries, boot_entry_compare);
2017-10-17 18:23:16 +02:00
return 0;
}
2017-10-20 17:58:13 +02:00
static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) {
size_t i, j;
2017-10-20 17:58:13 +02:00
bool non_unique = false;
assert(entries || n_entries == 0);
assert(arr || n_entries == 0);
2017-10-20 17:58:13 +02:00
for (i = 0; i < n_entries; i++)
arr[i] = false;
for (i = 0; i < n_entries; i++)
for (j = 0; j < n_entries; j++)
if (i != j && streq(boot_entry_title(entries + i),
boot_entry_title(entries + j)))
non_unique = arr[i] = arr[j] = true;
return non_unique;
}
static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
char *s;
size_t i;
2017-10-20 17:58:13 +02:00
int r;
bool arr[n_entries];
assert(entries || n_entries == 0);
2017-10-20 17:58:13 +02:00
/* Find _all_ non-unique titles */
if (!find_nonunique(entries, n_entries, arr))
return 0;
/* Add version to non-unique titles */
for (i = 0; i < n_entries; i++)
if (arr[i] && entries[i].version) {
r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version);
if (r < 0)
return -ENOMEM;
free_and_replace(entries[i].show_title, s);
}
if (!find_nonunique(entries, n_entries, arr))
return 0;
/* Add machine-id to non-unique titles */
for (i = 0; i < n_entries; i++)
if (arr[i] && entries[i].machine_id) {
r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id);
if (r < 0)
return -ENOMEM;
free_and_replace(entries[i].show_title, s);
}
if (!find_nonunique(entries, n_entries, arr))
return 0;
/* Add file name to non-unique titles */
for (i = 0; i < n_entries; i++)
if (arr[i]) {
r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
2017-10-20 17:58:13 +02:00
if (r < 0)
return -ENOMEM;
free_and_replace(entries[i].show_title, s);
}
return 0;
}
static int boot_entries_select_default(const BootConfig *config) {
2017-10-17 18:23:16 +02:00
int i;
assert(config);
2017-10-17 18:23:16 +02:00
if (config->entry_oneshot)
for (i = config->n_entries - 1; i >= 0; i--)
if (streq(config->entry_oneshot, config->entries[i].id)) {
log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
config->entries[i].id);
2017-10-17 18:23:16 +02:00
return i;
}
if (config->entry_default)
for (i = config->n_entries - 1; i >= 0; i--)
if (streq(config->entry_default, config->entries[i].id)) {
log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
config->entries[i].id);
2017-10-17 18:23:16 +02:00
return i;
}
if (config->default_pattern)
for (i = config->n_entries - 1; i >= 0; i--)
if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
config->entries[i].id, config->default_pattern);
2017-10-17 18:23:16 +02:00
return i;
}
if (config->n_entries > 0)
log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
2017-10-17 18:23:16 +02:00
else
log_debug("Found no default boot entry :(");
2017-10-17 18:23:16 +02:00
return config->n_entries - 1; /* -1 means "no default" */
}
int boot_entries_load_config(
const char *esp_path,
const char *xbootldr_path,
BootConfig *config) {
2017-10-17 18:23:16 +02:00
const char *p;
int r;
assert(config);
if (esp_path) {
p = strjoina(esp_path, "/loader/loader.conf");
r = boot_loader_read_conf(p, config);
if (r < 0)
return r;
2017-10-17 18:23:16 +02:00
p = strjoina(esp_path, "/loader/entries");
r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries);
if (r < 0)
return r;
}
if (xbootldr_path) {
p = strjoina(xbootldr_path, "/loader/entries");
r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries);
if (r < 0)
return r;
}
2017-10-17 18:23:16 +02:00
2017-10-20 17:58:13 +02:00
r = boot_entries_uniquify(config->entries, config->n_entries);
if (r < 0)
return log_error_errno(r, "Failed to uniquify boot entries: %m");
if (is_efi_boot()) {
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
}
2017-10-17 18:23:16 +02:00
config->default_entry = boot_entries_select_default(config);
return 0;
}
/********************************************************************************/
static int verify_esp_blkid(
dev_t devid,
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
bool searching,
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
sd_id128_t uuid = SD_ID128_NULL;
uint64_t pstart = 0, psize = 0;
uint32_t part = 0;
#if HAVE_BLKID
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
_cleanup_free_ char *node = NULL;
const char *v;
int r;
r = device_path_make_major_minor(S_IFBLK, devid, &node);
if (r < 0)
return log_error_errno(r, "Failed to format major/minor device path: %m");
errno = 0;
b = blkid_new_probe_from_filename(node);
if (!b)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
blkid_probe_enable_superblocks(b, 1);
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
errno = 0;
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)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
else if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
errno = 0;
r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node);
if (!streq(v, "vfat"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" is not FAT.", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
if (!streq(v, "gpt"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" is not on a GPT partition table.", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
r = sd_id128_from_string(v, &uuid);
if (r < 0)
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": m", node);
r = safe_atou32(v, &part);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
r = safe_atou64(v, &pstart);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
r = safe_atou64(v, &psize);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
#endif
if (ret_part)
*ret_part = part;
if (ret_pstart)
*ret_pstart = pstart;
if (ret_psize)
*ret_psize = psize;
if (ret_uuid)
*ret_uuid = uuid;
return 0;
}
static int verify_esp_udev(
dev_t devid,
bool searching,
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_free_ char *node = NULL;
sd_id128_t uuid = SD_ID128_NULL;
uint64_t pstart = 0, psize = 0;
uint32_t part = 0;
const char *v;
int r;
r = device_path_make_major_minor(S_IFBLK, devid, &node);
if (r < 0)
return log_error_errno(r, "Failed to format major/minor device path: %m");
r = sd_device_new_from_devnum(&d, 'b', devid);
if (r < 0)
return log_error_errno(r, "Failed to get device from device number: %m");
r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
if (!streq(v, "vfat"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" is not FAT.", node );
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
if (!streq(v, "gpt"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" is not on a GPT partition table.", node);
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
r = sd_id128_from_string(v, &uuid);
if (r < 0)
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
r = safe_atou32(v, &part);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
r = safe_atou64(v, &pstart);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
r = safe_atou64(v, &psize);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
if (ret_part)
*ret_part = part;
if (ret_pstart)
*ret_pstart = pstart;
if (ret_psize)
*ret_psize = psize;
if (ret_uuid)
*ret_uuid = uuid;
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,
bool unprivileged_mode,
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
bool relax_checks;
dev_t devid;
int r;
assert(p);
/* This logs about all errors, except:
*
* -ENOENT if 'searching' is set, and the dir doesn't exist
* -EADDRNOTAVAIL if 'searching' is set, and the dir doesn't look like an ESP
* -EACESS if 'unprivileged_mode' is set, and we have trouble acessing the thing
*/
relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 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 (!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) ||
(unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
"Failed to check file system type of \"%s\": %m", p);
if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
}
r = verify_fsroot_dir(p, searching, unprivileged_mode, &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 (detect_container() > 0 || relax_checks)
goto finish;
/* 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)
return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
else
return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
finish:
if (ret_part)
*ret_part = 0;
if (ret_pstart)
*ret_pstart = 0;
if (ret_psize)
*ret_psize = 0;
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
return 0;
}
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
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) {
int r;
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
/* 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
*/
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
if (path) {
r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid);
if (r < 0)
return r;
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
goto found;
}
path = getenv("SYSTEMD_ESP_PATH");
if (path) {
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",
path);
/* Note: when the user explicitly configured things with an env var we won't validate the mount
* point. After all we want this to be useful for testing. */
goto found;
}
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
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();
*ret_path = c;
}
return 0;
}
static int verify_xbootldr_blkid(
dev_t devid,
bool searching,
sd_id128_t *ret_uuid) {
sd_id128_t uuid = SD_ID128_NULL;
#if HAVE_BLKID
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
_cleanup_free_ char *node = NULL;
const char *v;
int r;
r = device_path_make_major_minor(S_IFBLK, devid, &node);
if (r < 0)
return log_error_errno(r, "Failed to format major/minor device path: %m");
errno = 0;
b = blkid_new_probe_from_filename(node);
if (!b)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
errno = 0;
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)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
else if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
if (streq(v, "gpt")) {
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"File system \"%s\" has wrong type for extended boot loader partition.", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
r = sd_id128_from_string(v, &uuid);
if (r < 0)
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
} else if (streq(v, "dos")) {
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
if (!streq(v, "0xea"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"File system \"%s\" has wrong type for extended boot loader partition.", node);
} else
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"File system \"%s\" is not on a GPT or DOS partition table.", node);
#endif
if (ret_uuid)
*ret_uuid = uuid;
return 0;
}
static int verify_xbootldr_udev(
dev_t devid,
bool searching,
sd_id128_t *ret_uuid) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_free_ char *node = NULL;
sd_id128_t uuid = SD_ID128_NULL;
const char *v;
int r;
r = device_path_make_major_minor(S_IFBLK, devid, &node);
if (r < 0)
return log_error_errno(r, "Failed to format major/minor device path: %m");
r = sd_device_new_from_devnum(&d, 'b', devid);
if (r < 0)
return log_error_errno(r, "Failed to get device from device number: %m");
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
if (streq(v, "gpt")) {
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"File system \"%s\" has wrong type for extended boot loader partition.", node);
r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
r = sd_id128_from_string(v, &uuid);
if (r < 0)
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
} else if (streq(v, "dos")) {
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
if (r < 0)
return log_error_errno(r, "Failed to get device property: %m");
if (!streq(v, "0xea"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"File system \"%s\" has wrong type for extended boot loader partition.", node);
} else
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
"File system \"%s\" is not on a GPT or DOS partition table.", node);
if (ret_uuid)
*ret_uuid = uuid;
return 0;
}
static int verify_xbootldr(
const char *p,
bool searching,
bool unprivileged_mode,
sd_id128_t *ret_uuid) {
bool relax_checks;
dev_t devid;
int r;
assert(p);
relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
if (r < 0)
return r;
if (detect_container() > 0 || relax_checks)
goto finish;
if (unprivileged_mode)
return verify_xbootldr_udev(devid, searching, ret_uuid);
else
return verify_xbootldr_blkid(devid, searching, ret_uuid);
finish:
if (ret_uuid)
*ret_uuid = SD_ID128_NULL;
return 0;
}
int find_xbootldr_and_warn(
const char *path,
bool unprivileged_mode,
char **ret_path,
sd_id128_t *ret_uuid) {
int r;
/* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
if (path) {
r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid);
if (r < 0)
return r;
goto found;
}
path = getenv("SYSTEMD_XBOOTLDR_PATH");
if (path) {
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",
path);
goto found;
}
r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid);
if (r >= 0) {
path = "/boot";
goto found;
}
if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
return r;
return -ENOKEY;
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
found:
if (ret_path) {
char *c;
c = strdup(path);
if (!c)
return log_oom();
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
*ret_path = c;
}
efi: rework find_esp() error propagation/logging a bit 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
2017-12-11 22:04:46 +01:00
return 0;
}
int find_default_boot_entry(
const char *esp_path,
const char *xbootldr_path,
BootConfig *config,
const BootEntry **e) {
_cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
int r;
assert(config);
assert(e);
r = find_esp_and_warn(esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
r = find_xbootldr_and_warn(xbootldr_path, false, &xbootldr_where, NULL);
if (r < 0 && r != -ENOKEY)
return r;
r = boot_entries_load_config(esp_where, xbootldr_where, config);
if (r < 0)
return log_error_errno(r, "Failed to load boot loader entries: %m");
if (config->default_entry < 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"No boot loader entry suitable as default, refusing to guess.");
*e = &config->entries[config->default_entry];
log_debug("Found default boot loader entry in file \"%s\"", (*e)->path);
return 0;
}