mirror of
https://github.com/systemd/systemd.git
synced 2025-01-10 05:18:17 +03:00
Merge pull request #26103 from lnussel/bootctl
bootctl: unlink and cleanup functions
This commit is contained in:
commit
0bdf70f3fa
@ -153,6 +153,21 @@
|
||||
disables the timeout while always showing the menu. When an empty string ("") is specified the
|
||||
bootloader will revert to its default menu timeout.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>unlink</option> <replaceable>ID</replaceable></term>
|
||||
|
||||
<listitem><para>Removes a boot loader entry including the files it refers to. Takes a single boot
|
||||
loader entry ID string or a glob pattern as argument. Referenced files such as kernel or initrd are
|
||||
only removed if no other entry refers to them.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>cleanup</option></term>
|
||||
|
||||
<listitem><para>Removes files from the ESP and XBOOTLDR partitions that belong to the entry token but
|
||||
are not referenced in any boot loader entries.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
@ -409,6 +424,14 @@
|
||||
the firmware's boot option menu.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--dry-run</option></term>
|
||||
<listitem><para>Dry run for <option>--unlink</option> and <option>--cleanup</option>.</para>
|
||||
|
||||
<para>In dry run mode, the unlink and cleanup operations only print the files that would get deleted
|
||||
without actually deleting them.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager"/>
|
||||
<xi:include href="standard-options.xml" xpointer="json" />
|
||||
<xi:include href="standard-options.xml" xpointer="help"/>
|
||||
|
@ -4051,6 +4051,7 @@ public_programs += exe
|
||||
if want_tests != 'false' and want_kernel_install
|
||||
test('test-kernel-install',
|
||||
test_kernel_install_sh,
|
||||
env : test_env,
|
||||
args : [exe.full_path(), loaderentry_install])
|
||||
endif
|
||||
|
||||
|
@ -31,7 +31,7 @@ _bootctl() {
|
||||
local i verb comps
|
||||
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||
local -A OPTS=(
|
||||
[STANDALONE]='-h --help -p --print-esp-path -x --print-boot-path --version --no-variables --no-pager --graceful'
|
||||
[STANDALONE]='-h --help -p --print-esp-path -x --print-boot-path --version --no-variables --no-pager --graceful --dry-run'
|
||||
[ARG]='--esp-path --boot-path --make-machine-id-directory --root --image --install-source'
|
||||
)
|
||||
|
||||
@ -67,8 +67,8 @@ _bootctl() {
|
||||
|
||||
local -A VERBS=(
|
||||
# systemd-efi-options takes an argument, but it is free-form, so we cannot complete it
|
||||
[STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list set-timeout set-timeout-oneshot'
|
||||
[BOOTENTRY]='set-default set-oneshot'
|
||||
[STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list set-timeout set-timeout-oneshot cleanup'
|
||||
[BOOTENTRY]='set-default set-oneshot unlink'
|
||||
[BOOLEAN]='reboot-to-firmware'
|
||||
)
|
||||
|
||||
|
@ -24,6 +24,10 @@ _bootctl_set-oneshot() {
|
||||
_bootctl_comp_ids
|
||||
}
|
||||
|
||||
_bootctl_unlink() {
|
||||
_bootctl_comp_ids
|
||||
}
|
||||
|
||||
_bootctl_reboot-to-firmware() {
|
||||
local -a _completions
|
||||
_completions=( yes no )
|
||||
@ -48,6 +52,8 @@ _bootctl_reboot-to-firmware() {
|
||||
"set-oneshot:Set the default boot loader entry only for the next boot"
|
||||
"set-timeout:Set the menu timeout"
|
||||
"set-timeout-oneshot:Set the menu timeout for the next boot only"
|
||||
"unlink:Remove boot loader entry"
|
||||
"cleanup:Remove files in ESP not referenced in any boot entry"
|
||||
)
|
||||
if (( CURRENT == 1 )); then
|
||||
_describe -t commands 'bootctl command' _bootctl_cmds || compadd "$@"
|
||||
@ -73,6 +79,7 @@ _arguments \
|
||||
'--no-variables[Do not touch EFI variables]' \
|
||||
'--no-pager[Do not pipe output into a pager]' \
|
||||
'--graceful[Do not fail when locating ESP or writing fails]' \
|
||||
'--dry-run[Dry run (unlink and cleanup)]' \
|
||||
'--root=[Operate under the specified directory]:PATH' \
|
||||
'--image=[Operate on the specified image]:PATH' \
|
||||
'--install-source[Where to pick files when using --root=/--image=]:options:(image host auto)' \
|
||||
|
@ -689,3 +689,42 @@ int chase_symlinks_and_fopen_unlocked(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_unlink(
|
||||
const char *path,
|
||||
const char *root,
|
||||
ChaseSymlinksFlags chase_flags,
|
||||
int unlink_flags,
|
||||
char **ret_path) {
|
||||
|
||||
_cleanup_free_ char *p = NULL, *rp = NULL, *dir = NULL, *fname = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = path_extract_directory(path, &dir);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = path_extract_filename(path, &fname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = chase_symlinks_and_open(dir, root, chase_flags, O_PATH|O_DIRECTORY|O_CLOEXEC, ret_path ? &p : NULL);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (p) {
|
||||
rp = path_join(p, fname);
|
||||
if (!rp)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (unlinkat(fd, fname, unlink_flags) < 0)
|
||||
return -errno;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(rp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -34,5 +34,6 @@ int chase_symlinks_and_opendir(const char *path, const char *root, ChaseSymlinks
|
||||
int chase_symlinks_and_stat(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
|
||||
int chase_symlinks_and_access(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int access_mode, char **ret_path, int *ret_fd);
|
||||
int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, ChaseSymlinksFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
|
||||
int chase_symlinks_and_unlink(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int unlink_flags, char **ret_path);
|
||||
|
||||
int chase_symlinks_at(int dir_fd, const char *path, ChaseSymlinksFlags flags, char **ret_path, int *ret_fd);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "find-esp.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tpm2-util.h"
|
||||
|
||||
@ -503,6 +504,250 @@ 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_put(known_files, 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;
|
||||
|
||||
/* 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_symlinks_and_access(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, &path, NULL);
|
||||
if (r < 0)
|
||||
log_info("Unable to determine whether \"%s\" exists, ignoring: %m", fn);
|
||||
else
|
||||
log_info("Would remove %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
r = chase_symlinks_and_unlink(fn, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, 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", path ?: fn);
|
||||
|
||||
_cleanup_free_ char *d = NULL;
|
||||
if (path_extract_directory(fn, &d) >= 0 && !path_equal(d, "/")) {
|
||||
r = chase_symlinks_and_unlink(d, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, AT_REMOVEDIR, NULL);
|
||||
if (r < 0 && !IN_SET(r, -ENOTEMPTY, -ENOENT))
|
||||
log_warning_errno(r, "Failed to remove directoy \"%s\", ignoring: %m", d);
|
||||
}
|
||||
}
|
||||
|
||||
static int count_known_files(const BootConfig *config, const char* root, Hashmap **ret_known_files) {
|
||||
_cleanup_(hashmap_free_free_keyp) Hashmap *known_files = NULL;
|
||||
int r = 0;
|
||||
|
||||
assert(config);
|
||||
assert(ret_known_files);
|
||||
|
||||
known_files = hashmap_new(&path_hash_ops);
|
||||
if (!known_files)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
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 -1;
|
||||
|
||||
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 -1;
|
||||
}
|
||||
|
||||
static int unlink_entry(const BootConfig *config, const char *root, const char *id) {
|
||||
_cleanup_(hashmap_free_free_keyp) 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 -ENOENT;
|
||||
|
||||
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);
|
||||
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_symlinks_and_unlink(e->path, root, CHASE_PROHIBIT_SYMLINKS, 0, NULL);
|
||||
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);
|
||||
assert(known_files);
|
||||
|
||||
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_warning_errno(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_free_keyp) Hashmap *known_files = NULL;
|
||||
_cleanup_free_ char *full = NULL, *p = NULL;
|
||||
_cleanup_close_ int dir_fd = -1;
|
||||
int r = -1;
|
||||
|
||||
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_symlinks_and_open(arg_entry_token, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
|
||||
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, 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;
|
||||
@ -534,6 +779,24 @@ int verb_list(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
return show_boot_entries(&config, arg_json_format_flags);
|
||||
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]);
|
||||
if (r == 0 || r != -ENOENT)
|
||||
return r;
|
||||
}
|
||||
return unlink_entry(&config, arg_esp_path, argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
int verb_unlink(int argc, char *argv[], void *userdata) {
|
||||
return verb_list(argc, argv, userdata);
|
||||
}
|
||||
|
@ -2,3 +2,4 @@
|
||||
|
||||
int verb_status(int argc, char *argv[], void *userdata);
|
||||
int verb_list(int argc, char *argv[], void *userdata);
|
||||
int verb_unlink(int argc, char *argv[], void *userdata);
|
||||
|
@ -48,6 +48,7 @@ char *arg_root = NULL;
|
||||
char *arg_image = NULL;
|
||||
InstallSource arg_install_source = ARG_INSTALL_SOURCE_AUTO;
|
||||
char *arg_efi_boot_option_description = NULL;
|
||||
bool arg_dry_run = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
|
||||
@ -145,6 +146,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" set-timeout SECONDS Set the menu timeout\n"
|
||||
" set-timeout-oneshot SECONDS\n"
|
||||
" Set the menu timeout for the next boot only\n"
|
||||
" unlink ID Remove boot loader entry\n"
|
||||
" cleanup Remove files in ESP not referenced in any boot entry\n"
|
||||
"\n%3$ssystemd-boot Commands:%4$s\n"
|
||||
" install Install systemd-boot to the ESP and EFI variables\n"
|
||||
" update Update systemd-boot in the ESP and EFI variables\n"
|
||||
@ -179,6 +182,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" Install all supported EFI architectures\n"
|
||||
" --efi-boot-option-description=DESCRIPTION\n"
|
||||
" Description of the entry in the boot option list\n"
|
||||
" --dry-run Dry run (unlink and cleanup)\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@ -206,6 +210,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_JSON,
|
||||
ARG_ARCH_ALL,
|
||||
ARG_EFI_BOOT_OPTION_DESCRIPTION,
|
||||
ARG_DRY_RUN,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -230,6 +235,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "all-architectures", no_argument, NULL, ARG_ARCH_ALL },
|
||||
{ "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
|
||||
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -379,6 +385,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_DRY_RUN:
|
||||
arg_dry_run = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -387,7 +397,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
|
||||
"install", "update", "remove", "is-installed", "random-seed"))
|
||||
"install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Options --root= and --image= are not supported with verb %s.",
|
||||
argv[optind]);
|
||||
@ -398,6 +408,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_install_source != ARG_INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
|
||||
|
||||
if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry is only supported with --unlink or --cleanup");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -412,6 +425,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_unlink },
|
||||
{ "cleanup", VERB_ANY, 1, 0, verb_list },
|
||||
{ "set-default", 2, 2, 0, verb_set_efivar },
|
||||
{ "set-oneshot", 2, 2, 0, verb_set_efivar },
|
||||
{ "set-timeout", 2, 2, 0, verb_set_efivar },
|
||||
|
@ -39,6 +39,7 @@ extern char *arg_root;
|
||||
extern char *arg_image;
|
||||
extern InstallSource arg_install_source;
|
||||
extern char *arg_efi_boot_option_description;
|
||||
extern bool arg_dry_run;
|
||||
|
||||
static inline const char *arg_dollar_boot_path(void) {
|
||||
/* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
|
||||
|
@ -32,7 +32,7 @@ MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
|
||||
ENTRY_TOKEN="$KERNEL_INSTALL_ENTRY_TOKEN"
|
||||
BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT"
|
||||
|
||||
BOOT_MNT="$(stat -c %m "$BOOT_ROOT")"
|
||||
[ -n "$BOOT_MNT" ] || BOOT_MNT="$(stat -c %m "$BOOT_ROOT")"
|
||||
if [ "$BOOT_MNT" = '/' ]; then
|
||||
ENTRY_DIR="$ENTRY_DIR_ABS"
|
||||
else
|
||||
|
@ -9,6 +9,8 @@ plugin="${2:?}"
|
||||
|
||||
D="$(mktemp --tmpdir --directory "test-kernel-install.XXXXXXXXXX")"
|
||||
|
||||
export _KERNEL_INSTALL_BOOTCTL="$PROJECT_BUILD_ROOT/bootctl"
|
||||
|
||||
# shellcheck disable=SC2064
|
||||
trap "rm -rf '$D'" EXIT INT QUIT PIPE
|
||||
mkdir -p "$D/boot"
|
||||
@ -31,6 +33,7 @@ EOF
|
||||
export KERNEL_INSTALL_CONF_ROOT="$D/sources"
|
||||
export KERNEL_INSTALL_PLUGINS="$plugin"
|
||||
export BOOT_ROOT="$D/boot"
|
||||
export BOOT_MNT="$D/boot"
|
||||
export MACHINE_ID='3e0484f3634a418b8e6a39e8828b03e3'
|
||||
|
||||
"$kernel_install" -v add 1.1.1 "$D/sources/linux" "$D/sources/initrd"
|
||||
@ -82,3 +85,32 @@ grep -qE '^initrd .*/the-token/1.1.1/initrd' "$entry"
|
||||
|
||||
grep -qE 'image' "$BOOT_ROOT/the-token/1.1.1/linux"
|
||||
grep -qE 'initrd' "$BOOT_ROOT/the-token/1.1.1/initrd"
|
||||
|
||||
if test -x "$_KERNEL_INSTALL_BOOTCTL"; then
|
||||
echo "Testing bootctl"
|
||||
e2="${entry%+*}_2.conf"
|
||||
cp "$entry" "$e2"
|
||||
export SYSTEMD_ESP_PATH=/
|
||||
|
||||
# create file that is not referenced. Check if cleanup removes
|
||||
# it but leaves the rest alone
|
||||
:> "$BOOT_ROOT/the-token/1.1.2/initrd"
|
||||
"$_KERNEL_INSTALL_BOOTCTL" --root="$BOOT_ROOT" cleanup
|
||||
test ! -e "$BOOT_ROOT/the-token/1.1.2/initrd"
|
||||
test -e "$BOOT_ROOT/the-token/1.1.2/linux"
|
||||
test -e "$BOOT_ROOT/the-token/1.1.1/linux"
|
||||
test -e "$BOOT_ROOT/the-token/1.1.1/initrd"
|
||||
# now remove duplicated entry and make sure files are left over
|
||||
"$_KERNEL_INSTALL_BOOTCTL" --root="$BOOT_ROOT" unlink "${e2##*/}"
|
||||
test -e "$BOOT_ROOT/the-token/1.1.1/linux"
|
||||
test -e "$BOOT_ROOT/the-token/1.1.1/initrd"
|
||||
test -e "$entry"
|
||||
test ! -e "$e2"
|
||||
# remove last entry referencing those files
|
||||
entry_id="${entry##*/}"
|
||||
entry_id="${entry_id%+*}.conf"
|
||||
"$_KERNEL_INSTALL_BOOTCTL" --root="$BOOT_ROOT" unlink "$entry_id"
|
||||
test ! -e "$entry"
|
||||
test ! -e "$BOOT_ROOT/the-token/1.1.1/linux"
|
||||
test ! -e "$BOOT_ROOT/the-token/1.1.1/initrd"
|
||||
fi
|
||||
|
@ -15,6 +15,7 @@ path = run_command(sh, '-c', 'echo "$PATH"', check: true).stdout().strip()
|
||||
test_env = environment()
|
||||
test_env.set('SYSTEMD_LANGUAGE_FALLBACK_MAP', language_fallback_map)
|
||||
test_env.set('PATH', project_build_root + ':' + path)
|
||||
test_env.set('PROJECT_BUILD_ROOT', project_build_root)
|
||||
|
||||
############################################################
|
||||
|
||||
|
@ -330,6 +330,13 @@ TEST(chase_symlinks) {
|
||||
assert_se(sd_id128_equal(a, b));
|
||||
}
|
||||
|
||||
assert_se(lstat(p, &st) >= 0);
|
||||
r = chase_symlinks_and_unlink(p, NULL, 0, 0, &result);
|
||||
assert_se(path_equal(result, p));
|
||||
result = mfree(result);
|
||||
assert_se(r == 0);
|
||||
assert_se(lstat(p, &st) == -1 && errno == ENOENT);
|
||||
|
||||
/* Test CHASE_NOFOLLOW */
|
||||
|
||||
p = strjoina(temp, "/target");
|
||||
|
Loading…
Reference in New Issue
Block a user