/* grub-fstest.c - debug tool for filesystem driver */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void
grub_putchar (int c)
{
putchar (c);
}
int
grub_getkey (void)
{
return -1;
}
grub_term_t
grub_term_get_current (void)
{
return 0;
}
void
grub_refresh (void)
{
}
static struct grub_command cmd_loopback;
static struct grub_command cmd_blocklist;
static struct grub_command cmd_ls;
grub_command_t
grub_register_command (const char *name,
grub_err_t (*func) (struct grub_arg_list * state,
int argc, char **args),
unsigned flags,
const char *summary __attribute__ ((unused)),
const char *description __attribute__ ((unused)),
const struct grub_arg_option *options)
{
grub_command_t cmd = 0;
if (!grub_strcmp (name, "loopback"))
cmd = &cmd_loopback;
else if (!grub_strcmp (name, "blocklist"))
cmd = &cmd_blocklist;
else if (!grub_strcmp (name, "ls"))
cmd = &cmd_ls;
if (cmd)
{
cmd->func = func;
cmd->flags = flags;
cmd->options = options;
}
return NULL;
}
grub_err_t
execute_command (grub_command_t cmd, int n, char **args)
{
int maxargs = 0;
grub_err_t ret = 0;
struct grub_arg_list *state;
struct grub_arg_option *parser;
char **parsed_arglist;
int numargs;
/* Count the amount of options the command has. */
parser = (struct grub_arg_option *) cmd->options;
while (parser && (parser++)->doc)
maxargs++;
/* Set up the option state. */
state = grub_malloc (sizeof (struct grub_arg_list) * maxargs);
grub_memset (state, 0, sizeof (struct grub_arg_list) * maxargs);
/* Start the command. */
if (!(cmd->flags & GRUB_COMMAND_FLAG_NO_ARG_PARSE))
{
if (grub_arg_parse (cmd, n, args, state, &parsed_arglist, &numargs))
ret = (cmd->func) (state, numargs, parsed_arglist);
}
else
ret = (cmd->func) (state, n, args);
grub_free (state);
return ret;
}
void
grub_unregister_command (const char *name __attribute__ ((unused)))
{
}
#define CMD_LS 1
#define CMD_CP 2
#define CMD_CMP 3
#define CMD_HEX 4
#define CMD_BLOCKLIST 5
#define BUF_SIZE 32256
static grub_off_t skip, leng;
static char *part;
static void
read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len))
{
static char buf[BUF_SIZE];
grub_file_t file;
grub_off_t ofs, len;
file = grub_file_open (pathname);
if (!file)
{
grub_util_error ("cannot open file %s.\n", pathname);
return;
}
if (skip > file->size)
{
grub_util_error ("invalid skip value %d.\n");
return;
}
ofs = skip;
len = file->size - skip;
if ((leng) && (leng < len))
len = leng;
file->offset = skip;
while (len)
{
grub_ssize_t sz;
sz = grub_file_read (file, buf, (len > BUF_SIZE) ? BUF_SIZE : len);
if (sz < 0)
{
grub_util_error ("read error at offset %llu.\n", ofs);
break;
}
if ((sz == 0) || (hook (ofs, buf, sz)))
break;
ofs += sz;
len -= sz;
}
grub_file_close (file);
}
static void
cmd_cp (char *src, char *dest)
{
FILE *ff;
auto int cp_hook (grub_off_t ofs, char *buf, int len);
int cp_hook (grub_off_t ofs, char *buf, int len)
{
(void) ofs;
if ((int) fwrite (buf, 1, len, ff) != len)
{
grub_util_error ("write error.\n");
return 1;
}
return 0;
}
ff = fopen (dest, "w");
if (ff == NULL)
{
grub_util_error ("open error.\n");
return;
}
read_file (src, cp_hook);
fclose (ff);
}
static void
cmd_cmp (char *src, char *dest)
{
FILE *ff;
static char buf_1[BUF_SIZE];
auto int cmp_hook (grub_off_t ofs, char *buf, int len);
int cmp_hook (grub_off_t ofs, char *buf, int len)
{
if ((int) fread (buf_1, 1, len, ff) != len)
{
grub_util_error ("read error at offset %llu.\n", ofs);
return 1;
}
if (grub_memcmp (buf, buf_1, len))
{
int i;
for (i = 0; i < len; i++, ofs++)
if (buf_1[i] != buf[i])
{
grub_util_error ("compare fail at offset %llu.\n", ofs);
return 1;
}
}
return 0;
}
ff = fopen (dest, "r");
if (ff == NULL)
{
grub_util_error ("open error.\n");
return;
}
if ((skip) && (fseek (ff, skip, SEEK_SET)))
grub_util_error ("fseek error.\n");
read_file (src, cmp_hook);
fclose (ff);
}
static void
cmd_hex (char *pathname)
{
auto int hex_hook (grub_off_t ofs, char *buf, int len);
int hex_hook (grub_off_t ofs, char *buf, int len)
{
hexdump (ofs, buf, len);
return 0;
}
read_file (pathname, hex_hook);
}
static void
fstest (char *image_path, int cmd, int n, char **args)
{
char host_file[7 + grub_strlen (image_path) + 1];
char device_name[(part) ? (6 + grub_strlen (part)) : 5];
char *argv[3] = { "-p", "loop", host_file };
grub_sprintf (host_file, "(host)/%s", image_path);
if (execute_command (&cmd_loopback, 3, argv))
{
grub_util_error ("loopback command fails.\n");
goto fail;
}
if (part)
grub_sprintf (device_name, "loop,%s", part);
else
grub_strcpy (device_name, "loop");
grub_env_set ("root", device_name);
switch (cmd)
{
case CMD_LS:
execute_command (&cmd_ls, n, args);
break;
case CMD_CP:
cmd_cp (args[0], args[1]);
break;
case CMD_CMP:
cmd_cmp (args[0], args[1]);
break;
case CMD_HEX:
cmd_hex (args[0]);
break;
case CMD_BLOCKLIST:
execute_command (&cmd_blocklist, n, args);
grub_printf ("\n");
}
fail:
argv[0] = "-d";
execute_command (&cmd_loopback, 2, argv);
}
static struct option options[] = {
{"part", required_argument, 0, 'p'},
{"skip", required_argument, 0, 's'},
{"length", required_argument, 0, 'n'},
{"debug", required_argument, 0, 'd'},
{"raw", no_argument, 0, 'r'},
{"long", no_argument, 0, 'l'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
static void
usage (int status)
{
if (status)
fprintf (stderr, "Try ``grub-fstest --help'' for more information.\n");
else
printf ("\
Usage: grub-fstest [OPTION]... IMAGE_PATH COMMANDS\n\
\n\
Debug tool for filesystem driver.\n\
\nCommands:\n\
ls PATH list files in PATH\n\
cp SRC DEST copy file to local system\n\
cmp SRC DEST compare files\n\
hex FILE hex dump FILE\n\
blocklist FILE display blocklist of FILE\n\
\nOptions:\n\
-p, --part=NUM select partition NUM\n\
-s, --skip=N skip N bytes from output file\n\
-n, --length=N handle N bytes in output file\n\
-d, --debug=S Set debug environment variable\n\
-r, --raw disable auto decompression\n\
-l, --long show long directory list\n\
-h, --help display this message and exit\n\
-V, --version print version information and exit\n\
-v, --verbose print verbose messages\n\
\n\
Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
exit (status);
}
int
main (int argc, char *argv[])
{
char *image_path, *debug_str = 0;
int cmd, is_raw = 0, is_long = 0;
progname = "grub-fstest";
/* Check for options. */
while (1)
{
int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0);
if (c == -1)
break;
else
switch (c)
{
case 'p':
part = optarg;
break;
case 's':
skip = grub_strtoul (optarg, NULL, 0);
break;
case 'n':
leng = grub_strtoul (optarg, NULL, 0);
break;
case 'd':
debug_str = optarg;
break;
case 'r':
is_raw = 1;
break;
case 'l':
is_long = 1;
break;
case 'h':
usage (0);
break;
case 'V':
printf ("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
return 0;
case 'v':
verbosity++;
break;
default:
usage (1);
break;
}
}
/* Obtain PATH. */
if (optind >= argc)
{
fprintf (stderr, "No path is specified.\n");
usage (1);
}
image_path = argv[optind];
if (*image_path != '/')
{
fprintf (stderr, "Must use absolute path.\n");
usage (1);
}
optind++;
cmd = 0;
if (optind < argc)
{
int nparm = 1;
if (!grub_strcmp (argv[optind], "ls"))
{
cmd = CMD_LS;
if (is_long)
argv[optind--] = "-l";
else
nparm = 0;
}
else if (!grub_strcmp (argv[optind], "cp"))
{
cmd = CMD_CP;
nparm = 2;
}
else if (!grub_strcmp (argv[optind], "cmp"))
{
cmd = CMD_CMP;
nparm = 2;
}
else if (!grub_strcmp (argv[optind], "hex"))
{
cmd = CMD_HEX;
}
else if (!grub_strcmp (argv[optind], "blocklist"))
{
cmd = CMD_BLOCKLIST;
}
else
{
fprintf (stderr, "Invalid command %s.\n", argv[optind]);
usage (1);
}
if (optind + 1 + nparm > argc)
{
fprintf (stderr, "Invalid parameter for command %s.\n",
argv[optind]);
usage (1);
}
optind++;
}
else
{
fprintf (stderr, "No command is specified.\n");
usage (1);
}
grub_hostfs_init ();
/* Initialize all modules. */
grub_init_all ();
if (is_raw)
grub_env_set ("filehook", "0");
if (debug_str)
grub_env_set ("debug", debug_str);
/* Do it. */
fstest (image_path + 1, cmd, argc - optind, argv + optind);
/* Free resources. */
grub_fini_all ();
grub_hostfs_fini ();
return 0;
}