[PATCH 07/14] discover/grub2: add support for grub2-style path specifiers in resources

Jeremy Kerr jk at ozlabs.org
Wed Nov 20 13:42:59 AEDT 2019


This change incorporates the grub2-style (device)/path specifiers in the
grub2 parser's resource code. This allows the boot option paths to use
device-specific references.

Device names are looked-up using the UUID and kernel IDs, but with the
lookup logic specific to a new function (grub2_lookup_device), so that
can be extended in a future change.

Signed-off-by: Jeremy Kerr <jk at ozlabs.org>
---
 discover/grub2/blscfg.c          | 19 +++----
 discover/grub2/builtins.c        | 15 ++----
 discover/grub2/grub2.c           | 56 +++++++++++++++-----
 discover/grub2/grub2.h           |  5 +-
 test/parser/Makefile.am          |  1 +
 test/parser/test-grub2-devpath.c | 88 ++++++++++++++++++++++++++++++++
 6 files changed, 147 insertions(+), 37 deletions(-)
 create mode 100644 test/parser/test-grub2-devpath.c

diff --git a/discover/grub2/blscfg.c b/discover/grub2/blscfg.c
index d4754aa..d08f8f0 100644
--- a/discover/grub2/blscfg.c
+++ b/discover/grub2/blscfg.c
@@ -166,7 +166,6 @@ static void bls_finish(struct conf_context *conf)
 	struct discover_context *dc = conf->dc;
 	struct discover_boot_option *opt = state->opt;
 	struct boot_option *option = opt->option;
