* [PATCH v4 1/4] kern/vercmp: Add functionality to compare kernel versions
2025-05-21 12:51 [PATCH v4 0/4] Add commands to load BLS and UKI files Alec Brown via Grub-devel
@ 2025-05-21 12:51 ` Alec Brown via Grub-devel
2025-06-05 13:55 ` Daniel Kiper via Grub-devel
2025-05-21 12:51 ` [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot Loader Specification snippets Alec Brown via Grub-devel
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Alec Brown via Grub-devel @ 2025-05-21 12:51 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
Add functionality to compare alpha and numeric version segments for kernels.
This can be useful in sorting newer from older kernels.
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
---
Makefile.util.def | 16 ++
grub-core/kern/vercmp.c | 316 +++++++++++++++++++++++++++++++++++++++
include/grub/vercmp.h | 35 +++++
tests/vercmp_unit_test.c | 65 ++++++++
4 files changed, 432 insertions(+)
create mode 100644 grub-core/kern/vercmp.c
create mode 100644 include/grub/vercmp.h
create mode 100644 tests/vercmp_unit_test.c
diff --git a/Makefile.util.def b/Makefile.util.def
index 038253b37..15be983f8 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1373,6 +1373,22 @@ program = {
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
+program = {
+ testcase = native;
+ name = vercmp_unit_test;
+ common = tests/vercmp_unit_test.c;
+ common = tests/lib/unit_test.c;
+ common = grub-core/kern/list.c;
+ common = grub-core/kern/misc.c;
+ common = grub-core/tests/lib/test.c;
+ common = grub-core/kern/vercmp.c;
+ ldadd = libgrubmods.a;
+ ldadd = libgrubgcry.a;
+ ldadd = libgrubkern.a;
+ ldadd = grub-core/lib/gnulib/libgnu.a;
+ ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+};
+
program = {
name = grub-menulst2cfg;
mansection = 1;
diff --git a/grub-core/kern/vercmp.c b/grub-core/kern/vercmp.c
new file mode 100644
index 000000000..c2d69d7fd
--- /dev/null
+++ b/grub-core/kern/vercmp.c
@@ -0,0 +1,316 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/vercmp.h>
+
+#define GOTO_RETURN(x) ({ *ret = (x); goto finish; })
+
+/*
+ * compare alpha and numeric segments of two versions
+ * return 1: a is newer than b
+ * 0: a and b are the same version
+ * -1: a is older than b
+ */
+grub_err_t
+grub_vercmp (const char *a, const char *b, int *ret)
+{
+ char oldch1, oldch2;
+ char *abuf, *bbuf;
+ char *str1, *str2;
+ char *one, *two;
+ int rc;
+ bool isnum;
+
+ if (ret == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("return parameter is not set"));
+ *ret = 0;
+
+ if (grub_strcmp (a, b) == 0)
+ return GRUB_ERR_NONE;
+
+ abuf = grub_strdup (a);
+ if (abuf == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate string to compare versions");
+
+ bbuf = grub_strdup (b);
+ if (bbuf == NULL)
+ {
+ grub_free (abuf);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate string to compare versions");
+ }
+
+ str1 = abuf;
+ str2 = bbuf;
+
+ one = str1;
+ two = str2;
+
+ /* Loop through each version segment of str1 and str2 and compare them. */
+ while (*one != '\0' || *two != '\0')
+ {
+ while (*one != '\0' && grub_isalnum (*one) == 0 && *one != '~' && *one != '+')
+ one++;
+ while (*two != '\0' && grub_isalnum (*two) == 0 && *two != '~' && *two != '+')
+ two++;
+
+ /* Handle the tilde separator, it sorts before everything else. */
+ if (*one == '~' || *two == '~')
+ {
+ if (*one != '~')
+ GOTO_RETURN (GRUB_VERCMP_NEWER);
+ if (*two != '~')
+ GOTO_RETURN (GRUB_VERCMP_OLDER);
+ one++;
+ two++;
+ continue;
+ }
+
+ /*
+ * Handle the plus separator. Concept is the same as tilde, except that if
+ * one of the strings ends (base version), the other is considered as the
+ * higher version.
+ */
+ if (*one == '+' || *two == '+')
+ {
+ if (*one == '\0')
+ GOTO_RETURN (GRUB_VERCMP_OLDER);
+ if (*two == '\0')
+ GOTO_RETURN (GRUB_VERCMP_NEWER);
+ if (*one != '+')
+ GOTO_RETURN (GRUB_VERCMP_NEWER);
+ if (*two != '+')
+ GOTO_RETURN (GRUB_VERCMP_OLDER);
+ one++;
+ two++;
+ continue;
+ }
+
+ /* If we ran to the end of either, we are finished with the loop. */
+ if (*one == '\0' || *two == '\0')
+ break;
+
+ str1 = one;
+ str2 = two;
+
+ /*
+ * Grab the first completely alpha or completely numeric segment.
+ * Leave one and two pointing to the start of the alpha or numeric
+ * segment and walk str1 and str2 to end of segment.
+ */
+ if (grub_isdigit (*str1) == 1)
+ {
+ while (*str1 != '\0' && grub_isdigit (*str1) == 1)
+ str1++;
+ while (*str2 != '\0' && grub_isdigit (*str2) == 1)
+ str2++;
+ isnum = true;
+ }
+ else
+ {
+ while (*str1 != '\0' && grub_isalpha (*str1) == 1)
+ str1++;
+ while (*str2 != '\0' && grub_isalpha (*str2) == 1)
+ str2++;
+ isnum = false;
+ }
+
+ /*
+ * Save the character at the end of the alpha or numeric segment so that
+ * they can be restored after the comparison.
+ */
+ oldch1 = *str1;
+ *str1 = '\0';
+ oldch2 = *str2;
+ *str2 = '\0';
+
+ /*
+ * This cannot happen, as we previously tested to make sure that
+ * the first string has a non-null segment.
+ */
+ if (one == str1)
+ GOTO_RETURN (GRUB_VERCMP_OLDER); /* arbitrary */
+
+ /*
+ * Take care of the case where the two version segments are different
+ * types: one numeric, the other alpha (i.e. empty). Numeric segments are
+ * always newer than alpha segments.
+ */
+ if (two == str2)
+ GOTO_RETURN (isnum ? GRUB_VERCMP_NEWER : GRUB_VERCMP_OLDER);
+
+ if (isnum == true)
+ {
+ grub_size_t onelen, twolen;
+ /*
+ * This used to be done by converting the digit segments to ints using
+ * atoi() - it's changed because long digit segments can overflow an int -
+ * this should fix that.
+ */
+
+ /* Throw away any leading zeros - it's a number, right? */
+ while (*one == '0')
+ one++;
+ while (*two == '0')
+ two++;
+
+ /* Whichever number that has more digits wins. */
+ onelen = grub_strlen (one);
+ twolen = grub_strlen (two);
+ if (onelen > twolen)
+ GOTO_RETURN (GRUB_VERCMP_NEWER);
+ if (twolen > onelen)
+ GOTO_RETURN (GRUB_VERCMP_OLDER);
+ }
+
+ /*
+ * grub_strcmp will return which one is greater - even if the two segments
+ * are alpha or if they are numeric. Don't return if they are equal
+ * because there might be more segments to compare.
+ */
+ rc = grub_strcmp (one, two);
+ if (rc != 0)
+ GOTO_RETURN (rc < 1 ? GRUB_VERCMP_OLDER : GRUB_VERCMP_NEWER);
+
+ /* Restore the character that was replaced by null above. */
+ *str1 = oldch1;
+ one = str1;
+ *str2 = oldch2;
+ two = str2;
+ }
+
+ /*
+ * This catches the case where all alpha and numeric segments have compared
+ * identically but the segment separating characters were different.
+ */
+ if (*one == '\0' && *two == '\0')
+ GOTO_RETURN (GRUB_VERCMP_SAME);
+
+ /* Whichever version that still has characters left over wins. */
+ if (*one == '\0')
+ GOTO_RETURN (GRUB_VERCMP_OLDER);
+ else
+ GOTO_RETURN (GRUB_VERCMP_NEWER);
+
+ finish:
+ grub_free (abuf);
+ grub_free (bbuf);
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * returns name/version/release
+ * NULL string pointer returned if nothing is found
+ */
+void
+grub_split_package_string (char *package_string, char **name,
+ char **version, char **release)
+{
+ char *package_version, *package_release;
+
+ /* Release */
+ package_release = grub_strrchr (package_string, '-');
+
+ if (package_release != NULL)
+ *package_release++ = '\0';
+
+ *release = package_release;
+
+ if (name == NULL)
+ *version = package_string;
+ else
+ {
+ /* Version */
+ package_version = grub_strrchr (package_string, '-');
+
+ if (package_version != NULL)
+ *package_version++ = '\0';
+
+ *version = package_version;
+ /* Name */
+ *name = package_string;
+ }
+
+ /* Bubble up non-null values from release to name */
+ if (name != NULL && *name == NULL)
+ {
+ *name = (*version == NULL ? *release : *version);
+ *version = *release;
+ *release = NULL;
+ }
+ if (*version == NULL)
+ {
+ *version = *release;
+ *release = NULL;
+ }
+}
+
+/*
+ * return 1: nvr0 is newer than nvr1
+ * 0: nvr0 and nvr1 are the same version
+ * -1: nvr1 is newer than nvr0
+ */
+grub_err_t
+grub_split_vercmp (const char *nvr0, const char *nvr1, bool has_name, int *ret)
+{
+ grub_err_t err = GRUB_ERR_NONE;
+ char *str0, *name0, *version0, *release0;
+ char *str1, *name1, *version1, *release1;
+
+ if (ret == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("return parameter is not set"));
+
+ str0 = grub_strdup (nvr0);
+ if (str0 == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate BLS filename to compare");
+ str1 = grub_strdup (nvr1);
+ if (str1 == NULL)
+ {
+ grub_free (str0);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate BLS filename to compare");
+ }
+
+ grub_split_package_string (str0, has_name ? &name0 : NULL, &version0, &release0);
+ grub_split_package_string (str1, has_name ? &name1 : NULL, &version1, &release1);
+
+ if (has_name == true)
+ {
+ err = grub_vercmp (name0 == NULL ? "" : name0, name1 == NULL ? "" : name1, ret);
+ if (*ret != 0 || err != GRUB_ERR_NONE)
+ {
+ grub_free (str0);
+ grub_free (str1);
+ return err;
+ }
+ }
+
+ err = grub_vercmp (version0 == NULL ? "" : version0, version1 == NULL ? "" : version1, ret);
+ if (*ret != 0 || err != GRUB_ERR_NONE)
+ {
+ grub_free (str0);
+ grub_free (str1);
+ return err;
+ }
+
+ err = grub_vercmp (release0 == NULL ? "" : release0, release1 == NULL ? "" : release1, ret);
+
+ grub_free (str0);
+ grub_free (str1);
+ return err;
+}
diff --git a/include/grub/vercmp.h b/include/grub/vercmp.h
new file mode 100644
index 000000000..e588ef530
--- /dev/null
+++ b/include/grub/vercmp.h
@@ -0,0 +1,35 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GRUB_VERCMP_H
+#define GRUB_VERCMP_H 1
+
+enum
+ {
+ GRUB_VERCMP_OLDER = -1,
+ GRUB_VERCMP_SAME = 0,
+ GRUB_VERCMP_NEWER = 1,
+ };
+
+grub_err_t grub_vercmp (const char *a, const char *b, int *ret);
+
+void grub_split_package_string (char *package_string, char **name,
+ char **version, char **release);
+
+grub_err_t grub_split_vercmp (const char *nvr0, const char *nvr1, bool has_name, int *ret);
+
+#endif /* ! GRUB_VERCMP_H */
diff --git a/tests/vercmp_unit_test.c b/tests/vercmp_unit_test.c
new file mode 100644
index 000000000..9eb6acefd
--- /dev/null
+++ b/tests/vercmp_unit_test.c
@@ -0,0 +1,65 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <grub/test.h>
+#include <grub/vercmp.h>
+
+#define MSG "vercmp test failed"
+
+static void
+vercmp_test (void)
+{
+ const char *s1 = "name0-1.0.0-1.0.0.el8uek.x86_64";
+ const char *s2 = "name0-1.0.0-1.1.0.el8uek.x86_64";
+ const char *s3 = "name0-1.1.0-1.0.0.el8uek.x86_64";
+ const char *s4 = "name1-1.0.0-1.0.0.el8uek.x86_64";
+ const char *s5 = "version0";
+ const char *s6 = "version1";
+ char *nvr, *name, *version, *release;
+ int ret;
+
+ nvr = strdup (s1);
+
+ grub_split_package_string (nvr, &name, &version, &release);
+ grub_test_assert (strcmp (name, "name0") == 0, MSG);
+ grub_test_assert (strcmp (version, "1.0.0") == 0, MSG);
+ grub_test_assert (strcmp (release, "1.0.0.el8uek.x86_64") == 0, MSG);
+ free (nvr);
+
+ grub_split_vercmp (s1, s1, true, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_SAME, MSG);
+ grub_split_vercmp (s1, s2, true, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_OLDER, MSG);
+ grub_split_vercmp (s1, s3, true, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_OLDER, MSG);
+ grub_split_vercmp (s1, s4, true, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_OLDER, MSG);
+ grub_split_vercmp (s2, s1, true, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_NEWER, MSG);
+
+ grub_vercmp (s5, s5, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_SAME, MSG);
+ grub_vercmp (s5, s6, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_OLDER, MSG);
+ grub_vercmp (s6, s5, &ret);
+ grub_test_assert (ret == GRUB_VERCMP_NEWER, MSG);
+}
+
+GRUB_UNIT_TEST ("vercmp_unit_test", vercmp_test);
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v4 1/4] kern/vercmp: Add functionality to compare kernel versions
2025-05-21 12:51 ` [PATCH v4 1/4] kern/vercmp: Add functionality to compare kernel versions Alec Brown via Grub-devel
@ 2025-06-05 13:55 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 10+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-06-05 13:55 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
On Wed, May 21, 2025 at 12:51:23PM +0000, Alec Brown wrote:
> Add functionality to compare alpha and numeric version segments for kernels.
I think this code applies not only for kernels.
> This can be useful in sorting newer from older kernels.
Where this code come from? Or maybe it is written from scratch. Anyway,
I think whatever it is it should be mentioned in the commit message.
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> Makefile.util.def | 16 ++
> grub-core/kern/vercmp.c | 316 +++++++++++++++++++++++++++++++++++++++
> include/grub/vercmp.h | 35 +++++
> tests/vercmp_unit_test.c | 65 ++++++++
> 4 files changed, 432 insertions(+)
> create mode 100644 grub-core/kern/vercmp.c
> create mode 100644 include/grub/vercmp.h
> create mode 100644 tests/vercmp_unit_test.c
>
> diff --git a/Makefile.util.def b/Makefile.util.def
> index 038253b37..15be983f8 100644
> --- a/Makefile.util.def
> +++ b/Makefile.util.def
> @@ -1373,6 +1373,22 @@ program = {
> ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
> };
>
> +program = {
> + testcase = native;
> + name = vercmp_unit_test;
> + common = tests/vercmp_unit_test.c;
> + common = tests/lib/unit_test.c;
> + common = grub-core/kern/list.c;
> + common = grub-core/kern/misc.c;
> + common = grub-core/tests/lib/test.c;
> + common = grub-core/kern/vercmp.c;
> + ldadd = libgrubmods.a;
> + ldadd = libgrubgcry.a;
> + ldadd = libgrubkern.a;
> + ldadd = grub-core/lib/gnulib/libgnu.a;
> + ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
> +};
> +
> program = {
> name = grub-menulst2cfg;
> mansection = 1;
> diff --git a/grub-core/kern/vercmp.c b/grub-core/kern/vercmp.c
> new file mode 100644
> index 000000000..c2d69d7fd
> --- /dev/null
> +++ b/grub-core/kern/vercmp.c
> @@ -0,0 +1,316 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2025 Free Software Foundation, Inc.
> + *
> + * GRUB is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * GRUB is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
> + */
> +#include <grub/misc.h>
> +#include <grub/mm.h>
> +#include <grub/err.h>
> +#include <grub/vercmp.h>
> +
> +#define GOTO_RETURN(x) ({ *ret = (x); goto finish; })
You convert an enum to an int. Why? I think it can be simplified...
> +/*
> + * compare alpha and numeric segments of two versions
> + * return 1: a is newer than b
> + * 0: a and b are the same version
> + * -1: a is older than b
> + */
> +grub_err_t
> +grub_vercmp (const char *a, const char *b, int *ret)
You can add GRUB_VERCMP_UNKNOWN/GRUB_VERCMP_ERR constant to "vercmp"
enum, return that type and drop ret from arguments. Then probably
GOTO_RETURN macro could be also dropped.
> +{
> + char oldch1, oldch2;
> + char *abuf, *bbuf;
> + char *str1, *str2;
> + char *one, *two;
> + int rc;
> + bool isnum;
> +
> + if (ret == NULL)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("return parameter is not set"));
> + *ret = 0;
> +
> + if (grub_strcmp (a, b) == 0)
> + return GRUB_ERR_NONE;
> +
> + abuf = grub_strdup (a);
> + if (abuf == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate string to compare versions");
> +
> + bbuf = grub_strdup (b);
> + if (bbuf == NULL)
> + {
> + grub_free (abuf);
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate string to compare versions");
> + }
The const in the arguments suggests you do not need these grub_strdup() calls.
> + str1 = abuf;
> + str2 = bbuf;
> +
> + one = str1;
> + two = str2;
Could not we reduce number of variables here? If not it should be
explained which one is used for what.
> + /* Loop through each version segment of str1 and str2 and compare them. */
> + while (*one != '\0' || *two != '\0')
> + {
> + while (*one != '\0' && grub_isalnum (*one) == 0 && *one != '~' && *one != '+')
> + one++;
> + while (*two != '\0' && grub_isalnum (*two) == 0 && *two != '~' && *two != '+')
> + two++;
> +
> + /* Handle the tilde separator, it sorts before everything else. */
> + if (*one == '~' || *two == '~')
> + {
> + if (*one != '~')
> + GOTO_RETURN (GRUB_VERCMP_NEWER);
> + if (*two != '~')
> + GOTO_RETURN (GRUB_VERCMP_OLDER);
> + one++;
> + two++;
> + continue;
> + }
> +
> + /*
> + * Handle the plus separator. Concept is the same as tilde, except that if
> + * one of the strings ends (base version), the other is considered as the
> + * higher version.
> + */
> + if (*one == '+' || *two == '+')
> + {
> + if (*one == '\0')
> + GOTO_RETURN (GRUB_VERCMP_OLDER);
> + if (*two == '\0')
> + GOTO_RETURN (GRUB_VERCMP_NEWER);
> + if (*one != '+')
> + GOTO_RETURN (GRUB_VERCMP_NEWER);
> + if (*two != '+')
> + GOTO_RETURN (GRUB_VERCMP_OLDER);
> + one++;
> + two++;
> + continue;
> + }
> +
> + /* If we ran to the end of either, we are finished with the loop. */
> + if (*one == '\0' || *two == '\0')
> + break;
> +
> + str1 = one;
> + str2 = two;
> +
> + /*
> + * Grab the first completely alpha or completely numeric segment.
> + * Leave one and two pointing to the start of the alpha or numeric
> + * segment and walk str1 and str2 to end of segment.
> + */
> + if (grub_isdigit (*str1) == 1)
> + {
> + while (*str1 != '\0' && grub_isdigit (*str1) == 1)
> + str1++;
> + while (*str2 != '\0' && grub_isdigit (*str2) == 1)
> + str2++;
> + isnum = true;
> + }
> + else
> + {
> + while (*str1 != '\0' && grub_isalpha (*str1) == 1)
> + str1++;
> + while (*str2 != '\0' && grub_isalpha (*str2) == 1)
> + str2++;
> + isnum = false;
> + }
> +
> + /*
> + * Save the character at the end of the alpha or numeric segment so that
> + * they can be restored after the comparison.
> + */
> + oldch1 = *str1;
> + *str1 = '\0';
> + oldch2 = *str2;
> + *str2 = '\0';
> +
> + /*
> + * This cannot happen, as we previously tested to make sure that
> + * the first string has a non-null segment.
> + */
> + if (one == str1)
> + GOTO_RETURN (GRUB_VERCMP_OLDER); /* arbitrary */
> +
> + /*
> + * Take care of the case where the two version segments are different
> + * types: one numeric, the other alpha (i.e. empty). Numeric segments are
> + * always newer than alpha segments.
> + */
> + if (two == str2)
> + GOTO_RETURN (isnum ? GRUB_VERCMP_NEWER : GRUB_VERCMP_OLDER);
> +
> + if (isnum == true)
> + {
> + grub_size_t onelen, twolen;
> + /*
> + * This used to be done by converting the digit segments to ints using
> + * atoi() - it's changed because long digit segments can overflow an int -
> + * this should fix that.
> + */
> +
> + /* Throw away any leading zeros - it's a number, right? */
> + while (*one == '0')
> + one++;
> + while (*two == '0')
> + two++;
> +
> + /* Whichever number that has more digits wins. */
> + onelen = grub_strlen (one);
> + twolen = grub_strlen (two);
This could be calculated above and then you do not need to put NUL char
into strings.
> + if (onelen > twolen)
> + GOTO_RETURN (GRUB_VERCMP_NEWER);
> + if (twolen > onelen)
> + GOTO_RETURN (GRUB_VERCMP_OLDER);
> + }
> +
> + /*
> + * grub_strcmp will return which one is greater - even if the two segments
> + * are alpha or if they are numeric. Don't return if they are equal
> + * because there might be more segments to compare.
> + */
> + rc = grub_strcmp (one, two);
grub_strncmp() is probably your friend here.
> + if (rc != 0)
> + GOTO_RETURN (rc < 1 ? GRUB_VERCMP_OLDER : GRUB_VERCMP_NEWER);
> +
> + /* Restore the character that was replaced by null above. */
> + *str1 = oldch1;
> + one = str1;
> + *str2 = oldch2;
> + two = str2;
> + }
> +
> + /*
> + * This catches the case where all alpha and numeric segments have compared
> + * identically but the segment separating characters were different.
> + */
> + if (*one == '\0' && *two == '\0')
> + GOTO_RETURN (GRUB_VERCMP_SAME);
> +
> + /* Whichever version that still has characters left over wins. */
> + if (*one == '\0')
> + GOTO_RETURN (GRUB_VERCMP_OLDER);
> + else
> + GOTO_RETURN (GRUB_VERCMP_NEWER);
> +
> + finish:
> + grub_free (abuf);
> + grub_free (bbuf);
> + return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * returns name/version/release
> + * NULL string pointer returned if nothing is found
> + */
> +void
> +grub_split_package_string (char *package_string, char **name,
> + char **version, char **release)
> +{
> + char *package_version, *package_release;
> +
> + /* Release */
> + package_release = grub_strrchr (package_string, '-');
> +
> + if (package_release != NULL)
> + *package_release++ = '\0';
> +
> + *release = package_release;
> +
> + if (name == NULL)
> + *version = package_string;
> + else
> + {
> + /* Version */
> + package_version = grub_strrchr (package_string, '-');
> +
> + if (package_version != NULL)
> + *package_version++ = '\0';
> +
> + *version = package_version;
> + /* Name */
> + *name = package_string;
> + }
> +
> + /* Bubble up non-null values from release to name */
> + if (name != NULL && *name == NULL)
> + {
> + *name = (*version == NULL ? *release : *version);
> + *version = *release;
> + *release = NULL;
> + }
> + if (*version == NULL)
> + {
> + *version = *release;
> + *release = NULL;
> + }
> +}
> +
> +/*
> + * return 1: nvr0 is newer than nvr1
> + * 0: nvr0 and nvr1 are the same version
> + * -1: nvr1 is newer than nvr0
> + */
> +grub_err_t
> +grub_split_vercmp (const char *nvr0, const char *nvr1, bool has_name, int *ret)
Same comments like for grub_vercmp().
> +{
> + grub_err_t err = GRUB_ERR_NONE;
> + char *str0, *name0, *version0, *release0;
> + char *str1, *name1, *version1, *release1;
> +
> + if (ret == NULL)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("return parameter is not set"));
> +
> + str0 = grub_strdup (nvr0);
> + if (str0 == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate BLS filename to compare");
> + str1 = grub_strdup (nvr1);
> + if (str1 == NULL)
> + {
> + grub_free (str0);
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't duplicate BLS filename to compare");
> + }
Do we really need these grub_strdup() calls?
> + grub_split_package_string (str0, has_name ? &name0 : NULL, &version0, &release0);
s/has_name/(has_name == true)/
> + grub_split_package_string (str1, has_name ? &name1 : NULL, &version1, &release1);
Ditto.
> + if (has_name == true)
> + {
> + err = grub_vercmp (name0 == NULL ? "" : name0, name1 == NULL ? "" : name1, ret);
This suggests that grub_vercmp() should treat NULLs as empty strings.
Then the code should be nicer and safer.
> + if (*ret != 0 || err != GRUB_ERR_NONE)
> + {
> + grub_free (str0);
> + grub_free (str1);
> + return err;
> + }
> + }
> +
> + err = grub_vercmp (version0 == NULL ? "" : version0, version1 == NULL ? "" : version1, ret);
Ditto.
> + if (*ret != 0 || err != GRUB_ERR_NONE)
> + {
> + grub_free (str0);
> + grub_free (str1);
> + return err;
> + }
> +
> + err = grub_vercmp (release0 == NULL ? "" : release0, release1 == NULL ? "" : release1, ret);
Ditto.
> + grub_free (str0);
> + grub_free (str1);
> + return err;
> +}
> diff --git a/include/grub/vercmp.h b/include/grub/vercmp.h
> new file mode 100644
> index 000000000..e588ef530
> --- /dev/null
> +++ b/include/grub/vercmp.h
> @@ -0,0 +1,35 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2025 Free Software Foundation, Inc.
> + *
> + * GRUB is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * GRUB is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
> + */
> +#ifndef GRUB_VERCMP_H
> +#define GRUB_VERCMP_H 1
> +
> +enum
> + {
> + GRUB_VERCMP_OLDER = -1,
> + GRUB_VERCMP_SAME = 0,
> + GRUB_VERCMP_NEWER = 1,
> + };
> +
> +grub_err_t grub_vercmp (const char *a, const char *b, int *ret);
extern EXPORT_FUNC (grub_vercmp) (const char *a, const char *b, int *ret);
> +
> +void grub_split_package_string (char *package_string, char **name,
> + char **version, char **release);
> +
> +grub_err_t grub_split_vercmp (const char *nvr0, const char *nvr1, bool has_name, int *ret);
Do we really need to export these two functions? If yes then my comment
above applies here too.
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot Loader Specification snippets
2025-05-21 12:51 [PATCH v4 0/4] Add commands to load BLS and UKI files Alec Brown via Grub-devel
2025-05-21 12:51 ` [PATCH v4 1/4] kern/vercmp: Add functionality to compare kernel versions Alec Brown via Grub-devel
@ 2025-05-21 12:51 ` Alec Brown via Grub-devel
2025-06-05 19:23 ` Daniel Kiper via Grub-devel
2025-05-21 12:51 ` [PATCH v4 3/4] blsuki: Check for mounted /boot in emu Alec Brown via Grub-devel
2025-05-21 12:51 ` [PATCH v4 4/4] blsuki: Add uki command to load Unified Kernel Image entries Alec Brown via Grub-devel
3 siblings, 1 reply; 10+ messages in thread
From: Alec Brown via Grub-devel @ 2025-05-21 12:51 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
From: Peter Jones <pjones@redhat.com>
The BootLoaderSpec (BLS) defines a scheme where different bootloaders can
share a format for boot items and a configuration directory that accepts
these common configurations as drop-in files.
The BLS Specification:
https://uapi-group.org/specifications/specs/boot_loader_specification/
Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Will Thompson <wjt@endlessm.com>
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
---
docs/grub.texi | 47 ++
grub-core/Makefile.core.def | 10 +
grub-core/commands/blsuki.c | 1198 ++++++++++++++++++++++++++++++++
grub-core/commands/legacycfg.c | 4 +-
grub-core/commands/menuentry.c | 8 +-
grub-core/normal/main.c | 6 +
| 15 +
include/grub/normal.h | 2 +-
8 files changed, 1284 insertions(+), 6 deletions(-)
create mode 100644 grub-core/commands/blsuki.c
diff --git a/docs/grub.texi b/docs/grub.texi
index d9b26fa36..adab93668 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6417,6 +6417,7 @@ you forget a command, you can run the command @command{help}
* background_image:: Load background image for active terminal
* badram:: Filter out bad regions of RAM
* blocklist:: Print a block list
+* blscfg:: Load Boot Loader Specification menu entries
* boot:: Start up your operating system
* cat:: Show the contents of a file
* clear:: Clear the screen
@@ -6603,6 +6604,52 @@ Print a block list (@pxref{Block list syntax}) for @var{file}.
@end deffn
+@node blscfg
+@subsection blscfg
+
+@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
+Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries
+generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in
+the GRUB menu. Also, entries generated from @command{blscfg} only generate in memory and
+don't update @file{grub.cfg}.
+
+The @option{--path} option overrides the default path to the directory containing
+the BLS entries. If this option isn't used, the default location is
+/loader/entries in @code{$BOOT}. If no BLS entries are found, the
+@option{--enable-fallback} option can be used to check for entries in the default
+directory.
+
+The @option{--show-default} option allows the default boot entry to be added to the
+GRUB menu from the BLS entries.
+
+The @option{--show-non-default} option allows non-default boot entries to be added to
+the GRUB menu from the BLS entries.
+
+The @option{--entry} option allows specific boot entries to be added to the GRUB menu
+from the BLS entries.
+
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
+are used to filter which BLS entries are added to the GRUB menu. If none are
+used, all entries in the default location or the location specified by @option{--path}
+will be added to the GRUB menu.
+
+A BLS config file example:
+@example
+# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
+title Fedora 19 (Rawhide)
+sort-key fedora
+machine-id 6a9857a393724b7a981ebb5b8495b9ea
+version 3.8.0-2.fc19.x86_64
+options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet
+architecture x64
+linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
+initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
+@end example
+
+References: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}
+@end deffn
+
+
@node boot
@subsection boot
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index f70e02e69..67628f65f 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -845,6 +845,16 @@ module = {
common = commands/blocklist.c;
};
+module = {
+ name = blsuki;
+ common = commands/blsuki.c;
+ common = kern/vercmp.c;
+ enable = powerpc_ieee1275;
+ enable = efi;
+ enable = i386_pc;
+ enable = emu;
+};
+
module = {
name = boot;
common = commands/boot.c;
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
new file mode 100644
index 000000000..2ad960ae3
--- /dev/null
+++ b/grub-core/commands/blsuki.c
@@ -0,0 +1,1198 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/list.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/fs.h>
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/safemath.h>
+#include <grub/vercmp.h>
+#include <grub/lib/envblk.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+
+static const struct grub_arg_option bls_opt[] =
+ {
+ {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME},
+ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE},
+ {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
+ {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
+ {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+struct keyval
+{
+ const char *key;
+ char *val;
+};
+
+static grub_blsuki_entry_t *entries = NULL;
+
+#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
+
+/*
+ * This function will add a new keyval pair to a list of keyvals stored in the
+ * entry parameter.
+ */
+static grub_err_t
+blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
+{
+ char *k, *v;
+ struct keyval **kvs, *kv;
+ grub_size_t size;
+ int new_n = entry->nkeyvals + 1;
+
+ if (entry->keyvals_size == 0)
+ {
+ size = sizeof (struct keyval *);
+ kvs = grub_malloc (size);
+ if (kvs == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values");
+
+ entry->keyvals = kvs;
+ entry->keyvals_size = size;
+ }
+ else if (entry->keyvals_size < new_n * sizeof (struct keyval *))
+ {
+ size = entry->keyvals_size * 2;
+ kvs = grub_realloc (entry->keyvals, size);
+ if (kvs == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values");
+
+ entry->keyvals = kvs;
+ entry->keyvals_size = size;
+ }
+
+ kv = grub_malloc (sizeof (struct keyval));
+ if (kv == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+
+ k = grub_strdup (key);
+ if (k == NULL)
+ {
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+ }
+
+ v = grub_strdup (val);
+ if (v == NULL)
+ {
+ grub_free (k);
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+ }
+
+ kv->key = k;
+ kv->val = v;
+
+ entry->keyvals[entry->nkeyvals] = kv;
+ entry->nkeyvals = new_n;
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * Find the value of the key named by keyname. If there are allowed to be
+ * more than one, pass a pointer to an int set to -1 the first time, and pass
+ * the same pointer through each time after, and it'll return them in sorted
+ * order as defined in the BLS fragment file.
+ */
+static char *
+blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
+{
+ int idx, start = 0;
+ struct keyval *kv = NULL;
+ char *ret = NULL;
+
+ if (last != NULL)
+ start = *last + 1;
+
+ for (idx = start; idx < entry->nkeyvals; idx++)
+ {
+ kv = entry->keyvals[idx];
+
+ if (grub_strcmp (keyname, kv->key) == 0)
+ {
+ ret = kv->val;
+ break;
+ }
+ }
+
+ if (last != NULL)
+ {
+ if (idx == entry->nkeyvals)
+ *last = -1;
+ else
+ *last = idx;
+ }
+
+ return ret;
+}
+
+/*
+ * Add a new grub_blsuki_entry_t struct to the entries list and sort it's
+ * position on the list.
+ */
+static grub_err_t
+blsuki_add_entry (grub_blsuki_entry_t *entry)
+{
+ grub_blsuki_entry_t *e, *last = NULL;
+ grub_err_t err;
+ int rc;
+
+ if (entries == NULL)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ entries = entry;
+ return GRUB_ERR_NONE;
+ }
+
+ FOR_BLSUKI_ENTRIES (e)
+ {
+ err = grub_split_vercmp (entry->filename, e->filename, true, &rc);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ if (rc == GRUB_VERCMP_SAME)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "duplicate file");
+
+ if (rc == GRUB_VERCMP_NEWER)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry));
+ if (entry->next == entries)
+ {
+ entries = entry;
+ entry->prev = NULL;
+ }
+ else
+ last->next = entry;
+ return GRUB_ERR_NONE;
+ }
+ last = e;
+ }
+
+ if (last != NULL)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ last->next = entry;
+ entry->prev = &last;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * This function parses each line of a BLS config file to obtain the key value
+ * pairs that will be used to setup the GRUB menu entries. The key value pair
+ * will be stored in a list in the entry parameter.
+ */
+static grub_err_t
+bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
+{
+ grub_err_t err = GRUB_ERR_NONE;
+
+ for (;;)
+ {
+ char *buf;
+ char *separator;
+
+ buf = grub_file_getline (f);
+ if (buf == NULL)
+ break;
+
+ while (buf != NULL && buf[0] != '\0' && (buf[0] == ' ' || buf[0] == '\t'))
+ buf++;
+ if (buf[0] == '#')
+ {
+ grub_free (buf);
+ continue;
+ }
+
+ separator = grub_strchr (buf, ' ');
+
+ if (separator == NULL)
+ separator = grub_strchr (buf, '\t');
+
+ if (separator == NULL || separator[1] == '\0')
+ {
+ grub_free (buf);
+ break;
+ }
+
+ separator[0] = '\0';
+
+ do
+ {
+ separator++;
+ }
+ while (*separator == ' ' || *separator == '\t');
+
+ err = blsuki_add_keyval (entry, buf, separator);
+ grub_free (buf);
+ if (err != GRUB_ERR_NONE)
+ break;
+ }
+
+ return err;
+}
+
+struct read_entry_info
+{
+ const char *devid;
+ const char *dirname;
+ grub_file_t file;
+};
+
+/*
+ * If a file hasn't already been opened, this function opens a BLS config file
+ * and initializes entry data before parsing keyvals and adding the entry to
+ * the list of BLS entries.
+ */
+static int
+blsuki_read_entry (const char *filename,
+ const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)),
+ void *data)
+{
+ grub_size_t prefix_len = 0, filename_len, ext_len = 5;
+ grub_err_t err;
+ char *p = NULL;
+ grub_file_t f = NULL;
+ grub_blsuki_entry_t *entry;
+ struct read_entry_info *info = (struct read_entry_info *) data;
+
+ grub_dprintf ("blsuki", "filename: \"%s\"\n", filename);
+
+ filename_len = grub_strlen (filename);
+
+ if (info->file != NULL)
+ f = info->file;
+ else
+ {
+ if (filename_len < ext_len || grub_strcmp (filename + filename_len - ext_len, ".conf") != 0)
+ return 0;
+
+ p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
+
+ f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
+ grub_free (p);
+ if (f == NULL)
+ goto finish;
+ }
+
+ entry = grub_zalloc (sizeof (*entry));
+ if (entry == NULL)
+ goto finish;
+
+ /*
+ * If a file is opened before this function, the filename may have a prefix.
+ * Since the filename is used for the ID of the GRUB menu entry, we can
+ * remove the prefix.
+ */
+ if (info->file != NULL)
+ {
+ char *slash;
+
+ slash = grub_strrchr (filename, '/');
+ if (slash == NULL)
+ slash = grub_strrchr (filename, '\\');
+
+ if (slash != NULL)
+ {
+ while (*slash == '/' || *slash == '\\')
+ slash++;
+
+ prefix_len = slash - filename;
+ }
+ }
+ filename_len -= prefix_len;
+
+ entry->filename = grub_strndup (filename + prefix_len, filename_len);
+ if (entry->filename == NULL)
+ {
+ grub_free (entry);
+ goto finish;
+ }
+
+ err = bls_parse_keyvals (f, entry);
+
+ if (err == GRUB_ERR_NONE)
+ blsuki_add_entry (entry);
+ else
+ grub_free (entry);
+
+ finish:
+ if (f != NULL)
+ grub_file_close (f);
+
+ return 0;
+}
+
+/*
+ * This function returns a list of values that had the same key in the BLS
+ * config file. The number of entries in this list is returned by the len
+ * parameter.
+ */
+static char **
+blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len)
+{
+ int last = -1;
+ char *val;
+ int nlist = 0;
+ char **list;
+
+ list = grub_zalloc (sizeof (char *));
+ if (list == NULL)
+ return NULL;
+
+ while (1)
+ {
+ char **new;
+
+ /*
+ * Since the same key might appear more than once, the 'last' variable
+ * starts at -1 and increments to indicate the last index in the list
+ * we obtained from blsuki_get_val().
+ */
+ val = blsuki_get_val (entry, key, &last);
+ if (val == NULL)
+ break;
+
+ new = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (new == NULL)
+ break;
+
+ list = new;
+ list[nlist++] = val;
+ list[nlist] = NULL;
+ }
+
+ if (nlist == 0)
+ {
+ grub_free (list);
+ return NULL;
+ }
+
+ if (len != NULL)
+ *len = nlist;
+
+ return list;
+}
+
+/*
+ * This function appends a field to the end of a buffer. If the field given is
+ * an enviornmental variable, it gets the value stored for that variable and
+ * appends that to the buffer instead.
+ */
+static char *
+blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end)
+{
+ char *tmp;
+ const char *field;
+ int term = (is_env_var == true) ? 2 : 1;
+ grub_size_t size = 0;
+
+ tmp = grub_strndup (start, end - start + 1);
+ if (tmp == NULL)
+ return NULL;
+
+ field = tmp;
+
+ if (is_env_var == true)
+ {
+ field = grub_env_get (tmp);
+ if (field == NULL)
+ return buffer;
+ }
+
+ if (grub_add (grub_strlen (field), term, &size))
+ return NULL;
+
+ if (buffer == NULL)
+ buffer = grub_zalloc (size);
+ else
+ {
+ if (grub_add (size, grub_strlen (buffer), &size))
+ return NULL;
+ buffer = grub_realloc (buffer, size);
+ }
+
+ if (buffer == NULL)
+ return NULL;
+
+ tmp = buffer + grub_strlen (buffer);
+ tmp = grub_stpcpy (tmp, field);
+
+ if (is_env_var == true)
+ tmp = grub_stpcpy (tmp, " ");
+
+ return buffer;
+}
+
+/*
+ * This function takes a value string, checks for environmental variables, and
+ * returns the value string with all environmental variables replaced with the
+ * value stored in the variable.
+ */
+static char *
+blsuki_expand_val (const char *value)
+{
+ char *buffer = NULL;
+ const char *start = value;
+ const char *end = value;
+ bool is_env_var = false;
+
+ if (value == NULL)
+ return NULL;
+
+ while (*value != '\0')
+ {
+ if (*value == '$')
+ {
+ if (start != end)
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ is_env_var = true;
+ start = value + 1;
+ }
+ else if (is_env_var == true)
+ {
+ if (grub_isalnum (*value) == 0 && *value != '_')
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ is_env_var = false;
+ start = value;
+ if (*start == ' ')
+ start++;
+ }
+ }
+
+ end = value;
+ value++;
+ }
+
+ if (start != end)
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ return buffer;
+}
+
+/*
+ * This function parses a string containing initrd paths and returns it as a
+ * list.
+ */
+static char **
+blsuki_early_initrd_list (const char *initrd)
+{
+ int nlist = 0;
+ char **list = NULL;
+ const char *separator = initrd;
+ char *tmp;
+
+ for (;;)
+ {
+ while (*separator != '\0' && *separator != ' ' && *separator != '\t')
+ separator++;
+ if (*separator == '\0')
+ break;
+
+ list = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (list == NULL)
+ return NULL;
+
+ tmp = grub_strndup (initrd, separator - initrd);
+ if (tmp == NULL)
+ {
+ grub_free (list);
+ return NULL;
+ }
+ list[nlist++] = tmp;
+ list[nlist] = NULL;
+ separator++;
+ initrd = separator;
+ }
+
+ list = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (list == NULL)
+ return NULL;
+
+ tmp = grub_strndup (initrd, grub_strlen (initrd));
+ if (tmp == NULL)
+ {
+ grub_free (list);
+ return NULL;
+ }
+ list[nlist++] = tmp;
+ list[nlist] = NULL;
+
+ return list;
+}
+
+/*
+ * This function returns a string with the command to load a linux kernel with
+ * kernel command-line options based on what was specified in the BLS config
+ * file.
+ */
+static char *
+bls_get_linux (grub_blsuki_entry_t *entry)
+{
+ char *linux_path;
+ char *linux_cmd = NULL;
+ char *options = NULL;
+ char *tmp;
+ grub_size_t size;
+
+ linux_path = blsuki_get_val (entry, "linux", NULL);
+ options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL));
+
+ if (options == NULL)
+ options = blsuki_expand_val (grub_env_get ("default_kernelopts"));
+
+ if (grub_add (grub_strlen ("linux "), grub_strlen (linux_path), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
+ goto finish;
+ }
+
+ if (options != NULL)
+ {
+ if (grub_add (size, grub_strlen (options), &size) ||
+ grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
+ goto finish;
+ }
+ }
+
+ linux_cmd = grub_malloc (size);
+ if (linux_cmd == NULL)
+ goto finish;
+
+ tmp = linux_cmd;
+ tmp = grub_stpcpy (tmp, "linux ");
+ tmp = grub_stpcpy (tmp, linux_path);
+ if (options != NULL)
+ {
+ tmp = grub_stpcpy (tmp, " ");
+ tmp = grub_stpcpy (tmp, options);
+ }
+ tmp = grub_stpcpy (tmp, "\n");
+
+ finish:
+ grub_free (options);
+
+ return linux_cmd;
+}
+
+/*
+ * This function returns a string with the command to load all initrds for a
+ * linux kernel image based on the list provided by the BLS config file.
+ */
+static char *
+bls_get_initrd (grub_blsuki_entry_t *entry)
+{
+ char **initrd_list;
+ char *initrd_cmd = NULL;
+ const char *early_initrd;
+ char **early_initrd_list = NULL;
+ char *tmp;
+ char *slash;
+ char *prefix = NULL;
+ grub_size_t prefix_len = 0;
+ char *linux_path;
+ grub_size_t size;
+ int i;
+
+ initrd_list = blsuki_make_list (entry, "initrd", NULL);
+ early_initrd = grub_env_get ("early_initrd");
+
+ if (early_initrd != NULL)
+ {
+ early_initrd_list = blsuki_early_initrd_list (early_initrd);
+ if (early_initrd_list == NULL)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to create early initrd list"));
+ goto finish;
+ }
+
+ if (initrd_list != NULL && initrd_list[0] != NULL)
+ {
+ slash = grub_strrchr (initrd_list[0], '/');
+ if (slash == NULL)
+ prefix_len = slash - initrd_list[0] + 1;
+ prefix = grub_strndup (initrd_list[0], prefix_len);
+ }
+ else
+ {
+ linux_path = blsuki_get_val (entry, "linux", NULL);
+ slash = grub_strrchr (linux_path, '/');
+ if (slash != NULL)
+ prefix_len = slash - linux_path + 1;
+ prefix = grub_strndup (linux_path, prefix_len);
+ }
+
+ if (prefix == NULL)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate initrd prefix buffer"));
+ goto finish;
+ }
+ }
+
+ if (early_initrd_list != NULL || initrd_list != NULL)
+ {
+
+ size = grub_strlen ("initrd");
+
+ for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
+ {
+ if (grub_add (size, 1, &size) ||
+ grub_add (size, grub_strlen (prefix), &size) ||
+ grub_add (size, grub_strlen (early_initrd_list[i]), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
+ goto finish;
+ }
+ }
+
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
+ {
+ if (grub_add (size, 1, &size) ||
+ grub_add (size, grub_strlen (initrd_list[i]), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
+ goto finish;
+ }
+ }
+
+ if (grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
+ goto finish;
+ }
+
+ initrd_cmd = grub_malloc (size);
+ if (initrd_cmd == NULL)
+ goto finish;
+
+ tmp = grub_stpcpy (initrd_cmd, "initrd");
+ for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
+ {
+ grub_dprintf ("blsuki", "adding early initrd %s\n", early_initrd_list[i]);
+ tmp = grub_stpcpy (tmp, " ");
+ tmp = grub_stpcpy (tmp, prefix);
+ tmp = grub_stpcpy (tmp, early_initrd_list[i]);
+ }
+
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
+ {
+ grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
+ tmp = grub_stpcpy (tmp, " ");
+ tmp = grub_stpcpy (tmp, initrd_list[i]);
+ }
+ tmp = grub_stpcpy (tmp, "\n");
+ }
+
+ finish:
+ for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
+ grub_free (early_initrd_list[i]);
+ grub_free (prefix);
+
+ return initrd_cmd;
+}
+
+/*
+ * This function returns a string with the command to load a device tree blob
+ * from the BLS config file.
+ */
+static char *
+bls_get_devicetree (grub_blsuki_entry_t *entry)
+{
+ char *dt_path;
+ char *dt_cmd = NULL;
+ char *tmp;
+ char *linux_path;
+ char *slash;
+ char *prefix = NULL;
+ grub_size_t prefix_len = 0;
+ grub_size_t size;
+ bool add_dt_prefix = false;
+
+ dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL));
+
+ if (dt_path == NULL)
+ {
+ dt_path = blsuki_expand_val (grub_env_get ("devicetree"));
+ add_dt_prefix = true;
+ }
+
+ if (dt_path != NULL)
+ {
+ if (add_dt_prefix == true)
+ {
+ linux_path = blsuki_get_val (entry, "linux", NULL);
+ slash = grub_strchr (linux_path, '/');
+ if (slash != NULL)
+ prefix_len = slash - linux_path + 1;
+ prefix = grub_strndup (linux_path, prefix_len);
+ if (prefix == NULL)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate prefix buffer"));
+ goto finish;
+ }
+ }
+
+ if (grub_add (grub_strlen ("devicetree "), grub_strlen (dt_path), &size) ||
+ grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
+ goto finish;
+ }
+
+ if (add_dt_prefix == true)
+ {
+ if (grub_add (size, grub_strlen (prefix), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
+ goto finish;
+ }
+ }
+ dt_cmd = grub_malloc (size);
+ if (dt_cmd == NULL)
+ goto finish;
+ tmp = dt_cmd;
+ tmp = grub_stpcpy (dt_cmd, "devicetree");
+ tmp = grub_stpcpy (tmp, " ");
+ if (add_dt_prefix == true)
+ tmp = grub_stpcpy (tmp, prefix);
+ tmp = grub_stpcpy (tmp, dt_path);
+ tmp = grub_stpcpy (tmp, "\n");
+ }
+
+ finish:
+ grub_free (prefix);
+
+ return dt_cmd;
+}
+
+/*
+ * This function puts together all of the commands generated from the contents
+ * of the BLS config file and creates a new entry in the GRUB boot menu.
+ */
+static void
+bls_create_entry (grub_blsuki_entry_t *entry)
+{
+ int argc = 0;
+ const char **argv = NULL;
+ char *title = NULL;
+ char *linux_path = NULL;
+ char *linux_cmd = NULL;
+ char *initrd_cmd = NULL;
+ char *dt_cmd = NULL;
+ char *id = entry->filename;
+ grub_size_t id_len;
+ char *hotkey = NULL;
+ char *users = NULL;
+ char **classes = NULL;
+ char **args = NULL;
+ char *src = NULL;
+ const char *sdval = NULL;
+ int i;
+ grub_size_t size;
+ bool savedefault;
+
+ linux_path = blsuki_get_val (entry, "linux", NULL);
+ if (linux_path == NULL)
+ {
+ grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", entry->filename);
+ goto finish;
+ }
+
+ id_len = grub_strlen (id);
+ if (id_len >= 5 && grub_strcmp (id + id_len - 5, ".conf") == 0)
+ id[id_len - 5] = '\0';
+
+ title = blsuki_get_val (entry, "title", NULL);
+ hotkey = blsuki_get_val (entry, "grub_hotkey", NULL);
+ users = blsuki_expand_val (blsuki_get_val (entry, "grub_users", NULL));
+ classes = blsuki_make_list (entry, "grub_class", NULL);
+ args = blsuki_make_list (entry, "grub_arg", &argc);
+
+ argc++;
+ if (grub_mul (argc + 1, sizeof (char *), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv list"));
+ goto finish;
+ }
+
+ argv = grub_malloc (size);
+ if (argv == NULL)
+ goto finish;
+ argv[0] = title ? title : linux_path;
+ for (i = 1; i < argc; i++)
+ argv[i] = args[i-1];
+ argv[argc] = NULL;
+
+ linux_cmd = bls_get_linux (entry);
+ if (linux_cmd == NULL)
+ goto finish;
+
+ initrd_cmd = bls_get_initrd (entry);
+ if (grub_errno != GRUB_ERR_NONE)
+ goto finish;
+
+ dt_cmd = bls_get_devicetree (entry);
+ if (grub_errno != GRUB_ERR_NONE)
+ goto finish;
+
+ sdval = grub_env_get ("save_default");
+ savedefault = ((NULL != sdval) && (grub_strcmp (sdval, "true") == 0));
+ src = grub_xasprintf ("%s%s%s%s",
+ savedefault ? "savedefault\n" : "",
+ linux_cmd, initrd_cmd ? initrd_cmd : "",
+ dt_cmd ? dt_cmd : "");
+
+ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry);
+
+ finish:
+ grub_free (linux_cmd);
+ grub_free (dt_cmd);
+ grub_free (initrd_cmd);
+ grub_free (classes);
+ grub_free (args);
+ grub_free (argv);
+ grub_free (src);
+}
+
+struct find_entry_info
+{
+ const char *dirname;
+ const char *devid;
+ grub_device_t dev;
+ grub_fs_t fs;
+};
+
+/*
+ * This function fills a find_entry_info struct passed in by the info parameter.
+ * If the dirname or devid parameters are set to NULL, the dirname and devid
+ * fields in the info parameter will be set to default values. If info already
+ * has a value in the dev fields, we can compare it to the value passed in by
+ * the devid parameter or the default devid to see if we need to open a new
+ * device.
+ */
+static grub_err_t
+blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid)
+{
+ grub_device_t dev;
+ grub_fs_t fs;
+
+ if (info == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set");
+
+ if (devid == NULL)
+ {
+ devid = grub_env_get ("root");
+ if (devid == NULL)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "root");
+ }
+
+ /* Check that we aren't closing and opening the same device. */
+ if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0)
+ {
+ grub_device_close (info->dev);
+ info->dev = NULL;
+ }
+ /* If we are using the same device, then we can skip this step and only set the directory. */
+ if (info->dev == NULL)
+ {
+ grub_dprintf ("blsuki", "opening %s\n", devid);
+ dev = grub_device_open (devid);
+ if (dev == NULL)
+ return grub_errno;
+
+ grub_dprintf ("blsuki", "probing fs\n");
+ fs = grub_fs_probe (dev);
+ if (fs == NULL)
+ {
+ grub_device_close (dev);
+ return grub_errno;
+ }
+
+ info->devid = devid;
+ info->dev = dev;
+ info->fs = fs;
+ }
+
+ info->dirname = dirname;
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * This function searches for BLS config files based on the data in the info
+ * parameter. If the fallback option is enabled, the default location will be
+ * checked for BLS config files if the first attempt fails.
+ */
+static void
+blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
+{
+ struct read_entry_info read_entry_info;
+ grub_fs_t dir_fs = NULL;
+ grub_device_t dir_dev = NULL;
+ bool fallback = false;
+ int r;
+
+ do
+ {
+ read_entry_info.file = NULL;
+ read_entry_info.dirname = info->dirname;
+
+ grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname);
+ dir_dev = info->dev;
+ dir_fs = info->fs;
+ read_entry_info.devid = info->devid;
+
+ r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry,
+ &read_entry_info);
+ if (r != 0)
+ {
+ grub_dprintf ("blsuki", "blsuki_read_entry returned error\n");
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ /*
+ * If we aren't able to find BLS entries in the directory given by info->dirname,
+ * we can fallback to the default location "/boot/loader/entries/" and see if we
+ * can find the files there.
+ */
+ if (entries == NULL && fallback == false && enable_fallback == true)
+ {
+ blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
+ grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
+ read_entry_info.dirname, info->dirname);
+ fallback = true;
+ }
+ else
+ fallback = false;
+ }
+ while (fallback == true);
+}
+
+static grub_err_t
+blsuki_load_entries (char *path, bool enable_fallback)
+{
+ grub_size_t len;
+ static grub_err_t r;
+ const char *devid = NULL;
+ char *dir = NULL;
+ struct find_entry_info info = {
+ .dev = NULL,
+ .fs = NULL,
+ .dirname = NULL,
+ };
+ struct read_entry_info rei = {
+ .devid = NULL,
+ .dirname = NULL,
+ };
+
+ if (path != NULL)
+ {
+ len = grub_strlen (path);
+ if (len >= 5 && grub_strcmp (path + len - 5, ".conf") == 0)
+ {
+ rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
+ if (rei.file == NULL)
+ return grub_errno;
+
+ /* blsuki_read_entry() closes the file. */
+ return blsuki_read_entry (path, NULL, &rei);
+ }
+ else if (path[0] == '(')
+ {
+ devid = path + 1;
+
+ dir = grub_strchr (path, ')');
+ if (dir == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path);
+
+ *dir = '\0';
+
+ /* Check if there is more than the devid in the path. */
+ if (dir + 1 < path + len)
+ dir = dir + 1;
+ }
+ else if (path[0] == '/')
+ dir = path;
+ }
+
+ if (dir == NULL)
+ dir = (char *) GRUB_BLS_CONFIG_PATH;
+
+ r = blsuki_set_find_entry_info (&info, dir, devid);
+ if (r == GRUB_ERR_NONE)
+ blsuki_find_entry (&info, enable_fallback);
+
+ if (info.dev != NULL)
+ grub_device_close (info.dev);
+
+ return r;
+}
+
+static bool
+blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int idx)
+{
+ const char *title;
+ const char *def_entry_end;
+ int def_idx;
+
+ if (def_entry == NULL)
+ return false;
+
+ if (grub_strcmp (def_entry, entry->filename) == 0)
+ return true;
+
+ title = blsuki_get_val (entry, "title", NULL);
+
+ if (title != NULL && grub_strcmp (def_entry, title) == 0)
+ return true;
+
+ def_idx = (int) grub_strtol (def_entry, &def_entry_end, 0);
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return false;
+ }
+ if (*def_entry == '\0' || *def_entry_end != '\0')
+ return false;
+
+ if (def_idx == idx)
+ return true;
+
+ return false;
+}
+
+/*
+ * This function creates a GRUB boot menu entry for each BLS entry in the
+ * entries list.
+ */
+static grub_err_t
+blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id)
+{
+ const char *def_entry = NULL;
+ grub_blsuki_entry_t *entry = NULL;
+ int idx = 0;
+
+ def_entry = grub_env_get ("default");
+
+ FOR_BLSUKI_ENTRIES(entry)
+ {
+ if (entry->visible == 1)
+ {
+ idx++;
+ continue;
+ }
+ if ((show_default == true && blsuki_is_default_entry (def_entry, entry, idx) == true) ||
+ (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) ||
+ (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0))
+ {
+ bls_create_entry (entry);
+ entry->visible = 1;
+ }
+ idx++;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ grub_err_t err;
+ struct grub_arg_list *state = ctxt->state;
+ char *path = NULL;
+ char *entry_id = NULL;
+ bool enable_fallback = false;
+ bool show_default = false;
+ bool show_non_default = false;
+ bool all = true;
+
+ if (state[0].set)
+ path = state[0].arg;
+ if (state[1].set)
+ enable_fallback = true;
+ if (state[2].set)
+ {
+ show_default = true;
+ all = false;
+ }
+ if (state[3].set)
+ {
+ show_non_default = true;
+ all = false;
+ }
+ if (state[4].set)
+ {
+ entry_id = state[4].arg;
+ all = false;
+ }
+ if (all == true)
+ {
+ show_default = true;
+ show_non_default = true;
+ }
+
+ err = blsuki_load_entries (path, enable_fallback);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ return blsuki_create_entries (show_default, show_non_default, entry_id);
+}
+
+static grub_extcmd_t bls_cmd;
+
+GRUB_MOD_INIT(blsuki)
+{
+ bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0,
+ N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
+ N_("Import Boot Loader Specification snippets."),
+ bls_opt);
+}
+
+GRUB_MOD_FINI(blsuki)
+{
+ grub_unregister_extcmd (bls_cmd);
+}
diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
index 3bf9fe2e4..f3c86dc7f 100644
--- a/grub-core/commands/legacycfg.c
+++ b/grub-core/commands/legacycfg.c
@@ -143,7 +143,7 @@ legacy_file (const char *filename)
args[0] = oldname;
grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
NULL, NULL,
- entrysrc, 0);
+ entrysrc, 0, NULL);
grub_free (args);
entrysrc[0] = 0;
grub_free (oldname);
@@ -204,7 +204,7 @@ legacy_file (const char *filename)
}
args[0] = entryname;
grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
- NULL, NULL, entrysrc, 0);
+ NULL, NULL, entrysrc, 0, NULL);
grub_free (args);
}
diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
index 720e6d8ea..09749c415 100644
--- a/grub-core/commands/menuentry.c
+++ b/grub-core/commands/menuentry.c
@@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
char **classes, const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu)
+ int submenu, grub_blsuki_entry_t *blsuki)
{
int menu_hotkey = 0;
char **menu_args = NULL;
@@ -188,6 +188,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
(*last)->args = menu_args;
(*last)->sourcecode = menu_sourcecode;
(*last)->submenu = submenu;
+ (*last)->blsuki = blsuki;
menu->size++;
return GRUB_ERR_NONE;
@@ -286,7 +287,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
users,
ctxt->state[2].arg, 0,
ctxt->state[3].arg,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's',
+ NULL);
src = args[argc - 1];
args[argc - 1] = NULL;
@@ -303,7 +305,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
ctxt->state[0].args, ctxt->state[4].arg,
users,
ctxt->state[2].arg, prefix, src + 1,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's', NULL);
src[len - 1] = ch;
args[argc - 1] = src;
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
index 04d058f55..2d493f21e 100644
--- a/grub-core/normal/main.c
+++ b/grub-core/normal/main.c
@@ -21,6 +21,7 @@
#include <grub/net.h>
#include <grub/normal.h>
#include <grub/dl.h>
+#include <grub/menu.h>
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
@@ -67,6 +68,11 @@ grub_normal_free_menu (grub_menu_t menu)
grub_free (entry->args);
}
+ if (entry->blsuki)
+ {
+ entry->blsuki->visible = 0;
+ }
+
grub_free ((void *) entry->id);
grub_free ((void *) entry->users);
grub_free ((void *) entry->title);
--git a/include/grub/menu.h b/include/grub/menu.h
index ee2b5e910..c25a0d16d 100644
--- a/include/grub/menu.h
+++ b/include/grub/menu.h
@@ -20,6 +20,18 @@
#ifndef GRUB_MENU_HEADER
#define GRUB_MENU_HEADER 1
+struct grub_blsuki_entry
+{
+ struct grub_blsuki_entry *next;
+ struct grub_blsuki_entry **prev;
+ struct keyval **keyvals;
+ grub_size_t keyvals_size;
+ int nkeyvals;
+ char *filename;
+ int visible;
+};
+typedef struct grub_blsuki_entry grub_blsuki_entry_t;
+
struct grub_menu_entry_class
{
char *name;
@@ -60,6 +72,9 @@ struct grub_menu_entry
/* The next element. */
struct grub_menu_entry *next;
+
+ /* BLS used to populate the entry */
+ grub_blsuki_entry_t *blsuki;
};
typedef struct grub_menu_entry *grub_menu_entry_t;
diff --git a/include/grub/normal.h b/include/grub/normal.h
index 218cbabcc..d0150e3c2 100644
--- a/include/grub/normal.h
+++ b/include/grub/normal.h
@@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes,
const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu);
+ int submenu, grub_blsuki_entry_t *blsuki);
grub_err_t
grub_normal_set_password (const char *user, const char *password);
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot Loader Specification snippets
2025-05-21 12:51 ` [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot Loader Specification snippets Alec Brown via Grub-devel
@ 2025-06-05 19:23 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 10+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-06-05 19:23 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
On Wed, May 21, 2025 at 12:51:24PM +0000, Alec Brown wrote:
> From: Peter Jones <pjones@redhat.com>
>
> The BootLoaderSpec (BLS) defines a scheme where different bootloaders can
> share a format for boot items and a configuration directory that accepts
> these common configurations as drop-in files.
>
> The BLS Specification:
> https://uapi-group.org/specifications/specs/boot_loader_specification/
>
> Signed-off-by: Peter Jones <pjones@redhat.com>
> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
> Signed-off-by: Will Thompson <wjt@endlessm.com>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> docs/grub.texi | 47 ++
> grub-core/Makefile.core.def | 10 +
> grub-core/commands/blsuki.c | 1198 ++++++++++++++++++++++++++++++++
> grub-core/commands/legacycfg.c | 4 +-
> grub-core/commands/menuentry.c | 8 +-
> grub-core/normal/main.c | 6 +
> include/grub/menu.h | 15 +
> include/grub/normal.h | 2 +-
> 8 files changed, 1284 insertions(+), 6 deletions(-)
> create mode 100644 grub-core/commands/blsuki.c
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index d9b26fa36..adab93668 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -6417,6 +6417,7 @@ you forget a command, you can run the command @command{help}
> * background_image:: Load background image for active terminal
> * badram:: Filter out bad regions of RAM
> * blocklist:: Print a block list
> +* blscfg:: Load Boot Loader Specification menu entries
> * boot:: Start up your operating system
> * cat:: Show the contents of a file
> * clear:: Clear the screen
> @@ -6603,6 +6604,52 @@ Print a block list (@pxref{Block list syntax}) for @var{file}.
> @end deffn
>
>
> +@node blscfg
> +@subsection blscfg
> +
> +@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
> +Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries
> +generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in
> +the GRUB menu. Also, entries generated from @command{blscfg} only generate in memory and
s/only generate/exist only/
> +don't update @file{grub.cfg}.
> +
> +The @option{--path} option overrides the default path to the directory containing
> +the BLS entries. If this option isn't used, the default location is
> +/loader/entries in @code{$BOOT}. If no BLS entries are found, the
What is "$BOOT"? AIUI this comes from BLS spec. Though it has to be
clearly explained or BLS spec properly referenced here.
> +@option{--enable-fallback} option can be used to check for entries in the default
> +directory.
What is a default directory?
> +The @option{--show-default} option allows the default boot entry to be added to the
> +GRUB menu from the BLS entries.
> +
> +The @option{--show-non-default} option allows non-default boot entries to be added to
> +the GRUB menu from the BLS entries.
> +
> +The @option{--entry} option allows specific boot entries to be added to the GRUB menu
> +from the BLS entries.
> +
> +The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
> +are used to filter which BLS entries are added to the GRUB menu. If none are
> +used, all entries in the default location or the location specified by @option{--path}
> +will be added to the GRUB menu.
> +
> +A BLS config file example:
> +@example
> +# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
> +title Fedora 19 (Rawhide)
> +sort-key fedora
> +machine-id 6a9857a393724b7a981ebb5b8495b9ea
> +version 3.8.0-2.fc19.x86_64
> +options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet
> +architecture x64
> +linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
> +initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
> +@end example
> +
> +References: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}
Nice, but it has to be clearly referenced from the text above.
> +@end deffn
> +
> +
> @node boot
> @subsection boot
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index f70e02e69..67628f65f 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -845,6 +845,16 @@ module = {
> common = commands/blocklist.c;
> };
>
> +module = {
> + name = blsuki;
> + common = commands/blsuki.c;
> + common = kern/vercmp.c;
I think this should not be a part of the module but the kernel.
> + enable = powerpc_ieee1275;
> + enable = efi;
> + enable = i386_pc;
> + enable = emu;
> +};
> +
> module = {
> name = boot;
> common = commands/boot.c;
> diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
> new file mode 100644
> index 000000000..2ad960ae3
> --- /dev/null
> +++ b/grub-core/commands/blsuki.c
> @@ -0,0 +1,1198 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2025 Free Software Foundation, Inc.
> + *
> + * GRUB is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * GRUB is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/list.h>
> +#include <grub/types.h>
> +#include <grub/misc.h>
> +#include <grub/mm.h>
> +#include <grub/err.h>
> +#include <grub/dl.h>
> +#include <grub/extcmd.h>
> +#include <grub/i18n.h>
> +#include <grub/fs.h>
> +#include <grub/env.h>
> +#include <grub/file.h>
> +#include <grub/normal.h>
> +#include <grub/safemath.h>
> +#include <grub/vercmp.h>
> +#include <grub/lib/envblk.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
> +
> +static const struct grub_arg_option bls_opt[] =
> + {
> + {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME},
> + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE},
> + {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
> + {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
> + {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE},
> + {0, 0, 0, 0, 0, 0}
> + };
> +
> +struct keyval
> +{
> + const char *key;
> + char *val;
> +};
> +
> +static grub_blsuki_entry_t *entries = NULL;
> +
> +#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
> +
> +/*
> + * This function will add a new keyval pair to a list of keyvals stored in the
> + * entry parameter.
> + */
> +static grub_err_t
> +blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
> +{
> + char *k, *v;
> + struct keyval **kvs, *kv;
> + grub_size_t size;
> + int new_n = entry->nkeyvals + 1;
> +
> + if (entry->keyvals_size == 0)
> + {
> + size = sizeof (struct keyval *);
> + kvs = grub_malloc (size);
> + if (kvs == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values");
> +
> + entry->keyvals = kvs;
> + entry->keyvals_size = size;
> + }
> + else if (entry->keyvals_size < new_n * sizeof (struct keyval *))
> + {
> + size = entry->keyvals_size * 2;
> + kvs = grub_realloc (entry->keyvals, size);
> + if (kvs == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values");
> +
> + entry->keyvals = kvs;
> + entry->keyvals_size = size;
> + }
> +
> + kv = grub_malloc (sizeof (struct keyval));
> + if (kv == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
> +
> + k = grub_strdup (key);
> + if (k == NULL)
> + {
> + grub_free (kv);
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
> + }
> +
> + v = grub_strdup (val);
> + if (v == NULL)
> + {
> + grub_free (k);
> + grub_free (kv);
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
> + }
> +
> + kv->key = k;
> + kv->val = v;
> +
> + entry->keyvals[entry->nkeyvals] = kv;
> + entry->nkeyvals = new_n;
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * Find the value of the key named by keyname. If there are allowed to be
> + * more than one, pass a pointer to an int set to -1 the first time, and pass
s/int/last/?
> + * the same pointer through each time after, and it'll return them in sorted
> + * order as defined in the BLS fragment file.
> + */
> +static char *
> +blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
> +{
> + int idx, start = 0;
start = (last != NULL) ? (*last + 1) : 0;
> + struct keyval *kv = NULL;
> + char *ret = NULL;
> +
> + if (last != NULL)
> + start = *last + 1;
... and you can drop this then...
> + for (idx = start; idx < entry->nkeyvals; idx++)
> + {
> + kv = entry->keyvals[idx];
> +
> + if (grub_strcmp (keyname, kv->key) == 0)
> + {
> + ret = kv->val;
> + break;
> + }
> + }
> +
> + if (last != NULL)
> + {
> + if (idx == entry->nkeyvals)
> + *last = -1;
> + else
> + *last = idx;
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * Add a new grub_blsuki_entry_t struct to the entries list and sort it's
> + * position on the list.
> + */
> +static grub_err_t
> +blsuki_add_entry (grub_blsuki_entry_t *entry)
> +{
> + grub_blsuki_entry_t *e, *last = NULL;
> + grub_err_t err;
> + int rc;
> +
> + if (entries == NULL)
> + {
> + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
> + entries = entry;
> + return GRUB_ERR_NONE;
> + }
> +
> + FOR_BLSUKI_ENTRIES (e)
> + {
> + err = grub_split_vercmp (entry->filename, e->filename, true, &rc);
> + if (err != GRUB_ERR_NONE)
> + return err;
> +
> + if (rc == GRUB_VERCMP_SAME)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, "duplicate file");
I would add file name to the error message... Though I am not sure how
this error can be triggered...
> + if (rc == GRUB_VERCMP_NEWER)
> + {
> + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
> + grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry));
> + if (entry->next == entries)
> + {
> + entries = entry;
> + entry->prev = NULL;
> + }
> + else
> + last->next = entry;
> + return GRUB_ERR_NONE;
> + }
> + last = e;
> + }
> +
> + if (last != NULL)
> + {
> + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
> + last->next = entry;
> + entry->prev = &last;
> + }
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * This function parses each line of a BLS config file to obtain the key value
> + * pairs that will be used to setup the GRUB menu entries. The key value pair
> + * will be stored in a list in the entry parameter.
> + */
> +static grub_err_t
> +bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
> +{
> + grub_err_t err = GRUB_ERR_NONE;
> +
> + for (;;)
> + {
> + char *buf;
> + char *separator;
> +
> + buf = grub_file_getline (f);
> + if (buf == NULL)
> + break;
> +
> + while (buf != NULL && buf[0] != '\0' && (buf[0] == ' ' || buf[0] == '\t'))
"buf != NULL" seems redundant here...
And I think buf[0] is confusing here and should be replaced with *buf.
> + buf++;
> + if (buf[0] == '#')
Ditto.
> + {
> + grub_free (buf);
You do not free what you want here...
> + continue;
> + }
> +
> + separator = grub_strchr (buf, ' ');
> +
> + if (separator == NULL)
> + separator = grub_strchr (buf, '\t');
> +
> + if (separator == NULL || separator[1] == '\0')
Hmmm... Could not you implement grub_strtok()? "man strtok" is your friend.
Copy from gnulib?
> + {
> + grub_free (buf);
> + break;
> + }
> +
> + separator[0] = '\0';
Even if 'separator[0]' work *separator is more appropriate here.
> + do
> + {
> + separator++;
> + }
> + while (*separator == ' ' || *separator == '\t');
> +
> + err = blsuki_add_keyval (entry, buf, separator);
> + grub_free (buf);
Again, buf may point to not what you expect...
> + if (err != GRUB_ERR_NONE)
> + break;
> + }
> +
> + return err;
> +}
> +
> +struct read_entry_info
> +{
> + const char *devid;
> + const char *dirname;
> + grub_file_t file;
> +};
I would prefer if you define all structs and variables at the beginning
of the file.
> +/*
> + * If a file hasn't already been opened, this function opens a BLS config file
> + * and initializes entry data before parsing keyvals and adding the entry to
> + * the list of BLS entries.
> + */
> +static int
> +blsuki_read_entry (const char *filename,
> + const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)),
> + void *data)
> +{
> + grub_size_t prefix_len = 0, filename_len, ext_len = 5;
> + grub_err_t err;
> + char *p = NULL;
> + grub_file_t f = NULL;
> + grub_blsuki_entry_t *entry;
> + struct read_entry_info *info = (struct read_entry_info *) data;
> +
> + grub_dprintf ("blsuki", "filename: \"%s\"\n", filename);
> +
> + filename_len = grub_strlen (filename);
> +
> + if (info->file != NULL)
> + f = info->file;
> + else
> + {
> + if (filename_len < ext_len || grub_strcmp (filename + filename_len - ext_len, ".conf") != 0)
Please use 'sizeof (".conf") - 1' instead of ext_len.
> + return 0;
> +
> + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
> +
> + f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
> + grub_free (p);
> + if (f == NULL)
> + goto finish;
> + }
> +
> + entry = grub_zalloc (sizeof (*entry));
> + if (entry == NULL)
> + goto finish;
> +
> + /*
> + * If a file is opened before this function, the filename may have a prefix.
I think you mean "path" instead of "prefix" here.
Additionally, could not you use grub_get_dirname() from
grub-core/gfxmenu/gui_string_util.c or find better alternative in the
GRUB? Of course if you use grub_get_dirname() then it should be moved to
the kernel...
> + * Since the filename is used for the ID of the GRUB menu entry, we can
> + * remove the prefix.
> + */
> + if (info->file != NULL)
> + {
> + char *slash;
> +
> + slash = grub_strrchr (filename, '/');
> + if (slash == NULL)
> + slash = grub_strrchr (filename, '\\');
I am not entirely sure why we care about "\"...
> + if (slash != NULL)
> + {
> + while (*slash == '/' || *slash == '\\')
> + slash++;
> +
> + prefix_len = slash - filename;
> + }
> + }
> + filename_len -= prefix_len;
> +
> + entry->filename = grub_strndup (filename + prefix_len, filename_len);
> + if (entry->filename == NULL)
> + {
> + grub_free (entry);
> + goto finish;
> + }
> +
> + err = bls_parse_keyvals (f, entry);
> +
> + if (err == GRUB_ERR_NONE)
> + blsuki_add_entry (entry);
> + else
> + grub_free (entry);
> +
> + finish:
> + if (f != NULL)
> + grub_file_close (f);
> +
> + return 0;
> +}
> +
> +/*
> + * This function returns a list of values that had the same key in the BLS
> + * config file. The number of entries in this list is returned by the len
> + * parameter.
> + */
> +static char **
> +blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len)
> +{
> + int last = -1;
> + char *val;
> + int nlist = 0;
> + char **list;
> +
> + list = grub_zalloc (sizeof (char *));
> + if (list == NULL)
> + return NULL;
> +
> + while (1)
> + {
> + char **new;
> +
> + /*
> + * Since the same key might appear more than once, the 'last' variable
> + * starts at -1 and increments to indicate the last index in the list
> + * we obtained from blsuki_get_val().
> + */
> + val = blsuki_get_val (entry, key, &last);
> + if (val == NULL)
> + break;
> +
> + new = grub_realloc (list, (nlist + 2) * sizeof (char *));
> + if (new == NULL)
> + break;
> +
> + list = new;
> + list[nlist++] = val;
Should not we artificially limit number of values to avoid overflows?
10000?
> + list[nlist] = NULL;
> + }
> +
> + if (nlist == 0)
> + {
> + grub_free (list);
> + return NULL;
> + }
> +
> + if (len != NULL)
> + *len = nlist;
> +
> + return list;
> +}
> +
> +/*
> + * This function appends a field to the end of a buffer. If the field given is
> + * an enviornmental variable, it gets the value stored for that variable and
> + * appends that to the buffer instead.
> + */
> +static char *
> +blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end)
> +{
> + char *tmp;
> + const char *field;
> + int term = (is_env_var == true) ? 2 : 1;
What is term?
> + grub_size_t size = 0;
> +
> + tmp = grub_strndup (start, end - start + 1);
> + if (tmp == NULL)
> + return NULL;
> +
> + field = tmp;
> +
> + if (is_env_var == true)
> + {
> + field = grub_env_get (tmp);
This looks like GRUB extension not compatible with BLS spec. I think it
should be documented in the GRUB docs. And of course it should be
mentioned it is not compatible with other bootloaders.
> + if (field == NULL)
> + return buffer;
> + }
> +
> + if (grub_add (grub_strlen (field), term, &size))
> + return NULL;
> +
> + if (buffer == NULL)
> + buffer = grub_zalloc (size);
> + else
> + {
> + if (grub_add (size, grub_strlen (buffer), &size))
> + return NULL;
> + buffer = grub_realloc (buffer, size);
> + }
> +
> + if (buffer == NULL)
> + return NULL;
> +
> + tmp = buffer + grub_strlen (buffer);
> + tmp = grub_stpcpy (tmp, field);
> +
> + if (is_env_var == true)
> + tmp = grub_stpcpy (tmp, " ");
> +
> + return buffer;
> +}
> +
> +/*
> + * This function takes a value string, checks for environmental variables, and
> + * returns the value string with all environmental variables replaced with the
> + * value stored in the variable.
> + */
> +static char *
> +blsuki_expand_val (const char *value)
> +{
> + char *buffer = NULL;
> + const char *start = value;
> + const char *end = value;
> + bool is_env_var = false;
> +
> + if (value == NULL)
> + return NULL;
> +
> + while (*value != '\0')
> + {
> + if (*value == '$')
> + {
> + if (start != end)
> + {
> + buffer = blsuki_field_append (is_env_var, buffer, start, end);
> + if (buffer == NULL)
> + return NULL;
> + }
> +
> + is_env_var = true;
> + start = value + 1;
> + }
> + else if (is_env_var == true)
> + {
> + if (grub_isalnum (*value) == 0 && *value != '_')
> + {
> + buffer = blsuki_field_append (is_env_var, buffer, start, end);
> + is_env_var = false;
> + start = value;
> + if (*start == ' ')
> + start++;
> + }
> + }
> +
> + end = value;
> + value++;
Probably spaces should be replaced with tab here...
> + }
> +
> + if (start != end)
> + {
> + buffer = blsuki_field_append (is_env_var, buffer, start, end);
> + if (buffer == NULL)
> + return NULL;
> + }
> +
> + return buffer;
> +}
> +
> +/*
> + * This function parses a string containing initrd paths and returns it as a
> + * list.
> + */
> +static char **
> +blsuki_early_initrd_list (const char *initrd)
> +{
> + int nlist = 0;
> + char **list = NULL;
> + const char *separator = initrd;
> + char *tmp;
> +
> + for (;;)
> + {
> + while (*separator != '\0' && *separator != ' ' && *separator != '\t')
> + separator++;
> + if (*separator == '\0')
> + break;
grub_strtok()?
> + list = grub_realloc (list, (nlist + 2) * sizeof (char *));
> + if (list == NULL)
> + return NULL;
> +
> + tmp = grub_strndup (initrd, separator - initrd);
> + if (tmp == NULL)
> + {
> + grub_free (list);
> + return NULL;
> + }
> + list[nlist++] = tmp;
> + list[nlist] = NULL;
> + separator++;
> + initrd = separator;
> + }
> +
> + list = grub_realloc (list, (nlist + 2) * sizeof (char *));
> + if (list == NULL)
> + return NULL;
> +
> + tmp = grub_strndup (initrd, grub_strlen (initrd));
> + if (tmp == NULL)
> + {
> + grub_free (list);
> + return NULL;
> + }
> + list[nlist++] = tmp;
> + list[nlist] = NULL;
> +
> + return list;
> +}
> +
> +/*
> + * This function returns a string with the command to load a linux kernel with
> + * kernel command-line options based on what was specified in the BLS config
> + * file.
> + */
> +static char *
> +bls_get_linux (grub_blsuki_entry_t *entry)
> +{
> + char *linux_path;
> + char *linux_cmd = NULL;
> + char *options = NULL;
> + char *tmp;
> + grub_size_t size;
> +
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL));
> +
> + if (options == NULL)
> + options = blsuki_expand_val (grub_env_get ("default_kernelopts"));
This variable has to be documented. In general all new variables have to
be documented...
> + if (grub_add (grub_strlen ("linux "), grub_strlen (linux_path), &size))
Do you need to get into account NUL at the end of string?
s/grub_strlen ("linux ")/sizeof ("linux ")/?
Though you have to remember that sizeof() counts NUL at the end of string.
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
> + goto finish;
> + }
> +
> + if (options != NULL)
> + {
> + if (grub_add (size, grub_strlen (options), &size) ||
> + grub_add (size, 1, &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
> + goto finish;
> + }
> + }
> +
> + linux_cmd = grub_malloc (size);
> + if (linux_cmd == NULL)
> + goto finish;
> +
> + tmp = linux_cmd;
> + tmp = grub_stpcpy (tmp, "linux ");
> + tmp = grub_stpcpy (tmp, linux_path);
> + if (options != NULL)
> + {
> + tmp = grub_stpcpy (tmp, " ");
> + tmp = grub_stpcpy (tmp, options);
> + }
> + tmp = grub_stpcpy (tmp, "\n");
> +
> + finish:
> + grub_free (options);
> +
> + return linux_cmd;
> +}
> +
> +/*
> + * This function returns a string with the command to load all initrds for a
> + * linux kernel image based on the list provided by the BLS config file.
> + */
> +static char *
> +bls_get_initrd (grub_blsuki_entry_t *entry)
> +{
> + char **initrd_list;
> + char *initrd_cmd = NULL;
> + const char *early_initrd;
> + char **early_initrd_list = NULL;
> + char *tmp;
> + char *slash;
> + char *prefix = NULL;
> + grub_size_t prefix_len = 0;
> + char *linux_path;
> + grub_size_t size;
> + int i;
> +
> + initrd_list = blsuki_make_list (entry, "initrd", NULL);
> + early_initrd = grub_env_get ("early_initrd");
Again, this variable has to be documented...
> + if (early_initrd != NULL)
> + {
> + early_initrd_list = blsuki_early_initrd_list (early_initrd);
> + if (early_initrd_list == NULL)
> + {
> + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to create early initrd list"));
> + goto finish;
> + }
> +
> + if (initrd_list != NULL && initrd_list[0] != NULL)
> + {
> + slash = grub_strrchr (initrd_list[0], '/');
> + if (slash == NULL)
> + prefix_len = slash - initrd_list[0] + 1;
> + prefix = grub_strndup (initrd_list[0], prefix_len);
> + }
> + else
> + {
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + slash = grub_strrchr (linux_path, '/');
> + if (slash != NULL)
> + prefix_len = slash - linux_path + 1;
> + prefix = grub_strndup (linux_path, prefix_len);
> + }
> +
> + if (prefix == NULL)
> + {
> + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate initrd prefix buffer"));
> + goto finish;
> + }
> + }
> +
> + if (early_initrd_list != NULL || initrd_list != NULL)
> + {
> +
> + size = grub_strlen ("initrd");
> +
> + for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
> + {
> + if (grub_add (size, 1, &size) ||
> + grub_add (size, grub_strlen (prefix), &size) ||
> + grub_add (size, grub_strlen (early_initrd_list[i]), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
> + goto finish;
> + }
> + }
> +
> + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
> + {
> + if (grub_add (size, 1, &size) ||
> + grub_add (size, grub_strlen (initrd_list[i]), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
> + goto finish;
> + }
> + }
> +
> + if (grub_add (size, 1, &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
> + goto finish;
> + }
> +
> + initrd_cmd = grub_malloc (size);
> + if (initrd_cmd == NULL)
> + goto finish;
> +
> + tmp = grub_stpcpy (initrd_cmd, "initrd");
> + for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
> + {
> + grub_dprintf ("blsuki", "adding early initrd %s\n", early_initrd_list[i]);
> + tmp = grub_stpcpy (tmp, " ");
> + tmp = grub_stpcpy (tmp, prefix);
> + tmp = grub_stpcpy (tmp, early_initrd_list[i]);
> + }
> +
> + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
> + {
> + grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
> + tmp = grub_stpcpy (tmp, " ");
> + tmp = grub_stpcpy (tmp, initrd_list[i]);
> + }
> + tmp = grub_stpcpy (tmp, "\n");
> + }
> +
> + finish:
> + for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
> + grub_free (early_initrd_list[i]);
> + grub_free (prefix);
> +
> + return initrd_cmd;
> +}
> +
> +/*
> + * This function returns a string with the command to load a device tree blob
> + * from the BLS config file.
> + */
> +static char *
> +bls_get_devicetree (grub_blsuki_entry_t *entry)
> +{
> + char *dt_path;
> + char *dt_cmd = NULL;
> + char *tmp;
> + char *linux_path;
> + char *slash;
> + char *prefix = NULL;
> + grub_size_t prefix_len = 0;
> + grub_size_t size;
> + bool add_dt_prefix = false;
> +
> + dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL));
> +
> + if (dt_path == NULL)
> + {
> + dt_path = blsuki_expand_val (grub_env_get ("devicetree"));
The GRUB documentation should be expanded for this variable.
> + add_dt_prefix = true;
> + }
> +
> + if (dt_path != NULL)
> + {
> + if (add_dt_prefix == true)
> + {
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + slash = grub_strchr (linux_path, '/');
> + if (slash != NULL)
> + prefix_len = slash - linux_path + 1;
> + prefix = grub_strndup (linux_path, prefix_len);
> + if (prefix == NULL)
> + {
> + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate prefix buffer"));
> + goto finish;
> + }
> + }
> +
> + if (grub_add (grub_strlen ("devicetree "), grub_strlen (dt_path), &size) ||
s/grub_strlen ("devicetree ")/sizeof ("devicetree ") - 1/?
Please check similar places too.
> + grub_add (size, 1, &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
> + goto finish;
> + }
> +
> + if (add_dt_prefix == true)
> + {
> + if (grub_add (size, grub_strlen (prefix), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
> + goto finish;
> + }
> + }
> + dt_cmd = grub_malloc (size);
> + if (dt_cmd == NULL)
> + goto finish;
> + tmp = dt_cmd;
> + tmp = grub_stpcpy (dt_cmd, "devicetree");
> + tmp = grub_stpcpy (tmp, " ");
> + if (add_dt_prefix == true)
> + tmp = grub_stpcpy (tmp, prefix);
> + tmp = grub_stpcpy (tmp, dt_path);
> + tmp = grub_stpcpy (tmp, "\n");
> + }
> +
> + finish:
> + grub_free (prefix);
> +
> + return dt_cmd;
> +}
> +
> +/*
> + * This function puts together all of the commands generated from the contents
> + * of the BLS config file and creates a new entry in the GRUB boot menu.
> + */
> +static void
> +bls_create_entry (grub_blsuki_entry_t *entry)
> +{
> + int argc = 0;
> + const char **argv = NULL;
> + char *title = NULL;
> + char *linux_path = NULL;
> + char *linux_cmd = NULL;
> + char *initrd_cmd = NULL;
> + char *dt_cmd = NULL;
> + char *id = entry->filename;
> + grub_size_t id_len;
> + char *hotkey = NULL;
> + char *users = NULL;
> + char **classes = NULL;
> + char **args = NULL;
> + char *src = NULL;
> + const char *sdval = NULL;
> + int i;
> + grub_size_t size;
> + bool savedefault;
> +
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + if (linux_path == NULL)
> + {
> + grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", entry->filename);
> + goto finish;
> + }
> +
> + id_len = grub_strlen (id);
> + if (id_len >= 5 && grub_strcmp (id + id_len - 5, ".conf") == 0)
> + id[id_len - 5] = '\0';
Please avoid cryptic constants. I think 5 should be replaced with
'sizeof (".conf") - 1'. Maybe it is worth defining constant for this
value because as I can see it is used in a few places. You can use
expression given above to define a constant.
> + title = blsuki_get_val (entry, "title", NULL);
> + hotkey = blsuki_get_val (entry, "grub_hotkey", NULL);
> + users = blsuki_expand_val (blsuki_get_val (entry, "grub_users", NULL));
> + classes = blsuki_make_list (entry, "grub_class", NULL);
> + args = blsuki_make_list (entry, "grub_arg", &argc);
These smell like non-standard extensions. They beg for documentation.
> + argc++;
> + if (grub_mul (argc + 1, sizeof (char *), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv list"));
> + goto finish;
> + }
> +
> + argv = grub_malloc (size);
> + if (argv == NULL)
> + goto finish;
> + argv[0] = title ? title : linux_path;
(title != NULL) ? title : linux_path;
> + for (i = 1; i < argc; i++)
> + argv[i] = args[i-1];
> + argv[argc] = NULL;
> +
> + linux_cmd = bls_get_linux (entry);
> + if (linux_cmd == NULL)
> + goto finish;
> +
> + initrd_cmd = bls_get_initrd (entry);
> + if (grub_errno != GRUB_ERR_NONE)
> + goto finish;
> +
> + dt_cmd = bls_get_devicetree (entry);
> + if (grub_errno != GRUB_ERR_NONE)
> + goto finish;
> +
> + sdval = grub_env_get ("save_default");
> + savedefault = ((NULL != sdval) && (grub_strcmp (sdval, "true") == 0));
> + src = grub_xasprintf ("%s%s%s%s",
> + savedefault ? "savedefault\n" : "",
> + linux_cmd, initrd_cmd ? initrd_cmd : "",
> + dt_cmd ? dt_cmd : "");
> +
> + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry);
> +
> + finish:
> + grub_free (linux_cmd);
> + grub_free (dt_cmd);
> + grub_free (initrd_cmd);
> + grub_free (classes);
> + grub_free (args);
> + grub_free (argv);
> + grub_free (src);
> +}
> +
> +struct find_entry_info
> +{
> + const char *dirname;
> + const char *devid;
> + grub_device_t dev;
> + grub_fs_t fs;
> +};
Please define this at the beginning of the file.
> +/*
> + * This function fills a find_entry_info struct passed in by the info parameter.
> + * If the dirname or devid parameters are set to NULL, the dirname and devid
> + * fields in the info parameter will be set to default values. If info already
> + * has a value in the dev fields, we can compare it to the value passed in by
> + * the devid parameter or the default devid to see if we need to open a new
> + * device.
> + */
> +static grub_err_t
> +blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid)
> +{
> + grub_device_t dev;
> + grub_fs_t fs;
> +
> + if (info == NULL)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set");
> +
> + if (devid == NULL)
> + {
> + devid = grub_env_get ("root");
> + if (devid == NULL)
> + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "root");
> + }
> +
> + /* Check that we aren't closing and opening the same device. */
> + if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0)
> + {
> + grub_device_close (info->dev);
> + info->dev = NULL;
> + }
> + /* If we are using the same device, then we can skip this step and only set the directory. */
> + if (info->dev == NULL)
> + {
> + grub_dprintf ("blsuki", "opening %s\n", devid);
> + dev = grub_device_open (devid);
> + if (dev == NULL)
> + return grub_errno;
> +
> + grub_dprintf ("blsuki", "probing fs\n");
> + fs = grub_fs_probe (dev);
> + if (fs == NULL)
> + {
> + grub_device_close (dev);
> + return grub_errno;
> + }
> +
> + info->devid = devid;
> + info->dev = dev;
> + info->fs = fs;
> + }
> +
> + info->dirname = dirname;
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * This function searches for BLS config files based on the data in the info
> + * parameter. If the fallback option is enabled, the default location will be
> + * checked for BLS config files if the first attempt fails.
> + */
> +static void
> +blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> +{
> + struct read_entry_info read_entry_info;
> + grub_fs_t dir_fs = NULL;
> + grub_device_t dir_dev = NULL;
> + bool fallback = false;
> + int r;
> +
> + do
> + {
> + read_entry_info.file = NULL;
> + read_entry_info.dirname = info->dirname;
> +
> + grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname);
> + dir_dev = info->dev;
> + dir_fs = info->fs;
> + read_entry_info.devid = info->devid;
> +
> + r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry,
> + &read_entry_info);
> + if (r != 0)
> + {
> + grub_dprintf ("blsuki", "blsuki_read_entry returned error\n");
> + grub_errno = GRUB_ERR_NONE;
> + }
> +
> + /*
> + * If we aren't able to find BLS entries in the directory given by info->dirname,
> + * we can fallback to the default location "/boot/loader/entries/" and see if we
> + * can find the files there.
> + */
> + if (entries == NULL && fallback == false && enable_fallback == true)
> + {
> + blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
> + grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
> + read_entry_info.dirname, info->dirname);
> + fallback = true;
> + }
> + else
> + fallback = false;
> + }
> + while (fallback == true);
> +}
> +
> +static grub_err_t
> +blsuki_load_entries (char *path, bool enable_fallback)
> +{
> + grub_size_t len;
> + static grub_err_t r;
> + const char *devid = NULL;
> + char *dir = NULL;
> + struct find_entry_info info = {
> + .dev = NULL,
> + .fs = NULL,
> + .dirname = NULL,
> + };
> + struct read_entry_info rei = {
> + .devid = NULL,
> + .dirname = NULL,
> + };
> +
> + if (path != NULL)
> + {
> + len = grub_strlen (path);
> + if (len >= 5 && grub_strcmp (path + len - 5, ".conf") == 0)
Again, please replace 5 with a constant/sizeof()...
> + {
> + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
> + if (rei.file == NULL)
> + return grub_errno;
> +
> + /* blsuki_read_entry() closes the file. */
> + return blsuki_read_entry (path, NULL, &rei);
> + }
> + else if (path[0] == '(')
> + {
> + devid = path + 1;
> +
> + dir = grub_strchr (path, ')');
> + if (dir == NULL)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path);
> +
> + *dir = '\0';
> +
> + /* Check if there is more than the devid in the path. */
> + if (dir + 1 < path + len)
> + dir = dir + 1;
> + }
> + else if (path[0] == '/')
> + dir = path;
> + }
> +
> + if (dir == NULL)
> + dir = (char *) GRUB_BLS_CONFIG_PATH;
> +
> + r = blsuki_set_find_entry_info (&info, dir, devid);
> + if (r == GRUB_ERR_NONE)
> + blsuki_find_entry (&info, enable_fallback);
> +
> + if (info.dev != NULL)
> + grub_device_close (info.dev);
> +
> + return r;
> +}
> +
> +static bool
> +blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int idx)
> +{
> + const char *title;
> + const char *def_entry_end;
> + int def_idx;
> +
> + if (def_entry == NULL)
> + return false;
> +
> + if (grub_strcmp (def_entry, entry->filename) == 0)
> + return true;
> +
> + title = blsuki_get_val (entry, "title", NULL);
> +
> + if (title != NULL && grub_strcmp (def_entry, title) == 0)
> + return true;
> +
> + def_idx = (int) grub_strtol (def_entry, &def_entry_end, 0);
> + if (grub_errno != GRUB_ERR_NONE)
This error check is unreliable. Please take a look at commit ac8a37dda
(net/http: Allow use of non-standard TCP/IP ports). It is good example
how it should be done properly.
> + {
> + grub_errno = GRUB_ERR_NONE;
> + return false;
> + }
> + if (*def_entry == '\0' || *def_entry_end != '\0')
> + return false;
> +
> + if (def_idx == idx)
> + return true;
> +
> + return false;
> +}
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4 3/4] blsuki: Check for mounted /boot in emu
2025-05-21 12:51 [PATCH v4 0/4] Add commands to load BLS and UKI files Alec Brown via Grub-devel
2025-05-21 12:51 ` [PATCH v4 1/4] kern/vercmp: Add functionality to compare kernel versions Alec Brown via Grub-devel
2025-05-21 12:51 ` [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot Loader Specification snippets Alec Brown via Grub-devel
@ 2025-05-21 12:51 ` Alec Brown via Grub-devel
2025-06-05 20:13 ` Daniel Kiper via Grub-devel
2025-05-21 12:51 ` [PATCH v4 4/4] blsuki: Add uki command to load Unified Kernel Image entries Alec Brown via Grub-devel
3 siblings, 1 reply; 10+ messages in thread
From: Alec Brown via Grub-devel @ 2025-05-21 12:51 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
From: Robbie Harwood <rharwood@redhat.com>
Irritatingly, BLS defines paths relative to the mountpoint of the
filesystem which contains its snippets, not / or any other fixed
location. So grub2-emu needs to know whether /boot is a separate
filesystem from / and conditionally prepend a path.
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
---
grub-core/Makefile.core.def | 4 ++
grub-core/commands/blsuki.c | 92 ++++++++++++++++++++++++++++++---
grub-core/osdep/linux/getroot.c | 8 +++
grub-core/osdep/unix/getroot.c | 10 ++++
include/grub/emu/misc.h | 2 +-
5 files changed, 107 insertions(+), 9 deletions(-)
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 67628f65f..93b88795e 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -367,6 +367,10 @@ kernel = {
emu = kern/emu/cache_s.S;
emu = kern/emu/hostdisk.c;
emu = osdep/unix/hostdisk.c;
+ emu = osdep/relpath.c;
+ emu = osdep/getroot.c;
+ emu = osdep/unix/getroot.c;
+ emu = osdep/devmapper/getroot.c;
emu = osdep/exec.c;
extra_dist = osdep/unix/exec.c;
emu = osdep/devmapper/hostdisk.c;
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
index 2ad960ae3..bf2edc5ac 100644
--- a/grub-core/commands/blsuki.c
+++ b/grub-core/commands/blsuki.c
@@ -32,6 +32,13 @@
#include <grub/vercmp.h>
#include <grub/lib/envblk.h>
+#ifdef GRUB_MACHINE_EMU
+#include <grub/emu/misc.h>
+#define GRUB_BOOT_DEVICE "/boot"
+#else
+#define GRUB_BOOT_DEVICE ""
+#endif
+
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
@@ -56,6 +63,40 @@ static grub_blsuki_entry_t *entries = NULL;
#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
+#ifdef GRUB_MACHINE_EMU
+/*
+ * Cache probing in blsuki_update_boot_device().
+ */
+static int separate_boot = -1;
+#endif
+
+/*
+ * BLS appears to make paths relative to the filesystem that snippets are
+ * on, not /. Attempt to cope.
+ */
+static char *blsuki_update_boot_device (char *tmp)
+{
+#ifdef GRUB_MACHINE_EMU
+ char *ret;
+
+ if (separate_boot != -1)
+ goto probed;
+
+ separate_boot = 0;
+
+ ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE);
+
+ if (ret != NULL && ret[0] == '\0')
+ separate_boot = 1;
+
+ probed:
+ if (!separate_boot)
+ return tmp;
+#endif
+
+ return grub_stpcpy (tmp, GRUB_BOOT_DEVICE);
+}
+
/*
* This function will add a new keyval pair to a list of keyvals stored in the
* entry parameter.
@@ -582,7 +623,7 @@ bls_get_linux (grub_blsuki_entry_t *entry)
if (options == NULL)
options = blsuki_expand_val (grub_env_get ("default_kernelopts"));
- if (grub_add (grub_strlen ("linux "), grub_strlen (linux_path), &size))
+ if (grub_add (grub_strlen ("linux " GRUB_BOOT_DEVICE), grub_strlen (linux_path), &size))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
goto finish;
@@ -604,6 +645,7 @@ bls_get_linux (grub_blsuki_entry_t *entry)
tmp = linux_cmd;
tmp = grub_stpcpy (tmp, "linux ");
+ tmp = blsuki_update_boot_device (tmp);
tmp = grub_stpcpy (tmp, linux_path);
if (options != NULL)
{
@@ -679,7 +721,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
{
- if (grub_add (size, 1, &size) ||
+ if (grub_add (size, grub_strlen (" " GRUB_BOOT_DEVICE), &size) ||
grub_add (size, grub_strlen (prefix), &size) ||
grub_add (size, grub_strlen (early_initrd_list[i]), &size))
{
@@ -690,7 +732,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
{
- if (grub_add (size, 1, &size) ||
+ if (grub_add (size, grub_strlen (" " GRUB_BOOT_DEVICE), &size) ||
grub_add (size, grub_strlen (initrd_list[i]), &size))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
@@ -713,6 +755,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
{
grub_dprintf ("blsuki", "adding early initrd %s\n", early_initrd_list[i]);
tmp = grub_stpcpy (tmp, " ");
+ tmp = blsuki_update_boot_device (tmp);
tmp = grub_stpcpy (tmp, prefix);
tmp = grub_stpcpy (tmp, early_initrd_list[i]);
}
@@ -721,6 +764,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
{
grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
tmp = grub_stpcpy (tmp, " ");
+ tmp = blsuki_update_boot_device (tmp);
tmp = grub_stpcpy (tmp, initrd_list[i]);
}
tmp = grub_stpcpy (tmp, "\n");
@@ -775,7 +819,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry)
}
}
- if (grub_add (grub_strlen ("devicetree "), grub_strlen (dt_path), &size) ||
+ if (grub_add (grub_strlen ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (dt_path), &size) ||
grub_add (size, 1, &size))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
@@ -796,6 +840,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry)
tmp = dt_cmd;
tmp = grub_stpcpy (dt_cmd, "devicetree");
tmp = grub_stpcpy (tmp, " ");
+ tmp = blsuki_update_boot_device (tmp);
if (add_dt_prefix == true)
tmp = grub_stpcpy (tmp, prefix);
tmp = grub_stpcpy (tmp, dt_path);
@@ -924,7 +969,11 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
if (devid == NULL)
{
+#ifdef GRUB_MACHINE_EMU
+ devid = "host";
+#else
devid = grub_env_get ("root");
+#endif
if (devid == NULL)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "root");
}
@@ -966,10 +1015,13 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
* parameter. If the fallback option is enabled, the default location will be
* checked for BLS config files if the first attempt fails.
*/
-static void
+static grub_err_t
blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
{
struct read_entry_info read_entry_info;
+ char *default_dir = NULL;
+ char *tmp;
+ grub_size_t default_size;
grub_fs_t dir_fs = NULL;
grub_device_t dir_dev = NULL;
bool fallback = false;
@@ -1000,7 +1052,15 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
*/
if (entries == NULL && fallback == false && enable_fallback == true)
{
- blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
+ default_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH);
+ default_dir = grub_malloc (default_size);
+ if (default_dir == NULL)
+ return grub_errno;
+
+ tmp = blsuki_update_boot_device (default_dir);
+ tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
+
+ blsuki_set_find_entry_info (info, default_dir, NULL);
grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
read_entry_info.dirname, info->dirname);
fallback = true;
@@ -1009,6 +1069,9 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
fallback = false;
}
while (fallback == true);
+
+ grub_free (default_dir);
+ return GRUB_ERR_NONE;
}
static grub_err_t
@@ -1018,6 +1081,9 @@ blsuki_load_entries (char *path, bool enable_fallback)
static grub_err_t r;
const char *devid = NULL;
char *dir = NULL;
+ char *default_dir = NULL;
+ char *tmp;
+ grub_size_t dir_size;
struct find_entry_info info = {
.dev = NULL,
.fs = NULL,
@@ -1059,15 +1125,25 @@ blsuki_load_entries (char *path, bool enable_fallback)
}
if (dir == NULL)
- dir = (char *) GRUB_BLS_CONFIG_PATH;
+ {
+ dir_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH);
+ default_dir = grub_malloc (dir_size);
+ if (default_dir == NULL)
+ return grub_errno;
+
+ tmp = blsuki_update_boot_device (default_dir);
+ tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
+ dir = default_dir;
+ }
r = blsuki_set_find_entry_info (&info, dir, devid);
if (r == GRUB_ERR_NONE)
- blsuki_find_entry (&info, enable_fallback);
+ r = blsuki_find_entry (&info, enable_fallback);
if (info.dev != NULL)
grub_device_close (info.dev);
+ grub_free (default_dir);
return r;
}
diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
index 527d4f0c5..2fc212276 100644
--- a/grub-core/osdep/linux/getroot.c
+++ b/grub-core/osdep/linux/getroot.c
@@ -131,6 +131,7 @@ struct mountinfo_entry
char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
};
+#ifdef GRUB_UTIL
static char **
grub_util_raid_getmembers (const char *name, int bootable)
{
@@ -191,6 +192,7 @@ grub_util_raid_getmembers (const char *name, int bootable)
return devicelist;
}
+#endif
/* Statting something on a btrfs filesystem always returns a virtual device
major/minor pair rather than the real underlying device, because btrfs
@@ -579,6 +581,7 @@ out:
return ret;
}
+#ifdef GRUB_UTIL
static char *
get_mdadm_uuid (const char *os_dev)
{
@@ -636,6 +639,7 @@ out:
return name;
}
+#endif
static int
grub_util_is_imsm (const char *os_dev)
@@ -968,6 +972,7 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st,
return path;
}
+#ifdef GRUB_UTIL
static char *
grub_util_get_raid_grub_dev (const char *os_dev)
{
@@ -1070,6 +1075,7 @@ grub_util_get_raid_grub_dev (const char *os_dev)
}
return grub_dev;
}
+#endif
enum grub_dev_abstraction_types
grub_util_get_dev_abstraction_os (const char *os_dev)
@@ -1086,6 +1092,7 @@ grub_util_get_dev_abstraction_os (const char *os_dev)
return GRUB_DEV_ABSTRACTION_NONE;
}
+#ifdef GRUB_UTIL
int
grub_util_pull_device_os (const char *os_dev,
enum grub_dev_abstraction_types ab)
@@ -1142,6 +1149,7 @@ grub_util_get_grub_dev_os (const char *os_dev)
return grub_dev;
}
+#endif
char *
grub_make_system_path_relative_to_its_root_os (const char *path)
diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c
index c7aa202ab..1380638d3 100644
--- a/grub-core/osdep/unix/getroot.c
+++ b/grub-core/osdep/unix/getroot.c
@@ -17,6 +17,14 @@
*/
#include <config.h>
+#undef _GL_INLINE_HEADER_BEGIN
+#undef _GL_INLINE_HEADER_END
+#undef _GL_GNUC_PREREQ
+#undef _GL_ATTRIBUTE_COLD
+#undef _GL_ATTRIBUTE_CONST
+#undef _GL_ATTRIBUTE_DEALLOC
+#undef _GL_ATTRIBUTE_FALLTHROUGH
+#undef _GL_ATTRIBUTE_MALLOC
#include <config-util.h>
#include <sys/stat.h>
@@ -566,6 +574,7 @@ grub_guess_root_devices (const char *dir_in)
#endif
+#ifdef GRUB_UTIL
void
grub_util_pull_lvm_by_command (const char *os_dev)
{
@@ -662,6 +671,7 @@ out:
free (buf);
free (vgid);
}
+#endif
/* ZFS has similar problems to those of btrfs (see above). */
void
diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
index fefbec499..542f4c18d 100644
--- a/include/grub/emu/misc.h
+++ b/include/grub/emu/misc.h
@@ -39,7 +39,7 @@ void grub_fini_all (void);
void grub_find_zpool_from_dir (const char *dir,
char **poolname, char **poolfs);
-char *grub_make_system_path_relative_to_its_root (const char *path)
+char * EXPORT_FUNC(grub_make_system_path_relative_to_its_root) (const char *path)
WARN_UNUSED_RESULT;
int
grub_util_device_is_mapped (const char *dev);
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v4 3/4] blsuki: Check for mounted /boot in emu
2025-05-21 12:51 ` [PATCH v4 3/4] blsuki: Check for mounted /boot in emu Alec Brown via Grub-devel
@ 2025-06-05 20:13 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 10+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-06-05 20:13 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
On Wed, May 21, 2025 at 12:51:25PM +0000, Alec Brown wrote:
> From: Robbie Harwood <rharwood@redhat.com>
>
> Irritatingly, BLS defines paths relative to the mountpoint of the
> filesystem which contains its snippets, not / or any other fixed
> location. So grub2-emu needs to know whether /boot is a separate
> filesystem from / and conditionally prepend a path.
>
> Signed-off-by: Robbie Harwood <rharwood@redhat.com>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> grub-core/Makefile.core.def | 4 ++
> grub-core/commands/blsuki.c | 92 ++++++++++++++++++++++++++++++---
> grub-core/osdep/linux/getroot.c | 8 +++
> grub-core/osdep/unix/getroot.c | 10 ++++
> include/grub/emu/misc.h | 2 +-
> 5 files changed, 107 insertions(+), 9 deletions(-)
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 67628f65f..93b88795e 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -367,6 +367,10 @@ kernel = {
> emu = kern/emu/cache_s.S;
> emu = kern/emu/hostdisk.c;
> emu = osdep/unix/hostdisk.c;
> + emu = osdep/relpath.c;
> + emu = osdep/getroot.c;
> + emu = osdep/unix/getroot.c;
> + emu = osdep/devmapper/getroot.c;
> emu = osdep/exec.c;
> extra_dist = osdep/unix/exec.c;
> emu = osdep/devmapper/hostdisk.c;
> diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
> index 2ad960ae3..bf2edc5ac 100644
> --- a/grub-core/commands/blsuki.c
> +++ b/grub-core/commands/blsuki.c
> @@ -32,6 +32,13 @@
> #include <grub/vercmp.h>
> #include <grub/lib/envblk.h>
>
> +#ifdef GRUB_MACHINE_EMU
> +#include <grub/emu/misc.h>
> +#define GRUB_BOOT_DEVICE "/boot"
> +#else
> +#define GRUB_BOOT_DEVICE ""
> +#endif
> +
> GRUB_MOD_LICENSE ("GPLv3+");
>
> #define GRUB_BLS_CONFIG_PATH "/loader/entries/"
> @@ -56,6 +63,40 @@ static grub_blsuki_entry_t *entries = NULL;
>
> #define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
>
> +#ifdef GRUB_MACHINE_EMU
> +/*
> + * Cache probing in blsuki_update_boot_device().
> + */
> +static int separate_boot = -1;
> +#endif
> +
> +/*
> + * BLS appears to make paths relative to the filesystem that snippets are
> + * on, not /. Attempt to cope.
> + */
> +static char *blsuki_update_boot_device (char *tmp)
> +{
> +#ifdef GRUB_MACHINE_EMU
> + char *ret;
> +
> + if (separate_boot != -1)
> + goto probed;
> +
> + separate_boot = 0;
> +
> + ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE);
> +
> + if (ret != NULL && ret[0] == '\0')
> + separate_boot = 1;
> +
> + probed:
> + if (!separate_boot)
> + return tmp;
> +#endif
> +
> + return grub_stpcpy (tmp, GRUB_BOOT_DEVICE);
> +}
> +
> /*
> * This function will add a new keyval pair to a list of keyvals stored in the
> * entry parameter.
> @@ -582,7 +623,7 @@ bls_get_linux (grub_blsuki_entry_t *entry)
> if (options == NULL)
> options = blsuki_expand_val (grub_env_get ("default_kernelopts"));
>
> - if (grub_add (grub_strlen ("linux "), grub_strlen (linux_path), &size))
> + if (grub_add (grub_strlen ("linux " GRUB_BOOT_DEVICE), grub_strlen (linux_path), &size))
> {
> grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
> goto finish;
> @@ -604,6 +645,7 @@ bls_get_linux (grub_blsuki_entry_t *entry)
>
> tmp = linux_cmd;
> tmp = grub_stpcpy (tmp, "linux ");
> + tmp = blsuki_update_boot_device (tmp);
> tmp = grub_stpcpy (tmp, linux_path);
> if (options != NULL)
> {
> @@ -679,7 +721,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
>
> for (i = 0; early_initrd_list != NULL && early_initrd_list[i] != NULL; i++)
> {
> - if (grub_add (size, 1, &size) ||
> + if (grub_add (size, grub_strlen (" " GRUB_BOOT_DEVICE), &size) ||
s/grub_strlen/sizeof (...) - 1/
> grub_add (size, grub_strlen (prefix), &size) ||
> grub_add (size, grub_strlen (early_initrd_list[i]), &size))
> {
> @@ -690,7 +732,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
>
> for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
> {
> - if (grub_add (size, 1, &size) ||
> + if (grub_add (size, grub_strlen (" " GRUB_BOOT_DEVICE), &size) ||
Ditto.
> grub_add (size, grub_strlen (initrd_list[i]), &size))
> {
> grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
> @@ -713,6 +755,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
> {
> grub_dprintf ("blsuki", "adding early initrd %s\n", early_initrd_list[i]);
> tmp = grub_stpcpy (tmp, " ");
> + tmp = blsuki_update_boot_device (tmp);
> tmp = grub_stpcpy (tmp, prefix);
> tmp = grub_stpcpy (tmp, early_initrd_list[i]);
> }
> @@ -721,6 +764,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
> {
> grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
> tmp = grub_stpcpy (tmp, " ");
> + tmp = blsuki_update_boot_device (tmp);
> tmp = grub_stpcpy (tmp, initrd_list[i]);
> }
> tmp = grub_stpcpy (tmp, "\n");
> @@ -775,7 +819,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry)
> }
> }
>
> - if (grub_add (grub_strlen ("devicetree "), grub_strlen (dt_path), &size) ||
> + if (grub_add (grub_strlen ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (dt_path), &size) ||
Ditto.
> grub_add (size, 1, &size))
> {
> grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
> @@ -796,6 +840,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry)
> tmp = dt_cmd;
> tmp = grub_stpcpy (dt_cmd, "devicetree");
> tmp = grub_stpcpy (tmp, " ");
> + tmp = blsuki_update_boot_device (tmp);
> if (add_dt_prefix == true)
> tmp = grub_stpcpy (tmp, prefix);
> tmp = grub_stpcpy (tmp, dt_path);
> @@ -924,7 +969,11 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
>
> if (devid == NULL)
> {
> +#ifdef GRUB_MACHINE_EMU
> + devid = "host";
> +#else
> devid = grub_env_get ("root");
> +#endif
> if (devid == NULL)
> return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "root");
> }
> @@ -966,10 +1015,13 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
> * parameter. If the fallback option is enabled, the default location will be
> * checked for BLS config files if the first attempt fails.
> */
> -static void
> +static grub_err_t
> blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> {
> struct read_entry_info read_entry_info;
> + char *default_dir = NULL;
> + char *tmp;
> + grub_size_t default_size;
> grub_fs_t dir_fs = NULL;
> grub_device_t dir_dev = NULL;
> bool fallback = false;
> @@ -1000,7 +1052,15 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> */
> if (entries == NULL && fallback == false && enable_fallback == true)
> {
> - blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
> + default_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH);
s/grub_strlen/sizeof/ * 2
Do not forget about NUL at the end...
> + default_dir = grub_malloc (default_size);
> + if (default_dir == NULL)
> + return grub_errno;
> +
> + tmp = blsuki_update_boot_device (default_dir);
> + tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
> +
> + blsuki_set_find_entry_info (info, default_dir, NULL);
> grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
> read_entry_info.dirname, info->dirname);
> fallback = true;
> @@ -1009,6 +1069,9 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> fallback = false;
> }
> while (fallback == true);
> +
> + grub_free (default_dir);
> + return GRUB_ERR_NONE;
> }
>
> static grub_err_t
> @@ -1018,6 +1081,9 @@ blsuki_load_entries (char *path, bool enable_fallback)
> static grub_err_t r;
> const char *devid = NULL;
> char *dir = NULL;
> + char *default_dir = NULL;
> + char *tmp;
> + grub_size_t dir_size;
> struct find_entry_info info = {
> .dev = NULL,
> .fs = NULL,
> @@ -1059,15 +1125,25 @@ blsuki_load_entries (char *path, bool enable_fallback)
> }
>
> if (dir == NULL)
> - dir = (char *) GRUB_BLS_CONFIG_PATH;
> + {
> + dir_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH);
Ditto...
> + default_dir = grub_malloc (dir_size);
> + if (default_dir == NULL)
> + return grub_errno;
> +
> + tmp = blsuki_update_boot_device (default_dir);
> + tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
> + dir = default_dir;
> + }
>
> r = blsuki_set_find_entry_info (&info, dir, devid);
> if (r == GRUB_ERR_NONE)
> - blsuki_find_entry (&info, enable_fallback);
> + r = blsuki_find_entry (&info, enable_fallback);
>
> if (info.dev != NULL)
> grub_device_close (info.dev);
>
> + grub_free (default_dir);
> return r;
> }
>
> diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
> index 527d4f0c5..2fc212276 100644
> --- a/grub-core/osdep/linux/getroot.c
> +++ b/grub-core/osdep/linux/getroot.c
> @@ -131,6 +131,7 @@ struct mountinfo_entry
> char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
> };
>
> +#ifdef GRUB_UTIL
> static char **
> grub_util_raid_getmembers (const char *name, int bootable)
> {
> @@ -191,6 +192,7 @@ grub_util_raid_getmembers (const char *name, int bootable)
>
> return devicelist;
> }
> +#endif
>
> /* Statting something on a btrfs filesystem always returns a virtual device
> major/minor pair rather than the real underlying device, because btrfs
> @@ -579,6 +581,7 @@ out:
> return ret;
> }
>
> +#ifdef GRUB_UTIL
> static char *
> get_mdadm_uuid (const char *os_dev)
> {
> @@ -636,6 +639,7 @@ out:
>
> return name;
> }
> +#endif
>
> static int
> grub_util_is_imsm (const char *os_dev)
> @@ -968,6 +972,7 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st,
> return path;
> }
>
> +#ifdef GRUB_UTIL
> static char *
> grub_util_get_raid_grub_dev (const char *os_dev)
> {
> @@ -1070,6 +1075,7 @@ grub_util_get_raid_grub_dev (const char *os_dev)
> }
> return grub_dev;
> }
> +#endif
>
> enum grub_dev_abstraction_types
> grub_util_get_dev_abstraction_os (const char *os_dev)
> @@ -1086,6 +1092,7 @@ grub_util_get_dev_abstraction_os (const char *os_dev)
> return GRUB_DEV_ABSTRACTION_NONE;
> }
>
> +#ifdef GRUB_UTIL
> int
> grub_util_pull_device_os (const char *os_dev,
> enum grub_dev_abstraction_types ab)
> @@ -1142,6 +1149,7 @@ grub_util_get_grub_dev_os (const char *os_dev)
>
> return grub_dev;
> }
> +#endif
>
> char *
> grub_make_system_path_relative_to_its_root_os (const char *path)
> diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c
> index c7aa202ab..1380638d3 100644
> --- a/grub-core/osdep/unix/getroot.c
> +++ b/grub-core/osdep/unix/getroot.c
The changes to this file look like unrelated ones. Are they really needed?
> @@ -17,6 +17,14 @@
> */
>
> #include <config.h>
> +#undef _GL_INLINE_HEADER_BEGIN
> +#undef _GL_INLINE_HEADER_END
> +#undef _GL_GNUC_PREREQ
> +#undef _GL_ATTRIBUTE_COLD
> +#undef _GL_ATTRIBUTE_CONST
> +#undef _GL_ATTRIBUTE_DEALLOC
> +#undef _GL_ATTRIBUTE_FALLTHROUGH
> +#undef _GL_ATTRIBUTE_MALLOC
> #include <config-util.h>
>
> #include <sys/stat.h>
> @@ -566,6 +574,7 @@ grub_guess_root_devices (const char *dir_in)
>
> #endif
>
> +#ifdef GRUB_UTIL
> void
> grub_util_pull_lvm_by_command (const char *os_dev)
> {
> @@ -662,6 +671,7 @@ out:
> free (buf);
> free (vgid);
> }
> +#endif
>
> /* ZFS has similar problems to those of btrfs (see above). */
> void
> diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
> index fefbec499..542f4c18d 100644
> --- a/include/grub/emu/misc.h
> +++ b/include/grub/emu/misc.h
> @@ -39,7 +39,7 @@ void grub_fini_all (void);
> void grub_find_zpool_from_dir (const char *dir,
> char **poolname, char **poolfs);
>
> -char *grub_make_system_path_relative_to_its_root (const char *path)
> +char * EXPORT_FUNC(grub_make_system_path_relative_to_its_root) (const char *path)
s/* EXPORT_FUNC(/*EXPORT_FUNC (/
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v4 4/4] blsuki: Add uki command to load Unified Kernel Image entries
2025-05-21 12:51 [PATCH v4 0/4] Add commands to load BLS and UKI files Alec Brown via Grub-devel
` (2 preceding siblings ...)
2025-05-21 12:51 ` [PATCH v4 3/4] blsuki: Check for mounted /boot in emu Alec Brown via Grub-devel
@ 2025-05-21 12:51 ` Alec Brown via Grub-devel
2025-06-06 13:28 ` Daniel Kiper via Grub-devel
3 siblings, 1 reply; 10+ messages in thread
From: Alec Brown via Grub-devel @ 2025-05-21 12:51 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
A Unified Kernel Image is a single UEFI PE file that combines a UEFI boot stub,
a Linux kernel image, an initrd, and further resources. The uki command will
locate where the UKI file is and create a GRUB menu entry to load it.
The Unified Kernel Image Specification:
https://uapi-group.org/specifications/specs/unified_kernel_image/
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
---
docs/grub.texi | 33 +++
grub-core/commands/blsuki.c | 463 +++++++++++++++++++++++++++++++++---
| 2 +
3 files changed, 463 insertions(+), 35 deletions(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index adab93668..9a63129c7 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6491,6 +6491,7 @@ you forget a command, you can run the command @command{help}
* tpm2_key_protector_clear:: Clear the TPM2 key protector
* true:: Do nothing, successfully
* trust:: Add public key to list of trusted keys
+* uki:: Load Unified Kernel Image menu entries
* unset:: Unset an environment variable
@comment * vbeinfo:: List available video modes
* verify_detached:: Verify detached digital signature
@@ -8184,6 +8185,38 @@ Unset the environment variable @var{envvar}.
@end deffn
+@node uki
+@subsection uki
+
+@deffn Command uki [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
+Load Unified Kernel Image (UKI) entries into the GRUB menu. Boot entries
+generated from @command{uki} won't interfere with entries from @file{grub.cfg} appearing in the
+GRUB menu. Also, entries generated from @command{uki} only generate in memory and don't
+update @file{grub.cfg}.
+
+The @option{--path} option overrides the default path to the directory containing
+the UKI entries. If this option isn't used, the default location is
+/EFI/Linux in the EFI system partition. If no UKI entries are found, the
+@option{--enable-fallback} option can be used to check for entries in the default
+directory.
+
+The @option{--show-default} option allows the default boot entry to be added to the
+GRUB menu from the UKI entries.
+
+The @option{--show-non-default} option allows non-default boot entries to be added to
+the GRUB menu from the UKI entries.
+
+The @option{--entry} option allows specific boot entries to be added to the GRUB menu
+from the UKI entries.
+
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
+are used to filter which UKI entries are added to the GRUB menu. If none are
+used, all entries in the default location or the location specified by @option{--path}
+will be added to the GRUB menu.
+
+References: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification}
+@end deffn
+
@ignore
@node vbeinfo
@subsection vbeinfo
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
index bf2edc5ac..3f067281d 100644
--- a/grub-core/commands/blsuki.c
+++ b/grub-core/commands/blsuki.c
@@ -32,6 +32,12 @@
#include <grub/vercmp.h>
#include <grub/lib/envblk.h>
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#include <grub/efi/disk.h>
+#include <grub/efi/pe32.h>
+#endif
+
#ifdef GRUB_MACHINE_EMU
#include <grub/emu/misc.h>
#define GRUB_BOOT_DEVICE "/boot"
@@ -42,6 +48,13 @@
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+#define GRUB_UKI_CONFIG_PATH "/EFI/Linux"
+
+enum
+ {
+ BLSUKI_BLS_CMD,
+ BLSUKI_UKI_CMD,
+ };
static const struct grub_arg_option bls_opt[] =
{
@@ -53,6 +66,18 @@ static const struct grub_arg_option bls_opt[] =
{0, 0, 0, 0, 0, 0}
};
+#ifdef GRUB_MACHINE_EFI
+static const struct grub_arg_option uki_opt[] =
+ {
+ {"path", 'p', 0, N_("Specify path to find UKI entries."), N_("DIR"), ARG_TYPE_PATHNAME},
+ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE},
+ {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the GRUB menu."), 0, ARG_TYPE_NONE},
+ {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added to the GRUB menu."), 0, ARG_TYPE_NONE},
+ {"entry", 'e', 0, N_("Allow specificUKII entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE},
+ {0, 0, 0, 0, 0, 0}
+ };
+#endif
+
struct keyval
{
const char *key;
@@ -162,7 +187,7 @@ blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
* Find the value of the key named by keyname. If there are allowed to be
* more than one, pass a pointer to an int set to -1 the first time, and pass
* the same pointer through each time after, and it'll return them in sorted
- * order as defined in the BLS fragment file.
+ * order.
*/
static char *
blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
@@ -304,27 +329,243 @@ bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
return err;
}
+/*
+ * This function searches for the .cmdline, .osrel, and .linux sections of a
+ * UKI. We only need to store the data for the .cmdline and .osrel sections,
+ * but we also need to verify that the .linux section exists.
+ */
+#ifdef GRUB_MACHINE_EFI
+static grub_err_t
+uki_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
+{
+ struct grub_msdos_image_header *dos = NULL;
+ struct grub_pe_image_header *pe = NULL;
+ grub_off_t section_offset = 0;
+ struct grub_pe32_section_table *section = NULL;
+ struct grub_pe32_coff_header *coff_header = NULL;
+ char *val = NULL;
+ char *key = NULL;
+ const char *target[] = {".cmdline", ".osrel", ".linux", NULL};
+ bool has_linux = false;
+ grub_err_t err = GRUB_ERR_NONE;
+
+ dos = grub_zalloc (sizeof (*dos));
+ if (dos == NULL)
+ return grub_errno;
+ if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos))
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read UKI image header");
+ goto finish;
+ }
+ if (dos->msdos_magic != GRUB_PE32_MAGIC)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("plain image kernel not supported"));
+ goto finish;
+ }
+
+ grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", dos->pe_image_header_offset);
+ pe = grub_zalloc (sizeof (*pe));
+ if (pe == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+ if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1 ||
+ grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe))
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header");
+ goto finish;
+ }
+ if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "non-native image not supported");
+ goto finish;
+ }
+
+ coff_header = &(pe->coff_header);
+ section_offset = dos->pe_image_header_offset + sizeof (*pe);
+
+ for (int i = 0; i < coff_header->num_sections; i++)
+ {
+ key = NULL;
+ val = NULL;
+ section = grub_zalloc (sizeof (*section));
+ if (section == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+
+ if (grub_file_seek (f, section_offset) == (grub_off_t) -1 ||
+ grub_file_read (f, section, sizeof (*section)) != sizeof (*section))
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section header");
+ goto finish;
+ }
+
+ key = grub_strndup (section->name, 8);
+ if (key == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+
+ for (int j = 0; target[j] != NULL; j++)
+ {
+ if (grub_strcmp (key, target[j]) == 0)
+ {
+ /*
+ * We don't need to read the contents of the .linux PE section, but we
+ * should verify that the section exists.
+ */
+ if (grub_strcmp (key, ".linux") == 0)
+ {
+ has_linux = true;
+ break;
+ }
+
+ val = grub_zalloc (section->raw_data_size);
+ if (val == NULL)
+ {
+ err = grub_errno;
+ goto finish;
+ }
+
+ if (grub_file_seek (f, section->raw_data_offset) == (grub_off_t) -1 ||
+ grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) section->raw_data_size)
+ {
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section");
+ goto finish;
+ }
+
+ err = blsuki_add_keyval (entry, key, val);
+ if (err != GRUB_ERR_NONE)
+ goto finish;
+
+ break;
+ }
+ }
+
+ section_offset += sizeof (*section);
+ grub_free (section);
+ grub_free (val);
+ grub_free (key);
+ section = NULL;
+ val = NULL;
+ key = NULL;
+ }
+
+ if (has_linux == false)
+ err = grub_error (GRUB_ERR_NO_KERNEL, "UKI is missing the '.linux' section");
+
+ finish:
+ grub_free (dos);
+ grub_free (pe);
+ grub_free (section);
+ grub_free (val);
+ grub_free (key);
+ return err;
+}
+#endif
+
+/*
+ * This function obtains the keyval pairs when the .osrel data is input into
+ * the content parameter and returns the full line that it obtained the keyval
+ * pair from.
+ */
+static char *
+uki_read_osrel (char *content, grub_off_t *pos, char **key_ret, char **val_ret)
+{
+ char *line;
+ char *value;
+ grub_size_t linelen;
+
+ for (;;)
+ {
+ line = content + *pos;
+ if (*line == '\0')
+ return NULL;
+
+ linelen = 0;
+ while (line[linelen] != '\0' && line[linelen] != '\n' && line[linelen] != '\r')
+ linelen++;
+
+ /* Move pos to the next line */
+ *pos += linelen;
+ if (content[*pos] != '\0')
+ (*pos)++;
+
+ /* Skip empty line */
+ if (linelen == 0)
+ continue;
+
+ line[linelen] = '\0';
+
+ /* Remove leading white space */
+ while (linelen > 0 && (*line == ' ' || *line == '\t'))
+ {
+ line++;
+ linelen--;
+ }
+
+ /* Remove trailing whitespace */
+ while (linelen > 0 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t'))
+ linelen--;
+ line[linelen] = '\0';
+
+ if (*line == '#')
+ continue;
+
+ /* Split key/value */
+ value = line;
+ while (*value != '\0' && *value != '=')
+ value++;
+ if (*value == '\0')
+ continue;
+ *value = '\0';
+ value++;
+ while (*value != '\0' && *value == '=')
+ value++;
+
+ /* Remove quotes from value */
+ if (*value == '\"' && line[linelen - 1] == '\"')
+ {
+ value++;
+ line[linelen - 1] = '\0';
+ }
+
+ *key_ret = line;
+ *val_ret = value;
+ break;
+ }
+
+ return line;
+}
+
struct read_entry_info
{
const char *devid;
const char *dirname;
+ int cmd_type;
grub_file_t file;
};
/*
* If a file hasn't already been opened, this function opens a BLS config file
- * and initializes entry data before parsing keyvals and adding the entry to
- * the list of BLS entries.
+ * or UKI and initializes entry data before parsing keyvals and adding the entry
+ * to the list of BLS or UKI entries.
*/
static int
blsuki_read_entry (const char *filename,
const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)),
void *data)
{
- grub_size_t prefix_len = 0, filename_len, ext_len = 5;
- grub_err_t err;
+ grub_size_t prefix_len = 0, filename_len, ext_len;
+ grub_err_t err = GRUB_ERR_NONE;
char *p = NULL;
+ const char *ext = NULL;
grub_file_t f = NULL;
+ enum grub_file_type file_type = 0;
grub_blsuki_entry_t *entry;
struct read_entry_info *info = (struct read_entry_info *) data;
@@ -332,16 +573,27 @@ blsuki_read_entry (const char *filename,
filename_len = grub_strlen (filename);
+ if (info->cmd_type == BLSUKI_BLS_CMD)
+ {
+ ext = ".conf";
+ file_type = GRUB_FILE_TYPE_CONFIG;
+ }
+ else if (info->cmd_type == BLSUKI_UKI_CMD)
+ {
+ ext = ".efi";
+ file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE;
+ }
+ ext_len = grub_strlen (ext);
+
if (info->file != NULL)
f = info->file;
else
{
- if (filename_len < ext_len || grub_strcmp (filename + filename_len - ext_len, ".conf") != 0)
+ if (filename_len < ext_len || grub_strcmp (filename + filename_len - ext_len, ext) != 0)
return 0;
p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
-
- f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
+ f = grub_file_open (p, file_type);
grub_free (p);
if (f == NULL)
goto finish;
@@ -381,7 +633,26 @@ blsuki_read_entry (const char *filename,
goto finish;
}
- err = bls_parse_keyvals (f, entry);
+ entry->dirname = grub_strdup (info->dirname);
+ if (entry->dirname == NULL)
+ {
+ grub_free (entry);
+ goto finish;
+ }
+
+ entry->devid = grub_strdup (info->devid);
+ if (entry->devid == NULL)
+ {
+ grub_free (entry);
+ goto finish;
+ }
+
+ if (info->cmd_type == BLSUKI_BLS_CMD)
+ err = bls_parse_keyvals (f, entry);
+#ifdef GRUB_MACHINE_EFI
+ else if (info->cmd_type == BLSUKI_UKI_CMD)
+ err = uki_parse_keyvals (f, entry);
+#endif
if (err == GRUB_ERR_NONE)
blsuki_add_entry (entry);
@@ -397,7 +668,7 @@ blsuki_read_entry (const char *filename,
/*
* This function returns a list of values that had the same key in the BLS
- * config file. The number of entries in this list is returned by the len
+ * config file or UKI. The number of entries in this list is returned by the len
* parameter.
*/
static char **
@@ -942,6 +1213,65 @@ bls_create_entry (grub_blsuki_entry_t *entry)
grub_free (src);
}
+/*
+ * This function puts together the section data recieved from the UKI and
+ * generates a new entry un the GRUB boot menu.
+ */
+static void
+uki_create_entry (grub_blsuki_entry_t *entry)
+{
+ const char **argv = NULL;
+ char *id = entry->filename;
+ char *title = NULL;
+ char *options = NULL;
+ char *osrel = NULL;
+ char *line;
+ char *key = NULL;
+ char *value = NULL;
+ char *src = NULL;
+ grub_off_t pos = 0;
+
+ /*
+ * Although .osrel is listed as optional in the UKI specification, the .osrel
+ * section is needed to generate the GRUB menu entry title.
+ */
+ osrel = blsuki_get_val (entry, ".osrel", NULL);
+ if (osrel == NULL)
+ {
+ grub_dprintf ("blsuki", "Skipping file %s with no '.osrel' key.\n", entry->filename);
+ goto finish;
+ }
+
+ line = osrel;
+ while ((line = uki_read_osrel (osrel, &pos, &key, &value)))
+ {
+ if (grub_strcmp ("PRETTY_NAME", key) == 0)
+ {
+ title = value;
+ break;
+ }
+ }
+
+ options = blsuki_get_val (entry, ".cmdline", NULL);
+
+ argv = grub_zalloc (2 * sizeof (char *));
+ if (argv == NULL)
+ goto finish;
+ argv[0] = title;
+
+ src = grub_xasprintf ("chainloader (%s)%s/%s%s%s\n",
+ entry->devid, entry->dirname,
+ entry->filename, options ? " " : "", options ? options : "");
+
+ grub_normal_add_menu_entry (1, argv, NULL, id, NULL, NULL, NULL, src, 0, entry);
+
+ finish:
+ grub_free (argv);
+ grub_free (src);
+ grub_free (options);
+ grub_free (osrel);
+}
+
struct find_entry_info
{
const char *dirname;
@@ -959,7 +1289,7 @@ struct find_entry_info
* device.
*/
static grub_err_t
-blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid)
+blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid, int cmd_type)
{
grub_device_t dev;
grub_fs_t fs;
@@ -969,10 +1299,21 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
if (devid == NULL)
{
+ if (cmd_type == BLSUKI_BLS_CMD)
+ {
#ifdef GRUB_MACHINE_EMU
- devid = "host";
+ devid = "host";
#else
- devid = grub_env_get ("root");
+ devid = grub_env_get ("root");
+#endif
+ }
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ {
+ grub_efi_loaded_image_t *image;
+ image = grub_efi_get_loaded_image (grub_efi_image_handle);
+ devid = grub_efidisk_get_device_name (image->device_handle);
+ }
#endif
if (devid == NULL)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "root");
@@ -1011,15 +1352,16 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
}
/*
- * This function searches for BLS config files based on the data in the info
- * parameter. If the fallback option is enabled, the default location will be
- * checked for BLS config files if the first attempt fails.
+ * This function searches for BLS config files and UKIs based on the data in the
+ * info parameter. If the fallback option is enabled, the default location will
+ * be checked for BLS config files or UKIs if the first attempt fails.
*/
static grub_err_t
-blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
+blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, int cmd_type)
{
struct read_entry_info read_entry_info;
char *default_dir = NULL;
+ const char *cmd_dir = NULL;
char *tmp;
grub_size_t default_size;
grub_fs_t dir_fs = NULL;
@@ -1036,6 +1378,7 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
dir_dev = info->dev;
dir_fs = info->fs;
read_entry_info.devid = info->devid;
+ read_entry_info.cmd_type = cmd_type;
r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry,
&read_entry_info);
@@ -1048,19 +1391,25 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
/*
* If we aren't able to find BLS entries in the directory given by info->dirname,
* we can fallback to the default location "/boot/loader/entries/" and see if we
- * can find the files there.
+ * can find the files there. If we can't find UKI entries, fallback to
+ * "/EFI/Linux" on the EFI system partition.
*/
if (entries == NULL && fallback == false && enable_fallback == true)
{
- default_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH);
+ if (cmd_type == BLSUKI_BLS_CMD)
+ cmd_dir = GRUB_BLS_CONFIG_PATH;
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ cmd_dir = GRUB_UKI_CONFIG_PATH;
+
+ default_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir);
default_dir = grub_malloc (default_size);
if (default_dir == NULL)
return grub_errno;
tmp = blsuki_update_boot_device (default_dir);
- tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
+ tmp = grub_stpcpy (tmp, cmd_dir);
- blsuki_set_find_entry_info (info, default_dir, NULL);
+ blsuki_set_find_entry_info (info, default_dir, NULL, cmd_type);
grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
read_entry_info.dirname, info->dirname);
fallback = true;
@@ -1075,15 +1424,18 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
}
static grub_err_t
-blsuki_load_entries (char *path, bool enable_fallback)
+blsuki_load_entries (char *path, bool enable_fallback, int cmd_type)
{
grub_size_t len;
+ grub_size_t ext_len;
static grub_err_t r;
const char *devid = NULL;
char *dir = NULL;
char *default_dir = NULL;
char *tmp;
+ const char *cmd_dir = NULL;
grub_size_t dir_size;
+ const char *ext = NULL;
struct find_entry_info info = {
.dev = NULL,
.fs = NULL,
@@ -1092,12 +1444,19 @@ blsuki_load_entries (char *path, bool enable_fallback)
struct read_entry_info rei = {
.devid = NULL,
.dirname = NULL,
+ .cmd_type = cmd_type,
};
if (path != NULL)
{
+ if (cmd_type == BLSUKI_BLS_CMD)
+ ext = ".conf";
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ ext = ".efi";
+
len = grub_strlen (path);
- if (len >= 5 && grub_strcmp (path + len - 5, ".conf") == 0)
+ ext_len = grub_strlen (ext);
+ if (len >= ext_len && grub_strcmp (path + len - ext_len, ext) == 0)
{
rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
if (rei.file == NULL)
@@ -1126,19 +1485,24 @@ blsuki_load_entries (char *path, bool enable_fallback)
if (dir == NULL)
{
- dir_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH);
+ if (cmd_type == BLSUKI_BLS_CMD)
+ cmd_dir = GRUB_BLS_CONFIG_PATH;
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ cmd_dir = GRUB_UKI_CONFIG_PATH;
+
+ dir_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir);
default_dir = grub_malloc (dir_size);
if (default_dir == NULL)
return grub_errno;
tmp = blsuki_update_boot_device (default_dir);
- tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
+ tmp = grub_stpcpy (tmp, cmd_dir);
dir = default_dir;
}
- r = blsuki_set_find_entry_info (&info, dir, devid);
+ r = blsuki_set_find_entry_info (&info, dir, devid, cmd_type);
if (r == GRUB_ERR_NONE)
- r = blsuki_find_entry (&info, enable_fallback);
+ r = blsuki_find_entry (&info, enable_fallback, cmd_type);
if (info.dev != NULL)
grub_device_close (info.dev);
@@ -1181,11 +1545,11 @@ blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int
}
/*
- * This function creates a GRUB boot menu entry for each BLS entry in the
- * entries list.
+ * This function creates a GRUB boot menu entry for each BLS or UKI entry in
+ * the entries list.
*/
static grub_err_t
-blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id)
+blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id, int cmd_type)
{
const char *def_entry = NULL;
grub_blsuki_entry_t *entry = NULL;
@@ -1204,7 +1568,10 @@ blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id)
(show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) ||
(entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0))
{
- bls_create_entry (entry);
+ if (cmd_type == BLSUKI_BLS_CMD)
+ bls_create_entry (entry);
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ uki_create_entry (entry);
entry->visible = 1;
}
idx++;
@@ -1214,8 +1581,7 @@ blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id)
}
static grub_err_t
-grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
- char **args __attribute__ ((unused)))
+blsuki_cmd (grub_extcmd_context_t ctxt, int cmd_type)
{
grub_err_t err;
struct grub_arg_list *state = ctxt->state;
@@ -1225,6 +1591,7 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
bool show_default = false;
bool show_non_default = false;
bool all = true;
+ entries = NULL;
if (state[0].set)
path = state[0].arg;
@@ -1251,24 +1618,50 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
show_non_default = true;
}
- err = blsuki_load_entries (path, enable_fallback);
+ err = blsuki_load_entries (path, enable_fallback, cmd_type);
if (err != GRUB_ERR_NONE)
return err;
- return blsuki_create_entries (show_default, show_non_default, entry_id);
+ return blsuki_create_entries (show_default, show_non_default, entry_id, cmd_type);
+}
+
+static grub_err_t
+grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ return blsuki_cmd (ctxt, BLSUKI_BLS_CMD);
}
static grub_extcmd_t bls_cmd;
+#ifdef GRUB_MACHINE_EFI
+static grub_err_t
+grub_cmd_uki (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ return blsuki_cmd (ctxt, BLSUKI_UKI_CMD);
+}
+
+static grub_extcmd_t uki_cmd;
+#endif
+
GRUB_MOD_INIT(blsuki)
{
bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0,
N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
N_("Import Boot Loader Specification snippets."),
bls_opt);
+#ifdef GRUB_MACHINE_EFI
+ uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0,
+ N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
+ N_("Import Unified Kernel Images"), uki_opt);
+#endif
}
GRUB_MOD_FINI(blsuki)
{
grub_unregister_extcmd (bls_cmd);
+#ifdef GRUB_MACHINE_EFI
+ grub_unregister_extcmd (uki_cmd);
+#endif
}
--git a/include/grub/menu.h b/include/grub/menu.h
index c25a0d16d..907373625 100644
--- a/include/grub/menu.h
+++ b/include/grub/menu.h
@@ -28,6 +28,8 @@ struct grub_blsuki_entry
grub_size_t keyvals_size;
int nkeyvals;
char *filename;
+ char *dirname;
+ char *devid;
int visible;
};
typedef struct grub_blsuki_entry grub_blsuki_entry_t;
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v4 4/4] blsuki: Add uki command to load Unified Kernel Image entries
2025-05-21 12:51 ` [PATCH v4 4/4] blsuki: Add uki command to load Unified Kernel Image entries Alec Brown via Grub-devel
@ 2025-06-06 13:28 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 10+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-06-06 13:28 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, ross.philipson, 93sam, phcoder
On Wed, May 21, 2025 at 12:51:26PM +0000, Alec Brown wrote:
> A Unified Kernel Image is a single UEFI PE file that combines a UEFI boot stub,
> a Linux kernel image, an initrd, and further resources. The uki command will
> locate where the UKI file is and create a GRUB menu entry to load it.
>
> The Unified Kernel Image Specification:
> https://uapi-group.org/specifications/specs/unified_kernel_image/
>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> docs/grub.texi | 33 +++
> grub-core/commands/blsuki.c | 463 +++++++++++++++++++++++++++++++++---
> include/grub/menu.h | 2 +
> 3 files changed, 463 insertions(+), 35 deletions(-)
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index adab93668..9a63129c7 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -6491,6 +6491,7 @@ you forget a command, you can run the command @command{help}
> * tpm2_key_protector_clear:: Clear the TPM2 key protector
> * true:: Do nothing, successfully
> * trust:: Add public key to list of trusted keys
> +* uki:: Load Unified Kernel Image menu entries
> * unset:: Unset an environment variable
> @comment * vbeinfo:: List available video modes
> * verify_detached:: Verify detached digital signature
> @@ -8184,6 +8185,38 @@ Unset the environment variable @var{envvar}.
> @end deffn
>
>
> +@node uki
> +@subsection uki
> +
> +@deffn Command uki [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
> +Load Unified Kernel Image (UKI) entries into the GRUB menu. Boot entries
> +generated from @command{uki} won't interfere with entries from @file{grub.cfg} appearing in the
> +GRUB menu. Also, entries generated from @command{uki} only generate in memory and don't
s/only generate/exist only/
> +update @file{grub.cfg}.
> +
> +The @option{--path} option overrides the default path to the directory containing
> +the UKI entries. If this option isn't used, the default location is
> +/EFI/Linux in the EFI system partition. If no UKI entries are found, the
> +@option{--enable-fallback} option can be used to check for entries in the default
> +directory.
What is "the default directory"?
> +The @option{--show-default} option allows the default boot entry to be added to the
> +GRUB menu from the UKI entries.
> +
> +The @option{--show-non-default} option allows non-default boot entries to be added to
> +the GRUB menu from the UKI entries.
> +
> +The @option{--entry} option allows specific boot entries to be added to the GRUB menu
> +from the UKI entries.
> +
> +The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
> +are used to filter which UKI entries are added to the GRUB menu. If none are
> +used, all entries in the default location or the location specified by @option{--path}
> +will be added to the GRUB menu.
> +
> +References: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification}
> +@end deffn
> +
> @ignore
> @node vbeinfo
> @subsection vbeinfo
> diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
> index bf2edc5ac..3f067281d 100644
> --- a/grub-core/commands/blsuki.c
> +++ b/grub-core/commands/blsuki.c
> @@ -32,6 +32,12 @@
> #include <grub/vercmp.h>
> #include <grub/lib/envblk.h>
>
> +#ifdef GRUB_MACHINE_EFI
> +#include <grub/efi/efi.h>
> +#include <grub/efi/disk.h>
> +#include <grub/efi/pe32.h>
> +#endif
> +
> #ifdef GRUB_MACHINE_EMU
> #include <grub/emu/misc.h>
> #define GRUB_BOOT_DEVICE "/boot"
> @@ -42,6 +48,13 @@
> GRUB_MOD_LICENSE ("GPLv3+");
>
> #define GRUB_BLS_CONFIG_PATH "/loader/entries/"
> +#define GRUB_UKI_CONFIG_PATH "/EFI/Linux"
> +
> +enum
> + {
> + BLSUKI_BLS_CMD,
> + BLSUKI_UKI_CMD,
> + };
>
> static const struct grub_arg_option bls_opt[] =
> {
> @@ -53,6 +66,18 @@ static const struct grub_arg_option bls_opt[] =
> {0, 0, 0, 0, 0, 0}
> };
>
> +#ifdef GRUB_MACHINE_EFI
> +static const struct grub_arg_option uki_opt[] =
> + {
> + {"path", 'p', 0, N_("Specify path to find UKI entries."), N_("DIR"), ARG_TYPE_PATHNAME},
> + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE},
> + {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the GRUB menu."), 0, ARG_TYPE_NONE},
> + {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added to the GRUB menu."), 0, ARG_TYPE_NONE},
> + {"entry", 'e', 0, N_("Allow specificUKII entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE},
> + {0, 0, 0, 0, 0, 0}
> + };
> +#endif
> +
> struct keyval
> {
> const char *key;
> @@ -162,7 +187,7 @@ blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
> * Find the value of the key named by keyname. If there are allowed to be
> * more than one, pass a pointer to an int set to -1 the first time, and pass
> * the same pointer through each time after, and it'll return them in sorted
> - * order as defined in the BLS fragment file.
> + * order.
> */
> static char *
> blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
> @@ -304,27 +329,243 @@ bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
> return err;
> }
>
> +/*
> + * This function searches for the .cmdline, .osrel, and .linux sections of a
> + * UKI. We only need to store the data for the .cmdline and .osrel sections,
> + * but we also need to verify that the .linux section exists.
> + */
> +#ifdef GRUB_MACHINE_EFI
> +static grub_err_t
> +uki_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
> +{
> + struct grub_msdos_image_header *dos = NULL;
> + struct grub_pe_image_header *pe = NULL;
> + grub_off_t section_offset = 0;
> + struct grub_pe32_section_table *section = NULL;
> + struct grub_pe32_coff_header *coff_header = NULL;
> + char *val = NULL;
> + char *key = NULL;
> + const char *target[] = {".cmdline", ".osrel", ".linux", NULL};
> + bool has_linux = false;
> + grub_err_t err = GRUB_ERR_NONE;
> +
> + dos = grub_zalloc (sizeof (*dos));
> + if (dos == NULL)
> + return grub_errno;
> + if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos))
> + {
> + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read UKI image header");
> + goto finish;
> + }
> + if (dos->msdos_magic != GRUB_PE32_MAGIC)
> + {
> + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("plain image kernel not supported"));
I would drop N_() from here. And s/kernel not/kernel is not/...
> + goto finish;
> + }
> +
> + grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", dos->pe_image_header_offset);
> + pe = grub_zalloc (sizeof (*pe));
> + if (pe == NULL)
> + {
> + err = grub_errno;
> + goto finish;
> + }
> + if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1 ||
> + grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe))
> + {
> + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header");
> + goto finish;
> + }
> + if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC)
> + {
> + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "non-native image not supported");
> + goto finish;
> + }
> +
> + coff_header = &(pe->coff_header);
> + section_offset = dos->pe_image_header_offset + sizeof (*pe);
> +
> + for (int i = 0; i < coff_header->num_sections; i++)
> + {
> + key = NULL;
> + val = NULL;
> + section = grub_zalloc (sizeof (*section));
> + if (section == NULL)
> + {
> + err = grub_errno;
> + goto finish;
> + }
> +
> + if (grub_file_seek (f, section_offset) == (grub_off_t) -1 ||
> + grub_file_read (f, section, sizeof (*section)) != sizeof (*section))
> + {
> + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section header");
> + goto finish;
> + }
> +
> + key = grub_strndup (section->name, 8);
> + if (key == NULL)
> + {
> + err = grub_errno;
> + goto finish;
> + }
> +
> + for (int j = 0; target[j] != NULL; j++)
> + {
> + if (grub_strcmp (key, target[j]) == 0)
> + {
> + /*
> + * We don't need to read the contents of the .linux PE section, but we
> + * should verify that the section exists.
> + */
> + if (grub_strcmp (key, ".linux") == 0)
> + {
> + has_linux = true;
> + break;
> + }
> +
> + val = grub_zalloc (section->raw_data_size);
You blindly assume it is valid section size. Probably we should enforce
sensible limits here...
> + if (val == NULL)
> + {
> + err = grub_errno;
> + goto finish;
> + }
> +
> + if (grub_file_seek (f, section->raw_data_offset) == (grub_off_t) -1 ||
> + grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) section->raw_data_size)
> + {
> + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section");
> + goto finish;
> + }
> +
> + err = blsuki_add_keyval (entry, key, val);
> + if (err != GRUB_ERR_NONE)
> + goto finish;
> +
> + break;
> + }
> + }
> +
> + section_offset += sizeof (*section);
> + grub_free (section);
> + grub_free (val);
> + grub_free (key);
> + section = NULL;
> + val = NULL;
> + key = NULL;
> + }
> +
> + if (has_linux == false)
> + err = grub_error (GRUB_ERR_NO_KERNEL, "UKI is missing the '.linux' section");
> +
> + finish:
> + grub_free (dos);
> + grub_free (pe);
> + grub_free (section);
> + grub_free (val);
> + grub_free (key);
> + return err;
> +}
> +#endif
> +
> +/*
> + * This function obtains the keyval pairs when the .osrel data is input into
> + * the content parameter and returns the full line that it obtained the keyval
> + * pair from.
> + */
> +static char *
> +uki_read_osrel (char *content, grub_off_t *pos, char **key_ret, char **val_ret)
> +{
> + char *line;
> + char *value;
> + grub_size_t linelen;
> +
> + for (;;)
> + {
> + line = content + *pos;
> + if (*line == '\0')
> + return NULL;
> +
> + linelen = 0;
> + while (line[linelen] != '\0' && line[linelen] != '\n' && line[linelen] != '\r')
> + linelen++;
> +
> + /* Move pos to the next line */
> + *pos += linelen;
> + if (content[*pos] != '\0')
> + (*pos)++;
> +
> + /* Skip empty line */
> + if (linelen == 0)
> + continue;
> +
> + line[linelen] = '\0';
> +
> + /* Remove leading white space */
> + while (linelen > 0 && (*line == ' ' || *line == '\t'))
> + {
> + line++;
> + linelen--;
> + }
> +
> + /* Remove trailing whitespace */
> + while (linelen > 0 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t'))
> + linelen--;
> + line[linelen] = '\0';
> +
> + if (*line == '#')
> + continue;
> +
> + /* Split key/value */
grub_strtok() here and there?
> + value = line;
> + while (*value != '\0' && *value != '=')
> + value++;
> + if (*value == '\0')
> + continue;
> + *value = '\0';
> + value++;
> + while (*value != '\0' && *value == '=')
> + value++;
> +
> + /* Remove quotes from value */
> + if (*value == '\"' && line[linelen - 1] == '\"')
> + {
> + value++;
> + line[linelen - 1] = '\0';
> + }
> +
> + *key_ret = line;
> + *val_ret = value;
> + break;
> + }
> +
> + return line;
> +}
> +
> struct read_entry_info
> {
> const char *devid;
> const char *dirname;
> + int cmd_type;
It should be an enum not an int. And if you put this together at the
beginning of the file you would realize that earlier...
> grub_file_t file;
> };
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot Loader Specification snippets
[not found] <mailman.2919.1747831917.8051.grub-devel@gnu.org>
@ 2025-05-29 10:59 ` Avnish Chouhan
0 siblings, 0 replies; 10+ messages in thread
From: Avnish Chouhan @ 2025-05-29 10:59 UTC (permalink / raw)
To: alec.r.brown; +Cc: grub-devel, Daniel Kiper
On 2025-05-21 18:21, grub-devel-request@gnu.org wrote:
> Send Grub-devel mailing list submissions to
> grub-devel@gnu.org
>
> To subscribe or unsubscribe via the World Wide Web, visit
> https://lists.gnu.org/mailman/listinfo/grub-devel
> or, via email, send a message with subject or body 'help' to
> grub-devel-request@gnu.org
>
> You can reach the person managing the list at
> grub-devel-owner@gnu.org
>
> When replying, please edit your Subject line so it is more specific
> than "Re: Contents of Grub-devel digest..."
>
>
> Today's Topics:
>
> 1. [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot
> Loader Specification snippets (Alec Brown)
>
>
> ----------------------------------------------------------------------
>
> Message: 1
> Date: Wed, 21 May 2025 12:51:24 +0000
> From: Alec Brown <alec.r.brown@oracle.com>
> To: grub-devel@gnu.org
> Cc: christopher.obbard@linaro.org, daniel.kiper@oracle.com,
> jan.setjeeilers@oracle.com, alec.r.brown@oracle.com,
> mate.kukri@canonical.com, pjones@redhat.com,
> ross.philipson@oracle.com, 93sam@debian.org, phcoder@gmail.com
> Subject: [PATCH v4 2/4] blsuki: Add blscfg command to parse Boot
> Loader Specification snippets
> Message-ID: <20250521125126.3928350-3-alec.r.brown@oracle.com>
>
> From: Peter Jones <pjones@redhat.com>
>
> The BootLoaderSpec (BLS) defines a scheme where different bootloaders
> can
> share a format for boot items and a configuration directory that
> accepts
> these common configurations as drop-in files.
>
> The BLS Specification:
> https://uapi-group.org/specifications/specs/boot_loader_specification/
>
> Signed-off-by: Peter Jones <pjones@redhat.com>
> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
> Signed-off-by: Will Thompson <wjt@endlessm.com>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> docs/grub.texi | 47 ++
> grub-core/Makefile.core.def | 10 +
> grub-core/commands/blsuki.c | 1198 ++++++++++++++++++++++++++++++++
> grub-core/commands/legacycfg.c | 4 +-
> grub-core/commands/menuentry.c | 8 +-
> grub-core/normal/main.c | 6 +
> include/grub/menu.h | 15 +
> include/grub/normal.h | 2 +-
> 8 files changed, 1284 insertions(+), 6 deletions(-)
> create mode 100644 grub-core/commands/blsuki.c
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index d9b26fa36..adab93668 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -6417,6 +6417,7 @@ you forget a command, you can run the command
> @command{help}
> * background_image:: Load background image for active
> terminal
> * badram:: Filter out bad regions of RAM
> * blocklist:: Print a block list
> +* blscfg:: Load Boot Loader Specification menu
> entries
> * boot:: Start up your operating system
> * cat:: Show the contents of a file
> * clear:: Clear the screen
> @@ -6603,6 +6604,52 @@ Print a block list (@pxref{Block list syntax})
> for @var{file}.
> @end deffn
>
>
> +@node blscfg
> +@subsection blscfg
> +
> +@deffn Command blscfg [@option{-p|--path} dir]
> [@option{-f|--enable-fallback}] [@option{-d|--show-default}]
> [@option{-n|--show-non-default}] [@option{-e|--entry} file]
> +Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot
> entries
> +generated from @command{blscfg} won't interfere with entries from
> @file{grub.cfg} appearing in
> +the GRUB menu. Also, entries generated from @command{blscfg} only
> generate in memory and
> +don't update @file{grub.cfg}.
> +
> +The @option{--path} option overrides the default path to the
> directory containing
> +the BLS entries. If this option isn't used, the default location is
> +/loader/entries in @code{$BOOT}. If no BLS entries are found, the
> +@option{--enable-fallback} option can be used to check for entries in
> the default
> +directory.
> +
> +The @option{--show-default} option allows the default boot entry to
> be added to the
> +GRUB menu from the BLS entries.
> +
> +The @option{--show-non-default} option allows non-default boot
> entries to be added to
> +the GRUB menu from the BLS entries.
> +
> +The @option{--entry} option allows specific boot entries to be added
> to the GRUB menu
> +from the BLS entries.
> +
> +The @option{--entry}, @option{--show-default}, and
> @option{--show-non-default} options
> +are used to filter which BLS entries are added to the GRUB menu. If
> none are
> +used, all entries in the default location or the location specified
> by @option{--path}
> +will be added to the GRUB menu.
> +
> +A BLS config file example:
> +@example
> +#
> /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
> +title Fedora 19 (Rawhide)
> +sort-key fedora
> +machine-id 6a9857a393724b7a981ebb5b8495b9ea
> +version 3.8.0-2.fc19.x86_64
> +options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet
> +architecture x64
> +linux
> /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
> +initrd
> /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
> +@end example
> +
> +References:
> @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/,
> The Boot Loader Specification}
> +@end deffn
> +
> +
> @node boot
> @subsection boot
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index f70e02e69..67628f65f 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -845,6 +845,16 @@ module = {
> common = commands/blocklist.c;
> };
>
> +module = {
> + name = blsuki;
> + common = commands/blsuki.c;
> + common = kern/vercmp.c;
> + enable = powerpc_ieee1275;
> + enable = efi;
> + enable = i386_pc;
> + enable = emu;
> +};
> +
> module = {
> name = boot;
> common = commands/boot.c;
> diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
> new file mode 100644
> index 000000000..2ad960ae3
> --- /dev/null
> +++ b/grub-core/commands/blsuki.c
> @@ -0,0 +1,1198 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2025 Free Software Foundation, Inc.
> + *
> + * GRUB is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published
> by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * GRUB is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/list.h>
> +#include <grub/types.h>
> +#include <grub/misc.h>
> +#include <grub/mm.h>
> +#include <grub/err.h>
> +#include <grub/dl.h>
> +#include <grub/extcmd.h>
> +#include <grub/i18n.h>
> +#include <grub/fs.h>
> +#include <grub/env.h>
> +#include <grub/file.h>
> +#include <grub/normal.h>
> +#include <grub/safemath.h>
> +#include <grub/vercmp.h>
> +#include <grub/lib/envblk.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
> +
> +static const struct grub_arg_option bls_opt[] =
> + {
> + {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"),
> ARG_TYPE_PATHNAME},
> + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if
> --path fails to find BLS entries.", 0, ARG_TYPE_NONE},
> + {"show-default", 'd', 0, "Allow the default BLS entry to be added
> to the GRUB menu.", 0, ARG_TYPE_NONE},
> + {"show-non-default", 'n', 0, "Allow the non-default BLS entries
> to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
> + {"entry", 'e', 0, "Allow specific BLS entries to be added to the
> GRUB menu.", N_("FILE"), ARG_TYPE_FILE},
> + {0, 0, 0, 0, 0, 0}
> + };
> +
> +struct keyval
> +{
> + const char *key;
> + char *val;
> +};
> +
> +static grub_blsuki_entry_t *entries = NULL;
> +
> +#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
> +
> +/*
> + * This function will add a new keyval pair to a list of keyvals
> stored in the
> + * entry parameter.
> + */
> +static grub_err_t
> +blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
> +{
> + char *k, *v;
> + struct keyval **kvs, *kv;
> + grub_size_t size;
> + int new_n = entry->nkeyvals + 1;
> +
> + if (entry->keyvals_size == 0)
> + {
> + size = sizeof (struct keyval *);
> + kvs = grub_malloc (size);
> + if (kvs == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space
> for BLS key values");
Hi Alec,
Indentation seems off here!
> +
> + entry->keyvals = kvs;
> + entry->keyvals_size = size;
> + }
> + else if (entry->keyvals_size < new_n * sizeof (struct keyval *))
> + {
> + size = entry->keyvals_size * 2;
> + kvs = grub_realloc (entry->keyvals, size);
> + if (kvs == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate
> space for BLS key values");
> +
Indentation seems off here!
> + entry->keyvals = kvs;
> + entry->keyvals_size = size;
> + }
> +
> + kv = grub_malloc (sizeof (struct keyval));
> + if (kv == NULL)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space
> for new BLS key value");
> +
> + k = grub_strdup (key);
> + if (k == NULL)
> + {
> + grub_free (kv);
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space
> for new BLS key value");
> + }
> +
> + v = grub_strdup (val);
> + if (v == NULL)
> + {
> + grub_free (k);
> + grub_free (kv);
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space
> for new BLS key value");
> + }
> +
> + kv->key = k;
> + kv->val = v;
> +
> + entry->keyvals[entry->nkeyvals] = kv;
> + entry->nkeyvals = new_n;
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * Find the value of the key named by keyname. If there are allowed to
> be
> + * more than one, pass a pointer to an int set to -1 the first time,
> and pass
> + * the same pointer through each time after, and it'll return them in
> sorted
> + * order as defined in the BLS fragment file.
> + */
> +static char *
> +blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int
> *last)
> +{
> + int idx, start = 0;
> + struct keyval *kv = NULL;
> + char *ret = NULL;
> +
> + if (last != NULL)
> + start = *last + 1;
> +
> + for (idx = start; idx < entry->nkeyvals; idx++)
> + {
> + kv = entry->keyvals[idx];
> +
> + if (grub_strcmp (keyname, kv->key) == 0)
> + {
> + ret = kv->val;
> + break;
> + }
Indentation seems off in if condition!
> + }
> +
> + if (last != NULL)
> + {
> + if (idx == entry->nkeyvals)
> + *last = -1;
> + else
> + *last = idx;
Same as above!
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * Add a new grub_blsuki_entry_t struct to the entries list and sort
> it's
> + * position on the list.
> + */
> +static grub_err_t
> +blsuki_add_entry (grub_blsuki_entry_t *entry)
> +{
> + grub_blsuki_entry_t *e, *last = NULL;
> + grub_err_t err;
> + int rc;
> +
> + if (entries == NULL)
> + {
> + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n",
> entry->filename);
> + entries = entry;
> + return GRUB_ERR_NONE;
> + }
> +
> + FOR_BLSUKI_ENTRIES (e)
> + {
> + err = grub_split_vercmp (entry->filename, e->filename, true,
> &rc);
> + if (err != GRUB_ERR_NONE)
> + return err;
Indentation seems off!
> +
> + if (rc == GRUB_VERCMP_SAME)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, "duplicate file");
> +
Same here!
> + if (rc == GRUB_VERCMP_NEWER)
> + {
> + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n",
> entry->filename);
> + grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry));
> + if (entry->next == entries)
> + {
> + entries = entry;
> + entry->prev = NULL;
> + }
> + else
> + last->next = entry;
> + return GRUB_ERR_NONE;
> + }
> + last = e;
> + }
Indentation seems off in if condition!
> +
> + if (last != NULL)
> + {
> + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n",
> entry->filename);
> + last->next = entry;
> + entry->prev = &last;
> + }
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * This function parses each line of a BLS config file to obtain the
> key value
> + * pairs that will be used to setup the GRUB menu entries. The key
> value pair
> + * will be stored in a list in the entry parameter.
> + */
> +static grub_err_t
> +bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
> +{
> + grub_err_t err = GRUB_ERR_NONE;
> +
> + for (;;)
> + {
> + char *buf;
> + char *separator;
> +
> + buf = grub_file_getline (f);
> + if (buf == NULL)
> + break;
Indentation seems off!
> +
> + while (buf != NULL && buf[0] != '\0' && (buf[0] == ' ' ||
> buf[0] == '\t'))
> + buf++;
Indentation seems off!
> + if (buf[0] == '#')
> + {
> + grub_free (buf);
> + continue;
> + }
Indentation seems off in if condition!
> +
> + separator = grub_strchr (buf, ' ');
> +
> + if (separator == NULL)
> + separator = grub_strchr (buf, '\t');
Indentation seems off!
> +
> + if (separator == NULL || separator[1] == '\0')
> + {
> + grub_free (buf);
> + break;
> + }
Indentation seems off in if condition!
> +
> + separator[0] = '\0';
> +
> + do
> + {
> + separator++;
> + }
Indentation seems off!
> + while (*separator == ' ' || *separator == '\t');
What's the use of this while condition? It may result in an infinite
loop...
> +
> + err = blsuki_add_keyval (entry, buf, separator);
> + grub_free (buf);
> + if (err != GRUB_ERR_NONE)
> + break;
Indentation seems off!
> + }
> +
> + return err;
> +}
> +
> +struct read_entry_info
> +{
> + const char *devid;
> + const char *dirname;
> + grub_file_t file;
> +};
> +
> +/*
> + * If a file hasn't already been opened, this function opens a BLS
> config file
> + * and initializes entry data before parsing keyvals and adding the
> entry to
> + * the list of BLS entries.
> + */
> +static int
> +blsuki_read_entry (const char *filename,
> + const struct grub_dirhook_info *dirhook_info __attribute__
> ((__unused__)),
> + void *data)
Indentation seems off!
blsuki_read_entry (const char *filename,
const struct grub_dirhook_info *dirhook_info
__attribute__ ((__unused__)),
void *data)
> +{
> + grub_size_t prefix_len = 0, filename_len, ext_len = 5;
> + grub_err_t err;
> + char *p = NULL;
> + grub_file_t f = NULL;
> + grub_blsuki_entry_t *entry;
> + struct read_entry_info *info = (struct read_entry_info *) data;
> +
> + grub_dprintf ("blsuki", "filename: \"%s\"\n", filename);
> +
> + filename_len = grub_strlen (filename);
> +
> + if (info->file != NULL)
> + f = info->file;
> + else
> + {
> + if (filename_len < ext_len || grub_strcmp (filename +
> filename_len - ext_len, ".conf") != 0)
> + return 0;
Indentation seems off!
> +
> + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname,
> filename);
> +
> + f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
> + grub_free (p);
> + if (f == NULL)
> + goto finish;
Indentation seems off!
> + }
> +
> + entry = grub_zalloc (sizeof (*entry));
> + if (entry == NULL)
> + goto finish;
> +
> + /*
> + * If a file is opened before this function, the filename may have a
> prefix.
> + * Since the filename is used for the ID of the GRUB menu entry, we
> can
> + * remove the prefix.
> + */
> + if (info->file != NULL)
> + {
> + char *slash;
> +
> + slash = grub_strrchr (filename, '/');
> + if (slash == NULL)
> + slash = grub_strrchr (filename, '\\');
Indentation seems off!
> +
> + if (slash != NULL)
> + {
> + while (*slash == '/' || *slash == '\\')
> + slash++;
> +
> + prefix_len = slash - filename;
> + }
Indentation seems off in if condition!
> + }
> + filename_len -= prefix_len;
> +
> + entry->filename = grub_strndup (filename + prefix_len,
> filename_len);
> + if (entry->filename == NULL)
> + {
> + grub_free (entry);
> + goto finish;
> + }
> +
> + err = bls_parse_keyvals (f, entry);
> +
> + if (err == GRUB_ERR_NONE)
> + blsuki_add_entry (entry);
> + else
> + grub_free (entry);
> +
> + finish:
> + if (f != NULL)
> + grub_file_close (f);
> +
> + return 0;
> +}
> +
> +/*
> + * This function returns a list of values that had the same key in the
> BLS
> + * config file. The number of entries in this list is returned by the
> len
> + * parameter.
> + */
> +static char **
> +blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int
> *len)
> +{
> + int last = -1;
> + char *val;
> + int nlist = 0;
> + char **list;
> +
> + list = grub_zalloc (sizeof (char *));
> + if (list == NULL)
> + return NULL;
> +
> + while (1)
> + {
> + char **new;
> +
> + /*
> + * Since the same key might appear more than once, the 'last'
> variable
> + * starts at -1 and increments to indicate the last index in the
> list
> + * we obtained from blsuki_get_val().
> + */
> + val = blsuki_get_val (entry, key, &last);
> + if (val == NULL)
> + break;
Indentation seems off!
> +
> + new = grub_realloc (list, (nlist + 2) * sizeof (char *));
> + if (new == NULL)
> + break;
Indentation seems off!
> +
> + list = new;
> + list[nlist++] = val;
> + list[nlist] = NULL;
> + }
> +
> + if (nlist == 0)
> + {
> + grub_free (list);
> + return NULL;
> + }
> +
> + if (len != NULL)
> + *len = nlist;
> +
> + return list;
> +}
> +
> +/*
> + * This function appends a field to the end of a buffer. If the field
> given is
> + * an enviornmental variable, it gets the value stored for that
> variable and
> + * appends that to the buffer instead.
> + */
> +static char *
> +blsuki_field_append (bool is_env_var, char *buffer, const char
> *start, const char *end)
> +{
> + char *tmp;
> + const char *field;
> + int term = (is_env_var == true) ? 2 : 1;
> + grub_size_t size = 0;
> +
> + tmp = grub_strndup (start, end - start + 1);
> + if (tmp == NULL)
> + return NULL;
> +
> + field = tmp;
> +
> + if (is_env_var == true)
> + {
> + field = grub_env_get (tmp);
> + if (field == NULL)
> + return buffer;
> + }
> +
> + if (grub_add (grub_strlen (field), term, &size))
> + return NULL;
> +
> + if (buffer == NULL)
> + buffer = grub_zalloc (size);
> + else
> + {
> + if (grub_add (size, grub_strlen (buffer), &size))
> + return NULL;
Indentation seems off!
Adding one new line would be good here!
> + buffer = grub_realloc (buffer, size);
> + }
> +
> + if (buffer == NULL)
> + return NULL;
> +
> + tmp = buffer + grub_strlen (buffer);
> + tmp = grub_stpcpy (tmp, field);
> +
> + if (is_env_var == true)
> + tmp = grub_stpcpy (tmp, " ");
> +
> + return buffer;
> +}
> +
> +/*
> + * This function takes a value string, checks for environmental
> variables, and
> + * returns the value string with all environmental variables replaced
> with the
> + * value stored in the variable.
> + */
> +static char *
> +blsuki_expand_val (const char *value)
> +{
> + char *buffer = NULL;
> + const char *start = value;
> + const char *end = value;
> + bool is_env_var = false;
> +
> + if (value == NULL)
> + return NULL;
> +
> + while (*value != '\0')
> + {
> + if (*value == '$')
> + {
> + if (start != end)
> + {
> + buffer = blsuki_field_append (is_env_var, buffer, start, end);
> + if (buffer == NULL)
> + return NULL;
> + }
> +
> + is_env_var = true;
> + start = value + 1;
> + }
> + else if (is_env_var == true)
> + {
> + if (grub_isalnum (*value) == 0 && *value != '_')
> + {
> + buffer = blsuki_field_append (is_env_var, buffer, start, end);
> + is_env_var = false;
> + start = value;
> + if (*start == ' ')
> + start++;
> + }
> + }
Indentation seems off in "if (*value == '$')" and else if conditions.
Missing else condition in if else ladder. Adding it would be good!
> +
> + end = value;
> + value++;
> + }
> +
> + if (start != end)
> + {
> + buffer = blsuki_field_append (is_env_var, buffer, start, end);
> + if (buffer == NULL)
> + return NULL;
Indentation seems off!
> + }
> +
> + return buffer;
> +}
> +
> +/*
> + * This function parses a string containing initrd paths and returns
> it as a
> + * list.
> + */
> +static char **
> +blsuki_early_initrd_list (const char *initrd)
> +{
> + int nlist = 0;
> + char **list = NULL;
> + const char *separator = initrd;
> + char *tmp;
> +
> + for (;;)
> + {
> + while (*separator != '\0' && *separator != ' ' && *separator !=
> '\t')
> + separator++;
Indentation seems off!
> + if (*separator == '\0')
> + break;
Indentation seems off!
> +
> + list = grub_realloc (list, (nlist + 2) * sizeof (char *));
Good to use grub_malloc here!
> + if (list == NULL)
> + return NULL;
Indentation seems off!
> +
> + tmp = grub_strndup (initrd, separator - initrd);
> + if (tmp == NULL)
> + {
> + grub_free (list);
> + return NULL;
> + }
Indentation seems off in if condition!
> + list[nlist++] = tmp;
> + list[nlist] = NULL;
> + separator++;
> + initrd = separator;
> + }
> +
> + list = grub_realloc (list, (nlist + 2) * sizeof (char *));
> + if (list == NULL)
> + return NULL;
> +
> + tmp = grub_strndup (initrd, grub_strlen (initrd));
> + if (tmp == NULL)
> + {
> + grub_free (list);
> + return NULL;
> + }
> + list[nlist++] = tmp;
> + list[nlist] = NULL;
> +
> + return list;
> +}
> +
> +/*
> + * This function returns a string with the command to load a linux
> kernel with
> + * kernel command-line options based on what was specified in the BLS
> config
> + * file.
> + */
> +static char *
> +bls_get_linux (grub_blsuki_entry_t *entry)
> +{
> + char *linux_path;
> + char *linux_cmd = NULL;
> + char *options = NULL;
> + char *tmp;
> + grub_size_t size;
> +
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + options = blsuki_expand_val (blsuki_get_val (entry, "options",
> NULL));
> +
> + if (options == NULL)
> + options = blsuki_expand_val (grub_env_get ("default_kernelopts"));
> +
> + if (grub_add (grub_strlen ("linux "), grub_strlen (linux_path),
> &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while
> calculating linux buffer size");
> + goto finish;
> + }
> +
> + if (options != NULL)
> + {
> + if (grub_add (size, grub_strlen (options), &size) ||
> + grub_add (size, 1, &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while
> calculating linux buffer size");
> + goto finish;
> + }
Indentation seems off!
if (grub_add (size, grub_strlen (options), &size) || grub_add
(size, 1, &size))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while
calculating linux buffer size");
goto finish;
}
> + }
> +
> + linux_cmd = grub_malloc (size);
> + if (linux_cmd == NULL)
> + goto finish;
> +
> + tmp = linux_cmd;
> + tmp = grub_stpcpy (tmp, "linux ");
> + tmp = grub_stpcpy (tmp, linux_path);
> + if (options != NULL)
> + {
> + tmp = grub_stpcpy (tmp, " ");
> + tmp = grub_stpcpy (tmp, options);
> + }
> + tmp = grub_stpcpy (tmp, "\n");
> +
> + finish:
> + grub_free (options);
> +
> + return linux_cmd;
> +}
> +
> +/*
> + * This function returns a string with the command to load all initrds
> for a
> + * linux kernel image based on the list provided by the BLS config
> file.
> + */
> +static char *
> +bls_get_initrd (grub_blsuki_entry_t *entry)
> +{
> + char **initrd_list;
> + char *initrd_cmd = NULL;
> + const char *early_initrd;
> + char **early_initrd_list = NULL;
> + char *tmp;
> + char *slash;
> + char *prefix = NULL;
> + grub_size_t prefix_len = 0;
> + char *linux_path;
> + grub_size_t size;
> + int i;
> +
> + initrd_list = blsuki_make_list (entry, "initrd", NULL);
> + early_initrd = grub_env_get ("early_initrd");
> +
> + if (early_initrd != NULL)
> + {
> + early_initrd_list = blsuki_early_initrd_list (early_initrd);
> + if (early_initrd_list == NULL)
> + {
> + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to create early
> initrd list"));
> + goto finish;
> + }
Indentation seems off in if condition!
> +
> + if (initrd_list != NULL && initrd_list[0] != NULL)
> + {
> + slash = grub_strrchr (initrd_list[0], '/');
> + if (slash == NULL)
> + prefix_len = slash - initrd_list[0] + 1;
> + prefix = grub_strndup (initrd_list[0], prefix_len);
> + }
Indentation seems off in if condition!
> + else
> + {
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + slash = grub_strrchr (linux_path, '/');
> + if (slash != NULL)
> + prefix_len = slash - linux_path + 1;
> + prefix = grub_strndup (linux_path, prefix_len);
> + }
Indentation seems off in else condition!
> +
> + if (prefix == NULL)
> + {
> + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate initrd
> prefix buffer"));
> + goto finish;
> + }
Indentation seems off in if condition!
> + }
> +
> + if (early_initrd_list != NULL || initrd_list != NULL)
> + {
> +
> + size = grub_strlen ("initrd");
> +
> + for (i = 0; early_initrd_list != NULL && early_initrd_list[i]
> != NULL; i++)
> + {
> + if (grub_add (size, 1, &size) ||
> + grub_add (size, grub_strlen (prefix), &size) ||
> + grub_add (size, grub_strlen (early_initrd_list[i]), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected
> calculating initrd buffer size");
> + goto finish;
> + }
> + }
> +
> + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
> + {
Indentation seems off!
> + if (grub_add (size, 1, &size) ||
> + grub_add (size, grub_strlen (initrd_list[i]), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected
> calculating initrd buffer size");
> + goto finish;
> + }
> + }
> +
> + if (grub_add (size, 1, &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating
> initrd buffer size");
> + goto finish;
> + }
Indentation seems off in if condition!
> +
> + initrd_cmd = grub_malloc (size);
> + if (initrd_cmd == NULL)
> + goto finish;
Indentation seems off!
> +
> + tmp = grub_stpcpy (initrd_cmd, "initrd");
> + for (i = 0; early_initrd_list != NULL && early_initrd_list[i]
> != NULL; i++)
> + {
Indentation seems off!
> + grub_dprintf ("blsuki", "adding early initrd %s\n",
> early_initrd_list[i]);
> + tmp = grub_stpcpy (tmp, " ");
> + tmp = grub_stpcpy (tmp, prefix);
> + tmp = grub_stpcpy (tmp, early_initrd_list[i]);
> + }
> +
> + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
> + {
Indentation seems off!
> + grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
> + tmp = grub_stpcpy (tmp, " ");
> + tmp = grub_stpcpy (tmp, initrd_list[i]);
> + }
> + tmp = grub_stpcpy (tmp, "\n");
> + }
> +
> + finish:
> + for (i = 0; early_initrd_list != NULL && early_initrd_list[i] !=
> NULL; i++)
> + grub_free (early_initrd_list[i]);
> + grub_free (prefix);
> +
> + return initrd_cmd;
> +}
> +
> +/*
> + * This function returns a string with the command to load a device
> tree blob
> + * from the BLS config file.
> + */
> +static char *
> +bls_get_devicetree (grub_blsuki_entry_t *entry)
> +{
> + char *dt_path;
> + char *dt_cmd = NULL;
> + char *tmp;
> + char *linux_path;
> + char *slash;
> + char *prefix = NULL;
> + grub_size_t prefix_len = 0;
> + grub_size_t size;
> + bool add_dt_prefix = false;
> +
> + dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree",
> NULL));
> +
> + if (dt_path == NULL)
> + {
> + dt_path = blsuki_expand_val (grub_env_get ("devicetree"));
> + add_dt_prefix = true;
> + }
> +
> + if (dt_path != NULL)
> + {
> + if (add_dt_prefix == true)
> + {
Indentation seems off!
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + slash = grub_strchr (linux_path, '/');
> + if (slash != NULL)
> + prefix_len = slash - linux_path + 1;
Adding a new line here would be good!
> + prefix = grub_strndup (linux_path, prefix_len);
> + if (prefix == NULL)
> + {
> + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate
> prefix buffer"));
> + goto finish;
> + }
> + }
> +
> + if (grub_add (grub_strlen ("devicetree "), grub_strlen
> (dt_path), &size) ||
> + grub_add (size, 1, &size))
Indentation seems off!
> + {
Indentation seems off!
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating
> device tree buffer size");
> + goto finish;
> + }
> +
> + if (add_dt_prefix == true)
> + {
Indentation seems off!
> + if (grub_add (size, grub_strlen (prefix), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected
> calculating device tree buffer size");
> + goto finish;
> + }
> + }
> + dt_cmd = grub_malloc (size);
> + if (dt_cmd == NULL)
> + goto finish;
Indentation seems off! and anding a new line would be good!
> + tmp = dt_cmd;
> + tmp = grub_stpcpy (dt_cmd, "devicetree");
> + tmp = grub_stpcpy (tmp, " ");
> + if (add_dt_prefix == true)
> + tmp = grub_stpcpy (tmp, prefix);
Adding a new line would be good!
> + tmp = grub_stpcpy (tmp, dt_path);
> + tmp = grub_stpcpy (tmp, "\n");
> + }
> +
> + finish:
> + grub_free (prefix);
> +
> + return dt_cmd;
> +}
> +
> +/*
> + * This function puts together all of the commands generated from the
> contents
> + * of the BLS config file and creates a new entry in the GRUB boot
> menu.
> + */
> +static void
> +bls_create_entry (grub_blsuki_entry_t *entry)
> +{
> + int argc = 0;
> + const char **argv = NULL;
> + char *title = NULL;
> + char *linux_path = NULL;
> + char *linux_cmd = NULL;
> + char *initrd_cmd = NULL;
> + char *dt_cmd = NULL;
> + char *id = entry->filename;
> + grub_size_t id_len;
> + char *hotkey = NULL;
> + char *users = NULL;
> + char **classes = NULL;
> + char **args = NULL;
> + char *src = NULL;
> + const char *sdval = NULL;
> + int i;
> + grub_size_t size;
> + bool savedefault;
> +
> + linux_path = blsuki_get_val (entry, "linux", NULL);
> + if (linux_path == NULL)
> + {
> + grub_dprintf ("blsuki", "Skipping file %s with no 'linux'
> key.\n", entry->filename);
> + goto finish;
> + }
> +
> + id_len = grub_strlen (id);
> + if (id_len >= 5 && grub_strcmp (id + id_len - 5, ".conf") == 0)
> + id[id_len - 5] = '\0';
> +
> + title = blsuki_get_val (entry, "title", NULL);
> + hotkey = blsuki_get_val (entry, "grub_hotkey", NULL);
> + users = blsuki_expand_val (blsuki_get_val (entry, "grub_users",
> NULL));
> + classes = blsuki_make_list (entry, "grub_class", NULL);
> + args = blsuki_make_list (entry, "grub_arg", &argc);
> +
> + argc++;
> + if (grub_mul (argc + 1, sizeof (char *), &size))
> + {
> + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected
> creating argv list"));
> + goto finish;
> + }
> +
> + argv = grub_malloc (size);
> + if (argv == NULL)
> + goto finish;
Adding a new line would be good!
> + argv[0] = title ? title : linux_path;
> + for (i = 1; i < argc; i++)
> + argv[i] = args[i-1];
Adding a new line would be good!
> + argv[argc] = NULL;
> +
> + linux_cmd = bls_get_linux (entry);
> + if (linux_cmd == NULL)
> + goto finish;
> +
> + initrd_cmd = bls_get_initrd (entry);
> + if (grub_errno != GRUB_ERR_NONE)
> + goto finish;
> +
> + dt_cmd = bls_get_devicetree (entry);
> + if (grub_errno != GRUB_ERR_NONE)
> + goto finish;
> +
> + sdval = grub_env_get ("save_default");
> + savedefault = ((NULL != sdval) && (grub_strcmp (sdval, "true") ==
> 0));
> + src = grub_xasprintf ("%s%s%s%s",
> + savedefault ? "savedefault\n" : "",
> + linux_cmd, initrd_cmd ? initrd_cmd : "",
> + dt_cmd ? dt_cmd : "");
Indentation seems off!
> +
> + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey,
> NULL, src, 0, entry);
> +
> + finish:
> + grub_free (linux_cmd);
> + grub_free (dt_cmd);
> + grub_free (initrd_cmd);
> + grub_free (classes);
> + grub_free (args);
> + grub_free (argv);
> + grub_free (src);
> +}
> +
> +struct find_entry_info
> +{
> + const char *dirname;
> + const char *devid;
> + grub_device_t dev;
> + grub_fs_t fs;
> +};
> +
> +/*
> + * This function fills a find_entry_info struct passed in by the info
> parameter.
> + * If the dirname or devid parameters are set to NULL, the dirname and
> devid
> + * fields in the info parameter will be set to default values. If info
> already
> + * has a value in the dev fields, we can compare it to the value
> passed in by
> + * the devid parameter or the default devid to see if we need to open
> a new
> + * device.
> + */
> +static grub_err_t
> +blsuki_set_find_entry_info (struct find_entry_info *info, const char
> *dirname, const char *devid)
> +{
> + grub_device_t dev;
> + grub_fs_t fs;
> +
> + if (info == NULL)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not
> set");
> +
> + if (devid == NULL)
> + {
> + devid = grub_env_get ("root");
> + if (devid == NULL)
> + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't
Indentation seems off!
> set"), "root");
> + }
> +
> + /* Check that we aren't closing and opening the same device. */
> + if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0)
> + {
> + grub_device_close (info->dev);
> + info->dev = NULL;
> + }
> + /* If we are using the same device, then we can skip this step and
> only set the directory. */
> + if (info->dev == NULL)
> + {
> + grub_dprintf ("blsuki", "opening %s\n", devid);
> + dev = grub_device_open (devid);
> + if (dev == NULL)
> + return grub_errno;
Indentation seems off!
> +
> + grub_dprintf ("blsuki", "probing fs\n");
> + fs = grub_fs_probe (dev);
> + if (fs == NULL)
> + {
> + grub_device_close (dev);
> + return grub_errno;
> + }
Indentation seems off in if condition!
> +
> + info->devid = devid;
> + info->dev = dev;
> + info->fs = fs;
> + }
> +
> + info->dirname = dirname;
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * This function searches for BLS config files based on the data in
> the info
> + * parameter. If the fallback option is enabled, the default location
> will be
> + * checked for BLS config files if the first attempt fails.
> + */
> +static void
> +blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> +{
> + struct read_entry_info read_entry_info;
> + grub_fs_t dir_fs = NULL;
> + grub_device_t dir_dev = NULL;
> + bool fallback = false;
> + int r;
> +
> + do
> + {
> + read_entry_info.file = NULL;
> + read_entry_info.dirname = info->dirname;
> +
> + grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname);
> + dir_dev = info->dev;
> + dir_fs = info->fs;
> + read_entry_info.devid = info->devid;
> +
> + r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname,
> blsuki_read_entry,
> + &read_entry_info);
Indentation seems off!
> + if (r != 0)
> + {
> + grub_dprintf ("blsuki", "blsuki_read_entry returned error\n");
> + grub_errno = GRUB_ERR_NONE;
> + }
Indentation seems off in if condition!
> +
> + /*
> + * If we aren't able to find BLS entries in the directory given
> by info->dirname,
> + * we can fallback to the default location
> "/boot/loader/entries/" and see if we
> + * can find the files there.
> + */
> + if (entries == NULL && fallback == false && enable_fallback ==
> true)
> + {
> + blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
> + grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to
> %s\n",
> + read_entry_info.dirname, info->dirname);
> + fallback = true;
> + }
Indentation seems off in if condition!
> + else
> + fallback = false;
Indentation seems off!
> + }
> + while (fallback == true);
> +}
> +
> +static grub_err_t
> +blsuki_load_entries (char *path, bool enable_fallback)
> +{
> + grub_size_t len;
> + static grub_err_t r;
> + const char *devid = NULL;
> + char *dir = NULL;
> + struct find_entry_info info = {
> + .dev = NULL,
> + .fs = NULL,
> + .dirname = NULL,
> + };
> + struct read_entry_info rei = {
> + .devid = NULL,
> + .dirname = NULL,
> + };
> +
> + if (path != NULL)
> + {
> + len = grub_strlen (path);
> + if (len >= 5 && grub_strcmp (path + len - 5, ".conf") == 0)
> + {
> + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
> + if (rei.file == NULL)
> + return grub_errno;
> +
> + /* blsuki_read_entry() closes the file. */
> + return blsuki_read_entry (path, NULL, &rei);
> + }
> + else if (path[0] == '(')
> + {
> + devid = path + 1;
> +
> + dir = grub_strchr (path, ')');
> + if (dir == NULL)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name
> `%s'"), path);
> +
> + *dir = '\0';
> +
> + /* Check if there is more than the devid in the path. */
> + if (dir + 1 < path + len)
> + dir = dir + 1;
> + }
> + else if (path[0] == '/')
> + dir = path;
> + }
Indentation seems off in if else ladder! adding else condition would be
good!
> +
> + if (dir == NULL)
> + dir = (char *) GRUB_BLS_CONFIG_PATH;
> +
> + r = blsuki_set_find_entry_info (&info, dir, devid);
> + if (r == GRUB_ERR_NONE)
> + blsuki_find_entry (&info, enable_fallback);
> +
> + if (info.dev != NULL)
> + grub_device_close (info.dev);
> +
> + return r;
> +}
> +
> +static bool
> +blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t
> *entry, int idx)
> +{
> + const char *title;
> + const char *def_entry_end;
> + int def_idx;
> +
> + if (def_entry == NULL)
> + return false;
> +
> + if (grub_strcmp (def_entry, entry->filename) == 0)
> + return true;
> +
> + title = blsuki_get_val (entry, "title", NULL);
> +
> + if (title != NULL && grub_strcmp (def_entry, title) == 0)
> + return true;
> +
> + def_idx = (int) grub_strtol (def_entry, &def_entry_end, 0);
> + if (grub_errno != GRUB_ERR_NONE)
> + {
> + grub_errno = GRUB_ERR_NONE;
> + return false;
> + }
> + if (*def_entry == '\0' || *def_entry_end != '\0')
> + return false;
> +
> + if (def_idx == idx)
> + return true;
> +
> + return false;
> +}
> +
> +/*
> + * This function creates a GRUB boot menu entry for each BLS entry in
> the
> + * entries list.
> + */
> +static grub_err_t
> +blsuki_create_entries (bool show_default, bool show_non_default, char
> *entry_id)
> +{
> + const char *def_entry = NULL;
> + grub_blsuki_entry_t *entry = NULL;
> + int idx = 0;
> +
> + def_entry = grub_env_get ("default");
> +
> + FOR_BLSUKI_ENTRIES(entry)
> + {
> + if (entry->visible == 1)
> + {
> + idx++;
> + continue;
> + }
> + if ((show_default == true && blsuki_is_default_entry
> (def_entry, entry, idx) == true) ||
> + (show_non_default == true && blsuki_is_default_entry (def_entry,
> entry, idx) == false) ||
> + (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0))
> + {
> + bls_create_entry (entry);
> + entry->visible = 1;
> + }
> + idx++;
> + }
Indentation seems off in both if conditions!
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__
> ((unused)),
> + char **args __attribute__ ((unused)))
> +{
> + grub_err_t err;
> + struct grub_arg_list *state = ctxt->state;
> + char *path = NULL;
> + char *entry_id = NULL;
> + bool enable_fallback = false;
> + bool show_default = false;
> + bool show_non_default = false;
> + bool all = true;
> +
> + if (state[0].set)
> + path = state[0].arg;
> + if (state[1].set)
> + enable_fallback = true;
> + if (state[2].set)
> + {
> + show_default = true;
> + all = false;
> + }
> + if (state[3].set)
> + {
> + show_non_default = true;
> + all = false;
> + }
> + if (state[4].set)
> + {
> + entry_id = state[4].arg;
> + all = false;
> + }
> + if (all == true)
> + {
> + show_default = true;
> + show_non_default = true;
> + }
> +
> + err = blsuki_load_entries (path, enable_fallback);
> + if (err != GRUB_ERR_NONE)
> + return err;
> +
> + return blsuki_create_entries (show_default, show_non_default,
> entry_id);
> +}
> +
> +static grub_extcmd_t bls_cmd;
> +
> +GRUB_MOD_INIT(blsuki)
> +{
> + bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0,
> + N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default]
> [-n|--show-non-default] [-e|--entry] FILE"),
> + N_("Import Boot Loader Specification snippets."),
> + bls_opt);
> +}
> +
> +GRUB_MOD_FINI(blsuki)
> +{
> + grub_unregister_extcmd (bls_cmd);
> +}
> diff --git a/grub-core/commands/legacycfg.c
> b/grub-core/commands/legacycfg.c
> index 3bf9fe2e4..f3c86dc7f 100644
> --- a/grub-core/commands/legacycfg.c
> +++ b/grub-core/commands/legacycfg.c
> @@ -143,7 +143,7 @@ legacy_file (const char *filename)
> args[0] = oldname;
> grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
> NULL, NULL,
> - entrysrc, 0);
> + entrysrc, 0, NULL);
> grub_free (args);
> entrysrc[0] = 0;
> grub_free (oldname);
> @@ -204,7 +204,7 @@ legacy_file (const char *filename)
> }
> args[0] = entryname;
> grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
> - NULL, NULL, entrysrc, 0);
> + NULL, NULL, entrysrc, 0, NULL);
> grub_free (args);
> }
>
> diff --git a/grub-core/commands/menuentry.c
> b/grub-core/commands/menuentry.c
> index 720e6d8ea..09749c415 100644
> --- a/grub-core/commands/menuentry.c
> +++ b/grub-core/commands/menuentry.c
> @@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char
> **args,
> char **classes, const char *id,
> const char *users, const char *hotkey,
> const char *prefix, const char *sourcecode,
> - int submenu)
> + int submenu, grub_blsuki_entry_t *blsuki)
> {
> int menu_hotkey = 0;
> char **menu_args = NULL;
> @@ -188,6 +188,7 @@ grub_normal_add_menu_entry (int argc, const char
> **args,
> (*last)->args = menu_args;
> (*last)->sourcecode = menu_sourcecode;
> (*last)->submenu = submenu;
> + (*last)->blsuki = blsuki;
>
> menu->size++;
> return GRUB_ERR_NONE;
> @@ -286,7 +287,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt,
> int argc, char **args)
> users,
> ctxt->state[2].arg, 0,
> ctxt->state[3].arg,
> - ctxt->extcmd->cmd->name[0] == 's');
> + ctxt->extcmd->cmd->name[0] == 's',
> + NULL);
>
> src = args[argc - 1];
> args[argc - 1] = NULL;
> @@ -303,7 +305,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt,
> int argc, char **args)
> ctxt->state[0].args, ctxt->state[4].arg,
> users,
> ctxt->state[2].arg, prefix, src + 1,
> - ctxt->extcmd->cmd->name[0] == 's');
> + ctxt->extcmd->cmd->name[0] == 's', NULL);
>
> src[len - 1] = ch;
> args[argc - 1] = src;
> diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
> index 04d058f55..2d493f21e 100644
> --- a/grub-core/normal/main.c
> +++ b/grub-core/normal/main.c
> @@ -21,6 +21,7 @@
> #include <grub/net.h>
> #include <grub/normal.h>
> #include <grub/dl.h>
> +#include <grub/menu.h>
> #include <grub/misc.h>
> #include <grub/file.h>
> #include <grub/mm.h>
> @@ -67,6 +68,11 @@ grub_normal_free_menu (grub_menu_t menu)
> grub_free (entry->args);
> }
>
> + if (entry->blsuki)
> + {
> + entry->blsuki->visible = 0;
> + }
Brackets not required!
Alec , I see many Indentation issues. I have tried pointing out as much
as I can. Please fix!
Thank you!
Regards,
Avnish Chouhan
> +
> End of Grub-devel Digest, Vol 255, Issue 51
> *******************************************
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 10+ messages in thread