summaryrefslogtreecommitdiff
path: root/usr/src/cmd/boot/installboot/installboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/boot/installboot/installboot.c')
-rw-r--r--usr/src/cmd/boot/installboot/installboot.c949
1 files changed, 949 insertions, 0 deletions
diff --git a/usr/src/cmd/boot/installboot/installboot.c b/usr/src/cmd/boot/installboot/installboot.c
new file mode 100644
index 0000000000..cab6670608
--- /dev/null
+++ b/usr/src/cmd/boot/installboot/installboot.c
@@ -0,0 +1,949 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <locale.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/multiboot.h>
+#include <sys/sysmacros.h>
+
+#include "installboot.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
+
+/*
+ * SPARC bootblock installation:
+ *
+ * The bootblock resides in blocks 1 to 15 (disk label is at block 0).
+ * The ZFS boot block is larger than what will fit into these first 7.5K so we
+ * break it up and write the remaining portion into the ZFS provided boot block
+ * region at offset 512K. If versioning is requested, we add a multiboot
+ * header at the end of the bootblock, followed by the extra payload area and
+ * place the extended information structure within the latter.
+ */
+
+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;
+
+static char *update_str;
+static int tgt_fs_type = TARGET_IS_UFS;
+char mboot_scan[MBOOT_SCAN_SIZE];
+
+/* Function prototypes. */
+static int read_bootblock_from_file(char *, ib_data_t *data);
+static int read_bootblock_from_disk(int, ib_bootblock_t *);
+static void add_bootblock_einfo(ib_bootblock_t *, char *);
+static int prepare_bootblock(ib_data_t *, char *);
+static int write_zfs_bootblock(ib_data_t *);
+static int write_bootblock(ib_data_t *);
+static int open_device(ib_device_t *);
+static int init_device(ib_device_t *, char *);
+static void cleanup_device(ib_device_t *);
+static int commit_to_disk(ib_data_t *, char *);
+static int handle_install(char *, char **);
+static int handle_getinfo(char *, char **);
+static int handle_mirror(char *, char **);
+static boolean_t is_update_necessary(ib_data_t *, char *);
+static int propagate_bootblock(ib_data_t *, ib_data_t *, char *);
+static void usage(char *);
+
+static int
+read_bootblock_from_file(char *file, ib_data_t *data)
+{
+ ib_device_t *device = &data->device;
+ ib_bootblock_t *bblock = &data->bootblock;
+ struct stat sb;
+ uint32_t buf_size;
+ int fd = -1;
+ int retval = BC_ERROR;
+
+ assert(data != NULL);
+ assert(file != NULL);
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ BOOT_DEBUG("Error opening %s\n", file);
+ perror("open");
+ goto out;
+ }
+
+ if (fstat(fd, &sb) == -1) {
+ BOOT_DEBUG("Error getting information (stat) about %s", file);
+ perror("stat");
+ goto outfd;
+ }
+
+ bblock->file_size = sb.st_size;
+ BOOT_DEBUG("bootblock file size is %x\n", bblock->file_size);
+
+ /* UFS and HSFS bootblocks need to fit in the reserved 7.5K. */
+ if (!is_zfs(device->type)) {
+ buf_size = P2ROUNDUP(bblock->file_size, SECTOR_SIZE);
+ if (buf_size > BBLK_DATA_RSVD_SIZE) {
+ BOOT_DEBUG("boot block size is bigger than allowed\n");
+ goto outfd;
+ }
+ } else {
+ buf_size = P2ROUNDUP(bblock->file_size + SECTOR_SIZE,
+ SECTOR_SIZE);
+ if (buf_size > BBLK_DATA_RSVD_SIZE + MBOOT_SCAN_SIZE) {
+ (void) fprintf(stderr, gettext("WARNING, bootblock size"
+ " does not allow to place extended versioning "
+ "information.. skipping\n"));
+ do_version = B_FALSE;
+ }
+ }
+
+ bblock->buf_size = buf_size;
+ BOOT_DEBUG("bootblock in-memory buffer size is %x\n",
+ bblock->buf_size);
+
+ bblock->buf = malloc(buf_size);
+ if (bblock->buf == NULL) {
+ perror(gettext("Memory allocation failure"));
+ goto outbuf;
+ }
+ bblock->file = bblock->buf;
+
+ if (read(fd, bblock->file, bblock->file_size) != bblock->file_size) {
+ BOOT_DEBUG("Read from %s failed\n", file);
+ perror("read");
+ goto outfd;
+ }
+
+ /* If not on ZFS, we are done here. */
+ if (!is_zfs(device->type)) {
+ BOOT_DEBUG("Reading of the bootblock done\n");
+ retval = BC_SUCCESS;
+ goto outfd;
+ }
+ /*
+ * We place the multiboot header right after the file, followed by
+ * the extended information structure.
+ */
+ bblock->mboot = (multiboot_header_t *)(bblock->file +
+ P2ROUNDUP(bblock->file_size, 8));
+ bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
+ BOOT_DEBUG("mboot at %p, extra at %p, buf=%p (size=%d)\n",
+ bblock->mboot, bblock->extra, bblock->buf, bblock->buf_size);
+
+ (void) close(fd);
+ return (BC_SUCCESS);
+
+outbuf:
+ (void) free(bblock->buf);
+ bblock->buf = NULL;
+outfd:
+ (void) close(fd);
+out:
+ return (retval);
+}
+
+static int
+read_bootblock_from_disk(int dev_fd, ib_bootblock_t *bblock)
+{
+ char *dest;
+ uint32_t size;
+ uint32_t buf_size;
+ uint32_t mboot_off;
+ multiboot_header_t *mboot;
+
+ assert(bblock != NULL);
+ assert(dev_fd != -1);
+
+ /*
+ * The ZFS bootblock is divided in two parts, but the fake multiboot
+ * header can only be in the second part (the one contained in the ZFS
+ * reserved area).
+ */
+ if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
+ BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
+ BOOT_DEBUG("Error reading ZFS reserved area\n");
+ perror("read");
+ return (BC_ERROR);
+ }
+
+ /* No multiboot means no chance of knowing bootblock 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);
+
+ /*
+ * 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);
+ bblock->file_size = size;
+
+ bblock->buf = malloc(buf_size);
+ if (bblock->buf == NULL) {
+ BOOT_DEBUG("Unable to allocate enough memory to read"
+ " the extra bootblock from the disk\n");
+ perror(gettext("Memory allocation failure"));
+ return (BC_ERROR);
+ }
+ bblock->buf_size = buf_size;
+
+ dest = bblock->buf;
+ size = BBLK_DATA_RSVD_SIZE;
+
+ if (read_in(dev_fd, dest, size, SECTOR_SIZE) != BC_SUCCESS) {
+ BOOT_DEBUG("Error reading first %d bytes of the bootblock\n",
+ size);
+ (void) free(bblock->buf);
+ bblock->buf = NULL;
+ return (BC_ERROR);
+ }
+
+ dest += BBLK_DATA_RSVD_SIZE;
+ size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
+
+ if (read_in(dev_fd, dest, size, BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
+ BOOT_DEBUG("Error reading ZFS reserved area the second time\n");
+ (void) free(bblock->buf);
+ bblock->buf = NULL;
+ return (BC_ERROR);
+ }
+
+ /* Update pointers. */
+ bblock->file = bblock->buf;
+ bblock->mboot_off = mboot_off;
+ bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off
+ + BBLK_DATA_RSVD_SIZE);
+ bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
+ return (BC_SUCCESS);
+}
+
+static boolean_t
+is_update_necessary(ib_data_t *data, char *updt_str)
+{
+ bblk_einfo_t *einfo;
+ bblk_hs_t bblock_hs;
+ ib_bootblock_t bblock_disk;
+ ib_bootblock_t *bblock_file = &data->bootblock;
+ ib_device_t *device = &data->device;
+ int dev_fd = device->fd;
+
+ assert(data != NULL);
+ assert(device->fd != -1);
+
+ /* Nothing to do if we are not updating a ZFS bootblock. */
+ if (!is_zfs(device->type))
+ return (B_TRUE);
+
+ bzero(&bblock_disk, sizeof (ib_bootblock_t));
+
+ if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) {
+ BOOT_DEBUG("Unable to read bootblock from %s\n", device->path);
+ return (B_TRUE);
+ }
+
+ einfo = find_einfo(bblock_disk.extra);
+ if (einfo == NULL) {
+ BOOT_DEBUG("No extended information available\n");
+ return (B_TRUE);
+ }
+
+ if (!do_version || updt_str == NULL) {
+ (void) fprintf(stdout, "WARNING: target device %s has a "
+ "versioned bootblock that is going to be overwritten by a "
+ "non versioned one\n", device->path);
+ return (B_TRUE);
+ }
+
+ if (force_update) {
+ BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
+ return (B_TRUE);
+ }
+
+ BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
+
+ bblock_hs.src_buf = (unsigned char *)bblock_file->file;
+ bblock_hs.src_size = bblock_file->file_size;
+
+ return (einfo_should_update(einfo, &bblock_hs, updt_str));
+}
+
+static void
+add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
+{
+ bblk_hs_t hs;
+ uint32_t avail_space;
+
+ assert(bblock != NULL);
+
+ if (updt_str == NULL) {
+ BOOT_DEBUG("WARNING: no update string passed to "
+ "add_bootblock_einfo()\n");
+ return;
+ }
+
+ /* Fill bootblock hashing source information. */
+ hs.src_buf = (unsigned char *)bblock->file;
+ hs.src_size = bblock->file_size;
+ /* How much space for the extended information structure? */
+ avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
+ /* Place the extended information structure. */
+ add_einfo(bblock->extra, updt_str, &hs, avail_space);
+}
+
+
+static int
+prepare_bootblock(ib_data_t *data, char *updt_str)
+{
+ ib_device_t *device = &data->device;
+ ib_bootblock_t *bblock = &data->bootblock;
+ multiboot_header_t *mboot;
+
+ assert(data != NULL);
+
+ /* Nothing to do if we are not on ZFS. */
+ if (!is_zfs(device->type))
+ return (BC_SUCCESS);
+
+ /*
+ * Write the fake multiboot structure followed by the extra information
+ * data. Both mboot and extra pointers have already been filled up to
+ * point to the right location in the buffer. We prepare the fake
+ * multiboot regardless if versioning was requested or not because
+ * we need it for mirroring support.
+ */
+ assert(bblock->mboot != NULL);
+ assert(bblock->extra != NULL);
+
+ mboot = bblock->mboot;
+
+ mboot->magic = MB_HEADER_MAGIC;
+ mboot->flags = MB_HEADER_FLAGS_64;
+ mboot->checksum = -(mboot->flags + mboot->magic);
+ /*
+ * Flags include the AOUT_KLUDGE and we use the extra members to specify
+ * the size of the bootblock.
+ */
+ mboot->header_addr = bblock->mboot_off;
+ mboot->load_addr = 0;
+ mboot->load_end_addr = bblock->file_size;
+
+ /*
+ * Now that we have the mboot header in place, we can add the extended
+ * versioning information. Since the multiboot header has been placed
+ * after the file image, the hashing will still reflect the one of the
+ * file on the disk.
+ */
+ if (do_version)
+ add_bootblock_einfo(bblock, updt_str);
+
+ return (BC_SUCCESS);
+}
+
+static int
+write_zfs_bootblock(ib_data_t *data)
+{
+ ib_device_t *device = &data->device;
+ ib_bootblock_t *bblock = &data->bootblock;
+ char *bufptr;
+ uint32_t size;
+
+ assert(data != NULL);
+ assert(device->fd != -1);
+
+ /*
+ * In the ZFS case we actually perform two different steps:
+ * - write the first 15 blocks of the bootblock to the reserved disk
+ * blocks.
+ * - write the remaining blocks in the ZFS reserved area at offset
+ * 512K.
+ */
+ bufptr = bblock->buf;
+ size = BBLK_DATA_RSVD_SIZE;
+
+ if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) {
+ BOOT_DEBUG("Error writing first 15 blocks of %s\n",
+ device->path);
+ perror("write");
+ return (BC_ERROR);
+ }
+
+ bufptr += BBLK_DATA_RSVD_SIZE;
+ size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
+
+ if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF)
+ != BC_SUCCESS) {
+ BOOT_DEBUG("Error writing the second part of ZFS bootblock "
+ "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF);
+ return (BC_ERROR);
+ }
+ return (BC_SUCCESS);
+}
+
+static int
+write_bootblock(ib_data_t *data)
+{
+ ib_device_t *device = &data->device;
+ ib_bootblock_t *bblock = &data->bootblock;
+ int ret;
+
+ assert(data != NULL);
+
+ /*
+ * If we are on UFS or HSFS we simply write out to the reserved
+ * blocks (1 to 15) the boot block.
+ */
+ if (!is_zfs(device->type)) {
+ if (write_out(device->fd, bblock->buf, bblock->buf_size,
+ SECTOR_SIZE) != BC_SUCCESS) {
+ BOOT_DEBUG("Error writing bootblock to %s\n",
+ device->path);
+ return (BC_ERROR);
+ } else {
+ return (BC_SUCCESS);
+ }
+ } else {
+ ret = write_zfs_bootblock(data);
+ return (ret);
+ }
+}
+
+static int
+open_device(ib_device_t *device)
+{
+ struct stat statbuf;
+
+ device->fd = open(device->path, O_RDWR);
+ if (device->fd == -1) {
+ BOOT_DEBUG("Unable to open %s\n", device->path);
+ perror("open");
+ return (BC_ERROR);
+ }
+
+ if (fstat(device->fd, &statbuf) != 0) {
+ BOOT_DEBUG("Unable to stat %s\n", device->path);
+ perror("stat");
+ (void) close(device->fd);
+ return (BC_ERROR);
+ }
+
+ if (S_ISCHR(statbuf.st_mode) == 0) {
+ (void) fprintf(stderr, gettext("%s: Not a character device\n"),
+ device->path);
+ return (BC_ERROR);
+ }
+
+ return (BC_SUCCESS);
+}
+
+static int
+init_device(ib_device_t *device, char *path)
+{
+ bzero(device, sizeof (*device));
+ device->fd = -1;
+
+ device->path = strdup(path);
+ if (path == NULL) {
+ perror(gettext("Memory allocation failure"));
+ return (BC_ERROR);
+ }
+
+ device->type = tgt_fs_type;
+ if (open_device(device) != BC_SUCCESS)
+ return (BC_ERROR);
+
+ return (BC_SUCCESS);
+}
+
+static void
+cleanup_device(ib_device_t *device)
+{
+ free(device->path);
+ bzero(device, sizeof (*device));
+
+ if (device->fd != -1)
+ (void) close(device->fd);
+}
+
+static void
+cleanup_bootblock(ib_bootblock_t *bblock)
+{
+ free(bblock->buf);
+ bzero(bblock, sizeof (ib_bootblock_t));
+}
+
+/*
+ * 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(ib_data_t *src, ib_data_t *dest, char *updt_str)
+{
+ ib_bootblock_t *src_bblock = &src->bootblock;
+ ib_bootblock_t *dest_bblock = &dest->bootblock;
+ uint32_t buf_size;
+
+ assert(src != NULL);
+ assert(dest != NULL);
+
+ cleanup_bootblock(dest_bblock);
+
+ if (updt_str != NULL) {
+ do_version = B_TRUE;
+ } else {
+ do_version = B_FALSE;
+ }
+
+ buf_size = src_bblock->file_size + SECTOR_SIZE;
+
+ dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
+ dest_bblock->buf = malloc(dest_bblock->buf_size);
+ if (dest_bblock->buf == NULL) {
+ perror(gettext("Memory Allocation Failure"));
+ return (BC_ERROR);
+ }
+ dest_bblock->file = dest_bblock->buf;
+ dest_bblock->file_size = src_bblock->file_size;
+ (void) memcpy(dest_bblock->file, src_bblock->file,
+ dest_bblock->file_size);
+
+ dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
+ P2ROUNDUP(dest_bblock->file_size, 8));
+ dest_bblock->extra = (char *)dest_bblock->mboot +
+ sizeof (multiboot_header_t);
+
+ (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
+ src->device.path, dest->device.path);
+
+ return (commit_to_disk(dest, updt_str));
+}
+
+static int
+commit_to_disk(ib_data_t *data, char *update_str)
+{
+ assert(data != NULL);
+
+ if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error updating the bootblock "
+ "image\n"));
+ return (BC_ERROR);
+ }
+
+ if (write_bootblock(data) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error writing bootblock to "
+ "disk\n"));
+ return (BC_ERROR);
+ }
+
+ return (BC_SUCCESS);
+}
+
+
+/*
+ * Install a new bootblock on the given device. handle_install() expects argv
+ * to contain 2 parameters (the target device path and the path to the
+ * bootblock.
+ *
+ * Returns: BC_SUCCESS - if the installation is successful
+ * BC_ERROR - if the installation failed
+ * BC_NOUPDT - if no installation was performed because the
+ * version currently installed is more recent than the
+ * supplied one.
+ *
+ */
+static int
+handle_install(char *progname, char **argv)
+{
+ ib_data_t install_data;
+ char *bootblock = NULL;
+ char *device_path = NULL;
+ int ret = BC_ERROR;
+
+ bootblock = strdup(argv[0]);
+ device_path = strdup(argv[1]);
+
+ if (!device_path || !bootblock) {
+ (void) fprintf(stderr, gettext("Missing parameter"));
+ usage(progname);
+ goto out;
+ }
+
+ BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path,
+ bootblock);
+ bzero(&install_data, sizeof (ib_data_t));
+
+ if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Unable to open device %s\n"),
+ device_path);
+ goto out;
+ }
+
+ if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Error reading %s\n"),
+ bootblock);
+ goto out_dev;
+ }
+ /* Versioning is only supported for the ZFS bootblock. */
+ if (do_version && !is_zfs(install_data.device.type)) {
+ (void) fprintf(stderr, gettext("Versioning is only supported on"
+ " ZFS... skipping.\n"));
+ do_version = B_FALSE;
+ }
+
+ /*
+ * 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("bootblock 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;
+ }
+
+ 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(bootblock);
+ free(device_path);
+ return (ret);
+}
+
+/*
+ * Retrieves from a device the extended information (einfo) associated to the
+ * installed bootblock.
+ * 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)
+{
+
+ ib_data_t data;
+ ib_bootblock_t *bblock = &data.bootblock;
+ ib_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 (ib_data_t));
+ BOOT_DEBUG("device path: %s\n", device_path);
+
+ if (init_device(device, device_path) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Unable to gather device "
+ "information from %s\n"), device_path);
+ goto out_dev;
+ }
+
+ if (!is_zfs(device->type)) {
+ (void) fprintf(stderr, gettext("Versioning only supported on "
+ "ZFS\n"));
+ goto out_dev;
+ }
+
+ ret = read_bootblock_from_disk(device->fd, bblock);
+ if (ret == BC_ERROR) {
+ (void) fprintf(stderr, gettext("Error reading bootblock from "
+ "%s\n"), device_path);
+ goto out_dev;
+ }
+
+ if (ret == BC_NOEXTRA) {
+ BOOT_DEBUG("No multiboot header found on %s, unable "
+ "to locate extra information area (old/non versioned "
+ "bootblock?) \n", device_path);
+ (void) fprintf(stderr, gettext("No extended information "
+ "found\n"));
+ retval = BC_NOEINFO;
+ goto out_dev;
+ }
+
+ einfo = find_einfo(bblock->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 = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) -
+ sizeof (multiboot_header_t);
+ print_einfo(flags, einfo, size);
+ retval = BC_SUCCESS;
+
+out_dev:
+ cleanup_device(&data.device);
+out:
+ free(device_path);
+ return (retval);
+
+}
+
+/*
+ * Attempt to mirror (propagate) the current bootblock 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)
+{
+ ib_data_t curr_data;
+ ib_data_t attach_data;
+ ib_device_t *curr_device = &curr_data.device;
+ ib_device_t *attach_device = &attach_data.device;
+ ib_bootblock_t *bblock_curr = &curr_data.bootblock;
+ ib_bootblock_t *bblock_attach = &attach_data.bootblock;
+ 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 (ib_data_t));
+ bzero(&attach_data, sizeof (ib_data_t));
+
+ if (tgt_fs_type != TARGET_IS_ZFS) {
+ (void) fprintf(stderr, gettext("Mirroring is only supported on "
+ "ZFS\n"));
+ return (BC_ERROR);
+ }
+
+ if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
+ (void) fprintf(stderr, gettext("Unable to gather device "
+ "information from %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 from %s (attaching device)\n"),
+ attach_device_path);
+ goto out_devs;
+ }
+
+ ret = read_bootblock_from_disk(curr_device->fd, bblock_curr);
+ if (ret == BC_ERROR) {
+ BOOT_DEBUG("Error reading bootblock 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 retrieve"
+ " the bootblock\n", curr_device->path);
+ retval = BC_NOEXTRA;
+ goto out_devs;
+ }
+
+ einfo_curr = find_einfo(bblock_curr->extra);
+ if (einfo_curr != NULL)
+ updt_str = einfo_get_string(einfo_curr);
+
+ retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
+ cleanup_bootblock(bblock_curr);
+ cleanup_bootblock(bblock_attach);
+out_devs:
+ cleanup_device(attach_device);
+out_currdev:
+ cleanup_device(curr_device);
+out:
+ free(curr_device_path);
+ free(attach_device_path);
+ return (retval);
+}
+
+#define USAGE_STRING "Usage: %s [-h|-f|-F fstype|-u verstr] bootblk " \
+ "raw-device\n" \
+ "\t%s [-e|-V] -i -F zfs raw-device\n" \
+ "\t%s -M -F zfs raw-device attach-raw-device\n" \
+ "\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n"
+
+#define CANON_USAGE_STR gettext(USAGE_STRING)
+
+static void
+usage(char *progname)
+{
+ (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
+}
+
+int
+main(int argc, char **argv)
+{
+ int opt;
+ int params = 2;
+ int ret;
+ char *progname;
+ char **handle_args;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) {
+ switch (opt) {
+ case 'F':
+ if (strcmp(optarg, "ufs") == 0) {
+ tgt_fs_type = TARGET_IS_UFS;
+ } else if (strcmp(optarg, "hsfs") == 0) {
+ tgt_fs_type = TARGET_IS_HSFS;
+ } else if (strcmp(optarg, "zfs") == 0) {
+ tgt_fs_type = TARGET_IS_ZFS;
+ } else {
+ (void) fprintf(stderr, gettext("Wrong "
+ "filesystem specified\n\n"));
+ usage(argv[0]);
+ exit(BC_ERROR);
+ }
+ break;
+ case 'e':
+ strip = B_TRUE;
+ break;
+ case 'f':
+ force_update = B_TRUE;
+ break;
+ case 'V':
+ verbose_dump = B_TRUE;
+ break;
+ case 'i':
+ do_getinfo = B_TRUE;
+ params = 1;
+ break;
+ case 'u':
+ do_version = B_TRUE;
+
+ update_str = malloc(strlen(optarg) + 1);
+ if (update_str == NULL) {
+ perror(gettext("Memory allocation failure"));
+ exit(BC_ERROR);
+ }
+ (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
+ break;
+ case 'M':
+ do_mirror_bblk = B_TRUE;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(BC_SUCCESS);
+ break;
+ case 'd':
+ boot_debug = B_TRUE;
+ break;
+ case 'n':
+ nowrite = B_TRUE;
+ break;
+ default:
+ /* fall through to process non-optional args */
+ break;
+ }
+ }
+
+ /* check arguments */
+ if (argc != optind + params) {
+ usage(argv[0]);
+ exit(BC_ERROR);
+ }
+ progname = argv[0];
+ handle_args = argv + optind;
+
+ /* check options. */
+ 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 (nowrite)
+ (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
+ " be written to disk.\n"));
+
+ 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);
+}