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