]> git.proxmox.com Git - grub2.git/blob - util/grub-fstest.c
fs/ntfs: Fix various OOB reads and writes (CVE-2023-4692, CVE-2023-4693)
[grub2.git] / util / grub-fstest.c
1 /* grub-fstest.c - debug tool for filesystem driver */
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 <config.h>
21 #include <grub/types.h>
22 #include <grub/emu/misc.h>
23 #include <grub/util/misc.h>
24 #include <grub/misc.h>
25 #include <grub/device.h>
26 #include <grub/disk.h>
27 #include <grub/file.h>
28 #include <grub/fs.h>
29 #include <grub/env.h>
30 #include <grub/term.h>
31 #include <grub/mm.h>
32 #include <grub/lib/hexdump.h>
33 #include <grub/crypto.h>
34 #include <grub/command.h>
35 #include <grub/i18n.h>
36 #include <grub/zfs/zfs.h>
37 #include <grub/emu/hostfile.h>
38
39 #include <stdio.h>
40 #include <errno.h>
41 #include <string.h>
42
43 #include "progname.h"
44 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
45 #pragma GCC diagnostic ignored "-Wmissing-declarations"
46 #include "argp.h"
47 #pragma GCC diagnostic error "-Wmissing-prototypes"
48 #pragma GCC diagnostic error "-Wmissing-declarations"
49
50 static grub_err_t
51 execute_command (const char *name, int n, char **args)
52 {
53 grub_command_t cmd;
54
55 cmd = grub_command_find (name);
56 if (! cmd)
57 grub_util_error (_("can't find command `%s'"), name);
58
59 return (cmd->func) (cmd, n, args);
60 }
61
62 enum {
63 CMD_LS = 1,
64 CMD_CP,
65 CMD_CAT,
66 CMD_CMP,
67 CMD_HEX,
68 CMD_CRC,
69 CMD_BLOCKLIST,
70 CMD_TESTLOAD,
71 CMD_ZFSINFO,
72 CMD_XNU_UUID
73 };
74 #define BUF_SIZE 32256
75
76 static grub_disk_addr_t skip, leng;
77 static int uncompress = 0;
78
79 static void
80 read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len, void *hook_arg), void *hook_arg)
81 {
82 static char buf[BUF_SIZE];
83 grub_file_t file;
84
85 if ((pathname[0] == '-') && (pathname[1] == 0))
86 {
87 grub_device_t dev;
88
89 dev = grub_device_open (0);
90 if ((! dev) || (! dev->disk))
91 grub_util_error ("%s", grub_errmsg);
92
93 grub_util_info ("total sectors : %" GRUB_HOST_PRIuLONG_LONG,
94 (unsigned long long) dev->disk->total_sectors);
95
96 if (! leng)
97 leng = (dev->disk->total_sectors << GRUB_DISK_SECTOR_BITS) - skip;
98
99 while (leng)
100 {
101 grub_size_t len;
102
103 len = (leng > BUF_SIZE) ? BUF_SIZE : leng;
104
105 if (grub_disk_read (dev->disk, 0, skip, len, buf))
106 {
107 char *msg = grub_xasprintf (_("disk read fails at offset %lld, length %lld"),
108 (long long) skip, (long long) len);
109 grub_util_error ("%s", msg);
110 }
111
112 if (hook (skip, buf, len, hook_arg))
113 break;
114
115 skip += len;
116 leng -= len;
117 }
118
119 grub_device_close (dev);
120 return;
121 }
122
123 file = grub_file_open (pathname, ((uncompress == 0)
124 ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE)
125 | GRUB_FILE_TYPE_FSTEST);
126 if (!file)
127 {
128 grub_util_error (_("cannot open `%s': %s"), pathname,
129 grub_errmsg);
130 return;
131 }
132
133 grub_util_info ("file size : %" GRUB_HOST_PRIuLONG_LONG,
134 (unsigned long long) file->size);
135
136 if (skip > file->size)
137 {
138 char *msg = grub_xasprintf (_("invalid skip value %lld"),
139 (unsigned long long) skip);
140 grub_util_error ("%s", msg);
141 return;
142 }
143
144 {
145 grub_off_t ofs, len;
146 ofs = skip;
147 len = file->size - skip;
148 if ((leng) && (leng < len))
149 len = leng;
150
151 file->offset = skip;
152
153 while (len)
154 {
155 grub_ssize_t sz;
156
157 sz = grub_file_read (file, buf, (len > BUF_SIZE) ? BUF_SIZE : len);
158 if (sz < 0)
159 {
160 char *msg = grub_xasprintf (_("read error at offset %llu: %s"),
161 (unsigned long long) ofs, grub_errmsg);
162 grub_util_error ("%s", msg);
163 break;
164 }
165
166 if ((sz == 0) || (hook (ofs, buf, sz, hook_arg)))
167 break;
168
169 ofs += sz;
170 len -= sz;
171 }
172 }
173
174 grub_file_close (file);
175 }
176
177 struct cp_hook_ctx
178 {
179 FILE *ff;
180 const char *dest;
181 };
182
183 static int
184 cp_hook (grub_off_t ofs, char *buf, int len, void *_ctx)
185 {
186 struct cp_hook_ctx *ctx = _ctx;
187 (void) ofs;
188
189 if ((int) fwrite (buf, 1, len, ctx->ff) != len)
190 {
191 grub_util_error (_("cannot write to `%s': %s"),
192 ctx->dest, strerror (errno));
193 return 1;
194 }
195
196 return 0;
197 }
198
199 static void
200 cmd_cp (char *src, const char *dest)
201 {
202 struct cp_hook_ctx ctx =
203 {
204 .dest = dest
205 };
206
207 ctx.ff = grub_util_fopen (dest, "wb");
208 if (ctx.ff == NULL)
209 {
210 grub_util_error (_("cannot open OS file `%s': %s"), dest,
211 strerror (errno));
212 return;
213 }
214 read_file (src, cp_hook, &ctx);
215 fclose (ctx.ff);
216 }
217
218 static int
219 cat_hook (grub_off_t ofs, char *buf, int len, void *_arg __attribute__ ((unused)))
220 {
221 (void) ofs;
222
223 if ((int) fwrite (buf, 1, len, stdout) != len)
224 {
225 grub_util_error (_("cannot write to the stdout: %s"),
226 strerror (errno));
227 return 1;
228 }
229
230 return 0;
231 }
232
233 static void
234 cmd_cat (char *src)
235 {
236 read_file (src, cat_hook, 0);
237 }
238
239 static int
240 cmp_hook (grub_off_t ofs, char *buf, int len, void *ff_in)
241 {
242 FILE *ff = ff_in;
243 static char buf_1[BUF_SIZE];
244 if ((int) fread (buf_1, 1, len, ff) != len)
245 {
246 char *msg = grub_xasprintf (_("read error at offset %llu: %s"),
247 (unsigned long long) ofs, grub_errmsg);
248 grub_util_error ("%s", msg);
249 return 1;
250 }
251
252 if (grub_memcmp (buf, buf_1, len) != 0)
253 {
254 int i;
255
256 for (i = 0; i < len; i++, ofs++)
257 if (buf_1[i] != buf[i])
258 {
259 char *msg = grub_xasprintf (_("compare fail at offset %llu"),
260 (unsigned long long) ofs);
261 grub_util_error ("%s", msg);
262 return 1;
263 }
264 }
265 return 0;
266 }
267
268
269 static void
270 cmd_cmp (char *src, char *dest)
271 {
272 FILE *ff;
273
274 if (grub_util_is_directory (dest))
275 {
276 grub_util_fd_dir_t dir = grub_util_fd_opendir (dest);
277 grub_util_fd_dirent_t entry;
278 if (dir == NULL)
279 {
280 grub_util_error (_("OS file %s open error: %s"), dest,
281 grub_util_fd_strerror ());
282 return;
283 }
284 while ((entry = grub_util_fd_readdir (dir)))
285 {
286 char *srcnew, *destnew;
287 char *ptr;
288 if (strcmp (entry->d_name, ".") == 0
289 || strcmp (entry->d_name, "..") == 0)
290 continue;
291 srcnew = xmalloc (strlen (src) + sizeof ("/")
292 + strlen (entry->d_name));
293 destnew = xmalloc (strlen (dest) + sizeof ("/")
294 + strlen (entry->d_name));
295 ptr = grub_stpcpy (srcnew, src);
296 *ptr++ = '/';
297 strcpy (ptr, entry->d_name);
298 ptr = grub_stpcpy (destnew, dest);
299 *ptr++ = '/';
300 strcpy (ptr, entry->d_name);
301
302 if (grub_util_is_special_file (destnew))
303 continue;
304
305 cmd_cmp (srcnew, destnew);
306 }
307 grub_util_fd_closedir (dir);
308 return;
309 }
310
311 ff = grub_util_fopen (dest, "rb");
312 if (ff == NULL)
313 {
314 grub_util_error (_("OS file %s open error: %s"), dest,
315 strerror (errno));
316 return;
317 }
318
319 if ((skip) && (fseeko (ff, skip, SEEK_SET)))
320 grub_util_error (_("cannot seek `%s': %s"), dest,
321 strerror (errno));
322
323 read_file (src, cmp_hook, ff);
324
325 {
326 grub_uint64_t pre;
327 pre = ftell (ff);
328 fseek (ff, 0, SEEK_END);
329 if (pre != ftell (ff))
330 grub_util_error ("%s", _("unexpected end of file"));
331 }
332 fclose (ff);
333 }
334
335 static int
336 hex_hook (grub_off_t ofs, char *buf, int len, void *arg __attribute__ ((unused)))
337 {
338 hexdump (ofs, buf, len);
339 return 0;
340 }
341
342 static void
343 cmd_hex (char *pathname)
344 {
345 read_file (pathname, hex_hook, 0);
346 }
347
348 static int
349 crc_hook (grub_off_t ofs, char *buf, int len, void *crc_ctx)
350 {
351 (void) ofs;
352
353 GRUB_MD_CRC32->write(crc_ctx, buf, len);
354 return 0;
355 }
356
357 static void
358 cmd_crc (char *pathname)
359 {
360 grub_uint8_t *crc32_context = xmalloc (GRUB_MD_CRC32->contextsize);
361 GRUB_MD_CRC32->init(crc32_context);
362
363 read_file (pathname, crc_hook, crc32_context);
364 GRUB_MD_CRC32->final(crc32_context);
365 printf ("%08x\n",
366 grub_be_to_cpu32 (grub_get_unaligned32 (GRUB_MD_CRC32->read (crc32_context))));
367 free (crc32_context);
368 }
369
370 static const char *root = NULL;
371 static int args_count = 0;
372 static int nparm = 0;
373 static int num_disks = 1;
374 static char **images = NULL;
375 static int cmd = 0;
376 static char *debug_str = NULL;
377 static char **args = NULL;
378 static int mount_crypt = 0;
379
380 static void
381 fstest (int n)
382 {
383 char *host_file;
384 char *loop_name;
385 int i;
386
387 for (i = 0; i < num_disks; i++)
388 {
389 char *argv[2];
390 loop_name = grub_xasprintf ("loop%d", i);
391 if (!loop_name)
392 grub_util_error ("%s", grub_errmsg);
393
394 host_file = grub_xasprintf ("(host)%s", images[i]);
395 if (!host_file)
396 grub_util_error ("%s", grub_errmsg);
397
398 argv[0] = loop_name;
399 argv[1] = host_file;
400
401 if (execute_command ("loopback", 2, argv))
402 grub_util_error (_("`loopback' command fails: %s"), grub_errmsg);
403
404 grub_free (loop_name);
405 grub_free (host_file);
406 }
407
408 {
409 if (mount_crypt)
410 {
411 char *argv[2] = { xstrdup ("-a"), NULL};
412 if (execute_command ("cryptomount", 1, argv))
413 grub_util_error (_("`cryptomount' command fails: %s"),
414 grub_errmsg);
415 free (argv[0]);
416 }
417 }
418
419 grub_ldm_fini ();
420 grub_lvm_fini ();
421 grub_mdraid09_fini ();
422 grub_mdraid1x_fini ();
423 grub_diskfilter_fini ();
424 grub_diskfilter_init ();
425 grub_mdraid09_init ();
426 grub_mdraid1x_init ();
427 grub_lvm_init ();
428 grub_ldm_init ();
429
430 switch (cmd)
431 {
432 case CMD_LS:
433 execute_command ("ls", n, args);
434 break;
435 case CMD_ZFSINFO:
436 execute_command ("zfsinfo", n, args);
437 break;
438 case CMD_CP:
439 cmd_cp (args[0], args[1]);
440 break;
441 case CMD_CAT:
442 cmd_cat (args[0]);
443 break;
444 case CMD_CMP:
445 cmd_cmp (args[0], args[1]);
446 break;
447 case CMD_HEX:
448 cmd_hex (args[0]);
449 break;
450 case CMD_CRC:
451 cmd_crc (args[0]);
452 break;
453 case CMD_BLOCKLIST:
454 execute_command ("blocklist", n, args);
455 grub_printf ("\n");
456 break;
457 case CMD_TESTLOAD:
458 execute_command ("testload", n, args);
459 grub_printf ("\n");
460 break;
461 case CMD_XNU_UUID:
462 {
463 grub_device_t dev;
464 grub_fs_t fs;
465 char *uuid = 0;
466 char *argv[3] = { xstrdup ("-l"), NULL, NULL};
467 dev = grub_device_open (n ? args[0] : 0);
468 if (!dev)
469 grub_util_error ("%s", grub_errmsg);
470 fs = grub_fs_probe (dev);
471 if (!fs)
472 grub_util_error ("%s", grub_errmsg);
473 if (!fs->fs_uuid)
474 grub_util_error ("%s", _("couldn't retrieve UUID"));
475 if (fs->fs_uuid (dev, &uuid))
476 grub_util_error ("%s", grub_errmsg);
477 if (!uuid)
478 grub_util_error ("%s", _("couldn't retrieve UUID"));
479 argv[1] = uuid;
480 execute_command ("xnu_uuid", 2, argv);
481 grub_free (argv[0]);
482 grub_free (uuid);
483 grub_device_close (dev);
484 }
485 }
486
487 for (i = 0; i < num_disks; i++)
488 {
489 char *argv[2];
490
491 loop_name = grub_xasprintf ("loop%d", i);
492 if (!loop_name)
493 grub_util_error ("%s", grub_errmsg);
494
495 argv[0] = xstrdup ("-d");
496 argv[1] = loop_name;
497
498 execute_command ("loopback", 2, argv);
499
500 grub_free (loop_name);
501 grub_free (argv[0]);
502 }
503 }
504
505 static struct argp_option options[] = {
506 {0, 0, 0 , OPTION_DOC, N_("Commands:"), 1},
507 {N_("ls PATH"), 0, 0 , OPTION_DOC, N_("List files in PATH."), 1},
508 {N_("cp FILE LOCAL"), 0, 0, OPTION_DOC, N_("Copy FILE to local file LOCAL."), 1},
509 {N_("cat FILE"), 0, 0 , OPTION_DOC, N_("Copy FILE to standard output."), 1},
510 {N_("cmp FILE LOCAL"), 0, 0, OPTION_DOC, N_("Compare FILE with local file LOCAL."), 1},
511 {N_("hex FILE"), 0, 0 , OPTION_DOC, N_("Show contents of FILE in hex."), 1},
512 {N_("crc FILE"), 0, 0 , OPTION_DOC, N_("Get crc32 checksum of FILE."), 1},
513 {N_("blocklist FILE"), 0, 0, OPTION_DOC, N_("Display blocklist of FILE."), 1},
514 {N_("xnu_uuid DEVICE"), 0, 0, OPTION_DOC, N_("Compute XNU UUID of the device."), 1},
515
516 {"root", 'r', N_("DEVICE_NAME"), 0, N_("Set root device."), 2},
517 {"skip", 's', N_("NUM"), 0, N_("Skip N bytes from output file."), 2},
518 {"length", 'n', N_("NUM"), 0, N_("Handle N bytes in output file."), 2},
519 {"diskcount", 'c', N_("NUM"), 0, N_("Specify the number of input files."), 2},
520 {"debug", 'd', N_("STRING"), 0, N_("Set debug environment variable."), 2},
521 {"crypto", 'C', NULL, 0, N_("Mount crypto devices."), 2},
522 {"zfs-key", 'K',
523 /* TRANSLATORS: "prompt" is a keyword. */
524 N_("FILE|prompt"), 0, N_("Load zfs crypto key."), 2},
525 {"verbose", 'v', NULL, 0, N_("print verbose messages."), 2},
526 {"uncompress", 'u', NULL, 0, N_("Uncompress data."), 2},
527 {0, 0, 0, 0, 0, 0}
528 };
529
530 /* Print the version information. */
531 static void
532 print_version (FILE *stream, struct argp_state *state)
533 {
534 fprintf (stream, "%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
535 }
536 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
537
538 static error_t
539 argp_parser (int key, char *arg, struct argp_state *state)
540 {
541 const char *p;
542
543 switch (key)
544 {
545 case 'r':
546 root = arg;
547 return 0;
548
549 case 'K':
550 if (strcmp (arg, "prompt") == 0)
551 {
552 char buf[1024];
553 grub_puts_ (N_("Enter ZFS password: "));
554 if (grub_password_get (buf, 1023))
555 {
556 grub_zfs_add_key ((grub_uint8_t *) buf, grub_strlen (buf), 1);
557 }
558 }
559 else
560 {
561 FILE *f;
562 ssize_t real_size;
563 grub_uint8_t buf[1024];
564 f = grub_util_fopen (arg, "rb");
565 if (!f)
566 {
567 printf (_("%s: error:"), program_name);
568 printf (_("cannot open `%s': %s"), arg, strerror (errno));
569 printf ("\n");
570 return 0;
571 }
572 real_size = fread (buf, 1, 1024, f);
573 fclose (f);
574 if (real_size < 0)
575 {
576 printf (_("%s: error:"), program_name);
577 printf (_("cannot read `%s': %s"), arg, strerror (errno));
578 printf ("\n");
579 return 0;
580 }
581 grub_zfs_add_key (buf, real_size, 0);
582 }
583 return 0;
584
585 case 'C':
586 mount_crypt = 1;
587 return 0;
588
589 case 's':
590 skip = grub_strtoul (arg, &p, 0);
591 if (*p == 's')
592 skip <<= GRUB_DISK_SECTOR_BITS;
593 return 0;
594
595 case 'n':
596 leng = grub_strtoul (arg, &p, 0);
597 if (*p == 's')
598 leng <<= GRUB_DISK_SECTOR_BITS;
599 return 0;
600
601 case 'c':
602 num_disks = grub_strtoul (arg, NULL, 0);
603 if (num_disks < 1)
604 {
605 fprintf (stderr, "%s", _("Invalid disk count.\n"));
606 argp_usage (state);
607 }
608 if (args_count != 0)
609 {
610 /* TRANSLATORS: disk count is optional but if it's there it must
611 be before disk list. So please don't imply disk count as mandatory.
612 */
613 fprintf (stderr, "%s", _("Disk count must precede disks list.\n"));
614 argp_usage (state);
615 }
616 return 0;
617
618 case 'd':
619 debug_str = arg;
620 return 0;
621
622 case 'v':
623 verbosity++;
624 return 0;
625
626 case 'u':
627 uncompress = 1;
628 return 0;
629
630 case ARGP_KEY_END:
631 if (args_count < num_disks)
632 {
633 fprintf (stderr, "%s", _("No command is specified.\n"));
634 argp_usage (state);
635 }
636 if (args_count - 1 - num_disks < nparm)
637 {
638 fprintf (stderr, "%s", _("Not enough parameters to command.\n"));
639 argp_usage (state);
640 }
641 return 0;
642
643 case ARGP_KEY_ARG:
644 break;
645
646 default:
647 return ARGP_ERR_UNKNOWN;
648 }
649
650 if (args_count < num_disks)
651 {
652 if (args_count == 0)
653 images = xcalloc (num_disks, sizeof (images[0]));
654 images[args_count] = grub_canonicalize_file_name (arg);
655 args_count++;
656 return 0;
657 }
658
659 if (args_count == num_disks)
660 {
661 if (!grub_strcmp (arg, "ls"))
662 {
663 cmd = CMD_LS;
664 }
665 else if (!grub_strcmp (arg, "zfsinfo"))
666 {
667 cmd = CMD_ZFSINFO;
668 }
669 else if (!grub_strcmp (arg, "cp"))
670 {
671 cmd = CMD_CP;
672 nparm = 2;
673 }
674 else if (!grub_strcmp (arg, "cat"))
675 {
676 cmd = CMD_CAT;
677 nparm = 1;
678 }
679 else if (!grub_strcmp (arg, "cmp"))
680 {
681 cmd = CMD_CMP;
682 nparm = 2;
683 }
684 else if (!grub_strcmp (arg, "hex"))
685 {
686 cmd = CMD_HEX;
687 nparm = 1;
688 }
689 else if (!grub_strcmp (arg, "crc"))
690 {
691 cmd = CMD_CRC;
692 nparm = 1;
693 }
694 else if (!grub_strcmp (arg, "blocklist"))
695 {
696 cmd = CMD_BLOCKLIST;
697 nparm = 1;
698 }
699 else if (!grub_strcmp (arg, "testload"))
700 {
701 cmd = CMD_TESTLOAD;
702 nparm = 1;
703 }
704 else if (grub_strcmp (arg, "xnu_uuid") == 0)
705 {
706 cmd = CMD_XNU_UUID;
707 nparm = 0;
708 }
709 else
710 {
711 fprintf (stderr, _("Invalid command %s.\n"), arg);
712 argp_usage (state);
713 }
714 args_count++;
715 return 0;
716 }
717
718 args[args_count - 1 - num_disks] = xstrdup (arg);
719 args_count++;
720 return 0;
721 }
722
723 struct argp argp = {
724 options, argp_parser, N_("IMAGE_PATH COMMANDS"),
725 N_("Debug tool for filesystem driver."),
726 NULL, NULL, NULL
727 };
728
729 int
730 main (int argc, char *argv[])
731 {
732 const char *default_root;
733 char *alloc_root;
734
735 grub_util_host_init (&argc, &argv);
736
737 args = xcalloc (argc, sizeof (args[0]));
738
739 argp_parse (&argp, argc, argv, 0, 0, 0);
740
741 /* Initialize all modules. */
742 grub_init_all ();
743 grub_gcry_init_all ();
744
745 if (debug_str)
746 grub_env_set ("debug", debug_str);
747
748 default_root = (num_disks == 1) ? "loop0" : "md0";
749 alloc_root = 0;
750 if (root)
751 {
752 if ((*root >= '0') && (*root <= '9'))
753 {
754 alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2);
755
756 sprintf (alloc_root, "%s,%s", default_root, root);
757 root = alloc_root;
758 }
759 }
760 else
761 root = default_root;
762
763 grub_env_set ("root", root);
764
765 if (alloc_root)
766 free (alloc_root);
767
768 /* Do it. */
769 fstest (args_count - 1 - num_disks);
770
771 /* Free resources. */
772 grub_gcry_fini_all ();
773 grub_fini_all ();
774
775 return 0;
776 }