PageRenderTime 55ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/grub-core/commands/test.c

https://bitbucket.org/erszcz/grub
C | 457 lines | 355 code | 51 blank | 51 comment | 104 complexity | afe2e1c924d3a8c00f86bcb38c3ee694 MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-3.0, BSD-3-Clause
  1. /* test.c -- The test command.. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2005,2007,2009 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/misc.h>
  21. #include <grub/mm.h>
  22. #include <grub/env.h>
  23. #include <grub/fs.h>
  24. #include <grub/device.h>
  25. #include <grub/file.h>
  26. #include <grub/command.h>
  27. #include <grub/i18n.h>
  28. GRUB_MOD_LICENSE ("GPLv3+");
  29. /* A simple implementation for signed numbers. */
  30. static int
  31. grub_strtosl (char *arg, char **end, int base)
  32. {
  33. if (arg[0] == '-')
  34. return -grub_strtoul (arg + 1, end, base);
  35. return grub_strtoul (arg, end, base);
  36. }
  37. /* Context for test_parse. */
  38. struct test_parse_ctx
  39. {
  40. int ret, discard, invert;
  41. int file_exists;
  42. struct grub_dirhook_info file_info;
  43. char *filename;
  44. };
  45. /* Take care of discarding and inverting. */
  46. static void
  47. update_val (int val, struct test_parse_ctx *ctx)
  48. {
  49. if (! ctx->discard)
  50. ctx->ret = ctx->invert ? ! val : val;
  51. ctx->invert = ctx->discard = 0;
  52. }
  53. /* A hook for iterating directories. */
  54. static int
  55. find_file (const char *cur_filename, const struct grub_dirhook_info *info,
  56. void *data)
  57. {
  58. struct test_parse_ctx *ctx = data;
  59. if ((info->case_insensitive ? grub_strcasecmp (cur_filename, ctx->filename)
  60. : grub_strcmp (cur_filename, ctx->filename)) == 0)
  61. {
  62. ctx->file_info = *info;
  63. ctx->file_exists = 1;
  64. return 1;
  65. }
  66. return 0;
  67. }
  68. /* Check if file exists and fetch its information. */
  69. static void
  70. get_fileinfo (char *path, struct test_parse_ctx *ctx)
  71. {
  72. char *pathname;
  73. char *device_name;
  74. grub_fs_t fs;
  75. grub_device_t dev;
  76. ctx->file_exists = 0;
  77. device_name = grub_file_get_device_name (path);
  78. dev = grub_device_open (device_name);
  79. if (! dev)
  80. {
  81. grub_free (device_name);
  82. return;
  83. }
  84. fs = grub_fs_probe (dev);
  85. if (! fs)
  86. {
  87. grub_free (device_name);
  88. grub_device_close (dev);
  89. return;
  90. }
  91. pathname = grub_strchr (path, ')');
  92. if (! pathname)
  93. pathname = path;
  94. else
  95. pathname++;
  96. /* Remove trailing '/'. */
  97. while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
  98. pathname[grub_strlen (pathname) - 1] = 0;
  99. /* Split into path and filename. */
  100. ctx->filename = grub_strrchr (pathname, '/');
  101. if (! ctx->filename)
  102. {
  103. path = grub_strdup ("/");
  104. ctx->filename = pathname;
  105. }
  106. else
  107. {
  108. ctx->filename++;
  109. path = grub_strdup (pathname);
  110. path[ctx->filename - pathname] = 0;
  111. }
  112. /* It's the whole device. */
  113. if (! *pathname)
  114. {
  115. ctx->file_exists = 1;
  116. grub_memset (&ctx->file_info, 0, sizeof (ctx->file_info));
  117. /* Root is always a directory. */
  118. ctx->file_info.dir = 1;
  119. /* Fetch writing time. */
  120. ctx->file_info.mtimeset = 0;
  121. if (fs->mtime)
  122. {
  123. if (! fs->mtime (dev, &ctx->file_info.mtime))
  124. ctx->file_info.mtimeset = 1;
  125. grub_errno = GRUB_ERR_NONE;
  126. }
  127. }
  128. else
  129. (fs->dir) (dev, path, find_file, ctx);
  130. grub_device_close (dev);
  131. grub_free (path);
  132. grub_free (device_name);
  133. }
  134. /* Parse a test expression starting from *argn. */
  135. static int
  136. test_parse (char **args, int *argn, int argc)
  137. {
  138. struct test_parse_ctx ctx = {
  139. .ret = 0,
  140. .discard = 0,
  141. .invert = 0
  142. };
  143. /* Here we have the real parsing. */
  144. while (*argn < argc)
  145. {
  146. /* First try 3 argument tests. */
  147. if (*argn + 2 < argc)
  148. {
  149. /* String tests. */
  150. if (grub_strcmp (args[*argn + 1], "=") == 0
  151. || grub_strcmp (args[*argn + 1], "==") == 0)
  152. {
  153. update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0,
  154. &ctx);
  155. (*argn) += 3;
  156. continue;
  157. }
  158. if (grub_strcmp (args[*argn + 1], "!=") == 0)
  159. {
  160. update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0,
  161. &ctx);
  162. (*argn) += 3;
  163. continue;
  164. }
  165. /* GRUB extension: lexicographical sorting. */
  166. if (grub_strcmp (args[*argn + 1], "<") == 0)
  167. {
  168. update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0,
  169. &ctx);
  170. (*argn) += 3;
  171. continue;
  172. }
  173. if (grub_strcmp (args[*argn + 1], "<=") == 0)
  174. {
  175. update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0,
  176. &ctx);
  177. (*argn) += 3;
  178. continue;
  179. }
  180. if (grub_strcmp (args[*argn + 1], ">") == 0)
  181. {
  182. update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0,
  183. &ctx);
  184. (*argn) += 3;
  185. continue;
  186. }
  187. if (grub_strcmp (args[*argn + 1], ">=") == 0)
  188. {
  189. update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0,
  190. &ctx);
  191. (*argn) += 3;
  192. continue;
  193. }
  194. /* Number tests. */
  195. if (grub_strcmp (args[*argn + 1], "-eq") == 0)
  196. {
  197. update_val (grub_strtosl (args[*argn], 0, 0)
  198. == grub_strtosl (args[*argn + 2], 0, 0), &ctx);
  199. (*argn) += 3;
  200. continue;
  201. }
  202. if (grub_strcmp (args[*argn + 1], "-ge") == 0)
  203. {
  204. update_val (grub_strtosl (args[*argn], 0, 0)
  205. >= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
  206. (*argn) += 3;
  207. continue;
  208. }
  209. if (grub_strcmp (args[*argn + 1], "-gt") == 0)
  210. {
  211. update_val (grub_strtosl (args[*argn], 0, 0)
  212. > grub_strtosl (args[*argn + 2], 0, 0), &ctx);
  213. (*argn) += 3;
  214. continue;
  215. }
  216. if (grub_strcmp (args[*argn + 1], "-le") == 0)
  217. {
  218. update_val (grub_strtosl (args[*argn], 0, 0)
  219. <= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
  220. (*argn) += 3;
  221. continue;
  222. }
  223. if (grub_strcmp (args[*argn + 1], "-lt") == 0)
  224. {
  225. update_val (grub_strtosl (args[*argn], 0, 0)
  226. < grub_strtosl (args[*argn + 2], 0, 0), &ctx);
  227. (*argn) += 3;
  228. continue;
  229. }
  230. if (grub_strcmp (args[*argn + 1], "-ne") == 0)
  231. {
  232. update_val (grub_strtosl (args[*argn], 0, 0)
  233. != grub_strtosl (args[*argn + 2], 0, 0), &ctx);
  234. (*argn) += 3;
  235. continue;
  236. }
  237. /* GRUB extension: compare numbers skipping prefixes.
  238. Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11. */
  239. if (grub_strcmp (args[*argn + 1], "-pgt") == 0
  240. || grub_strcmp (args[*argn + 1], "-plt") == 0)
  241. {
  242. int i;
  243. /* Skip common prefix. */
  244. for (i = 0; args[*argn][i] == args[*argn + 2][i]
  245. && args[*argn][i]; i++);
  246. /* Go the digits back. */
  247. i--;
  248. while (grub_isdigit (args[*argn][i]) && i > 0)
  249. i--;
  250. i++;
  251. if (grub_strcmp (args[*argn + 1], "-pgt") == 0)
  252. update_val (grub_strtoul (args[*argn] + i, 0, 0)
  253. > grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
  254. else
  255. update_val (grub_strtoul (args[*argn] + i, 0, 0)
  256. < grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
  257. (*argn) += 3;
  258. continue;
  259. }
  260. /* -nt and -ot tests. GRUB extension: when doing -?t<bias> bias
  261. will be added to the first mtime. */
  262. if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0
  263. || grub_memcmp (args[*argn + 1], "-ot", 3) == 0)
  264. {
  265. struct grub_dirhook_info file1;
  266. int file1exists;
  267. int bias = 0;
  268. /* Fetch fileinfo. */
  269. get_fileinfo (args[*argn], &ctx);
  270. file1 = ctx.file_info;
  271. file1exists = ctx.file_exists;
  272. get_fileinfo (args[*argn + 2], &ctx);
  273. if (args[*argn + 1][3])
  274. bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
  275. if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0)
  276. update_val ((file1exists && ! ctx.file_exists)
  277. || (file1.mtimeset && ctx.file_info.mtimeset
  278. && file1.mtime + bias > ctx.file_info.mtime),
  279. &ctx);
  280. else
  281. update_val ((! file1exists && ctx.file_exists)
  282. || (file1.mtimeset && ctx.file_info.mtimeset
  283. && file1.mtime + bias < ctx.file_info.mtime),
  284. &ctx);
  285. (*argn) += 3;
  286. continue;
  287. }
  288. }
  289. /* Two-argument tests. */
  290. if (*argn + 1 < argc)
  291. {
  292. /* File tests. */
  293. if (grub_strcmp (args[*argn], "-d") == 0)
  294. {
  295. get_fileinfo (args[*argn + 1], &ctx);
  296. update_val (ctx.file_exists && ctx.file_info.dir, &ctx);
  297. (*argn) += 2;
  298. return ctx.ret;
  299. }
  300. if (grub_strcmp (args[*argn], "-e") == 0)
  301. {
  302. get_fileinfo (args[*argn + 1], &ctx);
  303. update_val (ctx.file_exists, &ctx);
  304. (*argn) += 2;
  305. return ctx.ret;
  306. }
  307. if (grub_strcmp (args[*argn], "-f") == 0)
  308. {
  309. get_fileinfo (args[*argn + 1], &ctx);
  310. /* FIXME: check for other types. */
  311. update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx);
  312. (*argn) += 2;
  313. return ctx.ret;
  314. }
  315. if (grub_strcmp (args[*argn], "-s") == 0)
  316. {
  317. grub_file_t file;
  318. grub_file_filter_disable_compression ();
  319. file = grub_file_open (args[*argn + 1]);
  320. update_val (file && (grub_file_size (file) != 0), &ctx);
  321. if (file)
  322. grub_file_close (file);
  323. grub_errno = GRUB_ERR_NONE;
  324. (*argn) += 2;
  325. return ctx.ret;
  326. }
  327. /* String tests. */
  328. if (grub_strcmp (args[*argn], "-n") == 0)
  329. {
  330. update_val (args[*argn + 1][0], &ctx);
  331. (*argn) += 2;
  332. continue;
  333. }
  334. if (grub_strcmp (args[*argn], "-z") == 0)
  335. {
  336. update_val (! args[*argn + 1][0], &ctx);
  337. (*argn) += 2;
  338. continue;
  339. }
  340. }
  341. /* Special modifiers. */
  342. /* End of expression. return to parent. */
  343. if (grub_strcmp (args[*argn], ")") == 0)
  344. {
  345. (*argn)++;
  346. return ctx.ret;
  347. }
  348. /* Recursively invoke if parenthesis. */
  349. if (grub_strcmp (args[*argn], "(") == 0)
  350. {
  351. (*argn)++;
  352. update_val (test_parse (args, argn, argc), &ctx);
  353. continue;
  354. }
  355. if (grub_strcmp (args[*argn], "!") == 0)
  356. {
  357. ctx.invert = ! ctx.invert;
  358. (*argn)++;
  359. continue;
  360. }
  361. if (grub_strcmp (args[*argn], "-a") == 0)
  362. {
  363. /* If current value is 0 second value is to be discarded. */
  364. ctx.discard = ! ctx.ret;
  365. (*argn)++;
  366. continue;
  367. }
  368. if (grub_strcmp (args[*argn], "-o") == 0)
  369. {
  370. /* If current value is 1 second value is to be discarded. */
  371. ctx.discard = ctx.ret;
  372. (*argn)++;
  373. continue;
  374. }
  375. /* No test found. Interpret if as just a string. */
  376. update_val (args[*argn][0], &ctx);
  377. (*argn)++;
  378. }
  379. return ctx.ret;
  380. }
  381. static grub_err_t
  382. grub_cmd_test (grub_command_t cmd __attribute__ ((unused)),
  383. int argc, char **args)
  384. {
  385. int argn = 0;
  386. if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0)
  387. argc--;
  388. return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
  389. : grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
  390. }
  391. static grub_command_t cmd_1, cmd_2;
  392. GRUB_MOD_INIT(test)
  393. {
  394. cmd_1 = grub_register_command ("[", grub_cmd_test,
  395. N_("EXPRESSION ]"), N_("Evaluate an expression."));
  396. cmd_1->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
  397. cmd_2 = grub_register_command ("test", grub_cmd_test,
  398. N_("EXPRESSION"), N_("Evaluate an expression."));
  399. cmd_2->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
  400. }
  401. GRUB_MOD_FINI(test)
  402. {
  403. grub_unregister_command (cmd_1);
  404. grub_unregister_command (cmd_2);
  405. }