360 lines
8.3 KiB
C
360 lines
8.3 KiB
C
|
|
/*
|
|
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <grub/util/install.h>
|
|
#include <grub/util/misc.h>
|
|
#include <grub/emu/config.h>
|
|
|
|
#include <string.h>
|
|
|
|
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
|
|
#pragma GCC diagnostic ignored "-Wmissing-declarations"
|
|
#include <argp.h>
|
|
#pragma GCC diagnostic error "-Wmissing-prototypes"
|
|
#pragma GCC diagnostic error "-Wmissing-declarations"
|
|
|
|
static char *output_image;
|
|
static char **files;
|
|
static int nfiles;
|
|
const struct grub_install_image_target_desc *format;
|
|
static FILE *memdisk;
|
|
|
|
enum
|
|
{
|
|
OPTION_OUTPUT = 'o',
|
|
OPTION_FORMAT = 'O'
|
|
};
|
|
|
|
static struct argp_option options[] = {
|
|
GRUB_INSTALL_OPTIONS,
|
|
{"output", 'o', N_("FILE"),
|
|
0, N_("save output in FILE [required]"), 2},
|
|
{"format", 'O', N_("FILE"), 0, 0, 2},
|
|
{"compression", 'C', "xz|none|auto", OPTION_HIDDEN, 0, 2},
|
|
{0, 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
static char *
|
|
help_filter (int key, const char *text, void *input __attribute__ ((unused)))
|
|
{
|
|
switch (key)
|
|
{
|
|
case 'O':
|
|
{
|
|
char *formats = grub_install_get_image_targets_string (), *ret;
|
|
ret = xasprintf ("%s\n%s %s", _("generate an image in FORMAT"),
|
|
_("available formats:"), formats);
|
|
free (formats);
|
|
return ret;
|
|
}
|
|
default:
|
|
return grub_install_help_filter (key, text, input);
|
|
}
|
|
}
|
|
|
|
static error_t
|
|
argp_parser (int key, char *arg, struct argp_state *state)
|
|
{
|
|
if (key == 'C')
|
|
key = GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS;
|
|
|
|
if (grub_install_parse (key, arg))
|
|
return 0;
|
|
|
|
switch (key)
|
|
{
|
|
|
|
case 'o':
|
|
if (output_image)
|
|
free (output_image);
|
|
|
|
output_image = xstrdup (arg);
|
|
break;
|
|
|
|
case 'O':
|
|
{
|
|
format = grub_install_get_image_target (arg);
|
|
if (!format)
|
|
{
|
|
printf (_("unknown target format %s\n"), arg);
|
|
argp_usage (state);
|
|
exit (1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ARGP_KEY_ARG:
|
|
files[nfiles++] = xstrdup (arg);
|
|
break;
|
|
|
|
default:
|
|
return ARGP_ERR_UNKNOWN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct argp argp = {
|
|
options, argp_parser, N_("[OPTION] SOURCE..."),
|
|
N_("Generate a standalone image (containing all modules) in the selected format")"\v"N_("Graft point syntax (E.g. /boot/grub/grub.cfg=./grub.cfg) is accepted"),
|
|
NULL, help_filter, NULL
|
|
};
|
|
|
|
/* tar support */
|
|
#define MAGIC "ustar"
|
|
struct head
|
|
{
|
|
char name[100];
|
|
char mode[8];
|
|
char uid[8];
|
|
char gid[8];
|
|
char size[12];
|
|
char mtime[12];
|
|
char chksum[8];
|
|
char typeflag;
|
|
char linkname[100];
|
|
char magic[6];
|
|
char version[2];
|
|
char uname[32];
|
|
char gname[32];
|
|
char devmajor[8];
|
|
char devminor[8];
|
|
char prefix[155];
|
|
char pad[12];
|
|
} GRUB_PACKED;
|
|
|
|
static void
|
|
write_zeros (unsigned rsz)
|
|
{
|
|
char buf[512];
|
|
|
|
memset (buf, 0, 512);
|
|
fwrite (buf, 1, rsz, memdisk);
|
|
}
|
|
|
|
static void
|
|
write_pad (unsigned sz)
|
|
{
|
|
write_zeros ((~sz + 1) & 511);
|
|
}
|
|
|
|
static void
|
|
set_tar_value (char *field, grub_uint32_t val,
|
|
unsigned len)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < len - 1; i++)
|
|
field[len - 2 - i] = '0' + ((val >> (3 * i)) & 7);
|
|
}
|
|
|
|
static void
|
|
compute_checksum (struct head *hd)
|
|
{
|
|
unsigned int chk = 0;
|
|
unsigned char *ptr;
|
|
memset (hd->chksum, ' ', 8);
|
|
for (ptr = (unsigned char *) hd; ptr < (unsigned char *) (hd + 1); ptr++)
|
|
chk += *ptr;
|
|
set_tar_value (hd->chksum, chk, 8);
|
|
}
|
|
|
|
static void
|
|
add_tar_file (const char *from,
|
|
const char *to)
|
|
{
|
|
char *tcn;
|
|
const char *iptr;
|
|
char *optr;
|
|
struct head hd;
|
|
grub_util_fd_t in;
|
|
ssize_t r;
|
|
grub_uint32_t mtime = 0;
|
|
grub_uint32_t size;
|
|
|
|
COMPILE_TIME_ASSERT (sizeof (hd) == 512);
|
|
|
|
if (grub_util_is_special_file (from))
|
|
return;
|
|
|
|
mtime = grub_util_get_mtime (from);
|
|
|
|
optr = tcn = xmalloc (strlen (to) + 1);
|
|
for (iptr = to; *iptr == '/'; iptr++);
|
|
for (; *iptr; iptr++)
|
|
if (!(iptr[0] == '/' && iptr[1] == '/'))
|
|
*optr++ = *iptr;
|
|
*optr = '\0';
|
|
|
|
if (grub_util_is_directory (from))
|
|
{
|
|
grub_util_fd_dir_t d;
|
|
grub_util_fd_dirent_t de;
|
|
|
|
d = grub_util_fd_opendir (from);
|
|
|
|
while ((de = grub_util_fd_readdir (d)))
|
|
{
|
|
char *fp, *tfp;
|
|
if (strcmp (de->d_name, ".") == 0)
|
|
continue;
|
|
if (strcmp (de->d_name, "..") == 0)
|
|
continue;
|
|
fp = grub_util_path_concat (2, from, de->d_name);
|
|
tfp = xasprintf ("%s/%s", to, de->d_name);
|
|
add_tar_file (fp, tfp);
|
|
free (fp);
|
|
}
|
|
grub_util_fd_closedir (d);
|
|
free (tcn);
|
|
return;
|
|
}
|
|
|
|
if (optr - tcn > 99)
|
|
{
|
|
memset (&hd, 0, sizeof (hd));
|
|
memcpy (hd.name, tcn, 99);
|
|
memcpy (hd.mode, "0000600", 7);
|
|
memcpy (hd.uid, "0001750", 7);
|
|
memcpy (hd.gid, "0001750", 7);
|
|
|
|
set_tar_value (hd.size, optr - tcn, 12);
|
|
set_tar_value (hd.mtime, mtime, 12);
|
|
hd.typeflag = 'L';
|
|
memcpy (hd.magic, "ustar ", 7);
|
|
memcpy (hd.uname, "grub", 4);
|
|
memcpy (hd.gname, "grub", 4);
|
|
|
|
compute_checksum (&hd);
|
|
|
|
fwrite (&hd, 1, sizeof (hd), memdisk);
|
|
fwrite (tcn, 1, optr - tcn, memdisk);
|
|
|
|
write_pad (optr - tcn);
|
|
}
|
|
|
|
in = grub_util_fd_open (from, GRUB_UTIL_FD_O_RDONLY);
|
|
if (!GRUB_UTIL_FD_IS_VALID (in))
|
|
grub_util_error (_("cannot open `%s': %s"), from, grub_util_fd_strerror ());
|
|
|
|
if (!grub_install_copy_buffer)
|
|
grub_install_copy_buffer = xmalloc (GRUB_INSTALL_COPY_BUFFER_SIZE);
|
|
|
|
size = grub_util_get_fd_size (in, from, NULL);
|
|
|
|
memset (&hd, 0, sizeof (hd));
|
|
memcpy (hd.name, tcn, optr - tcn < 99 ? optr - tcn : 99);
|
|
memcpy (hd.mode, "0000600", 7);
|
|
memcpy (hd.uid, "0001750", 7);
|
|
memcpy (hd.gid, "0001750", 7);
|
|
|
|
set_tar_value (hd.size, size, 12);
|
|
set_tar_value (hd.mtime, mtime, 12);
|
|
hd.typeflag = '0';
|
|
memcpy (hd.magic, "ustar ", 7);
|
|
memcpy (hd.uname, "grub", 4);
|
|
memcpy (hd.gname, "grub", 4);
|
|
|
|
compute_checksum (&hd);
|
|
|
|
fwrite (&hd, 1, sizeof (hd), memdisk);
|
|
|
|
while (1)
|
|
{
|
|
r = grub_util_fd_read (in, grub_install_copy_buffer, GRUB_INSTALL_COPY_BUFFER_SIZE);
|
|
if (r <= 0)
|
|
break;
|
|
fwrite (grub_install_copy_buffer, 1, r, memdisk);
|
|
}
|
|
grub_util_fd_close (in);
|
|
|
|
write_pad (size);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
const char *pkglibdir;
|
|
int i;
|
|
|
|
grub_util_host_init (&argc, &argv);
|
|
grub_util_disable_fd_syncs ();
|
|
|
|
files = xmalloc ((argc + 1) * sizeof (files[0]));
|
|
|
|
argp_parse (&argp, argc, argv, 0, 0, 0);
|
|
|
|
pkglibdir = grub_util_get_pkglibdir ();
|
|
|
|
if (!output_image)
|
|
grub_util_error ("%s", _("output file must be specified"));
|
|
|
|
if (!format)
|
|
grub_util_error ("%s", _("Target format not specified (use the -O option)."));
|
|
|
|
if (!grub_install_source_directory)
|
|
grub_install_source_directory = grub_util_path_concat (2, pkglibdir, grub_util_get_target_dirname (format));
|
|
|
|
enum grub_install_plat plat = grub_install_get_target (grub_install_source_directory);
|
|
|
|
char *memdisk_dir = grub_util_make_temporary_dir ();
|
|
char *boot_grub = grub_util_path_concat (3, memdisk_dir, "boot", "grub");
|
|
grub_install_copy_files (grub_install_source_directory,
|
|
boot_grub, plat);
|
|
|
|
char *memdisk_img = grub_util_make_temporary_file ();
|
|
|
|
memdisk = grub_util_fopen (memdisk_img, "wb");
|
|
|
|
add_tar_file (memdisk_dir, "");
|
|
for (i = 0; i < nfiles; i++)
|
|
{
|
|
char *eq = grub_strchr (files[i], '=');
|
|
char *from, *to;
|
|
if (!eq)
|
|
{
|
|
from = files[i];
|
|
to = files[i];
|
|
}
|
|
else
|
|
{
|
|
*eq = '\0';
|
|
to = files[i];
|
|
from = eq + 1;
|
|
}
|
|
while (*to == '/')
|
|
to++;
|
|
add_tar_file (from, to);
|
|
}
|
|
write_zeros (512);
|
|
|
|
fclose (memdisk);
|
|
|
|
grub_util_unlink_recursive (memdisk_dir);
|
|
|
|
grub_install_push_module ("memdisk");
|
|
grub_install_push_module ("tar");
|
|
|
|
grub_install_make_image_wrap (grub_install_source_directory,
|
|
"(memdisk)/boot/grub", output_image,
|
|
memdisk_img, NULL,
|
|
grub_util_get_target_name (format), 0);
|
|
|
|
grub_util_unlink (memdisk_img);
|
|
return 0;
|
|
}
|