2N/A/* grub-fstest.c - debug tool for filesystem driver */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <config.h>
2N/A#include <grub/types.h>
2N/A#include <grub/emu/misc.h>
2N/A#include <grub/util/misc.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/device.h>
2N/A#include <grub/disk.h>
2N/A#include <grub/file.h>
2N/A#include <grub/fs.h>
2N/A#include <grub/env.h>
2N/A#include <grub/term.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/lib/hexdump.h>
2N/A#include <grub/crypto.h>
2N/A#include <grub/command.h>
2N/A#include <grub/i18n.h>
2N/A#include <grub/zfs/zfs.h>
2N/A
2N/A#include <stdio.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <stdlib.h>
2N/A
2N/A#include "progname.h"
2N/A#include "argp.h"
2N/A
2N/Astatic grub_err_t
2N/Aexecute_command (char *name, int n, char **args)
2N/A{
2N/A grub_command_t cmd;
2N/A
2N/A cmd = grub_command_find (name);
2N/A if (! cmd)
2N/A grub_util_error (_("can\'t find command %s"), name);
2N/A
2N/A return (cmd->func) (cmd, n, args);
2N/A}
2N/A
2N/Aenum {
2N/A CMD_LS = 1,
2N/A CMD_CP,
2N/A CMD_CAT,
2N/A CMD_CMP,
2N/A CMD_HEX,
2N/A CMD_CRC,
2N/A CMD_BLOCKLIST,
2N/A CMD_TESTLOAD,
2N/A CMD_ZFSINFO,
2N/A CMD_XNU_UUID
2N/A};
2N/A#define BUF_SIZE 32256
2N/A
2N/Astatic grub_disk_addr_t skip, leng;
2N/Astatic int uncompress = 0;
2N/A
2N/Astatic void
2N/Aread_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len))
2N/A{
2N/A static char buf[BUF_SIZE];
2N/A grub_file_t file;
2N/A grub_off_t ofs, len;
2N/A
2N/A if ((pathname[0] == '-') && (pathname[1] == 0))
2N/A {
2N/A grub_device_t dev;
2N/A
2N/A dev = grub_device_open (0);
2N/A if ((! dev) || (! dev->disk))
2N/A grub_util_error (_("can\'t open device"));
2N/A
2N/A grub_util_info ("total sectors : %lld",
2N/A (unsigned long long) dev->disk->total_sectors);
2N/A
2N/A if (! leng)
2N/A leng = (dev->disk->total_sectors << GRUB_DISK_SECTOR_BITS) - skip;
2N/A
2N/A while (leng)
2N/A {
2N/A grub_size_t len;
2N/A
2N/A len = (leng > BUF_SIZE) ? BUF_SIZE : leng;
2N/A
2N/A if (grub_disk_read (dev->disk, 0, skip, len, buf))
2N/A grub_util_error (_("disk read fails at offset %lld, length %d"),
2N/A skip, len);
2N/A
2N/A if (hook (skip, buf, len))
2N/A break;
2N/A
2N/A skip += len;
2N/A leng -= len;
2N/A }
2N/A
2N/A grub_device_close (dev);
2N/A return;
2N/A }
2N/A
2N/A if (uncompress == 0)
2N/A grub_file_filter_disable_compression ();
2N/A file = grub_file_open (pathname);
2N/A if (!file)
2N/A {
2N/A grub_util_error (_("cannot open file %s:%s"), pathname,
2N/A grub_errmsg);
2N/A return;
2N/A }
2N/A
2N/A grub_util_info ("file size : %lld", (unsigned long long) file->size);
2N/A
2N/A if (skip > file->size)
2N/A {
2N/A grub_util_error (_("invalid skip value %lld"), (unsigned long long) skip);
2N/A return;
2N/A }
2N/A
2N/A ofs = skip;
2N/A len = file->size - skip;
2N/A if ((leng) && (leng < len))
2N/A len = leng;
2N/A
2N/A file->offset = skip;
2N/A
2N/A while (len)
2N/A {
2N/A grub_ssize_t sz;
2N/A
2N/A sz = grub_file_read (file, buf, (len > BUF_SIZE) ? BUF_SIZE : len);
2N/A if (sz < 0)
2N/A {
2N/A grub_util_error (_("read error at offset %llu: %s"), ofs,
2N/A grub_errmsg);
2N/A break;
2N/A }
2N/A
2N/A if ((sz == 0) || (hook (ofs, buf, sz)))
2N/A break;
2N/A
2N/A ofs += sz;
2N/A len -= sz;
2N/A }
2N/A
2N/A grub_file_close (file);
2N/A}
2N/A
2N/Astatic void
2N/Acmd_cp (char *src, char *dest)
2N/A{
2N/A FILE *ff;
2N/A
2N/A auto int cp_hook (grub_off_t ofs, char *buf, int len);
2N/A int cp_hook (grub_off_t ofs, char *buf, int len)
2N/A {
2N/A (void) ofs;
2N/A
2N/A if ((int) fwrite (buf, 1, len, ff) != len)
2N/A {
2N/A grub_util_error (_("write error"));
2N/A return 1;
2N/A }
2N/A
2N/A return 0;
2N/A }
2N/A
2N/A ff = fopen (dest, "wb");
2N/A if (ff == NULL)
2N/A {
2N/A grub_util_error (_("OS file %s open error: %s"), dest,
2N/A strerror (errno));
2N/A return;
2N/A }
2N/A read_file (src, cp_hook);
2N/A fclose (ff);
2N/A}
2N/A
2N/Astatic void
2N/Acmd_cat (char *src)
2N/A{
2N/A auto int cat_hook (grub_off_t ofs, char *buf, int len);
2N/A int cat_hook (grub_off_t ofs, char *buf, int len)
2N/A {
2N/A (void) ofs;
2N/A
2N/A if ((int) fwrite (buf, 1, len, stdout) != len)
2N/A {
2N/A grub_util_error (_("write error"));
2N/A return 1;
2N/A }
2N/A
2N/A return 0;
2N/A }
2N/A
2N/A read_file (src, cat_hook);
2N/A}
2N/A
2N/Astatic void
2N/Acmd_cmp (char *src, char *dest)
2N/A{
2N/A FILE *ff;
2N/A static char buf_1[BUF_SIZE];
2N/A
2N/A auto int cmp_hook (grub_off_t ofs, char *buf, int len);
2N/A int cmp_hook (grub_off_t ofs, char *buf, int len)
2N/A {
2N/A if ((int) fread (buf_1, 1, len, ff) != len)
2N/A {
2N/A grub_util_error (_("read error at offset %llu: %s"), ofs, grub_errmsg);
2N/A return 1;
2N/A }
2N/A
2N/A if (grub_memcmp (buf, buf_1, len))
2N/A {
2N/A int i;
2N/A
2N/A for (i = 0; i < len; i++, ofs++)
2N/A if (buf_1[i] != buf[i])
2N/A {
2N/A grub_util_error (_("compare fail at offset %llu"), ofs);
2N/A return 1;
2N/A }
2N/A }
2N/A return 0;
2N/A }
2N/A
2N/A ff = fopen (dest, "rb");
2N/A if (ff == NULL)
2N/A {
2N/A grub_util_error (_("OS file %s open error: %s"), dest,
2N/A strerror (errno));
2N/A return;
2N/A }
2N/A
2N/A if ((skip) && (fseeko (ff, skip, SEEK_SET)))
2N/A grub_util_error (_("seek error"));
2N/A
2N/A read_file (src, cmp_hook);
2N/A
2N/A {
2N/A grub_uint64_t pre;
2N/A pre = ftell (ff);
2N/A fseek (ff, 0, SEEK_END);
2N/A if (pre != ftell (ff))
2N/A grub_util_error (_("unexpected end of file"));
2N/A }
2N/A fclose (ff);
2N/A}
2N/A
2N/Astatic void
2N/Acmd_hex (char *pathname)
2N/A{
2N/A auto int hex_hook (grub_off_t ofs, char *buf, int len);
2N/A int hex_hook (grub_off_t ofs, char *buf, int len)
2N/A {
2N/A hexdump (ofs, buf, len);
2N/A return 0;
2N/A }
2N/A
2N/A read_file (pathname, hex_hook);
2N/A}
2N/A
2N/Astatic void
2N/Acmd_crc (char *pathname)
2N/A{
2N/A grub_uint8_t crc32_context[GRUB_MD_CRC32->contextsize];
2N/A GRUB_MD_CRC32->init(crc32_context);
2N/A
2N/A auto int crc_hook (grub_off_t ofs, char *buf, int len);
2N/A int crc_hook (grub_off_t ofs, char *buf, int len)
2N/A {
2N/A (void) ofs;
2N/A
2N/A GRUB_MD_CRC32->write(crc32_context, buf, len);
2N/A return 0;
2N/A }
2N/A
2N/A read_file (pathname, crc_hook);
2N/A GRUB_MD_CRC32->final(crc32_context);
2N/A printf ("%08x\n",
2N/A grub_be_to_cpu32(*(grub_uint32_t*)GRUB_MD_CRC32->read(crc32_context)));
2N/A}
2N/A
2N/Astatic char *root = NULL;
2N/Astatic int args_count = 0;
2N/Astatic int nparm = 0;
2N/Astatic int num_disks = 1;
2N/Astatic char **images = NULL;
2N/Astatic int cmd = 0;
2N/Astatic char *debug_str = NULL;
2N/Astatic char **args = NULL;
2N/Astatic int mount_crypt = 0;
2N/A
2N/Astatic void
2N/Afstest (int n, char **args)
2N/A{
2N/A char *host_file;
2N/A char *loop_name;
2N/A int i;
2N/A
2N/A for (i = 0; i < num_disks; i++)
2N/A {
2N/A char *argv[2];
2N/A loop_name = grub_xasprintf ("loop%d", i);
2N/A if (!loop_name)
2N/A grub_util_error (grub_errmsg);
2N/A
2N/A host_file = grub_xasprintf ("(host)%s", images[i]);
2N/A if (!host_file)
2N/A grub_util_error (grub_errmsg);
2N/A
2N/A argv[0] = loop_name;
2N/A argv[1] = host_file;
2N/A
2N/A if (execute_command ("loopback", 2, argv))
2N/A grub_util_error (_("loopback command fails"));
2N/A
2N/A grub_free (loop_name);
2N/A grub_free (host_file);
2N/A }
2N/A
2N/A {
2N/A char *argv[2] = { "-a", NULL};
2N/A if (mount_crypt)
2N/A {
2N/A if (execute_command ("cryptomount", 1, argv))
2N/A grub_util_error (_("cryptomount command fails: %s"), grub_errmsg);
2N/A }
2N/A }
2N/A
2N/A grub_lvm_fini ();
2N/A grub_mdraid09_fini ();
2N/A grub_mdraid1x_fini ();
2N/A grub_raid_fini ();
2N/A grub_raid_init ();
2N/A grub_mdraid09_init ();
2N/A grub_mdraid1x_init ();
2N/A grub_lvm_init ();
2N/A
2N/A switch (cmd)
2N/A {
2N/A case CMD_LS:
2N/A execute_command ("ls", n, args);
2N/A break;
2N/A case CMD_ZFSINFO:
2N/A execute_command ("zfsinfo", n, args);
2N/A break;
2N/A case CMD_CP:
2N/A cmd_cp (args[0], args[1]);
2N/A break;
2N/A case CMD_CAT:
2N/A cmd_cat (args[0]);
2N/A break;
2N/A case CMD_CMP:
2N/A cmd_cmp (args[0], args[1]);
2N/A break;
2N/A case CMD_HEX:
2N/A cmd_hex (args[0]);
2N/A break;
2N/A case CMD_CRC:
2N/A cmd_crc (args[0]);
2N/A break;
2N/A case CMD_BLOCKLIST:
2N/A execute_command ("blocklist", n, args);
2N/A grub_printf ("\n");
2N/A case CMD_TESTLOAD:
2N/A execute_command ("testload", n, args);
2N/A grub_printf ("\n");
2N/A case CMD_XNU_UUID:
2N/A {
2N/A grub_device_t dev;
2N/A grub_fs_t fs;
2N/A char *uuid = 0;
2N/A char *argv[3] = { "-l", NULL, NULL};
2N/A dev = grub_device_open (n ? args[0] : 0);
2N/A if (!dev)
2N/A grub_util_error (grub_errmsg);
2N/A fs = grub_fs_probe (dev);
2N/A if (!fs)
2N/A grub_util_error (grub_errmsg);
2N/A if (!fs->uuid)
2N/A grub_util_error (_("couldn't retrieve UUID"));
2N/A if (fs->uuid (dev, &uuid))
2N/A grub_util_error (grub_errmsg);
2N/A if (!uuid)
2N/A grub_util_error (_("couldn't retrieve UUID"));
2N/A argv[1] = uuid;
2N/A execute_command ("xnu_uuid", 2, argv);
2N/A grub_free (uuid);
2N/A grub_device_close (dev);
2N/A }
2N/A }
2N/A
2N/A for (i = 0; i < num_disks; i++)
2N/A {
2N/A char *argv[2];
2N/A
2N/A loop_name = grub_xasprintf ("loop%d", i);
2N/A if (!loop_name)
2N/A grub_util_error (grub_errmsg);
2N/A
2N/A argv[0] = "-d";
2N/A argv[1] = loop_name;
2N/A
2N/A execute_command ("loopback", 2, argv);
2N/A
2N/A grub_free (loop_name);
2N/A }
2N/A}
2N/A
2N/Astatic struct argp_option options[] = {
2N/A {0, 0, 0 , OPTION_DOC, N_("Commands:"), 1},
2N/A {N_("ls PATH"), 0, 0 , OPTION_DOC, N_("List files in PATH."), 1},
2N/A {N_("cp FILE LOCAL"), 0, 0, OPTION_DOC, N_("Copy FILE to local file LOCAL."), 1},
2N/A {N_("cat FILE"), 0, 0 , OPTION_DOC, N_("Copy FILE to standard output."), 1},
2N/A {N_("cmp FILE LOCAL"), 0, 0, OPTION_DOC, N_("Compare FILE with local file LOCAL."), 1},
2N/A {N_("hex FILE"), 0, 0 , OPTION_DOC, N_("Hex dump FILE."), 1},
2N/A {N_("crc FILE"), 0, 0 , OPTION_DOC, N_("Get crc32 checksum of FILE."), 1},
2N/A {N_("blocklist FILE"), 0, 0, OPTION_DOC, N_("Display blocklist of FILE."), 1},
2N/A {N_("xnu_uuid"), 0, 0, OPTION_DOC, N_("Compute XNU UUID of the device."), 1},
2N/A
2N/A {"root", 'r', N_("DEVICE_NAME"), 0, N_("Set root device."), 2},
2N/A {"skip", 's', "N", 0, N_("Skip N bytes from output file."), 2},
2N/A {"length", 'n', "N", 0, N_("Handle N bytes in output file."), 2},
2N/A {"diskcount", 'c', "N", 0, N_("N input files."), 2},
2N/A {"debug", 'd', "S", 0, N_("Set debug environment variable."), 2},
2N/A {"crypto", 'C', NULL, OPTION_ARG_OPTIONAL, N_("Mount crypto devices."), 2},
2N/A {"zfs-key", 'K', N_("FILE|prompt"), 0, N_("Load zfs crypto key."), 2},
2N/A {"verbose", 'v', NULL, OPTION_ARG_OPTIONAL, N_("Print verbose messages."), 2},
2N/A {"uncompress", 'u', NULL, OPTION_ARG_OPTIONAL, N_("Uncompress data."), 2},
2N/A {0, 0, 0, 0, 0, 0}
2N/A};
2N/A
2N/A/* Print the version information. */
2N/Astatic void
2N/Aprint_version (FILE *stream, struct argp_state *state)
2N/A{
2N/A fprintf (stream, "%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
2N/A}
2N/Avoid (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
2N/A
2N/Aerror_t
2N/Aargp_parser (int key, char *arg, struct argp_state *state)
2N/A{
2N/A char *p;
2N/A
2N/A switch (key)
2N/A {
2N/A case 'r':
2N/A root = arg;
2N/A return 0;
2N/A
2N/A case 'K':
2N/A if (strcmp (arg, "prompt") == 0)
2N/A {
2N/A char buf[1024];
2N/A grub_puts_ (N_("Enter ZFS password: "));
2N/A if (grub_password_get (buf, 1023))
2N/A {
2N/A grub_zfs_add_key ((grub_uint8_t *) buf, grub_strlen (buf), 1);
2N/A }
2N/A }
2N/A else
2N/A {
2N/A FILE *f;
2N/A ssize_t real_size;
2N/A grub_uint8_t buf[1024];
2N/A f = fopen (arg, "rb");
2N/A if (!f)
2N/A {
2N/A printf (_("Error loading file %s: %s\n"), arg, strerror (errno));
2N/A return 0;
2N/A }
2N/A real_size = fread (buf, 1, 1024, f);
2N/A if (real_size < 0)
2N/A {
2N/A printf (_("Error loading file %s: %s\n"), arg, strerror (errno));
2N/A fclose (f);
2N/A return 0;
2N/A }
2N/A grub_zfs_add_key (buf, real_size, 0);
2N/A }
2N/A return 0;
2N/A
2N/A case 'C':
2N/A mount_crypt = 1;
2N/A return 0;
2N/A
2N/A case 's':
2N/A skip = grub_strtoul (arg, &p, 0);
2N/A if (*p == 's')
2N/A skip <<= GRUB_DISK_SECTOR_BITS;
2N/A return 0;
2N/A
2N/A case 'n':
2N/A leng = grub_strtoul (arg, &p, 0);
2N/A if (*p == 's')
2N/A leng <<= GRUB_DISK_SECTOR_BITS;
2N/A return 0;
2N/A
2N/A case 'c':
2N/A num_disks = grub_strtoul (arg, NULL, 0);
2N/A if (num_disks < 1)
2N/A {
2N/A fprintf (stderr, "%s", _("Invalid disk count.\n"));
2N/A argp_usage (state);
2N/A }
2N/A if (args_count != 0)
2N/A {
2N/A fprintf (stderr, "%s", _("Disk count must precede disks list.\n"));
2N/A argp_usage (state);
2N/A }
2N/A return 0;
2N/A
2N/A case 'd':
2N/A debug_str = arg;
2N/A return 0;
2N/A
2N/A case 'v':
2N/A verbosity++;
2N/A return 0;
2N/A
2N/A case 'u':
2N/A uncompress = 1;
2N/A return 0;
2N/A
2N/A case ARGP_KEY_END:
2N/A if (args_count < num_disks)
2N/A {
2N/A fprintf (stderr, "%s", _("No command is specified.\n"));
2N/A argp_usage (state);
2N/A }
2N/A if (args_count - 1 - num_disks < nparm)
2N/A {
2N/A fprintf (stderr, "%s", _("Not enough parameters to command.\n"));
2N/A argp_usage (state);
2N/A }
2N/A return 0;
2N/A
2N/A case ARGP_KEY_ARG:
2N/A break;
2N/A
2N/A default:
2N/A return ARGP_ERR_UNKNOWN;
2N/A }
2N/A
2N/A if (args_count < num_disks)
2N/A {
2N/A if (args_count == 0)
2N/A images = xmalloc (num_disks * sizeof (images[0]));
2N/A images[args_count] = canonicalize_file_name (arg);
2N/A args_count++;
2N/A return 0;
2N/A }
2N/A
2N/A if (args_count == num_disks)
2N/A {
2N/A if (!grub_strcmp (arg, "ls"))
2N/A {
2N/A cmd = CMD_LS;
2N/A }
2N/A else if (!grub_strcmp (arg, "zfsinfo"))
2N/A {
2N/A cmd = CMD_ZFSINFO;
2N/A }
2N/A else if (!grub_strcmp (arg, "cp"))
2N/A {
2N/A cmd = CMD_CP;
2N/A nparm = 2;
2N/A }
2N/A else if (!grub_strcmp (arg, "cat"))
2N/A {
2N/A cmd = CMD_CAT;
2N/A nparm = 1;
2N/A }
2N/A else if (!grub_strcmp (arg, "cmp"))
2N/A {
2N/A cmd = CMD_CMP;
2N/A nparm = 2;
2N/A }
2N/A else if (!grub_strcmp (arg, "hex"))
2N/A {
2N/A cmd = CMD_HEX;
2N/A nparm = 1;
2N/A }
2N/A else if (!grub_strcmp (arg, "crc"))
2N/A {
2N/A cmd = CMD_CRC;
2N/A nparm = 1;
2N/A }
2N/A else if (!grub_strcmp (arg, "blocklist"))
2N/A {
2N/A cmd = CMD_BLOCKLIST;
2N/A nparm = 1;
2N/A }
2N/A else if (!grub_strcmp (arg, "testload"))
2N/A {
2N/A cmd = CMD_TESTLOAD;
2N/A nparm = 1;
2N/A }
2N/A else if (grub_strcmp (arg, "xnu_uuid") == 0)
2N/A {
2N/A cmd = CMD_XNU_UUID;
2N/A nparm = 0;
2N/A }
2N/A else
2N/A {
2N/A fprintf (stderr, _("Invalid command %s.\n"), arg);
2N/A argp_usage (state);
2N/A }
2N/A args_count++;
2N/A return 0;
2N/A }
2N/A
2N/A args[args_count - 1 - num_disks] = xstrdup (arg);
2N/A args_count++;
2N/A return 0;
2N/A}
2N/A
2N/Astruct argp argp = {
2N/A options, argp_parser, N_("IMAGE_PATH COMMANDS"),
2N/A N_("Debug tool for filesystem driver."),
2N/A NULL, NULL, NULL
2N/A};
2N/A
2N/Aint
2N/Amain (int argc, char *argv[])
2N/A{
2N/A char *default_root, *alloc_root;
2N/A
2N/A set_program_name (argv[0]);
2N/A
2N/A grub_util_init_nls ();
2N/A
2N/A args = xmalloc (argc * sizeof (args[0]));
2N/A
2N/A argp_parse (&argp, argc, argv, 0, 0, 0);
2N/A
2N/A /* Initialize all modules. */
2N/A grub_init_all ();
2N/A grub_gcry_init_all ();
2N/A
2N/A if (debug_str)
2N/A grub_env_set ("debug", debug_str);
2N/A
2N/A default_root = (num_disks == 1) ? "loop0" : "md0";
2N/A alloc_root = 0;
2N/A if (root)
2N/A {
2N/A if ((*root >= '0') && (*root <= '9'))
2N/A {
2N/A alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2);
2N/A
2N/A sprintf (alloc_root, "%s,%s", default_root, root);
2N/A root = alloc_root;
2N/A }
2N/A }
2N/A else
2N/A root = default_root;
2N/A
2N/A grub_env_set ("root", root);
2N/A
2N/A if (alloc_root)
2N/A free (alloc_root);
2N/A
2N/A /* Do it. */
2N/A fstest (args_count - 1 - num_disks, args);
2N/A
2N/A /* Free resources. */
2N/A grub_gcry_fini_all ();
2N/A grub_fini_all ();
2N/A
2N/A return 0;
2N/A}