This is already being reviewed in separate thread Le lun. 30 sept. 2024, 20:46, Leo Sandoval <lsand...@redhat.com> a écrit :
> From: Peter Jones <pjo...@redhat.com> > > The BootLoaderSpec (BLS) defines a scheme where different bootloaders can > share a format for boot items and a configuration directory that accepts > these common configurations as drop-in files. > > Signed-off-by: Peter Jones <pjo...@redhat.com> > Signed-off-by: Javier Martinez Canillas <javi...@redhat.com> > [wjt: some cleanups and fixes] > Signed-off-by: Will Thompson <w...@endlessm.com> > --- > grub-core/Makefile.core.def | 11 + > grub-core/commands/blscfg.c | 1177 ++++++++++++++++++++++++++++++++ > grub-core/commands/legacycfg.c | 5 +- > grub-core/commands/loadenv.c | 77 +-- > grub-core/commands/loadenv.h | 93 +++ > grub-core/commands/menuentry.c | 20 +- > grub-core/normal/main.c | 6 + > include/grub/compiler.h | 2 + > include/grub/menu.h | 13 + > include/grub/normal.h | 2 +- > 10 files changed, 1324 insertions(+), 82 deletions(-) > create mode 100644 grub-core/commands/blscfg.c > create mode 100644 grub-core/commands/loadenv.h > > diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def > index 0bffbfea9..47c0fc755 100644 > --- a/grub-core/Makefile.core.def > +++ b/grub-core/Makefile.core.def > @@ -842,6 +842,16 @@ module = { > common = commands/blocklist.c; > }; > > +module = { > + name = blscfg; > + common = commands/blscfg.c; > + common = commands/loadenv.h; > + enable = powerpc_ieee1275; > + enable = efi; > + enable = i386_pc; > + enable = emu; > +}; > + > module = { > name = boot; > common = commands/boot.c; > @@ -1009,6 +1019,7 @@ module = { > module = { > name = loadenv; > common = commands/loadenv.c; > + common = commands/loadenv.h; > common = lib/envblk.c; > }; > > diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c > new file mode 100644 > index 000000000..e907a6a5d > --- /dev/null > +++ b/grub-core/commands/blscfg.c > @@ -0,0 +1,1177 @@ > +/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/ > + > +/* bls.c - implementation of the boot loader spec */ > + > +/* > + * GRUB -- GRand Unified Bootloader > + * > + * GRUB is free software: you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation, either version 3 of the License, or > + * (at your option) any later version. > + * > + * GRUB is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <grub/list.h> > +#include <grub/types.h> > +#include <grub/misc.h> > +#include <grub/mm.h> > +#include <grub/err.h> > +#include <grub/dl.h> > +#include <grub/extcmd.h> > +#include <grub/i18n.h> > +#include <grub/fs.h> > +#include <grub/env.h> > +#include <grub/file.h> > +#include <grub/normal.h> > +#include <grub/lib/envblk.h> > + > +#include <stdbool.h> > + > +GRUB_MOD_LICENSE ("GPLv3+"); > + > +#include "loadenv.h" > + > +#define GRUB_BLS_CONFIG_PATH "/loader/entries/" > +#ifdef GRUB_MACHINE_EMU > +#define GRUB_BOOT_DEVICE "/boot" > +#else > +#define GRUB_BOOT_DEVICE "($root)" > +#endif > + > +struct keyval > +{ > + const char *key; > + char *val; > +}; > + > +static struct bls_entry *entries = NULL; > + > +#define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) > + > +static int bls_add_keyval(struct bls_entry *entry, char *key, char *val) > +{ > + char *k, *v; > + struct keyval **kvs, *kv; > + int new_n = entry->nkeyvals + 1; > + > + kvs = grub_realloc (entry->keyvals, new_n * sizeof (struct keyval *)); > + if (!kvs) > + return grub_error (GRUB_ERR_OUT_OF_MEMORY, > + "couldn't find space for BLS entry"); > + entry->keyvals = kvs; > + > + kv = grub_malloc (sizeof (struct keyval)); > + if (!kv) > + return grub_error (GRUB_ERR_OUT_OF_MEMORY, > + "couldn't find space for BLS entry"); > + > + k = grub_strdup (key); > + if (!k) > + { > + grub_free (kv); > + return grub_error (GRUB_ERR_OUT_OF_MEMORY, > + "couldn't find space for BLS entry"); > + } > + > + v = grub_strdup (val); > + if (!v) > + { > + grub_free (k); > + grub_free (kv); > + return grub_error (GRUB_ERR_OUT_OF_MEMORY, > + "couldn't find space for BLS entry"); > + } > + > + kv->key = k; > + kv->val = v; > + > + entry->keyvals[entry->nkeyvals] = kv; > + grub_dprintf("blscfg", "new keyval at %p:%s:%s\n", > entry->keyvals[entry->nkeyvals], k, v); > + entry->nkeyvals = new_n; > + > + return 0; > +} > + > +/* Find they value of the key named by keyname. If there are allowed to > be > + * more than one, pass a pointer to an int set to -1 the first time, and > pass > + * the same pointer through each time after, and it'll return them in > sorted > + * order as defined in the BLS fragment file */ > +static char *bls_get_val(struct bls_entry *entry, const char *keyname, > int *last) > +{ > + int idx, start = 0; > + struct keyval *kv = NULL; > + > + if (last) > + start = *last + 1; > + > + for (idx = start; idx < entry->nkeyvals; idx++) { > + kv = entry->keyvals[idx]; > + > + if (!grub_strcmp (keyname, kv->key)) > + break; > + } > + > + if (idx == entry->nkeyvals) { > + if (last) > + *last = -1; > + return NULL; > + } > + > + if (last) > + *last = idx; > + > + return kv->val; > +} > + > +#define goto_return(x) ({ ret = (x); goto finish; }) > + > +/* compare alpha and numeric segments of two versions */ > +/* return 1: a is newer than b */ > +/* 0: a and b are the same version */ > +/* -1: b is newer than a */ > +static int vercmp(const char * a, const char * b) > +{ > + char oldch1, oldch2; > + char *abuf, *bbuf; > + char *str1, *str2; > + char * one, * two; > + int rc; > + int isnum; > + int ret = 0; > + > + grub_dprintf("blscfg", "%s comparing %s and %s\n", __func__, a, b); > + if (!grub_strcmp(a, b)) > + return 0; > + > + abuf = grub_malloc(grub_strlen(a) + 1); > + bbuf = grub_malloc(grub_strlen(b) + 1); > + str1 = abuf; > + str2 = bbuf; > + grub_strcpy(str1, a); > + grub_strcpy(str2, b); > + > + one = str1; > + two = str2; > + > + /* loop through each version segment of str1 and str2 and compare > them */ > + while (*one || *two) { > + while (*one && !grub_isalnum(*one) && *one != '~' && *one != '+') > one++; > + while (*two && !grub_isalnum(*two) && *two != '~' && *two != '+') > two++; > + > + /* handle the tilde separator, it sorts before everything else */ > + if (*one == '~' || *two == '~') { > + if (*one != '~') goto_return (1); > + if (*two != '~') goto_return (-1); > + one++; > + two++; > + continue; > + } > + > + /* > + * Handle plus separator. Concept is the same as tilde, > + * except that if one of the strings ends (base version), > + * the other is considered as higher version. > + */ > + if (*one == '+' || *two == '+') { > + if (!*one) return -1; > + if (!*two) return 1; > + if (*one != '+') goto_return (1); > + if (*two != '+') goto_return (-1); > + one++; > + two++; > + continue; > + } > + > + /* If we ran to the end of either, we are finished with the loop */ > + if (!(*one && *two)) break; > + > + str1 = one; > + str2 = two; > + > + /* grab first completely alpha or completely numeric segment */ > + /* leave one and two pointing to the start of the alpha or numeric > */ > + /* segment and walk str1 and str2 to end of segment */ > + if (grub_isdigit(*str1)) { > + while (*str1 && grub_isdigit(*str1)) str1++; > + while (*str2 && grub_isdigit(*str2)) str2++; > + isnum = 1; > + } else { > + while (*str1 && grub_isalpha(*str1)) str1++; > + while (*str2 && grub_isalpha(*str2)) str2++; > + isnum = 0; > + } > + > + /* save character at the end of the alpha or numeric segment */ > + /* so that they can be restored after the comparison */ > + oldch1 = *str1; > + *str1 = '\0'; > + oldch2 = *str2; > + *str2 = '\0'; > + > + /* this cannot happen, as we previously tested to make sure that */ > + /* the first string has a non-null segment */ > + if (one == str1) goto_return(-1); /* arbitrary */ > + > + /* take care of the case where the two version segments are */ > + /* different types: one numeric, the other alpha (i.e. empty) */ > + /* numeric segments are always newer than alpha segments */ > + /* XXX See patch #60884 (and details) from bugzilla #50977. */ > + if (two == str2) goto_return (isnum ? 1 : -1); > + > + if (isnum) { > + grub_size_t onelen, twolen; > + /* this used to be done by converting the digit segments */ > + /* to ints using atoi() - it's changed because long */ > + /* digit segments can overflow an int - this should fix that. > */ > + > + /* throw away any leading zeros - it's a number, right? */ > + while (*one == '0') one++; > + while (*two == '0') two++; > + > + /* whichever number has more digits wins */ > + onelen = grub_strlen(one); > + twolen = grub_strlen(two); > + if (onelen > twolen) goto_return (1); > + if (twolen > onelen) goto_return (-1); > + } > + > + /* grub_strcmp will return which one is greater - even if the two > */ > + /* segments are alpha or if they are numeric. don't return */ > + /* if they are equal because there might be more segments to */ > + /* compare */ > + rc = grub_strcmp(one, two); > + if (rc) goto_return (rc < 1 ? -1 : 1); > + > + /* restore character that was replaced by null above */ > + *str1 = oldch1; > + one = str1; > + *str2 = oldch2; > + two = str2; > + } > + > + /* this catches the case where all numeric and alpha segments have */ > + /* compared identically but the segment sepparating characters were */ > + /* different */ > + if ((!*one) && (!*two)) goto_return (0); > + > + /* whichever version still has characters left over wins */ > + if (!*one) goto_return (-1); else goto_return (1); > + > +finish: > + grub_free (abuf); > + grub_free (bbuf); > + return ret; > +} > + > +/* returns name/version/release */ > +/* NULL string pointer returned if nothing found */ > +static void > +split_package_string (char *package_string, char **name, > + char **version, char **release) > +{ > + char *package_version, *package_release; > + > + /* Release */ > + package_release = grub_strrchr (package_string, '-'); > + > + if (package_release != NULL) > + *package_release++ = '\0'; > + > + *release = package_release; > + > + if (name == NULL) > + { > + *version = package_string; > + } > + else > + { > + /* Version */ > + package_version = grub_strrchr(package_string, '-'); > + > + if (package_version != NULL) > + *package_version++ = '\0'; > + > + *version = package_version; > + /* Name */ > + *name = package_string; > + } > + > + /* Bubble up non-null values from release to name */ > + if (name != NULL && *name == NULL) > + { > + *name = (*version == NULL ? *release : *version); > + *version = *release; > + *release = NULL; > + } > + if (*version == NULL) > + { > + *version = *release; > + *release = NULL; > + } > +} > + > +static int > +split_cmp(char *nvr0, char *nvr1, int has_name) > +{ > + int ret = 0; > + char *name0, *version0, *release0; > + char *name1, *version1, *release1; > + > + split_package_string(nvr0, has_name ? &name0 : NULL, &version0, > &release0); > + split_package_string(nvr1, has_name ? &name1 : NULL, &version1, > &release1); > + > + if (has_name) > + { > + ret = vercmp(name0 == NULL ? "" : name0, > + name1 == NULL ? "" : name1); > + if (ret != 0) > + return ret; > + } > + > + ret = vercmp(version0 == NULL ? "" : version0, > + version1 == NULL ? "" : version1); > + if (ret != 0) > + return ret; > + > + ret = vercmp(release0 == NULL ? "" : release0, > + release1 == NULL ? "" : release1); > + return ret; > +} > + > +/* return 1: e0 is newer than e1 */ > +/* 0: e0 and e1 are the same version */ > +/* -1: e1 is newer than e0 */ > +static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1) > +{ > + char *id0, *id1; > + int r; > + > + id0 = grub_strdup(e0->filename); > + id1 = grub_strdup(e1->filename); > + > + r = split_cmp(id0, id1, 1); > + > + grub_free(id0); > + grub_free(id1); > + > + return r; > +} > + > +static void list_add_tail(struct bls_entry *head, struct bls_entry *item) > +{ > + item->next = head; > + if (head->prev) > + head->prev->next = item; > + item->prev = head->prev; > + head->prev = item; > +} > + > +static int bls_add_entry(struct bls_entry *entry) > +{ > + struct bls_entry *e, *last = NULL; > + int rc; > + > + if (!entries) { > + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", > entry->filename); > + entries = entry; > + return 0; > + } > + > + FOR_BLS_ENTRIES(e) { > + rc = bls_cmp(entry, e); > + > + if (!rc) > + return GRUB_ERR_BAD_ARGUMENT; > + > + if (rc == 1) { > + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", > entry->filename); > + list_add_tail (e, entry); > + if (e == entries) { > + entries = entry; > + entry->prev = NULL; > + } > + return 0; > + } > + last = e; > + } > + > + if (last) { > + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", > entry->filename); > + last->next = entry; > + entry->prev = last; > + } > + > + return 0; > +} > + > +struct read_entry_info { > + const char *devid; > + const char *dirname; > + grub_file_t file; > +}; > + > +static int read_entry ( > + const char *filename, > + const struct grub_dirhook_info *dirhook_info UNUSED, > + void *data) > +{ > + grub_size_t m = 0, n, clip = 0; > + int rc = 0; > + char *p = NULL; > + grub_file_t f = NULL; > + struct bls_entry *entry; > + struct read_entry_info *info = (struct read_entry_info *)data; > + > + grub_dprintf ("blscfg", "filename: \"%s\"\n", filename); > + > + n = grub_strlen (filename); > + > + if (info->file) > + { > + f = info->file; > + } > + else > + { > + if (filename[0] == '.') > + return 0; > + > + if (n <= 5) > + return 0; > + > + if (grub_strcmp (filename + n - 5, ".conf") != 0) > + return 0; > + > + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, > filename); > + > + f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); > + if (!f) > + goto finish; > + } > + > + entry = grub_zalloc (sizeof (*entry)); > + if (!entry) > + goto finish; > + > + if (info->file) > + { > + char *slash; > + > + if (n > 5 && !grub_strcmp (filename + n - 5, ".conf") == 0) > + clip = 5; > + > + slash = grub_strrchr (filename, '/'); > + if (!slash) > + slash = grub_strrchr (filename, '\\'); > + > + while (*slash == '/' || *slash == '\\') > + slash++; > + > + m = slash ? slash - filename : 0; > + } > + else > + { > + m = 0; > + clip = 5; > + } > + n -= m; > + > + entry->filename = grub_strndup(filename + m, n - clip); > + if (!entry->filename) > + goto finish; > + > + entry->filename[n - 5] = '\0'; > + > + for (;;) > + { > + char *buf; > + char *separator; > + > + buf = grub_file_getline (f); > + if (!buf) > + break; > + > + while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t')) > + buf++; > + if (buf[0] == '#') > + continue; > + > + separator = grub_strchr (buf, ' '); > + > + if (!separator) > + separator = grub_strchr (buf, '\t'); > + > + if (!separator || separator[1] == '\0') > + { > + grub_free (buf); > + break; > + } > + > + separator[0] = '\0'; > + > + do { > + separator++; > + } while (*separator == ' ' || *separator == '\t'); > + > + rc = bls_add_keyval (entry, buf, separator); > + grub_free (buf); > + if (rc < 0) > + break; > + } > + > + if (!rc) > + bls_add_entry(entry); > + > +finish: > + if (p) > + grub_free (p); > + > + if (f) > + grub_file_close (f); > + > + return 0; > +} > + > +static grub_envblk_t saved_env = NULL; > + > +static int UNUSED > +save_var (const char *name, const char *value, void *whitelist UNUSED) > +{ > + const char *val = grub_env_get (name); > + grub_dprintf("blscfg", "saving \"%s\"\n", name); > + > + if (val) > + grub_envblk_set (saved_env, name, value); > + > + return 0; > +} > + > +static int UNUSED > +unset_var (const char *name, const char *value UNUSED, void *whitelist) > +{ > + grub_dprintf("blscfg", "restoring \"%s\"\n", name); > + if (! whitelist) > + { > + grub_env_unset (name); > + return 0; > + } > + > + if (test_whitelist_membership (name, > + (const grub_env_whitelist_t *) whitelist)) > + grub_env_unset (name); > + > + return 0; > +} > + > +static char **bls_make_list (struct bls_entry *entry, const char *key, > int *num) > +{ > + int last = -1; > + char *val; > + > + int nlist = 0; > + char **list = NULL; > + > + list = grub_malloc (sizeof (char *)); > + if (!list) > + return NULL; > + list[0] = NULL; > + > + while (1) > + { > + char **new; > + > + val = bls_get_val (entry, key, &last); > + if (!val) > + break; > + > + new = grub_realloc (list, (nlist + 2) * sizeof (char *)); > + if (!new) > + break; > + > + list = new; > + list[nlist++] = val; > + list[nlist] = NULL; > + } > + > + if (!nlist) > + { > + grub_free (list); > + return NULL; > + } > + > + if (num) > + *num = nlist; > + > + return list; > +} > + > +static char *field_append(bool is_var, char *buffer, const char *start, > const char *end) > +{ > + char *tmp = grub_strndup(start, end - start + 1); > + const char *field = tmp; > + int term = is_var ? 2 : 1; > + > + if (is_var) { > + field = grub_env_get (tmp); > + if (!field) > + return buffer; > + } > + > + if (!buffer) > + buffer = grub_zalloc (grub_strlen(field) + term); > + else > + buffer = grub_realloc (buffer, grub_strlen(buffer) + > grub_strlen(field) + term); > + > + if (!buffer) > + return NULL; > + > + tmp = buffer + grub_strlen(buffer); > + tmp = grub_stpcpy (tmp, field); > + > + if (is_var) > + tmp = grub_stpcpy (tmp, " "); > + > + return buffer; > +} > + > +static char *expand_val(const char *value) > +{ > + char *buffer = NULL; > + const char *start = value; > + const char *end = value; > + bool is_var = false; > + > + if (!value) > + return NULL; > + > + while (*value) { > + if (*value == '$') { > + if (start != end) { > + buffer = field_append(is_var, buffer, start, end); > + if (!buffer) > + return NULL; > + } > + > + is_var = true; > + start = value + 1; > + } else if (is_var) { > + if (!grub_isalnum(*value) && *value != '_') { > + buffer = field_append(is_var, buffer, start, end); > + is_var = false; > + start = value; > + if (*start == ' ') > + start++; > + } > + } > + > + end = value; > + value++; > + } > + > + if (start != end) { > + buffer = field_append(is_var, buffer, start, end); > + if (!buffer) > + return NULL; > + } > + > + return buffer; > +} > + > +static char **early_initrd_list (const char *initrd) > +{ > + int nlist = 0; > + char **list = NULL; > + char *separator; > + > + while ((separator = grub_strchr (initrd, ' '))) > + { > + list = grub_realloc (list, (nlist + 2) * sizeof (char *)); > + if (!list) > + return NULL; > + > + list[nlist++] = grub_strndup(initrd, separator - initrd); > + list[nlist] = NULL; > + initrd = separator + 1; > + } > + > + list = grub_realloc (list, (nlist + 2) * sizeof (char *)); > + if (!list) > + return NULL; > + > + list[nlist++] = grub_strndup(initrd, grub_strlen(initrd)); > + list[nlist] = NULL; > + > + return list; > +} > + > +static void create_entry (struct bls_entry *entry) > +{ > + int argc = 0; > + const char **argv = NULL; > + > + char *title = NULL; > + char *clinux = NULL; > + char *options = NULL; > + char **initrds = NULL; > + char *initrd = NULL; > + const char *early_initrd = NULL; > + char **early_initrds = NULL; > + char *initrd_prefix = NULL; > + char *devicetree = NULL; > + char *dt = NULL; > + char *id = entry->filename; > + char *dotconf = id; > + char *hotkey = NULL; > + > + char *users = NULL; > + char **classes = NULL; > + > + char **args = NULL; > + > + char *src = NULL; > + int i, index; > + bool add_dt_prefix = false; > + > + grub_dprintf("blscfg", "%s got here\n", __func__); > + clinux = bls_get_val (entry, "linux", NULL); > + if (!clinux) > + { > + grub_dprintf ("blscfg", "Skipping file %s with no 'linux' key.\n", > entry->filename); > + goto finish; > + } > + > + /* > + * strip the ".conf" off the end before we make it our "id" field. > + */ > + do > + { > + dotconf = grub_strstr(dotconf, ".conf"); > + } while (dotconf != NULL && dotconf[5] != '\0'); > + if (dotconf) > + dotconf[0] = '\0'; > + > + title = bls_get_val (entry, "title", NULL); > + options = expand_val (bls_get_val (entry, "options", NULL)); > + > + if (!options) > + options = expand_val (grub_env_get("default_kernelopts")); > + > + initrds = bls_make_list (entry, "initrd", NULL); > + > + devicetree = expand_val (bls_get_val (entry, "devicetree", NULL)); > + > + if (!devicetree) > + { > + devicetree = expand_val (grub_env_get("devicetree")); > + add_dt_prefix = true; > + } > + > + hotkey = bls_get_val (entry, "grub_hotkey", NULL); > + users = expand_val (bls_get_val (entry, "grub_users", NULL)); > + classes = bls_make_list (entry, "grub_class", NULL); > + args = bls_make_list (entry, "grub_arg", &argc); > + > + argc += 1; > + argv = grub_malloc ((argc + 1) * sizeof (char *)); > + argv[0] = title ? title : clinux; > + for (i = 1; i < argc; i++) > + argv[i] = args[i-1]; > + argv[argc] = NULL; > + > + early_initrd = grub_env_get("early_initrd"); > + > + grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n", > + title, id); > + if (early_initrd) > + { > + early_initrds = early_initrd_list(early_initrd); > + if (!early_initrds) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); > + goto finish; > + } > + > + if (initrds != NULL && initrds[0] != NULL) > + { > + initrd_prefix = grub_strrchr (initrds[0], '/'); > + initrd_prefix = grub_strndup(initrds[0], initrd_prefix - > initrds[0] + 1); > + } > + else > + { > + initrd_prefix = grub_strrchr (clinux, '/'); > + initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1); > + } > + > + if (!initrd_prefix) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); > + goto finish; > + } > + } > + > + if (early_initrds || initrds) > + { > + int initrd_size = sizeof ("initrd"); > + char *tmp; > + > + for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++) > + initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \ > + + grub_strlen(initrd_prefix) \ > + + grub_strlen (early_initrds[i]) + 1; > + > + for (i = 0; initrds != NULL && initrds[i] != NULL; i++) > + initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \ > + + grub_strlen (initrds[i]) + 1; > + initrd_size += 1; > + > + initrd = grub_malloc (initrd_size); > + if (!initrd) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); > + goto finish; > + } > + > + tmp = grub_stpcpy(initrd, "initrd"); > + for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++) > + { > + grub_dprintf ("blscfg", "adding early initrd %s\n", > early_initrds[i]); > + tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); > + tmp = grub_stpcpy (tmp, initrd_prefix); > + tmp = grub_stpcpy (tmp, early_initrds[i]); > + grub_free(early_initrds[i]); > + } > + > + for (i = 0; initrds != NULL && initrds[i] != NULL; i++) > + { > + grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]); > + tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); > + tmp = grub_stpcpy (tmp, initrds[i]); > + } > + tmp = grub_stpcpy (tmp, "\n"); > + } > + > + if (devicetree) > + { > + char *prefix = NULL; > + int dt_size; > + > + if (add_dt_prefix) > + { > + prefix = grub_strrchr (clinux, '/'); > + prefix = grub_strndup(clinux, prefix - clinux + 1); > + if (!prefix) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); > + goto finish; > + } > + } > + > + dt_size = sizeof("devicetree " GRUB_BOOT_DEVICE) + > grub_strlen(devicetree) + 1; > + > + if (add_dt_prefix) > + { > + dt_size += grub_strlen(prefix); > + } > + > + dt = grub_malloc (dt_size); > + if (!dt) > + { > + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); > + goto finish; > + } > + char *tmp = dt; > + tmp = grub_stpcpy (dt, "devicetree"); > + tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); > + if (add_dt_prefix) > + tmp = grub_stpcpy (tmp, prefix); > + tmp = grub_stpcpy (tmp, devicetree); > + tmp = grub_stpcpy (tmp, "\n"); > + > + grub_free(prefix); > + } > + > + grub_dprintf ("blscfg2", "devicetree %s for id:\"%s\"\n", dt, id); > + > + const char *sdval = grub_env_get("save_default"); > + bool savedefault = ((NULL != sdval) && (grub_strcmp(sdval, "true") == > 0)); > + src = grub_xasprintf ("%sload_video\n" > + "set gfxpayload=keep\n" > + "insmod gzio\n" > + "linux %s%s%s%s\n" > + "%s%s", > + savedefault ? "savedefault\n" : "", > + GRUB_BOOT_DEVICE, clinux, options ? " " : "", > options ? options : "", > + initrd ? initrd : "", dt ? dt : ""); > + > + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, > NULL, src, 0, &index, entry); > + grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id); > + > +finish: > + grub_free (dt); > + grub_free (initrd); > + grub_free (initrd_prefix); > + grub_free (early_initrds); > + grub_free (devicetree); > + grub_free (initrds); > + grub_free (options); > + grub_free (classes); > + grub_free (args); > + grub_free (argv); > + grub_free (src); > +} > + > +struct find_entry_info { > + const char *dirname; > + const char *devid; > + grub_device_t dev; > + grub_fs_t fs; > +}; > + > +/* > + * info: the filesystem object the file is on. > + */ > +static int find_entry (struct find_entry_info *info) > +{ > + struct read_entry_info read_entry_info; > + grub_fs_t blsdir_fs = NULL; > + grub_device_t blsdir_dev = NULL; > + const char *blsdir = info->dirname; > + int fallback = 0; > + int r = 0; > + > + if (!blsdir) { > + blsdir = grub_env_get ("blsdir"); > + if (!blsdir) > + blsdir = GRUB_BLS_CONFIG_PATH; > + } > + > + read_entry_info.file = NULL; > + read_entry_info.dirname = blsdir; > + > + grub_dprintf ("blscfg", "scanning blsdir: %s\n", blsdir); > + > + blsdir_dev = info->dev; > + blsdir_fs = info->fs; > + read_entry_info.devid = info->devid; > + > +read_fallback: > + r = blsdir_fs->fs_dir (blsdir_dev, read_entry_info.dirname, read_entry, > + &read_entry_info); > + if (r != 0) { > + grub_dprintf ("blscfg", "read_entry returned error\n"); > + grub_err_t e; > + do > + { > + e = grub_error_pop(); > + } while (e); > + } > + > + if (r && !info->dirname && !fallback) { > + read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH; > + grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to > %s\n", > + blsdir, read_entry_info.dirname); > + fallback = 1; > + goto read_fallback; > + } > + > + return 0; > +} > + > +static grub_err_t > +bls_load_entries (const char *path) > +{ > + grub_size_t len; > + grub_fs_t fs; > + grub_device_t dev; > + static grub_err_t r; > + const char *devid = NULL; > + char *blsdir = NULL; > + struct find_entry_info info = { > + .dev = NULL, > + .fs = NULL, > + .dirname = NULL, > + }; > + struct read_entry_info rei = { > + .devid = NULL, > + .dirname = NULL, > + }; > + > + if (path) { > + len = grub_strlen (path); > + if (grub_strcmp (path + len - 5, ".conf") == 0) { > + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); > + if (!rei.file) > + return grub_errno; > + /* > + * read_entry() closes the file > + */ > + return read_entry(path, NULL, &rei); > + } else if (path[0] == '(') { > + devid = path + 1; > + > + blsdir = grub_strchr (path, ')'); > + if (!blsdir) > + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Filepath isn't > correct")); > + > + *blsdir = '\0'; > + blsdir = blsdir + 1; > + } > + } > + > + if (!devid) { > +#ifdef GRUB_MACHINE_EMU > + devid = "host"; > +#else > + devid = grub_env_get ("root"); > +#endif > + if (!devid) > + return grub_error (GRUB_ERR_FILE_NOT_FOUND, > + N_("variable `%s' isn't set"), "root"); > + } > + > + grub_dprintf ("blscfg", "opening %s\n", devid); > + dev = grub_device_open (devid); > + if (!dev) > + return grub_errno; > + > + grub_dprintf ("blscfg", "probing fs\n"); > + fs = grub_fs_probe (dev); > + if (!fs) > + { > + r = grub_errno; > + goto finish; > + } > + > + info.dirname = blsdir; > + info.devid = devid; > + info.dev = dev; > + info.fs = fs; > + find_entry(&info); > + > +finish: > + if (dev) > + grub_device_close (dev); > + > + return r; > +} > + > +static bool > +is_default_entry(const char *def_entry, struct bls_entry *entry, int idx) > +{ > + const char *title; > + int def_idx; > + > + if (!def_entry) > + return false; > + > + if (grub_strcmp(def_entry, entry->filename) == 0) > + return true; > + > + title = bls_get_val(entry, "title", NULL); > + > + if (title && grub_strcmp(def_entry, title) == 0) > + return true; > + > + def_idx = (int)grub_strtol(def_entry, NULL, 0); > + if (grub_errno == GRUB_ERR_BAD_NUMBER) { > + grub_errno = GRUB_ERR_NONE; > + return false; > + } > + > + if (def_idx == idx) > + return true; > + > + return false; > +} > + > +static grub_err_t > +bls_create_entries (bool show_default, bool show_non_default, char > *entry_id) > +{ > + const char *def_entry = NULL; > + struct bls_entry *entry = NULL; > + int idx = 0; > + > + def_entry = grub_env_get("default"); > + > + grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__); > + FOR_BLS_ENTRIES(entry) { > + if (entry->visible) { > + idx++; > + continue; > + } > + > + if ((show_default && is_default_entry(def_entry, entry, idx)) || > + (show_non_default && !is_default_entry(def_entry, entry, idx)) || > + (entry_id && grub_strcmp(entry_id, entry->filename) == 0)) { > + create_entry(entry); > + entry->visible = 1; > + } > + idx++; > + } > + > + return GRUB_ERR_NONE; > +} > + > +static grub_err_t > +grub_cmd_blscfg (grub_extcmd_context_t ctxt UNUSED, > + int argc, char **args) > +{ > + grub_err_t r; > + char *path = NULL; > + char *entry_id = NULL; > + bool show_default = true; > + bool show_non_default = true; > + > + if (argc == 1) { > + if (grub_strcmp (args[0], "default") == 0) { > + show_non_default = false; > + } else if (grub_strcmp (args[0], "non-default") == 0) { > + show_default = false; > + } else if (args[0][0] == '(') { > + path = args[0]; > + } else { > + entry_id = args[0]; > + show_default = false; > + show_non_default = false; > + } > + } > + > + r = bls_load_entries(path); > + if (r) > + return r; > + > + return bls_create_entries(show_default, show_non_default, entry_id); > +} > + > +static grub_extcmd_t cmd; > +static grub_extcmd_t oldcmd; > + > +GRUB_MOD_INIT(blscfg) > +{ > + grub_dprintf("blscfg", "%s got here\n", __func__); > + cmd = grub_register_extcmd ("blscfg", > + grub_cmd_blscfg, > + 0, > + NULL, > + N_("Import Boot Loader Specification > snippets."), > + NULL); > + oldcmd = grub_register_extcmd ("bls_import", > + grub_cmd_blscfg, > + 0, > + NULL, > + N_("Import Boot Loader Specification > snippets."), > + NULL); > +} > + > +GRUB_MOD_FINI(blscfg) > +{ > + grub_unregister_extcmd (cmd); > + grub_unregister_extcmd (oldcmd); > +} > diff --git a/grub-core/commands/legacycfg.c > b/grub-core/commands/legacycfg.c > index e9e9d94ef..2c5d1a0ef 100644 > --- a/grub-core/commands/legacycfg.c > +++ b/grub-core/commands/legacycfg.c > @@ -143,7 +143,7 @@ legacy_file (const char *filename) > args[0] = oldname; > grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy", > NULL, NULL, > - entrysrc, 0); > + entrysrc, 0, NULL, NULL); > grub_free (args); > entrysrc[0] = 0; > grub_free (oldname); > @@ -205,7 +205,8 @@ legacy_file (const char *filename) > } > args[0] = entryname; > grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, > - NULL, NULL, entrysrc, 0); > + NULL, NULL, entrysrc, 0, NULL, > + NULL); > grub_free (args); > } > > diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c > index 166445849..dfcb19e0f 100644 > --- a/grub-core/commands/loadenv.c > +++ b/grub-core/commands/loadenv.c > @@ -28,6 +28,8 @@ > #include <grub/extcmd.h> > #include <grub/i18n.h> > > +#include "loadenv.h" > + > GRUB_MOD_LICENSE ("GPLv3+"); > > static const struct grub_arg_option options[] = > @@ -79,81 +81,6 @@ open_envblk_file (char *filename, > return file; > } > > -static grub_envblk_t > -read_envblk_file (grub_file_t file) > -{ > - grub_off_t offset = 0; > - char *buf; > - grub_size_t size = grub_file_size (file); > - grub_envblk_t envblk; > - > - buf = grub_malloc (size); > - if (! buf) > - return 0; > - > - while (size > 0) > - { > - grub_ssize_t ret; > - > - ret = grub_file_read (file, buf + offset, size); > - if (ret <= 0) > - { > - grub_free (buf); > - return 0; > - } > - > - size -= ret; > - offset += ret; > - } > - > - envblk = grub_envblk_open (buf, offset); > - if (! envblk) > - { > - grub_free (buf); > - grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block"); > - return 0; > - } > - > - return envblk; > -} > - > -struct grub_env_whitelist > -{ > - grub_size_t len; > - char **list; > -}; > -typedef struct grub_env_whitelist grub_env_whitelist_t; > - > -static int > -test_whitelist_membership (const char* name, > - const grub_env_whitelist_t* whitelist) > -{ > - grub_size_t i; > - > - for (i = 0; i < whitelist->len; i++) > - if (grub_strcmp (name, whitelist->list[i]) == 0) > - return 1; /* found it */ > - > - return 0; /* not found */ > -} > - > -/* Helper for grub_cmd_load_env. */ > -static int > -set_var (const char *name, const char *value, void *whitelist) > -{ > - if (! whitelist) > - { > - grub_env_set (name, value); > - return 0; > - } > - > - if (test_whitelist_membership (name, > - (const grub_env_whitelist_t *) whitelist)) > - grub_env_set (name, value); > - > - return 0; > -} > - > static grub_err_t > grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args) > { > diff --git a/grub-core/commands/loadenv.h b/grub-core/commands/loadenv.h > new file mode 100644 > index 000000000..952f46121 > --- /dev/null > +++ b/grub-core/commands/loadenv.h > @@ -0,0 +1,93 @@ > +/* loadenv.c - command to load/save environment variable. */ > +/* > + * GRUB -- GRand Unified Bootloader > + * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc. > + * > + * GRUB is free software: you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation, either version 3 of the License, or > + * (at your option) any later version. > + * > + * GRUB is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +static grub_envblk_t UNUSED > +read_envblk_file (grub_file_t file) > +{ > + grub_off_t offset = 0; > + char *buf; > + grub_size_t size = grub_file_size (file); > + grub_envblk_t envblk; > + > + buf = grub_malloc (size); > + if (! buf) > + return 0; > + > + while (size > 0) > + { > + grub_ssize_t ret; > + > + ret = grub_file_read (file, buf + offset, size); > + if (ret <= 0) > + { > + grub_free (buf); > + return 0; > + } > + > + size -= ret; > + offset += ret; > + } > + > + envblk = grub_envblk_open (buf, offset); > + if (! envblk) > + { > + grub_free (buf); > + grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block"); > + return 0; > + } > + > + return envblk; > +} > + > +struct grub_env_whitelist > +{ > + grub_size_t len; > + char **list; > +}; > +typedef struct grub_env_whitelist grub_env_whitelist_t; > + > +static int UNUSED > +test_whitelist_membership (const char* name, > + const grub_env_whitelist_t* whitelist) > +{ > + grub_size_t i; > + > + for (i = 0; i < whitelist->len; i++) > + if (grub_strcmp (name, whitelist->list[i]) == 0) > + return 1; /* found it */ > + > + return 0; /* not found */ > +} > + > +/* Helper for grub_cmd_load_env. */ > +static int UNUSED > +set_var (const char *name, const char *value, void *whitelist) > +{ > + if (! whitelist) > + { > + grub_env_set (name, value); > + return 0; > + } > + > + if (test_whitelist_membership (name, > + (const grub_env_whitelist_t *) whitelist)) > + grub_env_set (name, value); > + > + return 0; > +} > diff --git a/grub-core/commands/menuentry.c > b/grub-core/commands/menuentry.c > index 720e6d8ea..b194123eb 100644 > --- a/grub-core/commands/menuentry.c > +++ b/grub-core/commands/menuentry.c > @@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args, > char **classes, const char *id, > const char *users, const char *hotkey, > const char *prefix, const char *sourcecode, > - int submenu) > + int submenu, int *index, struct bls_entry *bls) > { > int menu_hotkey = 0; > char **menu_args = NULL; > @@ -149,9 +149,12 @@ grub_normal_add_menu_entry (int argc, const char > **args, > if (! menu_title) > goto fail; > > + grub_dprintf ("menu", "id:\"%s\"\n", id); > + grub_dprintf ("menu", "title:\"%s\"\n", menu_title); > menu_id = grub_strdup (id ? : menu_title); > if (! menu_id) > goto fail; > + grub_dprintf ("menu", "menu_id:\"%s\"\n", menu_id); > > /* Save argc, args to pass as parameters to block arg later. */ > menu_args = grub_calloc (argc + 1, sizeof (char *)); > @@ -170,8 +173,12 @@ grub_normal_add_menu_entry (int argc, const char > **args, > } > > /* Add the menu entry at the end of the list. */ > + int ind=0; > while (*last) > - last = &(*last)->next; > + { > + ind++; > + last = &(*last)->next; > + } > > *last = grub_zalloc (sizeof (**last)); > if (! *last) > @@ -188,8 +195,11 @@ grub_normal_add_menu_entry (int argc, const char > **args, > (*last)->args = menu_args; > (*last)->sourcecode = menu_sourcecode; > (*last)->submenu = submenu; > + (*last)->bls = bls; > > menu->size++; > + if (index) > + *index = ind; > return GRUB_ERR_NONE; > > fail: > @@ -286,7 +296,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int > argc, char **args) > users, > ctxt->state[2].arg, 0, > ctxt->state[3].arg, > - ctxt->extcmd->cmd->name[0] == 's'); > + ctxt->extcmd->cmd->name[0] == 's', > + NULL, NULL); > > src = args[argc - 1]; > args[argc - 1] = NULL; > @@ -303,7 +314,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int > argc, char **args) > ctxt->state[0].args, ctxt->state[4].arg, > users, > ctxt->state[2].arg, prefix, src + 1, > - ctxt->extcmd->cmd->name[0] == 's'); > + ctxt->extcmd->cmd->name[0] == 's', NULL, > + NULL); > > src[len - 1] = ch; > args[argc - 1] = src; > diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c > index 08f48c71d..1317279c0 100644 > --- a/grub-core/normal/main.c > +++ b/grub-core/normal/main.c > @@ -21,6 +21,7 @@ > #include <grub/net.h> > #include <grub/normal.h> > #include <grub/dl.h> > +#include <grub/menu.h> > #include <grub/misc.h> > #include <grub/file.h> > #include <grub/mm.h> > @@ -70,6 +71,11 @@ grub_normal_free_menu (grub_menu_t menu) > grub_free (entry->args); > } > > + if (entry->bls) > + { > + entry->bls->visible = 0; > + } > + > grub_free ((void *) entry->id); > grub_free ((void *) entry->users); > grub_free ((void *) entry->title); > diff --git a/include/grub/compiler.h b/include/grub/compiler.h > index 0c5519387..441a9eca0 100644 > --- a/include/grub/compiler.h > +++ b/include/grub/compiler.h > @@ -56,4 +56,6 @@ > # define CLANG_PREREQ(maj,min) 0 > #endif > > +#define UNUSED __attribute__((__unused__)) > + > #endif /* ! GRUB_COMPILER_HEADER */ > diff --git a/include/grub/menu.h b/include/grub/menu.h > index ee2b5e910..0acdc2aa6 100644 > --- a/include/grub/menu.h > +++ b/include/grub/menu.h > @@ -20,6 +20,16 @@ > #ifndef GRUB_MENU_HEADER > #define GRUB_MENU_HEADER 1 > > +struct bls_entry > +{ > + struct bls_entry *next; > + struct bls_entry *prev; > + struct keyval **keyvals; > + int nkeyvals; > + char *filename; > + int visible; > +}; > + > struct grub_menu_entry_class > { > char *name; > @@ -60,6 +70,9 @@ struct grub_menu_entry > > /* The next element. */ > struct grub_menu_entry *next; > + > + /* BLS used to populate the entry */ > + struct bls_entry *bls; > }; > typedef struct grub_menu_entry *grub_menu_entry_t; > > diff --git a/include/grub/normal.h b/include/grub/normal.h > index 218cbabcc..8839ad85a 100644 > --- a/include/grub/normal.h > +++ b/include/grub/normal.h > @@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char > **args, char **classes, > const char *id, > const char *users, const char *hotkey, > const char *prefix, const char *sourcecode, > - int submenu); > + int submenu, int *index, struct bls_entry > *bls); > > grub_err_t > grub_normal_set_password (const char *user, const char *password); > -- > 2.46.1 > > > _______________________________________________ > Grub-devel mailing list > Grub-devel@gnu.org > https://lists.gnu.org/mailman/listinfo/grub-devel >
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel