Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / commands / loadenv.c
blob18ebb7eab83f66981e17f319e933ca6fc79c53e6
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.
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>
29 #include <grub/i18n.h>
31 GRUB_MOD_LICENSE ("GPLv3+");
33 static const struct grub_arg_option options[] =
35 /* TRANSLATORS: This option is used to override default filename
36 for loading and storing environment. */
37 {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
38 {0, 0, 0, 0, 0, 0}
41 static grub_file_t
42 open_envblk_file (char *filename)
44 grub_file_t file;
46 if (! filename)
48 const char *prefix;
50 prefix = grub_env_get ("prefix");
51 if (prefix)
53 int len;
55 len = grub_strlen (prefix);
56 filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
57 if (! filename)
58 return 0;
60 grub_strcpy (filename, prefix);
61 filename[len] = '/';
62 grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
63 grub_file_filter_disable_compression ();
64 file = grub_file_open (filename);
65 grub_free (filename);
66 return file;
68 else
70 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix");
71 return 0;
75 grub_file_filter_disable_compression ();
76 return grub_file_open (filename);
79 static grub_envblk_t
80 read_envblk_file (grub_file_t file)
82 grub_off_t offset = 0;
83 char *buf;
84 grub_size_t size = grub_file_size (file);
85 grub_envblk_t envblk;
87 buf = grub_malloc (size);
88 if (! buf)
89 return 0;
91 while (size > 0)
93 grub_ssize_t ret;
95 ret = grub_file_read (file, buf + offset, size);
96 if (ret <= 0)
98 grub_free (buf);
99 return 0;
102 size -= ret;
103 offset += ret;
106 envblk = grub_envblk_open (buf, offset);
107 if (! envblk)
109 grub_free (buf);
110 grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
111 return 0;
114 return envblk;
117 static grub_err_t
118 grub_cmd_load_env (grub_extcmd_context_t ctxt,
119 int argc __attribute__ ((unused)),
120 char **args __attribute__ ((unused)))
122 struct grub_arg_list *state = ctxt->state;
123 grub_file_t file;
124 grub_envblk_t envblk;
126 auto int set_var (const char *name, const char *value);
127 int set_var (const char *name, const char *value)
129 grub_env_set (name, value);
130 return 0;
133 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
134 if (! file)
135 return grub_errno;
137 envblk = read_envblk_file (file);
138 if (! envblk)
139 goto fail;
141 grub_envblk_iterate (envblk, set_var);
142 grub_envblk_close (envblk);
144 fail:
145 grub_file_close (file);
146 return grub_errno;
149 static grub_err_t
150 grub_cmd_list_env (grub_extcmd_context_t ctxt,
151 int argc __attribute__ ((unused)),
152 char **args __attribute__ ((unused)))
154 struct grub_arg_list *state = ctxt->state;
155 grub_file_t file;
156 grub_envblk_t envblk;
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)
162 grub_printf ("%s=%s\n", name, value);
163 return 0;
166 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
167 if (! file)
168 return grub_errno;
170 envblk = read_envblk_file (file);
171 if (! envblk)
172 goto fail;
174 grub_envblk_iterate (envblk, print_var);
175 grub_envblk_close (envblk);
177 fail:
178 grub_file_close (file);
179 return grub_errno;
182 /* Used to maintain a variable length of blocklists internally. */
183 struct blocklist
185 grub_disk_addr_t sector;
186 unsigned offset;
187 unsigned length;
188 struct blocklist *next;
191 static void
192 free_blocklists (struct blocklist *p)
194 struct blocklist *q;
196 for (; p; p = q)
198 q = p->next;
199 grub_free (p);
203 static grub_err_t
204 check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
205 grub_file_t file)
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;
214 /* Sanity checks. */
215 total_length = 0;
216 for (p = blocklists; p; p = p->next)
218 struct blocklist *q;
219 for (q = p->next; q; q = q->next)
221 /* Check if any pair of blocks overlap. */
222 if (p->sector == q->sector)
224 /* This might be actually valid, but it is unbelievable that
225 any filesystem makes such a silly allocation. */
226 return grub_error (GRUB_ERR_BAD_FS, "malformed file");
230 total_length += p->length;
233 if (total_length != grub_file_size (file))
235 /* Maybe sparse, unallocated sectors. No way in GRUB. */
236 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
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;
243 part_start = grub_partition_get_start (disk->partition);
245 buf = grub_envblk_buffer (envblk);
246 for (p = blocklists, index = 0; p; index += p->length, p = p->next)
248 char blockbuf[GRUB_DISK_SECTOR_SIZE];
250 if (grub_disk_read (disk, p->sector - part_start,
251 p->offset, p->length, blockbuf))
252 return grub_errno;
254 if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
255 return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
258 return GRUB_ERR_NONE;
261 static int
262 write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
263 grub_file_t file)
265 char *buf;
266 grub_disk_t disk;
267 grub_disk_addr_t part_start;
268 struct blocklist *p;
269 grub_size_t index;
271 buf = grub_envblk_buffer (envblk);
272 disk = file->device->disk;
273 part_start = grub_partition_get_start (disk->partition);
275 index = 0;
276 for (p = blocklists; p; index += p->length, p = p->next)
278 if (grub_disk_write (disk, p->sector - part_start,
279 p->offset, p->length, buf + index))
280 return 0;
283 return 1;
286 static grub_err_t
287 grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
289 struct grub_arg_list *state = ctxt->state;
290 grub_file_t file;
291 grub_envblk_t envblk;
292 struct blocklist *head = 0;
293 struct blocklist *tail = 0;
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)
302 struct blocklist *block;
304 if (offset + length > GRUB_DISK_SECTOR_SIZE)
305 /* Seemingly a bug. */
306 return;
308 block = grub_malloc (sizeof (*block));
309 if (! block)
310 return;
312 block->sector = sector;
313 block->offset = offset;
314 block->length = length;
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;
325 if (! argc)
326 return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
328 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
329 if (! file)
330 return grub_errno;
332 if (! file->device->disk)
334 grub_file_close (file);
335 return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
338 file->read_hook = read_hook;
339 envblk = read_envblk_file (file);
340 file->read_hook = 0;
341 if (! envblk)
342 goto fail;
344 if (check_blocklists (envblk, head, file))
345 goto fail;
347 while (argc)
349 const char *value;
351 value = grub_env_get (args[0]);
352 if (value)
354 if (! grub_envblk_set (envblk, args[0], value))
356 grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
357 goto fail;
361 argc--;
362 args++;
365 write_blocklists (envblk, head, file);
367 fail:
368 if (envblk)
369 grub_envblk_close (envblk);
370 free_blocklists (head);
371 grub_file_close (file);
372 return grub_errno;
375 static grub_extcmd_t cmd_load, cmd_list, cmd_save;
377 GRUB_MOD_INIT(loadenv)
379 cmd_load =
380 grub_register_extcmd ("load_env", grub_cmd_load_env, 0, N_("[-f FILE]"),
381 N_("Load variables from environment block file."),
382 options);
383 cmd_list =
384 grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"),
385 N_("List variables from environment block file."),
386 options);
387 cmd_save =
388 grub_register_extcmd ("save_env", grub_cmd_save_env, 0,
389 N_("[-f FILE] variable_name [...]"),
390 N_("Save variables to environment block file."),
391 options);
394 GRUB_MOD_FINI(loadenv)
396 grub_unregister_extcmd (cmd_load);
397 grub_unregister_extcmd (cmd_list);
398 grub_unregister_extcmd (cmd_save);