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
},
39 N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE
},
43 /* Opens 'filename' with compression filters disabled. Optionally disables the
44 PUBKEY filter (that insists upon properly signed files) as well. PUBKEY
45 filter is restored before the function returns. */
47 open_envblk_file (char *filename
, int untrusted
)
57 prefix
= grub_env_get ("prefix");
60 grub_error (GRUB_ERR_FILE_NOT_FOUND
, N_("variable `%s' isn't set"), "prefix");
64 len
= grub_strlen (prefix
);
65 buf
= grub_malloc (len
+ 1 + sizeof (GRUB_ENVBLK_DEFCFG
));
70 grub_strcpy (filename
, prefix
);
72 grub_strcpy (filename
+ len
+ 1, GRUB_ENVBLK_DEFCFG
);
75 /* The filters that are disabled will be re-enabled by the call to
76 grub_file_open() after this particular file is opened. */
77 grub_file_filter_disable_compression ();
79 grub_file_filter_disable_pubkey ();
81 file
= grub_file_open (filename
);
88 read_envblk_file (grub_file_t file
)
90 grub_off_t offset
= 0;
92 grub_size_t size
= grub_file_size (file
);
95 buf
= grub_malloc (size
);
103 ret
= grub_file_read (file
, buf
+ offset
, size
);
114 envblk
= grub_envblk_open (buf
, offset
);
118 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid environment block");
125 struct grub_env_whitelist
130 typedef struct grub_env_whitelist grub_env_whitelist_t
;
133 test_whitelist_membership (const char* name
,
134 const grub_env_whitelist_t
* whitelist
)
138 for (i
= 0; i
< whitelist
->len
; i
++)
139 if (grub_strcmp (name
, whitelist
->list
[i
]) == 0)
140 return 1; /* found it */
142 return 0; /* not found */
145 /* Helper for grub_cmd_load_env. */
147 set_var (const char *name
, const char *value
, void *whitelist
)
151 grub_env_set (name
, value
);
155 if (test_whitelist_membership (name
,
156 (const grub_env_whitelist_t
*) whitelist
))
157 grub_env_set (name
, value
);
163 grub_cmd_load_env (grub_extcmd_context_t ctxt
, int argc
, char **args
)
165 struct grub_arg_list
*state
= ctxt
->state
;
167 grub_envblk_t envblk
;
168 grub_env_whitelist_t whitelist
;
170 whitelist
.len
= argc
;
171 whitelist
.list
= args
;
173 /* state[0] is the -f flag; state[1] is the --skip-sig flag */
174 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0, state
[1].set
);
178 envblk
= read_envblk_file (file
);
182 /* argc > 0 indicates caller provided a whitelist of variables to read. */
183 grub_envblk_iterate (envblk
, argc
> 0 ? &whitelist
: 0, set_var
);
184 grub_envblk_close (envblk
);
187 grub_file_close (file
);
191 /* Print all variables in current context. */
193 print_var (const char *name
, const char *value
,
194 void *hook_data
__attribute__ ((unused
)))
196 grub_printf ("%s=%s\n", name
, value
);
201 grub_cmd_list_env (grub_extcmd_context_t ctxt
,
202 int argc
__attribute__ ((unused
)),
203 char **args
__attribute__ ((unused
)))
205 struct grub_arg_list
*state
= ctxt
->state
;
207 grub_envblk_t envblk
;
209 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0, 0);
213 envblk
= read_envblk_file (file
);
217 grub_envblk_iterate (envblk
, NULL
, print_var
);
218 grub_envblk_close (envblk
);
221 grub_file_close (file
);
225 /* Used to maintain a variable length of blocklists internally. */
228 grub_disk_addr_t sector
;
231 struct blocklist
*next
;
235 free_blocklists (struct blocklist
*p
)
247 check_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
250 grub_size_t total_length
;
253 grub_disk_addr_t part_start
;
259 for (p
= blocklists
; p
; p
= p
->next
)
262 /* Check if any pair of blocks overlap. */
263 for (q
= p
->next
; q
; q
= q
->next
)
265 grub_disk_addr_t s1
, s2
;
266 grub_disk_addr_t e1
, e2
;
269 e1
= s1
+ ((p
->length
+ GRUB_DISK_SECTOR_SIZE
- 1) >> GRUB_DISK_SECTOR_BITS
);
272 e2
= s2
+ ((q
->length
+ GRUB_DISK_SECTOR_SIZE
- 1) >> GRUB_DISK_SECTOR_BITS
);
274 if (s1
< e2
&& s2
< e1
)
276 /* This might be actually valid, but it is unbelievable that
277 any filesystem makes such a silly allocation. */
278 return grub_error (GRUB_ERR_BAD_FS
, "malformed file");
282 total_length
+= p
->length
;
285 if (total_length
!= grub_file_size (file
))
287 /* Maybe sparse, unallocated sectors. No way in GRUB. */
288 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "sparse file not allowed");
291 /* One more sanity check. Re-read all sectors by blocklists, and compare
292 those with the data read via a file. */
293 disk
= file
->device
->disk
;
295 part_start
= grub_partition_get_start (disk
->partition
);
297 buf
= grub_envblk_buffer (envblk
);
298 char *blockbuf
= NULL
;
299 grub_size_t blockbuf_len
= 0;
300 for (p
= blocklists
, index
= 0; p
; index
+= p
->length
, p
= p
->next
)
302 if (p
->length
> blockbuf_len
)
304 grub_free (blockbuf
);
305 blockbuf_len
= 2 * p
->length
;
306 blockbuf
= grub_malloc (blockbuf_len
);
311 if (grub_disk_read (disk
, p
->sector
- part_start
,
312 p
->offset
, p
->length
, blockbuf
))
315 if (grub_memcmp (buf
+ index
, blockbuf
, p
->length
) != 0)
316 return grub_error (GRUB_ERR_FILE_READ_ERROR
, "invalid blocklist");
319 return GRUB_ERR_NONE
;
323 write_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
328 grub_disk_addr_t part_start
;
332 buf
= grub_envblk_buffer (envblk
);
333 disk
= file
->device
->disk
;
334 part_start
= grub_partition_get_start (disk
->partition
);
337 for (p
= blocklists
; p
; index
+= p
->length
, p
= p
->next
)
339 if (grub_disk_write (disk
, p
->sector
- part_start
,
340 p
->offset
, p
->length
, buf
+ index
))
347 /* Context for grub_cmd_save_env. */
348 struct grub_cmd_save_env_ctx
350 struct blocklist
*head
, *tail
;
353 /* Store blocklists in a linked list. */
355 save_env_read_hook (grub_disk_addr_t sector
, unsigned offset
, unsigned length
,
358 struct grub_cmd_save_env_ctx
*ctx
= data
;
359 struct blocklist
*block
;
361 block
= grub_malloc (sizeof (*block
));
365 block
->sector
= sector
;
366 block
->offset
= offset
;
367 block
->length
= length
;
369 /* Slightly complicated, because the list should be FIFO. */
372 ctx
->tail
->next
= block
;
379 grub_cmd_save_env (grub_extcmd_context_t ctxt
, int argc
, char **args
)
381 struct grub_arg_list
*state
= ctxt
->state
;
383 grub_envblk_t envblk
;
384 struct grub_cmd_save_env_ctx ctx
= {
390 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "no variable is specified");
392 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0,
393 1 /* allow untrusted */);
397 if (! file
->device
->disk
)
399 grub_file_close (file
);
400 return grub_error (GRUB_ERR_BAD_DEVICE
, "disk device required");
403 file
->read_hook
= save_env_read_hook
;
404 file
->read_hook_data
= &ctx
;
405 envblk
= read_envblk_file (file
);
410 if (check_blocklists (envblk
, ctx
.head
, file
))
417 value
= grub_env_get (args
[0]);
420 if (! grub_envblk_set (envblk
, args
[0], value
))
422 grub_error (GRUB_ERR_BAD_ARGUMENT
, "environment block too small");
427 grub_envblk_delete (envblk
, args
[0]);
433 write_blocklists (envblk
, ctx
.head
, file
);
437 grub_envblk_close (envblk
);
438 free_blocklists (ctx
.head
);
439 grub_file_close (file
);
443 static grub_extcmd_t cmd_load
, cmd_list
, cmd_save
;
445 GRUB_MOD_INIT(loadenv
)
448 grub_register_extcmd ("load_env", grub_cmd_load_env
, 0,
449 N_("[-f FILE] [-s|--skip-sig] [variable_name_to_whitelist] [...]"),
450 N_("Load variables from environment block file."),
453 grub_register_extcmd ("list_env", grub_cmd_list_env
, 0, N_("[-f FILE]"),
454 N_("List variables from environment block file."),
457 grub_register_extcmd ("save_env", grub_cmd_save_env
, 0,
458 N_("[-f FILE] variable_name [...]"),
459 N_("Save variables to environment block file."),
463 GRUB_MOD_FINI(loadenv
)
465 grub_unregister_extcmd (cmd_load
);
466 grub_unregister_extcmd (cmd_list
);
467 grub_unregister_extcmd (cmd_save
);