This patch limits the probing of ebg config environments to the block
device the bootloader was started on. This setting can be overwritten by
configuring using the new API ebg_set_opt before creating / opening the
environment.
The boot block device is queried using the systemd boot loader
interface, or more precisely the LoaderDevicePartUUID efi variable. In
case this variable is not set, or the value does not point to a valid
device, all devices are searched.
We believe that changing the default to "this device" only is a
reasonable decision, because:
- parsing only the current device makes updates more robust, as no
environments from other disks (e.g. USB drives) are considered.
- more efficient as no hanging / slow devices are queried
- less errors in the syslog
To support the recovery use-case, tools that use libebgenv can
explicitly enable the search on all devices.
env/env_api.c | 19 +++++--
env/env_api_fat.c | 4 +-
env/env_config_partitions.c | 90 +++++++++++++++++++++++++++++++--
include/ebgenv.h | 9 ++++
include/ebgpart.h | 2 +-
include/env_api.h | 2 +-
include/env_config_partitions.h | 2 +-
tools/bg_printenv.c | 3 +-
tools/bg_setenv.c | 3 +-
tools/ebgpart.c | 27 ++++++----
10 files changed, 137 insertions(+), 24 deletions(-)
diff --git a/env/env_api.c b/env/env_api.c
index 11683bd..5c8877c 100644
--- a/env/env_api.c
+++ b/env/env_api.c
@@ -1,10 +1,11 @@
/*
* EFI Boot Guard
*
- * Copyright (c) Siemens AG, 2017
+ * Copyright (c) Siemens AG, 2017-2023
*
* Authors:
* Andreas Reichel <
andreas.r...@siemens.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
@@ -56,6 +57,18 @@ char16_t *str8to16(char16_t *buffer, const char *src)
return tmp;
}
+int ebg_set_opt(ebgenv_t *e, ebg_opt_t opt, void *value)
+{
+ switch (opt) {
+ case EBG_OPT_SEARCH_ALL_DEVICES:
+ e->opts.search_all_devs = (bool)value;
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
void ebg_beverbose(ebgenv_t __attribute__((unused)) *e, bool v)
{
bgenv_be_verbose(v);
@@ -63,7 +76,7 @@ void ebg_beverbose(ebgenv_t __attribute__((unused)) *e, bool v)
int ebg_env_create_new(ebgenv_t *e)
{
- if (!bgenv_init()) {
+ if (!bgenv_init(e)) {
return EIO;
}
@@ -96,7 +109,7 @@ int ebg_env_create_new(ebgenv_t *e)
int ebg_env_open_current(ebgenv_t *e)
{
- if (!bgenv_init()) {
+ if (!bgenv_init(e)) {
return EIO;
}
diff --git a/env/env_api_fat.c b/env/env_api_fat.c
index 18206a7..9b8ca72 100644
--- a/env/env_api_fat.c
+++ b/env/env_api_fat.c
@@ -168,13 +168,13 @@ BG_ENVDATA __attribute__((weak)) envdata[ENV_NUM_CONFIG_PARTS];
static bool initialized;
-bool bgenv_init(void)
+bool bgenv_init(ebgenv_t *e)
{
if (initialized) {
return true;
}
/* enumerate all config partitions */
- if (!probe_config_partitions(config_parts)) {
+ if (!probe_config_partitions(config_parts, e->opts.search_all_devs)) {
VERBOSE(stderr, "Error finding config partitions.\n");
return false;
}
diff --git a/env/env_config_partitions.c b/env/env_config_partitions.c
index be52d7f..bd3c7af 100644
--- a/env/env_config_partitions.c
+++ b/env/env_config_partitions.c
@@ -1,10 +1,11 @@
/*
* EFI Boot Guard
*
- * Copyright (c) Siemens AG, 2017
+ * Copyright (c) Siemens AG, 2017-2023
*
* Authors:
* Andreas Reichel <
andreas.r...@siemens.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
@@ -17,17 +18,100 @@
#include "env_config_partitions.h"
#include "env_config_file.h"
-bool probe_config_partitions(CONFIG_PART *cfgpart)
+#define LOADER_PROT_VENDOR_GUID "4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"
+#define GUID_LEN_CHARS 36
+#define EFI_ATTR_LEN_IN_WCHAR 2
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/**
+ * Read the ESP UUID from the efivars. This only works if the bootloader
+ * implements the LoaderDevicePartUUID from the systemd bootloader interface
+ * spec. Returns a device name (e.g. sda) or NULL. The returned string needs
+ * to be freed by the caller.
+ */
+static char *get_rootdev_from_efi(void)
+{
+ const char *vendor_guid = LOADER_PROT_VENDOR_GUID;
+ const char *basepath = "/sys/firmware/efi/efivars/";
+ char part_uuid[GUID_LEN_CHARS + 1];
+ FILE *f = 0;
+ union {
+ char aschar[512];
+ char16_t aswchar[256];
+ } buffer;
+
+ // read LoaderDevicePartUUID efi variable
+ snprintf(buffer.aschar, sizeof(buffer.aschar),
+ "%s/LoaderDevicePartUUID-%s", basepath, vendor_guid);
+ if (!(f = fopen(buffer.aschar, "r"))) {
+ VERBOSE(stderr, "Error, cannot access efi var at %s.\n",
+ buffer.aschar);
+ return NULL;
+ }
+ const size_t readnb = fread(buffer.aswchar, sizeof(*buffer.aswchar),
+ ARRAY_SIZE(buffer.aswchar), f);
+ if (readnb != GUID_LEN_CHARS + EFI_ATTR_LEN_IN_WCHAR) {
+ VERBOSE(stderr, "Data in LoaderDevicePartUUID not valid\n");
+ fclose(f);
+ return NULL;
+ }
+ fclose(f);
+
+ // convert char16_t to char and lowercase uuid, skip attributes
+ for (int i = 0; i < GUID_LEN_CHARS; i++) {
+ part_uuid[i] = tolower(
+ (char)buffer.aswchar[i + EFI_ATTR_LEN_IN_WCHAR]);
+ }
+ part_uuid[GUID_LEN_CHARS] = '\0';
+
+ // resolve device based on partition uuid
+ snprintf(buffer.aschar, sizeof(buffer.aschar),
+ "/dev/disk/by-partuuid/%s", part_uuid);
+ char *devpath = realpath(buffer.aschar, NULL);
+ if (!devpath) {
+ VERBOSE(stderr, "Error, no disk in %s\n", buffer.aschar);
+ return NULL;
+ }
+ VERBOSE(stdout, "resolved ESP to %s\n", devpath);
+ // get disk name from path
+ char *partition = strrchr(devpath, '/') + 1;
+
+ // resolve parent device. As the ESP must be a primary partition, the
+ // parent is the block device.
+ snprintf(buffer.aschar, sizeof(buffer.aschar), "/sys/class/block/%s/..",
+ partition);
+ free(devpath);
+
+ // resolve to e.g. /sys/devices/pci0000:00/0000:00:1f.2/<...>/block/sda
+ char *blockpath = realpath(buffer.aschar, NULL);
+ char *_blockdev = strrchr(blockpath, '/') + 1;
+ char *blockdev = strdup(_blockdev);
+ free(blockpath);
+ return blockdev;
+}
+
+bool probe_config_partitions(CONFIG_PART *cfgpart, bool search_all_devs)
{
PedDevice *dev = NULL;
char devpath[4096];
+ char *rootdev = NULL;
int count = 0;
if (!cfgpart) {
return false;
}
- ped_device_probe_all();
+ if (!search_all_devs) {
+ if (!(rootdev = get_rootdev_from_efi())) {
+ VERBOSE(stderr, "Warning, could not determine root "
+ "dev. Search on all devices\n");
+ } else {
+ VERBOSE(stdout, "Limit probing to disk %s\n", rootdev);
+ }
+ }
+
+ ped_device_probe_all(rootdev);
+ free(rootdev);
while ((dev = ped_device_get_next(dev))) {
printf_debug("Device: %s\n", dev->model);
diff --git a/include/ebgenv.h b/include/ebgenv.h
index a60c322..a78eb0c 100644
--- a/include/ebgenv.h
+++ b/include/ebgenv.h
@@ -36,11 +36,20 @@
#define USERVAR_STANDARD_TYPE_MASK ((1ULL << 32) - 1)
+typedef struct {
+ bool search_all_devs;
+} ebgenv_opts_t;
+
typedef struct {
void *bgenv;
void *gc_registry;
+ ebgenv_opts_t opts;
} ebgenv_t;
+typedef enum { EBG_OPT_SEARCH_ALL_DEVICES } ebg_opt_t;
+
+int ebg_set_opt(ebgenv_t *e, ebg_opt_t opt, void *value);
+
/** @brief Tell the library to output information for the user.
* @param e A pointer to an ebgenv_t context.
* @param v A boolean to set verbosity.
diff --git a/include/ebgpart.h b/include/ebgpart.h
index 6687156..65b2d1a 100644
--- a/include/ebgpart.h
+++ b/include/ebgpart.h
@@ -128,7 +128,7 @@ typedef struct _PedDisk {
PedPartition *part_list;
} PedDisk;
-void ped_device_probe_all(void);
+void ped_device_probe_all(char *rootdev);
PedDevice *ped_device_get_next(const PedDevice *dev);
PedDisk *ped_disk_new(const PedDevice *dev);
PedPartition *ped_disk_next_partition(const PedDisk *pd,
diff --git a/include/env_api.h b/include/env_api.h
index 7999727..371a762 100644
--- a/include/env_api.h
+++ b/include/env_api.h
@@ -81,7 +81,7 @@ extern char16_t *str8to16(char16_t *buffer, const char *src);
extern uint32_t bgenv_crc32(uint32_t, const void *, size_t);
-extern bool bgenv_init(void);
+extern bool bgenv_init(ebgenv_t *e);
extern void bgenv_finalize(void);
extern BGENV *bgenv_open_by_index(uint32_t index);
extern BGENV *bgenv_open_oldest(void);
diff --git a/include/env_config_partitions.h b/include/env_config_partitions.h
index e676d93..e2a0466 100644
--- a/include/env_config_partitions.h
+++ b/include/env_config_partitions.h
@@ -17,4 +17,4 @@
#include <stdbool.h>
#include "env_api.h"
-bool probe_config_partitions(CONFIG_PART *cfgpart);
+bool probe_config_partitions(CONFIG_PART *cfgpart, bool search_all_devs);
diff --git a/tools/bg_printenv.c b/tools/bg_printenv.c
index 9c52505..abf745b 100644
--- a/tools/bg_printenv.c
+++ b/tools/bg_printenv.c
@@ -344,7 +344,8 @@ error_t bg_printenv(int argc, char **argv)
}
/* not in file mode */
- if (!bgenv_init()) {
+ ebgenv_t ebgenv;
+ if (!bgenv_init(&ebgenv)) {
fprintf(stderr, "Error initializing FAT environment.\n");
return 1;
}
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 375cad8..27706ac 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -418,7 +418,8 @@ error_t bg_setenv(int argc, char **argv)
}
/* not in file mode */
- if (!bgenv_init()) {
+ ebgenv_t ebgenv;
+ if (!bgenv_init(&ebgenv)) {
fprintf(stderr, "Error initializing FAT environment.\n");
return 1;
}
diff --git a/tools/ebgpart.c b/tools/ebgpart.c
index 6f60c24..fc754df 100644
--- a/tools/ebgpart.c
+++ b/tools/ebgpart.c
@@ -416,9 +416,9 @@ static int get_major_minor(char *filename, unsigned int *major, unsigned int *mi
return 0;
}
-void ped_device_probe_all(void)
+void ped_device_probe_all(char *rootdev)
{
- struct dirent *sysblockfile;
+ struct dirent *sysblockfile = NULL;
char fullname[DEV_FILENAME_LEN+16];
DIR *sysblockdir = opendir(SYSBLOCKDIR);
@@ -429,16 +429,21 @@ void ped_device_probe_all(void)
/* get all files from sysblockdir */
do {
- sysblockfile = readdir(sysblockdir);
- if (!sysblockfile) {
- break;
- }
- if (strcmp(sysblockfile->d_name, ".") == 0 ||
- strcmp(sysblockfile->d_name, "..") == 0) {
- continue;
+ char *devname = rootdev;
+ if (!rootdev) {
+ sysblockfile = readdir(sysblockdir);
+ if (!sysblockfile) {
+ break;
+ }
+ if (strcmp(sysblockfile->d_name, ".") == 0 ||
+ strcmp(sysblockfile->d_name, "..") == 0) {
+ continue;
+ }
+ devname = sysblockfile->d_name;
}
+
(void)snprintf(fullname, sizeof(fullname), "/sys/block/%s/dev",
- sysblockfile->d_name);
+ devname);
/* Get major and minor revision from /sys/block/sdX/dev */
unsigned int fmajor, fminor;
if (get_major_minor(fullname, &fmajor, &fminor) < 0) {
@@ -449,7 +454,7 @@ void ped_device_probe_all(void)
fmajor, fminor, fullname);
/* Check if this file is really in the dev directory */
(void)snprintf(fullname, sizeof(fullname), "%s/%s", DEVDIR,
- sysblockfile->d_name);
+ devname);
struct stat statbuf;
if (stat(fullname, &statbuf) == -1) {
/* Node with same name not found in /dev, thus search
--
2.39.2