GRUB-1.98 changes
[grub2/jjazz.git] / commands / loadenv.c
blob51b88cbc9af470a6b205ddb7d243d28464c7176c
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 static const struct grub_arg_option options[] =
33 {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
34 {0, 0, 0, 0, 0, 0}
37 static grub_file_t
38 open_envblk_file (char *filename)
40 grub_file_t file;
42 if (! filename)
44 char *prefix;
46 prefix = grub_env_get ("prefix");
47 if (prefix)
49 int len;
51 len = grub_strlen (prefix);
52 filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
53 if (! filename)
54 return 0;
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;
63 else
65 grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found");
66 return 0;
70 return grub_file_open (filename);
73 static grub_envblk_t
74 read_envblk_file (grub_file_t file)
76 grub_off_t offset = 0;
77 char *buf;
78 grub_size_t size = grub_file_size (file);
79 grub_envblk_t envblk;
81 buf = grub_malloc (size);
82 if (! buf)
83 return 0;
85 while (size > 0)
87 grub_ssize_t ret;
89 ret = grub_file_read (file, buf + offset, size);
90 if (ret <= 0)
92 if (grub_errno == GRUB_ERR_NONE)
93 grub_error (GRUB_ERR_FILE_READ_ERROR, "cannot read");
94 grub_free (buf);
95 return 0;
98 size -= ret;
99 offset += ret;
102 envblk = grub_envblk_open (buf, offset);
103 if (! envblk)
105 grub_free (buf);
106 grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
107 return 0;
110 return envblk;
113 static grub_err_t
114 grub_cmd_load_env (grub_extcmd_t cmd,
115 int argc __attribute__ ((unused)),
116 char **args __attribute__ ((unused)))
118 struct grub_arg_list *state = cmd->state;
119 grub_file_t file;
120 grub_envblk_t envblk;
122 auto int set_var (const char *name, const char *value);
123 int set_var (const char *name, const char *value)
125 grub_env_set (name, value);
126 return 0;
129 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
130 if (! file)
131 return grub_errno;
133 envblk = read_envblk_file (file);
134 if (! envblk)
135 goto fail;
137 grub_envblk_iterate (envblk, set_var);
138 grub_envblk_close (envblk);
140 fail:
141 grub_file_close (file);
142 return grub_errno;
145 static grub_err_t
146 grub_cmd_list_env (grub_extcmd_t cmd,
147 int argc __attribute__ ((unused)),
148 char **args __attribute__ ((unused)))
150 struct grub_arg_list *state = cmd->state;
151 grub_file_t file;
152 grub_envblk_t envblk;
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)
158 grub_printf ("%s=%s\n", name, value);
159 return 0;
162 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
163 if (! file)
164 return grub_errno;
166 envblk = read_envblk_file (file);
167 if (! envblk)
168 goto fail;
170 grub_envblk_iterate (envblk, print_var);
171 grub_envblk_close (envblk);
173 fail:
174 grub_file_close (file);
175 return grub_errno;
178 /* Used to maintain a variable length of blocklists internally. */
179 struct blocklist
181 grub_disk_addr_t sector;
182 unsigned offset;
183 unsigned length;
184 struct blocklist *next;
187 static void
188 free_blocklists (struct blocklist *p)
190 struct blocklist *q;
192 for (; p; p = q)
194 q = p->next;
195 grub_free (p);
199 static grub_err_t
200 check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
201 grub_file_t file)
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;
210 /* Sanity checks. */
211 total_length = 0;
212 for (p = blocklists; p; p = p->next)
214 struct blocklist *q;
215 for (q = p->next; q; q = q->next)
217 /* Check if any pair of blocks overlap. */
218 if (p->sector == q->sector)
220 /* This might be actually valid, but it is unbelievable that
221 any filesystem makes such a silly allocation. */
222 return grub_error (GRUB_ERR_BAD_FS, "malformed file");
226 total_length += p->length;
229 if (total_length != grub_file_size (file))
231 /* Maybe sparse, unallocated sectors. No way in GRUB. */
232 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
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;
238 if (disk->partition)
239 part_start = grub_partition_get_start (disk->partition);
240 else
241 part_start = 0;
243 buf = grub_envblk_buffer (envblk);
244 for (p = blocklists, index = 0; p; index += p->length, p = p->next)
246 char blockbuf[GRUB_DISK_SECTOR_SIZE];
248 if (grub_disk_read (disk, p->sector - part_start,
249 p->offset, p->length, blockbuf))
250 return grub_errno;
252 if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
253 return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
256 return GRUB_ERR_NONE;
259 static int
260 write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
261 grub_file_t file)
263 char *buf;
264 grub_disk_t disk;
265 grub_disk_addr_t part_start;
266 struct blocklist *p;
267 grub_size_t index;
269 buf = grub_envblk_buffer (envblk);
270 disk = file->device->disk;
271 if (disk->partition)
272 part_start = grub_partition_get_start (disk->partition);
273 else
274 part_start = 0;
276 index = 0;
277 for (p = blocklists; p; index += p->length, p = p->next)
279 if (grub_disk_write (disk, p->sector - part_start,
280 p->offset, p->length, buf + index))
281 return 0;
284 return 1;
287 static grub_err_t
288 grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args)
290 struct grub_arg_list *state = cmd->state;
291 grub_file_t file;
292 grub_envblk_t envblk;
293 struct blocklist *head = 0;
294 struct blocklist *tail = 0;
296 /* Store blocklists in a linked list. */
297 auto void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
298 unsigned offset,
299 unsigned length);
300 void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
301 unsigned offset, unsigned length)
303 struct blocklist *block;
305 if (offset + length > GRUB_DISK_SECTOR_SIZE)
306 /* Seemingly a bug. */
307 return;
309 block = grub_malloc (sizeof (*block));
310 if (! block)
311 return;
313 block->sector = sector;
314 block->offset = offset;
315 block->length = length;
317 /* Slightly complicated, because the list should be FIFO. */
318 block->next = 0;
319 if (tail)
320 tail->next = block;
321 tail = block;
322 if (! head)
323 head = block;
326 if (! argc)
327 return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
329 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
330 if (! file)
331 return grub_errno;
333 if (! file->device->disk)
335 grub_file_close (file);
336 return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
339 file->read_hook = read_hook;
340 envblk = read_envblk_file (file);
341 file->read_hook = 0;
342 if (! envblk)
343 goto fail;
345 if (check_blocklists (envblk, head, file))
346 goto fail;
348 while (argc)
350 char *value;
352 value = grub_env_get (args[0]);
353 if (value)
355 if (! grub_envblk_set (envblk, args[0], value))
357 grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
358 goto fail;
362 argc--;
363 args++;
366 write_blocklists (envblk, head, file);
368 fail:
369 if (envblk)
370 grub_envblk_close (envblk);
371 free_blocklists (head);
372 grub_file_close (file);
373 return grub_errno;
376 static grub_extcmd_t cmd_load, cmd_list, cmd_save;
378 GRUB_MOD_INIT(loadenv)
380 cmd_load =
381 grub_register_extcmd ("load_env", grub_cmd_load_env,
382 GRUB_COMMAND_FLAG_BOTH,
383 N_("[-f FILE]"),
384 N_("Load variables from environment block file."),
385 options);
386 cmd_list =
387 grub_register_extcmd ("list_env", grub_cmd_list_env,
388 GRUB_COMMAND_FLAG_BOTH,
389 N_("[-f FILE]"),
390 N_("List variables from environment block file."),
391 options);
392 cmd_save =
393 grub_register_extcmd ("save_env", grub_cmd_save_env,
394 GRUB_COMMAND_FLAG_BOTH,
395 N_("[-f FILE] variable_name [...]"),
396 N_("Save variables to environment block file."),
397 options);
400 GRUB_MOD_FINI(loadenv)
402 grub_unregister_extcmd (cmd_load);
403 grub_unregister_extcmd (cmd_list);
404 grub_unregister_extcmd (cmd_save);