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  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  24  * Copyright 2017 Toomas Soome <tsoome@me.com>
  25  */
  26 
  27 #include <stdio.h>
  28 #include <errno.h>
  29 #include <unistd.h>
  30 #include <fcntl.h>
  31 #include <assert.h>
  32 #include <locale.h>
  33 #include <strings.h>
  34 #include <libfdisk.h>
  35 #include <libgen.h>
  36 
  37 #include <sys/dktp/fdisk.h>
  38 #include <sys/dkio.h>
  39 #include <sys/vtoc.h>
  40 #include <sys/multiboot.h>
  41 #include <sys/types.h>
  42 #include <sys/stat.h>
  43 #include <sys/sysmacros.h>
  44 #include <sys/efi_partition.h>
  45 #include <libfstyp.h>
  46 #include <uuid/uuid.h>
  47 
  48 #include "installboot.h"
  49 #include "../../common/bblk_einfo.h"
  50 #include "../../common/boot_utils.h"
  51 #include "../../common/mboot_extra.h"
  52 #include "getresponse.h"
  53 
  54 #ifndef TEXT_DOMAIN
  55 #define TEXT_DOMAIN     "SUNW_OST_OSCMD"
  56 #endif
  57 
  58 /*
  59  * BIOS bootblock installation:
  60  *
  61  * 1. MBR is first sector of the disk. If the file system on target is
  62  *    ufs or zfs, the same MBR code is installed on first sector of the
  63  *    partition as well; this will allow to have real MBR sector to be
  64  *    replaced by some other boot loader and have illumos chainloaded.
  65  *
  66  * installboot will record the start LBA and size of stage2 code in MBR code.
  67  * On boot, the MBR code will read the stage2 code and executes it.
  68  *
  69  * 2. Stage2 location depends on file system type;
  70  *    In case of zfs, installboot will store stage2 to zfs bootblk area,
  71  *    which is 512k bytes from partition start and size is 3.5MB.
  72  *
  73  *    In case of ufs, the stage2 location is 50 512B sectors from
  74  *    Solaris2 MBR partition start, within boot slice, boot slice size is
  75  *    one cylinder.
  76  *
  77  *    In case of pcfs, the stage2 location is 50 512B sectors from beginning
  78  *    of the disk, filling the space between MBR and first partition.
  79  *    This location assumes no other bootloader and the space is one cylinder,
  80  *    as first partition is starting from cylinder 1.
  81  *
  82  *    In case of GPT partitioning and if file system is not zfs, the boot
  83  *    support is only possible with dedicated boot partition. For GPT,
  84  *    the current implementation is using BOOT partition, which must exist.
  85  *    BOOT partition does only contain raw boot blocks, without any file system.
  86  *
  87  * Loader stage2 is created with embedded version, by using fake multiboot (MB)
  88  * header within first 32k and EINFO block is at the end of the actual
  89  * boot block. MB header load_addr is set to 0 and load_end_addr is set to
  90  * actual block end, so the EINFO size is (file size - load_end_addr).
  91  * installboot does also store the illumos boot partition LBA to MB space,
  92  * starting from bss_end_addr structure member location; stage2 will
  93  * detect the partition and file system based on this value.
  94  *
  95  * Stored location values in MBR/stage2 also mean the bootblocks must be
  96  * reinstalled in case the partition content is relocated.
  97  *
  98  * EFI boot program installation:
  99  * EFI boot requires EFI System partition with following directory
 100  * hierarchy:
 101  * EFI/VENDOR/file
 102  * EFI/BOOT/BOOTx64.EFI
 103  * Where EFI/BOOT is fallback directory. While we have no mechanism to set
 104  * EFI variable values to define vendor specific boot program location, we
 105  * will just use EFI/BOOT/BOOTx64.EFI; also this setup is only possible
 106  * solution for removable media.
 107  * For now the boot1.efi is used for boot program, boot1.efi is also
 108  * versioned as is gptzfsboot.
 109  */
 110 
 111 static boolean_t        write_mbr = B_FALSE;
 112 static boolean_t        force_mbr = B_FALSE;
 113 static boolean_t        force_update = B_FALSE;
 114 static boolean_t        do_getinfo = B_FALSE;
 115 static boolean_t        do_version = B_FALSE;
 116 static boolean_t        do_mirror_bblk = B_FALSE;
 117 static boolean_t        strip = B_FALSE;
 118 static boolean_t        verbose_dump = B_FALSE;
 119 
 120 #define EFIBOOT64       "bootx64.efi"
 121 #define EFIBOOT32       "bootia32.efi"
 122 
 123 /* Versioning string, if present. */
 124 static char             *update_str;
 125 
 126 /*
 127  * Temporary buffer to store the first 32K of data looking for a multiboot
 128  * signature.
 129  */
 130 char                    mboot_scan[MBOOT_SCAN_SIZE];
 131 
 132 /* Function prototypes. */
 133 static void check_options(char *);
 134 static int get_start_sector(ib_device_t *);
 135 
 136 static int read_stage1_from_file(char *, ib_data_t *);
 137 static int read_bootblock_from_file(char *, ib_bootblock_t *);
 138 static int read_bootblock_from_disk(ib_device_t *, ib_bootblock_t *, char **);
 139 static void add_bootblock_einfo(ib_bootblock_t *, char *);
 140 static int prepare_stage1(ib_data_t *);
 141 static int prepare_bootblock(ib_data_t *, char *);
 142 static int write_stage1(ib_data_t *);
 143 static int write_bootblock(ib_data_t *);
 144 static int init_device(ib_device_t *, char *);
 145 static void cleanup_device(ib_device_t *);
 146 static int commit_to_disk(ib_data_t *, char *);
 147 static int handle_install(char *, char **);
 148 static int handle_getinfo(char *, char **);
 149 static int handle_mirror(char *, char **);
 150 static boolean_t is_update_necessary(ib_data_t *, char *);
 151 static int propagate_bootblock(ib_data_t *, ib_data_t *, char *);
 152 static void usage(char *);
 153 
 154 static int
 155 read_stage1_from_file(char *path, ib_data_t *dest)
 156 {
 157         int     fd;
 158 
 159         assert(dest != NULL);
 160 
 161         /* read the stage1 file from filesystem */
 162         fd = open(path, O_RDONLY);
 163         if (fd == -1 ||
 164             read(fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) {
 165                 (void) fprintf(stderr, gettext("cannot read stage1 file %s\n"),
 166                     path);
 167                 return (BC_ERROR);
 168         }
 169         (void) close(fd);
 170         return (BC_SUCCESS);
 171 }
 172 
 173 static int
 174 read_bootblock_from_file(char *file, ib_bootblock_t *bblock)
 175 {
 176         struct stat     sb;
 177         uint32_t        buf_size;
 178         uint32_t        mboot_off;
 179         int             fd = -1;
 180         int             retval = BC_ERROR;
 181 
 182         assert(bblock != NULL);
 183         assert(file != NULL);
 184 
 185         fd = open(file, O_RDONLY);
 186         if (fd == -1) {
 187                 BOOT_DEBUG("Error opening %s\n", file);
 188                 perror("open");
 189                 goto out;
 190         }
 191 
 192         if (fstat(fd, &sb) == -1) {
 193                 BOOT_DEBUG("Error getting information (stat) about %s", file);
 194                 perror("stat");
 195                 goto outfd;
 196         }
 197 
 198         /* loader bootblock has version built in */
 199         buf_size = sb.st_size;
 200 
 201         bblock->buf_size = buf_size;
 202         BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
 203             bblock->buf_size);
 204 
 205         bblock->buf = malloc(buf_size);
 206         if (bblock->buf == NULL) {
 207                 perror(gettext("Memory allocation failure"));
 208                 goto outbuf;
 209         }
 210         bblock->file = bblock->buf;
 211 
 212         if (read(fd, bblock->file, bblock->buf_size) != bblock->buf_size) {
 213                 BOOT_DEBUG("Read from %s failed\n", file);
 214                 perror("read");
 215                 goto outfd;
 216         }
 217 
 218         if (find_multiboot(bblock->file, MBOOT_SCAN_SIZE, &mboot_off)
 219             != BC_SUCCESS) {
 220                 (void) fprintf(stderr,
 221                     gettext("Unable to find multiboot header\n"));
 222                 goto outfd;
 223         }
 224 
 225         bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off);
 226         bblock->mboot_off = mboot_off;
 227 
 228         bblock->file_size =
 229             bblock->mboot->load_end_addr - bblock->mboot->load_addr;
 230         BOOT_DEBUG("bootblock file size is %d\n", bblock->file_size);
 231 
 232         bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
 233         bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
 234 
 235         BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
 236             "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
 237             bblock->extra_size, bblock->buf, bblock->buf_size);
 238 
 239         (void) close(fd);
 240         return (BC_SUCCESS);
 241 
 242 outbuf:
 243         (void) free(bblock->buf);
 244         bblock->buf = NULL;
 245 outfd:
 246         (void) close(fd);
 247 out:
 248         return (retval);
 249 }
 250 
 251 static int
 252 read_bootblock_from_disk(ib_device_t *device, ib_bootblock_t *bblock,
 253     char **path)
 254 {
 255         int                     dev_fd;
 256         uint32_t                size, offset;
 257         uint32_t                buf_size;
 258         uint32_t                mboot_off;
 259         multiboot_header_t      *mboot;
 260 
 261         assert(device != NULL);
 262         assert(bblock != NULL);
 263 
 264         if (device->target.fstype == IG_FS_ZFS) {
 265                 dev_fd = device->target.fd;
 266                 offset = BBLK_ZFS_BLK_OFF * SECTOR_SIZE;
 267                 *path = device->target.path;
 268         } else {
 269                 dev_fd = device->stage.fd;
 270                 offset = device->stage.offset * SECTOR_SIZE;
 271                 *path = device->stage.path;
 272         }
 273 
 274         if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan), offset)
 275             != BC_SUCCESS) {
 276                 BOOT_DEBUG("Error reading bootblock area\n");
 277                 perror("read");
 278                 return (BC_ERROR);
 279         }
 280 
 281         /* No multiboot means no chance of knowing bootblock size */
 282         if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
 283             != BC_SUCCESS) {
 284                 BOOT_DEBUG("Unable to find multiboot header\n");
 285                 return (BC_NOEXTRA);
 286         }
 287         mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
 288 
 289         /*
 290          * make sure mboot has sane values
 291          */
 292         if (mboot->load_end_addr == 0 ||
 293             mboot->load_end_addr < mboot->load_addr)
 294                 return (BC_NOEXTRA);
 295 
 296         /*
 297          * Currently, the amount of space reserved for extra information
 298          * is "fixed". We may have to scan for the terminating extra payload
 299          * in the future.
 300          */
 301         size = mboot->load_end_addr - mboot->load_addr;
 302         buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
 303         bblock->file_size = size;
 304 
 305         bblock->buf = malloc(buf_size);
 306         if (bblock->buf == NULL) {
 307                 BOOT_DEBUG("Unable to allocate enough memory to read"
 308                     " the extra bootblock from the disk\n");
 309                 perror(gettext("Memory allocation failure"));
 310                 return (BC_ERROR);
 311         }
 312         bblock->buf_size = buf_size;
 313 
 314         if (read_in(dev_fd, bblock->buf, buf_size, offset) != BC_SUCCESS) {
 315                 BOOT_DEBUG("Error reading the bootblock\n");
 316                 (void) free(bblock->buf);
 317                 bblock->buf = NULL;
 318                 return (BC_ERROR);
 319         }
 320 
 321         /* Update pointers. */
 322         bblock->file = bblock->buf;
 323         bblock->mboot_off = mboot_off;
 324         bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off);
 325         bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
 326         bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
 327 
 328         BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
 329             "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
 330             bblock->extra_size, bblock->buf, bblock->buf_size);
 331 
 332         return (BC_SUCCESS);
 333 }
 334 
 335 static boolean_t
 336 is_update_necessary(ib_data_t *data, char *updt_str)
 337 {
 338         bblk_einfo_t    *einfo;
 339         bblk_einfo_t    *einfo_file;
 340         bblk_hs_t       bblock_hs;
 341         ib_bootblock_t  bblock_disk;
 342         ib_bootblock_t  *bblock_file = &data->bootblock;
 343         ib_device_t     *device = &data->device;
 344         int             ret;
 345         char            *path;
 346 
 347         assert(data != NULL);
 348 
 349         bzero(&bblock_disk, sizeof (ib_bootblock_t));
 350 
 351         ret = read_bootblock_from_disk(device, &bblock_disk, &path);
 352         if (ret != BC_SUCCESS) {
 353                 BOOT_DEBUG("Unable to read bootblock from %s\n", path);
 354                 return (B_TRUE);
 355         }
 356 
 357         einfo = find_einfo(bblock_disk.extra, bblock_disk.extra_size);
 358         if (einfo == NULL) {
 359                 BOOT_DEBUG("No extended information available on disk\n");
 360                 return (B_TRUE);
 361         }
 362 
 363         einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size);
 364         if (einfo_file == NULL) {
 365                 /*
 366                  * loader bootblock is versioned. missing version means
 367                  * probably incompatible block. installboot can not install
 368                  * grub, for example.
 369                  */
 370                 (void) fprintf(stderr,
 371                     gettext("ERROR: non versioned bootblock in file\n"));
 372                 return (B_FALSE);
 373         } else {
 374                 if (updt_str == NULL) {
 375                         updt_str = einfo_get_string(einfo_file);
 376                         do_version = B_TRUE;
 377                 }
 378         }
 379 
 380         if (!do_version || updt_str == NULL) {
 381                 (void) fprintf(stderr,
 382                     gettext("WARNING: target device %s has a "
 383                     "versioned bootblock that is going to be overwritten by a "
 384                     "non versioned one\n"), device->path);
 385                 return (B_TRUE);
 386         }
 387 
 388         if (force_update) {
 389                 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
 390                 return (B_TRUE);
 391         }
 392 
 393         BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
 394 
 395         bblock_hs.src_buf = (unsigned char *)bblock_file->file;
 396         bblock_hs.src_size = bblock_file->file_size;
 397 
 398         return (einfo_should_update(einfo, &bblock_hs, updt_str));
 399 }
 400 
 401 static void
 402 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
 403 {
 404         bblk_hs_t       hs;
 405         uint32_t        avail_space;
 406 
 407         assert(bblock != NULL);
 408 
 409         if (updt_str == NULL) {
 410                 BOOT_DEBUG("WARNING: no update string passed to "
 411                     "add_bootblock_einfo()\n");
 412                 return;
 413         }
 414 
 415         /* Fill bootblock hashing source information. */
 416         hs.src_buf = (unsigned char *)bblock->file;
 417         hs.src_size = bblock->file_size;
 418         /* How much space for the extended information structure? */
 419         avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
 420         /* Place the extended information structure. */
 421         add_einfo(bblock->extra, updt_str, &hs, avail_space);
 422 }
 423 
 424 /*
 425  * set up data for case stage1 is installed as MBR
 426  * set up location and size of bootblock
 427  * set disk guid to provide unique information for biosdev command
 428  */
 429 static int
 430 prepare_stage1(ib_data_t *data)
 431 {
 432         ib_device_t     *device;
 433 
 434         assert(data != NULL);
 435         device = &data->device;
 436 
 437         /* copy BPB */
 438         bcopy(device->mbr + STAGE1_BPB_OFFSET,
 439             data->stage1 + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
 440 
 441 
 442         /* copy MBR, note STAGE1_SIG == BOOTSZ */
 443         bcopy(device->mbr + STAGE1_SIG, data->stage1 + STAGE1_SIG,
 444             SECTOR_SIZE - STAGE1_SIG);
 445 
 446         /* set stage2 size */
 447         *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) =
 448             (uint16_t)(data->bootblock.buf_size / SECTOR_SIZE);
 449 
 450         /*
 451          * set stage2 location.
 452          * for zfs always use zfs embedding, for ufs/pcfs use partition_start
 453          * as base for stage2 location, for ufs/pcfs in MBR partition, use
 454          * free space after MBR record.
 455          */
 456         if (device->target.fstype == IG_FS_ZFS)
 457                 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
 458                     device->target.start + device->target.offset;
 459         else {
 460                 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
 461                     device->stage.start + device->stage.offset;
 462         }
 463 
 464         /*
 465          * set disk uuid. we only need reasonable amount of uniqueness
 466          * to allow biosdev to identify disk based on mbr differences.
 467          */
 468         uuid_generate(data->stage1 + STAGE1_STAGE2_UUID);
 469 
 470         return (BC_SUCCESS);
 471 }
 472 
 473 static int
 474 prepare_bootblock(ib_data_t *data, char *updt_str)
 475 {
 476         ib_bootblock_t          *bblock;
 477         ib_device_t             *device;
 478         uint64_t                *ptr;
 479 
 480         assert(data != NULL);
 481 
 482         bblock = &data->bootblock;
 483         device = &data->device;
 484 
 485         ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
 486         *ptr = device->target.start;
 487 
 488         /*
 489          * the loader bootblock has built in version, if custom
 490          * version was provided, update it.
 491          */
 492         if (do_version)
 493                 add_bootblock_einfo(bblock, updt_str);
 494 
 495         return (BC_SUCCESS);
 496 }
 497 
 498 static int
 499 write_bootblock(ib_data_t *data)
 500 {
 501         ib_device_t     *device = &data->device;
 502         ib_bootblock_t  *bblock = &data->bootblock;
 503         uint64_t abs;
 504         int dev_fd, ret;
 505         off_t offset;
 506         char *path;
 507 
 508         assert(data != NULL);
 509 
 510         /*
 511          * ZFS bootblock area is 3.5MB, make sure we can fit.
 512          * buf_size is size of bootblk+EINFO.
 513          */
 514         if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) {
 515                 (void) fprintf(stderr, gettext("bootblock is too large\n"));
 516                 return (BC_ERROR);
 517         }
 518 
 519         if (device->target.fstype == IG_FS_ZFS) {
 520                 dev_fd = device->target.fd;
 521                 abs = device->target.start + device->target.offset;
 522                 offset = BBLK_ZFS_BLK_OFF * SECTOR_SIZE;
 523                 path = device->target.path;
 524         } else {
 525                 dev_fd = device->stage.fd;
 526                 abs = device->stage.start + device->stage.offset;
 527                 offset = device->stage.offset * SECTOR_SIZE;
 528                 path = device->stage.path;
 529                 if (bblock->buf_size >
 530                     (device->stage.size - device->stage.offset) * SECTOR_SIZE) {
 531                         (void) fprintf(stderr, gettext("Device %s is "
 532                             "too small to fit the stage2\n"), path);
 533                         return (BC_ERROR);
 534                 }
 535         }
 536         ret = write_out(dev_fd, bblock->buf, bblock->buf_size, offset);
 537         if (ret != BC_SUCCESS) {
 538                 BOOT_DEBUG("Error writing the ZFS bootblock "
 539                     "to %s at offset %d\n", path, offset);
 540                 return (BC_ERROR);
 541         }
 542 
 543         (void) fprintf(stdout, gettext("bootblock written for %s,"
 544             " %d sectors starting at %d (abs %lld)\n"), path,
 545             (bblock->buf_size / SECTOR_SIZE) + 1, offset / SECTOR_SIZE, abs);
 546 
 547         return (BC_SUCCESS);
 548 }
 549 
 550 /*
 551  * Partition boot block or volume boot record (VBR). The VBR is
 552  * stored on partition relative sector 0 and allows chainloading
 553  * to read boot program from partition.
 554  *
 555  * As the VBR will use the first sector of the partition,
 556  * this means, we need to be sure the space is not used.
 557  * We do support three partitioning chemes:
 558  * 1. GPT: zfs and ufs have reserved space for first 8KB, but
 559  *      only zfs does have space for boot2. The pcfs has support
 560  *      for VBR, but no space for boot2. So with GPT, to support
 561  *      ufs or pcfs boot, we must have separate dedicated boot
 562  *      partition and we will store VBR on it.
 563  * 2. MBR: we have almost the same situation as with GPT, except that
 564  *      if the partitions start from cylinder 1, we will have space
 565  *      between MBR and cylinder 0. If so, we do not require separate
 566  *      boot partition.
 567  * 3. MBR+VTOC: with this combination we store VBR in sector 0 of the
 568  *      solaris2 MBR partition. The slice 0 will start from cylinder 1,
 569  *      and we do have space for boot2, so we do not require separate
 570  *      boot partition.
 571  */
 572 static int
 573 write_stage1(ib_data_t *data)
 574 {
 575         ib_device_t     *device = &data->device;
 576         uint64_t        start = 0;
 577 
 578         assert(data != NULL);
 579 
 580         /*
 581          * We have separate partition for boot programs and the stage1
 582          * location is not absolute sector 0.
 583          * We will write VBR and trigger MBR to read 1 sector from VBR.
 584          * This case does also cover MBR+VTOC case, as the solaris 2 partition
 585          * name and the root file system slice names are different.
 586          */
 587         if (device->stage.start != 0 &&
 588             strcmp(device->target.path, device->stage.path)) {
 589                 /* we got separate stage area, use it */
 590                 if (write_out(device->stage.fd, data->stage1,
 591                     sizeof (data->stage1), 0) != BC_SUCCESS) {
 592                         (void) fprintf(stdout, gettext("cannot write "
 593                             "partition boot sector\n"));
 594                         perror("write");
 595                         return (BC_ERROR);
 596                 }
 597 
 598                 (void) fprintf(stdout, gettext("stage1 written to "
 599                     "%s %d sector 0 (abs %d)\n"),
 600                     device->devtype == IG_DEV_MBR? "partition":"slice",
 601                     device->stage.id, device->stage.start);
 602                 start = device->stage.start;
 603         }
 604 
 605         /*
 606          * We have either GPT or MBR (without VTOC) and if the root
 607          * file system is not pcfs, we can store VBR. Also trigger
 608          * MBR to read 1 sector from VBR.
 609          */
 610         if (device->devtype != IG_DEV_VTOC &&
 611             device->target.fstype != IG_FS_PCFS) {
 612                 if (write_out(device->target.fd, data->stage1,
 613                     sizeof (data->stage1), 0) != BC_SUCCESS) {
 614                         (void) fprintf(stdout, gettext("cannot write "
 615                             "partition boot sector\n"));
 616                         perror("write");
 617                         return (BC_ERROR);
 618                 }
 619 
 620                 (void) fprintf(stdout, gettext("stage1 written to "
 621                     "%s %d sector 0 (abs %d)\n"),
 622                     device->devtype == IG_DEV_MBR? "partition":"slice",
 623                     device->target.id, device->target.start);
 624                 start = device->target.start;
 625         }
 626 
 627         if (write_mbr) {
 628                 /*
 629                  * If we did write partition boot block, update MBR to
 630                  * read partition boot block, not boot2.
 631                  */
 632                 if (start != 0) {
 633                         *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) = 1;
 634                         *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
 635                             start;
 636                 }
 637                 if (write_out(device->fd, data->stage1,
 638                     sizeof (data->stage1), 0) != BC_SUCCESS) {
 639                         (void) fprintf(stdout,
 640                             gettext("cannot write master boot sector\n"));
 641                         perror("write");
 642                         return (BC_ERROR);
 643                 }
 644                 (void) fprintf(stdout,
 645                     gettext("stage1 written to master boot sector\n"));
 646         }
 647 
 648         return (BC_SUCCESS);
 649 }
 650 
 651 /*
 652  * find partition/slice start sector. will be recorded in stage2 and used
 653  * by stage2 to identify partition with boot file system.
 654  */
 655 static int
 656 get_start_sector(ib_device_t *device)
 657 {
 658         uint32_t                secnum = 0, numsec = 0;
 659         int                     i, pno, rval, log_part = 0;
 660         struct mboot            *mboot;
 661         struct ipart            *part = NULL;
 662         ext_part_t              *epp;
 663         struct part_info        dkpi;
 664         struct extpart_info     edkpi;
 665 
 666         if (device->devtype == IG_DEV_EFI) {
 667                 struct dk_gpt *vtoc;
 668 
 669                 if (efi_alloc_and_read(device->fd, &vtoc) < 0)
 670                         return (BC_ERROR);
 671 
 672                 if (device->stage.start == 0) {
 673                         /* zero size means the fstype must be zfs */
 674                         assert(device->target.fstype == IG_FS_ZFS);
 675 
 676                         device->stage.start =
 677                             vtoc->efi_parts[device->stage.id].p_start;
 678                         device->stage.size =
 679                             vtoc->efi_parts[device->stage.id].p_size;
 680                         device->stage.offset = BBLK_ZFS_BLK_OFF;
 681                         device->target.offset = BBLK_ZFS_BLK_OFF;
 682                 }
 683 
 684                 device->target.start =
 685                     vtoc->efi_parts[device->target.id].p_start;
 686                 device->target.size =
 687                     vtoc->efi_parts[device->target.id].p_size;
 688 
 689                 /* with pcfs we always write MBR */
 690                 if (device->target.fstype == IG_FS_PCFS) {
 691                         force_mbr = 1;
 692                         write_mbr = 1;
 693                 }
 694 
 695                 efi_free(vtoc);
 696                 goto found_part;
 697         }
 698 
 699         mboot = (struct mboot *)device->mbr;
 700 
 701         /* For MBR we have device->stage filled already. */
 702         if (device->devtype == IG_DEV_MBR) {
 703                 /* MBR partition starts from 0 */
 704                 pno = device->target.id - 1;
 705                 part = (struct ipart *)mboot->parts + pno;
 706 
 707                 if (part->relsect == 0) {
 708                         (void) fprintf(stderr, gettext("Partition %d of the "
 709                             "disk has an incorrect offset\n"),
 710                             device->target.id);
 711                         return (BC_ERROR);
 712                 }
 713                 device->target.start = part->relsect;
 714                 device->target.size = part->numsect;
 715 
 716                 /* with pcfs we always write MBR */
 717                 if (device->target.fstype == IG_FS_PCFS) {
 718                         force_mbr = 1;
 719                         write_mbr = 1;
 720                 }
 721                 if (device->target.fstype == IG_FS_ZFS)
 722                         device->target.offset = BBLK_ZFS_BLK_OFF;
 723 
 724                 goto found_part;
 725         }
 726 
 727         /*
 728          * Search for Solaris fdisk partition
 729          * Get the solaris partition information from the device
 730          * and compare the offset of S2 with offset of solaris partition
 731          * from fdisk partition table.
 732          */
 733         if (ioctl(device->target.fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
 734                 if (ioctl(device->target.fd, DKIOCPARTINFO, &dkpi) < 0) {
 735                         (void) fprintf(stderr, gettext("cannot get the "
 736                             "slice information of the disk\n"));
 737                         return (BC_ERROR);
 738                 } else {
 739                         edkpi.p_start = dkpi.p_start;
 740                         edkpi.p_length = dkpi.p_length;
 741                 }
 742         }
 743 
 744         device->target.start = edkpi.p_start;
 745         device->target.size = edkpi.p_length;
 746         if (device->target.fstype == IG_FS_ZFS)
 747                 device->target.offset = BBLK_ZFS_BLK_OFF;
 748 
 749         for (i = 0; i < FD_NUMPART; i++) {
 750                 part = (struct ipart *)mboot->parts + i;
 751 
 752                 if (part->relsect == 0) {
 753                         (void) fprintf(stderr, gettext("Partition %d of the "
 754                             "disk has an incorrect offset\n"), i+1);
 755                         return (BC_ERROR);
 756                 }
 757 
 758                 if (edkpi.p_start >= part->relsect &&
 759                     edkpi.p_start < (part->relsect + part->numsect)) {
 760                         /* Found the partition */
 761                         break;
 762                 }
 763         }
 764 
 765         if (i == FD_NUMPART) {
 766                 /* No solaris fdisk partitions (primary or logical) */
 767                 (void) fprintf(stderr, gettext("Solaris partition not found. "
 768                     "Aborting operation.\n"));
 769                 return (BC_ERROR);
 770         }
 771 
 772         /*
 773          * We have found a Solaris fdisk partition (primary or extended)
 774          * Handle the simple case first: Solaris in a primary partition
 775          */
 776         if (!fdisk_is_dos_extended(part->systid)) {
 777                 device->stage.start = part->relsect;
 778                 device->stage.size = part->numsect;
 779                 if (device->target.fstype == IG_FS_ZFS)
 780                         device->stage.offset = BBLK_ZFS_BLK_OFF;
 781                 else
 782                         device->stage.offset = BBLK_BLKLIST_OFF;
 783                 device->stage.id = i + 1;
 784                 goto found_part;
 785         }
 786 
 787         /*
 788          * Solaris in a logical partition. Find that partition in the
 789          * extended part.
 790          */
 791 
 792         if ((rval = libfdisk_init(&epp, device->path, NULL, FDISK_READ_DISK))
 793             != FDISK_SUCCESS) {
 794                 switch (rval) {
 795                         /*
 796                          * The first 3 cases are not an error per-se, just that
 797                          * there is no Solaris logical partition
 798                          */
 799                         case FDISK_EBADLOGDRIVE:
 800                         case FDISK_ENOLOGDRIVE:
 801                         case FDISK_EBADMAGIC:
 802                                 (void) fprintf(stderr, gettext("Solaris "
 803                                     "partition not found. "
 804                                     "Aborting operation.\n"));
 805                                 return (BC_ERROR);
 806                         case FDISK_ENOVGEOM:
 807                                 (void) fprintf(stderr, gettext("Could not get "
 808                                     "virtual geometry\n"));
 809                                 return (BC_ERROR);
 810                         case FDISK_ENOPGEOM:
 811                                 (void) fprintf(stderr, gettext("Could not get "
 812                                     "physical geometry\n"));
 813                                 return (BC_ERROR);
 814                         case FDISK_ENOLGEOM:
 815                                 (void) fprintf(stderr, gettext("Could not get "
 816                                     "label geometry\n"));
 817                                 return (BC_ERROR);
 818                         default:
 819                                 (void) fprintf(stderr, gettext("Failed to "
 820                                     "initialize libfdisk.\n"));
 821                                 return (BC_ERROR);
 822                 }
 823         }
 824 
 825         rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
 826         libfdisk_fini(&epp);
 827         if (rval != FDISK_SUCCESS) {
 828                 /* No solaris logical partition */
 829                 (void) fprintf(stderr, gettext("Solaris partition not found. "
 830                     "Aborting operation.\n"));
 831                 return (BC_ERROR);
 832         }
 833 
 834         device->stage.start = secnum;
 835         device->stage.size = numsec;
 836         device->stage.id = pno;
 837         log_part = 1;
 838 
 839 found_part:
 840         /* get confirmation for -m */
 841         if (write_mbr && !force_mbr) {
 842                 (void) fprintf(stdout, gettext("Updating master boot sector "
 843                     "destroys existing boot managers (if any).\n"
 844                     "continue (y/n)? "));
 845                 if (!yes()) {
 846                         write_mbr = 0;
 847                         (void) fprintf(stdout, gettext("master boot sector "
 848                             "not updated\n"));
 849                         return (BC_ERROR);
 850                 }
 851         }
 852 
 853         /*
 854          * warn, if illumos in primary partition and loader not in MBR and
 855          * partition is not active
 856          */
 857         if (device->devtype != IG_DEV_EFI) {
 858                 if (!log_part && part->bootid != 128 && !write_mbr) {
 859                         (void) fprintf(stdout, gettext("Solaris fdisk "
 860                             "partition is inactive.\n"), device->stage.id);
 861                 }
 862         }
 863 
 864         return (BC_SUCCESS);
 865 }
 866 
 867 static int
 868 open_device(char *path)
 869 {
 870         struct stat     statbuf = {0};
 871         int             fd = -1;
 872 
 873         if (nowrite)
 874                 fd = open(path, O_RDONLY);
 875         else
 876                 fd = open(path, O_RDWR);
 877 
 878         if (fd == -1) {
 879                 BOOT_DEBUG("Unable to open %s\n", path);
 880                 perror("open");
 881                 return (-1);
 882         }
 883 
 884         if (fstat(fd, &statbuf) != 0) {
 885                 BOOT_DEBUG("Unable to stat %s\n", path);
 886                 perror("stat");
 887                 (void) close(fd);
 888                 return (-1);
 889         }
 890 
 891         if (S_ISCHR(statbuf.st_mode) == 0) {
 892                 (void) fprintf(stderr, gettext("%s: Not a character device\n"),
 893                     path);
 894                 (void) close(fd);
 895                 return (-1);
 896         }
 897 
 898         return (fd);
 899 }
 900 
 901 static int
 902 get_boot_partition(ib_device_t *device, struct mboot *mbr)
 903 {
 904         struct ipart *part;
 905         char *path, *ptr;
 906         int i;
 907 
 908         part = (struct ipart *)mbr->parts;
 909         for (i = 0; i < FD_NUMPART; i++) {
 910                 if (part[i].systid == X86BOOT)
 911                         break;
 912         }
 913 
 914         /* no X86BOOT, try to use space between MBR and first partition */
 915         if (i == FD_NUMPART) {
 916                 device->stage.path = strdup(device->path);
 917                 if (device->stage.path == NULL) {
 918                         perror(gettext("Memory allocation failure"));
 919                         return (BC_ERROR);
 920                 }
 921                 device->stage.fd = dup(device->fd);
 922                 device->stage.id = 0;
 923                 device->stage.devtype = IG_DEV_MBR;
 924                 device->stage.fstype = IG_FS_NONE;
 925                 device->stage.start = 0;
 926                 device->stage.size = part[0].relsect;
 927                 device->stage.offset = BBLK_BLKLIST_OFF;
 928                 return (BC_SUCCESS);
 929         }
 930 
 931         if ((path = strdup(device->path)) == NULL) {
 932                 perror(gettext("Memory allocation failure"));
 933                 return (BC_ERROR);
 934         }
 935 
 936         ptr = strrchr(path, 'p');
 937         ptr++;
 938         *ptr = '\0';
 939         (void) asprintf(&ptr, "%s%d", path, i+1); /* partitions are p1..p4 */
 940         free(path);
 941         if (ptr == NULL) {
 942                 perror(gettext("Memory allocation failure"));
 943                 return (BC_ERROR);
 944         }
 945         device->stage.path = ptr;
 946         device->stage.fd = open_device(ptr);
 947         device->stage.id = i + 1;
 948         device->stage.devtype = IG_DEV_MBR;
 949         device->stage.fstype = IG_FS_NONE;
 950         device->stage.start = part[i].relsect;
 951         device->stage.size = part[i].numsect;
 952         device->stage.offset = 1; /* leave sector 0 for VBR */
 953         return (BC_SUCCESS);
 954 }
 955 
 956 static int
 957 get_boot_slice(ib_device_t *device, struct dk_gpt *vtoc)
 958 {
 959         uint_t i;
 960         char *path, *ptr;
 961 
 962         for (i = 0; i < vtoc->efi_nparts; i++) {
 963                 if (vtoc->efi_parts[i].p_tag == V_SYSTEM) {
 964                         if ((path = strdup(device->target.path)) == NULL) {
 965                                 perror(gettext("Memory allocation failure"));
 966                                 return (BC_ERROR);
 967                         }
 968                         ptr = strrchr(path, 's');
 969                         ptr++;
 970                         *ptr = '\0';
 971                         (void) asprintf(&ptr, "%s%d", path, i);
 972                         free(path);
 973                         if (ptr == NULL) {
 974                                 perror(gettext("Memory allocation failure"));
 975                                 return (BC_ERROR);
 976                         }
 977                         device->system.path = ptr;
 978                         device->system.fd = open_device(ptr);
 979                         device->system.id = i;
 980                         device->system.devtype = IG_DEV_EFI;
 981                         device->system.fstype = IG_FS_PCFS;
 982                         device->system.start = vtoc->efi_parts[i].p_start;
 983                         device->system.size = vtoc->efi_parts[i].p_size;
 984                         device->system.offset = 0;
 985                 } else if (vtoc->efi_parts[i].p_tag == V_BOOT) {
 986                         if ((path = strdup(device->target.path)) == NULL) {
 987                                 perror(gettext("Memory allocation failure"));
 988                                 return (BC_ERROR);
 989                         }
 990                         ptr = strrchr(path, 's');
 991                         ptr++;
 992                         *ptr = '\0';
 993                         (void) asprintf(&ptr, "%s%d", path, i);
 994                         free(path);
 995                         if (ptr == NULL) {
 996                                 perror(gettext("Memory allocation failure"));
 997                                 return (BC_ERROR);
 998                         }
 999                         device->stage.path = ptr;
1000                         device->stage.fd = open_device(ptr);
1001                         device->stage.id = i;
1002                         device->stage.devtype = IG_DEV_EFI;
1003                         device->stage.fstype = IG_FS_NONE;
1004                         device->stage.start = vtoc->efi_parts[i].p_start;
1005                         device->stage.size = vtoc->efi_parts[i].p_size;
1006                         device->stage.offset = 1; /* leave sector 0 for VBR */
1007                 }
1008         }
1009         return (BC_SUCCESS);
1010 }
1011 
1012 static int
1013 init_device(ib_device_t *device, char *path)
1014 {
1015         struct dk_gpt *vtoc;
1016         fstyp_handle_t fhdl;
1017         const char *fident;
1018         char *p;
1019         int pathlen = strlen(path);
1020         int ret;
1021 
1022         bzero(device, sizeof (*device));
1023         device->fd = -1;     /* whole disk fd */
1024         device->stage.fd = -1;       /* bootblock partition fd */
1025         device->system.fd = -1;      /* efi system partition fd */
1026         device->target.fd = -1;      /* target fs partition fd */
1027 
1028         /* basic check, whole disk is not allowed */
1029         if ((p = strrchr(path, '/')) == NULL)
1030                 p = path;
1031         if ((strrchr(p, 'p') == NULL && strrchr(p, 's') == NULL) ||
1032             (path[pathlen-2] == 'p' && path[pathlen-1] == '0')) {
1033                 (void) fprintf(stderr, gettext("installing loader to "
1034                     "whole disk device is not supported\n"));
1035         }
1036 
1037         device->target.path = strdup(path);
1038         if (device->target.path == NULL) {
1039                 perror(gettext("Memory allocation failure"));
1040                 return (BC_ERROR);
1041         }
1042         device->path = strdup(path);
1043         if (device->path == NULL) {
1044                 perror(gettext("Memory allocation failure"));
1045                 return (BC_ERROR);
1046         }
1047 
1048         /* change device name to p0 */
1049         device->path[pathlen - 2] = 'p';
1050         device->path[pathlen - 1] = '0';
1051 
1052         if (strstr(device->target.path, "diskette")) {
1053                 (void) fprintf(stderr, gettext("installing loader to a floppy "
1054                     "disk is not supported\n"));
1055                 return (BC_ERROR);
1056         }
1057 
1058         /* Detect if the target device is a pcfs partition. */
1059         if (strstr(device->target.path, "p0:boot")) {
1060                 (void) fprintf(stderr, gettext("installing loader to x86 boot "
1061                     "partition is not supported\n"));
1062                 return (BC_ERROR);
1063         }
1064 
1065         if ((device->fd = open_device(device->path)) == -1)
1066                 return (BC_ERROR);
1067 
1068         /* read in the device boot sector. */
1069         if (read(device->fd, device->mbr, SECTOR_SIZE) != SECTOR_SIZE) {
1070                 (void) fprintf(stderr, gettext("Error reading boot sector\n"));
1071                 perror("read");
1072                 return (BC_ERROR);
1073         }
1074 
1075         device->devtype = IG_DEV_VTOC;
1076         if (efi_alloc_and_read(device->fd, &vtoc) >= 0) {
1077                 ret = get_boot_slice(device, vtoc);
1078                 device->devtype = IG_DEV_EFI;
1079                 efi_free(vtoc);
1080                 if (ret == BC_ERROR)
1081                         return (BC_ERROR);
1082         } else if (device->target.path[pathlen - 2] == 'p') {
1083                 device->devtype = IG_DEV_MBR;
1084                 ret = get_boot_partition(device, (struct mboot *)device->mbr);
1085                 if (ret == BC_ERROR)
1086                         return (BC_ERROR);
1087         } else if (device->target.path[pathlen - 1] == '2') {
1088                 /*
1089                  * NOTE: we could relax there and allow zfs boot on
1090                  * slice 2 for instance, but lets keep traditional limits.
1091                  */
1092                 (void) fprintf(stderr,
1093                     gettext("raw device must be a root slice (not s2)\n"));
1094                 return (BC_ERROR);
1095         }
1096 
1097         /* fill stage partition for case there is no boot partition */
1098         if (device->stage.path == NULL) {
1099                 if ((device->stage.path = strdup(path)) == NULL) {
1100                         perror(gettext("Memory allocation failure"));
1101                         return (BC_ERROR);
1102                 }
1103                 if (device->devtype == IG_DEV_VTOC) {
1104                         /* use slice 2 */
1105                         device->stage.path[pathlen - 2] = 's';
1106                         device->stage.path[pathlen - 1] = '2';
1107                         device->stage.id = 2;
1108                 } else {
1109                         p = strrchr(device->stage.path, 'p');
1110                         if (p == NULL)
1111                                 p = strrchr(device->stage.path, 's');
1112                         device->stage.id = atoi(++p);
1113                 }
1114                 device->stage.devtype = device->devtype;
1115                 device->stage.fd = open_device(device->stage.path);
1116         }
1117 
1118         p = strrchr(device->target.path, 'p');
1119         if (p == NULL)
1120                 p = strrchr(device->target.path, 's');
1121         device->target.id = atoi(++p);
1122 
1123         if (strcmp(device->stage.path, device->target.path) == 0)
1124                 device->target.fd = dup(device->stage.fd);
1125         else
1126                 device->target.fd = open_device(device->target.path);
1127 
1128         if (fstyp_init(device->target.fd, 0, NULL, &fhdl) != 0)
1129                 return (BC_ERROR);
1130 
1131         if (fstyp_ident(fhdl, NULL, &fident) != 0) {
1132                 fstyp_fini(fhdl);
1133                 (void) fprintf(stderr, gettext("Failed to detect file "
1134                     "system type\n"));
1135                 return (BC_ERROR);
1136         }
1137 
1138         /* at this moment non-boot partition has no size set, use this fact */
1139         if (device->devtype == IG_DEV_EFI && strcmp(fident, "zfs") &&
1140             device->stage.size == 0) {
1141                 fstyp_fini(fhdl);
1142                 (void) fprintf(stderr, gettext("Booting %s of EFI labeled "
1143                     "disks requires the boot partition.\n"), fident);
1144                 return (BC_ERROR);
1145         }
1146         if (strcmp(fident, "zfs") == 0)
1147                 device->target.fstype = IG_FS_ZFS;
1148         else if (strcmp(fident, "ufs") == 0) {
1149                 device->target.fstype = IG_FS_UFS;
1150         } else if (strcmp(fident, "pcfs") == 0) {
1151                 device->target.fstype = IG_FS_PCFS;
1152         } else {
1153                 (void) fprintf(stderr, gettext("File system %s is not "
1154                     "supported by loader\n"), fident);
1155                 fstyp_fini(fhdl);
1156                 return (BC_ERROR);
1157         }
1158         fstyp_fini(fhdl);
1159 
1160         /* check for boot partition content */
1161         if (device->stage.size) {
1162                 if (fstyp_init(device->stage.fd, 0, NULL, &fhdl) != 0)
1163                         return (BC_ERROR);
1164 
1165                 if (fstyp_ident(fhdl, NULL, &fident) == 0) {
1166                         (void) fprintf(stderr, gettext("Unexpected %s file "
1167                             "system on boot partition\n"), fident);
1168                         fstyp_fini(fhdl);
1169                         return (BC_ERROR);
1170                 }
1171                 fstyp_fini(fhdl);
1172         }
1173         return (get_start_sector(device));
1174 }
1175 
1176 static void
1177 cleanup_device(ib_device_t *device)
1178 {
1179         if (device->path)
1180                 free(device->path);
1181         if (device->stage.path)
1182                 free(device->stage.path);
1183         if (device->system.path)
1184                 free(device->system.path);
1185         if (device->target.path)
1186                 free(device->target.path);
1187 
1188         if (device->fd != -1)
1189                 (void) close(device->fd);
1190         if (device->stage.fd != -1)
1191                 (void) close(device->stage.fd);
1192         if (device->system.fd != -1)
1193                 (void) close(device->system.fd);
1194         if (device->target.fd != -1)
1195                 (void) close(device->target.fd);
1196         bzero(device, sizeof (*device));
1197 }
1198 
1199 static void
1200 cleanup_bootblock(ib_bootblock_t *bblock)
1201 {
1202         free(bblock->buf);
1203         bzero(bblock, sizeof (ib_bootblock_t));
1204 }
1205 
1206 /*
1207  * Propagate the bootblock on the source disk to the destination disk and
1208  * version it with 'updt_str' in the process. Since we cannot trust any data
1209  * on the attaching disk, we do not perform any specific check on a potential
1210  * target extended information structure and we just blindly update.
1211  */
1212 static int
1213 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
1214 {
1215         ib_bootblock_t  *src_bblock = &src->bootblock;
1216         ib_bootblock_t  *dest_bblock = &dest->bootblock;
1217 
1218         assert(src != NULL);
1219         assert(dest != NULL);
1220 
1221         /* read the stage1 file from source disk */
1222         if (read(src->device.fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) {
1223                 (void) fprintf(stderr, gettext("cannot read stage1 from %s\n"),
1224                     src->device.path);
1225                 return (BC_ERROR);
1226         }
1227 
1228         cleanup_bootblock(dest_bblock);
1229 
1230         dest_bblock->buf_size = src_bblock->buf_size;
1231         dest_bblock->buf = malloc(dest_bblock->buf_size);
1232         if (dest_bblock->buf == NULL) {
1233                 perror(gettext("Memory Allocation Failure"));
1234                 return (BC_ERROR);
1235         }
1236         dest_bblock->file = dest_bblock->buf;
1237         dest_bblock->file_size = src_bblock->file_size;
1238         (void) memcpy(dest_bblock->buf, src_bblock->buf,
1239             dest_bblock->buf_size);
1240 
1241         dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
1242             src_bblock->mboot_off);
1243         dest_bblock->mboot_off = src_bblock->mboot_off;
1244         dest_bblock->extra = (char *)dest_bblock->file +
1245             P2ROUNDUP(dest_bblock->file_size, 8);
1246         dest_bblock->extra_size = src_bblock->extra_size;
1247 
1248         (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
1249             src->device.path, dest->device.path);
1250 
1251         return (commit_to_disk(dest, updt_str));
1252 }
1253 
1254 static int
1255 commit_to_disk(ib_data_t *data, char *update_str)
1256 {
1257         assert(data != NULL);
1258 
1259         if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
1260                 (void) fprintf(stderr, gettext("Error updating the bootblock "
1261                     "image\n"));
1262                 return (BC_ERROR);
1263         }
1264 
1265         if (prepare_stage1(data) != BC_SUCCESS) {
1266                 (void) fprintf(stderr, gettext("Error updating the stage1 "
1267                     "image\n"));
1268                 return (BC_ERROR);
1269         }
1270 
1271         if (write_bootblock(data) != BC_SUCCESS) {
1272                 (void) fprintf(stderr, gettext("Error writing bootblock to "
1273                     "disk\n"));
1274                 return (BC_ERROR);
1275         }
1276 
1277         return (write_stage1(data));
1278 }
1279 
1280 /*
1281  * Install a new bootblock on the given device. handle_install() expects argv
1282  * to contain 3 parameters (the target device path and the path to the
1283  * bootblock.
1284  *
1285  * Returns:     BC_SUCCESS - if the installation is successful
1286  *              BC_ERROR   - if the installation failed
1287  *              BC_NOUPDT  - if no installation was performed because the
1288  *                           version currently installed is more recent than the
1289  *                           supplied one.
1290  *
1291  */
1292 static int
1293 handle_install(char *progname, char **argv)
1294 {
1295         ib_data_t       install_data;
1296         ib_bootblock_t  *bblock = &install_data.bootblock;
1297         ib_bootblock_t  *eblock = &install_data.efiblock;
1298         char            *stage1 = NULL;
1299         char            *bootblock = NULL;
1300         char            *efiboot = NULL;
1301         char            *device_path = NULL;
1302         char            *tmp;
1303         int             ret = BC_ERROR;
1304 
1305         stage1 = strdup(argv[0]);
1306         bootblock = strdup(argv[1]);
1307         device_path = strdup(argv[2]);
1308 
1309         if (!device_path || !bootblock || !stage1) {
1310                 (void) fprintf(stderr, gettext("Missing parameter"));
1311                 usage(progname);
1312                 goto out;
1313         }
1314 
1315         tmp = strdup(argv[1]);
1316         if (tmp == NULL) {
1317                 perror(gettext("Memory allocation failure"));
1318                 goto out;
1319         }
1320         (void) asprintf(&efiboot, "%s/" EFIBOOT64, dirname(tmp));
1321         free(tmp);
1322         if (efiboot == NULL) {
1323                 perror(gettext("Memory allocation failure"));
1324                 goto out;
1325         }
1326 
1327         BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
1328             device_path, stage1, bootblock);
1329         bzero(&install_data, sizeof (ib_data_t));
1330 
1331         if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
1332                 (void) fprintf(stderr, gettext("Unable to open device %s\n"),
1333                     device_path);
1334                 goto out;
1335         }
1336 
1337         if (read_stage1_from_file(stage1, &install_data) != BC_SUCCESS) {
1338                 (void) fprintf(stderr, gettext("Error opening %s\n"), stage1);
1339                 goto out_dev;
1340         }
1341 
1342         if (read_bootblock_from_file(bootblock, bblock) != BC_SUCCESS) {
1343                 (void) fprintf(stderr, gettext("Error reading %s\n"),
1344                     bootblock);
1345                 goto out_dev;
1346         }
1347 
1348         /* only read EFI boot program if there is system partition */
1349         if (install_data.device.system.fd != -1) {
1350                 if (read_bootblock_from_file(efiboot, eblock) != BC_SUCCESS) {
1351                         (void) fprintf(stderr, gettext("Error reading %s\n"),
1352                             efiboot);
1353                         goto out_dev;
1354                 }
1355         }
1356 
1357         /*
1358          * is_update_necessary() will take care of checking if versioning and/or
1359          * forcing the update have been specified. It will also emit a warning
1360          * if a non-versioned update is attempted over a versioned bootblock.
1361          */
1362         if (!is_update_necessary(&install_data, update_str)) {
1363                 (void) fprintf(stderr, gettext("bootblock version installed "
1364                     "on %s is more recent or identical\n"
1365                     "Use -F to override or install without the -u option\n"),
1366                     device_path);
1367                 ret = BC_NOUPDT;
1368                 goto out_dev;
1369         }
1370 
1371         BOOT_DEBUG("Ready to commit to disk\n");
1372         ret = commit_to_disk(&install_data, update_str);
1373 
1374 out_dev:
1375         cleanup_device(&install_data.device);
1376 out:
1377         free(efiboot);
1378         free(stage1);
1379         free(bootblock);
1380         free(device_path);
1381         return (ret);
1382 }
1383 
1384 /*
1385  * Retrieves from a device the extended information (einfo) associated to the
1386  * file or installed stage2.
1387  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0
1388  * or file name.
1389  * Returns:
1390  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
1391  *        - BC_ERROR (on error)
1392  *        - BC_NOEINFO (no extended information available)
1393  */
1394 static int
1395 handle_getinfo(char *progname, char **argv)
1396 {
1397         struct stat     sb;
1398         ib_bootblock_t  bblock;
1399         ib_device_t     device;
1400         bblk_einfo_t    *einfo;
1401         uint8_t         flags = 0;
1402         char            *device_path, *path;
1403         int             retval = BC_ERROR;
1404         int             ret;
1405 
1406         device_path = strdup(argv[0]);
1407         if (!device_path) {
1408                 (void) fprintf(stderr, gettext("Missing parameter"));
1409                 usage(progname);
1410                 goto out;
1411         }
1412 
1413         if (stat(device_path, &sb) == -1) {
1414                 perror("stat");
1415                 goto out;
1416         }
1417 
1418         bzero(&bblock, sizeof (bblock));
1419         bzero(&device, sizeof (device));
1420         BOOT_DEBUG("device path: %s\n", device_path);
1421 
1422         if (S_ISREG(sb.st_mode) != 0) {
1423                 path = device_path;
1424                 ret = read_bootblock_from_file(device_path, &bblock);
1425         } else {
1426                 if (init_device(&device, device_path) != BC_SUCCESS) {
1427                         (void) fprintf(stderr, gettext("Unable to gather "
1428                             "device information from %s\n"), device_path);
1429                         goto out_dev;
1430                 }
1431                 ret = read_bootblock_from_disk(&device, &bblock, &path);
1432         }
1433 
1434         if (ret == BC_ERROR) {
1435                 (void) fprintf(stderr, gettext("Error reading bootblock from "
1436                     "%s\n"), path);
1437                 goto out_dev;
1438         }
1439 
1440         if (ret == BC_NOEXTRA) {
1441                 BOOT_DEBUG("No multiboot header found on %s, unable "
1442                     "to locate extra information area (old/non versioned "
1443                     "bootblock?) \n", device_path);
1444                 (void) fprintf(stderr, gettext("No extended information "
1445                     "found\n"));
1446                 retval = BC_NOEINFO;
1447                 goto out_dev;
1448         }
1449 
1450         einfo = find_einfo(bblock.extra, bblock.extra_size);
1451         if (einfo == NULL) {
1452                 retval = BC_NOEINFO;
1453                 (void) fprintf(stderr, gettext("No extended information "
1454                     "found\n"));
1455                 goto out_dev;
1456         }
1457 
1458         /* Print the extended information. */
1459         if (strip)
1460                 flags |= EINFO_EASY_PARSE;
1461         if (verbose_dump)
1462                 flags |= EINFO_PRINT_HEADER;
1463 
1464         print_einfo(flags, einfo, bblock.extra_size);
1465         retval = BC_SUCCESS;
1466 
1467 out_dev:
1468         if (S_ISREG(sb.st_mode) == 0)
1469                 cleanup_device(&device);
1470 out:
1471         free(device_path);
1472         return (retval);
1473 }
1474 
1475 /*
1476  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
1477  *
1478  * Returns:
1479  *      - BC_SUCCESS (a successful propagation happened)
1480  *      - BC_ERROR (an error occurred)
1481  *      - BC_NOEXTRA (it is not possible to dump the current bootblock since
1482  *                      there is no multiboot information)
1483  */
1484 static int
1485 handle_mirror(char *progname, char **argv)
1486 {
1487         ib_data_t       curr_data;
1488         ib_data_t       attach_data;
1489         ib_device_t     *curr_device = &curr_data.device;
1490         ib_device_t     *attach_device = &attach_data.device;
1491         ib_bootblock_t  *bblock_curr = &curr_data.bootblock;
1492         ib_bootblock_t  *bblock_attach = &attach_data.bootblock;
1493         bblk_einfo_t    *einfo_curr = NULL;
1494         char            *curr_device_path;
1495         char            *attach_device_path;
1496         char            *updt_str = NULL;
1497         char            *path;
1498         int             retval = BC_ERROR;
1499         int             ret;
1500 
1501         curr_device_path = strdup(argv[0]);
1502         attach_device_path = strdup(argv[1]);
1503 
1504         if (!curr_device_path || !attach_device_path) {
1505                 (void) fprintf(stderr, gettext("Missing parameter"));
1506                 usage(progname);
1507                 goto out;
1508         }
1509         BOOT_DEBUG("Current device path is: %s, attaching device path is: "
1510             " %s\n", curr_device_path, attach_device_path);
1511 
1512         bzero(&curr_data, sizeof (ib_data_t));
1513         bzero(&attach_data, sizeof (ib_data_t));
1514 
1515         if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
1516                 (void) fprintf(stderr, gettext("Unable to gather device "
1517                     "information from %s (current device)\n"),
1518                     curr_device_path);
1519                 goto out_currdev;
1520         }
1521 
1522         if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
1523                 (void) fprintf(stderr, gettext("Unable to gather device "
1524                     "information from %s (attaching device)\n"),
1525                     attach_device_path);
1526                 goto out_devs;
1527         }
1528 
1529         ret = read_bootblock_from_disk(curr_device, bblock_curr, &path);
1530         if (ret == BC_ERROR) {
1531                 BOOT_DEBUG("Error reading bootblock from %s\n", path);
1532                 retval = BC_ERROR;
1533                 goto out_devs;
1534         }
1535 
1536         if (ret == BC_NOEXTRA) {
1537                 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
1538                     " the bootblock\n", path);
1539                 retval = BC_NOEXTRA;
1540                 goto out_devs;
1541         }
1542 
1543         write_mbr = B_TRUE;
1544         force_mbr = B_TRUE;
1545         einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size);
1546         if (einfo_curr != NULL)
1547                 updt_str = einfo_get_string(einfo_curr);
1548 
1549         retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
1550         cleanup_bootblock(bblock_curr);
1551         cleanup_bootblock(bblock_attach);
1552 out_devs:
1553         cleanup_device(attach_device);
1554 out_currdev:
1555         cleanup_device(curr_device);
1556 out:
1557         free(curr_device_path);
1558         free(attach_device_path);
1559         return (retval);
1560 }
1561 
1562 #define USAGE_STRING    "Usage:\t%s [-h|-m|-f|-n|-F|-u verstr] stage1 stage2 " \
1563                         "raw-device\n"                                  \
1564                         "\t%s -M [-n] raw-device attach-raw-device\n"   \
1565                         "\t%s [-e|-V] -i raw-device | file\n"
1566 
1567 #define CANON_USAGE_STR gettext(USAGE_STRING)
1568 
1569 static void
1570 usage(char *progname)
1571 {
1572         (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
1573 }
1574 
1575 int
1576 main(int argc, char **argv)
1577 {
1578         int     opt;
1579         int     params = 3;
1580         int     ret;
1581         char    *progname;
1582         char    **handle_args;
1583 
1584         (void) setlocale(LC_ALL, "");
1585         (void) textdomain(TEXT_DOMAIN);
1586         if (init_yes() < 0) {
1587                 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
1588                     strerror(errno));
1589                 exit(BC_ERROR);
1590         }
1591 
1592         while ((opt = getopt(argc, argv, "deFfhiMmnu:V")) != EOF) {
1593                 switch (opt) {
1594                 case 'd':
1595                         boot_debug = B_TRUE;
1596                         break;
1597                 case 'e':
1598                         strip = B_TRUE;
1599                         break;
1600                 case 'F':
1601                         force_update = B_TRUE;
1602                         break;
1603                 case 'f':
1604                         force_mbr = B_TRUE;
1605                         break;
1606                 case 'h':
1607                         usage(argv[0]);
1608                         exit(BC_SUCCESS);
1609                         break;
1610                 case 'i':
1611                         do_getinfo = B_TRUE;
1612                         params = 1;
1613                         break;
1614                 case 'M':
1615                         do_mirror_bblk = B_TRUE;
1616                         params = 2;
1617                         break;
1618                 case 'm':
1619                         write_mbr = B_TRUE;
1620                         break;
1621                 case 'n':
1622                         nowrite = B_TRUE;
1623                         break;
1624                 case 'u':
1625                         do_version = B_TRUE;
1626 
1627                         update_str = malloc(strlen(optarg) + 1);
1628                         if (update_str == NULL) {
1629                                 perror(gettext("Memory allocation failure"));
1630                                 exit(BC_ERROR);
1631                         }
1632                         (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
1633                         break;
1634                 case 'V':
1635                         verbose_dump = B_TRUE;
1636                         break;
1637                 default:
1638                         /* fall through to process non-optional args */
1639                         break;
1640                 }
1641         }
1642 
1643         /* check arguments */
1644         if (argc != optind + params) {
1645                 usage(argv[0]);
1646                 exit(BC_ERROR);
1647         }
1648         progname = argv[0];
1649         check_options(progname);
1650         handle_args = argv + optind;
1651 
1652         if (nowrite)
1653                 (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
1654                     " be written to disk.\n"));
1655 
1656         if (do_getinfo) {
1657                 ret = handle_getinfo(progname, handle_args);
1658         } else if (do_mirror_bblk) {
1659                 ret = handle_mirror(progname, handle_args);
1660         } else {
1661                 ret = handle_install(progname, handle_args);
1662         }
1663         return (ret);
1664 }
1665 
1666 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
1667 static void
1668 check_options(char *progname)
1669 {
1670         if (do_getinfo && do_mirror_bblk) {
1671                 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
1672                     "specified at the same time\n"));
1673                 usage(progname);
1674                 exit(BC_ERROR);
1675         }
1676 
1677         if (do_mirror_bblk) {
1678                 /*
1679                  * -u and -F may actually reflect a user intent that is not
1680                  * correct with this command (mirror can be interpreted
1681                  * "similar" to install. Emit a message and continue.
1682                  * -e and -V have no meaning, be quiet here and only report the
1683                  * incongruence if a debug output is requested.
1684                  */
1685                 if (do_version) {
1686                         (void) fprintf(stderr, MEANINGLESS_OPT, "-u");
1687                         do_version = B_FALSE;
1688                 }
1689                 if (force_update) {
1690                         (void) fprintf(stderr, MEANINGLESS_OPT, "-F");
1691                         force_update = B_FALSE;
1692                 }
1693                 if (strip || verbose_dump) {
1694                         BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
1695                         strip = B_FALSE;
1696                         verbose_dump = B_FALSE;
1697                 }
1698         }
1699 
1700         if (do_getinfo) {
1701                 if (write_mbr || force_mbr || do_version || force_update) {
1702                         BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
1703                         write_mbr = force_mbr = do_version = B_FALSE;
1704                         force_update = B_FALSE;
1705                 }
1706         }
1707 }