summaryrefslogtreecommitdiff
path: root/usr/src/cmd/boot/installgrub/installgrub.c
diff options
context:
space:
mode:
authorEnrico Perla - Sun Microsystems <Enrico.Perla@Sun.COM>2010-07-28 15:51:57 -0700
committerEnrico Perla - Sun Microsystems <Enrico.Perla@Sun.COM>2010-07-28 15:51:57 -0700
commitaf28f636873b7156cfd73ceffa927658cca33fd0 (patch)
treec7148377edbc72d270398f01aa43a6a0c2b8b11f /usr/src/cmd/boot/installgrub/installgrub.c
parentdde44d91a86b600c41f0e58ba2b3985bb425e52f (diff)
downloadillumos-joyent-af28f636873b7156cfd73ceffa927658cca33fd0.tar.gz
PSARC/2010/113 Boot Block Downgrade Avoidance
PSARC/2010/271 EOF and removal of installgrub(1m) floppy support 6944352 prevent downgrading the boot block on update on multi-BE environments 6972371 EOF and removal of installgrub(1m) floppy support [PSARC/2010/271] --HG-- rename : usr/src/cmd/boot/installgrub/floppy.c => usr/src/cmd/boot/installgrub/pcfs_glue.c
Diffstat (limited to 'usr/src/cmd/boot/installgrub/installgrub.c')
-rw-r--r--usr/src/cmd/boot/installgrub/installgrub.c1742
1 files changed, 1178 insertions, 564 deletions
diff --git a/usr/src/cmd/boot/installgrub/installgrub.c b/usr/src/cmd/boot/installgrub/installgrub.c
index f1beaa473b..452a188d9c 100644
--- a/usr/src/cmd/boot/installgrub/installgrub.c
+++ b/usr/src/cmd/boot/installgrub/installgrub.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
@@ -28,122 +27,154 @@
#include <libgen.h>
#include <malloc.h>
#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
+#include <libintl.h>
+#include <locale.h>
+#include <errno.h>
+#include <libfdisk.h>
+#include <stdarg.h>
+#include <assert.h>
+
#include <sys/mount.h>
#include <sys/mnttab.h>
#include <sys/dktp/fdisk.h>
#include <sys/dkio.h>
#include <sys/vtoc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/multiboot.h>
+#include <sys/sysmacros.h>
-#include <libintl.h>
-#include <locale.h>
#include "message.h"
-#include <errno.h>
-#include <libfdisk.h>
-#include <md5.h>
+#include "installgrub.h"
+#include "./../common/bblk_einfo.h"
+#include "./../common/boot_utils.h"
+#include "./../common/mboot_extra.h"
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SUNW_OST_OSCMD"
#endif
-#define SECTOR_SIZE 0x200
-#define HASH_SIZE 0x10
-#define VERSION_SIZE 0x50
-#define STAGE2_MEMADDR 0x8000 /* loading addr of stage2 */
-
-#define STAGE1_BPB_OFFSET 0x3
-#define STAGE1_BPB_SIZE 0x3B
-#define STAGE1_BOOT_DRIVE 0x40
-#define STAGE1_FORCE_LBA 0x41
-#define STAGE1_STAGE2_ADDRESS 0x42
-#define STAGE1_STAGE2_SECTOR 0x44
-#define STAGE1_STAGE2_SEGMENT 0x48
-
-#define STAGE2_BLOCKLIST (SECTOR_SIZE - 0x8)
-#define STAGE2_INSTALLPART (SECTOR_SIZE + 0x8)
-#define STAGE2_FORCE_LBA (SECTOR_SIZE + 0x11)
-#define STAGE2_VER_STRING (SECTOR_SIZE + 0x12)
-#define STAGE2_SIGN_OFFSET (SECTOR_SIZE + 0x60)
-#define STAGE2_PKG_VERSION (SECTOR_SIZE + 0x70)
-#define STAGE2_BLKOFF 50 /* offset from start of fdisk part */
-
-static char extended_sig[] = "\xCC\xCC\xCC\xCC\xAA\xAA\xAA\xAA\xBB\xBB\xBB\xBB"
-"\xBB\xBB\xBB\xBB";
-
-static int nowrite = 0;
-static int write_mboot = 0;
-static int force_mboot = 0;
-static int getinfo = 0;
-static int do_version = 0;
-static int is_floppy = 0;
-static int is_bootpar = 0;
-static int strip = 0;
-static int stage2_fd;
-static int partition, slice = 0xff;
-static char *device_p0;
-static uint32_t stage2_first_sector, stage2_second_sector;
-
-
-static char bpb_sect[SECTOR_SIZE];
-static char boot_sect[SECTOR_SIZE];
-static char stage1_buffer[SECTOR_SIZE];
-static char stage2_buffer[2 * SECTOR_SIZE];
-static char signature[HASH_SIZE];
-static char verstring[VERSION_SIZE];
-static unsigned int blocklist[SECTOR_SIZE / sizeof (unsigned int)];
-
-static int open_device(char *);
-static void read_bpb_sect(int);
-static void read_boot_sect(char *);
-static void write_boot_sect(char *);
-static void read_stage1_stage2(char *, char *);
-static void modify_and_write_stage1(int);
-static void modify_and_write_stage2(int);
-static unsigned int get_start_sector(int);
-static void copy_stage2(int, char *);
-static char *get_raw_partition(char *);
+/*
+ * Variables to track installgrub desired mode of operation.
+ * 'nowrite' and 'boot_debug' come from boot_common.h.
+ */
+static boolean_t write_mbr = B_FALSE;
+static boolean_t force_mbr = B_FALSE;
+static boolean_t force_update = B_FALSE;
+static boolean_t do_getinfo = B_FALSE;
+static boolean_t do_version = B_FALSE;
+static boolean_t do_mirror_bblk = B_FALSE;
+static boolean_t strip = B_FALSE;
+static boolean_t verbose_dump = B_FALSE;
+
+/* Installing the bootblock is the default operation. */
+static boolean_t do_install = B_TRUE;
+
+/* Versioning string, if present. */
+static char *update_str;
+
+/*
+ * Temporary buffer to store the first 32K of data looking for a multiboot
+ * signature.
+ */
+char mboot_scan[MBOOT_SCAN_SIZE];
+
+/* Function prototypes. */
+static void check_options(char *);
+static int handle_install(char *, char **);
+static int handle_mirror(char *, char **);
+static int handle_getinfo(char *, char **);
+static int commit_to_disk(ig_data_t *, char *);
+static int init_device(ig_device_t *, char *path);
+static void cleanup_device(ig_device_t *);
+static void cleanup_stage2(ig_stage2_t *);
+static int get_start_sector(ig_device_t *);
+static int get_disk_fd(ig_device_t *device);
+static int get_raw_partition_fd(ig_device_t *);
+static char *get_raw_partition_path(ig_device_t *);
+static boolean_t gather_stage2_from_dev(ig_data_t *);
+static int propagate_bootblock(ig_data_t *, ig_data_t *, char *);
+static int find_x86_bootpar(struct mboot *, int *, uint32_t *);
+static int copy_stage2_to_pcfs(ig_data_t *);
+static int write_stage2(ig_data_t *);
+static int write_stage1(ig_data_t *);
static void usage(char *);
-static void print_info();
-static int read_stage2_info(int);
-static void check_extended_support();
+static int read_stage1_from_file(char *, ig_data_t *);
+static int read_stage2_from_file(char *, ig_data_t *);
+static int read_stage1_from_disk(int, char *);
+static int read_stage2_from_disk(int, ig_stage2_t *);
+static int prepare_stage1(ig_data_t *);
+static int prepare_stage2(ig_data_t *, char *);
+static void prepare_fake_multiboot(ig_stage2_t *);
+static void add_stage2_einfo(ig_stage2_t *, char *updt_str);
+static boolean_t is_update_necessary(ig_data_t *, char *);
extern int read_stage2_blocklist(int, unsigned int *);
int
main(int argc, char *argv[])
{
- int dev_fd, opt, params = 3;
- char *stage1, *stage2, *device;
+ int opt;
+ int params = 3;
+ int ret;
+ char **handle_args;
+ char *progname;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
- while ((opt = getopt(argc, argv, "fmneis:")) != EOF) {
+ /*
+ * retro-compatibility: installing the bootblock is the default
+ * and there is no switch for it.
+ */
+ do_install = B_TRUE;
+
+ while ((opt = getopt(argc, argv, "dVMFfmneiu:")) != EOF) {
switch (opt) {
case 'm':
- write_mboot = 1;
+ write_mbr = B_TRUE;
break;
case 'n':
- nowrite = 1;
+ nowrite = B_TRUE;
break;
case 'f':
- force_mboot = 1;
+ force_mbr = B_TRUE;
break;
case 'i':
- getinfo = 1;
+ do_getinfo = B_TRUE;
+ do_install = B_FALSE;
params = 1;
break;
+ case 'V':
+ verbose_dump = B_TRUE;
+ break;
+ case 'd':
+ boot_debug = B_TRUE;
+ break;
+ case 'F':
+ force_update = B_TRUE;
+ break;
case 'e':
- strip = 1;
+ strip = B_TRUE;
+ break;
+ case 'M':
+ do_mirror_bblk = B_TRUE;
+ do_install = B_FALSE;
+ params = 2;
break;
- case 's':
- do_version = 1;
- (void) snprintf(verstring, sizeof (verstring), "%s",
- optarg);
+ case 'u':
+ do_version = B_TRUE;
+
+ update_str = malloc(strlen(optarg) + 1);
+ if (update_str == NULL) {
+ (void) fprintf(stderr, gettext("Unable to "
+ "allocate memory\n"));
+ exit(BC_ERROR);
+ }
+ (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
break;
default:
/* fall through to process non-optional args */
@@ -154,121 +185,543 @@ main(int argc, char *argv[])
/* check arguments */
if (argc != optind + params) {
usage(argv[0]);
+ exit(BC_ERROR);
}
- if (nowrite) {
+ /*
+ * clean up options (and bail out if an unrecoverable combination is
+ * requested.
+ */
+ progname = argv[0];
+ check_options(progname);
+ handle_args = argv + optind;
+
+ if (nowrite)
(void) fprintf(stdout, DRY_RUN);
+
+ if (do_getinfo) {
+ ret = handle_getinfo(progname, handle_args);
+ } else if (do_mirror_bblk) {
+ ret = handle_mirror(progname, handle_args);
+ } else {
+ ret = handle_install(progname, handle_args);
+ }
+ return (ret);
+}
+
+#define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
+static void
+check_options(char *progname)
+{
+ if (do_getinfo && do_mirror_bblk) {
+ (void) fprintf(stderr, gettext("Only one of -M and -i can be "
+ "specified at the same time\n"));
+ usage(progname);
+ exit(BC_ERROR);
}
- if (params == 1) {
- device = strdup(argv[optind]);
- if (!device) {
- usage(argv[0]);
+ if (do_mirror_bblk) {
+ /*
+ * -u and -F may actually reflect a user intent that is not
+ * correct with this command (mirror can be interpreted
+ * "similar" to install. Emit a message and continue.
+ * -e and -V have no meaning, be quiet here and only report the
+ * incongruence if a debug output is requested.
+ */
+ if (do_version) {
+ (void) fprintf(stderr, MEANINGLESS_OPT, "-u");
+ do_version = B_FALSE;
+ }
+ if (force_update) {
+ (void) fprintf(stderr, MEANINGLESS_OPT, "-F");
+ force_update = B_FALSE;
}
- } else if (params == 3) {
- stage1 = strdup(argv[optind]);
- stage2 = strdup(argv[optind + 1]);
- device = strdup(argv[optind + 2]);
+ if (strip || verbose_dump) {
+ BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
+ strip = B_FALSE;
+ verbose_dump = B_FALSE;
+ }
+ }
- if (!stage1 || !stage2 || !device) {
- usage(argv[0]);
+ if (do_getinfo) {
+ if (write_mbr || force_mbr || do_version || force_update) {
+ BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
+ write_mbr = force_mbr = do_version = B_FALSE;
+ force_update = B_FALSE;
}
}
+}
- /* open and check device type */
- dev_fd = open_device(device);
+/*
+ * Install a new stage1/stage2 pair on the specified device. handle_install()
+ * expects argv to contain 3 parameters (the path to stage1, the path to stage2,
+ * the target device).
+ *
+ * Returns: BC_SUCCESS - if the installation is successful
+ * BC_ERROR - if the installation failed
+ * BC_NOUPDT - if no installation was performed because the GRUB
+ * version currently installed is more recent than the
+ * supplied one.
+ *
+ */
+static int
+handle_install(char *progname, char **argv)
+{
+ ig_data_t install_data;
+ char *stage1_path = NULL;
+ char *stage2_path = NULL;
+ char *device_path = NULL;
+ int ret = BC_ERROR;
+
+ stage1_path = strdup(argv[0]);
+ stage2_path = strdup(argv[1]);
+ device_path = strdup(argv[2]);
+
+ bzero(&install_data, sizeof (ig_data_t));
+
+ if (!stage1_path || !stage2_path || !device_path) {
+ (void) fprintf(stderr, gettext("Missing parameter"));
+ usage(progname);
+ goto out;
+ }
- if (getinfo) {
- if (read_stage2_info(dev_fd) != 0) {
- fprintf(stderr, "Unable to read extended information"
- " from %s\n", device);
- exit(1);
- }
- print_info();
- (void) free(device);
- (void) close(dev_fd);
- return (0);
+ BOOT_DEBUG("stage1 path: %s, stage2 path: %s, device: %s\n",
+ stage1_path, stage2_path, device_path);
+
+ if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Unable to gather device "
+ "information for %s\n"), device_path);
+ goto out;
+ }
+
+ /* read in stage1 and stage2. */
+ if (read_stage1_from_file(stage1_path, &install_data) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error opening %s\n"),
+ stage1_path);
+ goto out_dev;
}
- /* read in stage1 and stage2 into buffer */
- read_stage1_stage2(stage1, stage2);
+ if (read_stage2_from_file(stage2_path, &install_data) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error opening %s\n"),
+ stage2_path);
+ goto out_dev;
+ }
- /* check if stage2 supports extended versioning */
- if (do_version)
- check_extended_support(stage2);
+ /* We do not support versioning on PCFS. */
+ if (is_bootpar(install_data.device.type) && do_version)
+ do_version = B_FALSE;
- /* In the pcfs case, write a fresh stage2 */
- if (is_floppy || is_bootpar) {
- copy_stage2(dev_fd, device);
- read_bpb_sect(dev_fd);
+ /*
+ * is_update_necessary() will take care of checking if versioning and/or
+ * forcing the update have been specified. It will also emit a warning
+ * if a non-versioned update is attempted over a versioned bootblock.
+ */
+ if (!is_update_necessary(&install_data, update_str)) {
+ (void) fprintf(stderr, gettext("GRUB version installed "
+ "on %s is more recent or identical\n"
+ "Use -F to override or install without the -u option\n"),
+ device_path);
+ ret = BC_NOUPDT;
+ goto out_dev;
}
+ /*
+ * We get here if:
+ * - the installed GRUB version is older than the one about to be
+ * installed.
+ * - no versioning string has been passed through the command line.
+ * - a forced update is requested (-F).
+ */
+ BOOT_DEBUG("Ready to commit to disk\n");
+ ret = commit_to_disk(&install_data, update_str);
+
+out_dev:
+ cleanup_device(&install_data.device);
+out:
+ free(stage1_path);
+ free(stage2_path);
+ free(device_path);
+ return (ret);
+}
- /* read in boot sector */
- if (!is_floppy)
- read_boot_sect(device);
+/*
+ * Retrieves from a device the extended information (einfo) associated to the
+ * installed stage2.
+ * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
+ * Returns:
+ * - BC_SUCCESS (and prints out einfo contents depending on 'flags')
+ * - BC_ERROR (on error)
+ * - BC_NOEINFO (no extended information available)
+ */
+static int
+handle_getinfo(char *progname, char **argv)
+{
+ ig_data_t data;
+ ig_stage2_t *stage2 = &data.stage2;
+ ig_device_t *device = &data.device;
+ bblk_einfo_t *einfo;
+ uint8_t flags = 0;
+ uint32_t size;
+ char *device_path;
+ int retval = BC_ERROR;
+ int ret;
+
+ device_path = strdup(argv[0]);
+ if (!device_path) {
+ (void) fprintf(stderr, gettext("Missing parameter"));
+ usage(progname);
+ goto out;
+ }
+
+ bzero(&data, sizeof (ig_data_t));
+ BOOT_DEBUG("device path: %s\n", device_path);
- /* modify stage1 based on grub needs */
- modify_and_write_stage1(dev_fd);
+ if (init_device(device, device_path) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Unable to gather device "
+ "information for %s\n"), device_path);
+ goto out_dev;
+ }
- /* modify stage2 and write to media */
- modify_and_write_stage2(dev_fd);
+ if (is_bootpar(device->type)) {
+ (void) fprintf(stderr, gettext("Versioning not supported on "
+ "PCFS\n"));
+ goto out_dev;
+ }
- if (!is_floppy && write_mboot)
- write_boot_sect(device);
+ ret = read_stage2_from_disk(device->part_fd, stage2);
+ if (ret == BC_ERROR) {
+ (void) fprintf(stderr, gettext("Error reading stage2 from "
+ "%s\n"), device_path);
+ goto out_dev;
+ }
- (void) close(dev_fd);
- free(device);
- free(stage1);
- free(stage2);
+ if (ret == BC_NOEXTRA) {
+ (void) fprintf(stdout, gettext("No multiboot header found on "
+ "%s, unable to locate extra information area\n"),
+ device_path);
+ retval = BC_NOEINFO;
+ goto out_dev;
+ }
- return (0);
+ einfo = find_einfo(stage2->extra);
+ if (einfo == NULL) {
+ retval = BC_NOEINFO;
+ (void) fprintf(stderr, gettext("No extended information "
+ "found\n"));
+ goto out_dev;
+ }
+
+ /* Print the extended information. */
+ if (strip)
+ flags |= EINFO_EASY_PARSE;
+ if (verbose_dump)
+ flags |= EINFO_PRINT_HEADER;
+
+ size = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
+ print_einfo(flags, einfo, size);
+ retval = BC_SUCCESS;
+
+out_dev:
+ cleanup_device(&data.device);
+out:
+ free(device_path);
+ return (retval);
}
-static unsigned int
-get_start_sector(int fd)
+/*
+ * Attempt to mirror (propagate) the current stage2 over the attaching disk.
+ *
+ * Returns:
+ * - BC_SUCCESS (a successful propagation happened)
+ * - BC_ERROR (an error occurred)
+ * - BC_NOEXTRA (it is not possible to dump the current bootblock since
+ * there is no multiboot information)
+ */
+static int
+handle_mirror(char *progname, char **argv)
{
- static unsigned int start_sect = 0;
- uint32_t secnum = 0, numsec = 0;
- int i, pno, rval, log_part = 0;
- struct mboot *mboot;
- struct ipart *part;
- ext_part_t *epp;
- struct part_info dkpi;
- struct extpart_info edkpi;
-
- if (start_sect)
- return (start_sect);
-
- mboot = (struct mboot *)boot_sect;
- for (i = 0; i < FD_NUMPART; i++) {
- part = (struct ipart *)mboot->parts + i;
- if (is_bootpar) {
- if (part->systid == 0xbe) {
- start_sect = part->relsect;
- partition = i;
- goto found_part;
- }
- }
+ ig_data_t curr_data;
+ ig_data_t attach_data;
+ ig_device_t *curr_device = &curr_data.device;
+ ig_device_t *attach_device = &attach_data.device;
+ ig_stage2_t *stage2_curr = &curr_data.stage2;
+ ig_stage2_t *stage2_attach = &attach_data.stage2;
+ bblk_einfo_t *einfo_curr = NULL;
+ char *curr_device_path;
+ char *attach_device_path;
+ char *updt_str = NULL;
+ int retval = BC_ERROR;
+ int ret;
+
+ curr_device_path = strdup(argv[0]);
+ attach_device_path = strdup(argv[1]);
+
+ if (!curr_device_path || !attach_device_path) {
+ (void) fprintf(stderr, gettext("Missing parameter"));
+ usage(progname);
+ goto out;
+ }
+ BOOT_DEBUG("Current device path is: %s, attaching device path is: "
+ " %s\n", curr_device_path, attach_device_path);
+
+ bzero(&curr_data, sizeof (ig_data_t));
+ bzero(&attach_data, sizeof (ig_data_t));
+
+ if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Unable to gather device "
+ "information for %s (current device)\n"), curr_device_path);
+ goto out_currdev;
+ }
+
+ if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Unable to gather device "
+ "information for %s (attaching device)\n"),
+ attach_device_path);
+ goto out_devs;
}
+ if (is_bootpar(curr_device->type) || is_bootpar(attach_device->type)) {
+ (void) fprintf(stderr, gettext("boot block mirroring is not "
+ "supported on PCFS\n"));
+ goto out_devs;
+ }
+
+ ret = read_stage2_from_disk(curr_device->part_fd, stage2_curr);
+ if (ret == BC_ERROR) {
+ BOOT_DEBUG("Error reading first stage2 blocks from %s\n",
+ curr_device->path);
+ retval = BC_ERROR;
+ goto out_devs;
+ }
+
+ if (ret == BC_NOEXTRA) {
+ BOOT_DEBUG("No multiboot header found on %s, unable to grab "
+ "stage2\n", curr_device->path);
+ retval = BC_NOEXTRA;
+ goto out_devs;
+ }
+
+ einfo_curr = find_einfo(stage2_curr->extra);
+ if (einfo_curr != NULL)
+ updt_str = einfo_get_string(einfo_curr);
+
+ write_mbr = B_TRUE;
+ force_mbr = B_TRUE;
+ retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
+ cleanup_stage2(stage2_curr);
+ cleanup_stage2(stage2_attach);
+
+out_devs:
+ cleanup_device(attach_device);
+out_currdev:
+ cleanup_device(curr_device);
+out:
+ free(curr_device_path);
+ free(attach_device_path);
+ return (retval);
+}
+
+static int
+commit_to_disk(ig_data_t *install, char *updt_str)
+{
+ assert(install != NULL);
/*
- * We will not support x86 boot partition on extended partitions
+ * vanilla stage1 and stage2 need to be updated at runtime.
+ * Update stage2 before stage1 because stage1 needs to know the first
+ * sector stage2 will be written to.
*/
- if (is_bootpar) {
- (void) fprintf(stderr, NOBOOTPAR);
- exit(-1);
+ if (prepare_stage2(install, updt_str) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error building stage2\n"));
+ return (BC_ERROR);
+ }
+ if (prepare_stage1(install) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error building stage1\n"));
+ return (BC_ERROR);
+ }
+
+ /* Write stage2 out to disk. */
+ if (write_stage2(install) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error writing stage2 to "
+ "disk\n"));
+ return (BC_ERROR);
+ }
+
+ /* Write stage1 to disk and, if requested, to the MBR. */
+ if (write_stage1(install) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error writing stage1 to "
+ "disk\n"));
+ return (BC_ERROR);
+ }
+
+ return (BC_SUCCESS);
+}
+
+/*
+ * Propagate the bootblock on the source disk to the destination disk and
+ * version it with 'updt_str' in the process. Since we cannot trust any data
+ * on the attaching disk, we do not perform any specific check on a potential
+ * target extended information structure and we just blindly update.
+ */
+static int
+propagate_bootblock(ig_data_t *source, ig_data_t *target, char *updt_str)
+{
+ ig_device_t *src_device = &source->device;
+ ig_device_t *dest_device = &target->device;
+ ig_stage2_t *src_stage2 = &source->stage2;
+ ig_stage2_t *dest_stage2 = &target->stage2;
+ uint32_t buf_size;
+ int retval;
+
+ assert(source != NULL);
+ assert(target != NULL);
+
+ /* read in stage1 from the source disk. */
+ if (read_stage1_from_disk(src_device->part_fd, target->stage1_buf)
+ != BC_SUCCESS)
+ return (BC_ERROR);
+
+ /* Prepare target stage2 for commit_to_disk. */
+ cleanup_stage2(dest_stage2);
+
+ if (updt_str != NULL)
+ do_version = B_TRUE;
+ else
+ do_version = B_FALSE;
+
+ buf_size = src_stage2->file_size + SECTOR_SIZE;
+
+ dest_stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
+ dest_stage2->buf = malloc(dest_stage2->buf_size);
+ if (dest_stage2->buf == NULL) {
+ perror(gettext("Memory allocation failed"));
+ return (BC_ERROR);
+ }
+ dest_stage2->file = dest_stage2->buf;
+ dest_stage2->file_size = src_stage2->file_size;
+ memcpy(dest_stage2->file, src_stage2->file, dest_stage2->file_size);
+ dest_stage2->extra = dest_stage2->buf +
+ P2ROUNDUP(dest_stage2->file_size, 8);
+
+ /* If we get down here we do have a mboot structure. */
+ assert(src_stage2->mboot);
+
+ dest_stage2->mboot_off = src_stage2->mboot_off;
+ dest_stage2->mboot = (multiboot_header_t *)(dest_stage2->buf +
+ dest_stage2->mboot_off);
+
+ (void) fprintf(stdout, gettext("Propagating %s stage1/stage2 to %s\n"),
+ src_device->path, dest_device->path);
+ retval = commit_to_disk(target, updt_str);
+
+ return (retval);
+}
+
+/*
+ * open the device and fill the various members of ig_device_t.
+ */
+static int
+init_device(ig_device_t *device, char *path)
+{
+ bzero(device, sizeof (*device));
+ device->part_fd = -1;
+ device->disk_fd = -1;
+ device->path_p0 = NULL;
+
+ device->path = strdup(path);
+ if (device->path == NULL) {
+ perror(gettext("Memory allocation failed"));
+ return (BC_ERROR);
+ }
+
+ if (strstr(device->path, "diskette")) {
+ (void) fprintf(stderr, gettext("installing GRUB to a floppy "
+ "disk is no longer supported\n"));
+ return (BC_ERROR);
+ }
+
+ /* Detect if the target device is a pcfs partition. */
+ if (strstr(device->path, "p0:boot"))
+ device->type = IG_DEV_X86BOOTPAR;
+
+ if (get_disk_fd(device) != BC_SUCCESS)
+ return (BC_ERROR);
+
+ /* read in the device boot sector. */
+ if (read(device->disk_fd, device->boot_sector, SECTOR_SIZE)
+ != SECTOR_SIZE) {
+ (void) fprintf(stderr, gettext("Error reading boot sector\n"));
+ perror("read");
+ return (BC_ERROR);
+ }
+
+ if (get_raw_partition_fd(device) != BC_SUCCESS)
+ return (BC_ERROR);
+
+ if (get_start_sector(device) != BC_SUCCESS)
+ return (BC_ERROR);
+
+ return (BC_SUCCESS);
+}
+
+static void
+cleanup_device(ig_device_t *device)
+{
+ if (device->path)
+ free(device->path);
+ if (device->path_p0)
+ free(device->path_p0);
+
+ if (device->part_fd != -1)
+ (void) close(device->part_fd);
+ if (device->disk_fd != -1)
+ (void) close(device->disk_fd);
+
+ bzero(device, sizeof (ig_device_t));
+ device->part_fd = -1;
+ device->disk_fd = -1;
+}
+
+static void
+cleanup_stage2(ig_stage2_t *stage2)
+{
+ if (stage2->buf)
+ free(stage2->buf);
+ bzero(stage2, sizeof (ig_stage2_t));
+}
+
+static int
+get_start_sector(ig_device_t *device)
+{
+ uint32_t secnum = 0, numsec = 0;
+ int i, pno, rval, log_part = 0;
+ struct mboot *mboot;
+ struct ipart *part;
+ ext_part_t *epp;
+ struct part_info dkpi;
+ struct extpart_info edkpi;
+
+ mboot = (struct mboot *)device->boot_sector;
+
+ if (is_bootpar(device->type)) {
+ if (find_x86_bootpar(mboot, &pno, &secnum) != BC_SUCCESS) {
+ (void) fprintf(stderr, NOBOOTPAR);
+ return (BC_ERROR);
+ } else {
+ device->start_sector = secnum;
+ device->partition = pno;
+ goto found_part;
+ }
}
/*
- * Not an x86 boot partition. Search for Solaris fdisk partition
+ * Search for Solaris fdisk partition
* Get the solaris partition information from the device
* and compare the offset of S2 with offset of solaris partition
* from fdisk partition table.
*/
- if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
- if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) {
+ if (ioctl(device->part_fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
+ if (ioctl(device->part_fd, DKIOCPARTINFO, &dkpi) < 0) {
(void) fprintf(stderr, PART_FAIL);
- exit(-1);
+ return (BC_ERROR);
} else {
edkpi.p_start = dkpi.p_start;
}
@@ -279,7 +732,7 @@ get_start_sector(int fd)
if (part->relsect == 0) {
(void) fprintf(stderr, BAD_PART, i);
- exit(-1);
+ return (BC_ERROR);
}
if (edkpi.p_start >= part->relsect &&
@@ -292,7 +745,7 @@ get_start_sector(int fd)
if (i == FD_NUMPART) {
/* No solaris fdisk partitions (primary or logical) */
(void) fprintf(stderr, NOSOLPAR);
- exit(-1);
+ return (BC_ERROR);
}
/*
@@ -300,8 +753,8 @@ get_start_sector(int fd)
* Handle the simple case first: Solaris in a primary partition
*/
if (!fdisk_is_dos_extended(part->systid)) {
- start_sect = part->relsect;
- partition = i;
+ device->start_sector = part->relsect;
+ device->partition = i;
goto found_part;
}
@@ -309,7 +762,7 @@ get_start_sector(int fd)
* Solaris in a logical partition. Find that partition in the
* extended part.
*/
- if ((rval = libfdisk_init(&epp, device_p0, NULL, FDISK_READ_DISK))
+ if ((rval = libfdisk_init(&epp, device->path_p0, NULL, FDISK_READ_DISK))
!= FDISK_SUCCESS) {
switch (rval) {
/*
@@ -320,23 +773,19 @@ get_start_sector(int fd)
case FDISK_ENOLOGDRIVE:
case FDISK_EBADMAGIC:
(void) fprintf(stderr, NOSOLPAR);
- exit(-1);
- /*NOTREACHED*/
+ return (BC_ERROR);
case FDISK_ENOVGEOM:
(void) fprintf(stderr, NO_VIRT_GEOM);
- exit(1);
- break;
+ return (BC_ERROR);
case FDISK_ENOPGEOM:
(void) fprintf(stderr, NO_PHYS_GEOM);
- exit(1);
- break;
+ return (BC_ERROR);
case FDISK_ENOLGEOM:
(void) fprintf(stderr, NO_LABEL_GEOM);
- exit(1);
- break;
+ return (BC_ERROR);
default:
(void) fprintf(stderr, LIBFDISK_INIT_FAIL);
- exit(1);
+ return (BC_ERROR);
break;
}
}
@@ -346,21 +795,21 @@ get_start_sector(int fd)
if (rval != FDISK_SUCCESS) {
/* No solaris logical partition */
(void) fprintf(stderr, NOSOLPAR);
- exit(-1);
+ return (BC_ERROR);
}
- start_sect = secnum;
- partition = pno - 1;
+ device->start_sector = secnum;
+ device->partition = pno - 1;
log_part = 1;
found_part:
/* get confirmation for -m */
- if (write_mboot && !force_mboot) {
+ if (write_mbr && !force_mbr) {
(void) fprintf(stdout, MBOOT_PROMPT);
if (getchar() != 'y') {
- write_mboot = 0;
+ write_mbr = 0;
(void) fprintf(stdout, MBOOT_NOT_UPDATED);
- exit(-1);
+ return (BC_ERROR);
}
}
@@ -368,318 +817,463 @@ found_part:
* Currently if Solaris is in an extended partition we need to
* write GRUB to the MBR. Check for this.
*/
- if (log_part && !write_mboot) {
- write_mboot = 1;
+ if (log_part && !write_mbr) {
+ (void) fprintf(stdout, gettext("Installing Solaris on an "
+ "extended partition... forcing MBR update\n"));
+ write_mbr = 1;
}
/*
* warn, if Solaris in primary partition and GRUB not in MBR and
* partition is not active
*/
- if (!log_part && part->bootid != 128 && !write_mboot) {
- (void) fprintf(stdout, SOLPAR_INACTIVE, partition + 1);
+ if (!log_part && part->bootid != 128 && !write_mbr) {
+ (void) fprintf(stdout, SOLPAR_INACTIVE, device->partition + 1);
}
- return (start_sect);
-}
-
-static void
-usage(char *progname)
-{
- (void) fprintf(stderr, USAGE, basename(progname));
- exit(-1);
+ return (BC_SUCCESS);
}
static int
-open_device(char *device)
+get_disk_fd(ig_device_t *device)
{
- int dev_fd;
- struct stat stat;
- char *raw_part;
-
- is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) &&
- strncmp(device, "/dev/dsk", strlen("/dev/dsk"));
-
- /* handle boot partition specification */
- if (!is_floppy && strstr(device, "p0:boot")) {
- is_bootpar = 1;
+ int i;
+ char save[2];
+ char *end = NULL;
+
+ assert(device != NULL);
+ assert(device->path != NULL);
+
+ if (is_bootpar(device->type)) {
+ end = strstr(device->path, "p0:boot");
+ /* tested at the start of init_device() */
+ assert(end != NULL);
+ /* chop off :boot */
+ save[0] = end[2];
+ end[2] = '\0';
+ } else {
+ i = strlen(device->path);
+ save[0] = device->path[i - 2];
+ save[1] = device->path[i - 1];
+ device->path[i - 2] = 'p';
+ device->path[i - 1] = '0';
}
- raw_part = get_raw_partition(device);
-
if (nowrite)
- dev_fd = open(raw_part, O_RDONLY);
+ device->disk_fd = open(device->path, O_RDONLY);
else
- dev_fd = open(raw_part, O_RDWR);
+ device->disk_fd = open(device->path, O_RDWR);
- if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) {
- (void) fprintf(stderr, OPEN_FAIL, raw_part);
- exit(-1);
+ device->path_p0 = strdup(device->path);
+ if (device->path_p0 == NULL) {
+ perror("strdup");
+ return (BC_ERROR);
}
- if (S_ISCHR(stat.st_mode) == 0) {
- (void) fprintf(stderr, NOT_RAW_DEVICE, raw_part);
- exit(-1);
+
+ if (is_bootpar(device->type)) {
+ end[2] = save[0];
+ } else {
+ device->path[i - 2] = save[0];
+ device->path[i - 1] = save[1];
+ }
+
+ if (device->disk_fd == -1) {
+ perror("open");
+ return (BC_ERROR);
}
- return (dev_fd);
+ return (BC_SUCCESS);
}
static void
-read_stage1_stage2(char *stage1, char *stage2)
+prepare_fake_multiboot(ig_stage2_t *stage2)
{
- int fd;
+ multiboot_header_t *mboot;
- /* read the stage1 file from filesystem */
- fd = open(stage1, O_RDONLY);
- if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) {
- (void) fprintf(stderr, READ_FAIL_STAGE1, stage1);
- exit(-1);
- }
- (void) close(fd);
+ assert(stage2 != NULL);
+ assert(stage2->mboot != NULL);
+ assert(stage2->buf != NULL);
- /* read first two blocks of stage 2 from filesystem */
- stage2_fd = open(stage2, O_RDONLY);
- if (stage2_fd == -1 ||
- read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE)
- != 2 * SECTOR_SIZE) {
- (void) fprintf(stderr, READ_FAIL_STAGE2, stage2);
- exit(-1);
- }
- /* leave the stage2 file open for later */
+ mboot = stage2->mboot;
+
+ /*
+ * Currently we expect find_multiboot() to have located a multiboot
+ * header with the AOUT kludge flag set.
+ */
+ assert(mboot->flags & BB_MBOOT_AOUT_FLAG);
+
+ /* Insert the information necessary to locate stage2. */
+ mboot->header_addr = stage2->mboot_off;
+ mboot->load_addr = 0;
+ mboot->load_end_addr = stage2->file_size;
}
static void
-read_bpb_sect(int dev_fd)
+add_stage2_einfo(ig_stage2_t *stage2, char *updt_str)
{
- if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) {
- (void) fprintf(stderr, READ_FAIL_BPB);
- exit(-1);
+ bblk_hs_t hs;
+ uint32_t avail_space;
+
+ assert(stage2 != NULL);
+
+ /* Fill bootblock hashing source information. */
+ hs.src_buf = (unsigned char *)stage2->file;
+ hs.src_size = stage2->file_size;
+ /* How much space for the extended information structure? */
+ avail_space = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8);
+ add_einfo(stage2->extra, updt_str, &hs, avail_space);
+}
+
+
+static int
+write_stage2(ig_data_t *install)
+{
+ ig_device_t *device = &install->device;
+ ig_stage2_t *stage2 = &install->stage2;
+ off_t offset;
+
+ assert(install != NULL);
+
+ if (is_bootpar(device->type)) {
+ /*
+ * stage2 is already on the filesystem, we only need to update
+ * the first two blocks (that we have modified during
+ * prepare_stage2())
+ */
+ if (write_out(device->part_fd, stage2->file, SECTOR_SIZE,
+ stage2->pcfs_first_sectors[0] * SECTOR_SIZE)
+ != BC_SUCCESS ||
+ write_out(device->part_fd, stage2->file + SECTOR_SIZE,
+ SECTOR_SIZE, stage2->pcfs_first_sectors[1] * SECTOR_SIZE)
+ != BC_SUCCESS) {
+ (void) fprintf(stderr, WRITE_FAIL_STAGE2);
+ return (BC_ERROR);
+ }
+ (void) fprintf(stdout, WRITE_STAGE2_PCFS);
+ return (BC_SUCCESS);
}
+
+ /*
+ * For disk, write stage2 starting at STAGE2_BLKOFF sector.
+ * Note that we use stage2->buf rather than stage2->file, because we
+ * may have extended information after the latter.
+ */
+ offset = STAGE2_BLKOFF * SECTOR_SIZE;
+ if (write_out(device->part_fd, stage2->buf, stage2->buf_size,
+ offset) != BC_SUCCESS) {
+ perror("write");
+ return (BC_ERROR);
+ }
+
+ /* Simulate the "old" installgrub output. */
+ (void) fprintf(stdout, WRITE_STAGE2_DISK, device->partition,
+ (stage2->buf_size / SECTOR_SIZE) + 1, STAGE2_BLKOFF,
+ stage2->first_sector);
+
+ return (BC_SUCCESS);
}
-static void
-read_boot_sect(char *device)
+static int
+write_stage1(ig_data_t *install)
{
- static int read_mbr = 0;
- int i, fd;
- char save[2];
-
- if (read_mbr)
- return;
- read_mbr = 1;
-
- /* get the whole disk (p0) */
- i = strlen(device);
- save[0] = device[i - 2];
- save[1] = device[i - 1];
- device[i - 2] = 'p';
- device[i - 1] = '0';
-
- device_p0 = strdup(device);
- fd = open(device, O_RDONLY);
- if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
- (void) fprintf(stderr, READ_FAIL_MBR, device);
- if (fd == -1)
- perror("open");
- else
- perror("read");
- exit(-1);
+ ig_device_t *device = &install->device;
+
+ assert(install != NULL);
+
+ if (write_out(device->part_fd, install->stage1_buf,
+ sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
+ (void) fprintf(stdout, WRITE_FAIL_PBOOT);
+ perror("write");
+ return (BC_ERROR);
}
- (void) close(fd);
- device[i - 2] = save[0];
- device[i - 1] = save[1];
+
+ /* Simulate "old" installgrub output. */
+ (void) fprintf(stdout, WRITE_PBOOT, device->partition,
+ device->start_sector);
+
+ if (write_mbr) {
+ if (write_out(device->disk_fd, install->stage1_buf,
+ sizeof (install->stage1_buf), 0) != BC_SUCCESS) {
+ (void) fprintf(stdout, WRITE_FAIL_BOOTSEC);
+ perror("write");
+ return (BC_ERROR);
+ }
+ /* Simulate "old" installgrub output. */
+ (void) fprintf(stdout, WRITE_MBOOT);
+ }
+
+ return (BC_SUCCESS);
}
+#define USAGE_STRING "%s [-m|-f|-n|-F|-u verstr] stage1 stage2 device\n" \
+ "%s -M [-n] device1 device2\n" \
+ "%s [-V|-e] -i device\n" \
+
+#define CANON_USAGE_STR gettext(USAGE_STRING)
+
static void
-write_boot_sect(char *device)
+usage(char *progname)
{
- int fd, len;
- char *raw, *end;
- struct stat stat;
+ (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
+}
- /* make a copy and chop off ":boot" */
- raw = strdup(device);
- end = strstr(raw, "p0:boot");
- if (end)
- end[2] = 0;
- /* open p0 (whole disk) */
- len = strlen(raw);
- raw[len - 2] = 'p';
- raw[len - 1] = '0';
- fd = open(raw, O_WRONLY);
- if (fd == -1 || fstat(fd, &stat) != 0) {
- (void) fprintf(stderr, OPEN_FAIL, raw);
- exit(-1);
- }
- if (!nowrite &&
- pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
- (void) fprintf(stderr, WRITE_FAIL_BOOTSEC);
- exit(-1);
+static int
+read_stage1_from_file(char *path, ig_data_t *dest)
+{
+ int fd;
+
+ assert(dest);
+
+ /* read the stage1 file from filesystem */
+ fd = open(path, O_RDONLY);
+ if (fd == -1 ||
+ read(fd, dest->stage1_buf, SECTOR_SIZE) != SECTOR_SIZE) {
+ (void) fprintf(stderr, READ_FAIL_STAGE1, path);
+ return (BC_ERROR);
}
- (void) fprintf(stdout, WRITE_MBOOT);
(void) close(fd);
+ return (BC_SUCCESS);
}
-static void
-modify_and_write_stage1(int dev_fd)
+static int
+read_stage2_from_file(char *path, ig_data_t *dest)
{
- if (is_floppy) {
- stage2_first_sector = blocklist[0];
- /* copy bios parameter block (for fat fs) */
- bcopy(bpb_sect + STAGE1_BPB_OFFSET,
- stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
- } else if (is_bootpar) {
- stage2_first_sector = get_start_sector(dev_fd) + blocklist[0];
- /* copy bios parameter block (for fat fs) and MBR */
- bcopy(bpb_sect + STAGE1_BPB_OFFSET,
- stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
- bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
- *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
+ int fd;
+ struct stat sb;
+ ig_stage2_t *stage2 = &dest->stage2;
+ ig_device_t *device = &dest->device;
+ uint32_t buf_size;
+
+ assert(dest);
+ assert(stage2->buf == NULL);
+
+ fd = open(path, O_RDONLY);
+ if (fstat(fd, &sb) == -1) {
+ perror("fstat");
+ goto out;
+ }
+
+ stage2->file_size = sb.st_size;
+
+ if (!is_bootpar(device->type)) {
+ /*
+ * buffer size needs to account for stage2 plus the extra
+ * versioning information at the end of it. We reserve one
+ * extra sector (plus we round up to the next sector boundary).
+ */
+ buf_size = stage2->file_size + SECTOR_SIZE;
} else {
- stage2_first_sector = get_start_sector(dev_fd) + STAGE2_BLKOFF;
- /* copy MBR to stage1 in case of overwriting MBR sector */
- bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ);
- *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1;
+ /* In the PCFS case we only need to read in stage2. */
+ buf_size = stage2->file_size;
}
- /* modify default stage1 file generated by GRUB */
- *((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR))
- = stage2_first_sector;
- *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS))
- = STAGE2_MEMADDR;
- *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT))
- = STAGE2_MEMADDR >> 4;
+ stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
+
+ BOOT_DEBUG("stage2 buffer size = %d (%d sectors)\n", stage2->buf_size,
+ stage2->buf_size / SECTOR_SIZE);
+
+ stage2->buf = malloc(stage2->buf_size);
+ if (stage2->buf == NULL) {
+ perror(gettext("Memory allocation failed"));
+ goto out_fd;
+ }
+
+ stage2->file = stage2->buf;
/*
- * XXX the default grub distribution also:
- * - Copy the possible MBR/extended part table
- * - Set the boot drive of stage1
+ * Extra information (e.g. the versioning structure) is placed at the
+ * end of stage2, aligned on a 8-byte boundary.
*/
+ if (!(is_bootpar(device->type)))
+ stage2->extra = stage2->file + P2ROUNDUP(stage2->file_size, 8);
- /* write stage1/pboot to 1st sector */
- if (!nowrite &&
- pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) {
- (void) fprintf(stderr, WRITE_FAIL_PBOOT);
- exit(-1);
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ perror("lseek");
+ goto out_alloc;
}
- if (is_floppy) {
- (void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY);
- } else {
- (void) fprintf(stdout, WRITE_PBOOT,
- partition, get_start_sector(dev_fd));
+ if (read(fd, stage2->file, stage2->file_size) < 0) {
+ perror(gettext("unable to read stage2"));
+ goto out_alloc;
}
-}
-static void check_extended_support(char *stage2)
-{
- char *cmp = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
+ (void) close(fd);
+ return (BC_SUCCESS);
- if ((*cmp++ != '\xEE') && memcmp(cmp, extended_sig, HASH_SIZE) != 0) {
- fprintf(stderr, "%s does not support extended versioning\n",
- stage2);
- do_version = 0;
- }
+out_alloc:
+ free(stage2->buf);
+ stage2->buf = NULL;
+out_fd:
+ (void) close(fd);
+out:
+ return (BC_ERROR);
}
-
-static void print_info()
+static int
+prepare_stage1(ig_data_t *install)
{
- int i;
+ ig_device_t *device = &install->device;
- if (strip) {
- fprintf(stdout, "%s\n", verstring);
- } else {
- fprintf(stdout, "Grub extended version information : %s\n",
- verstring);
- fprintf(stdout, "Grub stage2 (MD5) signature : ");
+ assert(install != NULL);
+
+ /* If PCFS add the BIOS Parameter Block. */
+ if (is_bootpar(device->type)) {
+ char bpb_sect[SECTOR_SIZE];
+
+ if (pread(device->part_fd, bpb_sect, SECTOR_SIZE, 0)
+ != SECTOR_SIZE) {
+ (void) fprintf(stderr, READ_FAIL_BPB);
+ return (BC_ERROR);
+ }
+ bcopy(bpb_sect + STAGE1_BPB_OFFSET,
+ install->stage1_buf + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
}
- for (i = 0; i < HASH_SIZE; i++)
- fprintf(stdout, "%02x", (unsigned char)signature[i]);
+ /* copy MBR to stage1 in case of overwriting MBR sector. */
+ bcopy(device->boot_sector + BOOTSZ, install->stage1_buf + BOOTSZ,
+ SECTOR_SIZE - BOOTSZ);
+ /* modify default stage1 file generated by GRUB. */
+ *((unsigned char *)(install->stage1_buf + STAGE1_FORCE_LBA)) = 1;
+ *((ulong_t *)(install->stage1_buf + STAGE1_STAGE2_SECTOR))
+ = install->stage2.first_sector;
+ *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_ADDRESS))
+ = STAGE2_MEMADDR;
+ *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_SEGMENT))
+ = STAGE2_MEMADDR >> 4;
- fprintf(stdout, "\n");
+ return (BC_SUCCESS);
}
+/*
+ * Grab stage1 from the specified device file descriptor.
+ */
static int
-read_stage2_info(int dev_fd)
+read_stage1_from_disk(int dev_fd, char *stage1_buf)
{
- int ret;
- int first_offset, second_offset;
- char *sign;
+ assert(stage1_buf != NULL);
- if (is_floppy || is_bootpar) {
-
- ret = pread(dev_fd, stage1_buffer, SECTOR_SIZE, 0);
- if (ret != SECTOR_SIZE) {
- perror("Error reading stage1 sector");
- return (1);
- }
+ if (read_in(dev_fd, stage1_buf, SECTOR_SIZE, 0) != BC_SUCCESS) {
+ perror(gettext("Unable to read stage1 from disk"));
+ return (BC_ERROR);
+ }
+ return (BC_SUCCESS);
+}
- first_offset = *((ulong_t *)(stage1_buffer +
- STAGE1_STAGE2_SECTOR));
+static int
+read_stage2_from_disk(int dev_fd, ig_stage2_t *stage2)
+{
+ uint32_t size;
+ uint32_t buf_size;
+ uint32_t mboot_off;
+ multiboot_header_t *mboot;
+
+ assert(stage2 != NULL);
+ assert(dev_fd != -1);
+
+ if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
+ STAGE2_BLKOFF * SECTOR_SIZE) != BC_SUCCESS) {
+ perror(gettext("Error reading stage2 sectors"));
+ return (BC_ERROR);
+ }
- /* Start reading in the first sector of stage 2 */
+ /* No multiboot means no chance of knowing stage2 size */
+ if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
+ != BC_SUCCESS) {
+ BOOT_DEBUG("Unable to find multiboot header\n");
+ return (BC_NOEXTRA);
+ }
+ mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
- ret = pread(dev_fd, stage2_buffer, SECTOR_SIZE, first_offset *
- SECTOR_SIZE);
- if (ret != SECTOR_SIZE) {
- perror("Error reading stage2 first sector");
- return (1);
- }
+ /*
+ * Unfilled mboot values mean an older version of installgrub installed
+ * the stage2. Again we have no chance of knowing stage2 size.
+ */
+ if (mboot->load_end_addr == 0 ||
+ mboot->load_end_addr < mboot->load_addr)
+ return (BC_NOEXTRA);
- /* From the block list section grab stage2 second sector */
+ /*
+ * Currently, the amount of space reserved for extra information
+ * is "fixed". We may have to scan for the terminating extra payload
+ * in the future.
+ */
+ size = mboot->load_end_addr - mboot->load_addr;
+ buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
- second_offset = *((ulong_t *)(stage2_buffer +
- STAGE2_BLOCKLIST));
+ stage2->buf = malloc(buf_size);
+ if (stage2->buf == NULL) {
+ perror(gettext("Memory allocation failed"));
+ return (BC_ERROR);
+ }
+ stage2->buf_size = buf_size;
- ret = pread(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
- second_offset * SECTOR_SIZE);
- if (ret != SECTOR_SIZE) {
- perror("Error reading stage2 second sector");
- return (1);
- }
- } else {
- ret = pread(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
- STAGE2_BLKOFF * SECTOR_SIZE);
- if (ret != 2 * SECTOR_SIZE) {
- perror("Error reading stage2 sectors");
- return (1);
- }
+ if (read_in(dev_fd, stage2->buf, buf_size, STAGE2_BLKOFF *
+ SECTOR_SIZE) != BC_SUCCESS) {
+ perror("read");
+ free(stage2->buf);
+ return (BC_ERROR);
}
- sign = stage2_buffer + STAGE2_SIGN_OFFSET - 1;
- if (*sign++ != '\xEE')
- return (1);
- (void) memcpy(signature, sign, HASH_SIZE);
- sign = stage2_buffer + STAGE2_PKG_VERSION;
- (void) strncpy(verstring, sign, VERSION_SIZE);
- return (0);
-}
+ /* Update pointers. */
+ stage2->file = stage2->buf;
+ stage2->file_size = size;
+ stage2->mboot_off = mboot_off;
+ stage2->mboot = (multiboot_header_t *)(stage2->buf + stage2->mboot_off);
+ stage2->extra = stage2->buf + P2ROUNDUP(stage2->file_size, 8);
+ return (BC_SUCCESS);
+}
-static int
-compute_and_write_md5hash(char *dest)
+static boolean_t
+is_update_necessary(ig_data_t *data, char *updt_str)
{
- struct stat sb;
- char *buffer;
+ bblk_einfo_t *einfo;
+ bblk_hs_t stage2_hs;
+ ig_stage2_t stage2_disk;
+ ig_stage2_t *stage2_file = &data->stage2;
+ ig_device_t *device = &data->device;
+ int dev_fd = device->part_fd;
+
+ assert(data != NULL);
+ assert(device->part_fd != -1);
+
+ bzero(&stage2_disk, sizeof (ig_stage2_t));
+
+ /* Gather stage2 (if present) from the target device. */
+ if (read_stage2_from_disk(dev_fd, &stage2_disk) != BC_SUCCESS) {
+ BOOT_DEBUG("Unable to read stage2 from %s\n", device->path);
+ BOOT_DEBUG("No multiboot wrapped stage2 on %s\n", device->path);
+ return (B_TRUE);
+ }
- if (fstat(stage2_fd, &sb) == -1)
- return (-1);
+ /*
+ * Look for the extended information structure in the extra payload
+ * area.
+ */
+ einfo = find_einfo(stage2_disk.extra);
+ if (einfo == NULL) {
+ BOOT_DEBUG("No extended information available\n");
+ return (B_TRUE);
+ }
- buffer = malloc(sb.st_size);
- if (buffer == NULL)
- return (-1);
+ if (!do_version || updt_str == NULL) {
+ (void) fprintf(stdout, "WARNING: target device %s has a "
+ "versioned stage2 that is going to be overwritten by a non "
+ "versioned one\n", device->path);
+ return (B_TRUE);
+ }
- if (lseek(stage2_fd, 0, SEEK_SET) == -1)
- return (-1);
- if (read(stage2_fd, buffer, sb.st_size) < 0)
- return (-1);
+ if (force_update) {
+ BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
+ return (B_TRUE);
+ }
- md5_calc(dest, buffer, sb.st_size);
- free(buffer);
- return (0);
+ /* Compare the two extended information structures. */
+ stage2_hs.src_buf = (unsigned char *)stage2_file->file;
+ stage2_hs.src_size = stage2_file->file_size;
+
+ return (einfo_should_update(einfo, &stage2_hs, updt_str));
}
@@ -687,198 +1281,230 @@ compute_and_write_md5hash(char *dest)
#define NUM_BLOCK(pos) (*(ushort_t *)((pos) + 4))
#define START_SEG(pos) (*(ushort_t *)((pos) + 6))
-static void
-modify_and_write_stage2(int dev_fd)
+static int
+prepare_stage2(ig_data_t *install, char *updt_str)
{
- int nrecord;
- off_t offset;
- char *dest;
-
- if (do_version) {
- dest = stage2_buffer + STAGE2_SIGN_OFFSET;
- if (compute_and_write_md5hash(dest) < 0)
- perror("MD5 operation");
- dest = stage2_buffer + STAGE2_PKG_VERSION;
- (void) strncpy(dest, verstring, VERSION_SIZE);
+ ig_device_t *device = &install->device;
+ ig_stage2_t *stage2 = &install->stage2;
+ uint32_t mboot_off = 0;
+
+ assert(install != NULL);
+ assert(stage2->file != NULL);
+
+ /* New stage2 files come with an embedded stage2. */
+ if (find_multiboot(stage2->file, stage2->file_size, &mboot_off)
+ != BC_SUCCESS) {
+ BOOT_DEBUG("WARNING: no multiboot structure found in stage2, "
+ "are you using an old GRUB stage2?\n");
+ if (do_version == B_TRUE) {
+ (void) fprintf(stderr, gettext("Versioning requested "
+ "but stage2 does not support it.. skipping.\n"));
+ do_version = B_FALSE;
+ }
+ } else {
+ /* Keep track of where the multiboot header is. */
+ stage2->mboot_off = mboot_off;
+ stage2->mboot = (multiboot_header_t *)(stage2->file +
+ mboot_off);
+ if (do_version) {
+ /*
+ * Adding stage2 information needs to happen before
+ * we modify the copy of stage2 we have in memory, so
+ * that the hashing reflects the one of the file.
+ * An error here is not fatal.
+ */
+ add_stage2_einfo(stage2, updt_str);
+ }
+ /*
+ * Fill multiboot information. We add them even without
+ * versioning to support as much as possible mirroring.
+ */
+ prepare_fake_multiboot(stage2);
}
- if (is_floppy || is_bootpar) {
- int i = 0;
- uint32_t partition_offset;
- uint32_t install_addr = 0x8200;
- uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST;
+ if (is_bootpar(device->type)) {
+ uint32_t blocklist[SECTOR_SIZE / sizeof (uint32_t)];
+ uint32_t install_addr = STAGE2_MEMADDR + SECTOR_SIZE;
+ int i = 0;
+ uchar_t *pos;
+
+ bzero(blocklist, sizeof (blocklist));
+ if (read_stage2_blocklist(device->part_fd, blocklist) != 0) {
+ (void) fprintf(stderr, gettext("Error reading pcfs "
+ "stage2 blocklist\n"));
+ return (BC_ERROR);
+ }
+
+ pos = (uchar_t *)stage2->file + STAGE2_BLOCKLIST;
+ stage2->first_sector = device->start_sector + blocklist[0];
+ stage2->pcfs_first_sectors[0] = blocklist[0];
+ BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
- stage2_first_sector = blocklist[0];
- /* figure out the second sector */
if (blocklist[1] > 1) {
blocklist[0]++;
blocklist[1]--;
} else {
i += 2;
}
- stage2_second_sector = blocklist[i];
- if (is_floppy)
- partition_offset = 0;
- else /* solaris boot partition */
- partition_offset = get_start_sector(dev_fd);
+ stage2->pcfs_first_sectors[1] = blocklist[i];
- /* install the blocklist at the end of stage2_buffer */
while (blocklist[i]) {
if (START_BLOCK(pos - 8) != 0 &&
START_BLOCK(pos - 8) != blocklist[i + 2]) {
(void) fprintf(stderr, PCFS_FRAGMENTED);
- exit(-1);
+ return (BC_ERROR);
}
- START_BLOCK(pos) = blocklist[i] + partition_offset;
+ START_BLOCK(pos) = blocklist[i] + device->start_sector;
START_SEG(pos) = (ushort_t)(install_addr >> 4);
NUM_BLOCK(pos) = blocklist[i + 1];
install_addr += blocklist[i + 1] * SECTOR_SIZE;
pos -= 8;
i += 2;
}
-
} else {
+ /* Solaris VTOC */
+ stage2->first_sector = device->start_sector + STAGE2_BLKOFF;
+ BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector);
/*
* In a solaris partition, stage2 is written to contiguous
* blocks. So we update the starting block only.
*/
- *((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) =
- stage2_first_sector + 1;
+ *((ulong_t *)(stage2->file + STAGE2_BLOCKLIST)) =
+ stage2->first_sector + 1;
}
- if (is_floppy) {
- /* modify the config file to add (fd0) */
- char *config_file = stage2_buffer + STAGE2_VER_STRING;
- while (*config_file++)
- ;
- strcpy(config_file, "(fd0)/boot/grub/menu.lst");
- } else {
- /* force lba and set disk partition */
- *((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1;
- *((long *)(stage2_buffer + STAGE2_INSTALLPART))
- = (partition << 16) | (slice << 8) | 0xff;
- }
-
- /* modification done, now do the writing */
- if (is_floppy || is_bootpar) {
- /* we rewrite block 0 and 1 and that's it */
- if (!nowrite &&
- (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
- stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE ||
- pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE,
- stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) {
- (void) fprintf(stderr, WRITE_FAIL_STAGE2);
- exit(-1);
- }
- (void) fprintf(stdout, WRITE_STAGE2_PCFS);
- return;
- }
-
- /* for disk, write stage2 starting at STAGE2_BLKOFF sector */
- offset = STAGE2_BLKOFF;
-
- /* write the modified first two sectors */
- if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE,
- offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) {
- (void) fprintf(stderr, WRITE_FAIL_STAGE2);
- exit(-1);
- }
-
- /* write the remaining sectors */
- nrecord = 2;
- offset += 2;
- for (;;) {
- int nread, nwrite;
- nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE,
- nrecord * SECTOR_SIZE);
- if (nread > 0 && !nowrite)
- nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE,
- offset * SECTOR_SIZE);
- else
- nwrite = SECTOR_SIZE;
- if (nread < 0 || nwrite != SECTOR_SIZE) {
- (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
- nread, nwrite);
- break;
- }
- if (nread > 0) {
- nrecord ++;
- offset ++;
- }
- if (nread < SECTOR_SIZE)
- break; /* end of file */
- }
- (void) fprintf(stdout, WRITE_STAGE2_DISK,
- partition, nrecord, STAGE2_BLKOFF, stage2_first_sector);
+ /* force lba and set disk partition */
+ *((unsigned char *) (stage2->file + STAGE2_FORCE_LBA)) = 1;
+ *((long *)(stage2->file + STAGE2_INSTALLPART))
+ = (device->partition << 16) | (device->slice << 8) | 0xff;
+
+ return (BC_SUCCESS);
}
-static char *
-get_raw_partition(char *device)
+static int
+find_x86_bootpar(struct mboot *mboot, int *part_num, uint32_t *start_sect)
{
- int len;
- struct mboot *mboot;
- static char *raw = NULL;
-
- if (raw)
- return (raw);
- raw = strdup(device);
+ int i;
- if (is_floppy)
- return (raw);
+ for (i = 0; i < FD_NUMPART; i++) {
+ struct ipart *part;
- if (is_bootpar) {
- int i;
- char *end = strstr(raw, "p0:boot");
+ part = (struct ipart *)mboot->parts + i;
+ if (part->systid == 0xbe) {
+ if (start_sect)
+ *start_sect = part->relsect;
+ if (part_num)
+ *part_num = i;
+ /* solaris boot part */
+ return (BC_SUCCESS);
+ }
+ }
+ return (BC_ERROR);
+}
- end[2] = 0; /* chop off :boot */
- read_boot_sect(raw);
- mboot = (struct mboot *)boot_sect;
- for (i = 0; i < FD_NUMPART; i++) {
- struct ipart *part = (struct ipart *)mboot->parts + i;
- if (part->systid == 0xbe) /* solaris boot part */
- break;
+static char *
+get_raw_partition_path(ig_device_t *device)
+{
+ char *raw;
+ int len;
+
+ if (is_bootpar(device->type)) {
+ int part;
+ struct mboot *mboot;
+
+ mboot = (struct mboot *)device->boot_sector;
+ if (find_x86_bootpar(mboot, &part, NULL) != BC_SUCCESS) {
+ (void) fprintf(stderr, BOOTPAR_NOTFOUND,
+ device->path_p0);
+ return (NULL);
}
- if (i == FD_NUMPART) {
- (void) fprintf(stderr, BOOTPAR_NOTFOUND, device);
- exit(-1);
+ raw = strdup(device->path_p0);
+ if (raw == NULL) {
+ perror(gettext("Memory allocation failed"));
+ return (NULL);
}
- end[1] = '1' + i; /* set partition name */
+
+ raw[strlen(raw) - 2] = '1' + part;
return (raw);
}
/* For disk, remember slice and return whole fdisk partition */
+ raw = strdup(device->path);
+ if (raw == NULL) {
+ perror(gettext("Memory allocation failed"));
+ return (NULL);
+ }
+
len = strlen(raw);
if (raw[len - 2] != 's' || raw[len - 1] == '2') {
(void) fprintf(stderr, NOT_ROOT_SLICE);
- exit(-1);
+ free(raw);
+ return (NULL);
}
- slice = atoi(&raw[len - 1]);
+ device->slice = atoi(&raw[len - 1]);
raw[len - 2] = 's';
raw[len - 1] = '2';
+
return (raw);
}
+static int
+get_raw_partition_fd(ig_device_t *device)
+{
+ struct stat stat = {0};
+ char *raw;
+
+ raw = get_raw_partition_path(device);
+ if (raw == NULL)
+ return (BC_ERROR);
+
+ if (nowrite)
+ device->part_fd = open(raw, O_RDONLY);
+ else
+ device->part_fd = open(raw, O_RDWR);
+
+ if (device->part_fd < 0 || fstat(device->part_fd, &stat) != 0) {
+ (void) fprintf(stderr, OPEN_FAIL, raw);
+ free(raw);
+ return (BC_ERROR);
+ }
+
+ if (S_ISCHR(stat.st_mode) == 0) {
+ (void) fprintf(stderr, NOT_RAW_DEVICE, raw);
+ (void) close(device->part_fd);
+ device->part_fd = -1;
+ free(raw);
+ return (BC_ERROR);
+ }
+
+ free(raw);
+ return (BC_SUCCESS);
+}
+
#define TMP_MNTPT "/tmp/installgrub_pcfs"
-static void
-copy_stage2(int dev_fd, char *device)
+static int
+copy_stage2_to_pcfs(ig_data_t *install)
{
- FILE *mntfp;
- int i, pcfs_fp;
- char buf[SECTOR_SIZE];
- char *cp;
- struct mnttab mp = {0}, mpref = {0};
+ FILE *mntfp;
+ int pcfs_fp;
+ int status = BC_ERROR;
+ char buf[SECTOR_SIZE];
+ char *cp;
+ struct mnttab mp = {0}, mpref = {0};
+ ig_device_t *device = &install->device;
+ ig_stage2_t *stage2 = &install->stage2;
/* convert raw to block device name by removing the first 'r' */
- (void) strncpy(buf, device, sizeof (buf));
+ (void) strncpy(buf, device->path, sizeof (buf));
buf[sizeof (buf) - 1] = 0;
cp = strchr(buf, 'r');
if (cp == NULL) {
- (void) fprintf(stderr, CONVERT_FAIL, device);
- exit(-1);
+ (void) fprintf(stderr, CONVERT_FAIL, device->path);
+ return (BC_ERROR);
}
do {
*cp = *(cp + 1);
@@ -888,7 +1514,7 @@ copy_stage2(int dev_fd, char *device)
mntfp = fopen("/etc/mnttab", "r");
if (mntfp == NULL) {
(void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab");
- exit(-1);
+ return (BC_ERROR);
}
mpref.mnt_special = buf;
@@ -904,7 +1530,7 @@ copy_stage2(int dev_fd, char *device)
bzero(&mp, sizeof (mp));
if (getmntany(mntfp, &mp, &mpref) != 0) {
(void) fprintf(stderr, MOUNT_FAIL, buf);
- exit(-1);
+ return (BC_ERROR);
}
}
@@ -919,33 +1545,21 @@ copy_stage2(int dev_fd, char *device)
if (pcfs_fp == -1) {
(void) fprintf(stderr, OPEN_FAIL_FILE, buf);
perror("open:");
- (void) umount(TMP_MNTPT);
- exit(-1);
- }
-
- /* write stage2 to pcfs */
- for (i = 0; ; i++) {
- int nread, nwrite;
- nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE);
- if (nowrite)
- nwrite = nread;
- else
- nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE);
- if (nread < 0 || nwrite != nread) {
- (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS,
- nread, nwrite);
- break;
- }
- if (nread < SECTOR_SIZE)
- break; /* end of file */
+ goto out;
+ }
+
+ /* write stage2 to the pcfs mounted filesystem. */
+ if (write(pcfs_fp, stage2->file, stage2->file_size)
+ != stage2->file_size) {
+ perror(gettext("Error writing stage2"));
+ goto out;
}
+
+ status = BC_SUCCESS;
+out_fd:
(void) close(pcfs_fp);
+out:
(void) umount(TMP_MNTPT);
-
- /*
- * Now, get the blocklist from the device.
- */
- bzero(blocklist, sizeof (blocklist));
- if (read_stage2_blocklist(dev_fd, blocklist) != 0)
- exit(-1);
+ (void) rmdir(TMP_MNTPT);
+ return (status);
}