2009-11-21 Samuel Thibault <samuel.thibault@ens-lyon.org>
[grub2.git] / commands / loadenv.c
blobc60eb835c2c2a32800c75af76db0cb68ba346e5f
1 /* loadenv.c - command to load/save environment variable. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
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>
30 static const struct grub_arg_option options[] =
32 {"file", 'f', 0, "specify filename", 0, ARG_TYPE_PATHNAME},
33 {0, 0, 0, 0, 0, 0}
36 static grub_file_t
37 open_envblk_file (char *filename)
39 grub_file_t file;
41 if (! filename)
43 char *prefix;
45 prefix = grub_env_get ("prefix");
46 if (prefix)
48 int len;
50 len = grub_strlen (prefix);
51 filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
52 if (! filename)
53 return 0;
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;
62 else
64 grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found");
65 return 0;
69 return grub_file_open (filename);
72 static grub_envblk_t
73 read_envblk_file (grub_file_t file)
75 grub_off_t offset = 0;
76 char *buf;
77 grub_size_t size = grub_file_size (file);
78 grub_envblk_t envblk;
80 buf = grub_malloc (size);
81 if (! buf)
82 return 0;
84 while (size > 0)
86 grub_ssize_t ret;
88 ret = grub_file_read (file, buf + offset, size);
89 if (ret <= 0)
91 if (grub_errno == GRUB_ERR_NONE)
92 grub_error (GRUB_ERR_FILE_READ_ERROR, "cannot read");
93 grub_free (buf);
94 return 0;
97 size -= ret;
98 offset += ret;
101 envblk = grub_envblk_open (buf, offset);
102 if (! envblk)
104 grub_free (buf);
105 grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
106 return 0;
109 return envblk;
112 static grub_err_t
113 grub_cmd_load_env (grub_extcmd_t cmd,
114 int argc __attribute__ ((unused)),
115 char **args __attribute__ ((unused)))
117 struct grub_arg_list *state = cmd->state;
118 grub_file_t file;
119 grub_envblk_t envblk;
121 auto int set_var (const char *name, const char *value);
122 int set_var (const char *name, const char *value)
124 grub_env_set (name, value);
125 return 0;
128 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
129 if (! file)
130 return grub_errno;
132 envblk = read_envblk_file (file);
133 if (! envblk)
134 goto fail;
136 grub_envblk_iterate (envblk, set_var);
137 grub_envblk_close (envblk);
139 fail:
140 grub_file_close (file);
141 return grub_errno;
144 static grub_err_t
145 grub_cmd_list_env (grub_extcmd_t cmd,
146 int argc __attribute__ ((unused)),
147 char **args __attribute__ ((unused)))
149 struct grub_arg_list *state = cmd->state;
150 grub_file_t file;
151 grub_envblk_t envblk;
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)
157 grub_printf ("%s=%s\n", name, value);
158 return 0;
161 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
162 if (! file)
163 return grub_errno;
165 envblk = read_envblk_file (file);
166 if (! envblk)
167 goto fail;
169 grub_envblk_iterate (envblk, print_var);
170 grub_envblk_close (envblk);
172 fail:
173 grub_file_close (file);
174 return grub_errno;
177 /* Used to maintain a variable length of blocklists internally. */
178 struct blocklist
180 grub_disk_addr_t sector;
181 unsigned offset;
182 unsigned length;
183 struct blocklist *next;
186 static void
187 free_blocklists (struct blocklist *p)
189 struct blocklist *q;
191 for (; p; p = q)
193 q = p->next;
194 grub_free (p);
198 static int
199 check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
200 grub_file_t file)
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;
209 /* Sanity checks. */
210 total_length = 0;
211 for (p = blocklists; p; p = p->next)
213 struct blocklist *q;
214 for (q = p->next; q; q = q->next)
216 /* Check if any pair of blocks overlap. */
217 if (p->sector == q->sector)
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;
226 total_length += p->length;
229 if (total_length != grub_file_size (file))
231 /* Maybe sparse, unallocated sectors. No way in GRUB. */
232 grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
233 return 0;
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;
244 buf = grub_envblk_buffer (envblk);
245 for (p = blocklists, index = 0; p; index += p->length, p = p->next)
247 char blockbuf[GRUB_DISK_SECTOR_SIZE];
249 if (grub_disk_read (disk, p->sector - part_start,
250 p->offset, p->length, blockbuf))
251 return 0;
253 if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
255 grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
256 return 0;
260 return 1;
263 static int
264 write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
265 grub_file_t file)
267 char *buf;
268 grub_disk_t disk;
269 grub_disk_addr_t part_start;
270 struct blocklist *p;
271 grub_size_t index;
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;
280 index = 0;
281 for (p = blocklists; p; index += p->length, p = p->next)
283 if (grub_disk_write (disk, p->sector - part_start,
284 p->offset, p->length, buf + index))
285 return 0;
288 return 1;
291 static grub_err_t
292 grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args)
294 struct grub_arg_list *state = cmd->state;
295 grub_file_t file;
296 grub_envblk_t envblk;
297 struct blocklist *head = 0;
298 struct blocklist *tail = 0;
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)
307 struct blocklist *block;
309 if (offset + length > GRUB_DISK_SECTOR_SIZE)
310 /* Seemingly a bug. */
311 return;
313 block = grub_malloc (sizeof (*block));
314 if (! block)
315 return;
317 block->sector = sector;
318 block->offset = offset;
319 block->length = length;
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;
330 if (! argc)
331 return grub_error (GRUB_ERR_BAD_ARGUMENT, "No variable is specified");
333 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
334 if (! file)
335 return grub_errno;
337 if (! file->device->disk)
339 grub_file_close (file);
340 return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
343 file->read_hook = read_hook;
344 envblk = read_envblk_file (file);
345 file->read_hook = 0;
346 if (! envblk)
347 goto fail;
349 if (! check_blocklists (envblk, head, file))
350 goto fail;
352 while (argc)
354 char *value;
356 value = grub_env_get (args[0]);
357 if (value)
359 if (! grub_envblk_set (envblk, args[0], value))
361 grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
362 goto fail;
366 argc--;
367 args++;
370 write_blocklists (envblk, head, file);
372 fail:
373 if (envblk)
374 grub_envblk_close (envblk);
375 free_blocklists (head);
376 grub_file_close (file);
377 return grub_errno;
380 static grub_extcmd_t cmd_load, cmd_list, cmd_save;
382 GRUB_MOD_INIT(loadenv)
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);
404 GRUB_MOD_FINI(loadenv)
406 grub_unregister_extcmd (cmd_load);
407 grub_unregister_extcmd (cmd_list);
408 grub_unregister_extcmd (cmd_save);