]> git.proxmox.com Git - grub2.git/blob - commands/loadenv.c
merge with mainline
[grub2.git] / commands / loadenv.c
1 /* loadenv.c - command to load/save environment variable. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
20 #include <grub/dl.h>
21 #include <grub/mm.h>
22 #include <grub/file.h>
23 #include <grub/disk.h>
24 #include <grub/misc.h>
25 #include <grub/env.h>
26 #include <grub/partition.h>
27 #include <grub/lib/envblk.h>
28 #include <grub/extcmd.h>
29 #include <grub/i18n.h>
30
31 static const struct grub_arg_option options[] =
32 {
33 {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
34 {0, 0, 0, 0, 0, 0}
35 };
36
37 static grub_file_t
38 open_envblk_file (char *filename)
39 {
40 grub_file_t file;
41
42 if (! filename)
43 {
44 char *prefix;
45
46 prefix = grub_env_get ("prefix");
47 if (prefix)
48 {
49 int len;
50
51 len = grub_strlen (prefix);
52 filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
53 if (! filename)
54 return 0;
55
56 grub_strcpy (filename, prefix);
57 filename[len] = '/';
58 grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
59 file = grub_file_open (filename);
60 grub_free (filename);
61 return file;
62 }
63 else
64 {
65 grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found");
66 return 0;
67 }
68 }
69
70 return grub_file_open (filename);
71 }
72
73 static grub_envblk_t
74 read_envblk_file (grub_file_t file)
75 {
76 grub_off_t offset = 0;
77 char *buf;
78 grub_size_t size = grub_file_size (file);
79 grub_envblk_t envblk;
80
81 buf = grub_malloc (size);
82 if (! buf)
83 return 0;
84
85 while (size > 0)
86 {
87 grub_ssize_t ret;
88
89 ret = grub_file_read (file, buf + offset, size);
90 if (ret <= 0)
91 {
92 if (grub_errno == GRUB_ERR_NONE)
93 grub_error (GRUB_ERR_FILE_READ_ERROR, "cannot read");
94 grub_free (buf);
95 return 0;
96 }
97
98 size -= ret;
99 offset += ret;
100 }
101
102 envblk = grub_envblk_open (buf, offset);
103 if (! envblk)
104 {
105 grub_free (buf);
106 grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
107 return 0;
108 }
109
110 return envblk;
111 }
112
113 static grub_err_t
114 grub_cmd_load_env (grub_extcmd_t cmd,
115 int argc __attribute__ ((unused)),
116 char **args __attribute__ ((unused)))
117 {
118 struct grub_arg_list *state = cmd->state;
119 grub_file_t file;
120 grub_envblk_t envblk;
121
122 auto int set_var (const char *name, const char *value);
123 int set_var (const char *name, const char *value)
124 {
125 grub_env_set (name, value);
126 return 0;
127 }
128
129 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
130 if (! file)
131 return grub_errno;
132
133 envblk = read_envblk_file (file);
134 if (! envblk)
135 goto fail;
136
137 grub_envblk_iterate (envblk, set_var);
138 grub_envblk_close (envblk);
139
140 fail:
141 grub_file_close (file);
142 return grub_errno;
143 }
144
145 static grub_err_t
146 grub_cmd_list_env (grub_extcmd_t cmd,
147 int argc __attribute__ ((unused)),
148 char **args __attribute__ ((unused)))
149 {
150 struct grub_arg_list *state = cmd->state;
151 grub_file_t file;
152 grub_envblk_t envblk;
153
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)
157 {
158 grub_printf ("%s=%s\n", name, value);
159 return 0;
160 }
161
162 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
163 if (! file)
164 return grub_errno;
165
166 envblk = read_envblk_file (file);
167 if (! envblk)
168 goto fail;
169
170 grub_envblk_iterate (envblk, print_var);
171 grub_envblk_close (envblk);
172
173 fail:
174 grub_file_close (file);
175 return grub_errno;
176 }
177
178 /* Used to maintain a variable length of blocklists internally. */
179 struct blocklist
180 {
181 grub_disk_addr_t sector;
182 unsigned offset;
183 unsigned length;
184 struct blocklist *next;
185 };
186
187 static void
188 free_blocklists (struct blocklist *p)
189 {
190 struct blocklist *q;
191
192 for (; p; p = q)
193 {
194 q = p->next;
195 grub_free (p);
196 }
197 }
198
199 static grub_err_t
200 check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
201 grub_file_t file)
202 {
203 grub_size_t total_length;
204 grub_size_t index;
205 grub_disk_t disk;
206 grub_disk_addr_t part_start;
207 struct blocklist *p;
208 char *buf;
209
210 /* Sanity checks. */
211 total_length = 0;
212 for (p = blocklists; p; p = p->next)
213 {
214 struct blocklist *q;
215 for (q = p->next; q; q = q->next)
216 {
217 /* Check if any pair of blocks overlap. */
218 if (p->sector == q->sector)
219 {
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");
223 }
224 }
225
226 total_length += p->length;
227 }
228
229 if (total_length != grub_file_size (file))
230 {
231 /* Maybe sparse, unallocated sectors. No way in GRUB. */
232 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
233 }
234
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;
238
239 part_start = grub_partition_get_start (disk->partition);
240
241 buf = grub_envblk_buffer (envblk);
242 for (p = blocklists, index = 0; p; index += p->length, p = p->next)
243 {
244 char blockbuf[GRUB_DISK_SECTOR_SIZE];
245
246 if (grub_disk_read (disk, p->sector - part_start,
247 p->offset, p->length, blockbuf))
248 return grub_errno;
249
250 if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
251 return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
252 }
253
254 return GRUB_ERR_NONE;
255 }
256
257 static int
258 write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
259 grub_file_t file)
260 {
261 char *buf;
262 grub_disk_t disk;
263 grub_disk_addr_t part_start;
264 struct blocklist *p;
265 grub_size_t index;
266
267 buf = grub_envblk_buffer (envblk);
268 disk = file->device->disk;
269 part_start = grub_partition_get_start (disk->partition);
270
271 index = 0;
272 for (p = blocklists; p; index += p->length, p = p->next)
273 {
274 if (grub_disk_write (disk, p->sector - part_start,
275 p->offset, p->length, buf + index))
276 return 0;
277 }
278
279 return 1;
280 }
281
282 static grub_err_t
283 grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args)
284 {
285 struct grub_arg_list *state = cmd->state;
286 grub_file_t file;
287 grub_envblk_t envblk;
288 struct blocklist *head = 0;
289 struct blocklist *tail = 0;
290
291 /* Store blocklists in a linked list. */
292 auto void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
293 unsigned offset,
294 unsigned length);
295 void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector,
296 unsigned offset, unsigned length)
297 {
298 struct blocklist *block;
299
300 if (offset + length > GRUB_DISK_SECTOR_SIZE)
301 /* Seemingly a bug. */
302 return;
303
304 block = grub_malloc (sizeof (*block));
305 if (! block)
306 return;
307
308 block->sector = sector;
309 block->offset = offset;
310 block->length = length;
311
312 /* Slightly complicated, because the list should be FIFO. */
313 block->next = 0;
314 if (tail)
315 tail->next = block;
316 tail = block;
317 if (! head)
318 head = block;
319 }
320
321 if (! argc)
322 return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
323
324 file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
325 if (! file)
326 return grub_errno;
327
328 if (! file->device->disk)
329 {
330 grub_file_close (file);
331 return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
332 }
333
334 file->read_hook = read_hook;
335 envblk = read_envblk_file (file);
336 file->read_hook = 0;
337 if (! envblk)
338 goto fail;
339
340 if (check_blocklists (envblk, head, file))
341 goto fail;
342
343 while (argc)
344 {
345 char *value;
346
347 value = grub_env_get (args[0]);
348 if (value)
349 {
350 if (! grub_envblk_set (envblk, args[0], value))
351 {
352 grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
353 goto fail;
354 }
355 }
356
357 argc--;
358 args++;
359 }
360
361 write_blocklists (envblk, head, file);
362
363 fail:
364 if (envblk)
365 grub_envblk_close (envblk);
366 free_blocklists (head);
367 grub_file_close (file);
368 return grub_errno;
369 }
370
371 static grub_extcmd_t cmd_load, cmd_list, cmd_save;
372
373 GRUB_MOD_INIT(loadenv)
374 {
375 cmd_load =
376 grub_register_extcmd ("load_env", grub_cmd_load_env,
377 GRUB_COMMAND_FLAG_BOTH,
378 N_("[-f FILE]"),
379 N_("Load variables from environment block file."),
380 options);
381 cmd_list =
382 grub_register_extcmd ("list_env", grub_cmd_list_env,
383 GRUB_COMMAND_FLAG_BOTH,
384 N_("[-f FILE]"),
385 N_("List variables from environment block file."),
386 options);
387 cmd_save =
388 grub_register_extcmd ("save_env", grub_cmd_save_env,
389 GRUB_COMMAND_FLAG_BOTH,
390 N_("[-f FILE] variable_name [...]"),
391 N_("Save variables to environment block file."),
392 options);
393 }
394
395 GRUB_MOD_FINI(loadenv)
396 {
397 grub_unregister_extcmd (cmd_load);
398 grub_unregister_extcmd (cmd_list);
399 grub_unregister_extcmd (cmd_save);
400 }