diff options
Diffstat (limited to 'discover/grub2')
-rw-r--r-- | discover/grub2/blscfg.c | 19 | ||||
-rw-r--r-- | discover/grub2/builtins.c | 208 | ||||
-rw-r--r-- | discover/grub2/env.c | 2 | ||||
-rw-r--r-- | discover/grub2/grub2-parser.y | 17 | ||||
-rw-r--r-- | discover/grub2/grub2.c | 110 | ||||
-rw-r--r-- | discover/grub2/grub2.h | 32 | ||||
-rw-r--r-- | discover/grub2/script.c | 29 |
7 files changed, 337 insertions, 80 deletions
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 7e92299..ab1407a 100644 --- a/discover/grub2/builtins.c +++ b/discover/grub2/builtins.c @@ -1,4 +1,7 @@ +#define _GNU_SOURCE + +#include <getopt.h> #include <stdio.h> #include <string.h> @@ -43,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) { @@ -58,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) @@ -74,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; } @@ -85,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 " @@ -99,35 +97,128 @@ 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; } +static const struct option search_options[] = { + { + .name = "set", + .has_arg = required_argument, + .val = 's', + }, + { + .name = "file", + .has_arg = no_argument, + .val = 'f', + }, + { + .name = "label", + .has_arg = no_argument, + .val = 'l', + }, + { + .name = "fs-uuid", + .has_arg = no_argument, + .val = 'u', + }, + { 0 }, +}; + static int builtin_search(struct grub2_script *script, void *data __attribute__((unused)), int argc, char *argv[]) { - const char *env_var, *spec; - int i; - - env_var = NULL; + const char *env_var, *spec, *res; + struct discover_device *dev; + enum { + LOOKUP_UUID = 'u', + LOOKUP_LABEL = 'l', + LOOKUP_FILE = 'f', + } lookup_type; + + env_var = "root"; + optind = 0; + + /* Default to UUID, for backwards compat with earlier petitboot + * versions. This argument is non-optional in GRUB. */ + lookup_type = LOOKUP_UUID; + + for (;;) { + int c = getopt_long(argc, argv, ":flu", search_options, NULL); + if (c == -1) + break; - for (i = 1; i < argc - 1; i++) { - if (!strncmp(argv[i], "--set=", strlen("--set="))) { - env_var = argv[i] + strlen("--set="); + switch (c) { + case 's': + env_var = optarg; + break; + case LOOKUP_UUID: + case LOOKUP_LABEL: + case LOOKUP_FILE: + lookup_type = c; + break; + case '?': + case ':': break; } } - if (!env_var) + if (!strlen(env_var)) return 0; - spec = argv[argc - 1]; + if (optind >= argc) + return -1; + + spec = argv[optind]; + res = NULL; + + switch (lookup_type) { + case LOOKUP_UUID: + dev = device_lookup_by_uuid(script->ctx->handler, + spec); + res = dev ? dev->device->id : spec; + break; + case LOOKUP_LABEL: + dev = device_lookup_by_label(script->ctx->handler, + spec); + if (dev) + res = dev->device->id; + break; + case LOOKUP_FILE: + /* not yet implemented */ + break; + } + + if (res) + script_env_set(script, env_var, res); + + return 0; +} + +static int parse_to_device_path(struct grub2_script *script, + const char *desc, struct discover_device **devp, + char **pathp) +{ + struct discover_device *dev; + struct grub2_file *file; + + file = grub2_parse_file(script, desc); + if (!file) + return -1; + + dev = script->ctx->device; + if (file->dev) + dev = grub2_lookup_device(script->ctx->handler, file->dev); + + if (!dev) + return -1; + + *devp = dev; + *pathp = talloc_strdup(script, file->path); - script_env_set(script, env_var, spec); + talloc_free(file); return 0; } @@ -139,12 +230,17 @@ static int builtin_search(struct grub2_script *script, static bool builtin_test_op_file(struct grub2_script *script, char op, const char *file) { + struct discover_device *dev; + struct stat statbuf; bool result; + char *path; int rc; - struct stat statbuf; - rc = parser_stat_path(script->ctx, script->ctx->device, - file, &statbuf); + rc = parse_to_device_path(script, file, &dev, &path); + if (rc) + return false; + + rc = parser_stat_path(script->ctx, dev, path, &statbuf); if (rc) return false; @@ -172,16 +268,21 @@ static bool builtin_test_op_file(struct grub2_script *script, char op, static bool builtin_test_op_dir(struct grub2_script *script, char op, const char *dir) { - int rc; + struct discover_device *dev; struct stat statbuf; + char *path; + int rc; if (op != 'd') return false; - rc = parser_stat_path(script->ctx, script->ctx->device, dir, &statbuf); - if (rc) { + rc = parse_to_device_path(script, dir, &dev, &path); + if (rc) + return false; + + rc = parser_stat_path(script->ctx, dev, path, &statbuf); + if (rc) return false; - } return S_ISDIR(statbuf.st_mode); } @@ -300,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)), @@ -390,7 +536,11 @@ static struct { { .name = "blscfg", .fn = builtin_blscfg, - } + }, + { + .name = "source", + .fn = builtin_source, + }, }; static const char *nops[] = { diff --git a/discover/grub2/env.c b/discover/grub2/env.c index 7eda095..74d5729 100644 --- a/discover/grub2/env.c +++ b/discover/grub2/env.c @@ -86,6 +86,8 @@ int builtin_load_env(struct grub2_script *script, if (!rc) { rc = parse_buf_to_env(script, buf, len); + if (rc) + pb_debug_fn("Failed to set env\n"); talloc_free(buf); } diff --git a/discover/grub2/grub2-parser.y b/discover/grub2/grub2-parser.y index 527a61c..f99bbfd 100644 --- a/discover/grub2/grub2-parser.y +++ b/discover/grub2/grub2-parser.y @@ -331,14 +331,15 @@ struct grub2_parser *grub2_parser_create(struct discover_context *ctx) return parser; } -void grub2_parser_parse(struct grub2_parser *parser, const char *filename, +/* performs a parse on buf, setting parser->script->statements */ +int grub2_parser_parse(struct grub2_parser *parser, const char *filename, char *buf, int len) { YY_BUFFER_STATE bufstate; int rc; if (!len) - return; + return -1; parser->script->filename = filename; @@ -349,6 +350,18 @@ void grub2_parser_parse(struct grub2_parser *parser, const char *filename, yy_delete_buffer(bufstate, parser->scanner); + parser->inter_word = false; + + return rc; +} + +void grub2_parser_parse_and_execute(struct grub2_parser *parser, + const char *filename, char *buf, int len) +{ + int rc; + + rc = grub2_parser_parse(parser, filename, buf, len); + if (!rc) script_execute(parser->script); } diff --git a/discover/grub2/grub2.c b/discover/grub2/grub2.c index f62ccdd..b176ce2 100644 --- a/discover/grub2/grub2.c +++ b/discover/grub2/grub2.c @@ -33,18 +33,35 @@ static const char *const grub2_conf_files[] = { NULL }; -struct grub2_resource_info { - char *root; - char *path; -}; +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 grub2_resource_info *info; + 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); @@ -52,19 +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) { - info = talloc(res, struct grub2_resource_info); - talloc_reference(info, root); - info->root = talloc_strdup(info, root); - info->path = talloc_strdup(info, path); + if (!file->dev && root && strlen(root)) + file->dev = talloc_strdup(file, root); - res->resolved = false; - res->info = info; + /* 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; } @@ -72,22 +99,59 @@ struct resource *create_grub2_resource(struct discover_boot_option *opt, bool resolve_grub2_resource(struct device_handler *handler, struct resource *res) { - struct grub2_resource_info *info = res->info; + struct grub2_file *file = res->info; struct discover_device *dev; assert(!res->resolved); - dev = device_lookup_by_uuid(handler, info->root); - + dev = grub2_lookup_device(handler, file->dev); if (!dev) return false; - resolve_resource_against_device(res, dev, info->path); - talloc_free(info); + resolve_resource_against_device(res, dev, file->path); + talloc_free(file); return true; } +struct grub2_file *grub2_parse_file(struct grub2_script *script, + const char *str) +{ + struct grub2_file *file; + size_t dev_len; + char *pos; + + if (!str) + return NULL; + + file = talloc_zero(script, struct grub2_file); + + if (*str != '(') { + /* just a path - no device, return path as-is */ + file->path = talloc_strdup(file, str); + + } else { + /* device plus path - split into components */ + + pos = strchr(str, ')'); + + /* no closing bracket, or zero-length path? */ + if (!pos || *(pos+1) == '\0') { + talloc_free(file); + return NULL; + } + + file->path = talloc_strdup(file, pos + 1); + + dev_len = pos - str - 1; + if (dev_len) + file->dev = talloc_strndup(file, str + 1, dev_len); + } + + return file; +} + + static int grub2_parse(struct discover_context *dc) { const char * const *filename; @@ -105,7 +169,7 @@ static int grub2_parse(struct discover_context *dc) continue; parser = grub2_parser_create(dc); - grub2_parser_parse(parser, *filename, buf, len); + grub2_parser_parse_and_execute(parser, *filename, buf, len); device_handler_status_dev_info(dc->handler, dc->device, _("Parsed GRUB configuration from %s"), *filename); diff --git a/discover/grub2/grub2.h b/discover/grub2/grub2.h index 68176fb..75f6aa0 100644 --- a/discover/grub2/grub2.h +++ b/discover/grub2/grub2.h @@ -91,6 +91,7 @@ struct grub2_statement_for { }; struct grub2_script { + struct grub2_parser *parser; struct grub2_statements *statements; struct list environment; struct list symtab; @@ -99,6 +100,7 @@ struct grub2_script { const char *filename; unsigned int n_options; struct list options; + int include_depth; }; struct grub2_parser { @@ -107,6 +109,14 @@ struct grub2_parser { bool inter_word; }; +/* References to files in grub2 consist of an optional device and a path + * (specified here by UUID). If the dev is unspecified, we fall back to a + * default - usually the 'root' environment variable. */ +struct grub2_file { + char *dev; + char *path; +}; + /* type for builtin functions */ typedef int (*grub2_function)(struct grub2_script *script, void *data, int argc, char *argv[]); @@ -156,6 +166,9 @@ void word_append(struct grub2_word *w1, struct grub2_word *w2); /* script interface */ void script_execute(struct grub2_script *script); +int statements_execute(struct grub2_script *script, + struct grub2_statements *stmts); + int statement_simple_execute(struct grub2_script *script, struct grub2_statement *statement); int statement_block_execute(struct grub2_script *script, @@ -183,16 +196,25 @@ 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); +/* grub-style device+path parsing */ +struct grub2_file *grub2_parse_file(struct grub2_script *script, + const char *str); +struct discover_device *grub2_lookup_device(struct device_handler *handler, + const char *desc); + +/* internal parse api */ +int grub2_parser_parse(struct grub2_parser *parser, const char *filename, + char *buf, int len); + /* external parser api */ struct grub2_parser *grub2_parser_create(struct discover_context *ctx); -void grub2_parser_parse(struct grub2_parser *parser, const char *filename, - char *buf, int len); +void grub2_parser_parse_and_execute(struct grub2_parser *parser, + const char *filename, char *buf, int len); #endif /* GRUB2_H */ diff --git a/discover/grub2/script.c b/discover/grub2/script.c index 1a802b9..14931f9 100644 --- a/discover/grub2/script.c +++ b/discover/grub2/script.c @@ -117,12 +117,10 @@ static bool option_is_default(struct grub2_script *script, if (end != var && *end == '\0') return default_idx == script->n_options; - /* if we don't have an explicit id for this option, fall back to - * the name */ - if (!id) - id = opt->option->name; + if (id && !strcmp(id, var)) + return true; - return !strcmp(id, var); + return !strcmp(opt->option->name, var); } static void append_text_to_current_arg(struct grub2_argv *argv, @@ -227,11 +225,11 @@ static void process_expansions(struct grub2_script *script, } /* we may have allocated an extra argv element but not populated it */ - if (!argv->argv[argv->argc - 1]) + if (argv->argv && !argv->argv[argv->argc - 1]) argv->argc--; } -static int statements_execute(struct grub2_script *script, +int statements_execute(struct grub2_script *script, struct grub2_statements *stmts) { struct grub2_statement *stmt; @@ -341,9 +339,16 @@ int statement_menuentry_execute(struct grub2_script *script, * implementation to get --id= working. */ for (i = 1; i < st->argv->argc; ++i) { - if (strncmp("--id=", st->argv->argv[i], 5) == 0) { - id = st->argv->argv[i] + 5; - break; + if (strncmp("--id", st->argv->argv[i], strlen("--id")) == 0) { + if (strlen(st->argv->argv[i]) > strlen("--id=")) { + id = st->argv->argv[i] + strlen("--id="); + break; + } + + if (i + 1 < st->argv->argc) { + id = st->argv->argv[i + 1]; + break; + } } } if (st->argv->argc > 0) @@ -489,6 +494,9 @@ void script_execute(struct grub2_script *script) { struct discover_boot_option *opt, *tmp; + if (!script) + return; + init_env(script); statements_execute(script, script->statements); @@ -510,6 +518,7 @@ struct grub2_script *create_script(struct grub2_parser *parser, script = talloc_zero(parser, struct grub2_script); script->ctx = ctx; + script->parser = parser; list_init(&script->symtab); list_init(&script->options); |