Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / commands / loadenv.c
blobacd93d123ed361e5bd7ae449f5166d5659104823
1 /* loadenv.c - command to load/save environment variable. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/dl.h>
21 #include <grub/mm.h>
22 #include <grub/file.h>
23 #include <grub/disk.h>
24 #include <grub/misc.h>
25 #include <grub/env.h>
26 #include <grub/partition.h>
27 #include <grub/lib/envblk.h>
28 #include <grub/extcmd.h>
29 #include <grub/i18n.h>
31 GRUB_MOD_LICENSE ("GPLv3+");
33 static const struct grub_arg_option options[] =
35 /* TRANSLATORS: This option is used to override default filename
36 for loading and storing environment. */
37 {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
38 {"skip-sig", 's', 0,
39 N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE},
40 {0, 0, 0, 0, 0, 0}
43 /* Opens 'filename' with compression filters disabled. Optionally disables the
44 PUBKEY filter (that insists upon properly signed files) as well. PUBKEY
45 filter is restored before the function returns. */
46 static grub_file_t
47 open_envblk_file (char *filename, int untrusted)
49 grub_file_t file;
50 char *buf = 0;
52 if (! filename)
54 const char *prefix;
55 int len;
57 prefix = grub_env_get ("prefix");
58 if (! prefix)
60 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix");
61 return 0;
64 len = grub_strlen (prefix);
65 buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
66 if (! buf)
67 return 0;
68 filename = buf;
70 grub_strcpy (filename, prefix);
71 filename[len] = '/';
72 grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
75 /* The filters that are disabled will be re-enabled by the call to
76 grub_file_open() after this particular file is opened. */
77 grub_file_filter_disable_compression ();
78 if (untrusted)
79 grub_file_filter_disable_pubkey ();
81 file = grub_file_open (filename);
83 grub_free (buf);
84 return file;
87 static grub_envblk_t
88 read_envblk_file (grub_file_t file)
90 grub_off_t offset = 0;
91 char *buf;
92 grub_size_t size = grub_file_size (file);
93 grub_envblk_t envblk;
95 buf = grub_malloc (size);
96 if (! buf)
97 return 0;
99 while (size > 0)
101 grub_ssize_t ret;
103 ret = grub_file_read (file, buf + offset, size);
104 if (ret <= 0)
106 grub_free (buf);
107 return 0;
110 size -= ret;
111 offset += ret;
114 envblk = grub_envblk_open (buf, offset);
115 if (! envblk)
117 grub_free (buf);
118 grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
119 return 0;
122 return envblk;
125 struct grub_env_whitelist
127 grub_size_t len;
128 char **list;
130 typedef struct grub_env_whitelist grub_env_whitelist_t;
132 static int
133 test_whitelist_membership (const char* name,
134 const grub_env_whitelist_t* whitelist)
136 grub_size_t i;
138 for (i = 0; i < whitelist->len; i++)
139 if (grub_strcmp (name, whitelist->list[i]) == 0)
140 return 1; /* found it */
142 return 0; /* not found */
145 /* Helper for grub_cmd_load_env. */
146 static int
147 set_var (const char *name, const char *value, void *whitelist)
149 if (! whitelist)
151 grub_env_set (name, value);
152 return 0;
155 if (test_whitelist_membership (name,
156 (const grub_env_whitelist_t *) whitelist))
157 grub_env_set (name, value);
159 return 0;
162 static grub_err_t
163 grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
165 struct grub_arg_list *state = ctxt->state;
166 grub_file_t file;
167 grub_envblk_t envblk;
168 grub_env_whitelist_t whitelist;
170 whitelist.len = argc;
171 whitelist.list = args;
173 /* state[0] is the -f flag; state[1] is the --skip-sig flag */
174 file = open_envblk_file ((state[0].set) ? state[0].arg : 0, state[1].set);
175 if (! file)
176 return grub_errno;
178 envblk = read_envblk_file (file);
179 if (! envblk)
180 goto fail;
182 /* argc > 0 indicates caller provided a whitelist of variables to read. */
183 grub_envblk_iterate (envblk, argc > 0 ? &whitelist : 0, set_var);
184 grub_envblk_close (envblk);
186 fail:
187 grub_file_close (file);
188 return grub_errno;
191 /* Print all variables in current context. */
192 static int
193 print_var (const char *name, const char *value,
194 void *hook_data __attribute__ ((unused)))
196 grub_printf ("%s=%s\n", name, value);
197 return 0;
200 static grub_err_t
201 grub_cmd_list_env (grub_extcmd_context_t ctxt,
202 int argc __attribute__ ((unused)),
203 char **args __attribute__ ((unused)))
205 struct grub_arg_list *state = ctxt->state;
206 grub_file_t file;
207 grub_envblk_t envblk;
209 file = open_envblk_file ((state[0].set) ? state[0].arg : 0, 0);
210 if (! file)
211 return grub_errno;
213 envblk = read_envblk_file (file);
214 if (! envblk)
215 goto fail;
217 grub_envblk_iterate (envblk, NULL, print_var);
218 grub_envblk_close (envblk);
220 fail:
221 grub_file_close (file);
222 return grub_errno;
225 /* Used to maintain a variable length of blocklists internally. */
226 struct blocklist
228 grub_disk_addr_t sector;
229 unsigned offset;
230 unsigned length;
231 struct blocklist *next;
234 static void
235 free_blocklists (struct blocklist *p)
237 struct blocklist *q;
239 for (; p; p = q)
241 q = p->next;
242 grub_free (p);
246 static grub_err_t
247 check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
248 grub_file_t file)
250 grub_size_t total_length;
251 grub_size_t index;
252 grub_disk_t disk;
253 grub_disk_addr_t part_start;
254 struct blocklist *p;
255 char *buf;
257 /* Sanity checks. */
258 total_length = 0;
259 for (p = blocklists; p; p = p->next)
261 struct blocklist *q;
262 /* Check if any pair of blocks overlap. */
263 for (q = p->next; q; q = q->next)
265 grub_disk_addr_t s1, s2;
266 grub_disk_addr_t e1, e2;
268 s1 = p->sector;
269 e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
271 s2 = q->sector;
272 e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
274 if (s1 < e2 && s2 < e1)
276 /* This might be actually valid, but it is unbelievable that
277 any filesystem makes such a silly allocation. */
278 return grub_error (GRUB_ERR_BAD_FS, "malformed file");
282 total_length += p->length;
285 if (total_length != grub_file_size (file))
287 /* Maybe sparse, unallocated sectors. No way in GRUB. */
288 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
291 /* One more sanity check. Re-read all sectors by blocklists, and compare
292 those with the data read via a file. */
293 disk = file->device->disk;
295 part_start = grub_partition_get_start (disk->partition);
297 buf = grub_envblk_buffer (envblk);
298 char *blockbuf = NULL;
299 grub_size_t blockbuf_len = 0;
300 for (p = blocklists, index = 0; p; index += p->length, p = p->next)
302 if (p->length > blockbuf_len)
304 grub_free (blockbuf);
305 blockbuf_len = 2 * p->length;
306 blockbuf = grub_malloc (blockbuf_len);
307 if (!blockbuf)
308 return grub_errno;
311 if (grub_disk_read (disk, p->sector - part_start,
312 p->offset, p->length, blockbuf))
313 return grub_errno;
315 if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
316 return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
319 return GRUB_ERR_NONE;
322 static int
323 write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
324 grub_file_t file)
326 char *buf;
327 grub_disk_t disk;
328 grub_disk_addr_t part_start;
329 struct blocklist *p;
330 grub_size_t index;
332 buf = grub_envblk_buffer (envblk);
333 disk = file->device->disk;
334 part_start = grub_partition_get_start (disk->partition);
336 index = 0;
337 for (p = blocklists; p; index += p->length, p = p->next)
339 if (grub_disk_write (disk, p->sector - part_start,
340 p->offset, p->length, buf + index))
341 return 0;
344 return 1;
347 /* Context for grub_cmd_save_env. */
348 struct grub_cmd_save_env_ctx
350 struct blocklist *head, *tail;
353 /* Store blocklists in a linked list. */
354 static void
355 save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
356 void *data)
358 struct grub_cmd_save_env_ctx *ctx = data;
359 struct blocklist *block;
361 block = grub_malloc (sizeof (*block));
362 if (! block)
363 return;
365 block->sector = sector;
366 block->offset = offset;
367 block->length = length;
369 /* Slightly complicated, because the list should be FIFO. */
370 block->next = 0;
371 if (ctx->tail)
372 ctx->tail->next = block;
373 ctx->tail = block;
374 if (! ctx->head)
375 ctx->head = block;
378 static grub_err_t
379 grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
381 struct grub_arg_list *state = ctxt->state;
382 grub_file_t file;
383 grub_envblk_t envblk;
384 struct grub_cmd_save_env_ctx ctx = {
385 .head = 0,
386 .tail = 0
389 if (! argc)
390 return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
392 file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
393 1 /* allow untrusted */);
394 if (! file)
395 return grub_errno;
397 if (! file->device->disk)
399 grub_file_close (file);
400 return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
403 file->read_hook = save_env_read_hook;
404 file->read_hook_data = &ctx;
405 envblk = read_envblk_file (file);
406 file->read_hook = 0;
407 if (! envblk)
408 goto fail;
410 if (check_blocklists (envblk, ctx.head, file))
411 goto fail;
413 while (argc)
415 const char *value;
417 value = grub_env_get (args[0]);
418 if (value)
420 if (! grub_envblk_set (envblk, args[0], value))
422 grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
423 goto fail;
426 else
427 grub_envblk_delete (envblk, args[0]);
429 argc--;
430 args++;
433 write_blocklists (envblk, ctx.head, file);
435 fail:
436 if (envblk)
437 grub_envblk_close (envblk);
438 free_blocklists (ctx.head);
439 grub_file_close (file);
440 return grub_errno;
443 static grub_extcmd_t cmd_load, cmd_list, cmd_save;
445 GRUB_MOD_INIT(loadenv)
447 cmd_load =
448 grub_register_extcmd ("load_env", grub_cmd_load_env, 0,
449 N_("[-f FILE] [-s|--skip-sig] [variable_name_to_whitelist] [...]"),
450 N_("Load variables from environment block file."),
451 options);
452 cmd_list =
453 grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"),
454 N_("List variables from environment block file."),
455 options);
456 cmd_save =
457 grub_register_extcmd ("save_env", grub_cmd_save_env, 0,
458 N_("[-f FILE] variable_name [...]"),
459 N_("Save variables to environment block file."),
460 options);
463 GRUB_MOD_FINI(loadenv)
465 grub_unregister_extcmd (cmd_load);
466 grub_unregister_extcmd (cmd_list);
467 grub_unregister_extcmd (cmd_save);