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>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 static const struct grub_arg_option options
[] =
37 /* TRANSLATORS: This option is used to override default filename
38 for loading and storing environment. */
39 {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME
},
41 N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE
},
45 /* Opens 'filename' with compression filters disabled. Optionally disables the
46 PUBKEY filter (that insists upon properly signed files) as well. PUBKEY
47 filter is restored before the function returns. */
49 open_envblk_file (char *filename
,
50 enum grub_file_type type
)
60 prefix
= grub_env_get ("prefix");
63 grub_error (GRUB_ERR_FILE_NOT_FOUND
, N_("variable `%s' isn't set"), "prefix");
67 len
= grub_strlen (prefix
);
68 buf
= grub_malloc (len
+ 1 + sizeof (GRUB_ENVBLK_DEFCFG
));
73 grub_strcpy (filename
, prefix
);
75 grub_strcpy (filename
+ len
+ 1, GRUB_ENVBLK_DEFCFG
);
78 file
= grub_file_open (filename
, type
);
85 grub_cmd_load_env (grub_extcmd_context_t ctxt
, int argc
, char **args
)
87 struct grub_arg_list
*state
= ctxt
->state
;
90 grub_env_whitelist_t whitelist
;
93 whitelist
.list
= args
;
95 /* state[0] is the -f flag; state[1] is the --skip-sig flag */
96 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0,
97 GRUB_FILE_TYPE_LOADENV
99 ? GRUB_FILE_TYPE_SKIP_SIGNATURE
: GRUB_FILE_TYPE_NONE
));
103 envblk
= read_envblk_file (file
);
107 /* argc > 0 indicates caller provided a whitelist of variables to read. */
108 grub_envblk_iterate (envblk
, argc
> 0 ? &whitelist
: 0, set_var
);
109 grub_envblk_close (envblk
);
112 grub_file_close (file
);
116 /* Print all variables in current context. */
118 print_var (const char *name
, const char *value
,
119 void *hook_data
__attribute__ ((unused
)))
121 grub_printf ("%s=%s\n", name
, value
);
126 grub_cmd_list_env (grub_extcmd_context_t ctxt
,
127 int argc
__attribute__ ((unused
)),
128 char **args
__attribute__ ((unused
)))
130 struct grub_arg_list
*state
= ctxt
->state
;
132 grub_envblk_t envblk
;
134 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0,
135 GRUB_FILE_TYPE_LOADENV
137 ? GRUB_FILE_TYPE_SKIP_SIGNATURE
: GRUB_FILE_TYPE_NONE
));
141 envblk
= read_envblk_file (file
);
145 grub_envblk_iterate (envblk
, NULL
, print_var
);
146 grub_envblk_close (envblk
);
149 grub_file_close (file
);
153 /* Used to maintain a variable length of blocklists internally. */
156 grub_disk_addr_t sector
;
159 struct blocklist
*next
;
163 free_blocklists (struct blocklist
*p
)
175 check_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
178 grub_size_t total_length
;
181 grub_disk_addr_t part_start
;
187 for (p
= blocklists
; p
; p
= p
->next
)
190 /* Check if any pair of blocks overlap. */
191 for (q
= p
->next
; q
; q
= q
->next
)
193 grub_disk_addr_t s1
, s2
;
194 grub_disk_addr_t e1
, e2
;
197 e1
= s1
+ ((p
->length
+ GRUB_DISK_SECTOR_SIZE
- 1) >> GRUB_DISK_SECTOR_BITS
);
200 e2
= s2
+ ((q
->length
+ GRUB_DISK_SECTOR_SIZE
- 1) >> GRUB_DISK_SECTOR_BITS
);
202 if (s1
< e2
&& s2
< e1
)
204 /* This might be actually valid, but it is unbelievable that
205 any filesystem makes such a silly allocation. */
206 return grub_error (GRUB_ERR_BAD_FS
, "malformed file");
210 total_length
+= p
->length
;
213 if (total_length
!= grub_file_size (file
))
215 /* Maybe sparse, unallocated sectors. No way in GRUB. */
216 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "sparse file not allowed");
219 /* One more sanity check. Re-read all sectors by blocklists, and compare
220 those with the data read via a file. */
221 disk
= file
->device
->disk
;
223 part_start
= grub_partition_get_start (disk
->partition
);
225 buf
= grub_envblk_buffer (envblk
);
226 char *blockbuf
= NULL
;
227 grub_size_t blockbuf_len
= 0;
228 for (p
= blocklists
, index
= 0; p
; index
+= p
->length
, p
= p
->next
)
230 if (p
->length
> blockbuf_len
)
232 grub_free (blockbuf
);
233 blockbuf_len
= 2 * p
->length
;
234 blockbuf
= grub_malloc (blockbuf_len
);
239 if (grub_disk_read (disk
, p
->sector
- part_start
,
240 p
->offset
, p
->length
, blockbuf
))
243 if (grub_memcmp (buf
+ index
, blockbuf
, p
->length
) != 0)
244 return grub_error (GRUB_ERR_FILE_READ_ERROR
, "invalid blocklist");
247 return GRUB_ERR_NONE
;
251 write_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
256 grub_disk_addr_t part_start
;
260 buf
= grub_envblk_buffer (envblk
);
261 disk
= file
->device
->disk
;
262 part_start
= grub_partition_get_start (disk
->partition
);
265 for (p
= blocklists
; p
; index
+= p
->length
, p
= p
->next
)
267 if (grub_disk_write (disk
, p
->sector
- part_start
,
268 p
->offset
, p
->length
, buf
+ index
))
275 /* Context for grub_cmd_save_env. */
276 struct grub_cmd_save_env_ctx
278 struct blocklist
*head
, *tail
;
281 /* Store blocklists in a linked list. */
283 save_env_read_hook (grub_disk_addr_t sector
, unsigned offset
, unsigned length
,
286 struct grub_cmd_save_env_ctx
*ctx
= data
;
287 struct blocklist
*block
;
289 block
= grub_malloc (sizeof (*block
));
293 block
->sector
= sector
;
294 block
->offset
= offset
;
295 block
->length
= length
;
297 /* Slightly complicated, because the list should be FIFO. */
300 ctx
->tail
->next
= block
;
307 grub_cmd_save_env (grub_extcmd_context_t ctxt
, int argc
, char **args
)
309 struct grub_arg_list
*state
= ctxt
->state
;
311 grub_envblk_t envblk
;
312 struct grub_cmd_save_env_ctx ctx
= {
318 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "no variable is specified");
320 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0,
321 GRUB_FILE_TYPE_SAVEENV
322 | GRUB_FILE_TYPE_SKIP_SIGNATURE
);
326 if (! file
->device
->disk
)
328 grub_file_close (file
);
329 return grub_error (GRUB_ERR_BAD_DEVICE
, "disk device required");
332 file
->read_hook
= save_env_read_hook
;
333 file
->read_hook_data
= &ctx
;
334 envblk
= read_envblk_file (file
);
339 if (check_blocklists (envblk
, ctx
.head
, file
))
346 value
= grub_env_get (args
[0]);
349 if (! grub_envblk_set (envblk
, args
[0], value
))
351 grub_error (GRUB_ERR_BAD_ARGUMENT
, "environment block too small");
356 grub_envblk_delete (envblk
, args
[0]);
362 write_blocklists (envblk
, ctx
.head
, file
);
366 grub_envblk_close (envblk
);
367 free_blocklists (ctx
.head
);
368 grub_file_close (file
);
372 static grub_extcmd_t cmd_load
, cmd_list
, cmd_save
;
374 GRUB_MOD_INIT(loadenv
)
377 grub_register_extcmd ("load_env", grub_cmd_load_env
, 0,
378 N_("[-f FILE] [-s|--skip-sig] [variable_name_to_whitelist] [...]"),
379 N_("Load variables from environment block file."),
382 grub_register_extcmd ("list_env", grub_cmd_list_env
, 0, N_("[-f FILE]"),
383 N_("List variables from environment block file."),
386 grub_register_extcmd ("save_env", grub_cmd_save_env
, 0,
387 N_("[-f FILE] variable_name [...]"),
388 N_("Save variables to environment block file."),
392 GRUB_MOD_FINI(loadenv
)
394 grub_unregister_extcmd (cmd_load
);
395 grub_unregister_extcmd (cmd_list
);
396 grub_unregister_extcmd (cmd_save
);