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