1 /* loadenv.c - command to load/save environment variable. */
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/>.
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>
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
},
42 open_envblk_file (char *filename
)
50 prefix
= grub_env_get ("prefix");
55 len
= grub_strlen (prefix
);
56 filename
= grub_malloc (len
+ 1 + sizeof (GRUB_ENVBLK_DEFCFG
));
60 grub_strcpy (filename
, prefix
);
62 grub_strcpy (filename
+ len
+ 1, GRUB_ENVBLK_DEFCFG
);
63 grub_file_filter_disable_compression ();
64 file
= grub_file_open (filename
);
70 grub_error (GRUB_ERR_FILE_NOT_FOUND
, N_("variable `%s' isn't set"), "prefix");
75 grub_file_filter_disable_compression ();
76 return grub_file_open (filename
);
80 read_envblk_file (grub_file_t file
)
82 grub_off_t offset
= 0;
84 grub_size_t size
= grub_file_size (file
);
87 buf
= grub_malloc (size
);
95 ret
= grub_file_read (file
, buf
+ offset
, size
);
106 envblk
= grub_envblk_open (buf
, offset
);
110 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid environment block");
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
;
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
);
133 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
137 envblk
= read_envblk_file (file
);
141 grub_envblk_iterate (envblk
, set_var
);
142 grub_envblk_close (envblk
);
145 grub_file_close (file
);
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
;
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
);
166 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
170 envblk
= read_envblk_file (file
);
174 grub_envblk_iterate (envblk
, print_var
);
175 grub_envblk_close (envblk
);
178 grub_file_close (file
);
182 /* Used to maintain a variable length of blocklists internally. */
185 grub_disk_addr_t sector
;
188 struct blocklist
*next
;
192 free_blocklists (struct blocklist
*p
)
204 check_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
207 grub_size_t total_length
;
210 grub_disk_addr_t part_start
;
216 for (p
= blocklists
; p
; p
= p
->next
)
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
))
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
;
262 write_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
267 grub_disk_addr_t part_start
;
271 buf
= grub_envblk_buffer (envblk
);
272 disk
= file
->device
->disk
;
273 part_start
= grub_partition_get_start (disk
->partition
);
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
))
287 grub_cmd_save_env (grub_extcmd_context_t ctxt
, int argc
, char **args
)
289 struct grub_arg_list
*state
= ctxt
->state
;
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
,
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. */
308 block
= grub_malloc (sizeof (*block
));
312 block
->sector
= sector
;
313 block
->offset
= offset
;
314 block
->length
= length
;
316 /* Slightly complicated, because the list should be FIFO. */
326 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "no variable is specified");
328 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
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
);
344 if (check_blocklists (envblk
, head
, file
))
351 value
= grub_env_get (args
[0]);
354 if (! grub_envblk_set (envblk
, args
[0], value
))
356 grub_error (GRUB_ERR_BAD_ARGUMENT
, "environment block too small");
365 write_blocklists (envblk
, head
, file
);
369 grub_envblk_close (envblk
);
370 free_blocklists (head
);
371 grub_file_close (file
);
375 static grub_extcmd_t cmd_load
, cmd_list
, cmd_save
;
377 GRUB_MOD_INIT(loadenv
)
380 grub_register_extcmd ("load_env", grub_cmd_load_env
, 0, N_("[-f FILE]"),
381 N_("Load variables from environment block file."),
384 grub_register_extcmd ("list_env", grub_cmd_list_env
, 0, N_("[-f FILE]"),
385 N_("List variables from environment block file."),
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."),
394 GRUB_MOD_FINI(loadenv
)
396 grub_unregister_extcmd (cmd_load
);
397 grub_unregister_extcmd (cmd_list
);
398 grub_unregister_extcmd (cmd_save
);