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
;
211 for (p
= blocklists
; p
; p
= p
->next
)
214 for (q
= p
->next
; q
; q
= q
->next
)
216 /* Check if any pair of blocks overlap. */
217 if (p
->sector
== q
->sector
)
219 /* This might be actually valid, but it is unbelievable that
220 any filesystem makes such a silly allocation. */
221 grub_error (GRUB_ERR_BAD_FS
, "malformed file");
226 total_length
+= p
->length
;
229 if (total_length
!= grub_file_size (file
))
231 /* Maybe sparse, unallocated sectors. No way in GRUB. */
232 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "sparse file not allowed");
236 /* One more sanity check. Re-read all sectors by blocklists, and compare
237 those with the data read via a file. */
238 disk
= file
->device
->disk
;
240 part_start
= grub_partition_get_start (disk
->partition
);
244 buf
= grub_envblk_buffer (envblk
);
245 for (p
= blocklists
, index
= 0; p
; index
+= p
->length
, p
= p
->next
)
247 char blockbuf
[GRUB_DISK_SECTOR_SIZE
];
249 if (grub_disk_read (disk
, p
->sector
- part_start
,
250 p
->offset
, p
->length
, blockbuf
))
253 if (grub_memcmp (buf
+ index
, blockbuf
, p
->length
) != 0)
255 grub_error (GRUB_ERR_FILE_READ_ERROR
, "invalid blocklist");
264 write_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
269 grub_disk_addr_t part_start
;
273 buf
= grub_envblk_buffer (envblk
);
274 disk
= file
->device
->disk
;
276 part_start
= grub_partition_get_start (disk
->partition
);
281 for (p
= blocklists
; p
; index
+= p
->length
, p
= p
->next
)
283 if (grub_disk_write (disk
, p
->sector
- part_start
,
284 p
->offset
, p
->length
, buf
+ index
))
292 grub_cmd_save_env (grub_extcmd_t cmd
, int argc
, char **args
)
294 struct grub_arg_list
*state
= cmd
->state
;
296 grub_envblk_t envblk
;
297 struct blocklist
*head
= 0;
298 struct blocklist
*tail
= 0;
300 /* Store blocklists in a linked list. */
301 auto void NESTED_FUNC_ATTR
read_hook (grub_disk_addr_t sector
,
304 void NESTED_FUNC_ATTR
read_hook (grub_disk_addr_t sector
,
305 unsigned offset
, unsigned length
)
307 struct blocklist
*block
;
309 if (offset
+ length
> GRUB_DISK_SECTOR_SIZE
)
310 /* Seemingly a bug. */
313 block
= grub_malloc (sizeof (*block
));
317 block
->sector
= sector
;
318 block
->offset
= offset
;
319 block
->length
= length
;
321 /* Slightly complicated, because the list should be FIFO. */
331 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "No variable is specified");
333 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
337 if (! file
->device
->disk
)
339 grub_file_close (file
);
340 return grub_error (GRUB_ERR_BAD_DEVICE
, "disk device required");
343 file
->read_hook
= read_hook
;
344 envblk
= read_envblk_file (file
);
349 if (! check_blocklists (envblk
, head
, file
))
356 value
= grub_env_get (args
[0]);
359 if (! grub_envblk_set (envblk
, args
[0], value
))
361 grub_error (GRUB_ERR_BAD_ARGUMENT
, "environment block too small");
370 write_blocklists (envblk
, head
, file
);
374 grub_envblk_close (envblk
);
375 free_blocklists (head
);
376 grub_file_close (file
);
380 static grub_extcmd_t cmd_load
, cmd_list
, cmd_save
;
382 GRUB_MOD_INIT(loadenv
)
385 grub_register_extcmd ("load_env", grub_cmd_load_env
,
386 GRUB_COMMAND_FLAG_BOTH
,
387 "load_env [-f FILE]",
388 "Load variables from environment block file.",
391 grub_register_extcmd ("list_env", grub_cmd_list_env
,
392 GRUB_COMMAND_FLAG_BOTH
,
393 "list_env [-f FILE]",
394 "List variables from environment block file.",
397 grub_register_extcmd ("save_env", grub_cmd_save_env
,
398 GRUB_COMMAND_FLAG_BOTH
,
399 "save_env [-f FILE] variable_name [...]",
400 "Save variables to environment block file.",
404 GRUB_MOD_FINI(loadenv
)
406 grub_unregister_extcmd (cmd_load
);
407 grub_unregister_extcmd (cmd_list
);
408 grub_unregister_extcmd (cmd_save
);