mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 04:03:36 +08:00
Merge pull request #24541 from poettering/bootspec-tweaks
bootspec: slightly stricter validation + process tries-left/tries-done counters in filenames
This commit is contained in:
commit
d96c7550a0
14
TODO
14
TODO
@ -597,10 +597,9 @@ Features:
|
||||
* doc: prep a document explaining PID 1's internal logic, i.e. transactions,
|
||||
jobs, units
|
||||
|
||||
* bootspec: remove tries counter from boot entry ids
|
||||
|
||||
* bootspec: bring UEFI and userspace enumeration of bootspec entries back into
|
||||
sync, i.e. parse out tries in both
|
||||
sync, i.e. parse out architecture field in sd-boot (currently only done in
|
||||
userspace)
|
||||
|
||||
* automatically ignore threaded cgroups in cg_xyz().
|
||||
|
||||
@ -1594,14 +1593,6 @@ Features:
|
||||
|
||||
* firstboot: make it useful to be run immediately after yum --installroot to set up a machine. (most specifically, make --copy-root-password work even if /etc/passwd already exists
|
||||
|
||||
* sd-boot: define a drop-in dir in the ESP that may contain X.509
|
||||
certificates. If the firmware is detected to be in setup mode, automatically
|
||||
enroll them as PK/KEK/db, turn off setup mode and proceed. Optionally,
|
||||
instead of auto-enrolling them add them to the sd-boot menu, giving the user
|
||||
the option to manually enroll them, after selecting the menu entry. This way,
|
||||
installer images can just drop the certfiicates in the ESP, and on first boot
|
||||
can easily enroll the keys without ever booting up.
|
||||
|
||||
* efi stub: optionally, load initrd from disk as a separate file, HMAC check it
|
||||
with key from TPM, bound to PCR, refusing if failing. This would then allow
|
||||
traditional distros that generate initrds locally to secure them with TPM:
|
||||
@ -1623,7 +1614,6 @@ Features:
|
||||
- show whether UEFI audit mode is available
|
||||
- teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
|
||||
- teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
|
||||
- bootspec: properly support boot attempt counters when parsing entry file names
|
||||
|
||||
* kernel-install:
|
||||
- optionally, support generating type #2 entries instead of type #1, including signing them
|
||||
|
@ -53,17 +53,231 @@ static void boot_entry_free(BootEntry *entry) {
|
||||
strv_free(entry->device_tree_overlay);
|
||||
}
|
||||
|
||||
static int mangle_path(
|
||||
const char *fname,
|
||||
unsigned line,
|
||||
const char *field,
|
||||
const char *p,
|
||||
char **ret) {
|
||||
|
||||
_cleanup_free_ char *c = NULL;
|
||||
|
||||
assert(field);
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
/* Spec leaves open if prefixed with "/" or not, let's normalize that */
|
||||
if (path_is_absolute(p))
|
||||
c = strdup(p);
|
||||
else
|
||||
c = strjoin("/", p);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We only reference files, never directories */
|
||||
if (endswith(c, "/")) {
|
||||
log_syntax(NULL, LOG_WARNING, fname, line, 0, "Path in field '%s' has trailing slash, ignoring: %s", field, c);
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove duplicate "/" */
|
||||
path_simplify(c);
|
||||
|
||||
/* No ".." or "." or so */
|
||||
if (!path_is_normalized(c)) {
|
||||
log_syntax(NULL, LOG_WARNING, fname, line, 0, "Path in field '%s' is not normalized, ignoring: %s", field, c);
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_path_one(
|
||||
const char *fname,
|
||||
unsigned line,
|
||||
const char *field,
|
||||
char **s,
|
||||
const char *p) {
|
||||
|
||||
_cleanup_free_ char *c = NULL;
|
||||
int r;
|
||||
|
||||
assert(field);
|
||||
assert(s);
|
||||
assert(p);
|
||||
|
||||
r = mangle_path(fname, line, field, p, &c);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
free_and_replace(*s, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_path_strv(
|
||||
const char *fname,
|
||||
unsigned line,
|
||||
const char *field,
|
||||
char ***s,
|
||||
const char *p) {
|
||||
|
||||
char *c;
|
||||
int r;
|
||||
|
||||
assert(field);
|
||||
assert(s);
|
||||
assert(p);
|
||||
|
||||
r = mangle_path(fname, line, field, p, &c);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return strv_consume(s, c);
|
||||
}
|
||||
|
||||
static int parse_path_many(
|
||||
const char *fname,
|
||||
unsigned line,
|
||||
const char *field,
|
||||
char ***s,
|
||||
const char *p) {
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL, **f = NULL;
|
||||
int r;
|
||||
|
||||
l = strv_split(p, NULL);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
char *c;
|
||||
|
||||
r = mangle_path(fname, line, field, *i, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = strv_consume(&f, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return strv_extend_strv(s, f, /* filter_duplicates= */ false);
|
||||
}
|
||||
|
||||
static int parse_tries(const char *fname, const char **p, unsigned *ret) {
|
||||
_cleanup_free_ char *d = NULL;
|
||||
unsigned tries;
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
assert(fname);
|
||||
assert(p);
|
||||
assert(*p);
|
||||
assert(ret);
|
||||
|
||||
n = strspn(*p, DIGITS);
|
||||
if (n == 0) {
|
||||
*ret = UINT_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
d = strndup(*p, n);
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
r = safe_atou_full(d, 10, &tries);
|
||||
if (r >= 0 && tries > INT_MAX) /* sd-boot allows INT_MAX, let's use the same limit */
|
||||
r = -ERANGE;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse tries counter of filename '%s': %m", fname);
|
||||
|
||||
*p = *p + n;
|
||||
*ret = tries;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int boot_filename_extract_tries(
|
||||
const char *fname,
|
||||
char **ret_stripped,
|
||||
unsigned *ret_tries_left,
|
||||
unsigned *ret_tries_done) {
|
||||
|
||||
unsigned tries_left = UINT_MAX, tries_done = UINT_MAX;
|
||||
_cleanup_free_ char *stripped = NULL;
|
||||
const char *p, *suffix, *m;
|
||||
int r;
|
||||
|
||||
assert(fname);
|
||||
assert(ret_stripped);
|
||||
assert(ret_tries_left);
|
||||
assert(ret_tries_done);
|
||||
|
||||
/* Be liberal with suffix, only insist on a dot. After all we want to cover any capitalization here
|
||||
* (vfat is case insensitive after all), and at least .efi and .conf as suffix. */
|
||||
suffix = strrchr(fname, '.');
|
||||
if (!suffix)
|
||||
goto nothing;
|
||||
|
||||
p = m = memrchr(fname, '+', suffix - fname);
|
||||
if (!p)
|
||||
goto nothing;
|
||||
p++;
|
||||
|
||||
r = parse_tries(fname, &p, &tries_left);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
goto nothing;
|
||||
|
||||
if (*p == '-') {
|
||||
p++;
|
||||
|
||||
r = parse_tries(fname, &p, &tries_done);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
goto nothing;
|
||||
}
|
||||
|
||||
if (p != suffix)
|
||||
goto nothing;
|
||||
|
||||
stripped = strndup(fname, m - fname);
|
||||
if (!stripped)
|
||||
return log_oom();
|
||||
|
||||
if (!strextend(&stripped, suffix))
|
||||
return log_oom();
|
||||
|
||||
*ret_stripped = TAKE_PTR(stripped);
|
||||
*ret_tries_left = tries_left;
|
||||
*ret_tries_done = tries_done;
|
||||
|
||||
return 0;
|
||||
|
||||
nothing:
|
||||
stripped = strdup(fname);
|
||||
if (!stripped)
|
||||
return log_oom();
|
||||
|
||||
*ret_stripped = TAKE_PTR(stripped);
|
||||
*ret_tries_left = *ret_tries_done = UINT_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int boot_entry_load_type1(
|
||||
FILE *f,
|
||||
const char *root,
|
||||
const char *dir,
|
||||
const char *id,
|
||||
const char *fname,
|
||||
BootEntry *entry) {
|
||||
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {
|
||||
.type = BOOT_ENTRY_CONF,
|
||||
};
|
||||
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = BOOT_ENTRY_INIT(BOOT_ENTRY_CONF);
|
||||
unsigned line = 1;
|
||||
char *c;
|
||||
int r;
|
||||
@ -71,27 +285,27 @@ static int boot_entry_load_type1(
|
||||
assert(f);
|
||||
assert(root);
|
||||
assert(dir);
|
||||
assert(id);
|
||||
assert(fname);
|
||||
assert(entry);
|
||||
|
||||
/* Loads a Type #1 boot menu entry from the specified FILE* object */
|
||||
|
||||
if (!efi_loader_entry_name_valid(id))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", id);
|
||||
r = boot_filename_extract_tries(fname, &tmp.id, &tmp.tries_left, &tmp.tries_done);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c = endswith_no_case(id, ".conf");
|
||||
if (!efi_loader_entry_name_valid(tmp.id))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", fname);
|
||||
|
||||
c = endswith_no_case(tmp.id, ".conf");
|
||||
if (!c)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", id);
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", fname);
|
||||
|
||||
tmp.id = strdup(id);
|
||||
if (!tmp.id)
|
||||
return log_oom();
|
||||
|
||||
tmp.id_old = strndup(id, c - id); /* Without .conf suffix */
|
||||
tmp.id_old = strndup(tmp.id, c - tmp.id); /* Without .conf suffix */
|
||||
if (!tmp.id_old)
|
||||
return log_oom();
|
||||
|
||||
tmp.path = path_join(dir, id);
|
||||
tmp.path = path_join(dir, fname);
|
||||
if (!tmp.path)
|
||||
return log_oom();
|
||||
|
||||
@ -107,30 +321,31 @@ static int boot_entry_load_type1(
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r == -ENOBUFS)
|
||||
return log_error_errno(r, "%s:%u: Line too long", tmp.path, line);
|
||||
return log_syntax(NULL, LOG_ERR, tmp.path, line, r, "Line too long.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s:%u: Error while reading: %m", tmp.path, line);
|
||||
return log_syntax(NULL, LOG_ERR, tmp.path, line, r, "Error while reading: %m");
|
||||
|
||||
line++;
|
||||
|
||||
if (IN_SET(*strstrip(buf), '#', '\0'))
|
||||
p = strstrip(buf);
|
||||
if (IN_SET(p[0], '#', '\0'))
|
||||
continue;
|
||||
|
||||
p = buf;
|
||||
r = extract_first_word(&p, &field, " \t", 0);
|
||||
r = extract_first_word(&p, &field, NULL, 0);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to parse config file %s line %u: %m", tmp.path, line);
|
||||
log_syntax(NULL, LOG_WARNING, tmp.path, line, r, "Failed to parse, ignoring line: %m");
|
||||
continue;
|
||||
}
|
||||
if (r == 0) {
|
||||
log_warning("%s:%u: Bad syntax", tmp.path, line);
|
||||
log_syntax(NULL, LOG_WARNING, tmp.path, line, 0, "Bad syntax, ignoring line.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isempty(p)) {
|
||||
/* Some fields can reasonably have an empty value. In other cases warn. */
|
||||
if (!STR_IN_SET(field, "options", "devicetree-overlay"))
|
||||
log_warning("%s:%u: Field %s without value", tmp.path, line, field);
|
||||
log_syntax(NULL, LOG_WARNING, tmp.path, line, 0, "Field '%s' without value, ignoring line.", field);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -147,27 +362,21 @@ static int boot_entry_load_type1(
|
||||
else if (streq(field, "options"))
|
||||
r = strv_extend(&tmp.options, p);
|
||||
else if (streq(field, "linux"))
|
||||
r = free_and_strdup(&tmp.kernel, p);
|
||||
r = parse_path_one(tmp.path, line, field, &tmp.kernel, p);
|
||||
else if (streq(field, "efi"))
|
||||
r = free_and_strdup(&tmp.efi, p);
|
||||
r = parse_path_one(tmp.path, line, field, &tmp.efi, p);
|
||||
else if (streq(field, "initrd"))
|
||||
r = strv_extend(&tmp.initrd, p);
|
||||
r = parse_path_strv(tmp.path, line, field, &tmp.initrd, p);
|
||||
else if (streq(field, "devicetree"))
|
||||
r = free_and_strdup(&tmp.device_tree, p);
|
||||
else if (streq(field, "devicetree-overlay")) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
l = strv_split(p, NULL);
|
||||
if (!l)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend_strv(&tmp.device_tree_overlay, l, false);
|
||||
} else {
|
||||
log_notice("%s:%u: Unknown line \"%s\", ignoring.", tmp.path, line, field);
|
||||
r = parse_path_one(tmp.path, line, field, &tmp.device_tree, p);
|
||||
else if (streq(field, "devicetree-overlay"))
|
||||
r = parse_path_many(tmp.path, line, field, &tmp.device_tree_overlay, p);
|
||||
else {
|
||||
log_syntax(NULL, LOG_WARNING, tmp.path, line, 0, "Unknown line '%s', ignoring.", field);
|
||||
continue;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s:%u: Error while reading: %m", tmp.path, line);
|
||||
return log_syntax(NULL, LOG_ERR, tmp.path, line, r, "Error while parsing: %m");
|
||||
}
|
||||
|
||||
*entry = tmp;
|
||||
@ -180,19 +389,19 @@ int boot_config_load_type1(
|
||||
FILE *f,
|
||||
const char *root,
|
||||
const char *dir,
|
||||
const char *id) {
|
||||
const char *fname) {
|
||||
int r;
|
||||
|
||||
assert(config);
|
||||
assert(f);
|
||||
assert(root);
|
||||
assert(dir);
|
||||
assert(id);
|
||||
assert(fname);
|
||||
|
||||
if (!GREEDY_REALLOC0(config->entries, config->n_entries + 1))
|
||||
return log_oom();
|
||||
|
||||
r = boot_entry_load_type1(f, root, dir, id, config->entries + config->n_entries);
|
||||
r = boot_entry_load_type1(f, root, dir, fname, config->entries + config->n_entries);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -239,23 +448,27 @@ int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) {
|
||||
if (r == 0)
|
||||
break;
|
||||
if (r == -ENOBUFS)
|
||||
return log_error_errno(r, "%s:%u: Line too long", path, line);
|
||||
return log_syntax(NULL, LOG_ERR, path, line, r, "Line too long.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
|
||||
return log_syntax(NULL, LOG_ERR, path, line, r, "Error while reading: %m");
|
||||
|
||||
line++;
|
||||
|
||||
if (IN_SET(*strstrip(buf), '#', '\0'))
|
||||
p = strstrip(buf);
|
||||
if (IN_SET(p[0], '#', '\0'))
|
||||
continue;
|
||||
|
||||
p = buf;
|
||||
r = extract_first_word(&p, &field, " \t", 0);
|
||||
r = extract_first_word(&p, &field, NULL, 0);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line);
|
||||
log_syntax(NULL, LOG_WARNING, path, line, r, "Failed to parse, ignoring line: %m");
|
||||
continue;
|
||||
}
|
||||
if (r == 0) {
|
||||
log_warning("%s:%u: Bad syntax", path, line);
|
||||
log_syntax(NULL, LOG_WARNING, path, line, 0, "Bad syntax, ignoring line.");
|
||||
continue;
|
||||
}
|
||||
if (isempty(p)) {
|
||||
log_syntax(NULL, LOG_WARNING, path, line, 0, "Field '%s' without value, ignoring line.", field);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -276,11 +489,11 @@ int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) {
|
||||
else if (streq(field, "beep"))
|
||||
r = free_and_strdup(&config->beep, p);
|
||||
else {
|
||||
log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
|
||||
log_syntax(NULL, LOG_WARNING, path, line, 0, "Unknown line '%s', ignoring.", field);
|
||||
continue;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
|
||||
return log_syntax(NULL, LOG_ERR, path, line, r, "Error while parsing: %m");
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -430,7 +643,7 @@ static int boot_entries_find_type1(
|
||||
continue;
|
||||
|
||||
r = boot_config_load_type1(config, f, root, dir, de->d_name);
|
||||
if (r == -ENOMEM)
|
||||
if (r == -ENOMEM) /* ignore all other errors */
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -444,11 +657,9 @@ static int boot_entry_load_unified(
|
||||
const char *cmdline,
|
||||
BootEntry *ret) {
|
||||
|
||||
_cleanup_free_ char *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
|
||||
_cleanup_free_ char *fname = NULL, *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
|
||||
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {
|
||||
.type = BOOT_ENTRY_UNIFIED,
|
||||
};
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = BOOT_ENTRY_INIT(BOOT_ENTRY_UNIFIED);
|
||||
const char *k, *good_name, *good_version, *good_sort_key;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
@ -491,10 +702,14 @@ static int boot_entry_load_unified(
|
||||
&good_sort_key))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
|
||||
|
||||
r = path_extract_filename(path, &tmp.id);
|
||||
r = path_extract_filename(path, &fname);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract file name from '%s': %m", path);
|
||||
|
||||
r = boot_filename_extract_tries(fname, &tmp.id, &tmp.tries_left, &tmp.tries_done);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!efi_loader_entry_name_valid(tmp.id))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", tmp.id);
|
||||
|
||||
@ -1040,6 +1255,8 @@ int boot_config_augment_from_loader(
|
||||
.title = TAKE_PTR(t),
|
||||
.path = TAKE_PTR(p),
|
||||
.reported_by_loader = true,
|
||||
.tries_left = UINT_MAX,
|
||||
.tries_done = UINT_MAX,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1132,6 +1349,15 @@ int show_boot_entry(
|
||||
|
||||
printf(" source: %s\n", link ?: e->path);
|
||||
}
|
||||
if (e->tries_left != UINT_MAX) {
|
||||
printf(" tries: %u left", e->tries_left);
|
||||
|
||||
if (e->tries_done != UINT_MAX)
|
||||
printf("; %u done\n", e->tries_done);
|
||||
else
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (e->sort_key)
|
||||
printf(" sort-key: %s\n", e->sort_key);
|
||||
if (e->version)
|
||||
@ -1210,7 +1436,9 @@ int show_boot_entries(const BootConfig *config, JsonFormatFlags json_format) {
|
||||
JSON_BUILD_PAIR_CONDITION(e->efi, "efi", JSON_BUILD_STRING(e->efi)),
|
||||
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", JSON_BUILD_STRV(e->initrd)),
|
||||
JSON_BUILD_PAIR_CONDITION(e->device_tree, "devicetree", JSON_BUILD_STRING(e->device_tree)),
|
||||
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", JSON_BUILD_STRV(e->device_tree_overlay))));
|
||||
JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", JSON_BUILD_STRV(e->device_tree_overlay)),
|
||||
JSON_BUILD_PAIR_CONDITION(e->tries_left != UINT_MAX, "triesLeft", JSON_BUILD_UNSIGNED(e->tries_left)),
|
||||
JSON_BUILD_PAIR_CONDITION(e->tries_done != UINT_MAX, "triesDone", JSON_BUILD_UNSIGNED(e->tries_done))));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
|
@ -39,8 +39,17 @@ typedef struct BootEntry {
|
||||
char **initrd;
|
||||
char *device_tree;
|
||||
char **device_tree_overlay;
|
||||
unsigned tries_left;
|
||||
unsigned tries_done;
|
||||
} BootEntry;
|
||||
|
||||
#define BOOT_ENTRY_INIT(t) \
|
||||
{ \
|
||||
.type = (t), \
|
||||
.tries_left = UINT_MAX, \
|
||||
.tries_done = UINT_MAX, \
|
||||
}
|
||||
|
||||
typedef struct BootConfig {
|
||||
char *default_pattern;
|
||||
char *timeout;
|
||||
@ -116,3 +125,5 @@ int show_boot_entry(
|
||||
int show_boot_entries(
|
||||
const BootConfig *config,
|
||||
JsonFormatFlags json_format);
|
||||
|
||||
int boot_filename_extract_tries(const char *fname, char **ret_stripped, unsigned *ret_tries_left, unsigned *ret_tries_done);
|
||||
|
@ -93,4 +93,57 @@ TEST_RET(bootspec_sort) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_extract_tries_one(const char *fname, int ret, const char *stripped, unsigned tries_left, unsigned tries_done) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
unsigned l = UINT_MAX, d = UINT_MAX;
|
||||
|
||||
assert_se(boot_filename_extract_tries(fname, &p, &l, &d) == ret);
|
||||
assert_se(streq_ptr(p, stripped));
|
||||
assert_se(l == tries_left);
|
||||
assert_se(d == tries_done);
|
||||
}
|
||||
|
||||
TEST_RET(bootspec_extract_tries) {
|
||||
test_extract_tries_one("foo.conf", 0, "foo.conf", UINT_MAX, UINT_MAX);
|
||||
|
||||
test_extract_tries_one("foo+0.conf", 0, "foo.conf", 0, UINT_MAX);
|
||||
test_extract_tries_one("foo+1.conf", 0, "foo.conf", 1, UINT_MAX);
|
||||
test_extract_tries_one("foo+2.conf", 0, "foo.conf", 2, UINT_MAX);
|
||||
test_extract_tries_one("foo+33.conf", 0, "foo.conf", 33, UINT_MAX);
|
||||
|
||||
assert_cc(INT_MAX == INT32_MAX);
|
||||
test_extract_tries_one("foo+2147483647.conf", 0, "foo.conf", 2147483647, UINT_MAX);
|
||||
test_extract_tries_one("foo+2147483648.conf", -ERANGE, NULL, UINT_MAX, UINT_MAX);
|
||||
|
||||
test_extract_tries_one("foo+33-0.conf", 0, "foo.conf", 33, 0);
|
||||
test_extract_tries_one("foo+33-1.conf", 0, "foo.conf", 33, 1);
|
||||
test_extract_tries_one("foo+33-107.conf", 0, "foo.conf", 33, 107);
|
||||
test_extract_tries_one("foo+33-107.efi", 0, "foo.efi", 33, 107);
|
||||
test_extract_tries_one("foo+33-2147483647.conf", 0, "foo.conf", 33, 2147483647);
|
||||
test_extract_tries_one("foo+33-2147483648.conf", -ERANGE, NULL, UINT_MAX, UINT_MAX);
|
||||
|
||||
test_extract_tries_one("foo+007-000008.conf", 0, "foo.conf", 7, 8);
|
||||
|
||||
test_extract_tries_one("foo-1.conf", 0, "foo-1.conf", UINT_MAX, UINT_MAX);
|
||||
test_extract_tries_one("foo-999.conf", 0, "foo-999.conf", UINT_MAX, UINT_MAX);
|
||||
test_extract_tries_one("foo-.conf", 0, "foo-.conf", UINT_MAX, UINT_MAX);
|
||||
|
||||
test_extract_tries_one("foo+.conf", 0, "foo+.conf", UINT_MAX, UINT_MAX);
|
||||
test_extract_tries_one("+.conf", 0, "+.conf", UINT_MAX, UINT_MAX);
|
||||
test_extract_tries_one("-.conf", 0, "-.conf", UINT_MAX, UINT_MAX);
|
||||
test_extract_tries_one("", 0, "", UINT_MAX, UINT_MAX);
|
||||
|
||||
test_extract_tries_one("+1.", 0, ".", 1, UINT_MAX);
|
||||
test_extract_tries_one("+1-7.", 0, ".", 1, 7);
|
||||
|
||||
test_extract_tries_one("some+name+24324-22.efi", 0, "some+name.efi", 24324, 22);
|
||||
test_extract_tries_one("sels+2-3+7-6.", 0, "sels+2-3.", 7, 6);
|
||||
test_extract_tries_one("a+1-2..", 0, "a+1-2..", UINT_MAX, UINT_MAX);
|
||||
test_extract_tries_one("ses.sgesge.+4-1.efi", 0, "ses.sgesge..efi", 4, 1);
|
||||
test_extract_tries_one("abc+0x4.conf", 0, "abc+0x4.conf", UINT_MAX, UINT_MAX);
|
||||
test_extract_tries_one("def+1-0x3.conf", 0, "def+1-0x3.conf", UINT_MAX, UINT_MAX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
Loading…
Reference in New Issue
Block a user