[PATCH 12/14] discover/grub2: implement 'source' command

Samuel Mendoza-Jonas sam at mendozajonas.com
Mon Dec 2 16:49:15 AEDT 2019


On Wed, 2019-11-20 at 10:43 +0800, Jeremy Kerr wrote:
> This change add support for the grub2 'source' command, executing a
> referenced script in the current parse context.
> 
> We impose a limit of 10 (concurrent) source commands, to prevent
> infinite recursion.
> 
> Signed-off-by: Jeremy Kerr <jk at ozlabs.org>
> ---
>  discover/grub2/builtins.c                     | 51 +++++++++++++++-
>  discover/grub2/grub2.h                        |  1 +
>  test/parser/Makefile.am                       |  4 ++
>  test/parser/test-grub2-source-functions.c     | 46 +++++++++++++++
>  .../test-grub2-source-recursion-infinite.c    | 43 ++++++++++++++
>  test/parser/test-grub2-source-recursion.c     | 58
> +++++++++++++++++++
>  test/parser/test-grub2-source.c               | 54 +++++++++++++++++
>  7 files changed, 256 insertions(+), 1 deletion(-)
>  create mode 100644 test/parser/test-grub2-source-functions.c
>  create mode 100644 test/parser/test-grub2-source-recursion-
> infinite.c
>  create mode 100644 test/parser/test-grub2-source-recursion.c
>  create mode 100644 test/parser/test-grub2-source.c
> 
> diff --git a/discover/grub2/builtins.c b/discover/grub2/builtins.c
> index c726216..ab1407a 100644
> --- a/discover/grub2/builtins.c
> +++ b/discover/grub2/builtins.c
> @@ -401,6 +401,51 @@ static int builtin_test(struct grub2_script
> *script,
>  	return rc ? 0 : 1;
>  }
>  
> +static int builtin_source(struct grub2_script *script,
> +		void *data __attribute__((unused)),
> +		int argc, char *argv[])
> +{
> +	struct grub2_statements *statements;
> +	struct discover_device *dev;
> +	const char *filename;
> +	char *path, *buf;
> +	int rc, len;
> +
> +	if (argc != 2)
> +		return false;
> +
> +	/* limit script recursion */
> +	if (script->include_depth >= 10)
> +		return false;
> +
> +	rc = parse_to_device_path(script, argv[1], &dev, &path);
> +	if (rc)
> +		return false;
> +
> +	rc = parser_request_file(script->ctx, dev, path, &buf, &len);
> +	if (rc)
> +		return false;
> +
> +	/* save current script state */
> +	statements = script->statements;
> +	filename = script->filename;
> +	script->include_depth++;
> +
> +	rc = grub2_parser_parse(script->parser, argv[1], buf, len);
> +
> +	if (!rc)
> +		statements_execute(script, script->statements);
> +
> +	talloc_free(script->statements);
> +
> +	/* restore state */
> +	script->statements = statements;
> +	script->filename = filename;
> +	script->include_depth--;
> +
> +	return !rc;
> +}
> +
>  static int builtin_true(struct grub2_script *script
> __attribute__((unused)),
>  		void *data __attribute__((unused)),
>  		int argc __attribute__((unused)),
> @@ -491,7 +536,11 @@ static struct {
>  	{
>  		.name = "blscfg",
>  		.fn = builtin_blscfg,
> -	}
> +	},
> +	{
> +		.name = "source",
> +		.fn = builtin_source,
> +	},
>  };
>  
>  static const char *nops[] = {
> diff --git a/discover/grub2/grub2.h b/discover/grub2/grub2.h
> index deaf976..75f6aa0 100644
> --- a/discover/grub2/grub2.h
> +++ b/discover/grub2/grub2.h
> @@ -100,6 +100,7 @@ struct grub2_script {
>  	const char			*filename;
>  	unsigned int			n_options;
>  	struct list			options;
> +	int				include_depth;
>  };
>  
>  struct grub2_parser {
> diff --git a/test/parser/Makefile.am b/test/parser/Makefile.am
> index c8e059b..5f1a93b 100644
> --- a/test/parser/Makefile.am
> +++ b/test/parser/Makefile.am
> @@ -46,6 +46,10 @@ parser_TESTS = \
>  	test/parser/test-grub2-lexer-error \
>  	test/parser/test-grub2-parser-error \
>  	test/parser/test-grub2-test-file-ops \
> +	test/parser/test-grub2-source \
> +	test/parser/test-grub2-source-functions \
> +	test/parser/test-grub2-source-recursion \
> +	test/parser/test-grub2-source-recursion-infinite \
>  	test/parser/test-grub2-single-yocto \
>  	test/parser/test-grub2-blscfg-default-filename \
>  	test/parser/test-grub2-blscfg-default-index \
> diff --git a/test/parser/test-grub2-source-functions.c
> b/test/parser/test-grub2-source-functions.c
> new file mode 100644
> index 0000000..a9da934
> --- /dev/null
> +++ b/test/parser/test-grub2-source-functions.c
> @@ -0,0 +1,46 @@
> +
> +/* check that we can source other scripts, and functions can be
> defined
> + * and called across sourced scripts */
> +
> +#include "parser-test.h"
> +
> +#if 0 /* PARSER_EMBEDDED_CONFIG */
> +
> +function f1 {
> +	menuentry "f1$1" { linux $2 }
> +}
> +
> +source /grub/2.cfg
> +
> +f2 a /vmlinux
> +
> +#endif
> +
> +void run_test(struct parser_test *test)
> +{
> +	struct discover_boot_option *opt;
> +	struct discover_context *ctx;
> +	struct discover_device *dev;
> +
> +	ctx = test->ctx;
> +	dev = ctx->device;
> +
> +	test_read_conf_embedded(test, "/grub/grub.cfg");
> +
> +	test_add_file_string(test, dev,
> +			"/grub/2.cfg",
> +			"function f2 { menuentry \"f2$1\" { linux $2 }
> }\n"
> +			"f1 a /vmlinux\n");
> +
> +	test_run_parser(test, "grub2");
> +
> +	check_boot_option_count(ctx, 2);
> +
> +	opt = get_boot_option(ctx, 0);
> +	check_name(opt, "f1a");
> +	check_resolved_local_resource(opt->boot_image, dev,
> "/vmlinux");
> +
> +	opt = get_boot_option(ctx, 1);
> +	check_name(opt, "f2a");
> +	check_resolved_local_resource(opt->boot_image, dev,
> "/vmlinux");
> +}
> diff --git a/test/parser/test-grub2-source-recursion-infinite.c
> b/test/parser/test-grub2-source-recursion-infinite.c
> new file mode 100644
> index 0000000..fbcc5a3
> --- /dev/null
> +++ b/test/parser/test-grub2-source-recursion-infinite.c
> @@ -0,0 +1,43 @@
> +
> +/* check that have a maximum source recursion limit */
> +
> +#include "parser-test.h"
> +
> +#if 0 /* PARSER_EMBEDDED_CONFIG */
> +
> +name=a$name
> +
> +menuentry $name {
> +	linux /a
> +}
> +
> +source /grub/grub.cfg
> +
> +#endif
> +
> +void run_test(struct parser_test *test)
> +{
> +	struct discover_boot_option *opt;
> +	struct discover_context *ctx;
> +	struct discover_device *dev;
> +
> +	ctx = test->ctx;
> +	dev = ctx->device;
> +
> +	test_read_conf_embedded(test, "/grub/grub.cfg");
> +
> +	test_run_parser(test, "grub2");
> +
> +	/* we error out after 10 levels, but we should still have
> +	 * parse results up to that point
> +	 */
> +	check_boot_option_count(ctx, 11);
> +
> +	opt = get_boot_option(ctx, 0);
> +	check_name(opt, "a");
> +	check_resolved_local_resource(opt->boot_image, dev, "/a");
> +
> +	opt = get_boot_option(ctx,10);
> +	check_name(opt, "aaaaaaaaaaa");

AAAAAAAAAA

> +	check_resolved_local_resource(opt->boot_image, dev, "/a");
> +}
> diff --git a/test/parser/test-grub2-source-recursion.c
> b/test/parser/test-grub2-source-recursion.c
> new file mode 100644
> index 0000000..21b6bd2
> --- /dev/null
> +++ b/test/parser/test-grub2-source-recursion.c
> @@ -0,0 +1,58 @@
> +/* check that we can source other files recursively */
> +
> +#include "parser-test.h"
> +
> +#if 0 /* PARSER_EMBEDDED_CONFIG */
> +
> +menuentry a {
> +	linux /a
> +}
> +
> +source /grub/2.cfg
> +
> +menuentry c {
> +	linux /c
> +}
> +
> +#endif
> +
> +void run_test(struct parser_test *test)
> +{
> +	struct discover_boot_option *opt;
> +	struct discover_context *ctx;
> +	struct discover_device *dev;
> +
> +	ctx = test->ctx;
> +	dev = ctx->device;
> +
> +	test_read_conf_embedded(test, "/grub/grub.cfg");
> +
> +	/* four levels of config files, the last defining a boot option
> */
> +	test_add_file_string(test, dev,
> +			"/grub/2.cfg",
> +			"source /grub/3.cfg\n");
> +
> +	test_add_file_string(test, dev,
> +			"/grub/3.cfg",
> +			"source /grub/4.cfg\n");
> +
> +	test_add_file_string(test, dev,
> +			"/grub/4.cfg",
> +			"menuentry b { linux /b }\n");
> +
> +	test_run_parser(test, "grub2");
> +
> +	check_boot_option_count(ctx, 3);
> +
> +	opt = get_boot_option(ctx, 0);
> +	check_name(opt, "a");
> +	check_resolved_local_resource(opt->boot_image, dev, "/a");
> +
> +	opt = get_boot_option(ctx, 1);
> +	check_name(opt, "b");
> +	check_resolved_local_resource(opt->boot_image, dev, "/b");
> +
> +	opt = get_boot_option(ctx, 2);
> +	check_name(opt, "c");
> +	check_resolved_local_resource(opt->boot_image, dev, "/c");
> +}
> diff --git a/test/parser/test-grub2-source.c b/test/parser/test-
> grub2-source.c
> new file mode 100644
> index 0000000..a14bef7
> --- /dev/null
> +++ b/test/parser/test-grub2-source.c
> @@ -0,0 +1,54 @@
> +
> +/* check that we can source other scripts, and variables get passed
> + * in to and out of sourced scripts */
> +
> +#include "parser-test.h"
> +
> +#if 0 /* PARSER_EMBEDDED_CONFIG */
> +
> +menuentry a {
> +	linux /a
> +}
> +
> +# var: outer -> inner -> outer
> +v=b
> +
> +source /grub/2.cfg
> +
> +menuentry $v {
> +	linux /c
> +}
> +
> +#endif
> +
> +void run_test(struct parser_test *test)
> +{
> +	struct discover_boot_option *opt;
> +	struct discover_context *ctx;
> +	struct discover_device *dev;
> +
> +	ctx = test->ctx;
> +	dev = ctx->device;
> +
> +	test_read_conf_embedded(test, "/grub/grub.cfg");
> +
> +	test_add_file_string(test, dev,
> +			"/grub/2.cfg",
> +			"menuentry $v { linux /b }\nv=c\n");
> +
> +	test_run_parser(test, "grub2");
> +
> +	check_boot_option_count(ctx, 3);
> +
> +	opt = get_boot_option(ctx, 0);
> +	check_name(opt, "a");
> +	check_resolved_local_resource(opt->boot_image, dev, "/a");
> +
> +	opt = get_boot_option(ctx, 1);
> +	check_name(opt, "b");
> +	check_resolved_local_resource(opt->boot_image, dev, "/b");
> +
> +	opt = get_boot_option(ctx, 2);
> +	check_name(opt, "c");
> +	check_resolved_local_resource(opt->boot_image, dev, "/c");
> +}



More information about the Petitboot mailing list