1 /* loadenv.c - command to load/save environment variable. */
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/>.
22 #include <grub/file.h>
23 #include <grub/disk.h>
24 #include <grub/misc.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
},
37 open_envblk_file (char *filename
)
45 prefix
= grub_env_get ("prefix");
50 len
= grub_strlen (prefix
);
51 filename
= grub_malloc (len
+ 1 + sizeof (GRUB_ENVBLK_DEFCFG
));
55 grub_strcpy (filename
, prefix
);
57 grub_strcpy (filename
+ len
+ 1, GRUB_ENVBLK_DEFCFG
);
58 file
= grub_file_open (filename
);
64 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "prefix is not found");
69 return grub_file_open (filename
);
73 read_envblk_file (grub_file_t file
)
75 grub_off_t offset
= 0;
77 grub_size_t size
= grub_file_size (file
);
80 buf
= grub_malloc (size
);
88 ret
= grub_file_read (file
, buf
+ offset
, size
);
91 if (grub_errno
== GRUB_ERR_NONE
)
92 grub_error (GRUB_ERR_FILE_READ_ERROR
, "cannot read");
101 envblk
= grub_envblk_open (buf
, offset
);
105 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid environment block");
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
;
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
);
128 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
132 envblk
= read_envblk_file (file
);
136 grub_envblk_iterate (envblk
, set_var
);
137 grub_envblk_close (envblk
);
140 grub_file_close (file
);
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
;
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
);
161 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
165 envblk
= read_envblk_file (file
);
169 grub_envblk_iterate (envblk
, print_var
);
170 grub_envblk_close (envblk
);
173 grub_file_close (file
);
177 /* Used to maintain a variable length of blocklists internally. */
180 grub_disk_addr_t sector
;
183 struct blocklist
*next
;
187 free_blocklists (struct blocklist
*p
)
199 check_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
202 grub_size_t total_length
;
205 grub_disk_addr_t part_start
;
207 grub_partition_t part
;
212 for (p
= blocklists
; p
; p
= p
->next
)
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");
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");
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
;
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
))
254 if (grub_memcmp (buf
+ index
, blockbuf
, p
->length
) != 0)
256 grub_error (GRUB_ERR_FILE_READ_ERROR
, "invalid blocklist");
265 write_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
270 grub_disk_addr_t part_start
;
274 buf
= grub_envblk_buffer (envblk
);
275 disk
= file
->device
->disk
;
277 part_start
= grub_partition_get_start (disk
->partition
);
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
))
293 grub_cmd_save_env (grub_extcmd_t cmd
, int argc
, char **args
)
295 struct grub_arg_list
*state
= cmd
->state
;
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
,
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. */
314 block
= grub_malloc (sizeof (*block
));
318 block
->sector
= sector
;
319 block
->offset
= offset
;
320 block
->length
= length
;
322 /* Slightly complicated, because the list should be FIFO. */
332 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "No variable is specified");
334 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
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
);
350 if (! check_blocklists (envblk
, head
, file
))
357 value
= grub_env_get (args
[0]);
360 if (! grub_envblk_set (envblk
, args
[0], value
))
362 grub_error (GRUB_ERR_BAD_ARGUMENT
, "environment block too small");
371 write_blocklists (envblk
, head
, file
);
375 grub_envblk_close (envblk
);
376 free_blocklists (head
);
377 grub_file_close (file
);
381 static grub_extcmd_t cmd_load
, cmd_list
, cmd_save
;
383 GRUB_MOD_INIT(loadenv
)
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.",
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.",
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.",
405 GRUB_MOD_FINI(loadenv
)
407 grub_unregister_extcmd (cmd_load
);
408 grub_unregister_extcmd (cmd_list
);
409 grub_unregister_extcmd (cmd_save
);