~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/commands/loadenv.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
31
static const struct grub_arg_option options[] =
 
32
  {
 
33
    {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
 
34
    {0, 0, 0, 0, 0, 0}
 
35
  };
 
36
 
 
37
static grub_file_t
 
38
open_envblk_file (char *filename)
 
39
{
 
40
  grub_file_t file;
 
41
 
 
42
  if (! filename)
 
43
    {
 
44
      char *prefix;
 
45
 
 
46
      prefix = grub_env_get ("prefix");
 
47
      if (prefix)
 
48
        {
 
49
          int len;
 
50
 
 
51
          len = grub_strlen (prefix);
 
52
          filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
 
53
          if (! filename)
 
54
            return 0;
 
55
 
 
56
          grub_strcpy (filename, prefix);
 
57
          filename[len] = '/';
 
58
          grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
 
59
          grub_file_filter_disable_compression ();
 
60
          file = grub_file_open (filename);
 
61
          grub_free (filename);
 
62
          return file;
 
63
        }
 
64
      else
 
65
        {
 
66
          grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found");
 
67
          return 0;
 
68
        }
 
69
    }
 
70
 
 
71
  grub_file_filter_disable_compression ();
 
72
  return grub_file_open (filename);
 
73
}
 
74
 
 
75
static grub_envblk_t
 
76
read_envblk_file (grub_file_t file)
 
77
{
 
78
  grub_off_t offset = 0;
 
79
  char *buf;
 
80
  grub_size_t size = grub_file_size (file);
 
81
  grub_envblk_t envblk;
 
82
 
 
83
  buf = grub_malloc (size);
 
84
  if (! buf)
 
85
    return 0;
 
86
 
 
87
  while (size > 0)
 
88
    {
 
89
      grub_ssize_t ret;
 
90
 
 
91
      ret = grub_file_read (file, buf + offset, size);
 
92
      if (ret <= 0)
 
93
        {
 
94
          if (grub_errno == GRUB_ERR_NONE)
 
95
            grub_error (GRUB_ERR_FILE_READ_ERROR, "cannot read");
 
96
          grub_free (buf);
 
97
          return 0;
 
98
        }
 
99
 
 
100
      size -= ret;
 
101
      offset += ret;
 
102
    }
 
103
 
 
104
  envblk = grub_envblk_open (buf, offset);
 
105
  if (! envblk)
 
106
    {
 
107
      grub_free (buf);
 
108
      grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
 
109
      return 0;
 
110
    }
 
111
 
 
112
  return envblk;
 
113
}
 
114
 
 
115
static grub_err_t
 
116
grub_cmd_load_env (grub_extcmd_context_t ctxt,
 
117
                   int argc __attribute__ ((unused)),
 
118
                   char **args __attribute__ ((unused)))
 
119
{
 
120
  struct grub_arg_list *state = ctxt->state;
 
121
  grub_file_t file;
 
122
  grub_envblk_t envblk;
 
123
 
 
124
  auto int set_var (const char *name, const char *value);
 
125
  int set_var (const char *name, const char *value)
 
126
  {
 
127
    grub_env_set (name, value);
 
128
    return 0;
 
129
  }
 
130
 
 
131
  file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
 
132
  if (! file)
 
133
    return grub_errno;
 
134
 
 
135
  envblk = read_envblk_file (file);
 
136
  if (! envblk)
 
137
    goto fail;
 
138
 
 
139
  grub_envblk_iterate (envblk, set_var);
 
140
  grub_envblk_close (envblk);
 
141
 
 
142
 fail:
 
143
  grub_file_close (file);
 
144
  return grub_errno;
 
145
}
 
146
 
 
147
static grub_err_t
 
148
grub_cmd_list_env (grub_extcmd_context_t ctxt,
 
149
                   int argc __attribute__ ((unused)),
 
150
                   char **args __attribute__ ((unused)))
 
151
{
 
152
  struct grub_arg_list *state = ctxt->state;
 
153
  grub_file_t file;
 
154
  grub_envblk_t envblk;
 
155
 
 
156
  /* Print all variables in current context.  */
 
157
  auto int print_var (const char *name, const char *value);
 
158
  int print_var (const char *name, const char *value)
 
159
    {
 
160
      grub_printf ("%s=%s\n", name, value);
 
161
      return 0;
 
162
    }
 
163
 
 
164
  file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
 
165
  if (! file)
 
166
    return grub_errno;
 
167
 
 
168
  envblk = read_envblk_file (file);
 
169
  if (! envblk)
 
170
    goto fail;
 
171
 
 
172
  grub_envblk_iterate (envblk, print_var);
 
173
  grub_envblk_close (envblk);
 
174
 
 
175
 fail:
 
176
  grub_file_close (file);
 
177
  return grub_errno;
 
178
}
 
179
 
 
180
/* Used to maintain a variable length of blocklists internally.  */
 
181
struct blocklist
 
182
{
 
183
  grub_disk_addr_t sector;
 
184
  unsigned offset;
 
185
  unsigned length;
 
186
  struct blocklist *next;
 
187
};
 
188
 
 
189
static void
 
190
free_blocklists (struct blocklist *p)
 
191
{
 
192
  struct blocklist *q;
 
193
 
 
194
  for (; p; p = q)
 
195
    {
 
196
      q = p->next;
 
197
      grub_free (p);
 
198
    }
 
199
}
 
200
 
 
201
static grub_err_t
 
202
check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
 
203
                  grub_file_t file)
 
