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 static const struct grub_arg_option options
[] =
33 {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME
},
38 open_envblk_file (char *filename
)
46 prefix
= grub_env_get ("prefix");
51 len
= grub_strlen (prefix
);
52 filename
= grub_malloc (len
+ 1 + sizeof (GRUB_ENVBLK_DEFCFG
));
56 grub_strcpy (filename
, prefix
);
58 grub_strcpy (filename
+ len
+ 1, GRUB_ENVBLK_DEFCFG
);
59 file
= grub_file_open (filename
);
65 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "prefix is not found");
70 return grub_file_open (filename
);
74 read_envblk_file (grub_file_t file
)
76 grub_off_t offset
= 0;
78 grub_size_t size
= grub_file_size (file
);
81 buf
= grub_malloc (size
);
89 ret
= grub_file_read (file
, buf
+ offset
, size
);
92 if (grub_errno
== GRUB_ERR_NONE
)
93 grub_error (GRUB_ERR_FILE_READ_ERROR
, "cannot read");
102 envblk
= grub_envblk_open (buf
, offset
);
106 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid environment block");
114 grub_cmd_load_env (grub_extcmd_t cmd
,
115 int argc
__attribute__ ((unused
)),
116 char **args
__attribute__ ((unused
)))
118 struct grub_arg_list
*state
= cmd
->state
;
120 grub_envblk_t envblk
;
122 auto int set_var (const char *name
, const char *value
);
123 int set_var (const char *name
, const char *value
)
125 grub_env_set (name
, value
);
129 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
133 envblk
= read_envblk_file (file
);
137 grub_envblk_iterate (envblk
, set_var
);
138 grub_envblk_close (envblk
);
141 grub_file_close (file
);
146 grub_cmd_list_env (grub_extcmd_t cmd
,
147 int argc
__attribute__ ((unused
)),
148 char **args
__attribute__ ((unused
)))
150 struct grub_arg_list
*state
= cmd
->state
;
152 grub_envblk_t envblk
;
154 /* Print all variables in current context. */
155 auto int print_var (const char *name
, const char *value
);
156 int print_var (const char *name
, const char *value
)
158 grub_printf ("%s=%s\n", name
, value
);
162 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
166 envblk
= read_envblk_file (file
);
170 grub_envblk_iterate (envblk
, print_var
);
171 grub_envblk_close (envblk
);
174 grub_file_close (file
);
178 /* Used to maintain a variable length of blocklists internally. */
181 grub_disk_addr_t sector
;
184 struct blocklist
*next
;
188 free_blocklists (struct blocklist
*p
)
200 check_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
203 grub_size_t total_length
;
206 grub_disk_addr_t part_start
;
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 return 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 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "sparse file not allowed");
235 /* One more sanity check. Re-read all sectors by blocklists, and compare
236 those with the data read via a file. */
237 disk
= file
->device
->disk
;
239 part_start
= grub_partition_get_start (disk
->partition
);
243 buf
= grub_envblk_buffer (envblk
);
244 for (p
= blocklists
, index
= 0; p
; index
+= p
->length
, p
= p
->next
)
246 char blockbuf
[GRUB_DISK_SECTOR_SIZE
];
248 if (grub_disk_read (disk
, p
->sector
- part_start
,
249 p
->offset
, p
->length
, blockbuf
))
252 if (grub_memcmp (buf
+ index
, blockbuf
, p
->length
) != 0)
253 return grub_error (GRUB_ERR_FILE_READ_ERROR
, "invalid blocklist");
256 return GRUB_ERR_NONE
;
260 write_blocklists (grub_envblk_t envblk
, struct blocklist
*blocklists
,
265 grub_disk_addr_t part_start
;
269 buf
= grub_envblk_buffer (envblk
);
270 disk
= file
->device
->disk
;
272 part_start
= grub_partition_get_start (disk
->partition
);
277 for (p
= blocklists
; p
; index
+= p
->length
, p
= p
->next
)
279 if (grub_disk_write (disk
, p
->sector
- part_start
,
280 p
->offset
, p
->length
, buf
+ index
))
288 grub_cmd_save_env (grub_extcmd_t cmd
, int argc
, char **args
)
290 struct grub_arg_list
*state
= cmd
->state
;
292 grub_envblk_t envblk
;
293 struct blocklist
*head
= 0;
294 struct blocklist
*tail
= 0;
296 /* Store blocklists in a linked list. */
297 auto void NESTED_FUNC_ATTR
read_hook (grub_disk_addr_t sector
,
300 void NESTED_FUNC_ATTR
read_hook (grub_disk_addr_t sector
,
301 unsigned offset
, unsigned length
)
303 struct blocklist
*block
;
305 if (offset
+ length
> GRUB_DISK_SECTOR_SIZE
)
306 /* Seemingly a bug. */
309 block
= grub_malloc (sizeof (*block
));
313 block
->sector
= sector
;
314 block
->offset
= offset
;
315 block
->length
= length
;
317 /* Slightly complicated, because the list should be FIFO. */
327 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "no variable is specified");
329 file
= open_envblk_file ((state
[0].set
) ? state
[0].arg
: 0);
333 if (! file
->device
->disk
)
335 grub_file_close (file
);
336 return grub_error (GRUB_ERR_BAD_DEVICE
, "disk device required");
339 file
->read_hook
= read_hook
;
340 envblk
= read_envblk_file (file
);
345 if (check_blocklists (envblk
, head
, file
))
352 value
= grub_env_get (args
[0]);
355 if (! grub_envblk_set (envblk
, args
[0], value
))
357 grub_error (GRUB_ERR_BAD_ARGUMENT
, "environment block too small");
366 write_blocklists (envblk
, head
, file
);
370 grub_envblk_close (envblk
);
371 free_blocklists (head
);
372 grub_file_close (file
);
376 static grub_extcmd_t cmd_load
, cmd_list
, cmd_save
;
378 GRUB_MOD_INIT(loadenv
)
381 grub_register_extcmd ("load_env", grub_cmd_load_env
,
382 GRUB_COMMAND_FLAG_BOTH
,
384 N_("Load variables from environment block file."),
387 grub_register_extcmd ("list_env", grub_cmd_list_env
,
388 GRUB_COMMAND_FLAG_BOTH
,
390 N_("List variables from environment block file."),
393 grub_register_extcmd ("save_env", grub_cmd_save_env
,
394 GRUB_COMMAND_FLAG_BOTH
,
395 N_("[-f FILE] variable_name [...]"),
396 N_("Save variables to environment block file."),
400 GRUB_MOD_FINI(loadenv
)
402 grub_unregister_extcmd (cmd_load
);
403 grub_unregister_extcmd (cmd_list
);
404 grub_unregister_extcmd (cmd_save
);