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         return (BC_SUCCESS);
 256 }
 257 
 258 static boolean_t
 259 is_update_necessary(ib_data_t *data, char *updt_str)
 260 {
 261         bblk_einfo_t    *einfo;
 262         bblk_hs_t       bblock_hs;
 263         ib_bootblock_t  bblock_disk;
 264         ib_bootblock_t  *bblock_file = &data->bootblock;
 265         ib_device_t     *device = &data->device;
 266         int             dev_fd = device->fd;
 267 
 268         assert(data != NULL);
 269         assert(device->fd != -1);
 270 
 271         /* Nothing to do if we are not updating a ZFS bootblock. */
 272         if (!is_zfs(device->type))
 273                 return (B_TRUE);
 274 
 275         bzero(&bblock_disk, sizeof (ib_bootblock_t));
 276 
 277         if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) {
 278                 BOOT_DEBUG("Unable to read bootblock from %s\n", device->path);
 279                 return (B_TRUE);
 280         }
 281 
 282         einfo = find_einfo(bblock_disk.extra);
 283         if (einfo == NULL) {
 284                 BOOT_DEBUG("No extended information available\n");
 285                 return (B_TRUE);
 286         }
 287 
 288         if (!do_version || updt_str == NULL) {
 289                 (void) fprintf(stdout, "WARNING: target device %s has a "
 290                     "versioned bootblock that is going to be overwritten by a "
 291                     "non versioned one\n", device->path);
 292                 return (B_TRUE);
 293         }
 294 
 295         if (force_update) {
 296                 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
 297                 return (B_TRUE);
 298         }
 299 
 300         BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
 301 
 302         bblock_hs.src_buf = (unsigned char *)bblock_file->file;
 303         bblock_hs.src_size = bblock_file->file_size;
 304 
 305         return (einfo_should_update(einfo, &bblock_hs, updt_str));
 306 }
 307 
 308 static void
 309 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
 310 {
 311         bblk_hs_t       hs;
 312         uint32_t        avail_space;
 313 
 314         assert(bblock != NULL);
 315 
 316         if (updt_str == NULL) {
 317                 BOOT_DEBUG("WARNING: no update string passed to "
 318                     "add_bootblock_einfo()\n");
 319                 return;
 320         }
 321 
 322         /* Fill bootblock hashing source information. */
 323         hs.src_buf = (unsigned char *)bblock->file;
 324         hs.src_size = bblock->file_size;
 325         /* How much space for the extended information structure? */
 326         avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
 327         /* Place the extended information structure. */
 328         add_einfo(bblock->extra, updt_str, &hs, avail_space);
 329 }
 330 
 331 
 332 static int
 333 prepare_bootblock(ib_data_t *data, char *updt_str)
 334 {
 335         ib_device_t             *device = &data->device;
 336         ib_bootblock_t          *bblock = &data->bootblock;
 337         multiboot_header_t      *mboot;
 338 
 339         assert(data != NULL);
 340 
 341         /* Nothing to do if we are not on ZFS. */
 342         if (!is_zfs(device->type))
 343                 return (BC_SUCCESS);
 344 
 345         /*
 346          * Write the fake multiboot structure followed by the extra information
 347          * data. Both mboot and extra pointers have already been filled up to
 348          * point to the right location in the buffer. We prepare the fake
 349          * multiboot regardless if versioning was requested or not because
 350          * we need it for mirroring support.
 351          */
 352         assert(bblock->mboot != NULL);
 353         assert(bblock->extra != NULL);
 354 
 355         mboot = bblock->mboot;
 356 
 357         mboot->magic = MB_HEADER_MAGIC;
 358         mboot->flags = MB_HEADER_FLAGS_64;
 359         mboot->checksum = -(mboot->flags + mboot->magic);
 360         /*
 361          * Flags include the AOUT_KLUDGE and we use the extra members to specify
 362          * the size of the bootblock.
 363          */
 364         mboot->header_addr = bblock->mboot_off;
 365         mboot->load_addr = 0;
 366         mboot->load_end_addr = bblock->file_size;
 367 
 368         /*
 369          * Now that we have the mboot header in place, we can add the extended
 370          * versioning information. Since the multiboot header has been placed
 371          * after the file image, the hashing will still reflect the one of the
 372          * file on the disk.
 373          */
 374         if (do_version)
 375                 add_bootblock_einfo(bblock, updt_str);
 376 
 377         return (BC_SUCCESS);
 378 }
 379 
 380 static int
 381 write_zfs_bootblock(ib_data_t *data)
 382 {
 383         ib_device_t     *device = &data->device;
 384         ib_bootblock_t  *bblock = &data->bootblock;
 385         char            *bufptr;
 386         uint32_t        size;
 387 
 388         assert(data != NULL);
 389         assert(device->fd != -1);
 390 
 391         /*
 392          * In the ZFS case we actually perform two different steps:
 393          * - write the first 15 blocks of the bootblock to the reserved disk
 394          *   blocks.
 395          * - write the remaining blocks in the ZFS reserved area at offset
 396          *   512K.
 397          */
 398         bufptr = bblock->buf;
 399         size = BBLK_DATA_RSVD_SIZE;
 400 
 401         if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) {
 402                 BOOT_DEBUG("Error writing first 15 blocks of %s\n",
 403                     device->path);
 404                 perror("write");
 405                 return (BC_ERROR);
 406         }
 407 
 408         bufptr += BBLK_DATA_RSVD_SIZE;
 409         size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
 410 
 411         if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF)
 412             != BC_SUCCESS) {
 413                 BOOT_DEBUG("Error writing the second part of ZFS bootblock "
 414                     "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF);
 415                 return (BC_ERROR);
 416         }
 417         return (BC_SUCCESS);
 418 }
 419 
 420 static int
 421 write_bootblock(ib_data_t *data)
 422 {
 423         ib_device_t     *device = &data->device;
 424         ib_bootblock_t  *bblock = &data->bootblock;
 425         int             ret;
 426 
 427         assert(data != NULL);
 428 
 429         /*
 430          * If we are on UFS or HSFS we simply write out to the reserved
 431          * blocks (1 to 15) the boot block.
 432          */
 433         if (!is_zfs(device->type)) {
 434                 if (write_out(device->fd, bblock->buf, bblock->buf_size,
 435                     SECTOR_SIZE) != BC_SUCCESS) {
 436                         BOOT_DEBUG("Error writing bootblock to %s\n",
 437                             device->path);
 438                         return (BC_ERROR);
 439                 } else {
 440                         return (BC_SUCCESS);
 441                 }
 442         } else {
 443                 ret = write_zfs_bootblock(data);
 444                 return (ret);
 445         }
 446 }
 447 
 448 static int
 449 open_device(ib_device_t *device)
 450 {
 451         struct stat     statbuf;
 452 
 453         device->fd = open(device->path, O_RDWR);
 454         if (device->fd == -1) {
 455                 BOOT_DEBUG("Unable to open %s\n", device->path);
 456                 perror("open");
 457                 return (BC_ERROR);
 458         }
 459 
 460         if (fstat(device->fd, &statbuf) != 0) {
 461                 BOOT_DEBUG("Unable to stat %s\n", device->path);
 462                 perror("stat");
 463                 (void) close(device->fd);
 464                 return (BC_ERROR);
 465         }
 466 
 467         if (S_ISCHR(statbuf.st_mode) == 0) {
 468                 (void) fprintf(stderr, gettext("%s: Not a character device\n"),
 469                     device->path);
 470                 return (BC_ERROR);
 471         }
 472 
 473         return (BC_SUCCESS);
 474 }
 475 
 476 static int
 477 init_device(ib_device_t *device, char *path)
 478 {
 479         bzero(device, sizeof (*device));
 480         device->fd = -1;
 481 
 482         device->path = strdup(path);
 483         if (path == NULL) {
 484                 perror(gettext("Memory allocation failure"));
 485                 return (BC_ERROR);
 486         }
 487 
 488         device->type = tgt_fs_type;
 489         if (open_device(device) != BC_SUCCESS)
 490                 return (BC_ERROR);
 491 
 492         return (BC_SUCCESS);
 493 }
 494 
 495 static void
 496 cleanup_device(ib_device_t *device)
 497 {
 498         free(device->path);
 499         bzero(device, sizeof (*device));
 500 
 501         if (device->fd != -1)
 502                 (void) close(device->fd);
 503 }
 504 
 505 static void
 506 cleanup_bootblock(ib_bootblock_t *bblock)
 507 {
 508         free(bblock->buf);
 509         bzero(bblock, sizeof (ib_bootblock_t));
 510 }
 511 
 512 /*
 513  * Propagate the bootblock on the source disk to the destination disk and
 514  * version it with 'updt_str' in the process. Since we cannot trust any data
 515  * on the attaching disk, we do not perform any specific check on a potential
 516  * target extended information structure and we just blindly update.
 517  */
 518 static int
 519 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
 520 {
 521         ib_bootblock_t  *src_bblock = &src->bootblock;
 522         ib_bootblock_t  *dest_bblock = &dest->bootblock;
 523         uint32_t        buf_size;
 524 
 525         assert(src != NULL);
 526         assert(dest != NULL);
 527 
 528         cleanup_bootblock(dest_bblock);
 529 
 530         if (updt_str != NULL) {
 531                 do_version = B_TRUE;
 532         } else {
 533                 do_version = B_FALSE;
 534         }
 535 
 536         buf_size = src_bblock->file_size + SECTOR_SIZE;
 537 
 538         dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
 539         dest_bblock->buf = malloc(dest_bblock->buf_size);
 540         if (dest_bblock->buf == NULL) {
 541                 perror(gettext("Memory Allocation Failure"));
 542                 return (BC_ERROR);
 543         }
 544         dest_bblock->file = dest_bblock->buf;
 545         dest_bblock->file_size = src_bblock->file_size;
 546         (void) memcpy(dest_bblock->file, src_bblock->file,
 547             dest_bblock->file_size);
 548 
 549         dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
 550             P2ROUNDUP(dest_bblock->file_size, 8));
 551         dest_bblock->extra = (char *)dest_bblock->mboot +
 552             sizeof (multiboot_header_t);
 553 
 554         (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
 555             src->device.path, dest->device.path);
 556 
 557         return (commit_to_disk(dest, updt_str));
 558 }
 559 
 560 static int
 561 commit_to_disk(ib_data_t *data, char *update_str)
 562 {
 563         assert(data != NULL);
 564 
 565         if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
 566                 (void) fprintf(stderr, gettext("Error updating the bootblock "
 567                     "image\n"));
 568                 return (BC_ERROR);
 569         }
 570 
 571         if (write_bootblock(data) != BC_SUCCESS) {
 572                 (void) fprintf(stderr, gettext("Error writing bootblock to "
 573                     "disk\n"));
 574                 return (BC_ERROR);
 575         }
 576 
 577         return (BC_SUCCESS);
 578 }
 579 
 580 
 581 /*
 582  * Install a new bootblock on the given device. handle_install() expects argv
 583  * to contain 2 parameters (the target device path and the path to the
 584  * bootblock.
 585  *
 586  * Returns:     BC_SUCCESS - if the installation is successful
 587  *              BC_ERROR   - if the installation failed
 588  *              BC_NOUPDT  - if no installation was performed because the
 589  *                           version currently installed is more recent than the
 590  *                           supplied one.
 591  *
 592  */
 593 static int
 594 handle_install(char *progname, char **argv)
 595 {
 596         ib_data_t       install_data;
 597         char            *bootblock = NULL;
 598         char            *device_path = NULL;
 599         int             ret = BC_ERROR;
 600 
 601         bootblock = strdup(argv[0]);
 602         device_path = strdup(argv[1]);
 603 
 604         if (!device_path || !bootblock) {
 605                 (void) fprintf(stderr, gettext("Missing parameter"));
 606                 usage(progname);
 607                 goto out;
 608         }
 609 
 610         BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path,
 611             bootblock);
 612         bzero(&install_data, sizeof (ib_data_t));
 613 
 614         if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
 615                 (void) fprintf(stderr, gettext("Unable to open device %s\n"),
 616                     device_path);
 617                 goto out;
 618         }
 619 
 620         if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
 621                 (void) fprintf(stderr, gettext("Error reading %s\n"),
 622                     bootblock);
 623                 goto out_dev;
 624         }
 625         /* Versioning is only supported for the ZFS bootblock. */
 626         if (do_version && !is_zfs(install_data.device.type)) {
 627                 (void) fprintf(stderr, gettext("Versioning is only supported on"
 628                     " ZFS... skipping.\n"));
 629                 do_version = B_FALSE;
 630         }
 631 
 632         /*
 633          * is_update_necessary() will take care of checking if versioning and/or
 634          * forcing the update have been specified. It will also emit a warning
 635          * if a non-versioned update is attempted over a versioned bootblock.
 636          */
 637         if (!is_update_necessary(&install_data, update_str)) {
 638                 (void) fprintf(stderr, gettext("bootblock version installed "
 639                     "on %s is more recent or identical\n"
 640                     "Use -F to override or install without the -u option\n"),
 641                     device_path);
 642                 ret = BC_NOUPDT;
 643                 goto out_dev;
 644         }
 645 
 646         BOOT_DEBUG("Ready to commit to disk\n");
 647         ret = commit_to_disk(&install_data, update_str);
 648 
 649 out_dev:
 650         cleanup_device(&install_data.device);
 651 out:
 652         free(bootblock);
 653         free(device_path);
 654         return (ret);
 655 }
 656 
 657 /*
 658  * Retrieves from a device the extended information (einfo) associated to the
 659  * installed bootblock.
 660  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
 661  * Returns:
 662  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
 663  *        - BC_ERROR (on error)
 664  *        - BC_NOEINFO (no extended information available)
 665  */
 666 static int
 667 handle_getinfo(char *progname, char **argv)
 668 {
 669 
 670         ib_data_t       data;
 671         ib_bootblock_t  *bblock = &data.bootblock;
 672         ib_device_t     *device = &data.device;
 673         bblk_einfo_t    *einfo;
 674         uint8_t         flags = 0;
 675         uint32_t        size;
 676         char            *device_path;
 677         int             retval = BC_ERROR;
 678         int             ret;
 679 
 680         device_path = strdup(argv[0]);
 681         if (!device_path) {
 682                 (void) fprintf(stderr, gettext("Missing parameter"));
 683                 usage(progname);
 684                 goto out;
 685         }
 686 
 687         bzero(&data, sizeof (ib_data_t));
 688         BOOT_DEBUG("device path: %s\n", device_path);
 689 
 690         if (init_device(device, device_path) != BC_SUCCESS) {
 691                 (void) fprintf(stderr, gettext("Unable to gather device "
 692                     "information from %s\n"), device_path);
 693                 goto out_dev;
 694         }
 695 
 696         if (!is_zfs(device->type)) {
 697                 (void) fprintf(stderr, gettext("Versioning only supported on "
 698                     "ZFS\n"));
 699                 goto out_dev;
 700         }
 701 
 702         ret = read_bootblock_from_disk(device->fd, bblock);
 703         if (ret == BC_ERROR) {
 704                 (void) fprintf(stderr, gettext("Error reading bootblock from "
 705                     "%s\n"), device_path);
 706                 goto out_dev;
 707         }
 708 
 709         if (ret == BC_NOEXTRA) {
 710                 BOOT_DEBUG("No multiboot header found on %s, unable "
 711                     "to locate extra information area (old/non versioned "
 712                     "bootblock?) \n", device_path);
 713                 (void) fprintf(stderr, gettext("No extended information "
 714                     "found\n"));
 715                 retval = BC_NOEINFO;
 716                 goto out_dev;
 717         }
 718 
 719         einfo = find_einfo(bblock->extra);
 720         if (einfo == NULL) {
 721                 retval = BC_NOEINFO;
 722                 (void) fprintf(stderr, gettext("No extended information "
 723                     "found\n"));
 724                 goto out_dev;
 725         }
 726 
 727         /* Print the extended information. */
 728         if (strip)
 729                 flags |= EINFO_EASY_PARSE;
 730         if (verbose_dump)
 731                 flags |= EINFO_PRINT_HEADER;
 732 
 733         size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) -
 734             sizeof (multiboot_header_t);
 735         print_einfo(flags, einfo, size);
 736         retval = BC_SUCCESS;
 737 
 738 out_dev:
 739         cleanup_device(&data.device);
 740 out:
 741         free(device_path);
 742         return (retval);
 743 
 744 }
 745 
 746 /*
 747  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
 748  *
 749  * Returns:
 750  *      - BC_SUCCESS (a successful propagation happened)
 751  *      - BC_ERROR (an error occurred)
 752  *      - BC_NOEXTRA (it is not possible to dump the current bootblock since
 753  *                      there is no multiboot information)
 754  */
 755 static int
 756 handle_mirror(char *progname, char **argv)
 757 {
 758         ib_data_t       curr_data;
 759         ib_data_t       attach_data;
 760         ib_device_t     *curr_device = &curr_data.device;
 761         ib_device_t     *attach_device = &attach_data.device;
 762         ib_bootblock_t  *bblock_curr = &curr_data.bootblock;
 763         ib_bootblock_t  *bblock_attach = &attach_data.bootblock;
 764         bblk_einfo_t    *einfo_curr = NULL;
 765         char            *curr_device_path;
 766         char            *attach_device_path;
 767         char            *updt_str = NULL;
 768         int             retval = BC_ERROR;
 769         int             ret;
 770 
 771         curr_device_path = strdup(argv[0]);
 772         attach_device_path = strdup(argv[1]);
 773 
 774         if (!curr_device_path || !attach_device_path) {
 775                 (void) fprintf(stderr, gettext("Missing parameter"));
 776                 usage(progname);
 777                 goto out;
 778         }
 779         BOOT_DEBUG("Current device path is: %s, attaching device path is: "
 780             " %s\n", curr_device_path, attach_device_path);
 781 
 782         bzero(&curr_data, sizeof (ib_data_t));
 783         bzero(&attach_data, sizeof (ib_data_t));
 784 
 785         if (tgt_fs_type != TARGET_IS_ZFS) {
 786                 (void) fprintf(stderr, gettext("Mirroring is only supported on "
 787                     "ZFS\n"));
 788                 return (BC_ERROR);
 789         }
 790 
 791         if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
 792                 (void) fprintf(stderr, gettext("Unable to gather device "
 793                     "information from %s (current device)\n"),
 794                     curr_device_path);
 795                 goto out_currdev;
 796         }
 797 
 798         if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
 799                 (void) fprintf(stderr, gettext("Unable to gather device "
 800                     "information from %s (attaching device)\n"),
 801                     attach_device_path);
 802                 goto out_devs;
 803         }
 804 
 805         ret = read_bootblock_from_disk(curr_device->fd, bblock_curr);
 806         if (ret == BC_ERROR) {
 807                 BOOT_DEBUG("Error reading bootblock from %s\n",
 808                     curr_device->path);
 809                 retval = BC_ERROR;
 810                 goto out_devs;
 811         }
 812 
 813         if (ret == BC_NOEXTRA) {
 814                 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
 815                     " the bootblock\n", curr_device->path);
 816                 retval = BC_NOEXTRA;
 817                 goto out_devs;
 818         }
 819 
 820         einfo_curr = find_einfo(bblock_curr->extra);
 821         if (einfo_curr != NULL)
 822                 updt_str = einfo_get_string(einfo_curr);
 823 
 824         retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
 825         cleanup_bootblock(bblock_curr);
 826         cleanup_bootblock(bblock_attach);
 827 out_devs:
 828         cleanup_device(attach_device);
 829 out_currdev:
 830         cleanup_device(curr_device);
 831 out:
 832         free(curr_device_path);
 833         free(attach_device_path);
 834         return (retval);
 835 }
 836 
 837 #define USAGE_STRING    "Usage: %s [-h|-f|-F fstype|-u verstr] bootblk "       \
 838                         "raw-device\n"                                         \
 839                         "\t%s [-e|-V] -i -F zfs raw-device\n"                  \
 840                         "\t%s -M -F zfs raw-device attach-raw-device\n"        \
 841                         "\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n"
 842 
 843 #define CANON_USAGE_STR gettext(USAGE_STRING)
 844 
 845 static void
 846 usage(char *progname)
 847 {
 848         (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
 849 }
 850 
 851 int
 852 main(int argc, char **argv)
 853 {
 854         int     opt;
 855         int     params = 2;
 856         int     ret;
 857         char    *progname;
 858         char    **handle_args;
 859 
 860         (void) setlocale(LC_ALL, "");
 861         (void) textdomain(TEXT_DOMAIN);
 862 
 863         while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) {
 864                 switch (opt) {
 865                 case 'F':
 866                         if (strcmp(optarg, "ufs") == 0) {
 867                                 tgt_fs_type = TARGET_IS_UFS;
 868                         } else if (strcmp(optarg, "hsfs") == 0) {
 869                                 tgt_fs_type = TARGET_IS_HSFS;
 870                         } else if (strcmp(optarg, "zfs") == 0) {
 871                                 tgt_fs_type = TARGET_IS_ZFS;
 872                         } else {
 873                                 (void) fprintf(stderr, gettext("Wrong "
 874                                     "filesystem specified\n\n"));
 875                                 usage(argv[0]);
 876                                 exit(BC_ERROR);
 877                         }
 878                         break;
 879                 case 'e':
 880                         strip = B_TRUE;
 881                         break;
 882                 case 'f':
 883                         force_update = B_TRUE;
 884                         break;
 885                 case 'V':
 886                         verbose_dump = B_TRUE;
 887                         break;
 888                 case 'i':
 889                         do_getinfo = B_TRUE;
 890                         params = 1;
 891                         break;
 892                 case 'u':
 893                         do_version = B_TRUE;
 894 
 895                         update_str = malloc(strlen(optarg) + 1);
 896                         if (update_str == NULL) {
 897                                 perror(gettext("Memory allocation failure"));
 898                                 exit(BC_ERROR);
 899                         }
 900                         (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
 901                         break;
 902                 case 'M':
 903                         do_mirror_bblk = B_TRUE;
 904                         break;
 905                 case 'h':
 906                         usage(argv[0]);
 907                         exit(BC_SUCCESS);
 908                         break;
 909                 case 'd':
 910                         boot_debug = B_TRUE;
 911                         break;
 912                 case 'n':
 913                         nowrite = B_TRUE;
 914                         break;
 915                 default:
 916                         /* fall through to process non-optional args */
 917                         break;
 918                 }
 919         }
 920 
 921         /* check arguments */
 922         if (argc != optind + params) {
 923                 usage(argv[0]);
 924                 exit(BC_ERROR);
 925         }
 926         progname = argv[0];
 927         handle_args = argv + optind;
 928 
 929         /* check options. */
 930         if (do_getinfo && do_mirror_bblk) {
 931                 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
 932                     "specified at the same time\n"));
 933                 usage(progname);
 934                 exit(BC_ERROR);
 935         }
 936 
 937         if (nowrite)
 938                 (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
 939                     " be written to disk.\n"));
 940 
 941         if (do_getinfo) {
 942                 ret = handle_getinfo(progname, handle_args);
 943         } else if (do_mirror_bblk) {
 944                 ret = handle_mirror(progname, handle_args);
 945         } else {
 946                 ret = handle_install(progname, handle_args);
 947         }
 948         return (ret);
 949 }