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