diff options
Diffstat (limited to 'util/grub-mkstandalone.c')
-rw-r--r-- | util/grub-mkstandalone.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/util/grub-mkstandalone.c b/util/grub-mkstandalone.c new file mode 100644 index 0000000..5f50a3b --- /dev/null +++ b/util/grub-mkstandalone.c @@ -0,0 +1,363 @@ + +/* + * 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, MAGIC, sizeof (hd.magic)); + 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, MAGIC, sizeof (hd.magic)); + 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); + free (tcn); +} + +int +main (int argc, char *argv[]) +{ + const char *pkglibdir; + int i; + + grub_util_host_init (&argc, &argv); + grub_util_disable_fd_syncs (); + + files = xcalloc (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); + + grub_set_install_backup_ponr (); + + 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; +} |