204
{
 
205
  grub_size_t total_length;
 
206
  grub_size_t index;
 
207
  grub_disk_t disk;
 
208
  grub_disk_addr_t part_start;
 
209
  struct blocklist *p;
 
210
  char *buf;
 
211
 
 
212
  /* Sanity checks.  */
 
213
  total_length = 0;
 
214
  for (p = blocklists; p; p = p->next)
 
215
    {
 
216
      struct blocklist *q;
 
217
      for (q = p->next; q; q = q->next)
 
218
        {
 
219
          /* Check if any pair of blocks overlap.  */
 
220
          if (p->sector == q->sector)
 
221
            {
 
222
              /* This might be actually valid, but it is unbelievable that
 
223
                 any filesystem makes such a silly allocation.  */
 
224
              return grub_error (GRUB_ERR_BAD_FS, "malformed file");
 
225
            }
 
226
        }
 
227
 
 
228
      total_length += p->length;
 
229
    }
 
230
 
 
231
  if (total_length != grub_file_size (file))
 
232
    {
 
233
      /* Maybe sparse, unallocated sectors. No way in GRUB.  */
 
234
      return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
 
235
    }
 
236
 
 
237
  /* One more sanity check. Re-read all sectors by blocklists, and compare
 
238
     those with the data read via a file.  */
 
239
  disk = file->device->disk;
 
240
 
 
241
  part_start = grub_partition_get_start (disk->partition);
 
242
 
 
243
  buf = grub_envblk_buffer (envblk);
 
244
  for (p = blocklists, index = 0; p; index += p->length, p = p->next)
 
245
    {
 
246
      char blockbuf[GRUB_DISK_SECTOR_SIZE];
 
247
 
 
248
      if (grub_disk_read (disk, p->sector - part_start,
 
249
                          p->offset, p->length, blockbuf))
 
250
        return grub_errno;
 
251
 
 
252
      if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
 
253
        return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
 
254
    }
 
255
 
 
256
  return GRUB_ERR_NONE;
 
257
}
 
258
 
 
259
static int
 
260
write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
 
261
                  grub_file_t file)
 
262
{
 
263
  char *buf;
 
264
  grub_disk_t disk;
 
265
  grub_disk_addr_t part_start;
 
266
  struct blocklist *p;
 
267
  grub_size_t index;
 
268
 
 
269
  buf = grub_envblk_buffer (envblk);
 
270
  disk = file->device->disk;
 
271
  part_start = grub_partition_get_start (disk->partition);
 
272
 
 
273
  index = 0;
 
274
  for (p = blocklists; p; index += p->length, p = p->next)
 
275
    {
 
276
      if (grub_disk_write (disk, p->sector - part_start,
 
277
                           p->offset, p->length, buf + index))
 
278
        return 0;
 
279
    }
 
280
 
 
281
  return 1;
 
282
}
 
