loadenv.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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. static const struct grub_arg_option options[] =
  30. {
  31. {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME},
  32. {0, 0, 0, 0, 0, 0}
  33. };
  34. static grub_file_t
  35. open_envblk_file (char *filename)
  36. {
  37. grub_file_t file;
  38. if (! filename)
  39. {
  40. char *e, *prefix;
  41. e = grub_env_get ("envfile");
  42. if (e)
  43. return grub_file_open (e);
  44. prefix = grub_env_get ("prefix");
  45. if (prefix)
  46. {
  47. int len;
  48. len = grub_strlen (prefix);
  49. filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG));
  50. if (! filename)
  51. return 0;
  52. grub_strcpy (filename, prefix);
  53. filename[len] = '/';
  54. grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG);
  55. file = grub_file_open (filename);
  56. grub_free (filename);
  57. return file;
  58. }
  59. else
  60. {
  61. grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found");
  62. return 0;
  63. }
  64. }
  65. return grub_file_open (filename);
  66. }
  67. static grub_envblk_t
  68. read_envblk_file (grub_file_t file)
  69. {
  70. grub_off_t offset = 0;
  71. char *buf;
  72. grub_size_t size = grub_file_size (file);
  73. grub_envblk_t envblk;
  74. buf = grub_malloc (size);
  75. if (! buf)
  76. return 0;
  77. while (size > 0)
  78. {
  79. grub_ssize_t ret;
  80. ret = grub_file_read (file, buf + offset, size);
  81. if (ret <= 0)
  82. {
  83. if (grub_errno == GRUB_ERR_NONE)
  84. grub_error (GRUB_ERR_FILE_READ_ERROR, "cannot read");
  85. grub_free (buf);
  86. return 0;
  87. }
  88. size -= ret;
  89. offset += ret;
  90. }
  91. envblk = grub_envblk_open (buf, offset);
  92. if (! envblk)
  93. {
  94. grub_free (buf);
  95. grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block");
  96. return 0;
  97. }
  98. return envblk;
  99. }
  100. static int
  101. set_var (const char *name, const char *value)
  102. {
  103. grub_env_set (name, value);
  104. return 0;
  105. }
  106. static grub_err_t
  107. grub_cmd_load_env (grub_extcmd_t cmd,
  108. int argc __attribute__ ((unused)),
  109. char **args __attribute__ ((unused)))
  110. {
  111. struct grub_arg_list *state = cmd->state;
  112. grub_file_t file;
  113. grub_envblk_t envblk;
  114. file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
  115. if (! file)
  116. return grub_errno;
  117. envblk = read_envblk_file (file);
  118. if (! envblk)
  119. goto fail;
  120. grub_envblk_iterate (envblk, set_var);
  121. grub_envblk_close (envblk);
  122. fail:
  123. grub_file_close (file);
  124. return grub_errno;
  125. }
  126. /* Print all variables in current context. */
  127. static int
  128. print_var (const char *name, const char *value)
  129. {
  130. grub_printf ("%s=%s\n", name, value);
  131. return 0;
  132. }
  133. static grub_err_t
  134. grub_cmd_list_env (grub_extcmd_t cmd,
  135. int argc __attribute__ ((unused)),
  136. char **args __attribute__ ((unused)))
  137. {
  138. struct grub_arg_list *state = cmd->state;
  139. grub_file_t file;
  140. grub_envblk_t envblk;
  141. file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
  142. if (! file)
  143. return grub_errno;
  144. envblk = read_envblk_file (file);
  145. if (! envblk)
  146. goto fail;
  147. grub_envblk_iterate (envblk, print_var);
  148. grub_envblk_close (envblk);
  149. fail:
  150. grub_file_close (file);
  151. return grub_errno;
  152. }
  153. /* Used to maintain a variable length of blocklists internally. */
  154. struct blocklist
  155. {
  156. grub_disk_addr_t sector;
  157. unsigned offset;
  158. unsigned length;
  159. struct blocklist *next;
  160. };
  161. static void
  162. free_blocklists (struct blocklist *p)
  163. {
  164. struct blocklist *q;
  165. for (; p; p = q)
  166. {
  167. q = p->next;
  168. grub_free (p);
  169. }
  170. }
  171. static grub_err_t
  172. check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
  173. grub_file_t file)
  174. {
  175. grub_size_t total_length;
  176. grub_size_t index;
  177. grub_disk_t disk;
  178. grub_disk_addr_t part_start;
  179. struct blocklist *p;
  180. char *buf;
  181. /* Sanity checks. */
  182. total_length = 0;
  183. for (p = blocklists; p; p = p->next)
  184. {
  185. struct blocklist *q;
  186. for (q = p->next; q; q = q->next)
  187. {
  188. /* Check if any pair of blocks overlap. */
  189. if (p->sector == q->sector)
  190. {
  191. /* This might be actually valid, but it is unbelievable that
  192. any filesystem makes such a silly allocation. */
  193. return grub_error (GRUB_ERR_BAD_FS, "malformed file");
  194. }
  195. }
  196. total_length += p->length;
  197. }
  198. if (total_length != grub_file_size (file))
  199. {
  200. /* Maybe sparse, unallocated sectors. No way in GRUB. */
  201. return grub_error (GRUB_ERR_BAD_FILE_TYPE, "sparse file not allowed");
  202. }
  203. /* One more sanity check. Re-read all sectors by blocklists, and compare
  204. those with the data read via a file. */
  205. disk = file->device->disk;
  206. part_start = grub_partition_get_start (disk->partition);
  207. buf = grub_envblk_buffer (envblk);
  208. for (p = blocklists, index = 0; p; index += p->length, p = p->next)
  209. {
  210. char blockbuf[GRUB_DISK_SECTOR_SIZE];
  211. if (grub_disk_read (disk, p->sector - part_start,
  212. p->offset, p->length, blockbuf))
  213. return grub_errno;
  214. if (grub_memcmp (buf + index, blockbuf, p->length) != 0)
  215. return grub_error (GRUB_ERR_FILE_READ_ERROR, "invalid blocklist");
  216. }
  217. return GRUB_ERR_NONE;
  218. }
  219. static int
  220. write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
  221. grub_file_t file)
  222. {
  223. char *buf;
  224. grub_disk_t disk;
  225. grub_disk_addr_t part_start;
  226. struct blocklist *p;
  227. grub_size_t index;
  228. buf = grub_envblk_buffer (envblk);
  229. disk = file->device->disk;
  230. part_start = grub_partition_get_start (disk->partition);
  231. index = 0;
  232. for (p = blocklists; p; index += p->length, p = p->next)
  233. {
  234. if (grub_disk_write (disk, p->sector - part_start,
  235. p->offset, p->length, buf + index))
  236. return 0;
  237. }
  238. return 1;
  239. }
  240. struct grub_cmd_save_env_closure
  241. {
  242. struct blocklist *head;
  243. struct blocklist *tail;
  244. };
  245. /* Store blocklists in a linked list. */
  246. static void
  247. read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
  248. void *closure)
  249. {
  250. struct grub_cmd_save_env_closure *c = closure;
  251. struct blocklist *block;
  252. if (offset + length > GRUB_DISK_SECTOR_SIZE)
  253. /* Seemingly a bug. */
  254. return;
  255. block = grub_malloc (sizeof (*block));
  256. if (! block)
  257. return;
  258. block->sector = sector;
  259. block->offset = offset;
  260. block->length = length;
  261. /* Slightly complicated, because the list should be FIFO. */
  262. block->next = 0;
  263. if (c->tail)
  264. c->tail->next = block;
  265. c->tail = block;
  266. if (! c->head)
  267. c->head = block;
  268. }
  269. #ifdef GRUB_MACHINE_EMU
  270. #include <stdio.h>
  271. #include <unistd.h>
  272. #include <grub/util/misc.h>
  273. static char *
  274. check_host_file (char *filename)
  275. {
  276. char *buf = 0;
  277. if (! filename)
  278. {
  279. filename = grub_env_get ("envfile");
  280. if (! filename)
  281. {
  282. char *prefix;
  283. prefix = grub_env_get ("prefix");
  284. if (! prefix)
  285. return 0;
  286. buf = grub_xasprintf ("%s/%s", prefix, GRUB_ENVBLK_DEFCFG);
  287. if (! buf)
  288. return 0;
  289. filename = buf;
  290. }
  291. }
  292. if (filename[0] == '(')
  293. {
  294. if (grub_memcmp (filename, "(host)", 6))
  295. goto fail;
  296. filename += 6;
  297. }
  298. else
  299. {
  300. char *root;
  301. root = grub_env_get ("root");
  302. if ((! root) || (grub_strcmp (root, "host")))
  303. goto fail;
  304. }
  305. filename = grub_strdup (filename);
  306. grub_free (buf);
  307. return filename;
  308. fail:
  309. grub_free (buf);
  310. return 0;
  311. }
  312. #endif
  313. static grub_err_t
  314. grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args)
  315. {
  316. struct grub_arg_list *state = cmd->state;
  317. grub_file_t file;
  318. grub_envblk_t envblk;
  319. struct grub_cmd_save_env_closure c;
  320. #ifdef GRUB_MACHINE_EMU
  321. char *host_file = check_host_file ((state[0].set) ? state[0].arg : 0);
  322. #endif
  323. c.head = 0;
  324. c.tail = 0;
  325. if (! argc)
  326. return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified");
  327. file = open_envblk_file ((state[0].set) ? state[0].arg : 0);
  328. if (! file)
  329. return grub_errno;
  330. if (! file->device->disk)
  331. {
  332. grub_file_close (file);
  333. return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required");
  334. }
  335. file->read_hook = read_hook;
  336. file->closure = &c;
  337. envblk = read_envblk_file (file);
  338. file->read_hook = 0;
  339. if (! envblk)
  340. goto fail;
  341. #ifdef GRUB_MACHINE_EMU
  342. if ((host_file == NULL) && (check_blocklists (envblk, c.head, file)))
  343. goto fail;
  344. #else
  345. if (check_blocklists (envblk, c.head, file))
  346. goto fail;
  347. #endif
  348. while (argc)
  349. {
  350. 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. argc--;
  361. args++;
  362. }
  363. #ifdef GRUB_MACHINE_EMU
  364. if (host_file)
  365. {
  366. FILE *fp;
  367. grub_file_close (file);
  368. fp = fopen (host_file, "wb");
  369. if (! fp)
  370. grub_util_error ("cannot open the file %s", host_file);
  371. grub_free (host_file);
  372. if (fwrite (grub_envblk_buffer (envblk), 1,
  373. grub_envblk_size (envblk), fp) != grub_envblk_size (envblk))
  374. grub_util_error ("cannot write to the file %s", host_file);
  375. fsync (fileno (fp));
  376. fclose (fp);
  377. if (envblk)
  378. grub_envblk_close (envblk);
  379. return grub_errno;
  380. }
  381. #endif
  382. write_blocklists (envblk, c.head, file);
  383. fail:
  384. if (envblk)
  385. grub_envblk_close (envblk);
  386. free_blocklists (c.head);
  387. grub_file_close (file);
  388. return grub_errno;
  389. }
  390. static grub_extcmd_t cmd_load, cmd_list, cmd_save;
  391. GRUB_MOD_INIT(loadenv)
  392. {
  393. cmd_load =
  394. grub_register_extcmd ("load_env", grub_cmd_load_env,
  395. GRUB_COMMAND_FLAG_BOTH,
  396. N_("[-f FILE]"),
  397. N_("Load variables from environment block file."),
  398. options);
  399. cmd_list =
  400. grub_register_extcmd ("list_env", grub_cmd_list_env,
  401. GRUB_COMMAND_FLAG_BOTH,
  402. N_("[-f FILE]"),
  403. N_("List variables from environment block file."),
  404. options);
  405. cmd_save =
  406. grub_register_extcmd ("save_env", grub_cmd_save_env,
  407. GRUB_COMMAND_FLAG_BOTH,
  408. N_("[-f FILE] variable_name [...]"),
  409. N_("Save variables to environment block file."),
  410. options);
  411. }
  412. GRUB_MOD_FINI(loadenv)
  413. {
  414. grub_unregister_extcmd (cmd_load);
  415. grub_unregister_extcmd (cmd_list);
  416. grub_unregister_extcmd (cmd_save);
  417. }