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