283
 
 
284
static grub_err_t
 
285
grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
 
286
{
 
287
  struct grub_arg_list *state = ctxt->state;
 
288
  grub_file_t file;
 
289
  grub_envblk_t envblk;
 
290
  struct blocklist *head = 0;
 
291
  struct blocklist *tail = 0;
 
292
 
 
293
  /* Store blocklists in a linked list.  */
 
294
  auto void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
 
295
                                        unsigned offset,
 
296
                                        unsigned length);
 
297
  void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
 
298
                                   unsigned offset, unsigned length)
 
299
    {
 
300
      struct blocklist *block;
 
301
 
 
302
      if (offset + length > GRUB_DISK_SECTOR_SIZE)
 
303
        /* Seemingly a bug.  */
 
304
        return;
 
305
 
 
306
      block = grub_malloc (sizeof (*block));
 
307
      if (! block)
 
308
        return;
 
309
 
 
310
      block->sector = sector;
 
311
      block->offset = offset;
 
312
      block->length = length;
 
313
 
 
314
      /* Slightly complicated, because the list should be FIFO.  */
 
315
      block->next = 0;
 
316
      if (tail)
 
317
        tail->next = block;
 
318
      tail = block;
 
319
      if (! head)
 
320
        head = block;
 
321
    }
 
322
 
 
323
  if (! argc)
 
324
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
 
325
 
 
326
  file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
 
327
  if (! file)
 
328
    return grub_errno;
 
329
 
 
330
  if (! file->device->disk)
 
331
    {
 
332
      grub_file_close (file);
 
333
      return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
 
334
    }
 
335
 
 
336
  file->read_hook = read_hook;
 
337
  envblk = read_envblk_file (file);
 
338
  file->read_hook = 0;
 
339
  if (! envblk)
 
340
    goto fail;
 
341
 
 
342
  if (check_blocklists (envblk, head, file))
 
343
    goto fail;
 
344
 
 
345
  while (argc)
 
346
    {
 
347
      char *value;
 
348
 
 
349
      value = grub_env_get (args[0]);
 
350
      if (value)
 
351
        {
 
352
          if (! grub_envblk_set (envblk, args[0], value))
 
353
            {
 
354
              grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
 
355
              goto fail;
 
356
            }
 
357
        }
 
358
 
 
359
      argc--;
 
360
      args++;
 
361
    }
 
362
 
 
363
  write_blocklists (envblk, head, file);
 
364
 
 
365
 fail:
 
366
  if (envblk)
 
367
    grub_envblk_close (envblk);
 
368
  free_blocklists (head);
 
369
  grub_file_close (file);
 
370
  return grub_errno;
 
371
}
 
372
 
 
373
static grub_extcmd_t cmd_load, cmd_list, cmd_save;
 
374
 
 
375
GRUB_MOD_INIT(loadenv)
 
376
{
 
377
  cmd_load =
 
378
    grub_register_extcmd ("load_env", grub_cmd_load_env, 0, N_("[-f FILE]"),
 
379
                          N_("Load variables from environment block file."),
 
380
                          options);
 
381
  cmd_list =
 
382
    grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"),
 
383
                          N_("List variables from environment block file."),
 
384
                          options);
 
385
  cmd_save =
 
386
    grub_register_extcmd ("save_env", grub_cmd_save_env, 0,
 
387
                          N_("[-f FILE] variable_name [...]"),
 
388
                          N_("Save variables to environment block file."),
 
389
                          options);
 
390
}
 
391
 
 
392
GRUB_MOD_FINI(loadenv)
 
393
{
 
394
  grub_unregister_extcmd (cmd_load);
 
395
  grub_unregister_extcmd (cmd_list);
 
396
  grub_unregister_extcmd (cmd_save);
 
397
}