mirror of
https://github.com/systemd/systemd
synced 2026-03-12 16:14:47 +01:00
Compare commits
7 Commits
8ae4aa26c1
...
5b501abfc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b501abfc2 | ||
|
|
48c1393bfb | ||
|
|
a01cf5b2e0 | ||
|
|
8b736d94cd | ||
|
|
6b6d7ccd59 | ||
|
|
7f33ee8bb4 | ||
|
|
05f50caacb |
2
mkosi.extra/usr/lib/repart.d/root.conf
Normal file
2
mkosi.extra/usr/lib/repart.d/root.conf
Normal file
@ -0,0 +1,2 @@
|
||||
[Partition]
|
||||
Type=root
|
||||
@ -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);
|
||||
|
||||
127
src/bootctl/bootctl-cleanup.c
Normal file
127
src/bootctl/bootctl-cleanup.c
Normal 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;
|
||||
}
|
||||
6
src/bootctl/bootctl-cleanup.h
Normal file
6
src/bootctl/bootctl-cleanup.h
Normal 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);
|
||||
@ -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) {
|
||||
|
||||
244
src/bootctl/bootctl-unlink.c
Normal file
244
src/bootctl/bootctl-unlink.c
Normal 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;
|
||||
}
|
||||
8
src/bootctl/bootctl-unlink.h
Normal file
8
src/bootctl/bootctl-unlink.h
Normal 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);
|
||||
@ -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 },
|
||||
|
||||
41
src/bootctl/bootspec-util.c
Normal file
41
src/bootctl/bootspec-util.c
Normal 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);
|
||||
}
|
||||
6
src/bootctl/bootspec-util.h
Normal file
6
src/bootctl/bootspec-util.h
Normal 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);
|
||||
@ -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 += [
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user