summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/env-file.c2
-rw-r--r--src/basic/fs-util.c15
-rw-r--r--src/basic/fs-util.h2
-rw-r--r--src/boot/bless-boot.c191
-rw-r--r--src/boot/bootctl.c394
-rw-r--r--src/boot/efi/boot.c202
-rw-r--r--src/boot/efi/crc32.c142
-rw-r--r--src/boot/efi/crc32.h8
-rw-r--r--src/boot/efi/meson.build4
-rw-r--r--src/boot/efi/stub.c15
-rw-r--r--src/boot/efi/util.c11
-rw-r--r--src/boot/efi/util.h7
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c86
-rw-r--r--src/shared/bootspec.c934
-rw-r--r--src/shared/bootspec.h8
-rw-r--r--src/shared/dissect-image.c104
-rw-r--r--src/shared/dissect-image.h1
-rw-r--r--src/shared/gpt.h1
-rw-r--r--src/shared/meson.build1
-rw-r--r--src/shared/pe-header.h59
-rw-r--r--src/systemctl/systemctl.c24
21 files changed, 1806 insertions, 405 deletions
diff --git a/src/basic/env-file.c b/src/basic/env-file.c
index 7f10f9ad39..a1f1308a54 100644
--- a/src/basic/env-file.c
+++ b/src/basic/env-file.c
@@ -559,6 +559,6 @@ int write_env_file(const char *fname, char **l) {
r = -errno;
}
- unlink(p);
+ (void) unlink(p);
return r;
}
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 3ff8615797..0d631093b2 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -1330,6 +1330,21 @@ int fsync_path_at(int at_fd, const char *path) {
return 0;
}
+int syncfs_path(int atfd, const char *path) {
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+
+ fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK);
+ if (fd < 0)
+ return -errno;
+
+ if (syncfs(fd) < 0)
+ return -errno;
+
+ return 0;
+}
+
int open_parent(const char *path, int flags, mode_t mode) {
_cleanup_free_ char *parent = NULL;
int fd;
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 7ad030be5d..9c9044669d 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -108,4 +108,6 @@ int unlinkat_deallocate(int fd, const char *name, int flags);
int fsync_directory_of_file(int fd);
int fsync_path_at(int at_fd, const char *path);
+int syncfs_path(int atfd, const char *path);
+
int open_parent(const char *path, int flags, mode_t mode);
diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c
index 42b9618aa9..b5d110f422 100644
--- a/src/boot/bless-boot.c
+++ b/src/boot/bless-boot.c
@@ -16,9 +16,9 @@
#include "verbs.h"
#include "virt.h"
-static char *arg_path = NULL;
+static char **arg_path = NULL;
-STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_path, strv_freep);
static int help(int argc, char *argv[], void *userdata) {
@@ -27,7 +27,7 @@ static int help(int argc, char *argv[], void *userdata) {
"Mark the boot process as good or bad.\n\n"
" -h --help Show this help\n"
" --version Print version\n"
- " --path=PATH Path to the EFI System Partition (ESP)\n"
+ " --path=PATH Path to the $BOOT partition (may be used multiple times)\n"
"\n"
"Commands:\n"
" good Mark this boot as good\n"
@@ -67,7 +67,7 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case ARG_PATH:
- r = free_and_strdup(&arg_path, optarg);
+ r = strv_extend(&arg_path, optarg);
if (r < 0)
return log_oom();
break;
@@ -82,20 +82,42 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int acquire_esp(void) {
- _cleanup_free_ char *np = NULL;
+static int acquire_path(void) {
+ _cleanup_free_ char *esp_path = NULL, *xbootldr_path = NULL;
+ char **a;
int r;
- r = find_esp_and_warn(arg_path, false, &np, NULL, NULL, NULL, NULL);
- if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn in this one error case, but in all others */
- return log_error_errno(r,
- "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.");
- if (r < 0)
+ if (!strv_isempty(arg_path))
+ return 0;
+
+ r = find_esp_and_warn(NULL, false, &esp_path, NULL, NULL, NULL, NULL);
+ if (r < 0 && r != -ENOKEY) /* ENOKEY means not found, and is the only error the function won't log about on its own */
+ return r;
+
+ r = find_xbootldr_and_warn(NULL, false, &xbootldr_path, NULL);
+ if (r < 0 && r != -ENOKEY)
return r;
- free_and_replace(arg_path, np);
- log_debug("Using EFI System Partition at %s.", arg_path);
+ if (!esp_path && !xbootldr_path)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n"
+ "Alternatively, use --path= to specify path to mount point.");
+
+ if (esp_path)
+ a = strv_new(esp_path, xbootldr_path);
+ else
+ a = strv_new(xbootldr_path);
+ if (!a)
+ return log_oom();
+
+ strv_free_and_replace(arg_path, a);
+
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *j;
+
+ j = strv_join(arg_path, ":");
+ log_debug("Using %s as boot loader drop-in search path.", j);
+ }
return 0;
}
@@ -282,10 +304,9 @@ static const char *skip_slash(const char *path) {
}
static int verb_status(int argc, char *argv[], void *userdata) {
-
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
- _cleanup_close_ int fd = -1;
uint64_t left, done;
+ char **p;
int r;
r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
@@ -296,7 +317,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = acquire_esp();
+ r = acquire_path();
if (r < 0)
return r;
@@ -308,50 +329,61 @@ static int verb_status(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_oom();
- log_debug("Booted file: %s%s\n"
- "The same modified for 'good': %s%s\n"
- "The same modified for 'bad': %s%s\n",
- arg_path, path,
- arg_path, good,
- arg_path, bad);
+ log_debug("Booted file: %s\n"
+ "The same modified for 'good': %s\n"
+ "The same modified for 'bad': %s\n",
+ path,
+ good,
+ bad);
log_debug("Tries left: %" PRIu64"\n"
"Tries done: %" PRIu64"\n",
left, done);
- fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
+ STRV_FOREACH(p, arg_path) {
+ _cleanup_close_ int fd = -1;
- if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
- puts("indeterminate");
- return 0;
- }
- if (errno != ENOENT)
- return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
+ fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ continue;
- if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
- puts("good");
- return 0;
- }
- if (errno != ENOENT)
- return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
+ return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
+ }
- if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
- puts("bad");
- return 0;
+ if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
+ puts("indeterminate");
+ return 0;
+ }
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
+
+ if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
+ puts("good");
+ return 0;
+ }
+
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
+
+ if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
+ puts("bad");
+ return 0;
+ }
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad);
+
+ /* We didn't find any of the three? If so, let's try the next directory, before we give up. */
}
- if (errno != ENOENT)
- return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad);
- return log_error_errno(errno, "Couldn't determine boot state: %m");
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Couldn't determine boot state: %m");
}
static int verb_set(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL;
const char *target, *source1, *source2;
- _cleanup_close_ int fd = -1;
uint64_t done;
+ char **p;
int r;
r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix);
@@ -360,7 +392,7 @@ static int verb_set(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = acquire_esp();
+ r = acquire_path();
if (r < 0)
return r;
@@ -372,10 +404,6 @@ static int verb_set(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_oom();
- fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
-
/* Figure out what rename to what */
if (streq(argv[0], "good")) {
target = good;
@@ -392,45 +420,58 @@ static int verb_set(int argc, char *argv[], void *userdata) {
source2 = bad;
}
- r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
- if (r == -EEXIST)
- goto exists;
- else if (r == -ENOENT) {
+ STRV_FOREACH(p, arg_path) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
- r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
+ r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
if (r == -EEXIST)
goto exists;
else if (r == -ENOENT) {
- if (access(target, F_OK) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
+ r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
+ if (r == -EEXIST)
goto exists;
+ else if (r == -ENOENT) {
+
+ if (faccessat(fd, skip_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
+ goto exists;
+
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine if %s already exists: %m", target);
+
+ /* We found none of the snippets here, try the next directory */
+ continue;
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
+ else
+ log_debug("Successfully renamed '%s' to '%s'.", source2, target);
- return log_error_errno(r, "Can't find boot counter source file for '%s': %m", target);
} else if (r < 0)
- return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
+ return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
else
- log_debug("Successfully renamed '%s' to '%s'.", source2, target);
-
- } else if (r < 0)
- return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
- else
- log_debug("Successfully renamed '%s' to '%s'.", source1, target);
+ log_debug("Successfully renamed '%s' to '%s'.", source1, target);
- /* First, fsync() the directory these files are located in */
- parent = dirname_malloc(path);
- if (!parent)
- return log_oom();
+ /* First, fsync() the directory these files are located in */
+ parent = dirname_malloc(target);
+ if (!parent)
+ return log_oom();
- r = fsync_path_at(fd, skip_slash(parent));
- if (r < 0)
- log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
+ r = fsync_path_at(fd, skip_slash(parent));
+ if (r < 0)
+ log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
- /* Secondly, syncfs() the whole file system these files are located in */
- if (syncfs(fd) < 0)
- log_debug_errno(errno, "Failed to synchronize ESP, ignoring: %m");
+ /* Secondly, syncfs() the whole file system these files are located in */
+ if (syncfs(fd) < 0)
+ log_debug_errno(errno, "Failed to synchronize $BOOT partition, ignoring: %m");
- log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done);
+ log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done);
+ }
+ log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Can't find boot counter source file for '%s': %m", target);
return 1;
exists:
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 323806a534..1e0d115fe3 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -45,12 +45,20 @@
#include "verbs.h"
#include "virt.h"
-static char *arg_path = NULL;
-static bool arg_print_path = false;
+static char *arg_esp_path = NULL;
+static char *arg_xbootldr_path = NULL;
+static bool arg_print_esp_path = false;
+static bool arg_print_dollar_boot_path = false;
static bool arg_touch_variables = true;
static PagerFlags arg_pager_flags = 0;
-STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
+
+static const char *arg_dollar_boot_path(void) {
+ /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
+ return arg_xbootldr_path ?: arg_esp_path;
+}
static int acquire_esp(
bool unprivileged_mode,
@@ -62,24 +70,44 @@ static int acquire_esp(
char *np;
int r;
- /* 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). */
+ /* 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 --esp-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);
+ r = find_esp_and_warn(arg_esp_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 or /efi.\n"
- "Alternatively, use --path= to specify path to mount point.");
+ "Alternatively, use --esp-path= to specify path to mount point.");
if (r < 0)
return r;
- free_and_replace(arg_path, np);
+ free_and_replace(arg_esp_path, np);
+ log_debug("Using EFI System Partition at %s.", arg_esp_path);
- log_debug("Using EFI System Partition at %s.", arg_path);
+ return 1;
+}
- return 0;
+static int acquire_xbootldr(bool unprivileged_mode, sd_id128_t *ret_uuid) {
+ char *np;
+ int r;
+
+ r = find_xbootldr_and_warn(arg_xbootldr_path, unprivileged_mode, &np, ret_uuid);
+ if (r == -ENOKEY) {
+ log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
+ if (ret_uuid)
+ *ret_uuid = SD_ID128_NULL;
+ return 0;
+ }
+ if (r < 0)
+ return r;
+
+ free_and_replace(arg_xbootldr_path, np);
+ log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path);
+
+ return 1;
}
/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
@@ -96,6 +124,10 @@ static int get_file_version(int fd, char **v) {
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat EFI binary: %m");
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return log_error_errno(errno, "EFI binary is not a regular file: %m");
+
if (st.st_size < 27) {
*v = NULL;
return 0;
@@ -112,8 +144,7 @@ static int get_file_version(int fd, char **v) {
e = memmem(s, st.st_size - (s - buf), " ####", 5);
if (!e || e - s < 3) {
- log_error("Malformed version string.");
- r = -EINVAL;
+ r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string.");
goto finish;
}
@@ -131,10 +162,13 @@ finish:
}
static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
- char *p;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
- int r = 0, c = 0;
+ int c = 0, r;
+ char *p;
+
+ assert(esp_path);
+ assert(path);
p = strjoina(esp_path, "/", path);
d = opendir(p);
@@ -146,8 +180,8 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
}
FOREACH_DIRENT(de, d, break) {
- _cleanup_close_ int fd = -1;
_cleanup_free_ char *v = NULL;
+ _cleanup_close_ int fd = -1;
if (!endswith_no_case(de->d_name, ".efi"))
continue;
@@ -166,6 +200,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
else
printf(" File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name);
+
c++;
}
@@ -188,20 +223,22 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
printf("\n");
r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
+ if (r < 0)
+ goto finish;
if (r == 0)
log_info("systemd-boot not installed in ESP.");
- else if (r < 0)
- return r;
r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
+ if (r < 0)
+ goto finish;
if (r == 0)
log_info("No default/fallback boot loader installed in ESP.");
- else if (r < 0)
- return r;
- printf("\n");
+ r = 0;
- return 0;
+finish:
+ printf("\n");
+ return r;
}
static int print_efi_option(uint16_t id, bool in_order) {
@@ -316,11 +353,34 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) {
return 0;
}
-static int status_entries(const char *esp_path, sd_id128_t partition) {
+static int status_entries(
+ const char *esp_path,
+ sd_id128_t esp_partition_uuid,
+ const char *xbootldr_path,
+ sd_id128_t xbootldr_partition_uuid) {
+
_cleanup_(boot_config_free) BootConfig config = {};
+ sd_id128_t dollar_boot_partition_uuid;
+ const char *dollar_boot_path;
int r;
- r = boot_entries_load_config(esp_path, &config);
+ assert(esp_path || xbootldr_path);
+
+ if (xbootldr_path) {
+ dollar_boot_path = xbootldr_path;
+ dollar_boot_partition_uuid = xbootldr_partition_uuid;
+ } else {
+ dollar_boot_path = esp_path;
+ dollar_boot_partition_uuid = esp_partition_uuid;
+ }
+
+ printf("Boot Loader Entries:\n"
+ " $BOOT: %s", dollar_boot_path);
+ if (!sd_id128_is_null(dollar_boot_partition_uuid))
+ printf(" (/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x)", SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
+ printf("\n\n");
+
+ r = boot_entries_load_config(esp_path, xbootldr_path, &config);
if (r < 0)
return r;
@@ -386,10 +446,8 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t
"Skipping \"%s\", since it's owned by another boot loader.",
to);
- if (compare_version(a, b) < 0) {
- log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to);
- return -ESTALE;
- }
+ if (compare_version(a, b) < 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since a newer boot loader version exists already.", to);
return 0;
}
@@ -468,20 +526,21 @@ static int mkdir_one(const char *prefix, const char *suffix) {
return 0;
}
-static const char *efi_subdirs[] = {
+static const char *const esp_subdirs[] = {
"EFI",
"EFI/systemd",
"EFI/BOOT",
"loader",
- "loader/entries",
+ /* Note that "/loader/entries" is not listed here, since it should be placed in $BOOT, which might
+ * not necessarily be the ESP */
NULL
};
-static int create_dirs(const char *esp_path) {
- const char **i;
+static int create_esp_subdirs(const char *esp_path) {
+ const char *const *i;
int r;
- STRV_FOREACH(i, efi_subdirs) {
+ STRV_FOREACH(i, esp_subdirs) {
r = mkdir_one(esp_path, *i);
if (r < 0)
return r;
@@ -520,22 +579,11 @@ static int install_binaries(const char *esp_path, bool force) {
_cleanup_closedir_ DIR *d = NULL;
int r = 0;
- if (force) {
- /* Don't create any of these directories when we are
- * just updating. When we update we'll drop-in our
- * files (unless there are newer ones already), but we
- * won't create the directories for them in the first
- * place. */
- r = create_dirs(esp_path);
- if (r < 0)
- return r;
- }
-
d = opendir(BOOTLIBDIR);
if (!d)
return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
- FOREACH_DIRENT(de, d, break) {
+ FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \""BOOTLIBDIR"\": %m")) {
int k;
if (!endswith_no_case(de->d_name, ".efi"))
@@ -751,18 +799,36 @@ static int rmdir_one(const char *prefix, const char *suffix) {
p = strjoina(prefix, "/", suffix);
if (rmdir(p) < 0) {
- if (!IN_SET(errno, ENOENT, ENOTEMPTY))
- return log_error_errno(errno, "Failed to remove \"%s\": %m", p);
+ bool ignore = IN_SET(errno, ENOENT, ENOTEMPTY);
+
+ log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to remove directory \"%s\": %m", p);
+ if (!ignore)
+ return -errno;
} else
log_info("Removed \"%s\".", p);
return 0;
}
+static int remove_esp_subdirs(const char *esp_path) {
+ size_t i;
+ int r = 0;
+
+ for (i = ELEMENTSOF(esp_subdirs)-1; i > 0; i--) {
+ int q;
+
+ q = rmdir_one(esp_path, esp_subdirs[i-1]);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
+
+ return r;
+}
+
static int remove_binaries(const char *esp_path) {
char *p;
int r, q;
- unsigned i;
p = strjoina(esp_path, "/EFI/systemd");
r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
@@ -771,15 +837,31 @@ static int remove_binaries(const char *esp_path) {
if (q < 0 && r == 0)
r = q;
- for (i = ELEMENTSOF(efi_subdirs)-1; i > 0; i--) {
- q = rmdir_one(esp_path, efi_subdirs[i-1]);
- if (q < 0 && r == 0)
- r = q;
- }
-
return r;
}
+static int remove_loader_config(const char *esp_path) {
+ const char *p;
+
+ assert(esp_path);
+
+ p = strjoina(esp_path, "/loader/loader.conf");
+ if (unlink(p) < 0) {
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to unlink file \"%s\": %m", p);
+ if (errno != ENOENT)
+ return -errno;
+ } else
+ log_info("Removed \"%s\".", p);
+
+ return 0;
+}
+
+static int remove_entries_directory(const char *dollar_boot_path) {
+ assert(dollar_boot_path);
+
+ return rmdir_one(dollar_boot_path, "/loader/entries");
+}
+
static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
uint16_t slot;
int r;
@@ -802,7 +884,6 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
}
static int install_loader_config(const char *esp_path) {
-
char machine_string[SD_ID128_STRING_MAX];
_cleanup_(unlink_and_freep) char *t = NULL;
_cleanup_fclose_ FILE *f = NULL;
@@ -810,15 +891,14 @@ static int install_loader_config(const char *esp_path) {
const char *p;
int r, fd;
- r = sd_id128_get_machine(&machine_id);
- if (r < 0)
- return log_error_errno(r, "Failed to get machine id: %m");
-
p = strjoina(esp_path, "/loader/loader.conf");
-
if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
return 0;
+ r = sd_id128_get_machine(&machine_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get machine id: %m");
+
fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t);
if (fd < 0)
return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
@@ -844,10 +924,15 @@ static int install_loader_config(const char *esp_path) {
return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
t = mfree(t);
-
return 1;
}
+static int install_entries_directory(const char *dollar_boot_path) {
+ assert(dollar_boot_path);
+
+ return mkdir_one(dollar_boot_path, "/loader/entries");
+}
+
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@@ -858,21 +943,23 @@ static int help(int argc, char *argv[], void *userdata) {
printf("%s [COMMAND] [OPTIONS...]\n\n"
"Install, update or remove the systemd-boot EFI boot manager.\n\n"
- " -h --help Show this help\n"
- " --version Print version\n"
- " --path=PATH Path to the EFI System Partition (ESP)\n"
- " -p --print-path Print path to the EFI partition\n"
- " --no-variables Don't touch EFI variables\n"
- " --no-pager Do not pipe output into a pager\n"
+ " -h --help Show this help\n"
+ " --version Print version\n"
+ " --esp-path=PATH Path to the EFI System Partition (ESP)\n"
+ " --boot-path=PATH Path to the $BOOT partition\n"
+ " -p --print-esp-path Print path to the EFI System Partition\n"
+ " --print-boot-path Print path to the $BOOT partition\n"
+ " --no-variables Don't touch EFI variables\n"
+ " --no-pager Do not pipe output into a pager\n"
"\nBoot Loader Commands:\n"
- " status Show status of installed systemd-boot and EFI variables\n"
- " install Install systemd-boot to the ESP and EFI variables\n"
- " update Update systemd-boot in the ESP and EFI variables\n"
- " remove Remove systemd-boot from the ESP and EFI variables\n"
+ " status Show status of installed systemd-boot and EFI variables\n"
+ " install Install systemd-boot to the ESP and EFI variables\n"
+ " update Update systemd-boot in the ESP and EFI variables\n"
+ " remove Remove systemd-boot from the ESP and EFI variables\n"
"\nBoot Loader Entries Commands:\n"
- " list List boot loader entries\n"
- " set-default ID Set default boot loader entry\n"
- " set-oneshot ID Set default boot loader entry, for next boot only\n"
+ " list List boot loader entries\n"
+ " set-default ID Set default boot loader entry\n"
+ " set-oneshot ID Set default boot loader entry, for next boot only\n"
"\nSee the %s for details.\n"
, program_invocation_short_name
, link);
@@ -882,19 +969,25 @@ static int help(int argc, char *argv[], void *userdata) {
static int parse_argv(int argc, char *argv[]) {
enum {
- ARG_PATH = 0x100,
+ ARG_ESP_PATH = 0x100,
+ ARG_BOOT_PATH,
+ ARG_PRINT_BOOT_PATH,
ARG_VERSION,
ARG_NO_VARIABLES,
ARG_NO_PAGER,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "path", required_argument, NULL, ARG_PATH },
- { "print-path", no_argument, NULL, 'p' },
- { "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "esp-path", required_argument, NULL, ARG_ESP_PATH },
+ { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */
+ { "boot-path", required_argument, NULL, ARG_BOOT_PATH },
+ { "print-esp-path", no_argument, NULL, 'p' },
+ { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */
+ { "print-boot-path", no_argument, NULL, ARG_PRINT_BOOT_PATH },
+ { "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{}
};
@@ -913,14 +1006,24 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERSION:
return version();
- case ARG_PATH:
- r = free_and_strdup(&arg_path, optarg);
+ case ARG_ESP_PATH:
+ r = free_and_strdup(&arg_esp_path, optarg);
+ if (r < 0)
+ return log_oom();
+ break;
+
+ case ARG_BOOT_PATH:
+ r = free_and_strdup(&arg_xbootldr_path, optarg);
if (r < 0)
return log_oom();
break;
case 'p':
- arg_print_path = true;
+ arg_print_esp_path = true;
+ break;
+
+ case ARG_PRINT_BOOT_PATH:
+ arg_print_dollar_boot_path = true;
break;
case ARG_NO_VARIABLES:
@@ -950,23 +1053,33 @@ static void read_loader_efi_var(const char *name, char **var) {
}
static int verb_status(int argc, char *argv[], void *userdata) {
-
- sd_id128_t uuid = SD_ID128_NULL;
+ sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
int r, k;
- r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
-
- if (arg_print_path) {
+ r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &esp_uuid);
+ if (arg_print_esp_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");
+ return log_error_errno(r, "Failed to determine ESP location: %m");
if (r < 0)
return r;
- puts(arg_path);
- return 0;
+ puts(arg_esp_path);
}
+ r = acquire_xbootldr(geteuid() != 0, &xbootldr_uuid);
+ if (arg_print_dollar_boot_path) {
+ if (r == -EACCES)
+ return log_error_errno(r, "Failed to determine XBOOTLDR location: %m");
+ if (r < 0)
+ return r;
+
+ puts(arg_dollar_boot_path());
+ }
+
+ if (arg_print_esp_path || arg_print_dollar_boot_path)
+ 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 */
@@ -1037,8 +1150,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
} else
printf("System:\n Not booted with EFI\n\n");
- if (arg_path) {
- k = status_binaries(arg_path, uuid);
+ if (arg_esp_path) {
+ k = status_binaries(arg_esp_path, esp_uuid);
if (k < 0)
r = k;
}
@@ -1049,8 +1162,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
r = k;
}
- if (arg_path) {
- k = status_entries(arg_path, uuid);
+ if (arg_esp_path || arg_xbootldr_path) {
+ k = status_entries(arg_esp_path, esp_uuid, arg_xbootldr_path, xbootldr_uuid);
if (k < 0)
r = k;
}
@@ -1061,20 +1174,25 @@ 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 = {};
_cleanup_free_ char **found_by_loader = NULL;
- sd_id128_t uuid = SD_ID128_NULL;
int r;
/* 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 = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
+ r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, NULL);
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;
- r = boot_entries_load_config(arg_path, &config);
+ r = acquire_xbootldr(geteuid() != 0, NULL);
+ if (r == -EACCES)
+ return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
+ if (r < 0)
+ return r;
+
+ r = boot_entries_load_config(arg_esp_path, arg_xbootldr_path, &config);
if (r < 0)
return r;
@@ -1114,24 +1232,25 @@ static int verb_list(int argc, char *argv[], void *userdata) {
return 0;
}
-static int sync_esp(void) {
- _cleanup_close_ int fd = -1;
+static int sync_everything(void) {
+ int ret = 0, k;
- if (!arg_path)
- return 0;
-
- fd = open(arg_path, O_CLOEXEC|O_DIRECTORY|O_RDONLY);
- if (fd < 0)
- return log_error_errno(errno, "Couldn't open ESP '%s' for synchronization: %m", arg_path);
+ if (arg_esp_path) {
+ k = syncfs_path(AT_FDCWD, arg_esp_path);
+ if (k < 0)
+ ret = log_error_errno(k, "Failed to synchronize the ESP '%s': %m", arg_esp_path);
+ }
- if (syncfs(fd) < 0)
- return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path);
+ if (arg_xbootldr_path) {
+ k = syncfs_path(AT_FDCWD, arg_xbootldr_path);
+ if (k < 0)
+ ret = log_error_errno(k, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path);
+ }
- return 1;
+ return ret;
}
static int verb_install(int argc, char *argv[], void *userdata) {
-
sd_id128_t uuid = SD_ID128_NULL;
uint64_t pstart = 0, psize = 0;
uint32_t part = 0;
@@ -1142,24 +1261,41 @@ static int verb_install(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
+ r = acquire_xbootldr(false, NULL);
+ if (r < 0)
+ return r;
+
install = streq(argv[0], "install");
RUN_WITH_UMASK(0002) {
- r = install_binaries(arg_path, install);
+ if (install) {
+ /* Don't create any of these directories when we are just updating. When we update
+ * we'll drop-in our files (unless there are newer ones already), but we won't create
+ * the directories for them in the first place. */
+ r = create_esp_subdirs(arg_esp_path);
+ if (r < 0)
+ return r;
+ }
+
+ r = install_binaries(arg_esp_path, install);
if (r < 0)
return r;
if (install) {
- r = install_loader_config(arg_path);
+ r = install_loader_config(arg_esp_path);
+ if (r < 0)
+ return r;
+
+ r = install_entries_directory(arg_dollar_boot_path());
if (r < 0)
return r;
}
}
- (void) sync_esp();
+ (void) sync_everything();
if (arg_touch_variables)
- r = install_variables(arg_path,
+ r = install_variables(arg_esp_path,
part, pstart, psize, uuid,
"/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
install);
@@ -1169,21 +1305,35 @@ static int verb_install(int argc, char *argv[], void *userdata) {
static int verb_remove(int argc, char *argv[], void *userdata) {
sd_id128_t uuid = SD_ID128_NULL;
- int r;
+ int r, q;
r = acquire_esp(false, NULL, NULL, NULL, &uuid);
if (r < 0)
return r;
- r = remove_binaries(arg_path);
+ r = acquire_xbootldr(false, NULL);
+ if (r < 0)
+ return r;
- (void) sync_esp();
+ r = remove_binaries(arg_esp_path);
- if (arg_touch_variables) {
- int q;
+ q = remove_loader_config(arg_esp_path);
+ if (q < 0 && r >= 0)
+ r = q;
+
+ q = remove_entries_directory(arg_dollar_boot_path());
+ if (q < 0 && r >= 0)
+ r = q;
+ q = remove_esp_subdirs(arg_esp_path);
+ if (q < 0 && r >= 0)
+ r = q;
+
+ (void) sync_everything();
+
+ if (arg_touch_variables) {
q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
- if (q < 0 && r == 0)
+ if (q < 0 && r >= 0)
r = q;
}
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 9bf6895831..c7ba088761 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -1,9 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <efi.h>
+#include <efigpt.h>
#include <efilib.h>
#include "console.h"
+#include "crc32.h"
#include "disk.h"
#include "graphics.h"
#include "linux.h"
@@ -1316,7 +1318,7 @@ static VOID config_entry_add_from_file(
entry->loader = stra_to_path(value);
/* do not add an entry for ourselves */
- if (StriCmp(entry->loader, loaded_image_path) == 0) {
+ if (loaded_image_path && StriCmp(entry->loader, loaded_image_path) == 0) {
entry->type = LOADER_UNDEFINED;
break;
}
@@ -1836,7 +1838,7 @@ static VOID config_entry_add_osx(Config *config) {
static VOID config_entry_add_linux(
Config *config,
- EFI_LOADED_IMAGE *loaded_image,
+ EFI_HANDLE *device,
EFI_FILE *root_dir) {
EFI_FILE_HANDLE linux_dir;
@@ -1926,7 +1928,7 @@ static VOID config_entry_add_linux(
conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
- entry = config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path);
+ entry = config_entry_add_loader(config, device, LOADER_LINUX, conf, 'l', os_name, path);
FreePool(content);
content = NULL;
@@ -1955,6 +1957,193 @@ static VOID config_entry_add_linux(
uefi_call_wrapper(linux_dir->Close, 1, linux_dir);
}
+/* Note that this is in GUID format, i.e. the first 32bit, and the following pair of 16bit are byteswapped. */
+static const UINT8 xbootldr_guid[16] = {
+ 0xff, 0xc2, 0x13, 0xbc, 0xe6, 0x59, 0x62, 0x42, 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72
+};
+
+EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
+ EFI_DEVICE_PATH *parent;
+ UINTN len;
+
+ len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
+ parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
+ CopyMem(parent, path, len);
+ CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH));
+
+ return parent;
+}
+
+static VOID config_load_xbootldr(
+ Config *config,
+ EFI_HANDLE *device) {
+
+ EFI_DEVICE_PATH *partition_path, *node, *disk_path, *copy;
+ UINT32 found_partition_number = (UINT32) -1;
+ UINT64 found_partition_start = (UINT64) -1;
+ UINT64 found_partition_size = (UINT64) -1;
+ UINT8 found_partition_signature[16] = {};
+ EFI_HANDLE new_device;
+ EFI_FILE *root_dir;
+ EFI_STATUS r;
+
+ partition_path = DevicePathFromHandle(device);
+ if (!partition_path)
+ return;
+
+ for (node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
+ EFI_HANDLE disk_handle;
+ EFI_BLOCK_IO *block_io;
+ EFI_DEVICE_PATH *p;
+ UINTN nr;
+
+ /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
+ * devices */
+ if (DevicePathType(node) != MESSAGING_DEVICE_PATH)
+ continue;
+
+ /* Determine the device path one level up */
+ disk_path = path_parent(partition_path, node);
+ p = disk_path;
+ r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle);
+ if (EFI_ERROR(r))
+ continue;
+
+ r = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io);
+ if (EFI_ERROR(r))
+ continue;
+
+ /* Filter out some block devices early. (We only care about block devices that aren't
+ * partitions themselves — we look for GPT partition tables to parse after all —, and only
+ * those which contain a medium and have at least 2 blocks.) */
+ if (block_io->Media->LogicalPartition ||
+ !block_io->Media->MediaPresent ||
+ block_io->Media->LastBlock <= 1)
+ continue;
+
+ /* Try both copies of the GPT header, in case one is corrupted */
+ for (nr = 0; nr < 2; nr++) {
+ _cleanup_freepool_ EFI_PARTITION_ENTRY* entries = NULL;
+ union {
+ EFI_PARTITION_TABLE_HEADER gpt_header;
+ uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512];
+ } gpt_header_buffer;
+ UINT64 where;
+ UINTN i, sz;
+ UINT32 c;
+
+ if (nr == 0)
+ where = 1; /* Read the first copy at LBA 1 */
+ else
+ where = block_io->Media->LastBlock; /* Read the second copy at the very last LBA of this block device */
+
+ /* Read the GPT header */
+ r = uefi_call_wrapper(block_io->ReadBlocks, 5, block_io, block_io->Media->MediaId, where, sizeof(gpt_header_buffer), &gpt_header_buffer);
+ if (EFI_ERROR(r))
+ continue;
+
+ /* Some superficial validation of the GPT header */
+ if (CompareMem(&gpt_header_buffer.gpt_header.Header.Signature, "EFI PART", sizeof(gpt_header_buffer.gpt_header.Header.Signature)) != 0)
+ continue;
+
+ if (gpt_header_buffer.gpt_header.Header.HeaderSize < 92 || gpt_header_buffer.gpt_header.Header.HeaderSize > 512)
+ continue;
+
+ if (gpt_header_buffer.gpt_header.Header.Revision != 0x00010000U)
+ continue;
+
+ /* Calculate CRC check */
+ c = ~crc32_exclude_offset((UINT32) -1, (const UINT8*) &gpt_header_buffer, gpt_header_buffer.gpt_header.Header.HeaderSize,
+ OFFSETOF(EFI_PARTITION_TABLE_HEADER, Header.CRC32), sizeof(gpt_header_buffer.gpt_header.Header.CRC32));
+ if (c != gpt_header_buffer.gpt_header.Header.CRC32)
+ continue;
+
+ if (gpt_header_buffer.gpt_header.MyLBA != where)
+ continue;
+
+ if (gpt_header_buffer.gpt_header.SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY))
+ continue;
+
+ if (gpt_header_buffer.gpt_header.NumberOfPartitionEntries <= 0 || gpt_header_buffer.gpt_header.NumberOfPartitionEntries > 1024)
+ continue;
+
+ /* Now load the GPT entry table */
+ sz = ((gpt_header_buffer.gpt_header.SizeOfPartitionEntry * gpt_header_buffer.gpt_header.NumberOfPartitionEntries + 511) / 512) * 512;
+ entries = AllocatePool(sz);
+
+ r = uefi_call_wrapper(block_io->ReadBlocks, 5, block_io, block_io->Media->MediaId, gpt_header_buffer.gpt_header.PartitionEntryLBA, sz, entries);
+ if (EFI_ERROR(r))
+ continue;
+
+ /* Calculate CRC of entries array, too */
+ c = ~crc32((UINT32) -1, entries, sz);
+ if (c != gpt_header_buffer.gpt_header.PartitionEntryArrayCRC32)
+ continue;
+
+ for (i = 0; i < gpt_header_buffer.gpt_header.NumberOfPartitionEntries; i++) {
+ EFI_PARTITION_ENTRY *entry;
+
+ entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + gpt_header_buffer.gpt_header.SizeOfPartitionEntry * i);
+
+ if (CompareMem(&entry->PartitionTypeGUID, xbootldr_guid, 16) == 0) {
+ UINT64 end;
+
+ /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */
+ CopyMem(&found_partition_start, &entry->StartingLBA, sizeof(found_partition_start));
+ CopyMem(&end, &entry->EndingLBA, sizeof(end));
+
+ if (end < found_partition_start) /* Bogus? */
+ continue;
+
+ found_partition_size = end - found_partition_start + 1;
+ CopyMem(found_partition_signature, &entry->UniquePartitionGUID, sizeof(found_partition_signature));
+
+ found_partition_number = i + 1;
+ goto found;
+ }
+ }
+
+ break; /* This GPT was fully valid, but we didn't find what we are looking for. This
+ * means there's no reason to check the second copy of the GPT header */
+ }
+ }
+
+ return; /* Not found */
+
+found:
+ copy = DuplicateDevicePath(partition_path);
+
+ /* Patch in the data we found */
+ for (node = copy; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
+ HARDDRIVE_DEVICE_PATH *hd;
+
+ if (DevicePathType(node) != MEDIA_DEVICE_PATH)
+ continue;
+
+ if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
+ continue;
+
+ hd = (HARDDRIVE_DEVICE_PATH*) node;
+ hd->PartitionNumber = found_partition_number;
+ hd->PartitionStart = found_partition_start;
+ hd->PartitionSize = found_partition_size;
+ CopyMem(hd->Signature, found_partition_signature, sizeof(hd->Signature));
+ hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
+ hd->SignatureType = SIGNATURE_TYPE_GUID;
+ }
+
+ r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &copy, &new_device);
+ if (EFI_ERROR(r))
+ return;
+
+ root_dir = LibOpenRoot(new_device);
+ if (!root_dir)
+ return;
+
+ config_entry_add_linux(config, new_device, root_dir);
+ config_load_entries(config, new_device, root_dir, NULL);
+}
+
static EFI_STATUS image_start(
EFI_HANDLE parent_image,
const Config *config,
@@ -2121,7 +2310,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
root_dir = LibOpenRoot(loaded_image->DeviceHandle);
if (!root_dir) {
- Print(L"Unable to open root directory: %r ", err);
+ Print(L"Unable to open root directory.");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return EFI_LOAD_ERROR;
}
@@ -2142,11 +2331,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
config_load_defaults(&config, root_dir);
/* scan /EFI/Linux/ directory */
- config_entry_add_linux(&config, loaded_image, root_dir);
+ config_entry_add_linux(&config, loaded_image->DeviceHandle, root_dir);
/* scan /loader/entries/\*.conf files */
config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path);
+ /* Similar, but on any XBOOTLDR partition */
+ config_load_xbootldr(&config, loaded_image->DeviceHandle);
+
/* sort entries after version number */
config_sort_entries(&config);
diff --git a/src/boot/efi/crc32.c b/src/boot/efi/crc32.c
new file mode 100644
index 0000000000..46b9aeea90
--- /dev/null
+++ b/src/boot/efi/crc32.c
@@ -0,0 +1,142 @@
+/* This is copied from util-linux, which in turn copied in the version from Gary S. Brown */
+
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1.
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way,
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to high-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly.
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera-
+ * tions for all combinations of data and CRC register values.
+ *
+ * The values must be right-shifted by eight bits by the "updcrc"
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions.
+ * polynomial $edb88320
+ *
+ */
+
+#include "crc32.h"
+
+static const UINT32 crc32_tab[] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+static inline UINT32 crc32_add_char(UINT32 crc, UINT8 c) {
+ return crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8);
+}
+
+/*
+ * This a generic crc32() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len) {
+ const UINT8 *p = buf;
+ UINT32 crc = seed;
+
+ while (len > 0) {
+ crc = crc32_add_char(crc, *p++);
+ len--;
+ }
+
+ return crc;
+}
+
+UINT32 crc32_exclude_offset(
+ UINT32 seed,
+ const VOID *buf,
+ UINTN len,
+ UINTN exclude_off,
+ UINTN exclude_len) {
+
+ const UINT8 *p = buf;
+ UINT32 crc = seed;
+ UINTN i;
+
+ for (i = 0; i < len; i++) {
+ UINT8 x = *p++;
+
+ if (i >= exclude_off && i < exclude_off + exclude_len)
+ x = 0;
+
+ crc = crc32_add_char(crc, x);
+ }
+
+ return crc;
+}
diff --git a/src/boot/efi/crc32.h b/src/boot/efi/crc32.h
new file mode 100644
index 0000000000..64150ee948
--- /dev/null
+++ b/src/boot/efi/crc32.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <efi.h>
+#include <efilib.h>
+
+UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len);
+UINT32 crc32_exclude_offset(UINT32 seed, const VOID *buf, UINTN len, UINTN exclude_off, UINTN exclude_len);
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 2140151844..0ae3191635 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -2,14 +2,15 @@
efi_headers = files('''
console.h
+ crc32.h
disk.h
graphics.h
linux.h
measure.h
pe.h
+ shim.h
splash.h
util.h
- shim.h
'''.split())
common_sources = '''
@@ -24,6 +25,7 @@ systemd_boot_sources = '''
boot.c
console.c
shim.c
+ crc32.c
'''.split()
stub_sources = '''
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 2a60f38bf7..26c204fb43 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -87,12 +87,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
#endif
}
- /* export the device path this image is started from */
- if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
- efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
+ /* Export the device path this image is started from, if it's not set yet */
+ if (efivar_get_raw(&loader_guid, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS)
+ if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
+ efivar_set(L"LoaderDevicePartUUID", uuid, FALSE);
/* if LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from UEFI */
- if (efivar_get_raw(&global_guid, L"LoaderImageIdentifier", &b, &size) != EFI_SUCCESS) {
+ if (efivar_get_raw(&loader_guid, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS) {
_cleanup_freepool_ CHAR16 *s;
s = DevicePathToStr(loaded_image->FilePath);
@@ -100,7 +101,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
/* if LoaderFirmwareInfo is not set, let's set it */
- if (efivar_get_raw(&global_guid, L"LoaderFirmwareInfo", &b, &size) != EFI_SUCCESS) {
+ if (efivar_get_raw(&loader_guid, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
_cleanup_freepool_ CHAR16 *s;
s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
@@ -108,7 +109,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
/* ditto for LoaderFirmwareType */
- if (efivar_get_raw(&global_guid, L"LoaderFirmwareType", &b, &size) != EFI_SUCCESS) {
+ if (efivar_get_raw(&loader_guid, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
_cleanup_freepool_ CHAR16 *s;
s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
@@ -116,7 +117,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
/* add StubInfo */
- if (efivar_get_raw(&global_guid, L"StubInfo", &b, &size) != EFI_SUCCESS)
+ if (efivar_get_raw(&loader_guid, L"StubInfo", NULL, NULL) != EFI_SUCCESS)
efivar_set(L"StubInfo", L"systemd-stub " GIT_VERSION, FALSE);
if (szs[3] > 0)
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index f1f1674c59..bc1d4ae5c4 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -117,6 +117,9 @@ EFI_STATUS efivar_get(const CHAR16 *name, CHAR16 **value) {
if ((size % 2) != 0)
return EFI_INVALID_PARAMETER;
+ if (!value)
+ return EFI_SUCCESS;
+
/* Return buffer directly if it happens to be NUL terminated already */
if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
*value = (CHAR16*) buf;
@@ -141,7 +144,7 @@ EFI_STATUS efivar_get_int(const CHAR16 *name, UINTN *i) {
EFI_STATUS err;
err = efivar_get(name, &val);
- if (!EFI_ERROR(err))
+ if (!EFI_ERROR(err) && i)
*i = Atoi(val);
return err;
@@ -159,8 +162,10 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **bu
err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf);
if (!EFI_ERROR(err)) {
- *buffer = buf;
- buf = NULL;
+
+ if (buffer)
+ *buffer = TAKE_PTR(buf);
+
if (size)
*size = l;
}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index 4ba7050a91..8c5e35ad25 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -55,3 +55,10 @@ const EFI_GUID loader_guid;
#define UINTN_MAX (~(UINTN)0)
#define INTN_MAX ((INTN)(UINTN_MAX>>1))
+
+#define TAKE_PTR(ptr) \
+ ({ \
+ typeof(ptr) _ptr_ = (ptr); \
+ (ptr) = NULL; \
+ _ptr_; \
+ })
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 65c504e10f..0f1e184eea 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -323,7 +323,6 @@ static int add_swap(const char *path) {
return generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, "wants", name);
}
-#if ENABLE_EFI
static int add_automount(
const char *id,
const char *what,
@@ -385,8 +384,43 @@ static int add_automount(
return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
}
-static int add_esp(DissectedPartition *p) {
- const char *esp;
+static int add_xbootldr(DissectedPartition *p) {
+ int r;
+
+ assert(p);
+
+ if (in_initrd()) {
+ log_debug("In initrd, ignoring the XBOOTLDR partition.");
+ return 0;
+ }
+
+ r = fstab_is_mount_point("/boot");
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse fstab: %m");
+ if (r > 0) {
+ log_debug("/boot specified in fstab, ignoring XBOOTLDR partition.");
+ return 0;
+ }
+
+ r = path_is_busy("/boot");
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ return add_automount("boot",
+ p->node,
+ "/boot",
+ p->fstype,
+ true,
+ "umask=0077",
+ "Boot Loader Partition",
+ 120 * USEC_PER_SEC);
+}
+
+#if ENABLE_EFI
+static int add_esp(DissectedPartition *p, bool has_xbootldr) {
+ const char *esp_path = NULL, *id = NULL;
int r;
assert(p);
@@ -396,21 +430,37 @@ static int add_esp(DissectedPartition *p) {
return 0;
}
- /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
- esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot";
+ /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice, but
+ * only if there's no explicit XBOOTLDR partition around. */
+ if (access("/efi", F_OK) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine whether /efi exists: %m");
+
+ /* Use /boot as fallback, but only if there's no XBOOTLDR partition */
+ if (!has_xbootldr) {
+ esp_path = "/boot";
+ id = "boot";
+ }
+ }
+ if (!esp_path)
+ esp_path = "/efi";
+ if (!id)
+ id = "efi";
/* We create an .automount which is not overridden by the .mount from the fstab generator. */
- r = fstab_is_mount_point(esp);
+ r = fstab_is_mount_point(esp_path);
if (r < 0)
return log_error_errno(r, "Failed to parse fstab: %m");
if (r > 0) {
- log_debug("%s specified in fstab, ignoring.", esp);
+ log_debug("%s specified in fstab, ignoring.", esp_path);
return 0;
}
- r = path_is_busy(esp);
- if (r != 0)
- return r < 0 ? r : 0;
+ r = path_is_busy(esp_path);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
if (is_efi_boot()) {
sd_id128_t loader_uuid;
@@ -426,15 +476,15 @@ static int add_esp(DissectedPartition *p) {
return log_error_errno(r, "Failed to read ESP partition UUID: %m");
if (!sd_id128_equal(p->uuid, loader_uuid)) {
- log_debug("Partition for %s does not appear to be the partition we are booted from.", esp);
+ log_debug("Partition for %s does not appear to be the partition we are booted from.", p->node);
return 0;
}
} else
log_debug("Not an EFI boot, skipping ESP check.");
- return add_automount("boot",
+ return add_automount(id,
p->node,
- esp,
+ esp_path,
p->fstype,
true,
"umask=0077",
@@ -442,7 +492,7 @@ static int add_esp(DissectedPartition *p) {
120 * USEC_PER_SEC);
}
#else
-static int add_esp(DissectedPartition *p) {
+static int add_esp(DissectedPartition *p, bool has_xbootldr) {
return 0;
}
#endif
@@ -570,8 +620,14 @@ static int enumerate_partitions(dev_t devnum) {
r = k;
}
+ if (m->partitions[PARTITION_XBOOTLDR].found) {
+ k = add_xbootldr(m->partitions + PARTITION_XBOOTLDR);
+ if (k < 0)
+ r = k;
+ }
+
if (m->partitions[PARTITION_ESP].found) {
- k = add_esp(m->partitions + PARTITION_ESP);
+ k = add_esp(m->partitions + PARTITION_ESP, m->partitions[PARTITION_XBOOTLDR].found);
if (k < 0)
r = k;
}
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index 7e276f1edf..53ab042404 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -3,6 +3,7 @@
#include <stdio.h>
#include <linux/magic.h>
+#include "sd-device.h"
#include "sd-id128.h"
#include "alloc-util.h"
@@ -11,15 +12,19 @@
#include "conf-files.h"
#include "def.h"
#include "device-nodes.h"
+#include "dirent-util.h"
#include "efivars.h"
+#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pe-header.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
+#include "unaligned.h"
#include "virt.h"
static void boot_entry_free(BootEntry *entry) {
@@ -27,6 +32,7 @@ static void boot_entry_free(BootEntry *entry) {
free(entry->id);
free(entry->path);
+ free(entry->root);
free(entry->title);
free(entry->show_title);
free(entry->version);
@@ -39,21 +45,24 @@ static void boot_entry_free(BootEntry *entry) {
free(entry->device_tree);
}
-static int boot_entry_load(const char *path, BootEntry *entry) {
+static int boot_entry_load(
+ const char *root,
+ const char *path,
+ BootEntry *entry) {
+
_cleanup_(boot_entry_free) BootEntry tmp = {};
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
char *b, *c;
int r;
+ assert(root);
assert(path);
assert(entry);
c = endswith_no_case(path, ".conf");
- if (!c) {
- log_error("Invalid loader entry filename: %s", path);
- return -EINVAL;
- }
+ if (!c)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
b = basename(path);
tmp.id = strndup(b, c - b);
@@ -64,6 +73,10 @@ static int boot_entry_load(const char *path, BootEntry *entry) {
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);
@@ -115,7 +128,7 @@ static int boot_entry_load(const char *path, BootEntry *entry) {
else if (streq(field, "devicetree"))
r = free_and_strdup(&tmp.device_tree, p);
else {
- log_notice("%s:%u: Unknown line \"%s\"", path, line, field);
+ log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
continue;
}
if (r < 0)
@@ -203,7 +216,7 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) {
else if (streq(field, "console-mode"))
r = free_and_strdup(&config->console_mode, p);
else {
- log_notice("%s:%u: Unknown line \"%s\"", path, line, field);
+ log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
continue;
}
if (r < 0)
@@ -217,36 +230,270 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
return str_verscmp(a->id, b->id);
}
-static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
+static int boot_entries_find(
+ const char *root,
+ const char *dir,
+ BootEntry **entries,
+ size_t *n_entries) {
+
_cleanup_strv_free_ char **files = NULL;
+ size_t n_allocated = *n_entries;
char **f;
int r;
- BootEntry *array = NULL;
- size_t n_allocated = 0, n = 0;
+ assert(root);
assert(dir);
- assert(ret_entries);
- assert(ret_n_entries);
+ assert(entries);
+ assert(n_entries);
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(array, n_allocated, n + 1))
+ if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
return log_oom();
- r = boot_entry_load(*f, array + n);
+ r = boot_entry_load(root, *f, *entries + *n_entries);
if (r < 0)
continue;
- n++;
+ (*n_entries) ++;
}
- typesafe_qsort(array, n, boot_entry_compare);
+ return 0;
+}
+
+static int boot_entry_load_unified(
+ const char *root,
+ const char *path,
+ const char *osrelease,
+ const char *cmdline,
+ BootEntry *ret) {
- *ret_entries = array;
- *ret_n_entries = n;
+ _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL;
+ _cleanup_(boot_entry_free) BootEntry tmp = {};
+ _cleanup_fclose_ FILE *f = NULL;
+ const char *k;
+ int r;
+
+ assert(root);
+ assert(path);
+ assert(osrelease);
+
+ k = path_startswith(path, root);
+ if (!k)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path);
+
+ f = fmemopen((void*) osrelease, strlen(osrelease), "r");
+ if (!f)
+ return log_error_errno(errno, "Failed to open os-release buffer: %m");
+
+ r = parse_env_file(f, "os-release",
+ "PRETTY_NAME", &os_pretty_name,
+ "ID", &os_id,
+ "VERSION_ID", &version_id,
+ "BUILD_ID", &build_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path);
+
+ if (!os_pretty_name || !os_id || !(version_id || build_id))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
+
+ tmp.id = strjoin(os_id, "-", version_id ?: build_id);
+ if (!tmp.id)
+ return log_oom();
+
+ tmp.path = strdup(path);
+ if (!tmp.path)
+ return log_oom();
+
+ tmp.root = strdup(root);
+ if (!tmp.root)
+ return log_oom();
+
+ tmp.kernel = strdup(skip_leading_chars(k, "/"));
+ if (!tmp.kernel)
+ return log_oom();
+
+ tmp.options = strv_new(skip_leading_chars(cmdline, WHITESPACE));
+ if (!tmp.options)
+ return log_oom();
+
+ delete_trailing_chars(tmp.options[0], WHITESPACE);
+
+ tmp.title = TAKE_PTR(os_pretty_name);
+
+ *ret = tmp;
+ tmp = (BootEntry) {};
+ return 0;
+}
+
+/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
+ * the ones we do care about and we are willing to load into memory have this size limit.) */
+#define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
+
+static int find_sections(
+ int fd,
+ char **ret_osrelease,
+ char **ret_cmdline) {
+
+ _cleanup_free_ struct PeSectionHeader *sections = NULL;
+ _cleanup_free_ char *osrelease = NULL, *cmdline = NULL;
+ size_t i, n_sections;
+ struct DosFileHeader dos;
+ struct PeHeader pe;
+ uint64_t start;
+ ssize_t n;
+
+ n = pread(fd, &dos, sizeof(dos), 0);
+ if (n < 0)
+ return log_error_errno(errno, "Failed read DOS header: %m");
+ if (n != sizeof(dos))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading DOS header, refusing.");
+
+ if (dos.Magic[0] != 'M' || dos.Magic[1] != 'Z')
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOS executable magic missing, refusing.");
+
+ start = unaligned_read_le32(&dos.ExeHeader);
+ n = pread(fd, &pe, sizeof(pe), start);
+ if (n < 0)
+ return log_error_errno(errno, "Failed to read PE header: %m");
+ if (n != sizeof(pe))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading PE header, refusing.");
+
+ if (pe.Magic[0] != 'P' || pe.Magic[1] != 'E' || pe.Magic[2] != 0 || pe.Magic[3] != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE executable magic missing, refusing.");
+
+ n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections);
+ if (n_sections > 96)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE header has too many sections, refusing.");
+
+ sections = new(struct PeSectionHeader, n_sections);
+ if (!sections)
+ return log_oom();
+
+ n = pread(fd, sections,
+ n_sections * sizeof(struct PeSectionHeader),
+ start + sizeof(pe) + unaligned_read_le16(&pe.FileHeader.SizeOfOptionalHeader));
+ if (n < 0)
+ return log_error_errno(errno, "Failed to read section data: %m");
+ if ((size_t) n != n_sections * sizeof(struct PeSectionHeader))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading sections, refusing.");
+
+ for (i = 0; i < n_sections; i++) {
+ _cleanup_free_ char *k = NULL;
+ uint32_t offset, size;
+ char **b;
+
+ if (strneq((char*) sections[i].Name, ".osrel", sizeof(sections[i].Name)))
+ b = &osrelease;
+ else if (strneq((char*) sections[i].Name, ".cmdline", sizeof(sections[i].Name)))
+ b = &cmdline;
+ else
+ continue;
+
+ if (*b)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section %s, refusing.", sections[i].Name);
+
+ offset = unaligned_read_le32(&sections[i].PointerToRawData);
+ size = unaligned_read_le32(&sections[i].VirtualSize);
+
+ if (size > PE_SECTION_SIZE_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section %s too large, refusing.", sections[i].Name);
+
+ k = new(char, size+1);
+ if (!k)
+ return log_oom();
+
+ n = pread(fd, k, size, offset);
+ if (n < 0)
+ return log_error_errno(errno, "Failed to read section payload: %m");
+ if (n != size)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading section payload, refusing:");
+
+ /* Allow one trailing NUL byte, but nothing more. */
+ if (size > 0 && memchr(k, 0, size - 1))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section contains embedded NUL byte: %m");
+
+ k[size] = 0;
+ *b = TAKE_PTR(k);
+ }
+
+ if (!osrelease)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Image lacks .osrel section, refusing.");
+
+ if (ret_osrelease)
+ *ret_osrelease = TAKE_PTR(osrelease);
+ if (ret_cmdline)
+ *ret_cmdline = TAKE_PTR(cmdline);
+
+ return 0;
+}
+
+static int boot_entries_find_unified(
+ const char *root,
+ const char *dir,
+ BootEntry **entries,
+ size_t *n_entries) {
+
+ _cleanup_(closedirp) DIR *d = NULL;
+ size_t n_allocated = *n_entries;
+ struct dirent *de;
+ int r;
+
+ assert(root);
+ assert(dir);
+ assert(entries);
+ assert(n_entries);
+
+ d = opendir(dir);
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ return log_error_errno(errno, "Failed to open %s: %m", dir);
+ }
+
+ FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", dir)) {
+ _cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL;
+ _cleanup_close_ int fd = -1;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (!endswith_no_case(de->d_name, ".efi"))
+ continue;
+
+ if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1))
+ return log_oom();
+
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0) {
+ log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", dir, de->d_name);
+ continue;
+ }
+
+ r = fd_verify_regular(fd);
+ if (r < 0) {
+ log_warning_errno(errno, "File %s/%s is not regular, ignoring: %m", dir, de->d_name);
+ continue;
+ }
+
+ r = find_sections(fd, &osrelease, &cmdline);
+ if (r < 0)
+ continue;
+
+ j = path_join(dir, de->d_name);
+ if (!j)
+ return log_oom();
+
+ r = boot_entry_load_unified(root, j, osrelease, cmdline, *entries + *n_entries);
+ if (r < 0)
+ continue;
+
+ (*n_entries) ++;
+ }
return 0;
}
@@ -358,22 +605,46 @@ static int boot_entries_select_default(const BootConfig *config) {
return config->n_entries - 1; /* -1 means "no default" */
}
-int boot_entries_load_config(const char *esp_path, BootConfig *config) {
+int boot_entries_load_config(
+ const char *esp_path,
+ const char *xbootldr_path,
+ BootConfig *config) {
+
const char *p;
int r;
- assert(esp_path);
assert(config);
- p = strjoina(esp_path, "/loader/loader.conf");
- r = boot_loader_read_conf(p, config);
- if (r < 0)
- return r;
+ if (esp_path) {
+ p = strjoina(esp_path, "/loader/loader.conf");
+ r = boot_loader_read_conf(p, config);
+ if (r < 0)
+ return r;
- p = strjoina(esp_path, "/loader/entries");
- r = boot_entries_find(p, &config->entries, &config->n_entries);
- if (r < 0)
- return r;
+ p = strjoina(esp_path, "/loader/entries");
+ r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries);
+ if (r < 0)
+ return r;
+
+ p = strjoina(esp_path, "/EFI/Linux/");
+ r = boot_entries_find_unified(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;
+
+ p = strjoina(xbootldr_path, "/EFI/Linux/");
+ r = boot_entries_find_unified(xbootldr_path, p, &config->entries, &config->n_entries);
+ if (r < 0)
+ return r;
+ }
+
+ typesafe_qsort(config->entries, config->n_entries, boot_entry_compare);
r = boot_entries_uniquify(config->entries, config->n_entries);
if (r < 0)
@@ -395,87 +666,32 @@ int boot_entries_load_config(const char *esp_path, BootConfig *config) {
/********************************************************************************/
-static int verify_esp(
- const char *p,
+static int verify_esp_blkid(
+ dev_t devid,
bool searching,
- bool unprivileged_mode,
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;
-#endif
- uint64_t pstart = 0, psize = 0;
- struct stat st, st2;
- const char *t2;
- struct statfs sfs;
- sd_id128_t uuid = SD_ID128_NULL;
- uint32_t part = 0;
- bool relax_checks;
int r;
- assert(p);
-
- 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) {
- 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(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)) {
- if (searching)
- return -EADDRNOTAVAIL;
-
- log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
- return -ENODEV;
- }
- }
-
- if (stat(p, &st) < 0)
- 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) {
- log_error("Block device node of %p is invalid.", p);
- return -ENODEV;
- }
-
- t2 = strjoina(p, "/..");
- r = stat(t2, &st2);
- 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) {
- log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
- return -ENODEV;
- }
-
- /* 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 || unprivileged_mode || relax_checks)
- goto finish;
-
-#if HAVE_BLKID
- r = device_path_make_major_minor(S_IFBLK, st.st_dev, &node);
+ 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 ?: ENOMEM, "Failed to open file system \"%s\": %m", p);
+ 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);
@@ -484,56 +700,52 @@ static int verify_esp(
errno = 0;
r = blkid_do_safeprobe(b);
- if (r == -2) {
- log_error("File system \"%s\" is ambiguous.", p);
- return -ENODEV;
- } else if (r == 1) {
- log_error("File system \"%s\" does not contain a label.", p);
- return -ENODEV;
- } else if (r != 0)
- return log_error_errno(errno ?: EIO, "Failed to probe file system \"%s\": %m", p);
+ 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 ?: EIO, "Failed to probe file system type \"%s\": %m", p);
- if (!streq(v, "vfat")) {
- log_error("File system \"%s\" is not FAT.", p);
- return -ENODEV;
- }
+ 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 ?: EIO, "Failed to probe partition scheme \"%s\": %m", p);
- if (!streq(v, "gpt")) {
- log_error("File system \"%s\" is not on a GPT partition table.", p);
- return -ENODEV;
- }
+ 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 \"%s\": %m", p);
- if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) {
- log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p);
- return -ENODEV;
- }
+ 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 ?: EIO, "Failed to probe partition entry UUID \"%s\": %m", p);
+ 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) {
- log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
- return -EIO;
- }
+ 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 ?: EIO, "Failed to probe partition number \"%s\": m", p);
+ 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.");
@@ -541,7 +753,7 @@ static int verify_esp(
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
if (r != 0)
- return log_error_errno(errno ?: EIO, "Failed to probe partition offset \"%s\": %m", p);
+ 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.");
@@ -549,13 +761,12 @@ static int verify_esp(
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
if (r != 0)
- return log_error_errno(errno ?: EIO, "Failed to probe partition size \"%s\": %m", p);
+ 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
-finish:
if (ret_part)
*ret_part = part;
if (ret_pstart)
@@ -568,6 +779,234 @@ finish:
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, *trigger;
+ int r;
+
+ assert(path);
+ assert(ret_dev);
+
+ /* 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 */
+ trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
+ (void) access(trigger, F_OK);
+
+ 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;
+}
+
int find_esp_and_warn(
const char *path,
bool unprivileged_mode,
@@ -631,35 +1070,260 @@ found:
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;
+
+found:
+ if (ret_path) {
+ char *c;
+
+ c = strdup(path);
+ if (!c)
+ return log_oom();
+
+ *ret_path = c;
+ }
+
+ return 0;
+}
+
int find_default_boot_entry(
const char *esp_path,
- char **esp_where,
+ const char *xbootldr_path,
BootConfig *config,
const BootEntry **e) {
- _cleanup_free_ char *where = NULL;
+ _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
int r;
assert(config);
assert(e);
- r = find_esp_and_warn(esp_path, false, &where, NULL, NULL, NULL, NULL);
+ r = find_esp_and_warn(esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
if (r < 0)
return r;
- r = boot_entries_load_config(where, config);
+ 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 bootspec config from \"%s/loader\": %m", where);
+ 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 entry suitable as default, refusing to guess.");
+ "No boot loader entry suitable as default, refusing to guess.");
*e = &config->entries[config->default_entry];
- log_debug("Found default boot entry in file \"%s\"", (*e)->path);
-
- if (esp_where)
- *esp_where = TAKE_PTR(where);
+ log_debug("Found default boot loader entry in file \"%s\"", (*e)->path);
return 0;
}
diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h
index ed576210fe..f184902558 100644
--- a/src/shared/bootspec.h
+++ b/src/shared/bootspec.h
@@ -10,7 +10,8 @@
typedef struct BootEntry {
char *id; /* This is the file basename without extension */
- char *path; /* This is the full path to the file */
+ char *path; /* This is the full path to the drop-in file */
+ char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */
char *title;
char *show_title;
char *version;
@@ -40,12 +41,13 @@ typedef struct BootConfig {
} BootConfig;
void boot_config_free(BootConfig *config);
-int boot_entries_load_config(const char *esp_path, BootConfig *config);
+int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config);
static inline const char* boot_entry_title(const BootEntry *entry) {
return entry->show_title ?: entry->title ?: entry->id;
}
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 find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid);
-int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e);
+int find_default_boot_entry(const char *esp_path, const char *xbootldr_path, BootConfig *config, const BootEntry **e);
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index d340487025..201a515589 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -343,10 +343,8 @@ int dissect_image(
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) {
- log_debug("Failed to identify any partition table.");
- return -ENOPKG;
- }
+ if (IN_SET(r, -2, 1))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
if (r != 0)
return -errno ?: -EIO;
@@ -497,6 +495,14 @@ int dissect_image(
designator = PARTITION_ESP;
fstype = "vfat";
+
+ } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) {
+
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
+ designator = PARTITION_XBOOTLDR;
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
}
#ifdef GPT_ROOT_NATIVE
else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
@@ -612,21 +618,53 @@ int dissect_image(
} else if (is_mbr) {
- if (pflags != 0x80) /* Bootable flag */
- continue;
+ switch (blkid_partition_get_type(pp)) {
- if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
- continue;
+ case 0x83: /* Linux partition */
+
+ if (pflags != 0x80) /* Bootable flag */
+ continue;
+
+ if (generic_node)
+ multiple_generic = true;
+ else {
+ generic_nr = nr;
+ generic_rw = true;
+ generic_node = strdup(node);
+ if (!generic_node)
+ return -ENOMEM;
+ }
+
+ break;
+
+ case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
+ _cleanup_free_ char *n = NULL;
+ sd_id128_t id = SD_ID128_NULL;
+ const char *sid;
+
+ /* First one wins */
+ if (m->partitions[PARTITION_XBOOTLDR].found)
+ continue;
+
+ sid = blkid_partition_get_uuid(pp);
+ if (sid)
+ (void) sd_id128_from_string(sid, &id);
- if (generic_node)
- multiple_generic = true;
- else {
- generic_nr = nr;
- generic_rw = true;
- generic_node = strdup(node);
- if (!generic_node)
+ n = strdup(node);
+ if (!n)
return -ENOMEM;
- }
+
+ m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
+ .found = true,
+ .partno = nr,
+ .rw = true,
+ .architecture = _ARCHITECTURE_INVALID,
+ .node = TAKE_PTR(n),
+ .uuid = id,
+ };
+
+ break;
+ }}
}
}
@@ -819,11 +857,15 @@ static int mount_partition(
return -ENOMEM;
}
- return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
+ r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
+ if (r < 0)
+ return r;
+
+ return 1;
}
int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
- int r;
+ int r, boot_mounted;
assert(m);
assert(where);
@@ -856,21 +898,26 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
if (r < 0)
return r;
+ boot_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
+ if (boot_mounted < 0)
+ return boot_mounted;
+
if (m->partitions[PARTITION_ESP].found) {
- const char *mp;
+ /* Mount the ESP to /efi if it exists. If it doesn't exist, use /boot instead, but only if it
+ * exists and is empty, and we didn't already mount the XBOOTLDR partition into it. */
- /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
+ r = chase_symlinks("/efi", where, CHASE_PREFIX_ROOT, NULL);
+ if (r >= 0) {
+ r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags);
+ if (r < 0)
+ return r;
- FOREACH_STRING(mp, "/efi", "/boot") {
+ } else if (boot_mounted <= 0) {
_cleanup_free_ char *p = NULL;
- r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p);
- if (r < 0)
- continue;
-
- r = dir_is_empty(p);
- if (r > 0) {
- r = mount_partition(m->partitions + PARTITION_ESP, where, mp, uid_shift, flags);
+ r = chase_symlinks("/boot", where, CHASE_PREFIX_ROOT, &p);
+ if (r >= 0 && dir_is_empty(p) > 0) {
+ r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags);
if (r < 0)
return r;
}
@@ -1499,6 +1546,7 @@ static const char *const partition_designator_table[] = {
[PARTITION_HOME] = "home",
[PARTITION_SRV] = "srv",
[PARTITION_ESP] = "esp",
+ [PARTITION_XBOOTLDR] = "xbootldr",
[PARTITION_SWAP] = "swap",
[PARTITION_ROOT_VERITY] = "root-verity",
[PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index f50b40ea11..ded43f331a 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -29,6 +29,7 @@ enum {
PARTITION_HOME,
PARTITION_SRV,
PARTITION_ESP,
+ PARTITION_XBOOTLDR,
PARTITION_SWAP,
PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
diff --git a/src/shared/gpt.h b/src/shared/gpt.h
index fd953fabc7..31e01bd5a5 100644
--- a/src/shared/gpt.h
+++ b/src/shared/gpt.h
@@ -15,6 +15,7 @@
#define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae)
#define GPT_ROOT_IA64 SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97)
#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b)
+#define GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72)
#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 99d6ba14f1..1a5ba8c0f4 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -117,6 +117,7 @@ shared_sources = files('''
pager.h
path-lookup.c
path-lookup.h
+ pe-header.h
pretty-print.c
pretty-print.h
ptyfwd.c
diff --git a/src/shared/pe-header.h b/src/shared/pe-header.h
new file mode 100644
index 0000000000..a362917523
--- /dev/null
+++ b/src/shared/pe-header.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <inttypes.h>
+
+#include "macro.h"
+#include "sparse-endian.h"
+
+struct DosFileHeader {
+ uint8_t Magic[2];
+ le16_t LastSize;
+ le16_t nBlocks;
+ le16_t nReloc;
+ le16_t HdrSize;
+ le16_t MinAlloc;
+ le16_t MaxAlloc;
+ le16_t ss;
+ le16_t sp;
+ le16_t Checksum;
+ le16_t ip;
+ le16_t cs;
+ le16_t RelocPos;
+ le16_t nOverlay;
+ le16_t reserved[4];
+ le16_t OEMId;
+ le16_t OEMInfo;
+ le16_t reserved2[10];
+ le32_t ExeHeader;
+} _packed_;
+
+#define PE_HEADER_MACHINE_I386 0x014cU
+#define PE_HEADER_MACHINE_X64 0x8664U
+
+struct PeFileHeader {
+ le16_t Machine;
+ le16_t NumberOfSections;
+ le32_t TimeDateStamp;
+ le32_t PointerToSymbolTable;
+ le32_t NumberOfSymbols;
+ le16_t SizeOfOptionalHeader;
+ le16_t Characteristics;
+} _packed_;
+
+struct PeHeader {
+ uint8_t Magic[4];
+ struct PeFileHeader FileHeader;
+} _packed_;
+
+struct PeSectionHeader {
+ uint8_t Name[8];
+ le32_t VirtualSize;
+ le32_t VirtualAddress;
+ le32_t SizeOfRawData;
+ le32_t PointerToRawData;
+ le32_t PointerToRelocations;
+ le32_t PointerToLinenumbers;
+ le16_t NumberOfRelocations;
+ le16_t NumberOfLinenumbers;
+ le32_t Characteristics;
+ } _packed_;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 63dae2c872..57e3111b8d 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -134,7 +134,6 @@ static const char *arg_kill_who = NULL;
static int arg_signal = SIGTERM;
static char *arg_root = NULL;
static usec_t arg_when = 0;
-static char *arg_esp_path = NULL;
static char *argv_cmdline = NULL;
static enum action {
ACTION_SYSTEMCTL,
@@ -3519,7 +3518,7 @@ static int prepare_firmware_setup(void) {
static int load_kexec_kernel(void) {
_cleanup_(boot_config_free) BootConfig config = {};
- _cleanup_free_ char *where = NULL, *kernel = NULL, *initrd = NULL, *options = NULL;
+ _cleanup_free_ char *kernel = NULL, *initrd = NULL, *options = NULL;
const BootEntry *e;
pid_t pid;
int r;
@@ -3532,21 +3531,27 @@ static int load_kexec_kernel(void) {
if (access(KEXEC, X_OK) < 0)
return log_error_errno(errno, KEXEC" is not available: %m");
- r = find_default_boot_entry(arg_esp_path, &where, &config, &e);
+ r = find_default_boot_entry(NULL, NULL, &config, &e);
if (r == -ENOKEY) /* find_default_boot_entry() 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;
- if (strv_length(e->initrd) > 1) {
- log_error("Boot entry specifies multiple initrds, which is not supported currently.");
- return -EINVAL;
+ if (strv_length(e->initrd) > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Boot entry specifies multiple initrds, which is not supported currently.");
+
+ kernel = path_join(e->root, e->kernel);
+ if (!kernel)
+ return log_oom();
+
+ if (!strv_isempty(e->initrd)) {
+ initrd = path_join(e->root, *e->initrd);
+ if (!initrd)
+ return log_oom();
}
- kernel = path_join(where, e->kernel);
- if (!strv_isempty(e->initrd))
- initrd = path_join(where, *e->initrd);
options = strv_join(e->options, " ");
if (!options)
return log_oom();
@@ -8814,7 +8819,6 @@ finish:
strv_free(arg_wall);
free(arg_root);
- free(arg_esp_path);
/* Note that we return r here, not 0, so that we can implement the LSB-like return codes */
return r;