diff options
Diffstat (limited to 'usr/src/cmd/boot/installboot/installboot.c')
-rw-r--r-- | usr/src/cmd/boot/installboot/installboot.c | 949 |
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); +} |