-	const char *root;
 	char *filename;
 
 	if (!state->image) {
@@ -192,23 +191,21 @@ static void bls_finish(struct conf_context *conf)
 	else
 		option->name = talloc_strdup(option, state->image);
 
-	root = script_env_get(state->script, "root");
-
-	opt->boot_image = create_grub2_resource(opt, conf->dc->device,
-						root, state->image);
+	opt->boot_image = create_grub2_resource(state->script, opt,
+						state->image);
 
 	if (state->initrd)
-		opt->initrd = create_grub2_resource(opt, conf->dc->device,
-						    root, state->initrd);
+		opt->initrd = create_grub2_resource(state->script, opt,
+						    state->initrd);
 
 	if (state->dtb)
-		opt->dtb = create_grub2_resource(opt, conf->dc->device,
-						 root, state->dtb);
+		opt->dtb = create_grub2_resource(state->script, opt,
+						 state->dtb);
 
 	char* args_sigfile_default = talloc_asprintf(opt,
 		"%s.cmdline.sig", state->image);
-	opt->args_sig_file = create_grub2_resource(opt, conf->dc->device,
-						root, args_sigfile_default);
+	opt->args_sig_file = create_grub2_resource(state->script, opt,
+						args_sigfile_default);
 	talloc_free(args_sigfile_default);
 
 	option->is_default = option_is_default(state, option);
diff --git a/discover/grub2/builtins.c b/discover/grub2/builtins.c
index 7cac9f1..765c4d7 100644
--- a/discover/grub2/builtins.c
+++ b/discover/grub2/builtins.c
@@ -46,7 +46,6 @@ static int builtin_linux(struct grub2_script *script,
 		int argc, char *argv[])
 {
 	struct discover_boot_option *opt = script->opt;
-	const char *root;
 	int i;
 
 	if (!opt) {
@@ -61,10 +60,7 @@ static int builtin_linux(struct grub2_script *script,
 		return -1;
 	}
 
-	root = script_env_get(script, "root");
-
-	opt->boot_image = create_grub2_resource(opt, script->ctx->device,
-						root, argv[1]);
+	opt->boot_image = create_grub2_resource(script, opt, argv[1]);
 	opt->option->boot_args = NULL;
 
 	if (argc > 2)
@@ -77,8 +73,8 @@ static int builtin_linux(struct grub2_script *script,
 
 	char* args_sigfile_default = talloc_asprintf(opt,
 		"%s.cmdline.sig", argv[1]);
-	opt->args_sig_file = create_grub2_resource(opt, script->ctx->device,
-						root, args_sigfile_default);
+	opt->args_sig_file = create_grub2_resource(script, opt,
+						args_sigfile_default);
 	talloc_free(args_sigfile_default);
 	return 0;
 }
@@ -88,7 +84,6 @@ static int builtin_initrd(struct grub2_script *script,
 		int argc, char *argv[])
 {
 	struct discover_boot_option *opt = script->opt;
-	const char *root;
 
 	if (!opt) {
 		pb_log("grub2 syntax error: 'initrd' statement outside "
@@ -102,9 +97,7 @@ static int builtin_initrd(struct grub2_script *script,
 		return -1;
 	}
 
-	root = script_env_get(script, "root");
-	opt->initrd = create_grub2_resource(opt, script->ctx->device,
-						root, argv[1]);
+	opt->initrd = create_grub2_resource(script, opt, argv[1]);
 
 	return 0;
 }
diff --git a/discover/grub2/grub2.c b/discover/grub2/grub2.c
index 3873720..a08a320 100644
--- a/discover/grub2/grub2.c
+++ b/discover/grub2/grub2.c
@@ -33,13 +33,35 @@ static const char *const grub2_conf_files[] = {
 	NULL
 };
 
+static struct discover_device *grub2_lookup_device(
+		struct device_handler *handler, const char *desc)
+{
+	struct discover_device *dev;
+
+	if (!desc || !*desc)
+		return NULL;
+
+	dev = device_lookup_by_id(handler, desc);
+	if (dev)
+		return dev;
+
+	/* for now, only lookup by UUID */
+	dev = device_lookup_by_uuid(handler, desc);
+	if (dev)
+		return dev;
+
+	return NULL;
+}
+
 /* we use slightly different resources for grub2 */
-struct resource *create_grub2_resource(struct discover_boot_option *opt,
-		struct discover_device *orig_device,
-		const char *root, const char *path)
+struct resource *create_grub2_resource(struct grub2_script *script,
+		struct discover_boot_option *opt,
+		const char *path)
 {
+	struct discover_device *dev;
 	struct grub2_file *file;
 	struct resource *res;
+	const char *root;
 
 	if (strstr(path, "://")) {
 		struct pb_url *url = pb_url_parse(opt, path);
@@ -47,18 +69,29 @@ struct resource *create_grub2_resource(struct discover_boot_option *opt,
 			return create_url_resource(opt, url);
 	}
 
+	file = grub2_parse_file(script, path);
+	if (!file)
+		return NULL;
+
 	res = talloc(opt, struct resource);
+	root = script_env_get(script, "root");
 
-	if (root) {
-		file = talloc(res, struct grub2_file);
+	if (!file->dev && root && strlen(root))
 		file->dev = talloc_strdup(file, root);
-		file->path = talloc_strdup(file, path);
 
-		res->resolved = false;
-		res->info = file;
+	/* if we don't have a device specified, or the lookup succeeds now,
+	 * then we can resolve the resource right away */
+	if (file->dev)
+		dev = grub2_lookup_device(script->ctx->handler, file->dev);
+	else
+		dev = script->ctx->device;
 
-	} else
-		resolve_resource_against_device(res, orig_device, path);
+	if (dev) {
+		resolve_resource_against_device(res, dev, file->path);
+	} else {
+		res->resolved = false;
+		res->info = talloc_steal(opt, file);
+	}
 
 	return res;
 }
@@ -71,8 +104,7 @@ bool resolve_grub2_resource(struct device_handler *handler,
 
 	assert(!res->resolved);
 
-	dev = device_lookup_by_uuid(handler, file->dev);
-
+	dev = grub2_lookup_device(handler, file->dev);
 	if (!dev)
 		return false;
 
diff --git a/discover/grub2/grub2.h b/discover/grub2/grub2.h
index 8c4839b..ef32d4b 100644
--- a/discover/grub2/grub2.h
+++ b/discover/grub2/grub2.h
@@ -191,9 +191,8 @@ void script_register_function(struct grub2_script *script,
 void register_builtins(struct grub2_script *script);
 
 /* resources */
-struct resource *create_grub2_resource(struct discover_boot_option *opt,
-		struct discover_device *orig_device,
-		const char *root, const char *path);
+struct resource *create_grub2_resource(struct grub2_script *script,
+		struct discover_boot_option *opt, const char *path);
 
 bool resolve_grub2_resource(struct device_handler *handler,
 		struct resource *res);
diff --git a/test/parser/Makefile.am b/test/parser/Makefile.am
index f5985d6..0378317 100644
--- a/test/parser/Makefile.am
+++ b/test/parser/Makefile.am
@@ -32,6 +32,7 @@ parser_TESTS = \
 	test/parser/test-grub2-search-args \
 	test/parser/test-grub2-search-uuid \
 	test/parser/test-grub2-search-label \
+	test/parser/test-grub2-devpath \
 	test/parser/test-grub2-load-env \
 	test/parser/test-grub2-save-env \
 	test/parser/test-grub2-save-env-dash-f \
diff --git a/test/parser/test-grub2-devpath.c b/test/parser/test-grub2-devpath.c
new file mode 100644
index 0000000..d1d00f1
--- /dev/null
+++ b/test/parser/test-grub2-devpath.c
@@ -0,0 +1,88 @@
+/* check grub2 device+path string parsing */
+
+#include "parser-test.h"
+
+#if 0 /* PARSER_EMBEDDED_CONFIG */
+
+# local
+menuentry a {
+	linux /vmlinux
+}
+
+# local, specified by root env var
+root=00000000-0000-0000-0000-000000000001
+menuentry b {
+	linux /vmlinux
+}
+
+# remote, specified by root env var
+root=00000000-0000-0000-0000-000000000002
+menuentry c {
+	linux /vmlinux
+}
+
+# local, full dev+path spec
+menuentry d {
+	linux (00000000-0000-0000-0000-000000000001)/vmlinux
+}
+
+# remote, full dev+path spec
+menuentry e {
+	linux (00000000-0000-0000-0000-000000000002)/vmlinux
+}
+
+# invalid: incomplete dev+path spec
+menuentry f {
+	linux (00000000-0000-0000-0000-000000000001
+}
+
+# invalid: no path
+menuentry g {
+	linux (00000000-0000-0000-0000-000000000001)
+}
+
+
+#endif
+
+void run_test(struct parser_test *test)
+{
+	struct discover_device *dev1, *dev2;
+	struct discover_boot_option *opt;
+	struct discover_context *ctx;
+
+	ctx = test->ctx;
+
+	/* set local uuid */
+	dev1 = test->ctx->device;
+	dev1->uuid = "00000000-0000-0000-0000-000000000001";
+
+	dev2 = test_create_device(test, "extdev");
+	dev2->uuid = "00000000-0000-0000-0000-000000000002";
+	device_handler_add_device(ctx->handler, dev2);
+
+	test_read_conf_embedded(test, "/grub/grub.cfg");
+
+	test_run_parser(test, "grub2");
+
+	check_boot_option_count(ctx, 5);
+
+	opt = get_boot_option(ctx, 0);
+	check_name(opt, "a");
+	check_resolved_local_resource(opt->boot_image, dev1, "/vmlinux");
+
+	opt = get_boot_option(ctx, 1);
+	check_name(opt, "b");
+	check_resolved_local_resource(opt->boot_image, dev1, "/vmlinux");
+
+	opt = get_boot_option(ctx, 2);
+	check_name(opt, "c");
+	check_resolved_local_resource(opt->boot_image, dev2, "/vmlinux");
+
+	opt = get_boot_option(ctx, 3);
+	check_name(opt, "d");
+	check_resolved_local_resource(opt->boot_image, dev1, "/vmlinux");
+
+	opt = get_boot_option(ctx, 4);
+	check_name(opt, "e");
+	check_resolved_local_resource(opt->boot_image, dev2, "/vmlinux");
+}
-- 
2.20.1



More information about the Petitboot mailing list