Merge pull request #3757 from poettering/efi-search

This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-07-25 16:17:48 -04:00
commit e28973ee18
8 changed files with 444 additions and 290 deletions

View file

@ -47,16 +47,16 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>status</command>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg> status</command>
</cmdsynopsis>
<cmdsynopsis>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>update</command>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg> update</command>
</cmdsynopsis>
<cmdsynopsis>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>install</command>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg> install</command>
</cmdsynopsis>
<cmdsynopsis>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg>remove</command>
<command>bootctl <arg choice="opt" rep="repeat">OPTIONS</arg> remove</command>
</cmdsynopsis>
</refsynopsisdiv>
@ -71,19 +71,14 @@
currently installed versions of the boot loader binaries and
all current EFI boot variables.</para>
<para><command>bootctl update</command> updates all installed
versions of systemd-boot, if the current version is newer than the
version installed in the EFI system partition. This also includes
the EFI default/fallback loader at /EFI/BOOT/BOOT*.EFI. A
systemd-boot entry in the EFI boot variables is created if there
is no current entry. The created entry will be added to the end of
the boot order list.</para>
<para><command>bootctl update</command> updates all installed versions of systemd-boot, if the current version is
newer than the version installed in the EFI system partition. This also includes the EFI default/fallback loader at
<filename>/EFI/BOOT/BOOT*.EFI</filename>. A systemd-boot entry in the EFI boot variables is created if there is no
current entry. The created entry will be added to the end of the boot order list.</para>
<para><command>bootctl install</command> installs systemd-boot into
the EFI system partition. A copy of systemd-boot will be stored as
the EFI default/fallback loader at /EFI/BOOT/BOOT*.EFI. A systemd-boot
entry in the EFI boot variables is created and added to the top
of the boot order list.</para>
<para><command>bootctl install</command> installs systemd-boot into the EFI system partition. A copy of
systemd-boot will be stored as the EFI default/fallback loader at <filename>/EFI/BOOT/BOOT*.EFI</filename>. A
systemd-boot entry in the EFI boot variables is created and added to the top of the boot order list.</para>
<para><command>bootctl remove</command> removes all installed
versions of systemd-boot from the EFI system partition, and removes
@ -101,8 +96,10 @@
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<varlistentry>
<term><option>--path</option></term>
<listitem><para>Path to the EFI system partition. The default is /boot.</para></listitem>
<term><option>--path=</option></term>
<listitem><para>Path to the EFI System Partition (ESP). If not specified, <filename>/efi</filename>,
<filename>/boot</filename>, and <filename>/boot/efi</filename> are checked in turn. It is recommended to mount
the ESP to <filename>/boot</filename>, if possible.</para></listitem>
</varlistentry>
<varlistentry>

View file

