zbyszek / rpms / grub2

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