removed accidental commit
[grub2/phcoder.git] / commands / loadenv.c
blob1cbf0d492e80ea5c007cfe8b8a6218db281b8aac
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 grub_partition_t part;
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 grub_error (GRUB_ERR_BAD_FS, "malformed file");
223 return 0;
227 total_length += p->length;
230 if (total_length != grub_file_size (file))
232 /* Maybe sparse, unallocated sectors. No way in GRUB. */
233 grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
234 return 0;
237 /* One more sanity check. Re-read all sectors by blocklists, and compare
238 those with the data read via a file. */
239 disk = file->device->disk;
241 part_start = 0;
242 for (part = disk->partition; part; part = part->parent)
243 part_start += grub_partition_get_start (part);
245 buf = grub_envblk_buffer (envblk);
246 for (p = blocklists, index = 0; p; p = p->next, index += p->length)
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 0;
254 if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
256 grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
257 return 0;
261 return 1;
264 static int
265 write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
266 grub_file_t file)
268 char *buf;
269 grub_disk_t disk;
270 grub_disk_addr_t part_start;
271 struct blocklist *p;
272 grub_size_t index;
274 buf = grub_envblk_buffer (envblk);
275 disk = file->device->disk;
276 if (disk->partition)
277 part_start = grub_partition_get_start (disk->partition);
278 else
279 part_start = 0;
281 index = 0;
282 for (p = blocklists; p; p = p->next, index += p->length)
284 if (grub_disk_write (disk, p->sector - part_start,
285 p->offset, p->length, buf + index))
286 return 0;
289 return 1;
292 static grub_err_t
293 grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args)
295 struct grub_arg_list *state = cmd->state;
296 grub_file_t file;
297 grub_envblk_t envblk;
298 struct blocklist *head = 0;
299 struct blocklist *tail = 0;
301 /* Store blocklists in a linked list. */
302 auto void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
303 unsigned offset,
304 unsigned length);
305 void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
306 unsigned offset, unsigned length)
308 struct blocklist *block;
310 if (offset + length > GRUB_DISK_SECTOR_SIZE)
311 /* Seemingly a bug. */
312 return;
314 block = grub_malloc (sizeof (*block));
315 if (! block)
316 return;
318 block->sector = sector;
319 block->offset = offset;
320 block->length = length;
322 /* Slightly complicated, because the list should be FIFO. */
323 block->next = 0;
324 if (tail)
325 tail->next = block;
326 tail = block;
327 if (! head)
328 head = block;
331 if (! argc)
332 return grub_error (GRUB_ERR_BAD_ARGUMENT, "No variable is specified");
334 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
335 if (! file)
336 return grub_errno;
338 if (! file->device->disk)
340 grub_file_close (file);
341 return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
344 file->read_hook = read_hook;
345 envblk = read_envblk_file (file);
346 file->read_hook = 0;
347 if (! envblk)
348 goto fail;
350 if (! check_blocklists (envblk, head, file))
351 goto fail;
353 while (argc)
355 char *value;
357 value = grub_env_get (args[0]);
358 if (value)
360 if (! grub_envblk_set (envblk, args[0], value))
362 grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
363 goto fail;
367 argc--;
368 args++;
371 write_blocklists (envblk, head, file);
373 fail:
374 if (envblk)
375 grub_envblk_close (envblk);
376 free_blocklists (head);
377 grub_file_close (file);
378 return grub_errno;
381 static grub_extcmd_t cmd_load, cmd_list, cmd_save;
383 GRUB_MOD_INIT(loadenv)
385 cmd_load =
386 grub_register_extcmd ("load_env", grub_cmd_load_env,
387 GRUB_COMMAND_FLAG_BOTH,
388 "load_env [-f FILE]",
389 "Load variables from environment block file.",
390 options);
391 cmd_list =
392 grub_register_extcmd ("list_env", grub_cmd_list_env,
393 GRUB_COMMAND_FLAG_BOTH,
394 "list_env [-f FILE]",
395 "List variables from environment block file.",
396 options);
397 cmd_save =
398 grub_register_extcmd ("save_env", grub_cmd_save_env,
399 GRUB_COMMAND_FLAG_BOTH,
400 "save_env [-f FILE] variable_name [...]",
401 "Save variables to environment block file.",
402 options);
405 GRUB_MOD_FINI(loadenv)
407 grub_unregister_extcmd (cmd_load);
408 grub_unregister_extcmd (cmd_list);
409 grub_unregister_extcmd (cmd_save);