@ -137,6 +137,11 @@
<entry>Swap</entry>
<entry>All swap partitions located on the disk the root partition is located on are enabled.</entry>
</row>
<row>
<entry>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</entry>
<entry>EFI System Partition (ESP)</entry>
<entry>The first ESP located on the disk the root partition is located on is mounted to <filename>/boot</filename> or <filename>/efi</filename>, see below.</entry>
</row>
</tbody>
</tgroup>
</table>
@ -150,16 +155,14 @@
<filename>/etc/crypttab</filename> with a different device mapper
device name.</para>
<para>Mount and automount units for the EFI System Partition (ESP),
mounting it to <filename>/boot</filename>, are generated on EFI
systems where the boot loader communicates the used ESP to the operating
system. Since this generator creates an automount unit, the mount will
only be activated on-demand, when accessed. On systems where
<filename>/boot</filename> is an explicitly configured mount
(for example, listed in
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
or where the <filename>/boot</filename> mount point is non-empty, no
mount units are generated.</para>
<para>Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP is mounted
to <filename>/boot</filename>, unless a mount point directory <filename>/efi</filename> exists, in which case it is
mounted there. Since this generator creates an automount unit, the mount will only be activated on-demand, when
accessed. On systems where <filename>/boot</filename> (or <filename>/efi</filename> if it exists) is an explicitly
configured mount (for example, listed in <citerefentry
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>) or where the
<filename>/boot</filename> (or <filename>/efi</filename>) mount point is non-empty, no mount units are
generated.</para>
<para>When using this generator in conjunction with btrfs file
systems, make sure to set the correct default subvolumes on them,

View file

@ -323,6 +323,14 @@ char ascii_tolower(char x) {
return x;
}
char ascii_toupper(char x) {
if (x >= 'a' && x <= 'z')
return x - 'a' + 'A';
return x;
}
char *ascii_strlower(char *t) {
char *p;
@ -334,6 +342,17 @@ char *ascii_strlower(char *t) {
return t;
}
char *ascii_strupper(char *t) {
char *p;
assert(t);
for (p = t; *p; p++)
*p = ascii_toupper(*p);
return t;
}
char *ascii_strlower_n(char *t, size_t n) {
size_t i;

View file

@ -137,6 +137,9 @@ char ascii_tolower(char x);
char *ascii_strlower(char *s);
char *ascii_strlower_n(char *s, size_t n);
char ascii_toupper(char x);
char *ascii_strupper(char *s);
int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);

View file

@ -26,6 +26,7 @@
#include <ftw.h>
#include <getopt.h>
#include <limits.h>
#include <linux/magic.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -42,22 +43,53 @@
#include "fd-util.h"
#include "fileio.h"
#include "locale-util.h"
#include "parse-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "strv.h"
#include "umask-util.h"
#include "util.h"
#include "verbs.h"
#include "virt.h"
#include "stat-util.h"
static char *arg_path = NULL;
static bool arg_touch_variables = true;
static int verify_esp(
bool searching,
const char *p,
uint32_t *ret_part,
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
struct statfs sfs;
struct stat st, st2;
_cleanup_free_ char *t = NULL;
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
int r;
_cleanup_free_ char *t = NULL;
uint64_t pstart = 0, psize = 0;
struct stat st, st2;
const char *v, *t2;
struct statfs sfs;
sd_id128_t uuid = SD_ID128_NULL;
uint32_t part = 0;
int r;
assert(p);
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;
if (statfs(p, &sfs) < 0)
return log_error_errno(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;
if (sfs.f_type != 0x4d44) {
log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
return -ENODEV;
}
@ -80,6 +112,11 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
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. */
if (detect_container() > 0)
goto finish;
r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev));
if (r < 0)
return log_oom();
@ -117,7 +154,6 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = errno ? -errno : -EIO;
return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p);
}
if (!streq(v, "vfat")) {
log_error("File system \"%s\" is not FAT.", p);
return -ENODEV;
@ -129,7 +165,6 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = errno ? -errno : -EIO;
return log_error_errno(r, "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;
@ -141,7 +176,6 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = errno ? -errno : -EIO;
return log_error_errno(r, "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;
@ -153,8 +187,7 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = errno ? -errno : -EIO;
return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p);
}
r = sd_id128_from_string(v, uuid);
r = sd_id128_from_string(v, &uuid);
if (r < 0) {
log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
return -EIO;
@ -166,7 +199,9 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = errno ? -errno : -EIO;
return log_error_errno(r, "Failed to probe partition number \"%s\": m", p);
}
*part = strtoul(v, NULL, 10);
r = safe_atou32(v, &part);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
@ -174,7 +209,9 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = errno ? -errno : -EIO;
return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p);
}
*pstart = strtoul(v, NULL, 10);
r = safe_atou64(v, &pstart);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
@ -182,11 +219,50 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = errno ? -errno : -EIO;
return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p);
}
*psize = strtoul(v, NULL, 10);
r = safe_atou64(v, &psize);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
finish:
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 find_esp(uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
const char *path;
int r;
if (arg_path)
return verify_esp(false, arg_path, part, pstart, psize, uuid);
FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
r = verify_esp(true, path, part, pstart, psize, uuid);
if (IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
continue;
if (r < 0)
return r;
arg_path = strdup(path);
if (!arg_path)
return log_oom();
log_info("Using EFI System Parition at %s.", path);
return 0;
}
log_error("Couldn't find EFI system partition. It is recommended to mount it to /boot. Alternatively, use --path= to specify path to mount point.");
return -ENOENT;
}
/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
static int get_file_version(int fd, char **v) {
struct stat st;
@ -199,14 +275,16 @@ static int get_file_version(int fd, char **v) {
assert(v);
if (fstat(fd, &st) < 0)
return -errno;
return log_error_errno(errno, "Failed to stat EFI binary: %m");
if (st.st_size < 27)
if (st.st_size < 27) {
*v = NULL;
return 0;
}
buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (buf == MAP_FAILED)
return -errno;
return log_error_errno(errno, "Failed to memory map EFI binary: %m");
s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
if (!s)
@ -228,7 +306,7 @@ static int get_file_version(int fd, char **v) {
r = 1;
finish:
munmap(buf, st.st_size);
(void) munmap(buf, st.st_size);
*v = x;
return r;
}
@ -338,9 +416,10 @@ static int status_variables(void) {
n_options = efi_get_boot_options(&options);
if (n_options == -ENOENT)
return log_error_errno(ENOENT, "Failed to access EFI variables, efivarfs"
return log_error_errno(n_options,
"Failed to access EFI variables, efivarfs"
" needs to be available at /sys/firmware/efi/efivars/.");
else if (n_options < 0)
if (n_options < 0)
return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
n_order = efi_get_boot_order(&order);
@ -360,11 +439,9 @@ static int status_variables(void) {
for (j = 0; j < n_order; j++)
if (options[i] == order[j])
goto next;
continue;
print_efi_option(options[i], false);
next:
continue;
}
return 0;
@ -523,15 +600,6 @@ error:
return r;
}
static char* strupper(char *s) {
char *p;
for (p = s; *p; p++)
*p = toupper(*p);
return s;
}
static int mkdir_one(const char *prefix, const char *suffix) {
char *p;
@ -554,11 +622,11 @@ static const char *efi_subdirs[] = {
};
static int create_dirs(const char *esp_path) {
const char **i;
int r;
unsigned i;
for (i = 0; i < ELEMENTSOF(efi_subdirs); i++) {
r = mkdir_one(esp_path, efi_subdirs[i]);
STRV_FOREACH(i, efi_subdirs) {
r = mkdir_one(esp_path, *i);
if (r < 0)
return r;
}
@ -580,7 +648,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
/* Create the EFI default boot loader name (specified for removable devices) */
v = strjoina(esp_path, "/EFI/BOOT/BOOT", name + strlen("systemd-boot"));
strupper(strrchr(v, '/') + 1);
ascii_strupper(strrchr(v, '/') + 1);
k = copy_file(p, v, force);
if (k < 0 && r == 0)
@ -751,8 +819,8 @@ static int install_variables(const char *esp_path,
if (access(p, F_OK) < 0) {
if (errno == ENOENT)
return 0;
else
return log_error_errno(errno, "Cannot access \"%s\": %m", p);
return log_error_errno(errno, "Cannot access \"%s\": %m", p);
}
r = find_slot(uuid, path, &slot);
@ -762,7 +830,7 @@ static int install_variables(const char *esp_path,
"Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
"Failed to determine current boot order: %m");
if (first || r == false) {
if (first || r == 0) {
r = efi_add_boot_option(slot, "Linux Boot Manager",
part, pstart, psize,
uuid, path);
@ -872,46 +940,39 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
if (in_order)
return remove_from_order(slot);
else
return 0;
}
static int install_loader_config(const char *esp_path) {
char *p;
char line[64];
char *machine = NULL;
_cleanup_fclose_ FILE *f = NULL, *g = NULL;
f = fopen("/etc/machine-id", "re");
if (!f)
return errno == ENOENT ? 0 : -errno;
if (fgets(line, sizeof(line), f) != NULL) {
char *s;
s = strchr(line, '\n');
if (s)
s[0] = '\0';
if (strlen(line) == 32)
machine = line;
}
if (!machine)
return -ESRCH;
p = strjoina(esp_path, "/loader/loader.conf");
g = fopen(p, "wxe");
if (g) {
fprintf(g, "#timeout 3\n");
fprintf(g, "default %s-*\n", machine);
if (ferror(g))
return log_error_errno(EIO, "Failed to write \"%s\": %m", p);
}
return 0;
}
static int help(void) {
static int install_loader_config(const char *esp_path) {
_cleanup_fclose_ FILE *f = NULL;
char machine_string[SD_ID128_STRING_MAX];
sd_id128_t machine_id;
const char *p;
int r;
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return log_error_errno(r, "Failed to get machine did: %m");
p = strjoina(esp_path, "/loader/loader.conf");
f = fopen(p, "wxe");
if (!f)
return log_error_errno(errno, "Failed to open loader.conf for writing: %m");
fprintf(f, "#timeout 3\n");
fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write \"%s\": %m", p);
return 0;
}
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"
@ -930,9 +991,6 @@ static int help(void) {
return 0;
}
static const char *arg_path = "/boot";
static bool arg_touch_variables = true;
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_PATH = 0x100,
@ -948,7 +1006,7 @@ static int parse_argv(int argc, char *argv[]) {
{ NULL, 0, NULL, 0 }
};
int c;
int c, r;
assert(argc >= 0);
assert(argv);
@ -957,14 +1015,16 @@ static int parse_argv(int argc, char *argv[]) {
switch (c) {
case 'h':
help();
help(0, NULL, NULL);
return 0;
case ARG_VERSION:
return version();
case ARG_PATH:
arg_path = optarg;
r = free_and_strdup(&arg_path, optarg);
if (r < 0)
return log_oom();
break;
case ARG_NO_VARIABLES:
@ -989,149 +1049,170 @@ static void read_loader_efi_var(const char *name, char **var) {
log_warning_errno(r, "Failed to read EFI variable %s: %m", name);
}
static int bootctl_main(int argc, char*argv[]) {
enum action {
ACTION_STATUS,
ACTION_INSTALL,
ACTION_UPDATE,
ACTION_REMOVE
} arg_action = ACTION_STATUS;
static const struct {
const char* verb;
enum action action;
} verbs[] = {
{ "status", ACTION_STATUS },
{ "install", ACTION_INSTALL },
{ "update", ACTION_UPDATE },
{ "remove", ACTION_REMOVE },
};
static int must_be_root(void) {
sd_id128_t uuid = {};
uint32_t part = 0;
uint64_t pstart = 0, psize = 0;
int r, q;
if (geteuid() == 0)
return 0;
if (argv[optind]) {
unsigned i;
log_error("Need to be root.");
return -EPERM;
}
for (i = 0; i < ELEMENTSOF(verbs); i++) {
if (!streq(argv[optind], verbs[i].verb))
continue;
arg_action = verbs[i].action;
break;
}
if (i >= ELEMENTSOF(verbs)) {
log_error("Unknown operation \"%s\"", argv[optind]);
return -EINVAL;
}
}
static int verb_status(int argc, char *argv[], void *userdata) {
if (geteuid() != 0)
return log_error_errno(EPERM, "Need to be root.");
sd_id128_t uuid = SD_ID128_NULL;
int r;
r = verify_esp(arg_path, &part, &pstart, &psize, &uuid);
if (r == -ENODEV && !arg_path)
log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot.");
r = must_be_root();
if (r < 0)
return r;
switch (arg_action) {
case ACTION_STATUS: {
_cleanup_free_ char *fw_type = NULL;
_cleanup_free_ char *fw_info = NULL;
_cleanup_free_ char *loader = NULL;
_cleanup_free_ char *loader_path = NULL;
sd_id128_t loader_part_uuid = {};
r = find_esp(NULL, NULL, NULL, &uuid);
if (r < 0)
return r;
if (is_efi_boot()) {
read_loader_efi_var("LoaderFirmwareType", &fw_type);
read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
read_loader_efi_var("LoaderInfo", &loader);
read_loader_efi_var("LoaderImageIdentifier", &loader_path);
if (loader_path)
efi_tilt_backslashes(loader_path);
r = efi_loader_get_device_part_uuid(&loader_part_uuid);
if (r < 0 && r == -ENOENT)
log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m");
if (is_efi_boot()) {
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
printf("System:\n");
printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
read_loader_efi_var("LoaderFirmwareType", &fw_type);
read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
read_loader_efi_var("LoaderInfo", &loader);
read_loader_efi_var("LoaderImageIdentifier", &loader_path);
r = is_efi_secure_boot();
if (r < 0)
log_warning_errno(r, "Failed to query secure boot status: %m");
else
printf(" Secure Boot: %s\n", r ? "enabled" : "disabled");
if (loader_path)
efi_tilt_backslashes(loader_path);
r = is_efi_secure_boot_setup_mode();
if (r < 0)
log_warning_errno(r, "Failed to query secure boot mode: %m");
else
printf(" Setup Mode: %s\n", r ? "setup" : "user");
printf("\n");
r = efi_loader_get_device_part_uuid(&loader_part_uuid);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m");
printf("Loader:\n");
printf(" Product: %s\n", strna(loader));
if (!sd_id128_is_null(loader_part_uuid))
printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
SD_ID128_FORMAT_VAL(loader_part_uuid));
else
printf(" Partition: n/a\n");
printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path));
printf("\n");
} else
printf("System:\n Not booted with EFI\n");
printf("System:\n");
printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
r = status_binaries(arg_path, uuid);
r = is_efi_secure_boot();
if (r < 0)
log_warning_errno(r, "Failed to query secure boot status: %m");
else
printf(" Secure Boot: %s\n", r ? "enabled" : "disabled");
r = is_efi_secure_boot_setup_mode();
if (r < 0)
log_warning_errno(r, "Failed to query secure boot mode: %m");
else
printf(" Setup Mode: %s\n", r ? "setup" : "user");
printf("\n");
printf("Loader:\n");
printf(" Product: %s\n", strna(loader));
if (!sd_id128_is_null(loader_part_uuid))
printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
SD_ID128_FORMAT_VAL(loader_part_uuid));
else
printf(" Partition: n/a\n");
printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path));
printf("\n");
} else
printf("System:\n Not booted with EFI\n");
r = status_binaries(arg_path, uuid);
if (r < 0)
return r;
if (arg_touch_variables)
r = status_variables();
return r;
}
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;
bool install;
int r;
r = must_be_root();
if (r < 0)
return r;
r = find_esp(&part, &pstart, &psize, &uuid);
if (r < 0)
return r;
install = streq(argv[0], "install");
RUN_WITH_UMASK(0002) {
r = install_binaries(arg_path, install);
if (r < 0)
return r;
if (arg_touch_variables)
r = status_variables();
break;
}
case ACTION_INSTALL:
case ACTION_UPDATE:
umask(0002);
r = install_binaries(arg_path, arg_action == ACTION_INSTALL);
if (r < 0)
return r;
if (arg_action == ACTION_INSTALL) {
if (install) {
r = install_loader_config(arg_path);
if (r < 0)
return r;
}
}
if (arg_touch_variables)
r = install_variables(arg_path,
part, pstart, psize, uuid,
"/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
arg_action == ACTION_INSTALL);
break;
if (arg_touch_variables)
r = install_variables(arg_path,
part, pstart, psize, uuid,
"/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
install);
case ACTION_REMOVE:
r = remove_binaries(arg_path);
return r;
}
if (arg_touch_variables) {
q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
if (q < 0 && r == 0)
r = q;
}
break;
static int verb_remove(int argc, char *argv[], void *userdata) {
sd_id128_t uuid = SD_ID128_NULL;
int r;
r = must_be_root();
if (r < 0)
return r;
r = find_esp(NULL, NULL, NULL, &uuid);
if (r < 0)
return r;
r = remove_binaries(arg_path);
if (arg_touch_variables) {
int q;
q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
if (q < 0 && r == 0)
r = q;
}
return r;
}
static int bootctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
{ "install", VERB_ANY, 1, 0, verb_install },
{ "update", VERB_ANY, 1, 0, verb_install },
{ "remove", VERB_ANY, 1, 0, verb_remove },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
int main(int argc, char *argv[]) {
int r;
log_parse_environment();
log_open();
/* If we run in a container, automatically turn of EFI file system access */
if (detect_container() > 0)
arg_touch_variables = false;
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
@ -1139,5 +1220,6 @@ int main(int argc, char *argv[]) {
r = bootctl_main(argc, argv);
finish:
free(arg_path);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

View file

@ -450,99 +450,101 @@ static int add_automount(
}
static int add_boot(const char *what) {
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype = NULL, *uuid = NULL;
sd_id128_t id, type_id;
const char *esp;
int r;
assert(what);
if (!is_efi_boot()) {
log_debug("Not an EFI boot, ignoring /boot.");
return 0;
}
if (in_initrd()) {
log_debug("In initrd, ignoring /boot.");
log_debug("In initrd, ignoring the ESP.");
return 0;
}
if (detect_container() > 0) {
log_debug("In a container, ignoring /boot.");
log_debug("In a container, ignoring the ESP.");
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";
/* We create an .automount which is not overridden by the .mount from the fstab generator. */
if (fstab_is_mount_point("/boot")) {
log_debug("/boot specified in fstab, ignoring.");
if (fstab_is_mount_point(esp)) {
log_debug("%s specified in fstab, ignoring.", esp);
return 0;
}
if (path_is_busy("/boot")) {
log_debug("/boot already populated, ignoring.");
if (path_is_busy(esp)) {
log_debug("%s already populated, ignoring.", esp);
return 0;
}
r = efi_loader_get_device_part_uuid(&id);
if (r == -ENOENT) {
log_debug("EFI loader partition unknown.");
return 0;
}
if (is_efi_boot()) {
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
const char *fstype = NULL, *uuid_string = NULL;
sd_id128_t loader_uuid, part_uuid;
if (r < 0)
return log_error_errno(r, "Failed to read ESP partition UUID: %m");
/* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
errno = 0;
b = blkid_new_probe_from_filename(what);
if (!b) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "Failed to allocate prober: %m");
}
r = efi_loader_get_device_part_uuid(&loader_uuid);
if (r == -ENOENT) {
log_debug("EFI loader partition unknown.");
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to read ESP partition UUID: %m");
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
errno = 0;
b = blkid_new_probe_from_filename(what);
if (!b) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "Failed to allocate prober: %m");
}
errno = 0;
r = blkid_do_safeprobe(b);
if (r == -2 || r == 1) /* no result or uncertain */
return 0;
else if (r != 0)
return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (!streq_ptr(fstype, "vfat")) {
log_debug("Partition for /boot is not a FAT filesystem, ignoring.");
return 0;
}
errno = 0;
r = blkid_do_safeprobe(b);
if (r == -2 || r == 1) /* no result or uncertain */
return 0;
else if (r != 0)
return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL);
if (r != 0) {
log_debug_errno(errno, "Partition for /boot does not have a UUID, ignoring.");
return 0;
}
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (!streq_ptr(fstype, "vfat")) {
log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp);
return 0;
}
if (sd_id128_from_string(uuid, &type_id) < 0) {
log_debug("Partition for /boot does not have a valid UUID, ignoring.");
return 0;
}
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid_string, NULL);
if (r != 0) {
log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp);
return 0;
}
if (!sd_id128_equal(type_id, id)) {
log_debug("Partition for /boot does not appear to be the partition we are booted from.");
return 0;
}
if (sd_id128_from_string(uuid_string, &part_uuid) < 0) {
log_debug("Partition for %s does not have a valid UUID, ignoring.", esp);
return 0;
}
r = add_automount("boot",
what,
"/boot",
"vfat",
true,
"umask=0077",
"EFI System Partition Automount",
120 * USEC_PER_SEC);
if (!sd_id128_equal(part_uuid, loader_uuid)) {
log_debug("Partition for %s does not appear to be the partition we are booted from.", esp);
return 0;
}
} else
log_debug("Not an EFI boot, skipping ESP check.");
return r;
return add_automount("boot",
what,
esp,
"vfat",
true,
"umask=0077",
"EFI System Partition Automount",
120 * USEC_PER_SEC);
}
#else
static int add_boot(const char *what) {

View file

@ -86,10 +86,15 @@ if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then
exit 1
fi
if [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then
if [[ -d /efi/loader/entries ]] || [[ -d /efi/$MACHINE_ID ]]; then
BOOT_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION"
elif [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then
BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION"
elif [[ -d /boot/efi/loader/entries ]] || [[ -d /boot/efi/$MACHINE_ID ]] \
|| mountpoint -q /boot/efi; then
elif [[ -d /boot/efi/loader/entries ]] || [[ -d /boot/efi/$MACHINE_ID ]]; then
BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION"
elif mountpoint -q /efi; then
BOOT_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION"
elif mountpoint -q /boot/efi; then
BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION"
else
BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION"

View file

@ -1794,17 +1794,18 @@ static int dissect_image(
char **root_device, bool *root_device_rw,
char **home_device, bool *home_device_rw,
char **srv_device, bool *srv_device_rw,
char **esp_device,
bool *secondary) {
#ifdef HAVE_BLKID
int home_nr = -1, srv_nr = -1;
int home_nr = -1, srv_nr = -1, esp_nr = -1;
#ifdef GPT_ROOT_NATIVE
int root_nr = -1;
#endif
#ifdef GPT_ROOT_SECONDARY
int secondary_root_nr = -1;
#endif
_cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *generic = NULL;
_cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *esp = NULL, *generic = NULL;
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
@ -1822,6 +1823,7 @@ static int dissect_image(
assert(root_device);
assert(home_device);
assert(srv_device);
assert(esp_device);
assert(secondary);
assert(arg_image);
@ -2035,6 +2037,16 @@ static int dissect_image(
r = free_and_strdup(&srv, node);
if (r < 0)
return log_oom();
} else if (sd_id128_equal(type_id, GPT_ESP)) {
if (esp && nr >= esp_nr)
continue;
esp_nr = nr;
r = free_and_strdup(&esp, node);
if (r < 0)
return log_oom();
}
#ifdef GPT_ROOT_NATIVE
else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
@ -2152,6 +2164,11 @@ static int dissect_image(
*srv_device_rw = srv_rw;
}
if (esp) {
*esp_device = esp;
esp = NULL;
}
return 0;
#else
log_error("--image= is not supported, compiled without blkid support.");
@ -2284,7 +2301,8 @@ static int mount_devices(
const char *where,
const char *root_device, bool root_device_rw,
const char *home_device, bool home_device_rw,
const char *srv_device, bool srv_device_rw) {
const char *srv_device, bool srv_device_rw,
const char *esp_device) {
int r;
assert(where);
@ -2307,6 +2325,27 @@ static int mount_devices(
return log_error_errno(r, "Failed to mount server data directory: %m");
}
if (esp_device) {
const char *mp, *x;
/* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
mp = "/efi";
x = strjoina(arg_directory, mp);
r = dir_is_empty(x);
if (r == -ENOENT) {
mp = "/boot";
x = strjoina(arg_directory, mp);
r = dir_is_empty(x);
}
if (r > 0) {
r = mount_device(esp_device, arg_directory, mp, true);
if (r < 0)
return log_error_errno(r, "Failed to mount ESP: %m");
}
}
return 0;
}
@ -2795,6 +2834,7 @@ static int outer_child(
const char *root_device, bool root_device_rw,
const char *home_device, bool home_device_rw,
const char *srv_device, bool srv_device_rw,
const char *esp_device,
bool interactive,
bool secondary,
int pid_socket,
@ -2856,7 +2896,8 @@ static int outer_child(
r = mount_devices(directory,
root_device, root_device_rw,
home_device, home_device_rw,
srv_device, srv_device_rw);
srv_device, srv_device_rw,
esp_device);
if (r < 0)
return r;
@ -3461,7 +3502,7 @@ static int load_settings(void) {
int main(int argc, char *argv[]) {
_cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *console = NULL;
_cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *esp_device = NULL, *console = NULL;
bool root_device_rw = true, home_device_rw = true, srv_device_rw = true;
_cleanup_close_ int master = -1, image_fd = -1;
_cleanup_fdset_free_ FDSet *fds = NULL;
@ -3643,6 +3684,7 @@ int main(int argc, char *argv[]) {
&root_device, &root_device_rw,
&home_device, &home_device_rw,
&srv_device, &srv_device_rw,
&esp_device,
&secondary);
if (r < 0)
goto finish;
@ -3817,6 +3859,7 @@ int main(int argc, char *argv[]) {
root_device, root_device_rw,
home_device, home_device_rw,
srv_device, srv_device_rw,
esp_device,
interactive,
secondary,
pid_socket_pair[1],