source: grub-pc/trunk/fuentes/grub-core/commands/loadenv.c @ 22

Last change on this file since 22 was 22, checked in by mabarracus, 4 years ago

updated version and apply net.ifnames=0 into debian/rules

File size: 11.2 KB
Line 
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.
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
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>
30
31GRUB_MOD_LICENSE ("GPLv3+");
32
33static const struct grub_arg_option options[] =
34  {
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}
41  };
42
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. */
46static grub_file_t
47open_envblk_file (char *filename, int untrusted)
48{
49  grub_file_t file;
50  char *buf = 0;
51
52  if (! filename)
53    {
54      const char *prefix;
55      int len;
56
57      prefix = grub_env_get ("prefix");
58      if (! prefix)
59        {
60          grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix");
61          return 0;
62        }
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);
73    }
74
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 ();
80
81  file = grub_file_open (filename);
82
83  grub_free (buf);
84  return file;
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;
94
95  buf = grub_malloc (size);
96  if (! buf)
97    return 0;
98
99  while (size > 0)
100    {
101      grub_ssize_t ret;
102
103      ret = grub_file_read (file, buf + offset, size);
104      if (ret <= 0)
105        {
106          grub_free (buf);
107          return 0;
108        }
109
110      size -= ret;
111      offset += ret;
112    }
113
114  envblk = grub_envblk_open (buf, offset);
115  if (! envblk)
116    {
117      grub_free (buf);
118      grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
119      return 0;
120    }
121
122  return envblk;
123}
124
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
145/* Helper for grub_cmd_load_env.  */
146static int
147set_var (const char *name, const char *value, void *whitelist)
148{
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
159  return 0;
160}
161
162static grub_err_t
163grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
164{
165  struct grub_arg_list *state = ctxt->state;
166  grub_file_t file;
167  grub_envblk_t envblk;
168  grub_env_whitelist_t whitelist;
169
170  whitelist.len = argc;
171  whitelist.list = args;
172
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;
177
178  envblk = read_envblk_file (file);
179  if (! envblk)
180    goto fail;
181
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);
185
186 fail:
187  grub_file_close (file);
188  return grub_errno;
189}
190
191/* Print all variables in current context.  */
192static int
193print_var (const char *name, const char *value,
194           void *hook_data __attribute__ ((unused)))
195{
196  grub_printf ("%s=%s\n", name, value);
197  return 0;
198}
199
200static grub_err_t
201grub_cmd_list_env (grub_extcmd_context_t ctxt,
202                   int argc __attribute__ ((unused)),
203                   char **args __attribute__ ((unused)))
204{
205  struct grub_arg_list *state = ctxt->state;
206  grub_file_t file;
207  grub_envblk_t envblk;
208
209  file = open_envblk_file ((state[0].set) ? state[0].arg : 0, 0);
210  if (! file)
211    return grub_errno;
212
213  envblk = read_envblk_file (file);
214  if (! envblk)
215    goto fail;
216
217  grub_envblk_iterate (envblk, NULL, print_var);
218  grub_envblk_close (envblk);
219
220 fail:
221  grub_file_close (file);
222  return grub_errno;
223}
224
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;
238
239  for (; p; p = q)
240    {
241      q = p->next;
242      grub_free (p);
243    }
244}
245
246static grub_err_t
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;
256
257  /* Sanity checks.  */
258  total_length = 0;
259  for (p = blocklists; p; p = p->next)
260    {
261      struct blocklist *q;
262      /* Check if any pair of blocks overlap.  */
263      for (q = p->next; q; q = q->next)
264        {
265          grub_disk_addr_t s1, s2;
266          grub_disk_addr_t e1, e2;
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
274          if (s1 < e2 && s2 < e1)
275            {
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");
279            }
280        }
281
282      total_length += p->length;
283    }
284
285  if (total_length != grub_file_size (file))
286    {
287      /* Maybe sparse, unallocated sectors. No way in GRUB.  */
288      return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
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;
294
295  part_start = grub_partition_get_start (disk->partition);
296
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)
301    {
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        }
310
311      if (grub_disk_read (disk, p->sector - part_start,
312                          p->offset, p->length, blockbuf))
313        return grub_errno;
314
315      if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
316        return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
317    }
318
319  return GRUB_ERR_NONE;
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;
331
332  buf = grub_envblk_buffer (envblk);
333  disk = file->device->disk;
334  part_start = grub_partition_get_start (disk->partition);
335
336  index = 0;
337  for (p = blocklists; p; index += p->length, p = p->next)
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;
345}
346
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
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
378static grub_err_t
379grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
380{
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
387  };
388
389  if (! argc)
390    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
391
392  file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
393                           1 /* allow untrusted */);
394  if (! file)
395    return grub_errno;
396
397  if (! file->device->disk)
398    {
399      grub_file_close (file);
400      return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
401    }
402
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;
409
410  if (check_blocklists (envblk, ctx.head, file))
411    goto fail;
412
413  while (argc)
414    {
415      const char *value;
416
417      value = grub_env_get (args[0]);
418      if (value)
419        {
420          if (! grub_envblk_set (envblk, args[0], value))
421            {
422              grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
423              goto fail;
424            }
425        }
426      else
427        grub_envblk_delete (envblk, args[0]);
428
429      argc--;
430      args++;
431    }
432
433  write_blocklists (envblk, ctx.head, file);
434
435 fail:
436  if (envblk)
437    grub_envblk_close (envblk);
438  free_blocklists (ctx.head);
439  grub_file_close (file);
440  return grub_errno;
441}
442
443static grub_extcmd_t cmd_load, cmd_list, cmd_save;
444
445GRUB_MOD_INIT(loadenv)
446{
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);
461}
462
463GRUB_MOD_FINI(loadenv)
464{
465  grub_unregister_extcmd (cmd_load);
466  grub_unregister_extcmd (cmd_list);
467  grub_unregister_extcmd (cmd_save);
468}
Note: See TracBrowser for help on using the repository browser.