]> git.proxmox.com Git - grub2.git/blame - grub-core/commands/loadenv.c
bufio: Use grub_size_t instead of plain int for size
[grub2.git] / grub-core / commands / loadenv.c
CommitLineData
2270f77b 1/* loadenv.c - command to load/save environment variable. */
2/*
3 * GRUB -- GRand Unified Bootloader
c893cc87 4 * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc.
2270f77b 5 *
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.
10 *
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.
15 *
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/>.
18 */
19
2270f77b 20#include <grub/dl.h>
21#include <grub/mm.h>
2270f77b 22#include <grub/file.h>
23#include <grub/disk.h>
24#include <grub/misc.h>
25#include <grub/env.h>
2270f77b 26#include <grub/partition.h>
a85cd5a0 27#include <grub/lib/envblk.h>
b1b797cb 28#include <grub/extcmd.h>
77a79592 29#include <grub/i18n.h>
2270f77b 30
e745cf0c
VS
31GRUB_MOD_LICENSE ("GPLv3+");
32
2270f77b 33static const struct grub_arg_option options[] =
34 {
40211ab8
VS
35 /* TRANSLATORS: This option is used to override default filename
36 for loading and storing environment. */
77a79592 37 {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
0340bdbc
JM
38 {"skip-sig", 's', 0,
39 N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE},
2270f77b 40 {0, 0, 0, 0, 0, 0}
41 };
42
0340bdbc
JM
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. */
2270f77b 46static grub_file_t
0340bdbc 47open_envblk_file (char *filename, int untrusted)
2270f77b 48{
2270f77b 49 grub_file_t file;
0340bdbc 50 char *buf = 0;
2270f77b 51
52 if (! filename)
53 {
d35d0d37 54 const char *prefix;
0340bdbc 55 int len;
2270f77b 56
57 prefix = grub_env_get ("prefix");
0340bdbc 58 if (! prefix)
2270f77b 59 {
9c4b5c13 60 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix");
2270f77b 61 return 0;
62 }
0340bdbc
JM
63
64 len = grub_strlen (prefix);
65 buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
66 if (! buf)
67 return 0;
68 filename = buf;
69
70 grub_strcpy (filename, prefix);
71 filename[len] = '/';
72 grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
2270f77b 73 }
74
0340bdbc
JM
75 /* The filters that are disabled will be re-enabled by the call to
76 grub_file_open() after this particular file is opened. */
fc2ef117 77 grub_file_filter_disable_compression ();
0340bdbc
JM
78 if (untrusted)
79 grub_file_filter_disable_pubkey ();
80
81 file = grub_file_open (filename);
82
83 grub_free (buf);
84 return file;
5709cfc4 85}
86
87static grub_envblk_t
88read_envblk_file (grub_file_t file)
89{
90 grub_off_t offset = 0;
91 char *buf;
92 grub_size_t size = grub_file_size (file);
93 grub_envblk_t envblk;
2270f77b 94
5709cfc4 95 buf = grub_malloc (size);
96 if (! buf)
97 return 0;
b39f9d20 98
5709cfc4 99 while (size > 0)
2270f77b 100 {
5709cfc4 101 grub_ssize_t ret;
102
103 ret = grub_file_read (file, buf + offset, size);
104 if (ret <= 0)
2270f77b 105 {
5709cfc4 106 grub_free (buf);
2270f77b 107 return 0;
108 }
2270f77b 109
5709cfc4 110 size -= ret;
111 offset += ret;
2270f77b 112 }
113
5709cfc4 114 envblk = grub_envblk_open (buf, offset);
2270f77b 115 if (! envblk)
116 {
5709cfc4 117 grub_free (buf);
118 grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
2270f77b 119 return 0;
120 }
121
5709cfc4 122 return envblk;
2270f77b 123}
124
0340bdbc
JM
125struct grub_env_whitelist
126{
127 grub_size_t len;
128 char **list;
129};
130typedef struct grub_env_whitelist grub_env_whitelist_t;
131
132static int
133test_whitelist_membership (const char* name,
134 const grub_env_whitelist_t* whitelist)
135{
136 grub_size_t i;
137
138 for (i = 0; i < whitelist->len; i++)
139 if (grub_strcmp (name, whitelist->list[i]) == 0)
140 return 1; /* found it */
141
142 return 0; /* not found */
143}
144
5c67ea6c
CW
145/* Helper for grub_cmd_load_env. */
146static int
0340bdbc 147set_var (const char *name, const char *value, void *whitelist)
5c67ea6c 148{
0340bdbc
JM
149 if (! whitelist)
150 {
151 grub_env_set (name, value);
152 return 0;
153 }
154
155 if (test_whitelist_membership (name,
156 (const grub_env_whitelist_t *) whitelist))
157 grub_env_set (name, value);
158
5c67ea6c
CW
159 return 0;
160}
161
2270f77b 162static grub_err_t
0340bdbc 163grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
2270f77b 164{
28be0e94 165 struct grub_arg_list *state = ctxt->state;
2270f77b 166 grub_file_t file;
5709cfc4 167 grub_envblk_t envblk;
0340bdbc
JM
168 grub_env_whitelist_t whitelist;
169
170 whitelist.len = argc;
171 whitelist.list = args;
2270f77b 172
0340bdbc
JM
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);
2270f77b 175 if (! file)
176 return grub_errno;
177
5709cfc4 178 envblk = read_envblk_file (file);
179 if (! envblk)
180 goto fail;
2270f77b 181
0340bdbc
JM
182 /* argc > 0 indicates caller provided a whitelist of variables to read. */
183 grub_envblk_iterate (envblk, argc > 0 ? &whitelist : 0, set_var);
5709cfc4 184 grub_envblk_close (envblk);
b39f9d20 185
5709cfc4 186 fail:
187 grub_file_close (file);
2270f77b 188 return grub_errno;
189}
190
74a27421
VS
191/* Print all variables in current context. */
192static int
0340bdbc
JM
193print_var (const char *name, const char *value,
194 void *hook_data __attribute__ ((unused)))
74a27421
VS
195{
196 grub_printf ("%s=%s\n", name, value);
197 return 0;
198}
199
2270f77b 200static grub_err_t
28be0e94 201grub_cmd_list_env (grub_extcmd_context_t ctxt,
b1b797cb 202 int argc __attribute__ ((unused)),
203 char **args __attribute__ ((unused)))
2270f77b 204{
28be0e94 205 struct grub_arg_list *state = ctxt->state;
2270f77b 206 grub_file_t file;
5709cfc4 207 grub_envblk_t envblk;
2270f77b 208
0340bdbc 209 file = open_envblk_file ((state[0].set) ? state[0].arg : 0, 0);
2270f77b 210 if (! file)
211 return grub_errno;
212
5709cfc4 213 envblk = read_envblk_file (file);
214 if (! envblk)
215 goto fail;
216
0340bdbc 217 grub_envblk_iterate (envblk, NULL, print_var);
5709cfc4 218 grub_envblk_close (envblk);
b39f9d20 219
5709cfc4 220 fail:
2270f77b 221 grub_file_close (file);
5709cfc4 222 return grub_errno;
223}
2270f77b 224
5709cfc4 225/* Used to maintain a variable length of blocklists internally. */
226struct blocklist
227{
228 grub_disk_addr_t sector;
229 unsigned offset;
230 unsigned length;
231 struct blocklist *next;
232};
233
234static void
235free_blocklists (struct blocklist *p)
236{
237 struct blocklist *q;
2270f77b 238
5709cfc4 239 for (; p; p = q)
240 {
241 q = p->next;
242 grub_free (p);
243 }
244}
245
c893cc87 246static grub_err_t
5709cfc4 247check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
248 grub_file_t file)
249{
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;
b39f9d20 256
5709cfc4 257 /* Sanity checks. */
258 total_length = 0;
259 for (p = blocklists; p; p = p->next)
260 {
261 struct blocklist *q;
cb72aa18 262 /* Check if any pair of blocks overlap. */
5709cfc4 263 for (q = p->next; q; q = q->next)
264 {
cb72aa18 265 grub_disk_addr_t s1, s2;
1f6af2a9 266 grub_disk_addr_t e1, e2;
cb72aa18
VS
267
268 s1 = p->sector;
269 e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
270
271 s2 = q->sector;
272 e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
273
1f6af2a9 274 if (s1 < e2 && s2 < e1)
5709cfc4 275 {
276 /* This might be actually valid, but it is unbelievable that
277 any filesystem makes such a silly allocation. */
c893cc87 278 return grub_error (GRUB_ERR_BAD_FS, "malformed file");
5709cfc4 279 }
280 }
b39f9d20 281
5709cfc4 282 total_length += p->length;
283 }
b39f9d20 284
5709cfc4 285 if (total_length != grub_file_size (file))
286 {
287 /* Maybe sparse, unallocated sectors. No way in GRUB. */
c893cc87 288 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
5709cfc4 289 }
290
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;
15cb7d43
VS
294
295 part_start = grub_partition_get_start (disk->partition);
5709cfc4 296
297 buf = grub_envblk_buffer (envblk);
cb72aa18
VS
298 char *blockbuf = NULL;
299 grub_size_t blockbuf_len = 0;
aa0f752d 300 for (p = blocklists, index = 0; p; index += p->length, p = p->next)
5709cfc4 301 {
cb72aa18
VS
302 if (p->length > blockbuf_len)
303 {
304 grub_free (blockbuf);
305 blockbuf_len = 2 * p->length;
306 blockbuf = grub_malloc (blockbuf_len);
307 if (!blockbuf)
308 return grub_errno;
309 }
b39f9d20 310
5709cfc4 311 if (grub_disk_read (disk, p->sector - part_start,
312 p->offset, p->length, blockbuf))
c893cc87 313 return grub_errno;
5709cfc4 314
315 if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
c893cc87 316 return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
5709cfc4 317 }
318
c893cc87 319 return GRUB_ERR_NONE;
5709cfc4 320}
321
322static int
323write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
324 grub_file_t file)
325{
326 char *buf;
327 grub_disk_t disk;
328 grub_disk_addr_t part_start;
329 struct blocklist *p;
330 grub_size_t index;
b39f9d20 331
5709cfc4 332 buf = grub_envblk_buffer (envblk);
333 disk = file->device->disk;
15cb7d43 334 part_start = grub_partition_get_start (disk->partition);
5709cfc4 335
336 index = 0;
aa0f752d 337 for (p = blocklists; p; index += p->length, p = p->next)
5709cfc4 338 {
339 if (grub_disk_write (disk, p->sector - part_start,
340 p->offset, p->length, buf + index))
341 return 0;
342 }
343
344 return 1;
2270f77b 345}
346
4eb8b756
CW
347/* Context for grub_cmd_save_env. */
348struct grub_cmd_save_env_ctx
349{
350 struct blocklist *head, *tail;
351};
352
353/* Store blocklists in a linked list. */
354static void
355save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
356 void *data)
357{
358 struct grub_cmd_save_env_ctx *ctx = data;
359 struct blocklist *block;
360
4eb8b756
CW
361 block = grub_malloc (sizeof (*block));
362 if (! block)
363 return;
364
365 block->sector = sector;
366 block->offset = offset;
367 block->length = length;
368
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;
376}
377
2270f77b 378static grub_err_t
28be0e94 379grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
2270f77b 380{
28be0e94 381 struct grub_arg_list *state = ctxt->state;
2270f77b 382 grub_file_t file;
5709cfc4 383 grub_envblk_t envblk;
4eb8b756
CW
384 struct grub_cmd_save_env_ctx ctx = {
385 .head = 0,
386 .tail = 0
387 };
2270f77b 388
389 if (! argc)
7fd0baee 390 return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
2270f77b 391
0340bdbc
JM
392 file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
393 1 /* allow untrusted */);
2270f77b 394 if (! file)
395 return grub_errno;
396
5709cfc4 397 if (! file->device->disk)
2270f77b 398 {
5709cfc4 399 grub_file_close (file);
400 return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
2270f77b 401 }
402
4eb8b756
CW
403 file->read_hook = save_env_read_hook;
404 file->read_hook_data = &ctx;
5709cfc4 405 envblk = read_envblk_file (file);
406 file->read_hook = 0;
407 if (! envblk)
408 goto fail;
2270f77b 409
4eb8b756 410 if (check_blocklists (envblk, ctx.head, file))
5709cfc4 411 goto fail;
b39f9d20 412
2270f77b 413 while (argc)
414 {
d35d0d37 415 const char *value;
2270f77b 416
417 value = grub_env_get (args[0]);
418 if (value)
419 {
5709cfc4 420 if (! grub_envblk_set (envblk, args[0], value))
2270f77b 421 {
422 grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
5709cfc4 423 goto fail;
2270f77b 424 }
425 }
5ade4a42
VS
426 else
427 grub_envblk_delete (envblk, args[0]);
2270f77b 428
429 argc--;
430 args++;
431 }
432
4eb8b756 433 write_blocklists (envblk, ctx.head, file);
b39f9d20 434
5709cfc4 435 fail:
436 if (envblk)
437 grub_envblk_close (envblk);
4eb8b756 438 free_blocklists (ctx.head);
2270f77b 439 grub_file_close (file);
2270f77b 440 return grub_errno;
441}
442
b1b797cb 443static grub_extcmd_t cmd_load, cmd_list, cmd_save;
444
2270f77b 445GRUB_MOD_INIT(loadenv)
446{
b1b797cb 447 cmd_load =
0340bdbc 448 grub_register_extcmd ("load_env", grub_cmd_load_env, 0,
bfdfeb25 449 N_("[-f FILE] [-s|--skip-sig] [variable_name_to_whitelist] [...]"),
29c44ad1 450 N_("Load variables from environment block file."),
b1b797cb 451 options);
452 cmd_list =
ed80f7d5 453 grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"),
29c44ad1 454 N_("List variables from environment block file."),
b1b797cb 455 options);
456 cmd_save =
ed80f7d5 457 grub_register_extcmd ("save_env", grub_cmd_save_env, 0,
29c44ad1 458 N_("[-f FILE] variable_name [...]"),
459 N_("Save variables to environment block file."),
b1b797cb 460 options);
2270f77b 461}
462
463GRUB_MOD_FINI(loadenv)
464{
b1b797cb 465 grub_unregister_extcmd (cmd_load);
466 grub_unregister_extcmd (cmd_list);
467 grub_unregister_extcmd (cmd_save);
2270f77b 468}