1
0
mirror of https://github.com/systemd/systemd synced 2026-03-12 08:04:46 +01:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Mike Yuan
5b501abfc2
terminal-util: handle the case where no system console is active (#40630)
/dev/console might have no backing driver, in which case
/sys/class/tty/console/active is empty. Unlike get_kernel_consoles()
resolve_dev_console() currently proceeds with empty devnode, resulting
in setup_input() -> acquire_terminal() emitting -EISDIR as we're trying
to open /dev/. Let's catch this and report -ENXIO.
2026-02-10 23:59:07 +01:00
Mike Yuan
48c1393bfb
terminal-util: also protect from empty /sys/class/tty/tty0/active 2026-02-10 22:21:53 +01:00
Mike Yuan
a01cf5b2e0
terminal-util: handle the case where no system console is active
/dev/console might have no backing driver, in which case
/sys/class/tty/console/active is empty. Unlike get_kernel_consoles()
resolve_dev_console() currently proceeds with empty devnode,
resulting in setup_input() -> acquire_terminal() emitting -EISDIR
as we're trying to open /dev/. Let's catch this and report -ENXIO.
2026-02-10 22:04:02 +01:00
Lennart Poettering
8b736d94cd bootctl: decouple "list", "unlink", "cleanup"
These operations to quite different things, they just share 2 common
funcs. Let's split them out into separate files.

This also splits up verb_list() into separate calls for the three
operations. This actually fixes issues, as for status/list we want
"unpriv" ESP discovery logic, but for the other two we really should
have privileged discovery logic.

This is preparation for adding "bootctl link" later, but this makes
sense either way, I am sure.
2026-02-10 19:43:04 +00:00
Luca Boccassi
6b6d7ccd59
Two mkosi fixes (#40625) 2026-02-10 17:07:11 +00:00
Daan De Meyer
7f33ee8bb4 mkosi: Grow the root partition on boot
Let's make sure the root partition takes up all the allocated space
on boot.
2026-02-10 16:32:05 +01:00
Daan De Meyer
05f50caacb mkosi: Move generic disk partition before root partition
Otherwise we can't grow the root partition.
2026-02-10 16:31:22 +01:00
14 changed files with 455 additions and 305 deletions

View File

@ -0,0 +1,2 @@
[Partition]
Type=root

View File

@ -1116,7 +1116,7 @@ int resolve_dev_console(char **ret) {
* is a sign for container setups). */
_cleanup_free_ char *chased = NULL;
r = chase("/dev/console", /* root= */ NULL, /* flags= */ 0, &chased, /* ret_fd= */ NULL);
r = chase("/dev/console", /* root= */ NULL, /* flags= */ 0, &chased, /* ret_fd= */ NULL);
if (r < 0)
return r;
if (!path_equal(chased, "/dev/console")) {
@ -1134,6 +1134,8 @@ int resolve_dev_console(char **ret) {
r = read_one_line_file("/sys/class/tty/console/active", &active);
if (r < 0)
return r;
if (r == 0)
return -ENXIO;
/* If multiple log outputs are configured the last one is what /dev/console points to */
const char *tty = strrchr(active, ' ');
@ -1149,6 +1151,8 @@ int resolve_dev_console(char **ret) {
r = read_one_line_file("/sys/class/tty/tty0/active", &active);
if (r < 0)
return r;
if (r == 0)
return -ENXIO;
tty = active;
}
@ -1192,6 +1196,10 @@ int get_kernel_consoles(char ***ret) {
r = read_one_line_file("/sys/class/tty/tty0/active", &tty);
if (r < 0)
return r;
if (r == 0) {
log_debug("No VT active, skipping /dev/tty0.");
continue;
}
}
path = path_join("/dev", tty);

View File

@ -0,0 +1,127 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <unistd.h>
#include "alloc-util.h"
#include "bootctl.h"
#include "bootctl-cleanup.h"
#include "bootctl-unlink.h"
#include "bootctl-util.h"
#include "bootspec.h"
#include "bootspec-util.h"
#include "chase.h"
#include "errno-util.h"
#include "fd-util.h"
#include "hashmap.h"
#include "log.h"
#include "path-util.h"
#include "recurse-dir.h"
static int list_remove_orphaned_file(
RecurseDirEvent event,
const char *path,
int dir_fd,
int inode_fd,
const struct dirent *de,
const struct statx *sx,
void *userdata) {
Hashmap *known_files = userdata;
assert(path);
if (event != RECURSE_DIR_ENTRY)
return RECURSE_DIR_CONTINUE;
if (hashmap_get(known_files, path))
return RECURSE_DIR_CONTINUE; /* keep! */
if (arg_dry_run)
log_info("Would remove %s", path);
else if (unlinkat(dir_fd, de->d_name, 0) < 0)
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to remove \"%s\", ignoring: %m", path);
else
log_info("Removed %s", path);
return RECURSE_DIR_CONTINUE;
}
static int cleanup_orphaned_files(
const BootConfig *config,
const char *root) {
_cleanup_hashmap_free_ Hashmap *known_files = NULL;
_cleanup_free_ char *full = NULL, *p = NULL;
_cleanup_close_ int dir_fd = -EBADF;
int r;
assert(config);
assert(root);
log_info("Cleaning %s", root);
r = settle_entry_token();
if (r < 0)
return r;
r = boot_config_count_known_files(config, root, &known_files);
if (r < 0)
return log_error_errno(r, "Failed to count files in %s: %m", root);
dir_fd = chase_and_open(arg_entry_token, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS,
O_DIRECTORY|O_CLOEXEC, &full);
if (dir_fd == -ENOENT)
return 0;
if (dir_fd < 0)
return log_error_errno(dir_fd, "Failed to open '%s/%s': %m", root, skip_leading_slash(arg_entry_token));
p = path_join("/", arg_entry_token);
if (!p)
return log_oom();
r = recurse_dir(dir_fd, p, 0, UINT_MAX, RECURSE_DIR_SORT, list_remove_orphaned_file, known_files);
if (r < 0)
return log_error_errno(r, "Failed to cleanup %s: %m", full);
return r;
}
int verb_cleanup(int argc, char *argv[], void *userdata) {
dev_t esp_devid = 0, xbootldr_devid = 0;
int r;
r = acquire_esp(/* unprivileged_mode= */ false,
/* graceful= */ false,
/* ret_part= */ NULL,
/* ret_pstart= */ NULL,
/* ret_psize= */ NULL,
/* ret_uuid= */ NULL,
&esp_devid);
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 location: %m");
if (r < 0)
return r;
r = acquire_xbootldr(
/* unprivileged_mode= */ false,
/* ret_uuid= */ NULL,
&xbootldr_devid);
if (r == -EACCES)
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
if (r < 0)
return r;
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
if (r < 0)
return r;
r = 0;
RET_GATHER(r, cleanup_orphaned_files(&config, arg_esp_path));
if (arg_xbootldr_path && xbootldr_devid != esp_devid)
RET_GATHER(r, cleanup_orphaned_files(&config, arg_xbootldr_path));
return r;
}

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "shared-forward.h"
int verb_cleanup(int argc, char *argv[], void *userdata);

View File

@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fnmatch.h>
#include <unistd.h>
#include "sd-varlink.h"
@ -10,56 +9,20 @@
#include "bootctl-status.h"
#include "bootctl-util.h"
#include "bootspec.h"
#include "bootspec-util.h"
#include "chase.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "efi-api.h"
#include "efi-loader.h"
#include "efivars.h"
#include "errno-util.h"
#include "fd-util.h"
#include "hashmap.h"
#include "log.h"
#include "pager.h"
#include "path-util.h"
#include "pretty-print.h"
#include "recurse-dir.h"
#include "string-util.h"
#include "strv.h"
#include "tpm2-util.h"
static int boot_config_load_and_select(
BootConfig *config,
const char *esp_path,
dev_t esp_devid,
const char *xbootldr_path,
dev_t xbootldr_devid) {
int r;
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
* find the same entries twice. */
bool same = esp_path && xbootldr_path && devnum_set_and_equal(esp_devid, xbootldr_devid);
r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
if (r < 0)
return r;
if (!arg_root) {
_cleanup_strv_free_ char **efi_entries = NULL;
r = efi_loader_get_entries(&efi_entries);
if (r == -ENOENT || ERRNO_IS_NEG_NOT_SUPPORTED(r))
log_debug_errno(r, "Boot loader reported no entries.");
else if (r < 0)
log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
else
(void) boot_config_augment_from_loader(config, efi_entries, /* auto_only= */ false);
}
return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
}
static int status_entries(
const BootConfig *config,
const char *esp_path,
@ -637,256 +600,6 @@ int verb_status(int argc, char *argv[], void *userdata) {
return r;
}
static int ref_file(Hashmap **known_files, const char *fn, int increment) {
char *k = NULL;
int n, r;
assert(known_files);
/* just gracefully ignore this. This way the caller doesn't have to verify whether the bootloader
* entry is relevant. */
if (!fn)
return 0;
n = PTR_TO_INT(hashmap_get2(*known_files, fn, (void**)&k));
n += increment;
assert(n >= 0);
if (n == 0) {
(void) hashmap_remove(*known_files, fn);
free(k);
} else if (!k) {
_cleanup_free_ char *t = NULL;
t = strdup(fn);
if (!t)
return -ENOMEM;
r = hashmap_ensure_put(known_files, &path_hash_ops_free, t, INT_TO_PTR(n));
if (r < 0)
return r;
TAKE_PTR(t);
} else {
r = hashmap_update(*known_files, fn, INT_TO_PTR(n));
if (r < 0)
return r;
}
return n;
}
static void deref_unlink_file(Hashmap **known_files, const char *fn, const char *root) {
_cleanup_free_ char *path = NULL;
int r;
assert(known_files);
/* just gracefully ignore this. This way the caller doesn't
have to verify whether the bootloader entry is relevant */
if (!fn || !root)
return;
r = ref_file(known_files, fn, -1);
if (r < 0)
return (void) log_warning_errno(r, "Failed to deref \"%s\", ignoring: %m", fn);
if (r > 0)
return;
if (arg_dry_run) {
r = chase_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, F_OK, &path);
if (r < 0)
log_info_errno(r, "Unable to determine whether \"%s\" exists, ignoring: %m", fn);
else
log_info("Would remove \"%s\"", path);
return;
}
r = chase_and_unlink(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, 0, &path);
if (r >= 0)
log_info("Removed \"%s\"", path);
else if (r != -ENOENT)
return (void) log_warning_errno(r, "Failed to remove \"%s\", ignoring: %m", fn);
_cleanup_free_ char *d = NULL;
if (path_extract_directory(fn, &d) >= 0 && !path_equal(d, "/")) {
r = chase_and_unlink(d, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, AT_REMOVEDIR, NULL);
if (r < 0 && !IN_SET(r, -ENOTEMPTY, -ENOENT))
log_warning_errno(r, "Failed to remove directory \"%s\", ignoring: %m", d);
}
}
static int count_known_files(const BootConfig *config, const char* root, Hashmap **ret_known_files) {
_cleanup_hashmap_free_ Hashmap *known_files = NULL;
int r;
assert(config);
assert(ret_known_files);
for (size_t i = 0; i < config->n_entries; i++) {
const BootEntry *e = config->entries + i;
if (!path_equal(e->root, root))
continue;
r = ref_file(&known_files, e->kernel, +1);
if (r < 0)
return r;
r = ref_file(&known_files, e->efi, +1);
if (r < 0)
return r;
r = ref_file(&known_files, e->uki, +1);
if (r < 0)
return r;
STRV_FOREACH(s, e->initrd) {
r = ref_file(&known_files, *s, +1);
if (r < 0)
return r;
}
r = ref_file(&known_files, e->device_tree, +1);
if (r < 0)
return r;
STRV_FOREACH(s, e->device_tree_overlay) {
r = ref_file(&known_files, *s, +1);
if (r < 0)
return r;
}
}
*ret_known_files = TAKE_PTR(known_files);
return 0;
}
static int boot_config_find_in(const BootConfig *config, const char *root, const char *id) {
assert(config);
if (!root || !id)
return -ENOENT;
for (size_t i = 0; i < config->n_entries; i++)
if (path_equal(config->entries[i].root, root) &&
fnmatch(id, config->entries[i].id, FNM_CASEFOLD) == 0)
return i;
return -ENOENT;
}
static int unlink_entry(const BootConfig *config, const char *root, const char *id) {
_cleanup_hashmap_free_ Hashmap *known_files = NULL;
const BootEntry *e = NULL;
int r;
assert(config);
r = count_known_files(config, root, &known_files);
if (r < 0)
return log_error_errno(r, "Failed to count files in %s: %m", root);
r = boot_config_find_in(config, root, id);
if (r < 0)
return 0; /* There is nothing to remove. */
if (r == config->default_entry)
log_warning("%s is the default boot entry", id);
if (r == config->selected_entry)
log_warning("%s is the selected boot entry", id);
e = &config->entries[r];
deref_unlink_file(&known_files, e->kernel, e->root);
deref_unlink_file(&known_files, e->efi, e->root);
deref_unlink_file(&known_files, e->uki, e->root);
STRV_FOREACH(s, e->initrd)
deref_unlink_file(&known_files, *s, e->root);
deref_unlink_file(&known_files, e->device_tree, e->root);
STRV_FOREACH(s, e->device_tree_overlay)
deref_unlink_file(&known_files, *s, e->root);
if (arg_dry_run)
log_info("Would remove \"%s\"", e->path);
else {
r = chase_and_unlink(e->path, root, CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, 0, NULL);
if (r == -ENOENT)
return 0; /* Already removed? */
if (r < 0)
return log_error_errno(r, "Failed to remove \"%s\": %m", e->path);
log_info("Removed %s", e->path);
}
return 0;
}
static int list_remove_orphaned_file(
RecurseDirEvent event,
const char *path,
int dir_fd,
int inode_fd,
const struct dirent *de,
const struct statx *sx,
void *userdata) {
Hashmap *known_files = userdata;
assert(path);
if (event != RECURSE_DIR_ENTRY)
return RECURSE_DIR_CONTINUE;
if (hashmap_get(known_files, path))
return RECURSE_DIR_CONTINUE; /* keep! */
if (arg_dry_run)
log_info("Would remove %s", path);
else if (unlinkat(dir_fd, de->d_name, 0) < 0)
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to remove \"%s\", ignoring: %m", path);
else
log_info("Removed %s", path);
return RECURSE_DIR_CONTINUE;
}
static int cleanup_orphaned_files(
const BootConfig *config,
const char *root) {
_cleanup_hashmap_free_ Hashmap *known_files = NULL;
_cleanup_free_ char *full = NULL, *p = NULL;
_cleanup_close_ int dir_fd = -EBADF;
int r;
assert(config);
assert(root);
log_info("Cleaning %s", root);
r = settle_entry_token();
if (r < 0)
return r;
r = count_known_files(config, root, &known_files);
if (r < 0)
return log_error_errno(r, "Failed to count files in %s: %m", root);
dir_fd = chase_and_open(arg_entry_token, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS,
O_DIRECTORY|O_CLOEXEC, &full);
if (dir_fd == -ENOENT)
return 0;
if (dir_fd < 0)
return log_error_errno(dir_fd, "Failed to open '%s/%s': %m", root, skip_leading_slash(arg_entry_token));
p = path_join("/", arg_entry_token);
if (!p)
return log_oom();
r = recurse_dir(dir_fd, p, 0, UINT_MAX, RECURSE_DIR_SORT, list_remove_orphaned_file, known_files);
if (r < 0)
return log_error_errno(r, "Failed to cleanup %s: %m", full);
return r;
}
int verb_list(int argc, char *argv[], void *userdata) {
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
dev_t esp_devid = 0, xbootldr_devid = 0;
@ -918,19 +631,8 @@ int verb_list(int argc, char *argv[], void *userdata) {
return 0;
}
if (streq(argv[0], "list")) {
pager_open(arg_pager_flags);
return show_boot_entries(&config, arg_json_format_flags);
} else if (streq(argv[0], "cleanup")) {
if (arg_xbootldr_path && xbootldr_devid != esp_devid)
cleanup_orphaned_files(&config, arg_xbootldr_path);
return cleanup_orphaned_files(&config, arg_esp_path);
} else {
assert(streq(argv[0], "unlink"));
if (arg_xbootldr_path && xbootldr_devid != esp_devid)
r = unlink_entry(&config, arg_xbootldr_path, argv[1]);
return RET_GATHER(r, unlink_entry(&config, arg_esp_path, argv[1]));
}
pager_open(arg_pager_flags);
return show_boot_entries(&config, arg_json_format_flags);
}
int vl_method_list_boot_entries(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {

View File

@ -0,0 +1,244 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <fnmatch.h>
#include "alloc-util.h"
#include "bootctl.h"
#include "bootctl-unlink.h"
#include "bootspec.h"
#include "bootspec-util.h"
#include "chase.h"
#include "errno-util.h"
#include "hashmap.h"
#include "log.h"
#include "path-util.h"
#include "strv.h"
static int ref_file(Hashmap **known_files, const char *fn, int increment) {
char *k = NULL;
int n, r;
assert(known_files);
/* just gracefully ignore this. This way the caller doesn't have to verify whether the bootloader
* entry is relevant. */
if (!fn)
return 0;
n = PTR_TO_INT(hashmap_get2(*known_files, fn, (void**)&k));
n += increment;
assert(n >= 0);
if (n == 0) {
(void) hashmap_remove(*known_files, fn);
free(k);
} else if (!k) {
_cleanup_free_ char *t = NULL;
t = strdup(fn);
if (!t)
return -ENOMEM;
r = hashmap_ensure_put(known_files, &path_hash_ops_free, t, INT_TO_PTR(n));
if (r < 0)
return r;
TAKE_PTR(t);
} else {
r = hashmap_update(*known_files, fn, INT_TO_PTR(n));
if (r < 0)
return r;
}
return n;
}
int boot_config_count_known_files(
const BootConfig *config,
const char* root,
Hashmap **ret_known_files) {
_cleanup_hashmap_free_ Hashmap *known_files = NULL;
int r;
assert(config);
assert(ret_known_files);
for (size_t i = 0; i < config->n_entries; i++) {
const BootEntry *e = config->entries + i;
if (!path_equal(e->root, root))
continue;
r = ref_file(&known_files, e->kernel, +1);
if (r < 0)
return r;
r = ref_file(&known_files, e->efi, +1);
if (r < 0)
return r;
r = ref_file(&known_files, e->uki, +1);
if (r < 0)
return r;
STRV_FOREACH(s, e->initrd) {
r = ref_file(&known_files, *s, +1);
if (r < 0)
return r;
}
r = ref_file(&known_files, e->device_tree, +1);
if (r < 0)
return r;
STRV_FOREACH(s, e->device_tree_overlay) {
r = ref_file(&known_files, *s, +1);
if (r < 0)
return r;
}
}
*ret_known_files = TAKE_PTR(known_files);
return 0;
}
static void deref_unlink_file(Hashmap **known_files, const char *fn, const char *root) {
_cleanup_free_ char *path = NULL;
int r;
assert(known_files);
/* just gracefully ignore this. This way the caller doesn't
have to verify whether the bootloader entry is relevant */
if (!fn || !root)
return;
r = ref_file(known_files, fn, -1);
if (r < 0)
return (void) log_warning_errno(r, "Failed to deref \"%s\", ignoring: %m", fn);
if (r > 0)
return;
if (arg_dry_run) {
r = chase_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, F_OK, &path);
if (r < 0)
log_info_errno(r, "Unable to determine whether \"%s\" exists, ignoring: %m", fn);
else
log_info("Would remove \"%s\"", path);
return;
}
r = chase_and_unlink(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, 0, &path);
if (r >= 0)
log_info("Removed \"%s\"", path);
else if (r != -ENOENT)
return (void) log_warning_errno(r, "Failed to remove \"%s\", ignoring: %m", fn);
_cleanup_free_ char *d = NULL;
if (path_extract_directory(fn, &d) >= 0 && !path_equal(d, "/")) {
r = chase_and_unlink(d, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, AT_REMOVEDIR, NULL);
if (r < 0 && !IN_SET(r, -ENOTEMPTY, -ENOENT))
log_warning_errno(r, "Failed to remove directory \"%s\", ignoring: %m", d);
}
}
static int boot_config_find_in(const BootConfig *config, const char *root, const char *id) {
assert(config);
if (!root || !id)
return -ENOENT;
for (size_t i = 0; i < config->n_entries; i++)
if (path_equal(config->entries[i].root, root) &&
fnmatch(id, config->entries[i].id, FNM_CASEFOLD) == 0)
return i;
return -ENOENT;
}
static int unlink_entry(const BootConfig *config, const char *root, const char *id) {
_cleanup_hashmap_free_ Hashmap *known_files = NULL;
const BootEntry *e = NULL;
int r;
assert(config);
r = boot_config_count_known_files(config, root, &known_files);
if (r < 0)
return log_error_errno(r, "Failed to count files in %s: %m", root);
r = boot_config_find_in(config, root, id);
if (r < 0)
return 0; /* There is nothing to remove. */
if (r == config->default_entry)
log_warning("%s is the default boot entry", id);
if (r == config->selected_entry)
log_warning("%s is the selected boot entry", id);
e = &config->entries[r];
deref_unlink_file(&known_files, e->kernel, e->root);
deref_unlink_file(&known_files, e->efi, e->root);
deref_unlink_file(&known_files, e->uki, e->root);
STRV_FOREACH(s, e->initrd)
deref_unlink_file(&known_files, *s, e->root);
deref_unlink_file(&known_files, e->device_tree, e->root);
STRV_FOREACH(s, e->device_tree_overlay)
deref_unlink_file(&known_files, *s, e->root);
if (arg_dry_run)
log_info("Would remove \"%s\"", e->path);
else {
r = chase_and_unlink(e->path, root, CHASE_PROHIBIT_SYMLINKS|CHASE_TRIGGER_AUTOFS, 0, NULL);
if (r == -ENOENT)
return 0; /* Already removed? */
if (r < 0)
return log_error_errno(r, "Failed to remove \"%s\": %m", e->path);
log_info("Removed %s", e->path);
}
return 0;
}
int verb_unlink(int argc, char *argv[], void *userdata) {
dev_t esp_devid = 0, xbootldr_devid = 0;
int r;
r = acquire_esp(/* unprivileged_mode= */ false,
/* graceful= */ false,
/* ret_part= */ NULL,
/* ret_pstart= */ NULL,
/* ret_psize= */ NULL,
/* ret_uuid= */ NULL,
&esp_devid);
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 location: %m");
if (r < 0)
return r;
r = acquire_xbootldr(
/* unprivileged_mode= */ false,
/* ret_uuid= */ NULL,
&xbootldr_devid);
if (r == -EACCES)
return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
if (r < 0)
return r;
_cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
r = boot_config_load_and_select(
&config,
arg_esp_path,
esp_devid,
arg_xbootldr_path,
xbootldr_devid);
if (r < 0)
return r;
r = 0;
RET_GATHER(r, unlink_entry(&config, arg_esp_path, argv[1]));
if (arg_xbootldr_path && xbootldr_devid != esp_devid)
RET_GATHER(r, unlink_entry(&config, arg_xbootldr_path, argv[1]));
return r;
}

View File

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "shared-forward.h"
int verb_unlink(int argc, char *argv[], void *userdata);
int boot_config_count_known_files(const BootConfig *config, const char* root, Hashmap **ret_known_files);

View File

@ -8,12 +8,14 @@
#include "blockdev-util.h"
#include "boot-entry.h"
#include "bootctl.h"
#include "bootctl-cleanup.h"
#include "bootctl-install.h"
#include "bootctl-random-seed.h"
#include "bootctl-reboot-to-firmware.h"
#include "bootctl-set-efivar.h"
#include "bootctl-status.h"
#include "bootctl-uki.h"
#include "bootctl-unlink.h"
#include "build.h"
#include "devnum-util.h"
#include "dissect-image.h"
@ -697,8 +699,8 @@ static int bootctl_main(int argc, char *argv[]) {
{ "kernel-identify", 2, 2, 0, verb_kernel_identify },
{ "kernel-inspect", 2, 2, 0, verb_kernel_inspect },
{ "list", VERB_ANY, 1, 0, verb_list },
{ "unlink", 2, 2, 0, verb_list },
{ "cleanup", VERB_ANY, 1, 0, verb_list },
{ "unlink", 2, 2, 0, verb_unlink },
{ "cleanup", VERB_ANY, 1, 0, verb_cleanup },
{ "set-default", 2, 2, 0, verb_set_efivar },
{ "set-oneshot", 2, 2, 0, verb_set_efivar },
{ "set-timeout", 2, 2, 0, verb_set_efivar },

View File

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bootctl.h"
#include "bootspec-util.h"
#include "devnum-util.h"
#include "efi-loader.h"
#include "errno-util.h"
#include "log.h"
#include "strv.h"
int boot_config_load_and_select(
BootConfig *config,
const char *esp_path,
dev_t esp_devid,
const char *xbootldr_path,
dev_t xbootldr_devid) {
int r;
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
* find the same entries twice. */
bool same = esp_path && xbootldr_path && devnum_set_and_equal(esp_devid, xbootldr_devid);
r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
if (r < 0)
return r;
if (!arg_root) {
_cleanup_strv_free_ char **efi_entries = NULL;
r = efi_loader_get_entries(&efi_entries);
if (r == -ENOENT || ERRNO_IS_NEG_NOT_SUPPORTED(r))
log_debug_errno(r, "Boot loader reported no entries.");
else if (r < 0)
log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
else
(void) boot_config_augment_from_loader(config, efi_entries, /* auto_only= */ false);
}
return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
}

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "bootspec.h"
int boot_config_load_and_select(BootConfig *config, const char *esp_path, dev_t esp_devid, const char *xbootldr_path, dev_t xbootldr_devid);

View File

@ -1,14 +1,17 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
bootctl_sources = files(
'bootctl.c',
'bootctl-install.c',
'bootctl-random-seed.c',
'bootctl-reboot-to-firmware.c',
'bootctl-set-efivar.c',
'bootctl-status.c',
'bootctl-uki.c',
'bootctl-unlink.c',
'bootctl-cleanup.c',
'bootctl-util.c',
'bootctl.c',
'bootspec-util.c',
)
executables += [

View File

@ -46,6 +46,7 @@ typedef enum UserStorage UserStorage;
typedef struct Bitmap Bitmap;
typedef struct BPFProgram BPFProgram;
typedef struct BootConfig BootConfig;
typedef struct BusObjectImplementation BusObjectImplementation;
typedef struct CalendarSpec CalendarSpec;
typedef struct Condition Condition;