/
niceSOFT
/
systemd
Обзор
Документация
Войти
/
niceSOFT
/
systemd
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/bootctl/bootspec-util.c
255 строк
9 KB
Lennart Poettering
bootctl: add helpers that format a type1 menu entry filename from a commit nr
01 май 2026, 08:10
01 май 2026, 08:10
4d0f6ac
Код
Авторство
О чём код?
/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" #include "boot-entry.h" #include "bootspec-util.h" #include "devnum-util.h" #include "efi-loader.h" #include "errno-util.h" #include "log.h" #include "parse-util.h" #include "path-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "utf8.h" int boot_config_load_and_select( BootConfig *config, const char *root, 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 (!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= */ !!root); } int boot_entry_make_commit_filename( const char *entry_token, uint64_t entry_commit, const char *version, unsigned profile_nr, unsigned tries_left, char **ret) { assert(entry_token); assert(ret); /* Generate a new entry filename from the entry token, the commit number, and (optionally) the * image/OS version, (if non-zero) the profile number, and (unless UINT_MAX) the number of tries * left. */ if (!boot_entry_token_valid(entry_token)) return -EINVAL; if (!entry_commit_valid(entry_commit)) return -EINVAL; _cleanup_free_ char *filename = asprintf_safe("%s-commit_%" PRIu64, entry_token, entry_commit); if (!filename) return -ENOMEM; if (version && !strextend(&filename, ".", version)) return -ENOMEM; if (profile_nr > 0 && strextendf(&filename, "@%u", profile_nr) < 0) return -ENOMEM; if (tries_left != UINT_MAX && strextendf(&filename, "+%u", tries_left) < 0) return -ENOMEM; if (!strextend(&filename, ".conf")) return -ENOMEM; if (!filename_is_valid(filename) || string_has_cc(filename, /* ok= */ NULL) || !utf8_is_valid(filename)) return -EINVAL; *ret = TAKE_PTR(filename); return 0; } int boot_entry_parse_commit_filename( const char *filename, char **ret_entry_token, uint64_t *ret_entry_commit) { int r; assert(filename); if (!filename_is_valid(filename)) return -EINVAL; _cleanup_free_ char *stripped = NULL; r = boot_filename_extract_tries(filename, &stripped, /* ret_tries_left= */ NULL, /* ret_tries_done= */ NULL); if (r < 0) return r; const char *a = strrstr_no_case(stripped, "-commit_"); if (!a) return -EBADMSG; const char *c = endswith_no_case(stripped, ".conf"); if (!c) return -EBADMSG; assert(a < c); _cleanup_free_ char *entry_token = strndup(stripped, a - stripped); if (!entry_token) return -ENOMEM; if (!boot_entry_token_valid(entry_token)) return -EBADMSG; const char *b = a + STRLEN("-commit_"); size_t n = strspn(b, DIGITS); if (n <= 0 || !IN_SET(b[n], '+', '.', '@')) return -EBADMSG; _cleanup_free_ char *entry_commit_string = strndup(b, n); if (!entry_commit_string) return -ENOMEM; uint64_t entry_commit; r = safe_atou64_full(entry_commit_string, 10, &entry_commit); if (r < 0) return r; if (!entry_commit_valid(entry_commit)) return -EBADMSG; if (ret_entry_token) *ret_entry_token = TAKE_PTR(entry_token); if (ret_entry_commit) *ret_entry_commit = entry_commit; return 0; } int boot_entry_parse_commit( BootEntry *entry, char **ret_entry_token, uint64_t *ret_entry_commit) { int r; assert(entry); if (entry->type != BOOT_ENTRY_TYPE1) return -EADDRNOTAVAIL; _cleanup_free_ char *fn = NULL; r = path_extract_filename(entry->path, &fn); if (r < 0) return r; return boot_entry_parse_commit_filename(fn, ret_entry_token, ret_entry_commit); } int boot_config_find_oldest_commit( BootConfig *config, const char *entry_token, char ***ret_ids) { int r; assert(config); assert(entry_token); assert(ret_ids); uint64_t commit_oldest = UINT64_MAX, commit_2nd_oldest = UINT64_MAX, commit_blocked = UINT64_MAX; /* First, determine which commit is the oldest (that isn't the current one), and hence the candidate * to be removed */ FOREACH_ARRAY(b, config->entries, config->n_entries) { _cleanup_free_ char *et = NULL; uint64_t ec; r = boot_entry_parse_commit(b, &et, &ec); if (r == -EADDRNOTAVAIL) continue; if (r < 0) { log_debug_errno(r, "Failed to parse entry filename of '%s', ignoring: %m", strna(b->id)); continue; } if (!streq(et, entry_token)) /* Not ours? */ continue; if (ec < commit_oldest) { commit_2nd_oldest = commit_oldest; commit_oldest = ec; } else if (ec > commit_oldest && ec < commit_2nd_oldest) commit_2nd_oldest = ec; if (boot_config_selected_entry(config) == b) { assert(commit_blocked == UINT64_MAX); commit_blocked = ec; } } uint64_t commit_picked; if (commit_oldest == UINT64_MAX) return log_debug_errno(SYNTHETIC_ERRNO(ENXIO), "No matching entry found while determining oldest entry."); if (commit_oldest != commit_blocked) commit_picked = commit_oldest; else { if (commit_2nd_oldest == UINT64_MAX) return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Only matching entry found while determining oldest entry is current one, skipping it."); assert(commit_2nd_oldest != commit_blocked); commit_picked = commit_2nd_oldest; } log_debug("Determined commit %" PRIu64 " to be oldest.", commit_picked); /* Second loop: actually remove all entries matching this commit (which can be multiple, since UKIs * have profiles) */ _cleanup_(strv_freep) char **l = NULL; FOREACH_ARRAY(b, config->entries, config->n_entries) { _cleanup_free_ char *et = NULL; uint64_t ec; r = boot_entry_parse_commit(b, &et, &ec); if (r == -EADDRNOTAVAIL) continue; if (r < 0) { log_debug_errno(r, "Failed to parse entry filename of '%s', ignoring: %m", strna(b->id)); continue; } if (!streq(et, entry_token)) /* Not ours? */ continue; if (ec != commit_picked) continue; r = strv_extend(&l, b->id); if (r < 0) return r; } /* The list cannot be empty, the first loop above and the 2nd loop must have found the same matching * entries, and if the first loop didn't find any we'd not come this far. */ assert(!strv_isempty(l)); *ret_ids = TAKE_PTR(l); return 0; }