installboot: fix stage2 size check for MBR
[unleashed.git] / usr / src / cmd / boot / installboot / i386 / installboot.c
blobcbaf6d95f397169d40841d1bf77ceaca2c81cb28
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
24 * Copyright 2016 Toomas Soome <tsoome@me.com>
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>
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>
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"
53 #ifndef TEXT_DOMAIN
54 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
55 #endif
58 * BIOS bootblock installation:
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.
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.
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.
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.
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.
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.
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.
94 * Stored location values in MBR/stage2 also mean the bootblocks must be
95 * reinstalled in case the partition content is relocated.
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;
107 /* Versioning string, if present. */
108 static char *update_str;
111 * Temporary buffer to store the first 32K of data looking for a multiboot
112 * signature.
114 char mboot_scan[MBOOT_SCAN_SIZE];
116 /* Function prototypes. */
117 static void check_options(char *);
118 static int get_start_sector(ib_device_t *);
120 static int read_stage1_from_file(char *, ib_data_t *data);
121 static int read_bootblock_from_file(char *, ib_data_t *data);
122 static int read_bootblock_from_disk(ib_device_t *device, ib_bootblock_t *,
123 char **);
124 static void add_bootblock_einfo(ib_bootblock_t *, char *);
125 static int prepare_stage1(ib_data_t *);
126 static int prepare_bootblock(ib_data_t *, char *);
127 static int write_stage1(ib_data_t *);
128 static int write_bootblock(ib_data_t *);
129 static int init_device(ib_device_t *, char *);
130 static void cleanup_device(ib_device_t *);
131 static int commit_to_disk(ib_data_t *, char *);
132 static int handle_install(char *, char **);
133 static int handle_getinfo(char *, char **);
134 static int handle_mirror(char *, char **);
135 static boolean_t is_update_necessary(ib_data_t *, char *);
136 static int propagate_bootblock(ib_data_t *, ib_data_t *, char *);
137 static void usage(char *);
139 static int
140 read_stage1_from_file(char *path, ib_data_t *dest)
142 int fd;
144 assert(dest != NULL);
146 /* read the stage1 file from filesystem */
147 fd = open(path, O_RDONLY);
148 if (fd == -1 ||
149 read(fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) {
150 (void) fprintf(stderr, gettext("cannot read stage1 file %s\n"),
151 path);
152 return (BC_ERROR);
154 (void) close(fd);
155 return (BC_SUCCESS);
158 static int
159 read_bootblock_from_file(char *file, ib_data_t *data)
161 ib_bootblock_t *bblock = &data->bootblock;
162 struct stat sb;
163 uint32_t buf_size;
164 uint32_t mboot_off;
165 int fd = -1;
166 int retval = BC_ERROR;
168 assert(data != NULL);
169 assert(file != NULL);
171 fd = open(file, O_RDONLY);
172 if (fd == -1) {
173 BOOT_DEBUG("Error opening %s\n", file);
174 perror("open");
175 goto out;
178 if (fstat(fd, &sb) == -1) {
179 BOOT_DEBUG("Error getting information (stat) about %s", file);
180 perror("stat");
181 goto outfd;
184 /* loader bootblock has version built in */
185 buf_size = sb.st_size;
187 bblock->buf_size = buf_size;
188 BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
189 bblock->buf_size);
191 bblock->buf = malloc(buf_size);
192 if (bblock->buf == NULL) {
193 perror(gettext("Memory allocation failure"));
194 goto outbuf;
196 bblock->file = bblock->buf;
198 if (read(fd, bblock->file, bblock->buf_size) != bblock->buf_size) {
199 BOOT_DEBUG("Read from %s failed\n", file);
200 perror("read");
201 goto outfd;
204 if (find_multiboot(bblock->file, MBOOT_SCAN_SIZE, &mboot_off)
205 != BC_SUCCESS) {
206 (void) fprintf(stderr,
207 gettext("Unable to find multiboot header\n"));
208 goto outfd;
211 bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off);
212 bblock->mboot_off = mboot_off;
214 bblock->file_size =
215 bblock->mboot->load_end_addr - bblock->mboot->load_addr;
216 BOOT_DEBUG("bootblock file size is %d\n", bblock->file_size);
218 bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
219 bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
221 BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
222 "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
223 bblock->extra_size, bblock->buf, bblock->buf_size);
225 (void) close(fd);
226 return (BC_SUCCESS);
228 outbuf:
229 (void) free(bblock->buf);
230 bblock->buf = NULL;
231 outfd:
232 (void) close(fd);
233 out:
234 return (retval);
237 static int
238 read_bootblock_from_disk(ib_device_t *device, ib_bootblock_t *bblock,
239 char **path)
241 int dev_fd;
242 uint32_t size, offset;
243 uint32_t buf_size;
244 uint32_t mboot_off;
245 multiboot_header_t *mboot;
247 assert(device != NULL);
248 assert(bblock != NULL);
250 if (device->target.fstype == IG_FS_ZFS) {
251 dev_fd = device->target.fd;
252 offset = BBLK_ZFS_BLK_OFF * SECTOR_SIZE;
253 *path = device->target.path;
254 } else {
255 dev_fd = device->stage.fd;
256 offset = device->stage.offset * SECTOR_SIZE;
257 *path = device->stage.path;
260 if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan), offset)
261 != BC_SUCCESS) {
262 BOOT_DEBUG("Error reading bootblock area\n");
263 perror("read");
264 return (BC_ERROR);
267 /* No multiboot means no chance of knowing bootblock size */
268 if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
269 != BC_SUCCESS) {
270 BOOT_DEBUG("Unable to find multiboot header\n");
271 return (BC_NOEXTRA);
273 mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
276 * make sure mboot has sane values
278 if (mboot->load_end_addr == 0 ||
279 mboot->load_end_addr < mboot->load_addr)
280 return (BC_NOEXTRA);
283 * Currently, the amount of space reserved for extra information
284 * is "fixed". We may have to scan for the terminating extra payload
285 * in the future.
287 size = mboot->load_end_addr - mboot->load_addr;
288 buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
289 bblock->file_size = size;
291 bblock->buf = malloc(buf_size);
292 if (bblock->buf == NULL) {
293 BOOT_DEBUG("Unable to allocate enough memory to read"
294 " the extra bootblock from the disk\n");
295 perror(gettext("Memory allocation failure"));
296 return (BC_ERROR);
298 bblock->buf_size = buf_size;
300 if (read_in(dev_fd, bblock->buf, buf_size, offset) != BC_SUCCESS) {
301 BOOT_DEBUG("Error reading the bootblock\n");
302 (void) free(bblock->buf);
303 bblock->buf = NULL;
304 return (BC_ERROR);
307 /* Update pointers. */
308 bblock->file = bblock->buf;
309 bblock->mboot_off = mboot_off;
310 bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off);
311 bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
312 bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
314 BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
315 "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
316 bblock->extra_size, bblock->buf, bblock->buf_size);
318 return (BC_SUCCESS);
321 static boolean_t
322 is_update_necessary(ib_data_t *data, char *updt_str)
324 bblk_einfo_t *einfo;
325 bblk_einfo_t *einfo_file;
326 bblk_hs_t bblock_hs;
327 ib_bootblock_t bblock_disk;
328 ib_bootblock_t *bblock_file = &data->bootblock;
329 ib_device_t *device = &data->device;
330 int ret;
331 char *path;
333 assert(data != NULL);
335 bzero(&bblock_disk, sizeof (ib_bootblock_t));
337 ret = read_bootblock_from_disk(device, &bblock_disk, &path);
338 if (ret != BC_SUCCESS) {
339 BOOT_DEBUG("Unable to read bootblock from %s\n", path);
340 return (B_TRUE);
343 einfo = find_einfo(bblock_disk.extra, bblock_disk.extra_size);
344 if (einfo == NULL) {
345 BOOT_DEBUG("No extended information available on disk\n");
346 return (B_TRUE);
349 einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size);
350 if (einfo_file == NULL) {
352 * loader bootblock is versioned. missing version means
353 * probably incompatible block. installboot can not install
354 * grub, for example.
356 (void) fprintf(stderr,
357 gettext("ERROR: non versioned bootblock in file\n"));
358 return (B_FALSE);
359 } else {
360 if (updt_str == NULL) {
361 updt_str = einfo_get_string(einfo_file);
362 do_version = B_TRUE;
366 if (!do_version || updt_str == NULL) {
367 (void) fprintf(stderr,
368 gettext("WARNING: target device %s has a "
369 "versioned bootblock that is going to be overwritten by a "
370 "non versioned one\n"), device->path);
371 return (B_TRUE);
374 if (force_update) {
375 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
376 return (B_TRUE);
379 BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
381 bblock_hs.src_buf = (unsigned char *)bblock_file->file;
382 bblock_hs.src_size = bblock_file->file_size;
384 return (einfo_should_update(einfo, &bblock_hs, updt_str));
387 static void
388 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
390 bblk_hs_t hs;
391 uint32_t avail_space;
393 assert(bblock != NULL);
395 if (updt_str == NULL) {
396 BOOT_DEBUG("WARNING: no update string passed to "
397 "add_bootblock_einfo()\n");
398 return;
401 /* Fill bootblock hashing source information. */
402 hs.src_buf = (unsigned char *)bblock->file;
403 hs.src_size = bblock->file_size;
404 /* How much space for the extended information structure? */
405 avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
406 /* Place the extended information structure. */
407 add_einfo(bblock->extra, updt_str, &hs, avail_space);
411 * set up data for case stage1 is installed as MBR
412 * set up location and size of bootblock
413 * set disk guid to provide unique information for biosdev command
415 static int
416 prepare_stage1(ib_data_t *data)
418 ib_device_t *device;
420 assert(data != NULL);
421 device = &data->device;
423 /* copy BPB */
424 bcopy(device->mbr + STAGE1_BPB_OFFSET,
425 data->stage1 + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
428 /* copy MBR, note STAGE1_SIG == BOOTSZ */
429 bcopy(device->mbr + STAGE1_SIG, data->stage1 + STAGE1_SIG,
430 SECTOR_SIZE - STAGE1_SIG);
432 /* set stage2 size */
433 *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) =
434 (uint16_t)(data->bootblock.buf_size / SECTOR_SIZE);
437 * set stage2 location.
438 * for zfs always use zfs embedding, for ufs/pcfs use partition_start
439 * as base for stage2 location, for ufs/pcfs in MBR partition, use
440 * free space after MBR record.
442 if (device->target.fstype == IG_FS_ZFS)
443 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
444 device->target.start + device->target.offset;
445 else {
446 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
447 device->stage.start + device->stage.offset;
451 * set disk uuid. we only need reasonable amount of uniqueness
452 * to allow biosdev to identify disk based on mbr differences.
454 uuid_generate(data->stage1 + STAGE1_STAGE2_UUID);
456 return (BC_SUCCESS);
459 static int
460 prepare_bootblock(ib_data_t *data, char *updt_str)
462 ib_bootblock_t *bblock;
463 ib_device_t *device;
464 uint64_t *ptr;
466 assert(data != NULL);
468 bblock = &data->bootblock;
469 device = &data->device;
471 ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
472 *ptr = device->target.start;
475 * the loader bootblock has built in version, if custom
476 * version was provided, update it.
478 if (do_version)
479 add_bootblock_einfo(bblock, updt_str);
481 return (BC_SUCCESS);
484 static int
485 write_bootblock(ib_data_t *data)
487 ib_device_t *device = &data->device;
488 ib_bootblock_t *bblock = &data->bootblock;
489 uint64_t abs;
490 int dev_fd, ret;
491 off_t offset;
492 char *path;
494 assert(data != NULL);
497 * ZFS bootblock area is 3.5MB, make sure we can fit.
498 * buf_size is size of bootblk+EINFO.
500 if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) {
501 (void) fprintf(stderr, gettext("bootblock is too large\n"));
502 return (BC_ERROR);
505 if (device->target.fstype == IG_FS_ZFS) {
506 dev_fd = device->target.fd;
507 abs = device->target.start + device->target.offset;
508 offset = BBLK_ZFS_BLK_OFF * SECTOR_SIZE;
509 path = device->target.path;
510 } else {
511 dev_fd = device->stage.fd;
512 abs = device->stage.start + device->stage.offset;
513 offset = device->stage.offset * SECTOR_SIZE;
514 path = device->stage.path;
515 if (bblock->buf_size >
516 (device->stage.size - device->stage.offset) * SECTOR_SIZE) {
517 (void) fprintf(stderr, gettext("Device %s is "
518 "too small to fit the stage2\n"), path);
519 return (BC_ERROR);
522 ret = write_out(dev_fd, bblock->buf, bblock->buf_size, offset);
523 if (ret != BC_SUCCESS) {
524 BOOT_DEBUG("Error writing the ZFS bootblock "
525 "to %s at offset %d\n", path, offset);
526 return (BC_ERROR);
529 (void) fprintf(stdout, gettext("bootblock written for %s,"
530 " %d sectors starting at %d (abs %lld)\n"), path,
531 (bblock->buf_size / SECTOR_SIZE) + 1, offset / SECTOR_SIZE, abs);
533 return (BC_SUCCESS);
536 static int
537 write_stage1(ib_data_t *data)
539 ib_device_t *device = &data->device;
541 assert(data != NULL);
544 * Partition boot block or volume boot record.
545 * This is essentially copy of MBR (1 sector) and we store it
546 * to support multi boot setups.
548 * Not all combinations are supported; as pcfs does not leave
549 * space, we will not write to pcfs target.
550 * In addition, in VTOC setup, we will only write VBR to slice 2.
552 if (device->stage.start != 0 &&
553 strcmp(device->target.path, device->stage.path)) {
554 /* we got separate stage area, use it */
555 if (write_out(device->stage.fd, data->stage1,
556 sizeof (data->stage1), 0) != BC_SUCCESS) {
557 (void) fprintf(stdout, gettext("cannot write "
558 "partition boot sector\n"));
559 perror("write");
560 return (BC_ERROR);
563 (void) fprintf(stdout, gettext("stage1 written to "
564 "%s %d sector 0 (abs %d)\n"),
565 device->devtype == IG_DEV_MBR? "partition":"slice",
566 device->stage.id, device->stage.start);
570 * both ufs and zfs have initial 8k reserved for VTOC/boot
571 * so its safe to use this area. however, only
572 * write to target if we have MBR/GPT.
574 if (device->devtype != IG_DEV_VTOC &&
575 device->target.fstype != IG_FS_PCFS) {
576 if (write_out(device->target.fd, data->stage1,
577 sizeof (data->stage1), 0) != BC_SUCCESS) {
578 (void) fprintf(stdout, gettext("cannot write "
579 "partition boot sector\n"));
580 perror("write");
581 return (BC_ERROR);
584 (void) fprintf(stdout, gettext("stage1 written to "
585 "%s %d sector 0 (abs %d)\n"),
586 device->devtype == IG_DEV_MBR? "partition":"slice",
587 device->target.id, device->target.start);
590 if (write_mbr) {
591 if (write_out(device->fd, data->stage1,
592 sizeof (data->stage1), 0) != BC_SUCCESS) {
593 (void) fprintf(stdout,
594 gettext("cannot write master boot sector\n"));
595 perror("write");
596 return (BC_ERROR);
598 (void) fprintf(stdout,
599 gettext("stage1 written to master boot sector\n"));
602 return (BC_SUCCESS);
606 * find partition/slice start sector. will be recorded in stage2 and used
607 * by stage2 to identify partition with boot file system.
609 static int
610 get_start_sector(ib_device_t *device)
612 uint32_t secnum = 0, numsec = 0;
613 int i, pno, rval, log_part = 0;
614 struct mboot *mboot;
615 struct ipart *part = NULL;
616 ext_part_t *epp;
617 struct part_info dkpi;
618 struct extpart_info edkpi;
620 if (device->devtype == IG_DEV_EFI) {
621 struct dk_gpt *vtoc;
623 if (efi_alloc_and_read(device->fd, &vtoc) < 0)
624 return (BC_ERROR);
626 if (device->stage.start == 0) {
627 /* zero size means the fstype must be zfs */
628 assert(device->target.fstype == IG_FS_ZFS);
630 device->stage.start =
631 vtoc->efi_parts[device->stage.id].p_start;
632 device->stage.size =
633 vtoc->efi_parts[device->stage.id].p_size;
634 device->stage.offset = BBLK_ZFS_BLK_OFF;
635 device->target.offset = BBLK_ZFS_BLK_OFF;
638 device->target.start =
639 vtoc->efi_parts[device->target.id].p_start;
640 device->target.size =
641 vtoc->efi_parts[device->target.id].p_size;
643 /* with pcfs we always write MBR */
644 if (device->target.fstype == IG_FS_PCFS) {
645 force_mbr = 1;
646 write_mbr = 1;
649 efi_free(vtoc);
650 goto found_part;
653 mboot = (struct mboot *)device->mbr;
655 /* For MBR we have device->stage filled already. */
656 if (device->devtype == IG_DEV_MBR) {
657 /* MBR partition starts from 0 */
658 pno = device->target.id - 1;
659 part = (struct ipart *)mboot->parts + pno;
661 if (part->relsect == 0) {
662 (void) fprintf(stderr, gettext("Partition %d of the "
663 "disk has an incorrect offset\n"),
664 device->target.id);
665 return (BC_ERROR);
667 device->target.start = part->relsect;
668 device->target.size = part->numsect;
670 /* with pcfs we always write MBR */
671 if (device->target.fstype == IG_FS_PCFS) {
672 force_mbr = 1;
673 write_mbr = 1;
675 if (device->target.fstype == IG_FS_ZFS)
676 device->target.offset = BBLK_ZFS_BLK_OFF;
678 goto found_part;
682 * Search for Solaris fdisk partition
683 * Get the solaris partition information from the device
684 * and compare the offset of S2 with offset of solaris partition
685 * from fdisk partition table.
687 if (ioctl(device->target.fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
688 if (ioctl(device->target.fd, DKIOCPARTINFO, &dkpi) < 0) {
689 (void) fprintf(stderr, gettext("cannot get the "
690 "slice information of the disk\n"));
691 return (BC_ERROR);
692 } else {
693 edkpi.p_start = dkpi.p_start;
694 edkpi.p_length = dkpi.p_length;
698 device->target.start = edkpi.p_start;
699 device->target.size = edkpi.p_length;
700 if (device->target.fstype == IG_FS_ZFS)
701 device->target.offset = BBLK_ZFS_BLK_OFF;
703 for (i = 0; i < FD_NUMPART; i++) {
704 part = (struct ipart *)mboot->parts + i;
706 if (part->relsect == 0) {
707 (void) fprintf(stderr, gettext("Partition %d of the "
708 "disk has an incorrect offset\n"), i+1);
709 return (BC_ERROR);
712 if (edkpi.p_start >= part->relsect &&
713 edkpi.p_start < (part->relsect + part->numsect)) {
714 /* Found the partition */
715 break;
719 if (i == FD_NUMPART) {
720 /* No solaris fdisk partitions (primary or logical) */
721 (void) fprintf(stderr, gettext("Solaris partition not found. "
722 "Aborting operation.\n"));
723 return (BC_ERROR);
727 * We have found a Solaris fdisk partition (primary or extended)
728 * Handle the simple case first: Solaris in a primary partition
730 if (!fdisk_is_dos_extended(part->systid)) {
731 device->stage.start = part->relsect;
732 device->stage.size = part->numsect;
733 if (device->target.fstype == IG_FS_ZFS)
734 device->stage.offset = BBLK_ZFS_BLK_OFF;
735 else
736 device->stage.offset = BBLK_BLKLIST_OFF;
737 device->stage.id = i + 1;
738 goto found_part;
742 * Solaris in a logical partition. Find that partition in the
743 * extended part.
746 if ((rval = libfdisk_init(&epp, device->path, NULL, FDISK_READ_DISK))
747 != FDISK_SUCCESS) {
748 switch (rval) {
750 * The first 3 cases are not an error per-se, just that
751 * there is no Solaris logical partition
753 case FDISK_EBADLOGDRIVE:
754 case FDISK_ENOLOGDRIVE:
755 case FDISK_EBADMAGIC:
756 (void) fprintf(stderr, gettext("Solaris "
757 "partition not found. "
758 "Aborting operation.\n"));
759 return (BC_ERROR);
760 case FDISK_ENOVGEOM:
761 (void) fprintf(stderr, gettext("Could not get "
762 "virtual geometry\n"));
763 return (BC_ERROR);
764 case FDISK_ENOPGEOM:
765 (void) fprintf(stderr, gettext("Could not get "
766 "physical geometry\n"));
767 return (BC_ERROR);
768 case FDISK_ENOLGEOM:
769 (void) fprintf(stderr, gettext("Could not get "
770 "label geometry\n"));
771 return (BC_ERROR);
772 default:
773 (void) fprintf(stderr, gettext("Failed to "
774 "initialize libfdisk.\n"));
775 return (BC_ERROR);
779 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
780 libfdisk_fini(&epp);
781 if (rval != FDISK_SUCCESS) {
782 /* No solaris logical partition */
783 (void) fprintf(stderr, gettext("Solaris partition not found. "
784 "Aborting operation.\n"));
785 return (BC_ERROR);
788 device->stage.start = secnum;
789 device->stage.size = numsec;
790 device->stage.id = pno;
791 log_part = 1;
793 found_part:
794 /* get confirmation for -m */
795 if (write_mbr && !force_mbr) {
796 (void) fprintf(stdout, gettext("Updating master boot sector "
797 "destroys existing boot managers (if any).\n"
798 "continue (y/n)? "));
799 if (!yes()) {
800 write_mbr = 0;
801 (void) fprintf(stdout, gettext("master boot sector "
802 "not updated\n"));
803 return (BC_ERROR);
808 * warn, if illumos in primary partition and loader not in MBR and
809 * partition is not active
811 if (device->devtype != IG_DEV_EFI) {
812 if (!log_part && part->bootid != 128 && !write_mbr) {
813 (void) fprintf(stdout, gettext("Solaris fdisk "
814 "partition is inactive.\n"), device->stage.id);
818 return (BC_SUCCESS);
821 static int
822 open_device(char *path)
824 struct stat statbuf = {0};
825 int fd = -1;
827 if (nowrite)
828 fd = open(path, O_RDONLY);
829 else
830 fd = open(path, O_RDWR);
832 if (fd == -1) {
833 BOOT_DEBUG("Unable to open %s\n", path);
834 perror("open");
835 return (-1);
838 if (fstat(fd, &statbuf) != 0) {
839 BOOT_DEBUG("Unable to stat %s\n", path);
840 perror("stat");
841 (void) close(fd);
842 return (-1);
845 if (S_ISCHR(statbuf.st_mode) == 0) {
846 (void) fprintf(stderr, gettext("%s: Not a character device\n"),
847 path);
848 (void) close(fd);
849 return (-1);
852 return (fd);
855 static int
856 get_boot_partition(ib_device_t *device, struct mboot *mbr)
858 struct ipart *part;
859 char *path, *ptr;
860 int i;
862 part = (struct ipart *) mbr->parts;
863 for (i = 0; i < FD_NUMPART; i++) {
864 if (part[i].systid == X86BOOT)
865 break;
868 /* no X86BOOT, try to use space between MBR and first partition */
869 if (i == FD_NUMPART) {
870 device->stage.path = strdup(device->path);
871 if (device->stage.path == NULL) {
872 perror(gettext("Memory allocation failure"));
873 return (BC_ERROR);
875 device->stage.fd = dup(device->fd);
876 device->stage.id = 0;
877 device->stage.devtype = IG_DEV_MBR;
878 device->stage.fstype = IG_FS_NONE;
879 device->stage.start = 0;
880 device->stage.size = part[0].relsect;
881 device->stage.offset = BBLK_BLKLIST_OFF;
882 return (BC_SUCCESS);
885 if ((path = strdup(device->path)) == NULL) {
886 perror(gettext("Memory allocation failure"));
887 return (BC_ERROR);
890 ptr = strrchr(path, 'p');
891 ptr++;
892 *ptr = '\0';
893 (void) asprintf(&ptr, "%s%d", path, i+1); /* partitions are p1..p4 */
894 free(path);
895 if (ptr == NULL) {
896 perror(gettext("Memory allocation failure"));
897 return (BC_ERROR);
899 device->stage.path = ptr;
900 device->stage.fd = open_device(ptr);
901 device->stage.id = i + 1;
902 device->stage.devtype = IG_DEV_MBR;
903 device->stage.fstype = IG_FS_NONE;
904 device->stage.start = part[i].relsect;
905 device->stage.size = part[i].numsect;
906 device->stage.offset = 1; /* leave sector 0 for VBR */
907 return (BC_SUCCESS);
910 static int
911 get_boot_slice(ib_device_t *device, struct dk_gpt *vtoc)
913 uint_t i;
914 char *path, *ptr;
916 for (i = 0; i < vtoc->efi_nparts; i++) {
917 if (vtoc->efi_parts[i].p_tag == V_BOOT) {
918 if ((path = strdup(device->target.path)) == NULL) {
919 perror(gettext("Memory allocation failure"));
920 return (BC_ERROR);
922 ptr = strrchr(path, 's');
923 ptr++;
924 *ptr = '\0';
925 (void) asprintf(&ptr, "%s%d", path, i);
926 free(path);
927 if (ptr == NULL) {
928 perror(gettext("Memory allocation failure"));
929 return (BC_ERROR);
931 device->stage.path = ptr;
932 device->stage.fd = open_device(ptr);
933 device->stage.id = i;
934 device->stage.devtype = IG_DEV_EFI;
935 device->stage.fstype = IG_FS_NONE;
936 device->stage.start = vtoc->efi_parts[i].p_start;
937 device->stage.size = vtoc->efi_parts[i].p_size;
938 device->stage.offset = 1; /* leave sector 0 for VBR */
939 return (BC_SUCCESS);
942 return (BC_SUCCESS);
945 static int
946 init_device(ib_device_t *device, char *path)
948 struct dk_gpt *vtoc;
949 fstyp_handle_t fhdl;
950 const char *fident;
951 char *p;
952 int pathlen = strlen(path);
953 int ret;
955 bzero(device, sizeof (*device));
956 device->fd = -1; /* whole disk fd */
957 device->stage.fd = -1; /* bootblock partition fd */
958 device->target.fd = -1; /* target fs partition fd */
960 /* basic check, whole disk is not allowed */
961 if ((p = strrchr(path, '/')) == NULL)
962 p = path;
963 if ((strrchr(p, 'p') == NULL && strrchr(p, 's') == NULL) ||
964 (path[pathlen-2] == 'p' && path[pathlen-1] == '0')) {
965 (void) fprintf(stderr, gettext("installing loader to "
966 "whole disk device is not supported\n"));
969 device->target.path = strdup(path);
970 if (device->target.path == NULL) {
971 perror(gettext("Memory allocation failure"));
972 return (BC_ERROR);
974 device->path = strdup(path);
975 if (device->path == NULL) {
976 perror(gettext("Memory allocation failure"));
977 return (BC_ERROR);
980 /* change device name to p0 */
981 device->path[pathlen - 2] = 'p';
982 device->path[pathlen - 1] = '0';
984 if (strstr(device->target.path, "diskette")) {
985 (void) fprintf(stderr, gettext("installing loader to a floppy "
986 "disk is not supported\n"));
987 return (BC_ERROR);
990 /* Detect if the target device is a pcfs partition. */
991 if (strstr(device->target.path, "p0:boot")) {
992 (void) fprintf(stderr, gettext("installing loader to x86 boot "
993 "partition is not supported\n"));
994 return (BC_ERROR);
997 if ((device->fd = open_device(device->path)) == -1)
998 return (BC_ERROR);
1000 /* read in the device boot sector. */
1001 if (read(device->fd, device->mbr, SECTOR_SIZE) != SECTOR_SIZE) {
1002 (void) fprintf(stderr, gettext("Error reading boot sector\n"));
1003 perror("read");
1004 return (BC_ERROR);
1007 device->devtype = IG_DEV_VTOC;
1008 if (efi_alloc_and_read(device->fd, &vtoc) >= 0) {
1009 ret = get_boot_slice(device, vtoc);
1010 device->devtype = IG_DEV_EFI;
1011 efi_free(vtoc);
1012 if (ret == BC_ERROR)
1013 return (BC_ERROR);
1014 } else if (device->target.path[pathlen - 2] == 'p') {
1015 device->devtype = IG_DEV_MBR;
1016 ret = get_boot_partition(device, (struct mboot *)device->mbr);
1017 if (ret == BC_ERROR)
1018 return (BC_ERROR);
1019 } else if (device->target.path[pathlen - 1] == '2') {
1021 * NOTE: we could relax there and allow zfs boot on
1022 * slice 2 for instance, but lets keep traditional limits.
1024 (void) fprintf(stderr,
1025 gettext("raw device must be a root slice (not s2)\n"));
1026 return (BC_ERROR);
1029 /* fill stage partition for case there is no boot partition */
1030 if (device->stage.path == NULL) {
1031 if ((device->stage.path = strdup(path)) == NULL) {
1032 perror(gettext("Memory allocation failure"));
1033 return (BC_ERROR);
1035 if (device->devtype == IG_DEV_VTOC) {
1036 /* use slice 2 */
1037 device->stage.path[pathlen - 2] = 's';
1038 device->stage.path[pathlen - 1] = '2';
1039 device->stage.id = 2;
1040 } else {
1041 p = strrchr(device->stage.path, 'p');
1042 if (p == NULL)
1043 p = strrchr(device->stage.path, 's');
1044 device->stage.id = atoi(++p);
1046 device->stage.devtype = device->devtype;
1047 device->stage.fd = open_device(device->stage.path);
1050 p = strrchr(device->target.path, 'p');
1051 if (p == NULL)
1052 p = strrchr(device->target.path, 's');
1053 device->target.id = atoi(++p);
1055 if (strcmp(device->stage.path, device->target.path) == 0)
1056 device->target.fd = dup(device->stage.fd);
1057 else
1058 device->target.fd = open_device(device->target.path);
1060 if (fstyp_init(device->target.fd, 0, NULL, &fhdl) != 0)
1061 return (BC_ERROR);
1063 if (fstyp_ident(fhdl, NULL, &fident) != 0) {
1064 fstyp_fini(fhdl);
1065 (void) fprintf(stderr, gettext("Failed to detect file "
1066 "system type\n"));
1067 return (BC_ERROR);
1070 /* at this moment non-boot partition has no size set, use this fact */
1071 if (device->devtype == IG_DEV_EFI && strcmp(fident, "zfs") &&
1072 device->stage.size == 0) {
1073 fstyp_fini(fhdl);
1074 (void) fprintf(stderr, gettext("Booting %s of EFI labeled "
1075 "disks requires the boot partition.\n"), fident);
1076 return (BC_ERROR);
1078 if (strcmp(fident, "zfs") == 0)
1079 device->target.fstype = IG_FS_ZFS;
1080 else if (strcmp(fident, "ufs") == 0) {
1081 device->target.fstype = IG_FS_UFS;
1082 } else if (strcmp(fident, "pcfs") == 0) {
1083 device->target.fstype = IG_FS_PCFS;
1084 } else {
1085 (void) fprintf(stderr, gettext("File system %s is not "
1086 "supported by loader\n"), fident);
1087 fstyp_fini(fhdl);
1088 return (BC_ERROR);
1090 fstyp_fini(fhdl);
1092 /* check for boot partition content */
1093 if (device->stage.size) {
1094 if (fstyp_init(device->stage.fd, 0, NULL, &fhdl) != 0)
1095 return (BC_ERROR);
1097 if (fstyp_ident(fhdl, NULL, &fident) == 0) {
1098 (void) fprintf(stderr, gettext("Unexpected %s file "
1099 "system on boot partition\n"), fident);
1100 fstyp_fini(fhdl);
1101 return (BC_ERROR);
1103 fstyp_fini(fhdl);
1105 return (get_start_sector(device));
1108 static void
1109 cleanup_device(ib_device_t *device)
1111 if (device->path)
1112 free(device->path);
1113 if (device->stage.path)
1114 free(device->stage.path);
1115 if (device->target.path)
1116 free(device->target.path);
1118 if (device->fd != -1)
1119 (void) close(device->fd);
1120 if (device->stage.fd != -1)
1121 (void) close(device->stage.fd);
1122 if (device->target.fd != -1)
1123 (void) close(device->target.fd);
1124 bzero(device, sizeof (*device));
1127 static void
1128 cleanup_bootblock(ib_bootblock_t *bblock)
1130 free(bblock->buf);
1131 bzero(bblock, sizeof (ib_bootblock_t));
1135 * Propagate the bootblock on the source disk to the destination disk and
1136 * version it with 'updt_str' in the process. Since we cannot trust any data
1137 * on the attaching disk, we do not perform any specific check on a potential
1138 * target extended information structure and we just blindly update.
1140 static int
1141 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
1143 ib_bootblock_t *src_bblock = &src->bootblock;
1144 ib_bootblock_t *dest_bblock = &dest->bootblock;
1146 assert(src != NULL);
1147 assert(dest != NULL);
1149 /* read the stage1 file from source disk */
1150 if (read(src->device.fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) {
1151 (void) fprintf(stderr, gettext("cannot read stage1 from %s\n"),
1152 src->device.path);
1153 return (BC_ERROR);
1156 cleanup_bootblock(dest_bblock);
1158 dest_bblock->buf_size = src_bblock->buf_size;
1159 dest_bblock->buf = malloc(dest_bblock->buf_size);
1160 if (dest_bblock->buf == NULL) {
1161 perror(gettext("Memory Allocation Failure"));
1162 return (BC_ERROR);
1164 dest_bblock->file = dest_bblock->buf;
1165 dest_bblock->file_size = src_bblock->file_size;
1166 (void) memcpy(dest_bblock->buf, src_bblock->buf,
1167 dest_bblock->buf_size);
1169 dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
1170 src_bblock->mboot_off);
1171 dest_bblock->mboot_off = src_bblock->mboot_off;
1172 dest_bblock->extra = (char *)dest_bblock->file +
1173 P2ROUNDUP(dest_bblock->file_size, 8);
1174 dest_bblock->extra_size = src_bblock->extra_size;
1176 (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
1177 src->device.path, dest->device.path);
1179 return (commit_to_disk(dest, updt_str));
1182 static int
1183 commit_to_disk(ib_data_t *data, char *update_str)
1185 assert(data != NULL);
1187 if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
1188 (void) fprintf(stderr, gettext("Error updating the bootblock "
1189 "image\n"));
1190 return (BC_ERROR);
1193 if (prepare_stage1(data) != BC_SUCCESS) {
1194 (void) fprintf(stderr, gettext("Error updating the stage1 "
1195 "image\n"));
1196 return (BC_ERROR);
1199 if (write_bootblock(data) != BC_SUCCESS) {
1200 (void) fprintf(stderr, gettext("Error writing bootblock to "
1201 "disk\n"));
1202 return (BC_ERROR);
1205 return (write_stage1(data));
1209 * Install a new bootblock on the given device. handle_install() expects argv
1210 * to contain 3 parameters (the target device path and the path to the
1211 * bootblock.
1213 * Returns: BC_SUCCESS - if the installation is successful
1214 * BC_ERROR - if the installation failed
1215 * BC_NOUPDT - if no installation was performed because the
1216 * version currently installed is more recent than the
1217 * supplied one.
1220 static int
1221 handle_install(char *progname, char **argv)
1223 ib_data_t install_data;
1224 char *stage1 = NULL;
1225 char *bootblock = NULL;
1226 char *device_path = NULL;
1227 int ret = BC_ERROR;
1229 stage1 = strdup(argv[0]);
1230 bootblock = strdup(argv[1]);
1231 device_path = strdup(argv[2]);
1233 if (!device_path || !bootblock || !stage1) {
1234 (void) fprintf(stderr, gettext("Missing parameter"));
1235 usage(progname);
1236 goto out;
1239 BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
1240 device_path, stage1, bootblock);
1241 bzero(&install_data, sizeof (ib_data_t));
1243 if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
1244 (void) fprintf(stderr, gettext("Unable to open device %s\n"),
1245 device_path);
1246 goto out;
1249 if (read_stage1_from_file(stage1, &install_data) != BC_SUCCESS) {
1250 (void) fprintf(stderr, gettext("Error opening %s\n"), stage1);
1251 goto out_dev;
1254 if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
1255 (void) fprintf(stderr, gettext("Error reading %s\n"),
1256 bootblock);
1257 goto out_dev;
1261 * is_update_necessary() will take care of checking if versioning and/or
1262 * forcing the update have been specified. It will also emit a warning
1263 * if a non-versioned update is attempted over a versioned bootblock.
1265 if (!is_update_necessary(&install_data, update_str)) {
1266 (void) fprintf(stderr, gettext("bootblock version installed "
1267 "on %s is more recent or identical\n"
1268 "Use -F to override or install without the -u option\n"),
1269 device_path);
1270 ret = BC_NOUPDT;
1271 goto out_dev;
1274 BOOT_DEBUG("Ready to commit to disk\n");
1275 ret = commit_to_disk(&install_data, update_str);
1277 out_dev:
1278 cleanup_device(&install_data.device);
1279 out:
1280 free(stage1);
1281 free(bootblock);
1282 free(device_path);
1283 return (ret);
1287 * Retrieves from a device the extended information (einfo) associated to the
1288 * installed stage2.
1289 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
1290 * Returns:
1291 * - BC_SUCCESS (and prints out einfo contents depending on 'flags')
1292 * - BC_ERROR (on error)
1293 * - BC_NOEINFO (no extended information available)
1295 static int
1296 handle_getinfo(char *progname, char **argv)
1299 ib_data_t data;
1300 ib_bootblock_t *bblock = &data.bootblock;
1301 ib_device_t *device = &data.device;
1302 bblk_einfo_t *einfo;
1303 uint8_t flags = 0;
1304 char *device_path, *path;
1305 int retval = BC_ERROR;
1306 int ret;
1308 device_path = strdup(argv[0]);
1309 if (!device_path) {
1310 (void) fprintf(stderr, gettext("Missing parameter"));
1311 usage(progname);
1312 goto out;
1315 bzero(&data, sizeof (ib_data_t));
1316 BOOT_DEBUG("device path: %s\n", device_path);
1318 if (init_device(device, device_path) != BC_SUCCESS) {
1319 (void) fprintf(stderr, gettext("Unable to gather device "
1320 "information from %s\n"), device_path);
1321 goto out_dev;
1324 ret = read_bootblock_from_disk(device, bblock, &path);
1325 if (ret == BC_ERROR) {
1326 (void) fprintf(stderr, gettext("Error reading bootblock from "
1327 "%s\n"), path);
1328 goto out_dev;
1331 if (ret == BC_NOEXTRA) {
1332 BOOT_DEBUG("No multiboot header found on %s, unable "
1333 "to locate extra information area (old/non versioned "
1334 "bootblock?) \n", device_path);
1335 (void) fprintf(stderr, gettext("No extended information "
1336 "found\n"));
1337 retval = BC_NOEINFO;
1338 goto out_dev;
1341 einfo = find_einfo(bblock->extra, bblock->extra_size);
1342 if (einfo == NULL) {
1343 retval = BC_NOEINFO;
1344 (void) fprintf(stderr, gettext("No extended information "
1345 "found\n"));
1346 goto out_dev;
1349 /* Print the extended information. */
1350 if (strip)
1351 flags |= EINFO_EASY_PARSE;
1352 if (verbose_dump)
1353 flags |= EINFO_PRINT_HEADER;
1355 print_einfo(flags, einfo, bblock->extra_size);
1356 retval = BC_SUCCESS;
1358 out_dev:
1359 cleanup_device(&data.device);
1360 out:
1361 free(device_path);
1362 return (retval);
1366 * Attempt to mirror (propagate) the current bootblock over the attaching disk.
1368 * Returns:
1369 * - BC_SUCCESS (a successful propagation happened)
1370 * - BC_ERROR (an error occurred)
1371 * - BC_NOEXTRA (it is not possible to dump the current bootblock since
1372 * there is no multiboot information)
1374 static int
1375 handle_mirror(char *progname, char **argv)
1377 ib_data_t curr_data;
1378 ib_data_t attach_data;
1379 ib_device_t *curr_device = &curr_data.device;
1380 ib_device_t *attach_device = &attach_data.device;
1381 ib_bootblock_t *bblock_curr = &curr_data.bootblock;
1382 ib_bootblock_t *bblock_attach = &attach_data.bootblock;
1383 bblk_einfo_t *einfo_curr = NULL;
1384 char *curr_device_path;
1385 char *attach_device_path;
1386 char *updt_str = NULL;
1387 char *path;
1388 int retval = BC_ERROR;
1389 int ret;
1391 curr_device_path = strdup(argv[0]);
1392 attach_device_path = strdup(argv[1]);
1394 if (!curr_device_path || !attach_device_path) {
1395 (void) fprintf(stderr, gettext("Missing parameter"));
1396 usage(progname);
1397 goto out;
1399 BOOT_DEBUG("Current device path is: %s, attaching device path is: "
1400 " %s\n", curr_device_path, attach_device_path);
1402 bzero(&curr_data, sizeof (ib_data_t));
1403 bzero(&attach_data, sizeof (ib_data_t));
1405 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
1406 (void) fprintf(stderr, gettext("Unable to gather device "
1407 "information from %s (current device)\n"),
1408 curr_device_path);
1409 goto out_currdev;
1412 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
1413 (void) fprintf(stderr, gettext("Unable to gather device "
1414 "information from %s (attaching device)\n"),
1415 attach_device_path);
1416 goto out_devs;
1419 ret = read_bootblock_from_disk(curr_device, bblock_curr, &path);
1420 if (ret == BC_ERROR) {
1421 BOOT_DEBUG("Error reading bootblock from %s\n", path);
1422 retval = BC_ERROR;
1423 goto out_devs;
1426 if (ret == BC_NOEXTRA) {
1427 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
1428 " the bootblock\n", path);
1429 retval = BC_NOEXTRA;
1430 goto out_devs;
1433 write_mbr = B_TRUE;
1434 force_mbr = B_TRUE;
1435 einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size);
1436 if (einfo_curr != NULL)
1437 updt_str = einfo_get_string(einfo_curr);
1439 retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
1440 cleanup_bootblock(bblock_curr);
1441 cleanup_bootblock(bblock_attach);
1442 out_devs:
1443 cleanup_device(attach_device);
1444 out_currdev:
1445 cleanup_device(curr_device);
1446 out:
1447 free(curr_device_path);
1448 free(attach_device_path);
1449 return (retval);
1452 #define USAGE_STRING "Usage:\t%s [-h|-m|-f|-n|-F|-u verstr] stage1 stage2 " \
1453 "raw-device\n" \
1454 "\t%s -M [-n] raw-device attach-raw-device\n" \
1455 "\t%s [-e|-V] -i raw-device\n"
1457 #define CANON_USAGE_STR gettext(USAGE_STRING)
1459 static void
1460 usage(char *progname)
1462 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
1466 main(int argc, char **argv)
1468 int opt;
1469 int params = 3;
1470 int ret;
1471 char *progname;
1472 char **handle_args;
1474 (void) setlocale(LC_ALL, "");
1475 (void) textdomain(TEXT_DOMAIN);
1476 if (init_yes() < 0) {
1477 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
1478 strerror(errno));
1479 exit(BC_ERROR);
1482 while ((opt = getopt(argc, argv, "deFfhiMmnu:V")) != EOF) {
1483 switch (opt) {
1484 case 'd':
1485 boot_debug = B_TRUE;
1486 break;
1487 case 'e':
1488 strip = B_TRUE;
1489 break;
1490 case 'F':
1491 force_update = B_TRUE;
1492 break;
1493 case 'f':
1494 force_mbr = B_TRUE;
1495 break;
1496 case 'h':
1497 usage(argv[0]);
1498 exit(BC_SUCCESS);
1499 break;
1500 case 'i':
1501 do_getinfo = B_TRUE;
1502 params = 1;
1503 break;
1504 case 'M':
1505 do_mirror_bblk = B_TRUE;
1506 params = 2;
1507 break;
1508 case 'm':
1509 write_mbr = B_TRUE;
1510 break;
1511 case 'n':
1512 nowrite = B_TRUE;
1513 break;
1514 case 'u':
1515 do_version = B_TRUE;
1517 update_str = malloc(strlen(optarg) + 1);
1518 if (update_str == NULL) {
1519 perror(gettext("Memory allocation failure"));
1520 exit(BC_ERROR);
1522 (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
1523 break;
1524 case 'V':
1525 verbose_dump = B_TRUE;
1526 break;
1527 default:
1528 /* fall through to process non-optional args */
1529 break;
1533 /* check arguments */
1534 if (argc != optind + params) {
1535 usage(argv[0]);
1536 exit(BC_ERROR);
1538 progname = argv[0];
1539 check_options(progname);
1540 handle_args = argv + optind;
1542 if (nowrite)
1543 (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
1544 " be written to disk.\n"));
1546 if (do_getinfo) {
1547 ret = handle_getinfo(progname, handle_args);
1548 } else if (do_mirror_bblk) {
1549 ret = handle_mirror(progname, handle_args);
1550 } else {
1551 ret = handle_install(progname, handle_args);
1553 return (ret);
1556 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
1557 static void
1558 check_options(char *progname)
1560 if (do_getinfo && do_mirror_bblk) {
1561 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
1562 "specified at the same time\n"));
1563 usage(progname);
1564 exit(BC_ERROR);
1567 if (do_mirror_bblk) {
1569 * -u and -F may actually reflect a user intent that is not
1570 * correct with this command (mirror can be interpreted
1571 * "similar" to install. Emit a message and continue.
1572 * -e and -V have no meaning, be quiet here and only report the
1573 * incongruence if a debug output is requested.
1575 if (do_version) {
1576 (void) fprintf(stderr, MEANINGLESS_OPT, "-u");
1577 do_version = B_FALSE;
1579 if (force_update) {
1580 (void) fprintf(stderr, MEANINGLESS_OPT, "-F");
1581 force_update = B_FALSE;
1583 if (strip || verbose_dump) {
1584 BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
1585 strip = B_FALSE;
1586 verbose_dump = B_FALSE;
1590 if (do_getinfo) {
1591 if (write_mbr || force_mbr || do_version || force_update) {
1592 BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
1593 write_mbr = force_mbr = do_version = B_FALSE;
1594 force_update = B_FALSE;