]> TLD Linux GIT Repositories - packages/grub2.git/blob - blscfg.patch
- merged 2.12 from PLD
[packages/grub2.git] / blscfg.patch
1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2 From: Peter Jones <pjones@redhat.com>
3 Date: Tue, 22 Jan 2013 06:31:38 +0100
4 Subject: [PATCH] blscfg: add blscfg module to parse Boot Loader Specification
5  snippets
6
7 The BootLoaderSpec (BLS) defines a scheme where different bootloaders can
8 share a format for boot items and a configuration directory that accepts
9 these common configurations as drop-in files.
10
11 Signed-off-by: Peter Jones <pjones@redhat.com>
12 Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
13 [wjt: some cleanups and fixes]
14 Signed-off-by: Will Thompson <wjt@endlessm.com>
15 ---
16  grub-core/Makefile.core.def    |   11 +
17  grub-core/commands/blscfg.c    | 1096 ++++++++++++++++++++++++++++++++++++++++
18  grub-core/commands/legacycfg.c |    5 +-
19  grub-core/commands/loadenv.c   |   77 +--
20  grub-core/commands/menuentry.c |   20 +-
21  grub-core/normal/main.c        |    6 +
22  grub-core/commands/loadenv.h   |   93 ++++
23  include/grub/compiler.h        |    2 +
24  include/grub/menu.h            |   13 +
25  include/grub/normal.h          |    2 +-
26  10 files changed, 1243 insertions(+), 82 deletions(-)
27  create mode 100644 grub-core/commands/blscfg.c
28  create mode 100644 grub-core/commands/loadenv.h
29
30 diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
31 index 41b5e16a3ce..57e253ab1a1 100644
32 --- a/grub-core/Makefile.core.def
33 +++ b/grub-core/Makefile.core.def
34 @@ -811,6 +811,16 @@ module = {
35    common = commands/blocklist.c;
36  };
37  
38 +module = {
39 +  name = blscfg;
40 +  common = commands/blscfg.c;
41 +  common = commands/loadenv.h;
42 +  enable = powerpc_ieee1275;
43 +  enable = efi;
44 +  enable = i386_pc;
45 +  enable = emu;
46 +};
47 +
48  module = {
49    name = boot;
50    common = commands/boot.c;
51 @@ -988,6 +998,7 @@ module = {
52  module = {
53    name = loadenv;
54    common = commands/loadenv.c;
55 +  common = commands/loadenv.h;
56    common = lib/envblk.c;
57  };
58  
59 diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c
60 new file mode 100644
61 index 00000000000..54458b14518
62 --- /dev/null
63 +++ b/grub-core/commands/blscfg.c
64 @@ -0,0 +1,1096 @@
65 +/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/
66 +
67 +/* bls.c - implementation of the boot loader spec */
68 +
69 +/*
70 + *  GRUB  --  GRand Unified Bootloader
71 + *
72 + *  GRUB is free software: you can redistribute it and/or modify
73 + *  it under the terms of the GNU General Public License as published by
74 + *  the Free Software Foundation, either version 3 of the License, or
75 + *  (at your option) any later version.
76 + *
77 + *  GRUB is distributed in the hope that it will be useful,
78 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
79 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
80 + *  GNU General Public License for more details.
81 + *
82 + *  You should have received a copy of the GNU General Public License
83 + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
84 + */
85 +
86 +#include <grub/list.h>
87 +#include <grub/types.h>
88 +#include <grub/misc.h>
89 +#include <grub/mm.h>
90 +#include <grub/err.h>
91 +#include <grub/dl.h>
92 +#include <grub/extcmd.h>
93 +#include <grub/i18n.h>
94 +#include <grub/fs.h>
95 +#include <grub/env.h>
96 +#include <grub/file.h>
97 +#include <grub/normal.h>
98 +#include <grub/lib/envblk.h>
99 +
100 +#include <stdbool.h>
101 +
102 +GRUB_MOD_LICENSE ("GPLv3+");
103 +
104 +#include "loadenv.h"
105 +
106 +#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
107 +#ifdef GRUB_MACHINE_EMU
108 +#define GRUB_BOOT_DEVICE "/boot"
109 +#else
110 +#define GRUB_BOOT_DEVICE "($root)"
111 +#endif
112 +
113 +struct keyval
114 +{
115 +  const char *key;
116 +  char *val;
117 +};
118 +
119 +static struct bls_entry *entries = NULL;
120 +
121 +#define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
122 +
123 +static int bls_add_keyval(struct bls_entry *entry, char *key, char *val)
124 +{
125 +  char *k, *v;
126 +  struct keyval **kvs, *kv;
127 +  int new_n = entry->nkeyvals + 1;
128 +
129 +  kvs = grub_realloc (entry->keyvals, new_n * sizeof (struct keyval *));
130 +  if (!kvs)
131 +    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
132 +                      "couldn't find space for BLS entry");
133 +  entry->keyvals = kvs;
134 +
135 +  kv = grub_malloc (sizeof (struct keyval));
136 +  if (!kv)
137 +    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
138 +                      "couldn't find space for BLS entry");
139 +
140 +  k = grub_strdup (key);
141 +  if (!k)
142 +    {
143 +      grub_free (kv);
144 +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
145 +                        "couldn't find space for BLS entry");
146 +    }
147 +
148 +  v = grub_strdup (val);
149 +  if (!v)
150 +    {
151 +      grub_free (k);
152 +      grub_free (kv);
153 +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
154 +                        "couldn't find space for BLS entry");
155 +    }
156 +
157 +  kv->key = k;
158 +  kv->val = v;
159 +
160 +  entry->keyvals[entry->nkeyvals] = kv;
161 +  grub_dprintf("blscfg", "new keyval at %p:%s:%s\n", entry->keyvals[entry->nkeyvals], k, v);
162 +  entry->nkeyvals = new_n;
163 +
164 +  return 0;
165 +}
166 +
167 +/* Find they value of the key named by keyname.  If there are allowed to be
168 + * more than one, pass a pointer to an int set to -1 the first time, and pass
169 + * the same pointer through each time after, and it'll return them in sorted
170 + * order as defined in the BLS fragment file */
171 +static char *bls_get_val(struct bls_entry *entry, const char *keyname, int *last)
172 +{
173 +  int idx, start = 0;
174 +  struct keyval *kv = NULL;
175 +
176 +  if (last)
177 +    start = *last + 1;
178 +
179 +  for (idx = start; idx < entry->nkeyvals; idx++) {
180 +    kv = entry->keyvals[idx];
181 +
182 +    if (!grub_strcmp (keyname, kv->key))
183 +      break;
184 +  }
185 +
186 +  if (idx == entry->nkeyvals) {
187 +    if (last)
188 +      *last = -1;
189 +    return NULL;
190 +  }
191 +
192 +  if (last)
193 +    *last = idx;
194 +
195 +  return kv->val;
196 +}
197 +
198 +#define goto_return(x) ({ ret = (x); goto finish; })
199 +
200 +/* compare alpha and numeric segments of two versions */
201 +/* return 1: a is newer than b */
202 +/*        0: a and b are the same version */
203 +/*       -1: b is newer than a */
204 +static int vercmp(const char * a, const char * b)
205 +{
206 +    char oldch1, oldch2;
207 +    char *abuf, *bbuf;
208 +    char *str1, *str2;
209 +    char * one, * two;
210 +    int rc;
211 +    int isnum;
212 +    int ret = 0;
213 +
214 +    grub_dprintf("blscfg", "%s comparing %s and %s\n", __func__, a, b);
215 +    if (!grub_strcmp(a, b))
216 +           return 0;
217 +
218 +    abuf = grub_malloc(grub_strlen(a) + 1);
219 +    bbuf = grub_malloc(grub_strlen(b) + 1);
220 +    str1 = abuf;
221 +    str2 = bbuf;
222 +    grub_strcpy(str1, a);
223 +    grub_strcpy(str2, b);
224 +
225 +    one = str1;
226 +    two = str2;
227 +
228 +    /* loop through each version segment of str1 and str2 and compare them */
229 +    while (*one || *two) {
230 +       while (*one && !grub_isalnum(*one) && *one != '~') one++;
231 +       while (*two && !grub_isalnum(*two) && *two != '~') two++;
232 +
233 +       /* handle the tilde separator, it sorts before everything else */
234 +       if (*one == '~' || *two == '~') {
235 +           if (*one != '~') goto_return (1);
236 +           if (*two != '~') goto_return (-1);
237 +           one++;
238 +           two++;
239 +           continue;
240 +       }
241 +
242 +       /* If we ran to the end of either, we are finished with the loop */
243 +       if (!(*one && *two)) break;
244 +
245 +       str1 = one;
246 +       str2 = two;
247 +
248 +       /* grab first completely alpha or completely numeric segment */
249 +       /* leave one and two pointing to the start of the alpha or numeric */
250 +       /* segment and walk str1 and str2 to end of segment */
251 +       if (grub_isdigit(*str1)) {
252 +           while (*str1 && grub_isdigit(*str1)) str1++;
253 +           while (*str2 && grub_isdigit(*str2)) str2++;
254 +           isnum = 1;
255 +       } else {
256 +           while (*str1 && grub_isalpha(*str1)) str1++;
257 +           while (*str2 && grub_isalpha(*str2)) str2++;
258 +           isnum = 0;
259 +       }
260 +
261 +       /* save character at the end of the alpha or numeric segment */
262 +       /* so that they can be restored after the comparison */
263 +       oldch1 = *str1;
264 +       *str1 = '\0';
265 +       oldch2 = *str2;
266 +       *str2 = '\0';
267 +
268 +       /* this cannot happen, as we previously tested to make sure that */
269 +       /* the first string has a non-null segment */
270 +       if (one == str1) goto_return(-1);       /* arbitrary */
271 +
272 +       /* take care of the case where the two version segments are */
273 +       /* different types: one numeric, the other alpha (i.e. empty) */
274 +       /* numeric segments are always newer than alpha segments */
275 +       /* XXX See patch #60884 (and details) from bugzilla #50977. */
276 +       if (two == str2) goto_return (isnum ? 1 : -1);
277 +
278 +       if (isnum) {
279 +           grub_size_t onelen, twolen;
280 +           /* this used to be done by converting the digit segments */
281 +           /* to ints using atoi() - it's changed because long  */
282 +           /* digit segments can overflow an int - this should fix that. */
283 +
284 +           /* throw away any leading zeros - it's a number, right? */
285 +           while (*one == '0') one++;
286 +           while (*two == '0') two++;
287 +
288 +           /* whichever number has more digits wins */
289 +           onelen = grub_strlen(one);
290 +           twolen = grub_strlen(two);
291 +           if (onelen > twolen) goto_return (1);
292 +           if (twolen > onelen) goto_return (-1);
293 +       }
294 +
295 +       /* grub_strcmp will return which one is greater - even if the two */
296 +       /* segments are alpha or if they are numeric.  don't return  */
297 +       /* if they are equal because there might be more segments to */
298 +       /* compare */
299 +       rc = grub_strcmp(one, two);
300 +       if (rc) goto_return (rc < 1 ? -1 : 1);
301 +
302 +       /* restore character that was replaced by null above */
303 +       *str1 = oldch1;
304 +       one = str1;
305 +       *str2 = oldch2;
306 +       two = str2;
307 +    }
308 +
309 +    /* this catches the case where all numeric and alpha segments have */
310 +    /* compared identically but the segment sepparating characters were */
311 +    /* different */
312 +    if ((!*one) && (!*two)) goto_return (0);
313 +
314 +    /* whichever version still has characters left over wins */
315 +    if (!*one) goto_return (-1); else goto_return (1);
316 +
317 +finish:
318 +    grub_free (abuf);
319 +    grub_free (bbuf);
320 +    return ret;
321 +}
322 +
323 +/* returns name/version/release */
324 +/* NULL string pointer returned if nothing found */
325 +static void
326 +split_package_string (char *package_string, char **name,
327 +                     char **version, char **release)
328 +{
329 +  char *package_version, *package_release;
330 +
331 +  /* Release */
332 +  package_release = grub_strrchr (package_string, '-');
333 +
334 +  if (package_release != NULL)
335 +      *package_release++ = '\0';
336 +
337 +  *release = package_release;
338 +
339 +  if (name == NULL)
340 +    {
341 +      *version = package_string;
342 +    }
343 +  else
344 +    {
345 +      /* Version */
346 +      package_version = grub_strrchr(package_string, '-');
347 +
348 +      if (package_version != NULL)
349 +       *package_version++ = '\0';
350 +
351 +      *version = package_version;
352 +      /* Name */
353 +      *name = package_string;
354 +    }
355 +
356 +  /* Bubble up non-null values from release to name */
357 +  if (name != NULL && *name == NULL)
358 +    {
359 +      *name = (*version == NULL ? *release : *version);
360 +      *version = *release;
361 +      *release = NULL;
362 +    }
363 +  if (*version == NULL)
364 +    {
365 +      *version = *release;
366 +      *release = NULL;
367 +    }
368 +}
369 +
370 +static int
371 +split_cmp(char *nvr0, char *nvr1, int has_name)
372 +{
373 +  int ret = 0;
374 +  char *name0, *version0, *release0;
375 +  char *name1, *version1, *release1;
376 +
377 +  split_package_string(nvr0, has_name ? &name0 : NULL, &version0, &release0);
378 +  split_package_string(nvr1, has_name ? &name1 : NULL, &version1, &release1);
379 +
380 +  if (has_name)
381 +    {
382 +      ret = vercmp(name0 == NULL ? "" : name0,
383 +                  name1 == NULL ? "" : name1);
384 +      if (ret != 0)
385 +       return ret;
386 +    }
387 +
388 +  ret = vercmp(version0 == NULL ? "" : version0,
389 +              version1 == NULL ? "" : version1);
390 +  if (ret != 0)
391 +    return ret;
392 +
393 +  ret = vercmp(release0 == NULL ? "" : release0,
394 +              release1 == NULL ? "" : release1);
395 +  return ret;
396 +}
397 +
398 +/* return 1: e0 is newer than e1 */
399 +/*        0: e0 and e1 are the same version */
400 +/*       -1: e1 is newer than e0 */
401 +static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1)
402 +{
403 +  char *id0, *id1;
404 +  int r;
405 +
406 +  id0 = grub_strdup(e0->filename);
407 +  id1 = grub_strdup(e1->filename);
408 +
409 +  r = split_cmp(id0, id1, 1);
410 +
411 +  grub_free(id0);
412 +  grub_free(id1);
413 +
414 +  return r;
415 +}
416 +
417 +static void list_add_tail(struct bls_entry *head, struct bls_entry *item)
418 +{
419 +  item->next = head;
420 +  if (head->prev)
421 +    head->prev->next = item;
422 +  item->prev = head->prev;
423 +  head->prev = item;
424 +}
425 +
426 +static int bls_add_entry(struct bls_entry *entry)
427 +{
428 +  struct bls_entry *e, *last = NULL;
429 +  int rc;
430 +
431 +  if (!entries) {
432 +    grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
433 +    entries = entry;
434 +    return 0;
435 +  }
436 +
437 +  FOR_BLS_ENTRIES(e) {
438 +    rc = bls_cmp(entry, e);
439 +
440 +    if (!rc)
441 +      return GRUB_ERR_BAD_ARGUMENT;
442 +
443 +    if (rc == 1) {
444 +      grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
445 +      list_add_tail (e, entry);
446 +      if (e == entries) {
447 +       entries = entry;
448 +       entry->prev = NULL;
449 +      }
450 +      return 0;
451 +    }
452 +    last = e;
453 +  }
454 +
455 +  if (last) {
456 +    grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
457 +    last->next = entry;
458 +    entry->prev = last;
459 +  }
460 +
461 +  return 0;
462 +}
463 +
464 +struct read_entry_info {
465 +  const char *devid;
466 +  const char *dirname;
467 +  grub_file_t file;
468 +};
469 +
470 +static int read_entry (
471 +    const char *filename,
472 +    const struct grub_dirhook_info *dirhook_info UNUSED,
473 +    void *data)
474 +{
475 +  grub_size_t m = 0, n, clip = 0;
476 +  int rc = 0;
477 +  char *p = NULL;
478 +  grub_file_t f = NULL;
479 +  struct bls_entry *entry;
480 +  struct read_entry_info *info = (struct read_entry_info *)data;
481 +
482 +  grub_dprintf ("blscfg", "filename: \"%s\"\n", filename);
483 +
484 +  n = grub_strlen (filename);
485 +
486 +  if (info->file)
487 +    {
488 +      f = info->file;
489 +    }
490 +  else
491 +    {
492 +      if (filename[0] == '.')
493 +       return 0;
494 +
495 +      if (n <= 5)
496 +       return 0;
497 +
498 +      if (grub_strcmp (filename + n - 5, ".conf") != 0)
499 +       return 0;
500 +
501 +      p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
502 +
503 +      f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
504 +      if (!f)
505 +       goto finish;
506 +    }
507 +
508 +  entry = grub_zalloc (sizeof (*entry));
509 +  if (!entry)
510 +    goto finish;
511 +
512 +  if (info->file)
513 +    {
514 +      char *slash;
515 +
516 +      if (n > 5 && !grub_strcmp (filename + n - 5, ".conf") == 0)
517 +       clip = 5;
518 +
519 +      slash = grub_strrchr (filename, '/');
520 +      if (!slash)
521 +       slash = grub_strrchr (filename, '\\');
522 +
523 +      while (*slash == '/' || *slash == '\\')
524 +       slash++;
525 +
526 +      m = slash ? slash - filename : 0;
527 +    }
528 +  else
529 +    {
530 +      m = 0;
531 +      clip = 5;
532 +    }
533 +  n -= m;
534 +
535 +  entry->filename = grub_strndup(filename + m, n - clip);
536 +  if (!entry->filename)
537 +    goto finish;
538 +
539 +  entry->filename[n - 5] = '\0';
540 +
541 +  for (;;)
542 +    {
543 +      char *buf;
544 +      char *separator;
545 +
546 +      buf = grub_file_getline (f);
547 +      if (!buf)
548 +       break;
549 +
550 +      while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t'))
551 +       buf++;
552 +      if (buf[0] == '#')
553 +       continue;
554 +
555 +      separator = grub_strchr (buf, ' ');
556 +
557 +      if (!separator)
558 +       separator = grub_strchr (buf, '\t');
559 +
560 +      if (!separator || separator[1] == '\0')
561 +       {
562 +         grub_free (buf);
563 +         break;
564 +       }
565 +
566 +      separator[0] = '\0';
567 +
568 +      do {
569 +       separator++;
570 +      } while (*separator == ' ' || *separator == '\t');
571 +
572 +      rc = bls_add_keyval (entry, buf, separator);
573 +      grub_free (buf);
574 +      if (rc < 0)
575 +       break;
576 +    }
577 +
578 +    if (!rc)
579 +      bls_add_entry(entry);
580 +
581 +finish:
582 +  if (p)
583 +    grub_free (p);
584 +
585 +  if (f)
586 +    grub_file_close (f);
587 +
588 +  return 0;
589 +}
590 +
591 +static grub_envblk_t saved_env = NULL;
592 +
593 +static int UNUSED
594 +save_var (const char *name, const char *value, void *whitelist UNUSED)
595 +{
596 +  const char *val = grub_env_get (name);
597 +  grub_dprintf("blscfg", "saving \"%s\"\n", name);
598 +
599 +  if (val)
600 +    grub_envblk_set (saved_env, name, value);
601 +
602 +  return 0;
603 +}
604 +
605 +static int UNUSED
606 +unset_var (const char *name, const char *value UNUSED, void *whitelist)
607 +{
608 +  grub_dprintf("blscfg", "restoring \"%s\"\n", name);
609 +  if (! whitelist)
610 +    {
611 +      grub_env_unset (name);
612 +      return 0;
613 +    }
614 +
615 +  if (test_whitelist_membership (name,
616 +                                (const grub_env_whitelist_t *) whitelist))
617 +    grub_env_unset (name);
618 +
619 +  return 0;
620 +}
621 +
622 +static char **bls_make_list (struct bls_entry *entry, const char *key, int *num)
623 +{
624 +  int last = -1;
625 +  char *val;
626 +
627 +  int nlist = 0;
628 +  char **list = NULL;
629 +
630 +  list = grub_malloc (sizeof (char *));
631 +  if (!list)
632 +    return NULL;
633 +  list[0] = NULL;
634 +
635 +  while (1)
636 +    {
637 +      char **new;
638 +
639 +      val = bls_get_val (entry, key, &last);
640 +      if (!val)
641 +       break;
642 +
643 +      new = grub_realloc (list, (nlist + 2) * sizeof (char *));
644 +      if (!new)
645 +       break;
646 +
647 +      list = new;
648 +      list[nlist++] = val;
649 +      list[nlist] = NULL;
650 +  }
651 +
652 +  if (num)
653 +    *num = nlist;
654 +
655 +  return list;
656 +}
657 +
658 +static char *field_append(bool is_var, char *buffer, char *start, char *end)
659 +{
660 +  char *temp = grub_strndup(start, end - start + 1);
661 +  const char *field = temp;
662 +
663 +  if (is_var) {
664 +    field = grub_env_get (temp);
665 +    if (!field)
666 +      return buffer;
667 +  }
668 +
669 +  if (!buffer) {
670 +    buffer = grub_strdup(field);
671 +    if (!buffer)
672 +      return NULL;
673 +  } else {
674 +    buffer = grub_realloc (buffer, grub_strlen(buffer) + grub_strlen(field));
675 +    if (!buffer)
676 +      return NULL;
677 +
678 +    grub_stpcpy (buffer + grub_strlen(buffer), field);
679 +  }
680 +
681 +  return buffer;
682 +}
683 +
684 +static char *expand_val(char *value)
685 +{
686 +  char *buffer = NULL;
687 +  char *start = value;
688 +  char *end = value;
689 +  bool is_var = false;
690 +
691 +  if (!value)
692 +    return NULL;
693 +
694 +  while (*value) {
695 +    if (*value == '$') {
696 +      if (start != end) {
697 +       buffer = field_append(is_var, buffer, start, end);
698 +       if (!buffer)
699 +         return NULL;
700 +      }
701 +
702 +      is_var = true;
703 +      start = value + 1;
704 +    } else if (is_var) {
705 +      if (!grub_isalnum(*value) && *value != '_') {
706 +       buffer = field_append(is_var, buffer, start, end);
707 +       is_var = false;
708 +       start = value;
709 +      }
710 +    }
711 +
712 +    end = value;
713 +    value++;
714 +  }
715 +
716 +  if (start != end) {
717 +    buffer = field_append(is_var, buffer, start, end);
718 +    if (!buffer)
719 +      return NULL;
720 +  }
721 +
722 +  return buffer;
723 +}
724 +
725 +static char **early_initrd_list (const char *initrd)
726 +{
727 +  int nlist = 0;
728 +  char **list = NULL;
729 +  char *separator;
730 +
731 +  while ((separator = grub_strchr (initrd, ' ')))
732 +    {
733 +      list = grub_realloc (list, (nlist + 2) * sizeof (char *));
734 +      if (!list)
735 +        return NULL;
736 +
737 +      list[nlist++] = grub_strndup(initrd, separator - initrd);
738 +      list[nlist] = NULL;
739 +      initrd = separator + 1;
740 +  }
741 +
742 +  list = grub_realloc (list, (nlist + 2) * sizeof (char *));
743 +  if (!list)
744 +    return NULL;
745 +
746 +  list[nlist++] = grub_strndup(initrd, grub_strlen(initrd));
747 +  list[nlist] = NULL;
748 +
749 +  return list;
750 +}
751 +
752 +static void create_entry (struct bls_entry *entry)
753 +{
754 +  int argc = 0;
755 +  const char **argv = NULL;
756 +
757 +  char *title = NULL;
758 +  char *clinux = NULL;
759 +  char *options = NULL;
760 +  char **initrds = NULL;
761 +  char *initrd = NULL;
762 +  const char *early_initrd = NULL;
763 +  char **early_initrds = NULL;
764 +  char *initrd_prefix = NULL;
765 +  char *id = entry->filename;
766 +  char *dotconf = id;
767 +  char *hotkey = NULL;
768 +
769 +  char *users = NULL;
770 +  char **classes = NULL;
771 +
772 +  char **args = NULL;
773 +
774 +  char *src = NULL;
775 +  int i, index;
776 +
777 +  grub_dprintf("blscfg", "%s got here\n", __func__);
778 +  clinux = bls_get_val (entry, "linux", NULL);
779 +  if (!clinux)
780 +    {
781 +      grub_dprintf ("blscfg", "Skipping file %s with no 'linux' key.\n", entry->filename);
782 +      goto finish;
783 +    }
784 +
785 +  /*
786 +   * strip the ".conf" off the end before we make it our "id" field.
787 +   */
788 +  do
789 +    {
790 +      dotconf = grub_strstr(dotconf, ".conf");
791 +    } while (dotconf != NULL && dotconf[5] != '\0');
792 +  if (dotconf)
793 +    dotconf[0] = '\0';
794 +
795 +  title = bls_get_val (entry, "title", NULL);
796 +  options = expand_val (bls_get_val (entry, "options", NULL));
797 +
798 +  if (!options)
799 +    options = expand_val (grub_env_get("default_kernelopts"));
800 +
801 +  initrds = bls_make_list (entry, "initrd", NULL);
802 +
803 +  hotkey = bls_get_val (entry, "grub_hotkey", NULL);
804 +  users = expand_val (bls_get_val (entry, "grub_users", NULL));
805 +  classes = bls_make_list (entry, "grub_class", NULL);
806 +  args = bls_make_list (entry, "grub_arg", &argc);
807 +
808 +  argc += 1;
809 +  argv = grub_malloc ((argc + 1) * sizeof (char *));
810 +  argv[0] = title ? title : clinux;
811 +  for (i = 1; i < argc; i++)
812 +    argv[i] = args[i-1];
813 +  argv[argc] = NULL;
814 +
815 +  early_initrd = grub_env_get("early_initrd");
816 +
817 +  grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n",
818 +               title, id);
819 +  if (early_initrd)
820 +    {
821 +      early_initrds = early_initrd_list(early_initrd);
822 +      if (!early_initrds)
823 +      {
824 +       grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
825 +       goto finish;
826 +      }
827 +
828 +      if (initrds != NULL && initrds[0] != NULL)
829 +       {
830 +         initrd_prefix = grub_strrchr (initrds[0], '/');
831 +         initrd_prefix = grub_strndup(initrds[0], initrd_prefix - initrds[0] + 1);
832 +       }
833 +      else
834 +       {
835 +         initrd_prefix = grub_strrchr (clinux, '/');
836 +         initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1);
837 +       }
838 +
839 +      if (!initrd_prefix)
840 +       {
841 +         grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
842 +         goto finish;
843 +       }
844 +    }
845 +
846 +  if (early_initrds || initrds)
847 +    {
848 +      int initrd_size = sizeof ("initrd");
849 +      char *tmp;
850 +
851 +      for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
852 +       initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
853 +                      + grub_strlen(initrd_prefix)  \
854 +                      + grub_strlen (early_initrds[i]) + 1;
855 +
856 +      for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
857 +       initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \
858 +                      + grub_strlen (initrds[i]) + 1;
859 +      initrd_size += 1;
860 +
861 +      initrd = grub_malloc (initrd_size);
862 +      if (!initrd)
863 +       {
864 +         grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
865 +         goto finish;
866 +       }
867 +
868 +
869 +      tmp = grub_stpcpy(initrd, "initrd");
870 +      for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
871 +       {
872 +         grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]);
873 +         tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
874 +         tmp = grub_stpcpy (tmp, initrd_prefix);
875 +         tmp = grub_stpcpy (tmp, early_initrds[i]);
876 +         grub_free(early_initrds[i]);
877 +       }
878 +
879 +      for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
880 +       {
881 +         grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]);
882 +         tmp = grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE);
883 +         tmp = grub_stpcpy (tmp, initrds[i]);
884 +       }
885 +      tmp = grub_stpcpy (tmp, "\n");
886 +    }
887 +
888 +  src = grub_xasprintf ("load_video\n"
889 +                       "set gfxpayload=keep\n"
890 +                       "insmod gzio\n"
891 +                       "linux %s%s%s%s\n"
892 +                       "%s",
893 +                       GRUB_BOOT_DEVICE, clinux, options ? " " : "", options ? options : "",
894 +                       initrd ? initrd : "");
895 +
896 +  grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry);
897 +  grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id);
898 +
899 +finish:
900 +  grub_free (initrd);
901 +  grub_free (initrd_prefix);
902 +  grub_free (early_initrds);
903 +  grub_free (initrds);
904 +  grub_free (options);
905 +  grub_free (classes);
906 +  grub_free (args);
907 +  grub_free (argv);
908 +  grub_free (src);
909 +}
910 +
911 +struct find_entry_info {
912 +       const char *dirname;
913 +       const char *devid;
914 +       grub_device_t dev;
915 +       grub_fs_t fs;
916 +};
917 +
918 +/*
919 + * info: the filesystem object the file is on.
920 + */
921 +static int find_entry (struct find_entry_info *info)
922 +{
923 +  struct read_entry_info read_entry_info;
924 +  grub_fs_t blsdir_fs = NULL;
925 +  grub_device_t blsdir_dev = NULL;
926 +  const char *blsdir = info->dirname;
927 +  int fallback = 0;
928 +  int r = 0;
929 +
930 +  if (!blsdir) {
931 +    blsdir = grub_env_get ("blsdir");
932 +    if (!blsdir)
933 +      blsdir = GRUB_BLS_CONFIG_PATH;
934 +  }
935 +
936 +  read_entry_info.file = NULL;
937 +  read_entry_info.dirname = blsdir;
938 +
939 +  grub_dprintf ("blscfg", "scanning blsdir: %s\n", blsdir);
940 +
941 +  blsdir_dev = info->dev;
942 +  blsdir_fs = info->fs;
943 +  read_entry_info.devid = info->devid;
944 +
945 +read_fallback:
946 +  r = blsdir_fs->fs_dir (blsdir_dev, read_entry_info.dirname, read_entry,
947 +                        &read_entry_info);
948 +  if (r != 0) {
949 +      grub_dprintf ("blscfg", "read_entry returned error\n");
950 +      grub_err_t e;
951 +      do
952 +       {
953 +         e = grub_error_pop();
954 +       } while (e);
955 +  }
956 +
957 +  if (r && !info->dirname && !fallback) {
958 +    read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH;
959 +    grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to %s\n",
960 +                 blsdir, read_entry_info.dirname);
961 +    fallback = 1;
962 +    goto read_fallback;
963 +  }
964 +
965 +  return 0;
966 +}
967 +
968 +static grub_err_t
969 +bls_load_entries (const char *path)
970 +{
971 +  grub_size_t len;
972 +  grub_fs_t fs;
973 +  grub_device_t dev;
974 +  static grub_err_t r;
975 +  const char *devid = NULL;
976 +  char *blsdir = NULL;
977 +  struct find_entry_info info = {
978 +      .dev = NULL,
979 +      .fs = NULL,
980 +      .dirname = NULL,
981 +  };
982 +  struct read_entry_info rei = {
983 +      .devid = NULL,
984 +      .dirname = NULL,
985 +  };
986 +
987 +  if (path) {
988 +    len = grub_strlen (path);
989 +    if (grub_strcmp (path + len - 5, ".conf") == 0) {
990 +      rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
991 +      if (!rei.file)
992 +       return grub_errno;
993 +      /*
994 +       * read_entry() closes the file
995 +       */
996 +      return read_entry(path, NULL, &rei);
997 +    } else if (path[0] == '(') {
998 +      devid = path + 1;
999 +
1000 +      blsdir = grub_strchr (path, ')');
1001 +      if (!blsdir)
1002 +       return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Filepath isn't correct"));
1003 +
1004 +      *blsdir = '\0';
1005 +      blsdir = blsdir + 1;
1006 +    }
1007 +  }
1008 +
1009 +  if (!devid) {
1010 +#ifdef GRUB_MACHINE_EMU
1011 +    devid = "host";
1012 +#elif defined(GRUB_MACHINE_EFI)
1013 +    devid = grub_env_get ("root");
1014 +#else
1015 +    devid = grub_env_get ("boot");
1016 +#endif
1017 +    if (!devid)
1018 +      return grub_error (GRUB_ERR_FILE_NOT_FOUND,
1019 +                        N_("variable `%s' isn't set"), "boot");
1020 +  }
1021 +
1022 +  grub_dprintf ("blscfg", "opening %s\n", devid);
1023 +  dev = grub_device_open (devid);
1024 +  if (!dev)
1025 +    return grub_errno;
1026 +
1027 +  grub_dprintf ("blscfg", "probing fs\n");
1028 +  fs = grub_fs_probe (dev);
1029 +  if (!fs)
1030 +    {
1031 +      r = grub_errno;
1032 +      goto finish;
1033 +    }
1034 +
1035 +  info.dirname = blsdir;
1036 +  info.devid = devid;
1037 +  info.dev = dev;
1038 +  info.fs = fs;
1039 +  find_entry(&info);
1040 +
1041 +finish:
1042 +  if (dev)
1043 +    grub_device_close (dev);
1044 +
1045 +  return r;
1046 +}
1047 +
1048 +static bool
1049 +is_default_entry(const char *def_entry, struct bls_entry *entry, int idx)
1050 +{
1051 +  const char *title;
1052 +  int def_idx;
1053 +
1054 +  if (!def_entry)
1055 +    return false;
1056 +
1057 +  if (grub_strcmp(def_entry, entry->filename) == 0)
1058 +    return true;
1059 +
1060 +  title = bls_get_val(entry, "title", NULL);
1061 +
1062 +  if (title && grub_strcmp(def_entry, title) == 0)
1063 +    return true;
1064 +
1065 +  def_idx = (int)grub_strtol(def_entry, NULL, 0);
1066 +  if (grub_errno == GRUB_ERR_BAD_NUMBER) {
1067 +    grub_errno = GRUB_ERR_NONE;
1068 +    return false;
1069 +  }
1070 +
1071 +  if (def_idx == idx)
1072 +    return true;
1073 +
1074 +  return false;
1075 +}
1076 +
1077 +static grub_err_t
1078 +bls_create_entries (bool show_default, bool show_non_default, char *entry_id)
1079 +{
1080 +  const char *def_entry = NULL;
1081 +  struct bls_entry *entry = NULL;
1082 +  int idx = 0;
1083 +
1084 +  def_entry = grub_env_get("default");
1085 +
1086 +  grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__);
1087 +  FOR_BLS_ENTRIES(entry) {
1088 +    if (entry->visible) {
1089 +      idx++;
1090 +      continue;
1091 +    }
1092 +
1093 +    if ((show_default && is_default_entry(def_entry, entry, idx)) ||
1094 +       (show_non_default && !is_default_entry(def_entry, entry, idx)) ||
1095 +       (entry_id && grub_strcmp(entry_id, entry->filename) == 0)) {
1096 +      create_entry(entry);
1097 +      entry->visible = 1;
1098 +    }
1099 +    idx++;
1100 +  }
1101 +
1102 +  return GRUB_ERR_NONE;
1103 +}
1104 +
1105 +static grub_err_t
1106 +grub_cmd_blscfg (grub_extcmd_context_t ctxt UNUSED,
1107 +                int argc, char **args)
1108 +{
1109 +  grub_err_t r;
1110 +  char *path = NULL;
1111 +  char *entry_id = NULL;
1112 +  bool show_default = true;
1113 +  bool show_non_default = true;
1114 +
1115 +  if (argc == 1) {
1116 +    if (grub_strcmp (args[0], "default") == 0) {
1117 +      show_non_default = false;
1118 +    } else if (grub_strcmp (args[0], "non-default") == 0) {
1119 +      show_default = false;
1120 +    } else if (args[0][0] == '(') {
1121 +      path = args[0];
1122 +    } else {
1123 +      entry_id = args[0];
1124 +      show_default = false;
1125 +      show_non_default = false;
1126 +    }
1127 +  }
1128 +
1129 +  r = bls_load_entries(path);
1130 +  if (r)
1131 +    return r;
1132 +
1133 +  return bls_create_entries(show_default, show_non_default, entry_id);
1134 +}
1135 +
1136 +static grub_extcmd_t cmd;
1137 +static grub_extcmd_t oldcmd;
1138 +
1139 +GRUB_MOD_INIT(blscfg)
1140 +{
1141 +  grub_dprintf("blscfg", "%s got here\n", __func__);
1142 +  cmd = grub_register_extcmd ("blscfg",
1143 +                             grub_cmd_blscfg,
1144 +                             0,
1145 +                             NULL,
1146 +                             N_("Import Boot Loader Specification snippets."),
1147 +                             NULL);
1148 +  oldcmd = grub_register_extcmd ("bls_import",
1149 +                                grub_cmd_blscfg,
1150 +                                0,
1151 +                                NULL,
1152 +                                N_("Import Boot Loader Specification snippets."),
1153 +                                NULL);
1154 +}
1155 +
1156 +GRUB_MOD_FINI(blscfg)
1157 +{
1158 +  grub_unregister_extcmd (cmd);
1159 +  grub_unregister_extcmd (oldcmd);
1160 +}
1161 diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
1162 index db7a8f00273..891eac5a33f 100644
1163 --- a/grub-core/commands/legacycfg.c
1164 +++ b/grub-core/commands/legacycfg.c
1165 @@ -133,7 +133,7 @@ legacy_file (const char *filename)
1166             args[0] = oldname;
1167             grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
1168                                         NULL, NULL,
1169 -                                       entrysrc, 0);
1170 +                                       entrysrc, 0, NULL, NULL);
1171             grub_free (args);
1172             entrysrc[0] = 0;
1173             grub_free (oldname);
1174 @@ -186,7 +186,8 @@ legacy_file (const char *filename)
1175         }
1176        args[0] = entryname;
1177        grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
1178 -                                 NULL, NULL, entrysrc, 0);
1179 +                                 NULL, NULL, entrysrc, 0, NULL,
1180 +                                 NULL);
1181        grub_free (args);
1182      }
1183  
1184 diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c
1185 index 3fd664aac33..163b9a09042 100644
1186 --- a/grub-core/commands/loadenv.c
1187 +++ b/grub-core/commands/loadenv.c
1188 @@ -28,6 +28,8 @@
1189  #include <grub/extcmd.h>
1190  #include <grub/i18n.h>
1191  
1192 +#include "loadenv.h"
1193 +
1194  GRUB_MOD_LICENSE ("GPLv3+");
1195  
1196  static const struct grub_arg_option options[] =
1197 @@ -79,81 +81,6 @@ open_envblk_file (char *filename,
1198    return file;
1199  }
1200  
1201 -static grub_envblk_t
1202 -read_envblk_file (grub_file_t file)
1203 -{
1204 -  grub_off_t offset = 0;
1205 -  char *buf;
1206 -  grub_size_t size = grub_file_size (file);
1207 -  grub_envblk_t envblk;
1208 -
1209 -  buf = grub_malloc (size);
1210 -  if (! buf)
1211 -    return 0;
1212 -
1213 -  while (size > 0)
1214 -    {
1215 -      grub_ssize_t ret;
1216 -
1217 -      ret = grub_file_read (file, buf + offset, size);
1218 -      if (ret <= 0)
1219 -        {
1220 -          grub_free (buf);
1221 -          return 0;
1222 -        }
1223 -
1224 -      size -= ret;
1225 -      offset += ret;
1226 -    }
1227 -
1228 -  envblk = grub_envblk_open (buf, offset);
1229 -  if (! envblk)
1230 -    {
1231 -      grub_free (buf);
1232 -      grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
1233 -      return 0;
1234 -    }
1235 -
1236 -  return envblk;
1237 -}
1238 -
1239 -struct grub_env_whitelist
1240 -{
1241 -  grub_size_t len;
1242 -  char **list;
1243 -};
1244 -typedef struct grub_env_whitelist grub_env_whitelist_t;
1245 -
1246 -static int
1247 -test_whitelist_membership (const char* name,
1248 -                           const grub_env_whitelist_t* whitelist)
1249 -{
1250 -  grub_size_t i;
1251 -
1252 -  for (i = 0; i < whitelist->len; i++)
1253 -    if (grub_strcmp (name, whitelist->list[i]) == 0)
1254 -      return 1;  /* found it */
1255 -
1256 -  return 0;  /* not found */
1257 -}
1258 -
1259 -/* Helper for grub_cmd_load_env.  */
1260 -static int
1261 -set_var (const char *name, const char *value, void *whitelist)
1262 -{
1263 -  if (! whitelist)
1264 -    {
1265 -      grub_env_set (name, value);
1266 -      return 0;
1267 -    }
1268 -
1269 -  if (test_whitelist_membership (name,
1270 -                                (const grub_env_whitelist_t *) whitelist))
1271 -    grub_env_set (name, value);
1272 -
1273 -  return 0;
1274 -}
1275 -
1276  static grub_err_t
1277  grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
1278  {
1279 diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
1280 index 2c5363da7f5..9faf2be0f64 100644
1281 --- a/grub-core/commands/menuentry.c
1282 +++ b/grub-core/commands/menuentry.c
1283 @@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
1284                             char **classes, const char *id,
1285                             const char *users, const char *hotkey,
1286                             const char *prefix, const char *sourcecode,
1287 -                           int submenu)
1288 +                           int submenu, int *index, struct bls_entry *bls)
1289  {
1290    int menu_hotkey = 0;
1291    char **menu_args = NULL;
1292 @@ -149,9 +149,12 @@ grub_normal_add_menu_entry (int argc, const char **args,
1293    if (! menu_title)
1294      goto fail;
1295  
1296 +  grub_dprintf ("menu", "id:\"%s\"\n", id);
1297 +  grub_dprintf ("menu", "title:\"%s\"\n", menu_title);
1298    menu_id = grub_strdup (id ? : menu_title);
1299    if (! menu_id)
1300      goto fail;
1301 +  grub_dprintf ("menu", "menu_id:\"%s\"\n", menu_id);
1302  
1303    /* Save argc, args to pass as parameters to block arg later. */
1304    menu_args = grub_calloc (argc + 1, sizeof (char *));
1305 @@ -170,8 +173,12 @@ grub_normal_add_menu_entry (int argc, const char **args,
1306    }
1307  
1308    /* Add the menu entry at the end of the list.  */
1309 +  int ind=0;
1310    while (*last)
1311 -    last = &(*last)->next;
1312 +    {
1313 +      ind++;
1314 +      last = &(*last)->next;
1315 +    }
1316  
1317    *last = grub_zalloc (sizeof (**last));
1318    if (! *last)
1319 @@ -188,8 +195,11 @@ grub_normal_add_menu_entry (int argc, const char **args,
1320    (*last)->args = menu_args;
1321    (*last)->sourcecode = menu_sourcecode;
1322    (*last)->submenu = submenu;
1323 +  (*last)->bls = bls;
1324  
1325    menu->size++;
1326 +  if (index)
1327 +    *index = ind;
1328    return GRUB_ERR_NONE;
1329  
1330   fail:
1331 @@ -286,7 +296,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
1332                                        users,
1333                                        ctxt->state[2].arg, 0,
1334                                        ctxt->state[3].arg,
1335 -                                      ctxt->extcmd->cmd->name[0] == 's');
1336 +                                      ctxt->extcmd->cmd->name[0] == 's',
1337 +                                      NULL, NULL);
1338  
1339    src = args[argc - 1];
1340    args[argc - 1] = NULL;
1341 @@ -303,7 +314,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
1342                                   ctxt->state[0].args, ctxt->state[4].arg,
1343                                   users,
1344                                   ctxt->state[2].arg, prefix, src + 1,
1345 -                                 ctxt->extcmd->cmd->name[0] == 's');
1346 +                                 ctxt->extcmd->cmd->name[0] == 's', NULL,
1347 +                                 NULL);
1348  
1349    src[len - 1] = ch;
1350    args[argc - 1] = src;
1351 diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
1352 index 9ef98481f70..a326b192c89 100644
1353 --- a/grub-core/normal/main.c
1354 +++ b/grub-core/normal/main.c
1355 @@ -20,6 +20,7 @@
1356  #include <grub/net.h>
1357  #include <grub/normal.h>
1358  #include <grub/dl.h>
1359 +#include <grub/menu.h>
1360  #include <grub/misc.h>
1361  #include <grub/file.h>
1362  #include <grub/mm.h>
1363 @@ -70,6 +71,11 @@ grub_normal_free_menu (grub_menu_t menu)
1364           grub_free (entry->args);
1365         }
1366  
1367 +      if (entry->bls)
1368 +       {
1369 +         entry->bls->visible = 0;
1370 +       }
1371 +
1372        grub_free ((void *) entry->id);
1373        grub_free ((void *) entry->users);
1374        grub_free ((void *) entry->title);
1375 diff --git a/grub-core/commands/loadenv.h b/grub-core/commands/loadenv.h
1376 new file mode 100644
1377 index 00000000000..952f46121bd
1378 --- /dev/null
1379 +++ b/grub-core/commands/loadenv.h
1380 @@ -0,0 +1,93 @@
1381 +/* loadenv.c - command to load/save environment variable.  */
1382 +/*
1383 + *  GRUB  --  GRand Unified Bootloader
1384 + *  Copyright (C) 2008,2009,2010  Free Software Foundation, Inc.
1385 + *
1386 + *  GRUB is free software: you can redistribute it and/or modify
1387 + *  it under the terms of the GNU General Public License as published by
1388 + *  the Free Software Foundation, either version 3 of the License, or
1389 + *  (at your option) any later version.
1390 + *
1391 + *  GRUB is distributed in the hope that it will be useful,
1392 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1393 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1394 + *  GNU General Public License for more details.
1395 + *
1396 + *  You should have received a copy of the GNU General Public License
1397 + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
1398 + */
1399 +
1400 +static grub_envblk_t UNUSED
1401 +read_envblk_file (grub_file_t file)
1402 +{
1403 +  grub_off_t offset = 0;
1404 +  char *buf;
1405 +  grub_size_t size = grub_file_size (file);
1406 +  grub_envblk_t envblk;
1407 +
1408 +  buf = grub_malloc (size);
1409 +  if (! buf)
1410 +    return 0;
1411 +
1412 +  while (size > 0)
1413 +    {
1414 +      grub_ssize_t ret;
1415 +
1416 +      ret = grub_file_read (file, buf + offset, size);
1417 +      if (ret <= 0)
1418 +        {
1419 +          grub_free (buf);
1420 +          return 0;
1421 +        }
1422 +
1423 +      size -= ret;
1424 +      offset += ret;
1425 +    }
1426 +
1427 +  envblk = grub_envblk_open (buf, offset);
1428 +  if (! envblk)
1429 +    {
1430 +      grub_free (buf);
1431 +      grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
1432 +      return 0;
1433 +    }
1434 +
1435 +  return envblk;
1436 +}
1437 +
1438 +struct grub_env_whitelist
1439 +{
1440 +  grub_size_t len;
1441 +  char **list;
1442 +};
1443 +typedef struct grub_env_whitelist grub_env_whitelist_t;
1444 +
1445 +static int UNUSED
1446 +test_whitelist_membership (const char* name,
1447 +                           const grub_env_whitelist_t* whitelist)
1448 +{
1449 +  grub_size_t i;
1450 +
1451 +  for (i = 0; i < whitelist->len; i++)
1452 +    if (grub_strcmp (name, whitelist->list[i]) == 0)
1453 +      return 1;  /* found it */
1454 +
1455 +  return 0;  /* not found */
1456 +}
1457 +
1458 +/* Helper for grub_cmd_load_env.  */
1459 +static int UNUSED
1460 +set_var (const char *name, const char *value, void *whitelist)
1461 +{
1462 +  if (! whitelist)
1463 +    {
1464 +      grub_env_set (name, value);
1465 +      return 0;
1466 +    }
1467 +
1468 +  if (test_whitelist_membership (name,
1469 +                                (const grub_env_whitelist_t *) whitelist))
1470 +    grub_env_set (name, value);
1471 +
1472 +  return 0;
1473 +}
1474 diff --git a/include/grub/compiler.h b/include/grub/compiler.h
1475 index c9e1d7a73dc..9859ff4cc79 100644
1476 --- a/include/grub/compiler.h
1477 +++ b/include/grub/compiler.h
1478 @@ -48,4 +48,6 @@
1479  #  define CLANG_PREREQ(maj,min) 0
1480  #endif
1481  
1482 +#define UNUSED __attribute__((__unused__))
1483 +
1484  #endif /* ! GRUB_COMPILER_HEADER */
1485 diff --git a/include/grub/menu.h b/include/grub/menu.h
1486 index ee2b5e91045..0acdc2aa6bf 100644
1487 --- a/include/grub/menu.h
1488 +++ b/include/grub/menu.h
1489 @@ -20,6 +20,16 @@
1490  #ifndef GRUB_MENU_HEADER
1491  #define GRUB_MENU_HEADER 1
1492  
1493 +struct bls_entry
1494 +{
1495 +  struct bls_entry *next;
1496 +  struct bls_entry *prev;
1497 +  struct keyval **keyvals;
1498 +  int nkeyvals;
1499 +  char *filename;
1500 +  int visible;
1501 +};
1502 +
1503  struct grub_menu_entry_class
1504  {
1505    char *name;
1506 @@ -60,6 +70,9 @@ struct grub_menu_entry
1507  
1508    /* The next element.  */
1509    struct grub_menu_entry *next;
1510 +
1511 +  /* BLS used to populate the entry */
1512 +  struct bls_entry *bls;
1513  };
1514  typedef struct grub_menu_entry *grub_menu_entry_t;
1515  
1516 diff --git a/include/grub/normal.h b/include/grub/normal.h
1517 index 218cbabccaf..8839ad85a19 100644
1518 --- a/include/grub/normal.h
1519 +++ b/include/grub/normal.h
1520 @@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes,
1521                             const char *id,
1522                             const char *users, const char *hotkey,
1523                             const char *prefix, const char *sourcecode,
1524 -                           int submenu);
1525 +                           int submenu, int *index, struct bls_entry *bls);
1526  
1527  grub_err_t
1528  grub_normal_set_password (const char *user, const char *password);