Skip to content

Commit

Permalink
discover/grub2: implement 'source' command
Browse files Browse the repository at this point in the history
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@ozlabs.org>
  • Loading branch information
jk-ozlabs committed Nov 29, 2019
1 parent 9711179 commit 967cfa7
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 1 deletion.
51 changes: 50 additions & 1 deletion discover/grub2/builtins.c
Expand Up @@ -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)),
Expand Down Expand Up @@ -491,7 +536,11 @@ static struct {
{
.name = "blscfg",
.fn = builtin_blscfg,
}
},
{
.name = "source",
.fn = builtin_source,
},
};

static const char *nops[] = {
Expand Down
1 change: 1 addition & 0 deletions discover/grub2/grub2.h
Expand Up @@ -100,6 +100,7 @@ struct grub2_script {
const char *filename;
unsigned int n_options;
struct list options;
int include_depth;
};

struct grub2_parser {
Expand Down
4 changes: 4 additions & 0 deletions test/parser/Makefile.am
Expand Up @@ -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 \
Expand Down
46 changes: 46 additions & 0 deletions 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");
}
43 changes: 43 additions & 0 deletions 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");
check_resolved_local_resource(opt->boot_image, dev, "/a");
}
58 changes: 58 additions & 0 deletions 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");
}
54 changes: 54 additions & 0 deletions 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");
}

0 comments on commit 967cfa7

Please sign in to comment.