blob: e9f55ed8848f0213cac109c1f77e0ab8edcaad17 [file] [log] [blame]
Klaus Heinrich Kiwid1cd8c52020-02-27 12:43:47 -03001From 772139fd8f8d8b290a5c0a320d88a896b628624b Mon Sep 17 00:00:00 2001
2From: Jeremy Kerr <jk@ozlabs.org>
3Date: Thu, 14 Nov 2019 15:06:26 +0800
4Subject: [PATCH 15/18] discover/grub2: implement 'source' command
5
6This change add support for the grub2 'source' command, executing a
7referenced script in the current parse context.
8
9We impose a limit of 10 (concurrent) source commands, to prevent
10infinite recursion.
11
12Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
13(cherry picked from commit 967cfa7e5c1bfb4d2cf78bb3de3dc6d36b78c440)
14Signed-off-by: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com>
15---
16 discover/grub2/builtins.c | 51 +++++++++++++++-
17 discover/grub2/grub2.h | 1 +
18 test/parser/Makefile.am | 4 ++
19 test/parser/test-grub2-source-functions.c | 46 +++++++++++++++
20 .../test-grub2-source-recursion-infinite.c | 43 ++++++++++++++
21 test/parser/test-grub2-source-recursion.c | 58 +++++++++++++++++++
22 test/parser/test-grub2-source.c | 54 +++++++++++++++++
23 7 files changed, 256 insertions(+), 1 deletion(-)
24 create mode 100644 test/parser/test-grub2-source-functions.c
25 create mode 100644 test/parser/test-grub2-source-recursion-infinite.c
26 create mode 100644 test/parser/test-grub2-source-recursion.c
27 create mode 100644 test/parser/test-grub2-source.c
28
29diff --git a/discover/grub2/builtins.c b/discover/grub2/builtins.c
30index c726216..ab1407a 100644
31--- a/discover/grub2/builtins.c
32+++ b/discover/grub2/builtins.c
33@@ -401,6 +401,51 @@ static int builtin_test(struct grub2_script *script,
34 return rc ? 0 : 1;
35 }
36
37+static int builtin_source(struct grub2_script *script,
38+ void *data __attribute__((unused)),
39+ int argc, char *argv[])
40+{
41+ struct grub2_statements *statements;
42+ struct discover_device *dev;
43+ const char *filename;
44+ char *path, *buf;
45+ int rc, len;
46+
47+ if (argc != 2)
48+ return false;
49+
50+ /* limit script recursion */
51+ if (script->include_depth >= 10)
52+ return false;
53+
54+ rc = parse_to_device_path(script, argv[1], &dev, &path);
55+ if (rc)
56+ return false;
57+
58+ rc = parser_request_file(script->ctx, dev, path, &buf, &len);
59+ if (rc)
60+ return false;
61+
62+ /* save current script state */
63+ statements = script->statements;
64+ filename = script->filename;
65+ script->include_depth++;
66+
67+ rc = grub2_parser_parse(script->parser, argv[1], buf, len);
68+
69+ if (!rc)
70+ statements_execute(script, script->statements);
71+
72+ talloc_free(script->statements);
73+
74+ /* restore state */
75+ script->statements = statements;
76+ script->filename = filename;
77+ script->include_depth--;
78+
79+ return !rc;
80+}
81+
82 static int builtin_true(struct grub2_script *script __attribute__((unused)),
83 void *data __attribute__((unused)),
84 int argc __attribute__((unused)),
85@@ -491,7 +536,11 @@ static struct {
86 {
87 .name = "blscfg",
88 .fn = builtin_blscfg,
89- }
90+ },
91+ {
92+ .name = "source",
93+ .fn = builtin_source,
94+ },
95 };
96
97 static const char *nops[] = {
98diff --git a/discover/grub2/grub2.h b/discover/grub2/grub2.h
99index deaf976..75f6aa0 100644
100--- a/discover/grub2/grub2.h
101+++ b/discover/grub2/grub2.h
102@@ -100,6 +100,7 @@ struct grub2_script {
103 const char *filename;
104 unsigned int n_options;
105 struct list options;
106+ int include_depth;
107 };
108
109 struct grub2_parser {
110diff --git a/test/parser/Makefile.am b/test/parser/Makefile.am
111index c8e059b..5f1a93b 100644
112--- a/test/parser/Makefile.am
113+++ b/test/parser/Makefile.am
114@@ -46,6 +46,10 @@ parser_TESTS = \
115 test/parser/test-grub2-lexer-error \
116 test/parser/test-grub2-parser-error \
117 test/parser/test-grub2-test-file-ops \
118+ test/parser/test-grub2-source \
119+ test/parser/test-grub2-source-functions \
120+ test/parser/test-grub2-source-recursion \
121+ test/parser/test-grub2-source-recursion-infinite \
122 test/parser/test-grub2-single-yocto \
123 test/parser/test-grub2-blscfg-default-filename \
124 test/parser/test-grub2-blscfg-default-index \
125diff --git a/test/parser/test-grub2-source-functions.c b/test/parser/test-grub2-source-functions.c
126new file mode 100644
127index 0000000..a9da934
128--- /dev/null
129+++ b/test/parser/test-grub2-source-functions.c
130@@ -0,0 +1,46 @@
131+
132+/* check that we can source other scripts, and functions can be defined
133+ * and called across sourced scripts */
134+
135+#include "parser-test.h"
136+
137+#if 0 /* PARSER_EMBEDDED_CONFIG */
138+
139+function f1 {
140+ menuentry "f1$1" { linux $2 }
141+}
142+
143+source /grub/2.cfg
144+
145+f2 a /vmlinux
146+
147+#endif
148+
149+void run_test(struct parser_test *test)
150+{
151+ struct discover_boot_option *opt;
152+ struct discover_context *ctx;
153+ struct discover_device *dev;
154+
155+ ctx = test->ctx;
156+ dev = ctx->device;
157+
158+ test_read_conf_embedded(test, "/grub/grub.cfg");
159+
160+ test_add_file_string(test, dev,
161+ "/grub/2.cfg",
162+ "function f2 { menuentry \"f2$1\" { linux $2 } }\n"
163+ "f1 a /vmlinux\n");
164+
165+ test_run_parser(test, "grub2");
166+
167+ check_boot_option_count(ctx, 2);
168+
169+ opt = get_boot_option(ctx, 0);
170+ check_name(opt, "f1a");
171+ check_resolved_local_resource(opt->boot_image, dev, "/vmlinux");
172+
173+ opt = get_boot_option(ctx, 1);
174+ check_name(opt, "f2a");
175+ check_resolved_local_resource(opt->boot_image, dev, "/vmlinux");
176+}
177diff --git a/test/parser/test-grub2-source-recursion-infinite.c b/test/parser/test-grub2-source-recursion-infinite.c
178new file mode 100644
179index 0000000..fbcc5a3
180--- /dev/null
181+++ b/test/parser/test-grub2-source-recursion-infinite.c
182@@ -0,0 +1,43 @@
183+
184+/* check that have a maximum source recursion limit */
185+
186+#include "parser-test.h"
187+
188+#if 0 /* PARSER_EMBEDDED_CONFIG */
189+
190+name=a$name
191+
192+menuentry $name {
193+ linux /a
194+}
195+
196+source /grub/grub.cfg
197+
198+#endif
199+
200+void run_test(struct parser_test *test)
201+{
202+ struct discover_boot_option *opt;
203+ struct discover_context *ctx;
204+ struct discover_device *dev;
205+
206+ ctx = test->ctx;
207+ dev = ctx->device;
208+
209+ test_read_conf_embedded(test, "/grub/grub.cfg");
210+
211+ test_run_parser(test, "grub2");
212+
213+ /* we error out after 10 levels, but we should still have
214+ * parse results up to that point
215+ */
216+ check_boot_option_count(ctx, 11);
217+
218+ opt = get_boot_option(ctx, 0);
219+ check_name(opt, "a");
220+ check_resolved_local_resource(opt->boot_image, dev, "/a");
221+
222+ opt = get_boot_option(ctx,10);
223+ check_name(opt, "aaaaaaaaaaa");
224+ check_resolved_local_resource(opt->boot_image, dev, "/a");
225+}
226diff --git a/test/parser/test-grub2-source-recursion.c b/test/parser/test-grub2-source-recursion.c
227new file mode 100644
228index 0000000..21b6bd2
229--- /dev/null
230+++ b/test/parser/test-grub2-source-recursion.c
231@@ -0,0 +1,58 @@
232+/* check that we can source other files recursively */
233+
234+#include "parser-test.h"
235+
236+#if 0 /* PARSER_EMBEDDED_CONFIG */
237+
238+menuentry a {
239+ linux /a
240+}
241+
242+source /grub/2.cfg
243+
244+menuentry c {
245+ linux /c
246+}
247+
248+#endif
249+
250+void run_test(struct parser_test *test)
251+{
252+ struct discover_boot_option *opt;
253+ struct discover_context *ctx;
254+ struct discover_device *dev;
255+
256+ ctx = test->ctx;
257+ dev = ctx->device;
258+
259+ test_read_conf_embedded(test, "/grub/grub.cfg");
260+
261+ /* four levels of config files, the last defining a boot option */
262+ test_add_file_string(test, dev,
263+ "/grub/2.cfg",
264+ "source /grub/3.cfg\n");
265+
266+ test_add_file_string(test, dev,
267+ "/grub/3.cfg",
268+ "source /grub/4.cfg\n");
269+
270+ test_add_file_string(test, dev,
271+ "/grub/4.cfg",
272+ "menuentry b { linux /b }\n");
273+
274+ test_run_parser(test, "grub2");
275+
276+ check_boot_option_count(ctx, 3);
277+
278+ opt = get_boot_option(ctx, 0);
279+ check_name(opt, "a");
280+ check_resolved_local_resource(opt->boot_image, dev, "/a");
281+
282+ opt = get_boot_option(ctx, 1);
283+ check_name(opt, "b");
284+ check_resolved_local_resource(opt->boot_image, dev, "/b");
285+
286+ opt = get_boot_option(ctx, 2);
287+ check_name(opt, "c");
288+ check_resolved_local_resource(opt->boot_image, dev, "/c");
289+}
290diff --git a/test/parser/test-grub2-source.c b/test/parser/test-grub2-source.c
291new file mode 100644
292index 0000000..a14bef7
293--- /dev/null
294+++ b/test/parser/test-grub2-source.c
295@@ -0,0 +1,54 @@
296+
297+/* check that we can source other scripts, and variables get passed
298+ * in to and out of sourced scripts */
299+
300+#include "parser-test.h"
301+
302+#if 0 /* PARSER_EMBEDDED_CONFIG */
303+
304+menuentry a {
305+ linux /a
306+}
307+
308+# var: outer -> inner -> outer
309+v=b
310+
311+source /grub/2.cfg
312+
313+menuentry $v {
314+ linux /c
315+}
316+
317+#endif
318+
319+void run_test(struct parser_test *test)
320+{
321+ struct discover_boot_option *opt;
322+ struct discover_context *ctx;
323+ struct discover_device *dev;
324+
325+ ctx = test->ctx;
326+ dev = ctx->device;
327+
328+ test_read_conf_embedded(test, "/grub/grub.cfg");
329+
330+ test_add_file_string(test, dev,
331+ "/grub/2.cfg",
332+ "menuentry $v { linux /b }\nv=c\n");
333+
334+ test_run_parser(test, "grub2");
335+
336+ check_boot_option_count(ctx, 3);
337+
338+ opt = get_boot_option(ctx, 0);
339+ check_name(opt, "a");
340+ check_resolved_local_resource(opt->boot_image, dev, "/a");
341+
342+ opt = get_boot_option(ctx, 1);
343+ check_name(opt, "b");
344+ check_resolved_local_resource(opt->boot_image, dev, "/b");
345+
346+ opt = get_boot_option(ctx, 2);
347+ check_name(opt, "c");
348+ check_resolved_local_resource(opt->boot_image, dev, "/c");
349+}
350--
3512.17.1
352