loadenv.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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. #include <grub/dl.h>
  20. #include <grub/mm.h>
  21. #include <grub/file.h>
  22. #include <grub/disk.h>
  23. #include <grub/misc.h>
  24. #include <grub/env.h>
  25. #include <grub/partition.h>
  26. #include <grub/lib/envblk.h>
  27. #include <grub/extcmd.h>
  28. #include <grub/i18n.h>
  29. GRUB_MOD_LICENSE ("GPLv3+");
  30. static const struct grub_arg_option options[] =
  31. {
  32. /* TRANSLATORS: This option is used to override default filename
  33. for loading and storing environment. */
  34. {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
  35. {"skip-sig", 's', 0,
  36. N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE},
  37. {0, 0, 0, 0, 0, 0}
  38. };
  39. /* Opens 'filename' with compression filters disabled. Optionally disables the
  40. PUBKEY filter (that insists upon properly signed files) as well. PUBKEY
  41. filter is restored before the function returns. */
  42. static grub_file_t
  43. open_envblk_file (char *filename,
  44. enum grub_file_type type)
  45. {
  46. grub_file_t file;
  47. char *buf = 0;
  48. if (! filename)
  49. {
  50. const char *prefix;
  51. int len;
  52. prefix = grub_env_get ("prefix");
  53. if (! prefix)
  54. {
  55. grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix");
  56. return 0;
  57. }
  58. len = grub_strlen (prefix);
  59. buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
  60. if (! buf)
  61. return 0;
  62. filename = buf;
  63. grub_strcpy (filename, prefix);
  64. filename[len] = '/';
  65. grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
  66. }
  67. file = grub_file_open (filename, type);
  68. grub_free (buf);
  69. return file;
  70. }
  71. static grub_envblk_t
  72. read_envblk_file (grub_file_t file)
  73. {
  74. grub_off_t offset = 0;
  75. char *buf;
  76. grub_size_t size = grub_file_size (file);
  77. grub_envblk_t envblk;
  78. buf = grub_malloc (size);
  79. if (! buf)
  80. return 0;
  81. while (size > 0)
  82. {
  83. grub_ssize_t ret;
  84. ret = grub_file_read (file, buf + offset, size);
  85. if (ret <= 0)
  86. {
  87. grub_free (buf);
  88. return 0;
  89. }
  90. size -= ret;
  91. offset += ret;
  92. }
  93. envblk = grub_envblk_open (buf, offset);
  94. if (! envblk)
  95. {
  96. grub_free (buf);
  97. grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
  98. return 0;
  99. }
  100. return envblk;
  101. }
  102. struct grub_env_whitelist
  103. {
  104. grub_size_t len;
  105. char **list;
  106. };
  107. typedef struct grub_env_whitelist grub_env_whitelist_t;
  108. static int
  109. test_whitelist_membership (const char* name,
  110. const grub_env_whitelist_t* whitelist)
  111. {
  112. grub_size_t i;
  113. for (i = 0; i < whitelist->len; i++)
  114. if (grub_strcmp (name, whitelist->list[i]) == 0)
  115. return 1; /* found it */
  116. return 0; /* not found */
  117. }
  118. /* Helper for grub_cmd_load_env. */
  119. static int
  120. set_var (const char *name, const char *value, void *whitelist)
  121. {
  122. if (! whitelist)
  123. {
  124. grub_env_set (name, value);
  125. return 0;
  126. }
  127. if (test_whitelist_membership (name,
  128. (const grub_env_whitelist_t *) whitelist))
  129. grub_env_set (name, value);
  130. return 0;
  131. }
  132. static grub_err_t
  133. grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args)
  134. {
  135. struct grub_arg_list *state = ctxt->state;
  136. grub_file_t file;
  137. grub_envblk_t envblk;
  138. grub_env_whitelist_t whitelist;
  139. whitelist.len = argc;
  140. whitelist.list = args;
  141. /* state[0] is the -f flag; state[1] is the --skip-sig flag */
  142. file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
  143. GRUB_FILE_TYPE_LOADENV
  144. | (state[1].set
  145. ? GRUB_FILE_TYPE_SKIP_SIGNATURE : GRUB_FILE_TYPE_NONE));
  146. if (! file)
  147. return grub_errno;
  148. envblk = read_envblk_file (file);
  149. if (! envblk)
  150. goto fail;
  151. /* argc > 0 indicates caller provided a whitelist of variables to read. */
  152. grub_envblk_iterate (envblk, argc > 0 ? &whitelist : 0, set_var);
  153. grub_envblk_close (envblk);
  154. fail:
  155. grub_file_close (file);
  156. return grub_errno;
  157. }
  158. /* Print all variables in current context. */
  159. static int
  160. print_var (const char *name, const char *value,
  161. void *hook_data __attribute__ ((unused)))
  162. {
  163. grub_printf ("%s=%s\n", name, value);
  164. return 0;
  165. }
  166. static grub_err_t
  167. grub_cmd_list_env (grub_extcmd_context_t ctxt,
  168. int argc __attribute__ ((unused)),
  169. char **args __attribute__ ((unused)))
  170. {
  171. struct grub_arg_list *state = ctxt->state;
  172. grub_file_t file;
  173. grub_envblk_t envblk;
  174. file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
  175. GRUB_FILE_TYPE_LOADENV
  176. | (state[1].set
  177. ? GRUB_FILE_TYPE_SKIP_SIGNATURE : GRUB_FILE_TYPE_NONE));
  178. if (! file)
  179. return grub_errno;
  180. envblk = read_envblk_file (file);
  181. if (! envblk)
  182. goto fail;
  183. grub_envblk_iterate (envblk, NULL, print_var);
  184. grub_envblk_close (envblk);
  185. fail:
  186. grub_file_close (file);
  187. return grub_errno;
  188. }
  189. /* Used to maintain a variable length of blocklists internally. */
  190. struct blocklist
  191. {
  192. grub_disk_addr_t sector;
  193. unsigned offset;
  194. unsigned length;
  195. struct blocklist *next;
  196. };
  197. static void
  198. free_blocklists (struct blocklist *p)
  199. {
  200. struct blocklist *q;
  201. for (; p; p = q)
  202. {
  203. q = p->next;
  204. grub_free (p);
  205. }
  206. }
  207. static grub_err_t
  208. check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
  209. grub_file_t file)
  210. {
  211. grub_size_t total_length;
  212. grub_size_t index;
  213. grub_disk_t disk;
  214. grub_disk_addr_t part_start;
  215. struct blocklist *p;
  216. char *buf;
  217. /* Sanity checks. */
  218. total_length = 0;
  219. for (p = blocklists; p; p = p->next)
  220. {
  221. struct blocklist *q;
  222. /* Check if any pair of blocks overlap. */
  223. for (q = p->next; q; q = q->next)
  224. {
  225. grub_disk_addr_t s1, s2;
  226. grub_disk_addr_t e1, e2;
  227. s1 = p->sector;
  228. e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
  229. s2 = q->sector;
  230. e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
  231. if (s1 < e2 && s2 < e1)
  232. {
  233. /* This might be actually valid, but it is unbelievable that
  234. any filesystem makes such a silly allocation. */
  235. return grub_error (GRUB_ERR_BAD_FS, "malformed file");
  236. }
  237. }
  238. total_length += p->length;
  239. }
  240. if (total_length != grub_file_size (file))
  241. {
  242. /* Maybe sparse, unallocated sectors. No way in GRUB. */
  243. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
  244. }
  245. /* One more sanity check. Re-read all sectors by blocklists, and compare
  246. those with the data read via a file. */
  247. disk = file->device->disk;
  248. part_start = grub_partition_get_start (disk->partition);
  249. buf = grub_envblk_buffer (envblk);
  250. char *blockbuf = NULL;
  251. grub_size_t blockbuf_len = 0;
  252. for (p = blocklists, index = 0; p; index += p->length, p = p->next)
  253. {
  254. if (p->length > blockbuf_len)
  255. {
  256. grub_free (blockbuf);
  257. blockbuf_len = 2 * p->length;
  258. blockbuf = grub_malloc (blockbuf_len);
  259. if (!blockbuf)
  260. return grub_errno;
  261. }
  262. if (grub_disk_read (disk, p->sector - part_start,
  263. p->offset, p->length, blockbuf))
  264. return grub_errno;
  265. if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
  266. return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
  267. }
  268. return GRUB_ERR_NONE;
  269. }
  270. static int
  271. write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
  272. grub_file_t file)
  273. {
  274. char *buf;
  275. grub_disk_t disk;
  276. grub_disk_addr_t part_start;
  277. struct blocklist *p;
  278. grub_size_t index;
  279. buf = grub_envblk_buffer (envblk);
  280. disk = file->device->disk;
  281. part_start = grub_partition_get_start (disk->partition);
  282. index = 0;
  283. for (p = blocklists; p; index += p->length, p = p->next)
  284. {
  285. if (grub_disk_write (disk, p->sector - part_start,
  286. p->offset, p->length, buf + index))
  287. return 0;
  288. }
  289. return 1;
  290. }
  291. /* Context for grub_cmd_save_env. */
  292. struct grub_cmd_save_env_ctx
  293. {
  294. struct blocklist *head, *tail;
  295. };
  296. /* Store blocklists in a linked list. */
  297. static grub_err_t
  298. save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
  299. char *buf __attribute__ ((unused)), void *data)
  300. {
  301. struct grub_cmd_save_env_ctx *ctx = data;
  302. struct blocklist *block;
  303. block = grub_malloc (sizeof (*block));
  304. if (! block)
  305. return GRUB_ERR_NONE;
  306. block->sector = sector;
  307. block->offset = offset;
  308. block->length = length;
  309. /* Slightly complicated, because the list should be FIFO. */
  310. block->next = 0;
  311. if (ctx->tail)
  312. ctx->tail->next = block;
  313. ctx->tail = block;
  314. if (! ctx->head)
  315. ctx->head = block;
  316. return GRUB_ERR_NONE;
  317. }
  318. static grub_err_t
  319. grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args)
  320. {
  321. struct grub_arg_list *state = ctxt->state;
  322. grub_file_t file;
  323. grub_envblk_t envblk;
  324. struct grub_cmd_save_env_ctx ctx = {
  325. .head = 0,
  326. .tail = 0
  327. };
  328. if (! argc)
  329. return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
  330. file = open_envblk_file ((state[0].set) ? state[0].arg : 0,
  331. GRUB_FILE_TYPE_SAVEENV
  332. | GRUB_FILE_TYPE_SKIP_SIGNATURE);
  333. if (! file)
  334. return grub_errno;
  335. if (! file->device->disk)
  336. {
  337. grub_file_close (file);
  338. return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
  339. }
  340. file->read_hook = save_env_read_hook;
  341. file->read_hook_data = &ctx;
  342. envblk = read_envblk_file (file);
  343. file->read_hook = 0;
  344. if (! envblk)
  345. goto fail;
  346. if (check_blocklists (envblk, ctx.head, file))
  347. goto fail;
  348. while (argc)
  349. {
  350. const char *value;
  351. value = grub_env_get (args[0]);
  352. if (value)
  353. {
  354. if (! grub_envblk_set (envblk, args[0], value))
  355. {
  356. grub_error (GRUB_ERR_BAD_ARGUMENT, "environment block too small");
  357. goto fail;
  358. }
  359. }
  360. else
  361. grub_envblk_delete (envblk, args[0]);
  362. argc--;
  363. args++;
  364. }
  365. write_blocklists (envblk, ctx.head, file);
  366. fail:
  367. if (envblk)
  368. grub_envblk_close (envblk);
  369. free_blocklists (ctx.head);
  370. grub_file_close (file);
  371. return grub_errno;
  372. }
  373. static grub_extcmd_t cmd_load, cmd_list, cmd_save;
  374. GRUB_MOD_INIT(loadenv)
  375. {
  376. cmd_load =
  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."),
  380. options);
  381. cmd_list =
  382. grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"),
  383. N_("List variables from environment block file."),
  384. options);
  385. cmd_save =
  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."),
  389. options);
  390. }
  391. GRUB_MOD_FINI(loadenv)
  392. {
  393. grub_unregister_extcmd (cmd_load);
  394. grub_unregister_extcmd (cmd_list);
  395. grub_unregister_extcmd (cmd_save);
  396. }