1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <stdio.h>
  26 #include <errno.h>
  27 #include <unistd.h>
  28 #include <fcntl.h>
  29 #include <assert.h>
  30 #include <locale.h>
  31 #include <strings.h>
  32 #include <sys/types.h>
  33 #include <sys/stat.h>
  34 #include <sys/multiboot.h>
  35 #include <sys/sysmacros.h>
  36 
  37 #include "installboot.h"
  38 #include "./../common/bblk_einfo.h"
  39 #include "./../common/boot_utils.h"
  40 #include "./../common/mboot_extra.h"
  41 
  42 #ifndef TEXT_DOMAIN
  43 #define TEXT_DOMAIN     "SUNW_OST_OSCMD"
  44 #endif
  45 
  46 /*
  47  * SPARC bootblock installation:
  48  *
  49  * The bootblock resides in blocks 1 to 15 (disk label is at block 0).
  50  * The ZFS boot block is larger than what will fit into these first 7.5K so we
  51  * break it up and write the remaining portion into the ZFS provided boot block
  52  * region at offset 512K. If versioning is requested, we add a multiboot
  53  * header at the end of the bootblock, followed by the extra payload area and
  54  * place the extended information structure within the latter.
  55  */
  56 
  57 static boolean_t        force_update = B_FALSE;
  58 static boolean_t        do_getinfo = B_FALSE;
  59 static boolean_t        do_version = B_FALSE;
  60 static boolean_t        do_mirror_bblk = B_FALSE;
  61 static boolean_t        strip = B_FALSE;
  62 static boolean_t        verbose_dump = B_FALSE;
  63 
  64 static char             *update_str;
  65 static int              tgt_fs_type = TARGET_IS_UFS;
  66 char                    mboot_scan[MBOOT_SCAN_SIZE];
  67 
  68 /* Function prototypes. */
  69 static int read_bootblock_from_file(char *, ib_data_t *data);
  70 static int read_bootblock_from_disk(int, ib_bootblock_t *);
  71 static void add_bootblock_einfo(ib_bootblock_t *, char *);
  72 static int prepare_bootblock(ib_data_t *, char *);
  73 static int write_zfs_bootblock(ib_data_t *);
  74 static int write_bootblock(ib_data_t *);
  75 static int open_device(ib_device_t *);
  76 static int init_device(ib_device_t *, char *);
  77 static void cleanup_device(ib_device_t *);
  78 static int commit_to_disk(ib_data_t *, char *);
  79 static int handle_install(char *, char **);
  80 static int handle_getinfo(char *, char **);
  81 static int handle_mirror(char *, char **);
  82 static boolean_t is_update_necessary(ib_data_t *, char *);
  83 static int propagate_bootblock(ib_data_t *, ib_data_t *, char *);
  84 static void usage(char *);
  85 
  86 static int
  87 read_bootblock_from_file(char *file, ib_data_t *data)
  88 {
  89         ib_device_t     *device = &data->device;
  90         ib_bootblock_t  *bblock = &data->bootblock;
  91         struct stat     sb;
  92         uint32_t        buf_size;
  93         int             fd = -1;
  94         int             retval = BC_ERROR;
  95 
  96         assert(data != NULL);
  97         assert(file != NULL);
  98 
  99         fd = open(file, O_RDONLY);
 100         if (fd == -1) {
 101                 BOOT_DEBUG("Error opening %s\n", file);
 102                 perror("open");
 103                 goto out;
 104         }
 105 
 106         if (fstat(fd, &sb) == -1) {
 107                 BOOT_DEBUG("Error getting information (stat) about %s", file);
 108                 perror("stat");
 109                 goto outfd;
 110         }
 111 
 112         bblock->file_size = sb.st_size;
 113         BOOT_DEBUG("bootblock file size is %x\n", bblock->file_size);
 114 
 115         /* UFS and HSFS bootblocks need to fit in the reserved 7.5K. */
 116         if (!is_zfs(device->type)) {
 117                 buf_size = P2ROUNDUP(bblock->file_size, SECTOR_SIZE);
 118                 if (buf_size > BBLK_DATA_RSVD_SIZE) {
 119                         BOOT_DEBUG("boot block size is bigger than allowed\n");
 120                         goto outfd;
 121                 }
 122         } else {
 123                 buf_size = P2ROUNDUP(bblock->file_size + SECTOR_SIZE,
 124                     SECTOR_SIZE);
 125                 if (buf_size > BBLK_DATA_RSVD_SIZE + MBOOT_SCAN_SIZE) {
 126                         (void) fprintf(stderr, gettext("WARNING, bootblock size"
 127                             " does not allow to place extended versioning "
 128                             "information.. skipping\n"));
 129                         do_version = B_FALSE;
 130                 }
 131         }
 132 
 133         bblock->buf_size = buf_size;
 134         BOOT_DEBUG("bootblock in-memory buffer size is %x\n",
 135             bblock->buf_size);
 136 
 137         bblock->buf = malloc(buf_size);
 138         if (bblock->buf == NULL) {
 139                 perror(gettext("Memory allocation failure"));
 140                 goto outbuf;
 141         }
 142         bblock->file = bblock->buf;
 143 
 144         if (read(fd, bblock->file, bblock->file_size) != bblock->file_size) {
 145                 BOOT_DEBUG("Read from %s failed\n", file);
 146                 perror("read");
 147                 goto outfd;
 148         }
 149 
 150         /* If not on ZFS, we are done here. */
 151         if (!is_zfs(device->type)) {
 152                 BOOT_DEBUG("Reading of the bootblock done\n");
 153                 retval = BC_SUCCESS;
 154                 goto outfd;
 155         }
 156         /*
 157          * We place the multiboot header right after the file, followed by
 158          * the extended information structure.
 159          */
 160         bblock->mboot = (multiboot_header_t *)(bblock->file +
 161             P2ROUNDUP(bblock->file_size, 8));
 162         bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
 163         BOOT_DEBUG("mboot at %p, extra at %p, buf=%p (size=%d)\n",
 164             bblock->mboot, bblock->extra, bblock->buf, bblock->buf_size);
 165 
 166         (void) close(fd);
 167         return (BC_SUCCESS);
 168 
 169 outbuf:
 170         (void) free(bblock->buf);
 171         bblock->buf = NULL;
 172 outfd:
 173         (void) close(fd);
 174 out:
 175         return (retval);
 176 }
 177 
 178 static int
 179 read_bootblock_from_disk(int dev_fd, ib_bootblock_t *bblock)
 180 {
 181         char                    *dest;
 182         uint32_t                size;
 183         uint32_t                buf_size;
 184         uint32_t                mboot_off;
 185         multiboot_header_t      *mboot;
 186 
 187         assert(bblock != NULL);
 188         assert(dev_fd != -1);
 189 
 190         /*
 191          * The ZFS bootblock is divided in two parts, but the fake multiboot
 192          * header can only be in the second part (the one contained in the ZFS
 193          * reserved area).
 194          */
 195         if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
 196             BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
 197                 BOOT_DEBUG("Error reading ZFS reserved area\n");
 198                 perror("read");
 199                 return (BC_ERROR);
 200         }
 201 
 202         /* No multiboot means no chance of knowing bootblock size */
 203         if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
 204             != BC_SUCCESS) {
 205                 BOOT_DEBUG("Unable to find multiboot header\n");
 206                 return (BC_NOEXTRA);
 207         }
 208         mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
 209 
 210         /*
 211          * Currently, the amount of space reserved for extra information
 212          * is "fixed". We may have to scan for the terminating extra payload
 213          * in the future.
 214          */
 215         size = mboot->load_end_addr - mboot->load_addr;
 216         buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
 217         bblock->file_size = size;
 218 
 219         bblock->buf = malloc(buf_size);
 220         if (bblock->buf == NULL) {
 221                 BOOT_DEBUG("Unable to allocate enough memory to read"
 222                     " the extra bootblock from the disk\n");
 223                 perror(gettext("Memory allocation failure"));
 224                 return (BC_ERROR);
 225         }
 226         bblock->buf_size = buf_size;
 227 
 228         dest = bblock->buf;
 229         size = BBLK_DATA_RSVD_SIZE;
 230 
 231         if (read_in(dev_fd, dest, size, SECTOR_SIZE) != BC_SUCCESS) {
 232                 BOOT_DEBUG("Error reading first %d bytes of the bootblock\n",
 233                     size);
 234                 (void) free(bblock->buf);
 235                 bblock->buf = NULL;
 236                 return (BC_ERROR);
 237         }
 238 
 239         dest += BBLK_DATA_RSVD_SIZE;
 240         size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
 241 
 242         if (read_in(dev_fd, dest, size, BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
 243                 BOOT_DEBUG("Error reading ZFS reserved area the second time\n");
 244                 (void) free(bblock->buf);
 245                 bblock->buf = NULL;
 246                 return (BC_ERROR);
 247         }
 248 
 249         /* Update pointers. */
 250         bblock->file = bblock->buf;
 251         bblock->mboot_off = mboot_off;
 252         bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off
 253             + BBLK_DATA_RSVD_SIZE);
 254         bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
 255         bblock->extra_size = bblock->buf_size - bblock->mboot_off
 256             - BBLK_DATA_RSVD_SIZE - sizeof (multiboot_header_t);
 257         return (BC_SUCCESS);
 258 }
 259 
 260 static boolean_t
 261 is_update_necessary(ib_data_t *data, char *updt_str)
 262 {
 263         bblk_einfo_t    *einfo;
 264         bblk_hs_t       bblock_hs;
 265         ib_bootblock_t  bblock_disk;
 266         ib_bootblock_t  *bblock_file = &data->bootblock;
 267         ib_device_t     *device = &data->device;
 268         int             dev_fd = device->fd;
 269 
 270         assert(data != NULL);
 271         assert(device->fd != -1);
 272 
 273         /* Nothing to do if we are not updating a ZFS bootblock. */
 274         if (!is_zfs(device->type))
 275                 return (B_TRUE);
 276 
 277         bzero(&bblock_disk, sizeof (ib_bootblock_t));
 278 
 279         if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) {
 280                 BOOT_DEBUG("Unable to read bootblock from %s\n", device->path);
 281                 return (B_TRUE);
 282         }
 283 
 284         einfo = find_einfo(bblock_disk.extra, bblock_disk.extra_size);
 285         if (einfo == NULL) {
 286                 BOOT_DEBUG("No extended information available\n");
 287                 return (B_TRUE);
 288         }
 289 
 290         if (!do_version || updt_str == NULL) {
 291                 (void) fprintf(stdout, "WARNING: target device %s has a "
 292                     "versioned bootblock that is going to be overwritten by a "
 293                     "non versioned one\n", device->path);
 294                 return (B_TRUE);
 295         }
 296 
 297         if (force_update) {
 298                 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
 299                 return (B_TRUE);
 300         }
 301 
 302         BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
 303 
 304         bblock_hs.src_buf = (unsigned char *)bblock_file->file;
 305         bblock_hs.src_size = bblock_file->file_size;
 306 
 307         return (einfo_should_update(einfo, &bblock_hs, updt_str));
 308 }
 309 
 310 static void
 311 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
 312 {
 313         bblk_hs_t       hs;
 314         uint32_t        avail_space;
 315 
 316         assert(bblock != NULL);
 317 
 318         if (updt_str == NULL) {
 319                 BOOT_DEBUG("WARNING: no update string passed to "
 320                     "add_bootblock_einfo()\n");
 321                 return;
 322         }
 323 
 324         /* Fill bootblock hashing source information. */
 325         hs.src_buf = (unsigned char *)bblock->file;
 326         hs.src_size = bblock->file_size;
 327         /* How much space for the extended information structure? */
 328         avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
 329         /* Place the extended information structure. */
 330         add_einfo(bblock->extra, updt_str, &hs, avail_space);
 331 }
 332 
 333 
 334 static int
 335 prepare_bootblock(ib_data_t *data, char *updt_str)
 336 {
 337         ib_device_t             *device = &data->device;
 338         ib_bootblock_t          *bblock = &data->bootblock;
 339         multiboot_header_t      *mboot;
 340 
 341         assert(data != NULL);
 342 
 343         /* Nothing to do if we are not on ZFS. */
 344         if (!is_zfs(device->type))
 345                 return (BC_SUCCESS);
 346 
 347         /*
 348          * Write the fake multiboot structure followed by the extra information
 349          * data. Both mboot and extra pointers have already been filled up to
 350          * point to the right location in the buffer. We prepare the fake
 351          * multiboot regardless if versioning was requested or not because
 352          * we need it for mirroring support.
 353          */
 354         assert(bblock->mboot != NULL);
 355         assert(bblock->extra != NULL);
 356 
 357         mboot = bblock->mboot;
 358 
 359         mboot->magic = MB_HEADER_MAGIC;
 360         mboot->flags = MB_HEADER_FLAGS_64;
 361         mboot->checksum = -(mboot->flags + mboot->magic);
 362         /*
 363          * Flags include the AOUT_KLUDGE and we use the extra members to specify
 364          * the size of the bootblock.
 365          */
 366         mboot->header_addr = bblock->mboot_off;
 367         mboot->load_addr = 0;
 368         mboot->load_end_addr = bblock->file_size;
 369 
 370         /*
 371          * Now that we have the mboot header in place, we can add the extended
 372          * versioning information. Since the multiboot header has been placed
 373          * after the file image, the hashing will still reflect the one of the
 374          * file on the disk.
 375          */
 376         if (do_version)
 377                 add_bootblock_einfo(bblock, updt_str);
 378 
 379         return (BC_SUCCESS);
 380 }
 381 
 382 static int
 383 write_zfs_bootblock(ib_data_t *data)
 384 {
 385         ib_device_t     *device = &data->device;
 386         ib_bootblock_t  *bblock = &data->bootblock;
 387         char            *bufptr;
 388         uint32_t        size;
 389 
 390         assert(data != NULL);
 391         assert(device->fd != -1);
 392 
 393         /*
 394          * In the ZFS case we actually perform two different steps:
 395          * - write the first 15 blocks of the bootblock to the reserved disk
 396          *   blocks.
 397          * - write the remaining blocks in the ZFS reserved area at offset
 398          *   512K.
 399          */
 400         bufptr = bblock->buf;
 401         size = BBLK_DATA_RSVD_SIZE;
 402 
 403         if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) {
 404                 BOOT_DEBUG("Error writing first 15 blocks of %s\n",
 405                     device->path);
 406                 perror("write");
 407                 return (BC_ERROR);
 408         }
 409 
 410         bufptr += BBLK_DATA_RSVD_SIZE;
 411         size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
 412 
 413         if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF)
 414             != BC_SUCCESS) {
 415                 BOOT_DEBUG("Error writing the second part of ZFS bootblock "
 416                     "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF);
 417                 return (BC_ERROR);
 418         }
 419         return (BC_SUCCESS);
 420 }
 421 
 422 static int
 423 write_bootblock(ib_data_t *data)
 424 {
 425         ib_device_t     *device = &data->device;
 426         ib_bootblock_t  *bblock = &data->bootblock;
 427         int             ret;
 428 
 429         assert(data != NULL);
 430 
 431         /*
 432          * If we are on UFS or HSFS we simply write out to the reserved
 433          * blocks (1 to 15) the boot block.
 434          */
 435         if (!is_zfs(device->type)) {
 436                 if (write_out(device->fd, bblock->buf, bblock->buf_size,
 437                     SECTOR_SIZE) != BC_SUCCESS) {
 438                         BOOT_DEBUG("Error writing bootblock to %s\n",
 439                             device->path);
 440                         return (BC_ERROR);
 441                 } else {
 442                         return (BC_SUCCESS);
 443                 }
 444         } else {
 445                 ret = write_zfs_bootblock(data);
 446                 return (ret);
 447         }
 448 }
 449 
 450 static int
 451 open_device(ib_device_t *device)
 452 {
 453         struct stat     statbuf;
 454 
 455         device->fd = open(device->path, O_RDWR);
 456         if (device->fd == -1) {
 457                 BOOT_DEBUG("Unable to open %s\n", device->path);
 458                 perror("open");
 459                 return (BC_ERROR);
 460         }
 461 
 462         if (fstat(device->fd, &statbuf) != 0) {
 463                 BOOT_DEBUG("Unable to stat %s\n", device->path);
 464                 perror("stat");
 465                 (void) close(device->fd);
 466                 return (BC_ERROR);
 467         }
 468 
 469         if (S_ISCHR(statbuf.st_mode) == 0) {
 470                 (void) fprintf(stderr, gettext("%s: Not a character device\n"),
 471                     device->path);
 472                 return (BC_ERROR);
 473         }
 474 
 475         return (BC_SUCCESS);
 476 }
 477 
 478 static int
 479 init_device(ib_device_t *device, char *path)
 480 {
 481         bzero(device, sizeof (*device));
 482         device->fd = -1;
 483 
 484         device->path = strdup(path);
 485         if (path == NULL) {
 486                 perror(gettext("Memory allocation failure"));
 487                 return (BC_ERROR);
 488         }
 489 
 490         device->type = tgt_fs_type;
 491         if (open_device(device) != BC_SUCCESS)
 492                 return (BC_ERROR);
 493 
 494         return (BC_SUCCESS);
 495 }
 496 
 497 static void
 498 cleanup_device(ib_device_t *device)
 499 {
 500         free(device->path);
 501         bzero(device, sizeof (*device));
 502 
 503         if (device->fd != -1)
 504                 (void) close(device->fd);
 505 }
 506 
 507 static void
 508 cleanup_bootblock(ib_bootblock_t *bblock)
 509 {
 510         free(bblock->buf);
 511         bzero(bblock, sizeof (ib_bootblock_t));
 512 }
 513 
 514 /*
 515  * Propagate the bootblock on the source disk to the destination disk and
 516  * version it with 'updt_str' in the process. Since we cannot trust any data
 517  * on the attaching disk, we do not perform any specific check on a potential
 518  * target extended information structure and we just blindly update.
 519  */
 520 static int
 521 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
 522 {
 523         ib_bootblock_t  *src_bblock = &src->bootblock;
 524         ib_bootblock_t  *dest_bblock = &dest->bootblock;
 525         uint32_t        buf_size;
 526 
 527         assert(src != NULL);
 528         assert(dest != NULL);
 529 
 530         cleanup_bootblock(dest_bblock);
 531 
 532         if (updt_str != NULL) {
 533                 do_version = B_TRUE;
 534         } else {
 535                 do_version = B_FALSE;
 536         }
 537 
 538         buf_size = src_bblock->file_size + SECTOR_SIZE;
 539 
 540         dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
 541         dest_bblock->buf = malloc(dest_bblock->buf_size);
 542         if (dest_bblock->buf == NULL) {
 543                 perror(gettext("Memory Allocation Failure"));
 544                 return (BC_ERROR);
 545         }
 546         dest_bblock->file = dest_bblock->buf;
 547         dest_bblock->file_size = src_bblock->file_size;
 548         (void) memcpy(dest_bblock->file, src_bblock->file,
 549             dest_bblock->file_size);
 550 
 551         dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
 552             P2ROUNDUP(dest_bblock->file_size, 8));
 553         dest_bblock->extra = (char *)dest_bblock->mboot +
 554             sizeof (multiboot_header_t);
 555 
 556         (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
 557             src->device.path, dest->device.path);
 558 
 559         return (commit_to_disk(dest, updt_str));
 560 }
 561 
 562 static int
 563 commit_to_disk(ib_data_t *data, char *update_str)
 564 {
 565         assert(data != NULL);
 566 
 567         if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
 568                 (void) fprintf(stderr, gettext("Error updating the bootblock "
 569                     "image\n"));
 570                 return (BC_ERROR);
 571         }
 572 
 573         if (write_bootblock(data) != BC_SUCCESS) {
 574                 (void) fprintf(stderr, gettext("Error writing bootblock to "
 575                     "disk\n"));
 576                 return (BC_ERROR);
 577         }
 578 
 579         return (BC_SUCCESS);
 580 }
 581 
 582 
 583 /*
 584  * Install a new bootblock on the given device. handle_install() expects argv
 585  * to contain 2 parameters (the target device path and the path to the
 586  * bootblock.
 587  *
 588  * Returns:     BC_SUCCESS - if the installation is successful
 589  *              BC_ERROR   - if the installation failed
 590  *              BC_NOUPDT  - if no installation was performed because the
 591  *                           version currently installed is more recent than the
 592  *                           supplied one.
 593  *
 594  */
 595 static int
 596 handle_install(char *progname, char **argv)
 597 {
 598         ib_data_t       install_data;
 599         char            *bootblock = NULL;
 600         char            *device_path = NULL;
 601         int             ret = BC_ERROR;
 602 
 603         bootblock = strdup(argv[0]);
 604         device_path = strdup(argv[1]);
 605 
 606         if (!device_path || !bootblock) {
 607                 (void) fprintf(stderr, gettext("Missing parameter"));
 608                 usage(progname);
 609                 goto out;
 610         }
 611 
 612         BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path,
 613             bootblock);
 614         bzero(&install_data, sizeof (ib_data_t));
 615 
 616         if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
 617                 (void) fprintf(stderr, gettext("Unable to open device %s\n"),
 618                     device_path);
 619                 goto out;
 620         }
 621 
 622         if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
 623                 (void) fprintf(stderr, gettext("Error reading %s\n"),
 624                     bootblock);
 625                 goto out_dev;
 626         }
 627         /* Versioning is only supported for the ZFS bootblock. */
 628         if (do_version && !is_zfs(install_data.device.type)) {
 629                 (void) fprintf(stderr, gettext("Versioning is only supported on"
 630                     " ZFS... skipping.\n"));
 631                 do_version = B_FALSE;
 632         }
 633 
 634         /*
 635          * is_update_necessary() will take care of checking if versioning and/or
 636          * forcing the update have been specified. It will also emit a warning
 637          * if a non-versioned update is attempted over a versioned bootblock.
 638          */
 639         if (!is_update_necessary(&install_data, update_str)) {
 640                 (void) fprintf(stderr, gettext("bootblock version installed "
 641                     "on %s is more recent or identical\n"
 642                     "Use -F to override or install without the -u option\n"),
 643                     device_path);
 644                 ret = BC_NOUPDT;
 645                 goto out_dev;
 646         }
 647 
 648         BOOT_DEBUG("Ready to commit to disk\n");
 649         ret = commit_to_disk(&install_data, update_str);
 650 
 651 out_dev:
 652         cleanup_device(&install_data.device);
 653 out:
 654         free(bootblock);
 655         free(device_path);
 656         return (ret);
 657 }
 658 
 659 /*
 660  * Retrieves from a device the extended information (einfo) associated to the
 661  * installed bootblock.
 662  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
 663  * Returns:
 664  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
 665  *        - BC_ERROR (on error)
 666  *        - BC_NOEINFO (no extended information available)
 667  */
 668 static int
 669 handle_getinfo(char *progname, char **argv)
 670 {
 671 
 672         ib_data_t       data;
 673         ib_bootblock_t  *bblock = &data.bootblock;
 674         ib_device_t     *device = &data.device;
 675         bblk_einfo_t    *einfo;
 676         uint8_t         flags = 0;
 677         uint32_t        size;
 678         char            *device_path;
 679         int             retval = BC_ERROR;
 680         int             ret;
 681 
 682         device_path = strdup(argv[0]);
 683         if (!device_path) {
 684                 (void) fprintf(stderr, gettext("Missing parameter"));
 685                 usage(progname);
 686                 goto out;
 687         }
 688 
 689         bzero(&data, sizeof (ib_data_t));
 690         BOOT_DEBUG("device path: %s\n", device_path);
 691 
 692         if (init_device(device, device_path) != BC_SUCCESS) {
 693                 (void) fprintf(stderr, gettext("Unable to gather device "
 694                     "information from %s\n"), device_path);
 695                 goto out_dev;
 696         }
 697 
 698         if (!is_zfs(device->type)) {
 699                 (void) fprintf(stderr, gettext("Versioning only supported on "
 700                     "ZFS\n"));
 701                 goto out_dev;
 702         }
 703 
 704         ret = read_bootblock_from_disk(device->fd, bblock);
 705         if (ret == BC_ERROR) {
 706                 (void) fprintf(stderr, gettext("Error reading bootblock from "
 707                     "%s\n"), device_path);
 708                 goto out_dev;
 709         }
 710 
 711         if (ret == BC_NOEXTRA) {
 712                 BOOT_DEBUG("No multiboot header found on %s, unable "
 713                     "to locate extra information area (old/non versioned "
 714                     "bootblock?) \n", device_path);
 715                 (void) fprintf(stderr, gettext("No extended information "
 716                     "found\n"));
 717                 retval = BC_NOEINFO;
 718                 goto out_dev;
 719         }
 720 
 721         einfo = find_einfo(bblock->extra, bblock->extra_size);
 722         if (einfo == NULL) {
 723                 retval = BC_NOEINFO;
 724                 (void) fprintf(stderr, gettext("No extended information "
 725                     "found\n"));
 726                 goto out_dev;
 727         }
 728 
 729         /* Print the extended information. */
 730         if (strip)
 731                 flags |= EINFO_EASY_PARSE;
 732         if (verbose_dump)
 733                 flags |= EINFO_PRINT_HEADER;
 734 
 735         size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) -
 736             sizeof (multiboot_header_t);
 737         print_einfo(flags, einfo, size);
 738         retval = BC_SUCCESS;
 739 
 740 out_dev:
 741         cleanup_device(&data.device);
 742 out:
 743         free(device_path);
 744         return (retval);
 745 
 746 }
 747 
 748 /*
 749  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
 750  *
 751  * Returns:
 752  *      - BC_SUCCESS (a successful propagation happened)
 753  *      - BC_ERROR (an error occurred)
 754  *      - BC_NOEXTRA (it is not possible to dump the current bootblock since
 755  *                      there is no multiboot information)
 756  */
 757 static int
 758 handle_mirror(char *progname, char **argv)
 759 {
 760         ib_data_t       curr_data;
 761         ib_data_t       attach_data;
 762         ib_device_t     *curr_device = &curr_data.device;
 763         ib_device_t     *attach_device = &attach_data.device;
 764         ib_bootblock_t  *bblock_curr = &curr_data.bootblock;
 765         ib_bootblock_t  *bblock_attach = &attach_data.bootblock;
 766         bblk_einfo_t    *einfo_curr = NULL;
 767         char            *curr_device_path;
 768         char            *attach_device_path;
 769         char            *updt_str = NULL;
 770         int             retval = BC_ERROR;
 771         int             ret;
 772 
 773         curr_device_path = strdup(argv[0]);
 774         attach_device_path = strdup(argv[1]);
 775 
 776         if (!curr_device_path || !attach_device_path) {
 777                 (void) fprintf(stderr, gettext("Missing parameter"));
 778                 usage(progname);
 779                 goto out;
 780         }
 781         BOOT_DEBUG("Current device path is: %s, attaching device path is: "
 782             " %s\n", curr_device_path, attach_device_path);
 783 
 784         bzero(&curr_data, sizeof (ib_data_t));
 785         bzero(&attach_data, sizeof (ib_data_t));
 786 
 787         if (tgt_fs_type != TARGET_IS_ZFS) {
 788                 (void) fprintf(stderr, gettext("Mirroring is only supported on "
 789                     "ZFS\n"));
 790                 return (BC_ERROR);
 791         }
 792 
 793         if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
 794                 (void) fprintf(stderr, gettext("Unable to gather device "
 795                     "information from %s (current device)\n"),
 796                     curr_device_path);
 797                 goto out_currdev;
 798         }
 799 
 800         if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
 801                 (void) fprintf(stderr, gettext("Unable to gather device "
 802                     "information from %s (attaching device)\n"),
 803                     attach_device_path);
 804                 goto out_devs;
 805         }
 806 
 807         ret = read_bootblock_from_disk(curr_device->fd, bblock_curr);
 808         if (ret == BC_ERROR) {
 809                 BOOT_DEBUG("Error reading bootblock from %s\n",
 810                     curr_device->path);
 811                 retval = BC_ERROR;
 812                 goto out_devs;
 813         }
 814 
 815         if (ret == BC_NOEXTRA) {
 816                 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
 817                     " the bootblock\n", curr_device->path);
 818                 retval = BC_NOEXTRA;
 819                 goto out_devs;
 820         }
 821 
 822         einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size);
 823         if (einfo_curr != NULL)
 824                 updt_str = einfo_get_string(einfo_curr);
 825 
 826         retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
 827         cleanup_bootblock(bblock_curr);
 828         cleanup_bootblock(bblock_attach);
 829 out_devs:
 830         cleanup_device(attach_device);
 831 out_currdev:
 832         cleanup_device(curr_device);
 833 out:
 834         free(curr_device_path);
 835         free(attach_device_path);
 836         return (retval);
 837 }
 838 
 839 #define USAGE_STRING    "Usage: %s [-h|-f|-F fstype|-u verstr] bootblk "       \
 840                         "raw-device\n"                                         \
 841                         "\t%s [-e|-V] -i -F zfs raw-device\n"                  \
 842                         "\t%s -M -F zfs raw-device attach-raw-device\n"        \
 843                         "\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n"
 844 
 845 #define CANON_USAGE_STR gettext(USAGE_STRING)
 846 
 847 static void
 848 usage(char *progname)
 849 {
 850         (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
 851 }
 852 
 853 int
 854 main(int argc, char **argv)
 855 {
 856         int     opt;
 857         int     params = 2;
 858         int     ret;
 859         char    *progname;
 860         char    **handle_args;
 861 
 862         (void) setlocale(LC_ALL, "");
 863         (void) textdomain(TEXT_DOMAIN);
 864 
 865         while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) {
 866                 switch (opt) {
 867                 case 'F':
 868                         if (strcmp(optarg, "ufs") == 0) {
 869                                 tgt_fs_type = TARGET_IS_UFS;
 870                         } else if (strcmp(optarg, "hsfs") == 0) {
 871                                 tgt_fs_type = TARGET_IS_HSFS;
 872                         } else if (strcmp(optarg, "zfs") == 0) {
 873                                 tgt_fs_type = TARGET_IS_ZFS;
 874                         } else {
 875                                 (void) fprintf(stderr, gettext("Wrong "
 876                                     "filesystem specified\n\n"));
 877                                 usage(argv[0]);
 878                                 exit(BC_ERROR);
 879                         }
 880                         break;
 881                 case 'e':
 882                         strip = B_TRUE;
 883                         break;
 884                 case 'f':
 885                         force_update = B_TRUE;
 886                         break;
 887                 case 'V':
 888                         verbose_dump = B_TRUE;
 889                         break;
 890                 case 'i':
 891                         do_getinfo = B_TRUE;
 892                         params = 1;
 893                         break;
 894                 case 'u':
 895                         do_version = B_TRUE;
 896 
 897                         update_str = malloc(strlen(optarg) + 1);
 898                         if (update_str == NULL) {
 899                                 perror(gettext("Memory allocation failure"));
 900                                 exit(BC_ERROR);
 901                         }
 902                         (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
 903                         break;
 904                 case 'M':
 905                         do_mirror_bblk = B_TRUE;
 906                         break;
 907                 case 'h':
 908                         usage(argv[0]);
 909                         exit(BC_SUCCESS);
 910                         break;
 911                 case 'd':
 912                         boot_debug = B_TRUE;
 913                         break;
 914                 case 'n':
 915                         nowrite = B_TRUE;
 916                         break;
 917                 default:
 918                         /* fall through to process non-optional args */
 919                         break;
 920                 }
 921         }
 922 
 923         /* check arguments */
 924         if (argc != optind + params) {
 925                 usage(argv[0]);
 926                 exit(BC_ERROR);
 927         }
 928         progname = argv[0];
 929         handle_args = argv + optind;
 930 
 931         /* check options. */
 932         if (do_getinfo && do_mirror_bblk) {
 933                 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
 934                     "specified at the same time\n"));
 935                 usage(progname);
 936                 exit(BC_ERROR);
 937         }
 938 
 939         if (nowrite)
 940                 (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
 941                     " be written to disk.\n"));
 942 
 943         if (do_getinfo) {
 944                 ret = handle_getinfo(progname, handle_args);
 945         } else if (do_mirror_bblk) {
 946                 ret = handle_mirror(progname, handle_args);
 947         } else {
 948                 ret = handle_install(progname, handle_args);
 949         }
 950         return (ret);
 951 }