installboot: implement loader support for i386 platform
[unleashed.git] / usr / src / cmd / boot / installboot / i386 / installboot.c
blob5f54db9822a20831e6ca85bbab0aceb9cc5bbf70
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);
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 if (device->devtype == IG_DEV_MBR) {
256 dev_fd = device->fd;
257 offset = BBLK_BLKLIST_OFF * SECTOR_SIZE;
258 *path = device->path;
259 } else {
260 dev_fd = device->stage.fd;
261 offset = device->stage.offset * SECTOR_SIZE;
262 *path = device->stage.path;
266 if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan), offset)
267 != BC_SUCCESS) {
268 BOOT_DEBUG("Error reading bootblock area\n");
269 perror("read");
270 return (BC_ERROR);
273 /* No multiboot means no chance of knowing bootblock size */
274 if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
275 != BC_SUCCESS) {
276 BOOT_DEBUG("Unable to find multiboot header\n");
277 return (BC_NOEXTRA);
279 mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
282 * make sure mboot has sane values
284 if (mboot->load_end_addr == 0 ||
285 mboot->load_end_addr < mboot->load_addr)
286 return (BC_NOEXTRA);
289 * Currently, the amount of space reserved for extra information
290 * is "fixed". We may have to scan for the terminating extra payload
291 * in the future.
293 size = mboot->load_end_addr - mboot->load_addr;
294 buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
295 bblock->file_size = size;
297 bblock->buf = malloc(buf_size);
298 if (bblock->buf == NULL) {
299 BOOT_DEBUG("Unable to allocate enough memory to read"
300 " the extra bootblock from the disk\n");
301 perror(gettext("Memory allocation failure"));
302 return (BC_ERROR);
304 bblock->buf_size = buf_size;
306 if (read_in(dev_fd, bblock->buf, buf_size, offset) != BC_SUCCESS) {
307 BOOT_DEBUG("Error reading the bootblock\n");
308 (void) free(bblock->buf);
309 bblock->buf = NULL;
310 return (BC_ERROR);
313 /* Update pointers. */
314 bblock->file = bblock->buf;
315 bblock->mboot_off = mboot_off;
316 bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off);
317 bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
318 bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
320 BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p "
321 "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra,
322 bblock->extra_size, bblock->buf, bblock->buf_size);
324 return (BC_SUCCESS);
327 static boolean_t
328 is_update_necessary(ib_data_t *data, char *updt_str)
330 bblk_einfo_t *einfo;
331 bblk_einfo_t *einfo_file;
332 bblk_hs_t bblock_hs;
333 ib_bootblock_t bblock_disk;
334 ib_bootblock_t *bblock_file = &data->bootblock;
335 ib_device_t *device = &data->device;
336 int ret;
337 char *path;
339 assert(data != NULL);
341 bzero(&bblock_disk, sizeof (ib_bootblock_t));
343 ret = read_bootblock_from_disk(device, &bblock_disk, &path);
344 if (ret != BC_SUCCESS) {
345 BOOT_DEBUG("Unable to read bootblock from %s\n", path);
346 return (B_TRUE);
349 einfo = find_einfo(bblock_disk.extra, bblock_disk.extra_size);
350 if (einfo == NULL) {
351 BOOT_DEBUG("No extended information available on disk\n");
352 return (B_TRUE);
355 einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size);
356 if (einfo_file == NULL) {
358 * loader bootblock is versioned. missing version means
359 * probably incompatible block. installboot can not install
360 * grub, for example.
362 (void) fprintf(stderr,
363 gettext("ERROR: non versioned bootblock in file\n"));
364 return (B_FALSE);
365 } else {
366 if (updt_str == NULL) {
367 updt_str = einfo_get_string(einfo_file);
368 do_version = B_TRUE;
372 if (!do_version || updt_str == NULL) {
373 (void) fprintf(stderr,
374 gettext("WARNING: target device %s has a "
375 "versioned bootblock that is going to be overwritten by a "
376 "non versioned one\n"), device->path);
377 return (B_TRUE);
380 if (force_update) {
381 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
382 return (B_TRUE);
385 BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
387 bblock_hs.src_buf = (unsigned char *)bblock_file->file;
388 bblock_hs.src_size = bblock_file->file_size;
390 return (einfo_should_update(einfo, &bblock_hs, updt_str));
393 static void
394 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
396 bblk_hs_t hs;
397 uint32_t avail_space;
399 assert(bblock != NULL);
401 if (updt_str == NULL) {
402 BOOT_DEBUG("WARNING: no update string passed to "
403 "add_bootblock_einfo()\n");
404 return;
407 /* Fill bootblock hashing source information. */
408 hs.src_buf = (unsigned char *)bblock->file;
409 hs.src_size = bblock->file_size;
410 /* How much space for the extended information structure? */
411 avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
412 /* Place the extended information structure. */
413 add_einfo(bblock->extra, updt_str, &hs, avail_space);
417 * set up data for case stage1 is installed as MBR
418 * set up location and size of bootblock
419 * set disk guid to provide unique information for biosdev command
421 static int
422 prepare_stage1(ib_data_t *data)
424 ib_device_t *device;
426 assert(data != NULL);
427 device = &data->device;
429 /* copy BPB */
430 bcopy(device->mbr + STAGE1_BPB_OFFSET,
431 data->stage1 + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
434 /* copy MBR, note STAGE1_SIG == BOOTSZ */
435 bcopy(device->mbr + STAGE1_SIG, data->stage1 + STAGE1_SIG,
436 SECTOR_SIZE - STAGE1_SIG);
438 /* set stage2 size */
439 *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) =
440 (uint16_t)(data->bootblock.buf_size / SECTOR_SIZE);
443 * set stage2 location.
444 * for zfs always use zfs embedding, for ufs/pcfs use partition_start
445 * as base for stage2 location, for ufs/pcfs in MBR partition, use
446 * free space after MBR record.
448 if (device->target.fstype == IG_FS_ZFS)
449 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
450 device->target.start + device->target.offset;
451 else {
452 if (device->devtype == IG_DEV_MBR)
453 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
454 BBLK_BLKLIST_OFF;
455 else
456 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
457 device->stage.start + device->stage.offset;
461 * set disk uuid. we only need reasonable amount of uniqueness
462 * to allow biosdev to identify disk based on mbr differences.
464 uuid_generate(data->stage1 + STAGE1_STAGE2_UUID);
466 return (BC_SUCCESS);
469 static int
470 prepare_bootblock(ib_data_t *data, char *updt_str)
472 ib_bootblock_t *bblock;
473 ib_device_t *device;
474 uint64_t *ptr;
476 assert(data != NULL);
478 bblock = &data->bootblock;
479 device = &data->device;
481 ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
482 *ptr = device->target.start;
485 * the loader bootblock has built in version, if custom
486 * version was provided, update it.
488 if (do_version)
489 add_bootblock_einfo(bblock, updt_str);
491 return (BC_SUCCESS);
494 static int
495 write_bootblock(ib_data_t *data)
497 ib_device_t *device = &data->device;
498 ib_bootblock_t *bblock = &data->bootblock;
499 uint64_t abs;
500 int dev_fd, ret;
501 off_t offset;
502 char *path;
504 assert(data != NULL);
507 * ZFS bootblock area is 3.5MB, make sure we can fit.
508 * buf_size is size of bootblk+EINFO.
510 if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) {
511 (void) fprintf(stderr, gettext("bootblock is too large\n"));
512 return (BC_ERROR);
515 if (device->target.fstype == IG_FS_ZFS) {
516 dev_fd = device->target.fd;
517 abs = device->target.start + device->target.offset;
518 offset = BBLK_ZFS_BLK_OFF * SECTOR_SIZE;
519 path = device->target.path;
520 } else {
521 if (device->devtype == IG_DEV_MBR) {
522 dev_fd = device->fd;
523 abs = BBLK_BLKLIST_OFF;
524 offset = BBLK_BLKLIST_OFF * SECTOR_SIZE;
525 path = device->path;
526 } else {
527 dev_fd = device->stage.fd;
528 abs = device->stage.start + device->stage.offset;
529 offset = device->stage.offset * SECTOR_SIZE;
530 path = device->stage.path;
531 if (bblock->buf_size >
532 (device->stage.size - device->stage.offset) *
533 SECTOR_SIZE) {
534 (void) fprintf(stderr, gettext("Device %s is "
535 "too small to fit the stage2\n"), path);
536 return (BC_ERROR);
540 ret = write_out(dev_fd, bblock->buf, bblock->buf_size, offset);
541 if (ret != BC_SUCCESS) {
542 BOOT_DEBUG("Error writing the ZFS bootblock "
543 "to %s at offset %d\n", path, offset);
544 return (BC_ERROR);
547 (void) fprintf(stdout, gettext("bootblock written for %s,"
548 " %d sectors starting at %d (abs %lld)\n"), path,
549 (bblock->buf_size / SECTOR_SIZE) + 1, offset / SECTOR_SIZE, abs);
551 return (BC_SUCCESS);
554 static int
555 write_stage1(ib_data_t *data)
557 ib_device_t *device = &data->device;
559 assert(data != NULL);
562 * Partition boot block or volume boot record.
563 * This is essentially copy of MBR (1 sector) and we store it
564 * to support multi boot setups.
566 * Not all combinations are supported; as pcfs does not leave
567 * space, we will not write to pcfs target.
568 * In addition, in VTOC setup, we will only write VBR to slice 2.
570 if (strcmp(device->target.path, device->stage.path)) {
571 /* we got separate stage area, use it */
572 if (write_out(device->stage.fd, data->stage1,
573 sizeof (data->stage1), 0) != BC_SUCCESS) {
574 (void) fprintf(stdout, gettext("cannot write "
575 "partition boot sector\n"));
576 perror("write");
577 return (BC_ERROR);
580 (void) fprintf(stdout, gettext("stage1 written to "
581 "%s %d sector 0 (abs %d)\n"),
582 device->devtype == IG_DEV_MBR? "partition":"slice",
583 device->stage.id, device->stage.start);
587 * both ufs and zfs have initial 8k reserved for VTOC/boot
588 * so its safe to use this area. however, only
589 * write to target if we have MBR/GPT.
591 if (device->devtype != IG_DEV_VTOC &&
592 device->target.fstype != IG_FS_PCFS) {
593 if (write_out(device->target.fd, data->stage1,
594 sizeof (data->stage1), 0) != BC_SUCCESS) {
595 (void) fprintf(stdout, gettext("cannot write "
596 "partition boot sector\n"));
597 perror("write");
598 return (BC_ERROR);
601 (void) fprintf(stdout, gettext("stage1 written to "
602 "%s %d sector 0 (abs %d)\n"),
603 device->devtype == IG_DEV_MBR? "partition":"slice",
604 device->target.id, device->target.start);
607 if (write_mbr) {
608 if (write_out(device->fd, data->stage1,
609 sizeof (data->stage1), 0) != BC_SUCCESS) {
610 (void) fprintf(stdout,
611 gettext("cannot write master boot sector\n"));
612 perror("write");
613 return (BC_ERROR);
615 (void) fprintf(stdout,
616 gettext("stage1 written to master boot sector\n"));
619 return (BC_SUCCESS);
623 * find partition/slice start sector. will be recorded in stage2 and used
624 * by stage2 to identify partition with boot file system.
626 static int
627 get_start_sector(ib_device_t *device)
629 uint32_t secnum = 0, numsec = 0;
630 int i, pno, rval, log_part = 0;
631 struct mboot *mboot;
632 struct ipart *part;
633 ext_part_t *epp;
634 struct part_info dkpi;
635 struct extpart_info edkpi;
637 if (device->devtype == IG_DEV_EFI) {
638 struct dk_gpt *vtoc;
640 if (efi_alloc_and_read(device->fd, &vtoc) < 0)
641 return (BC_ERROR);
643 if (device->stage.start == 0) {
644 /* zero size means the fstype must be zfs */
645 assert(device->target.fstype == IG_FS_ZFS);
647 device->stage.start =
648 vtoc->efi_parts[device->stage.id].p_start;
649 device->stage.size =
650 vtoc->efi_parts[device->stage.id].p_size;
651 device->stage.offset = BBLK_ZFS_BLK_OFF;
652 device->target.offset = BBLK_ZFS_BLK_OFF;
655 device->target.start =
656 vtoc->efi_parts[device->target.id].p_start;
657 device->target.size =
658 vtoc->efi_parts[device->target.id].p_size;
660 /* with pcfs we always write MBR */
661 if (device->target.fstype == IG_FS_PCFS) {
662 force_mbr = 1;
663 write_mbr = 1;
666 efi_free(vtoc);
667 goto found_part;
670 mboot = (struct mboot *)device->mbr;
672 if (device->devtype == IG_DEV_MBR) {
673 /* MBR partition starts from 0 */
674 pno = device->target.id - 1;
675 part = (struct ipart *)mboot->parts + pno;
677 if (part->relsect == 0) {
678 (void) fprintf(stderr, gettext("Partition %d of the "
679 "disk has an incorrect offset\n"),
680 device->target.id);
681 return (BC_ERROR);
683 device->target.start = part->relsect;
684 device->target.size = part->numsect;
686 /* with pcfs we always write MBR */
687 if (device->target.fstype == IG_FS_PCFS) {
688 force_mbr = 1;
689 write_mbr = 1;
691 if (device->target.fstype == IG_FS_ZFS)
692 device->target.offset = BBLK_ZFS_BLK_OFF;
694 pno = device->stage.id - 1;
695 part = (struct ipart *)mboot->parts + pno;
697 if (part->relsect == 0) {
698 (void) fprintf(stderr, gettext("Partition %d of the "
699 "disk has an incorrect offset\n"),
700 device->stage.id);
701 return (BC_ERROR);
703 device->stage.start = part->relsect;
704 device->stage.size = part->numsect;
705 if (device->target.fstype == IG_FS_ZFS)
706 device->stage.offset = BBLK_ZFS_BLK_OFF;
707 goto found_part;
711 * Search for Solaris fdisk partition
712 * Get the solaris partition information from the device
713 * and compare the offset of S2 with offset of solaris partition
714 * from fdisk partition table.
716 if (ioctl(device->target.fd, DKIOCEXTPARTINFO, &edkpi) < 0) {
717 if (ioctl(device->target.fd, DKIOCPARTINFO, &dkpi) < 0) {
718 (void) fprintf(stderr, gettext("cannot get the "
719 "slice information of the disk\n"));
720 return (BC_ERROR);
721 } else {
722 edkpi.p_start = dkpi.p_start;
723 edkpi.p_length = dkpi.p_length;
727 device->target.start = edkpi.p_start;
728 device->target.size = edkpi.p_length;
729 if (device->target.fstype == IG_FS_ZFS)
730 device->target.offset = BBLK_ZFS_BLK_OFF;
732 for (i = 0; i < FD_NUMPART; i++) {
733 part = (struct ipart *)mboot->parts + i;
735 if (part->relsect == 0) {
736 (void) fprintf(stderr, gettext("Partition %d of the "
737 "disk has an incorrect offset\n"), i+1);
738 return (BC_ERROR);
741 if (edkpi.p_start >= part->relsect &&
742 edkpi.p_start < (part->relsect + part->numsect)) {
743 /* Found the partition */
744 break;
748 if (i == FD_NUMPART) {
749 /* No solaris fdisk partitions (primary or logical) */
750 (void) fprintf(stderr, gettext("Solaris partition not found. "
751 "Aborting operation.\n"));
752 return (BC_ERROR);
756 * We have found a Solaris fdisk partition (primary or extended)
757 * Handle the simple case first: Solaris in a primary partition
759 if (!fdisk_is_dos_extended(part->systid)) {
760 device->stage.start = part->relsect;
761 device->stage.size = part->numsect;
762 if (device->target.fstype == IG_FS_ZFS)
763 device->stage.offset = BBLK_ZFS_BLK_OFF;
764 else
765 device->stage.offset = BBLK_BLKLIST_OFF;
766 device->stage.id = i + 1;
767 goto found_part;
771 * Solaris in a logical partition. Find that partition in the
772 * extended part.
775 if ((rval = libfdisk_init(&epp, device->path, NULL, FDISK_READ_DISK))
776 != FDISK_SUCCESS) {
777 switch (rval) {
779 * The first 3 cases are not an error per-se, just that
780 * there is no Solaris logical partition
782 case FDISK_EBADLOGDRIVE:
783 case FDISK_ENOLOGDRIVE:
784 case FDISK_EBADMAGIC:
785 (void) fprintf(stderr, gettext("Solaris "
786 "partition not found. "
787 "Aborting operation.\n"));
788 return (BC_ERROR);
789 case FDISK_ENOVGEOM:
790 (void) fprintf(stderr, gettext("Could not get "
791 "virtual geometry\n"));
792 return (BC_ERROR);
793 case FDISK_ENOPGEOM:
794 (void) fprintf(stderr, gettext("Could not get "
795 "physical geometry\n"));
796 return (BC_ERROR);
797 case FDISK_ENOLGEOM:
798 (void) fprintf(stderr, gettext("Could not get "
799 "label geometry\n"));
800 return (BC_ERROR);
801 default:
802 (void) fprintf(stderr, gettext("Failed to "
803 "initialize libfdisk.\n"));
804 return (BC_ERROR);
808 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
809 libfdisk_fini(&epp);
810 if (rval != FDISK_SUCCESS) {
811 /* No solaris logical partition */
812 (void) fprintf(stderr, gettext("Solaris partition not found. "
813 "Aborting operation.\n"));
814 return (BC_ERROR);
817 device->stage.start = secnum;
818 device->stage.size = numsec;
819 device->stage.id = pno;
820 log_part = 1;
822 found_part:
823 /* get confirmation for -m */
824 if (write_mbr && !force_mbr) {
825 (void) fprintf(stdout, gettext("Updating master boot sector "
826 "destroys existing boot managers (if any).\n"
827 "continue (y/n)? "));
828 if (!yes()) {
829 write_mbr = 0;
830 (void) fprintf(stdout, gettext("master boot sector "
831 "not updated\n"));
832 return (BC_ERROR);
837 * warn, if illumos in primary partition and loader not in MBR and
838 * partition is not active
840 if (device->devtype != IG_DEV_EFI) {
841 if (!log_part && part->bootid != 128 && !write_mbr) {
842 (void) fprintf(stdout, gettext("Solaris fdisk "
843 "partition is inactive.\n"), device->stage.id);
847 return (BC_SUCCESS);
850 static int
851 open_device(char *path)
853 struct stat statbuf = {0};
854 int fd = -1;
856 if (nowrite)
857 fd = open(path, O_RDONLY);
858 else
859 fd = open(path, O_RDWR);
861 if (fd == -1) {
862 BOOT_DEBUG("Unable to open %s\n", path);
863 perror("open");
864 return (-1);
867 if (fstat(fd, &statbuf) != 0) {
868 BOOT_DEBUG("Unable to stat %s\n", path);
869 perror("stat");
870 (void) close(fd);
871 return (-1);
874 if (S_ISCHR(statbuf.st_mode) == 0) {
875 (void) fprintf(stderr, gettext("%s: Not a character device\n"),
876 path);
877 (void) close(fd);
878 return (-1);
881 return (fd);
884 static int
885 get_boot_partition(ib_device_t *device, struct dk_gpt *vtoc)
887 uint_t i;
888 char *path, *ptr;
890 for (i = 0; i < vtoc->efi_nparts; i++) {
891 if (vtoc->efi_parts[i].p_tag == V_BOOT) {
892 if ((path = strdup(device->target.path)) == NULL) {
893 perror(gettext("Memory allocation failure"));
894 return (BC_ERROR);
896 ptr = strrchr(path, 's');
897 ptr++;
898 *ptr = '\0';
899 (void) asprintf(&ptr, "%s%d", path, i);
900 free(path);
901 if (ptr == NULL) {
902 perror(gettext("Memory allocation failure"));
903 return (BC_ERROR);
905 device->stage.path = ptr;
906 device->stage.fd = open_device(ptr);
907 device->stage.id = i;
908 device->stage.devtype = IG_DEV_EFI;
909 device->stage.fstype = IG_FS_NONE;
910 device->stage.start = vtoc->efi_parts[i].p_start;
911 device->stage.size = vtoc->efi_parts[i].p_size;
912 device->stage.offset = 1; /* leave sector 0 for VBR */
913 return (BC_SUCCESS);
916 return (BC_SUCCESS);
919 static int
920 init_device(ib_device_t *device, char *path)
922 struct dk_gpt *vtoc;
923 fstyp_handle_t fhdl;
924 const char *fident;
925 char *p;
926 int pathlen = strlen(path);
927 int ret;
929 bzero(device, sizeof (*device));
930 device->fd = -1; /* whole disk fd */
931 device->stage.fd = -1; /* bootblock partition fd */
932 device->target.fd = -1; /* target fs partition fd */
934 /* basic check, whole disk is not allowed */
935 if ((p = strrchr(path, '/')) == NULL)
936 p = path;
937 if ((strrchr(p, 'p') == NULL && strrchr(p, 's') == NULL) ||
938 (path[pathlen-2] == 'p' && path[pathlen-1] == '0')) {
939 (void) fprintf(stderr, gettext("installing loader to "
940 "whole disk device is not supported\n"));
943 device->target.path = strdup(path);
944 if (device->target.path == NULL) {
945 perror(gettext("Memory allocation failure"));
946 return (BC_ERROR);
948 device->path = strdup(path);
949 if (device->path == NULL) {
950 perror(gettext("Memory allocation failure"));
951 return (BC_ERROR);
954 /* change device name to p0 */
955 device->path[pathlen - 2] = 'p';
956 device->path[pathlen - 1] = '0';
958 if (strstr(device->target.path, "diskette")) {
959 (void) fprintf(stderr, gettext("installing loader to a floppy "
960 "disk is not supported\n"));
961 return (BC_ERROR);
964 /* Detect if the target device is a pcfs partition. */
965 if (strstr(device->target.path, "p0:boot")) {
966 (void) fprintf(stderr, gettext("installing loader to x86 boot "
967 "partition is not supported\n"));
968 return (BC_ERROR);
971 if ((device->fd = open_device(device->path)) == -1)
972 return (BC_ERROR);
974 /* read in the device boot sector. */
975 if (read(device->fd, device->mbr, SECTOR_SIZE) != SECTOR_SIZE) {
976 (void) fprintf(stderr, gettext("Error reading boot sector\n"));
977 perror("read");
978 return (BC_ERROR);
981 device->devtype = IG_DEV_VTOC;
982 if (efi_alloc_and_read(device->fd, &vtoc) >= 0) {
983 ret = get_boot_partition(device, vtoc);
984 device->devtype = IG_DEV_EFI;
985 efi_free(vtoc);
986 if (ret == BC_ERROR)
987 return (BC_ERROR);
988 } else if (device->target.path[pathlen - 2] == 'p')
989 device->devtype = IG_DEV_MBR;
990 else if (device->target.path[pathlen - 1] == '2') {
992 * NOTE: we could relax there and allow zfs boot on
993 * slice 2 for instance, but lets keep traditional limits.
995 (void) fprintf(stderr,
996 gettext("raw device must be a root slice (not s2)\n"));
997 return (BC_ERROR);
1000 /* fill stage partition for case there is no boot partition */
1001 if (device->stage.path == NULL) {
1002 if ((device->stage.path = strdup(path)) == NULL) {
1003 perror(gettext("Memory allocation failure"));
1004 return (BC_ERROR);
1006 if (device->devtype == IG_DEV_VTOC) {
1007 /* use slice 2 */
1008 device->stage.path[pathlen - 2] = 's';
1009 device->stage.path[pathlen - 1] = '2';
1010 device->stage.id = 2;
1011 } else {
1012 p = strrchr(device->stage.path, 'p');
1013 if (p == NULL)
1014 p = strrchr(device->stage.path, 's');
1015 device->stage.id = atoi(++p);
1017 device->stage.devtype = device->devtype;
1018 device->stage.fd = open_device(device->stage.path);
1021 p = strrchr(device->target.path, 'p');
1022 if (p == NULL)
1023 p = strrchr(device->target.path, 's');
1024 device->target.id = atoi(++p);
1026 if (strcmp(device->stage.path, device->target.path) == 0)
1027 device->target.fd = dup(device->stage.fd);
1028 else
1029 device->target.fd = open_device(device->target.path);
1031 if (fstyp_init(device->target.fd, 0, NULL, &fhdl) != 0)
1032 return (BC_ERROR);
1034 if (fstyp_ident(fhdl, NULL, &fident) != 0) {
1035 fstyp_fini(fhdl);
1036 (void) fprintf(stderr, gettext("Failed to detect file "
1037 "system type\n"));
1038 return (BC_ERROR);
1041 /* at this moment non-boot partition has no size set, use this fact */
1042 if (device->devtype == IG_DEV_EFI && strcmp(fident, "zfs") &&
1043 device->stage.size == 0) {
1044 fstyp_fini(fhdl);
1045 (void) fprintf(stderr, gettext("Booting %s of EFI labeled "
1046 "disks requires the boot partition.\n"), fident);
1047 return (BC_ERROR);
1049 if (strcmp(fident, "zfs") == 0)
1050 device->target.fstype = IG_FS_ZFS;
1051 else if (strcmp(fident, "ufs") == 0) {
1052 device->target.fstype = IG_FS_UFS;
1053 } else if (strcmp(fident, "pcfs") == 0) {
1054 device->target.fstype = IG_FS_PCFS;
1055 } else {
1056 (void) fprintf(stderr, gettext("File system %s is not "
1057 "supported by loader\n"), fident);
1058 fstyp_fini(fhdl);
1059 return (BC_ERROR);
1061 fstyp_fini(fhdl);
1063 /* check for boot partition content */
1064 if (device->stage.size) {
1065 if (fstyp_init(device->stage.fd, 0, NULL, &fhdl) != 0)
1066 return (BC_ERROR);
1068 if (fstyp_ident(fhdl, NULL, &fident) == 0) {
1069 (void) fprintf(stderr, gettext("Unexpected %s file "
1070 "system on boot partition\n"), fident);
1071 fstyp_fini(fhdl);
1072 return (BC_ERROR);
1074 fstyp_fini(fhdl);
1076 return (get_start_sector(device));
1079 static void
1080 cleanup_device(ib_device_t *device)
1082 if (device->path)
1083 free(device->path);
1084 if (device->stage.path)
1085 free(device->stage.path);
1086 if (device->target.path)
1087 free(device->target.path);
1089 if (device->fd != -1)
1090 (void) close(device->fd);
1091 if (device->stage.fd != -1)
1092 (void) close(device->stage.fd);
1093 if (device->target.fd != -1)
1094 (void) close(device->target.fd);
1095 bzero(device, sizeof (*device));
1098 static void
1099 cleanup_bootblock(ib_bootblock_t *bblock)
1101 free(bblock->buf);
1102 bzero(bblock, sizeof (ib_bootblock_t));
1106 * Propagate the bootblock on the source disk to the destination disk and
1107 * version it with 'updt_str' in the process. Since we cannot trust any data
1108 * on the attaching disk, we do not perform any specific check on a potential
1109 * target extended information structure and we just blindly update.
1111 static int
1112 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
1114 ib_bootblock_t *src_bblock = &src->bootblock;
1115 ib_bootblock_t *dest_bblock = &dest->bootblock;
1117 assert(src != NULL);
1118 assert(dest != NULL);
1120 /* read the stage1 file from source disk */
1121 if (read(src->device.fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) {
1122 (void) fprintf(stderr, gettext("cannot read stage1 from %s\n"),
1123 src->device.path);
1124 return (BC_ERROR);
1127 cleanup_bootblock(dest_bblock);
1129 dest_bblock->buf_size = src_bblock->buf_size;
1130 dest_bblock->buf = malloc(dest_bblock->buf_size);
1131 if (dest_bblock->buf == NULL) {
1132 perror(gettext("Memory Allocation Failure"));
1133 return (BC_ERROR);
1135 dest_bblock->file = dest_bblock->buf;
1136 dest_bblock->file_size = src_bblock->file_size;
1137 (void) memcpy(dest_bblock->buf, src_bblock->buf,
1138 dest_bblock->buf_size);
1140 dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
1141 src_bblock->mboot_off);
1142 dest_bblock->mboot_off = src_bblock->mboot_off;
1143 dest_bblock->extra = (char *)dest_bblock->file +
1144 P2ROUNDUP(dest_bblock->file_size, 8);
1145 dest_bblock->extra_size = src_bblock->extra_size;
1147 (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
1148 src->device.path, dest->device.path);
1150 return (commit_to_disk(dest, updt_str));
1153 static int
1154 commit_to_disk(ib_data_t *data, char *update_str)
1156 assert(data != NULL);
1158 if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
1159 (void) fprintf(stderr, gettext("Error updating the bootblock "
1160 "image\n"));
1161 return (BC_ERROR);
1164 if (prepare_stage1(data) != BC_SUCCESS) {
1165 (void) fprintf(stderr, gettext("Error updating the stage1 "
1166 "image\n"));
1167 return (BC_ERROR);
1170 if (write_bootblock(data) != BC_SUCCESS) {
1171 (void) fprintf(stderr, gettext("Error writing bootblock to "
1172 "disk\n"));
1173 return (BC_ERROR);
1176 return (write_stage1(data));
1180 * Install a new bootblock on the given device. handle_install() expects argv
1181 * to contain 3 parameters (the target device path and the path to the
1182 * bootblock.
1184 * Returns: BC_SUCCESS - if the installation is successful
1185 * BC_ERROR - if the installation failed
1186 * BC_NOUPDT - if no installation was performed because the
1187 * version currently installed is more recent than the
1188 * supplied one.
1191 static int
1192 handle_install(char *progname, char **argv)
1194 ib_data_t install_data;
1195 char *stage1 = NULL;
1196 char *bootblock = NULL;
1197 char *device_path = NULL;
1198 int ret = BC_ERROR;
1200 stage1 = strdup(argv[0]);
1201 bootblock = strdup(argv[1]);
1202 device_path = strdup(argv[2]);
1204 if (!device_path || !bootblock || !stage1) {
1205 (void) fprintf(stderr, gettext("Missing parameter"));
1206 usage(progname);
1207 goto out;
1210 BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n",
1211 device_path, stage1, bootblock);
1212 bzero(&install_data, sizeof (ib_data_t));
1214 if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
1215 (void) fprintf(stderr, gettext("Unable to open device %s\n"),
1216 device_path);
1217 goto out;
1220 if (read_stage1_from_file(stage1, &install_data) != BC_SUCCESS) {
1221 (void) fprintf(stderr, gettext("Error opening %s\n"), stage1);
1222 goto out_dev;
1225 if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
1226 (void) fprintf(stderr, gettext("Error reading %s\n"),
1227 bootblock);
1228 goto out_dev;
1232 * is_update_necessary() will take care of checking if versioning and/or
1233 * forcing the update have been specified. It will also emit a warning
1234 * if a non-versioned update is attempted over a versioned bootblock.
1236 if (!is_update_necessary(&install_data, update_str)) {
1237 (void) fprintf(stderr, gettext("bootblock version installed "
1238 "on %s is more recent or identical\n"
1239 "Use -F to override or install without the -u option\n"),
1240 device_path);
1241 ret = BC_NOUPDT;
1242 goto out_dev;
1245 BOOT_DEBUG("Ready to commit to disk\n");
1246 ret = commit_to_disk(&install_data, update_str);
1248 out_dev:
1249 cleanup_device(&install_data.device);
1250 out:
1251 free(stage1);
1252 free(bootblock);
1253 free(device_path);
1254 return (ret);
1258 * Retrieves from a device the extended information (einfo) associated to the
1259 * installed stage2.
1260 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
1261 * Returns:
1262 * - BC_SUCCESS (and prints out einfo contents depending on 'flags')
1263 * - BC_ERROR (on error)
1264 * - BC_NOEINFO (no extended information available)
1266 static int
1267 handle_getinfo(char *progname, char **argv)
1270 ib_data_t data;
1271 ib_bootblock_t *bblock = &data.bootblock;
1272 ib_device_t *device = &data.device;
1273 bblk_einfo_t *einfo;
1274 uint8_t flags = 0;
1275 char *device_path, *path;
1276 int retval = BC_ERROR;
1277 int ret;
1279 device_path = strdup(argv[0]);
1280 if (!device_path) {
1281 (void) fprintf(stderr, gettext("Missing parameter"));
1282 usage(progname);
1283 goto out;
1286 bzero(&data, sizeof (ib_data_t));
1287 BOOT_DEBUG("device path: %s\n", device_path);
1289 if (init_device(device, device_path) != BC_SUCCESS) {
1290 (void) fprintf(stderr, gettext("Unable to gather device "
1291 "information from %s\n"), device_path);
1292 goto out_dev;
1295 ret = read_bootblock_from_disk(device, bblock, &path);
1296 if (ret == BC_ERROR) {
1297 (void) fprintf(stderr, gettext("Error reading bootblock from "
1298 "%s\n"), path);
1299 goto out_dev;
1302 if (ret == BC_NOEXTRA) {
1303 BOOT_DEBUG("No multiboot header found on %s, unable "
1304 "to locate extra information area (old/non versioned "
1305 "bootblock?) \n", device_path);
1306 (void) fprintf(stderr, gettext("No extended information "
1307 "found\n"));
1308 retval = BC_NOEINFO;
1309 goto out_dev;
1312 einfo = find_einfo(bblock->extra, bblock->extra_size);
1313 if (einfo == NULL) {
1314 retval = BC_NOEINFO;
1315 (void) fprintf(stderr, gettext("No extended information "
1316 "found\n"));
1317 goto out_dev;
1320 /* Print the extended information. */
1321 if (strip)
1322 flags |= EINFO_EASY_PARSE;
1323 if (verbose_dump)
1324 flags |= EINFO_PRINT_HEADER;
1326 print_einfo(flags, einfo, bblock->extra_size);
1327 retval = BC_SUCCESS;
1329 out_dev:
1330 cleanup_device(&data.device);
1331 out:
1332 free(device_path);
1333 return (retval);
1337 * Attempt to mirror (propagate) the current bootblock over the attaching disk.
1339 * Returns:
1340 * - BC_SUCCESS (a successful propagation happened)
1341 * - BC_ERROR (an error occurred)
1342 * - BC_NOEXTRA (it is not possible to dump the current bootblock since
1343 * there is no multiboot information)
1345 static int
1346 handle_mirror(char *progname, char **argv)
1348 ib_data_t curr_data;
1349 ib_data_t attach_data;
1350 ib_device_t *curr_device = &curr_data.device;
1351 ib_device_t *attach_device = &attach_data.device;
1352 ib_bootblock_t *bblock_curr = &curr_data.bootblock;
1353 ib_bootblock_t *bblock_attach = &attach_data.bootblock;
1354 bblk_einfo_t *einfo_curr = NULL;
1355 char *curr_device_path;
1356 char *attach_device_path;
1357 char *updt_str = NULL;
1358 char *path;
1359 int retval = BC_ERROR;
1360 int ret;
1362 curr_device_path = strdup(argv[0]);
1363 attach_device_path = strdup(argv[1]);
1365 if (!curr_device_path || !attach_device_path) {
1366 (void) fprintf(stderr, gettext("Missing parameter"));
1367 usage(progname);
1368 goto out;
1370 BOOT_DEBUG("Current device path is: %s, attaching device path is: "
1371 " %s\n", curr_device_path, attach_device_path);
1373 bzero(&curr_data, sizeof (ib_data_t));
1374 bzero(&attach_data, sizeof (ib_data_t));
1376 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
1377 (void) fprintf(stderr, gettext("Unable to gather device "
1378 "information from %s (current device)\n"),
1379 curr_device_path);
1380 goto out_currdev;
1383 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
1384 (void) fprintf(stderr, gettext("Unable to gather device "
1385 "information from %s (attaching device)\n"),
1386 attach_device_path);
1387 goto out_devs;
1390 ret = read_bootblock_from_disk(curr_device, bblock_curr, &path);
1391 if (ret == BC_ERROR) {
1392 BOOT_DEBUG("Error reading bootblock from %s\n", path);
1393 retval = BC_ERROR;
1394 goto out_devs;
1397 if (ret == BC_NOEXTRA) {
1398 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
1399 " the bootblock\n", path);
1400 retval = BC_NOEXTRA;
1401 goto out_devs;
1404 write_mbr = B_TRUE;
1405 force_mbr = B_TRUE;
1406 einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size);
1407 if (einfo_curr != NULL)
1408 updt_str = einfo_get_string(einfo_curr);
1410 retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
1411 cleanup_bootblock(bblock_curr);
1412 cleanup_bootblock(bblock_attach);
1413 out_devs:
1414 cleanup_device(attach_device);
1415 out_currdev:
1416 cleanup_device(curr_device);
1417 out:
1418 free(curr_device_path);
1419 free(attach_device_path);
1420 return (retval);
1423 #define USAGE_STRING "Usage:\t%s [-h|-m|-f|-n|-F|-u verstr] stage1 stage2 " \
1424 "raw-device\n" \
1425 "\t%s -M [-n] raw-device attach-raw-device\n" \
1426 "\t%s [-e|-V] -i raw-device\n"
1428 #define CANON_USAGE_STR gettext(USAGE_STRING)
1430 static void
1431 usage(char *progname)
1433 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
1437 main(int argc, char **argv)
1439 int opt;
1440 int params = 3;
1441 int ret;
1442 char *progname;
1443 char **handle_args;
1445 (void) setlocale(LC_ALL, "");
1446 (void) textdomain(TEXT_DOMAIN);
1447 if (init_yes() < 0) {
1448 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
1449 strerror(errno));
1450 exit(BC_ERROR);
1453 while ((opt = getopt(argc, argv, "deFfhiMmnu:V")) != EOF) {
1454 switch (opt) {
1455 case 'd':
1456 boot_debug = B_TRUE;
1457 break;
1458 case 'e':
1459 strip = B_TRUE;
1460 break;
1461 case 'F':
1462 force_update = B_TRUE;
1463 break;
1464 case 'f':
1465 force_mbr = B_TRUE;
1466 break;
1467 case 'h':
1468 usage(argv[0]);
1469 exit(BC_SUCCESS);
1470 break;
1471 case 'i':
1472 do_getinfo = B_TRUE;
1473 params = 1;
1474 break;
1475 case 'M':
1476 do_mirror_bblk = B_TRUE;
1477 params = 2;
1478 break;
1479 case 'm':
1480 write_mbr = B_TRUE;
1481 break;
1482 case 'n':
1483 nowrite = B_TRUE;
1484 break;
1485 case 'u':
1486 do_version = B_TRUE;
1488 update_str = malloc(strlen(optarg) + 1);
1489 if (update_str == NULL) {
1490 perror(gettext("Memory allocation failure"));
1491 exit(BC_ERROR);
1493 (void) strlcpy(update_str, optarg, strlen(optarg) + 1);
1494 break;
1495 case 'V':
1496 verbose_dump = B_TRUE;
1497 break;
1498 default:
1499 /* fall through to process non-optional args */
1500 break;
1504 /* check arguments */
1505 if (argc != optind + params) {
1506 usage(argv[0]);
1507 exit(BC_ERROR);
1509 progname = argv[0];
1510 check_options(progname);
1511 handle_args = argv + optind;
1513 if (nowrite)
1514 (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
1515 " be written to disk.\n"));
1517 if (do_getinfo) {
1518 ret = handle_getinfo(progname, handle_args);
1519 } else if (do_mirror_bblk) {
1520 ret = handle_mirror(progname, handle_args);
1521 } else {
1522 ret = handle_install(progname, handle_args);
1524 return (ret);
1527 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
1528 static void
1529 check_options(char *progname)
1531 if (do_getinfo && do_mirror_bblk) {
1532 (void) fprintf(stderr, gettext("Only one of -M and -i can be "
1533 "specified at the same time\n"));
1534 usage(progname);
1535 exit(BC_ERROR);
1538 if (do_mirror_bblk) {
1540 * -u and -F may actually reflect a user intent that is not
1541 * correct with this command (mirror can be interpreted
1542 * "similar" to install. Emit a message and continue.
1543 * -e and -V have no meaning, be quiet here and only report the
1544 * incongruence if a debug output is requested.
1546 if (do_version) {
1547 (void) fprintf(stderr, MEANINGLESS_OPT, "-u");
1548 do_version = B_FALSE;
1550 if (force_update) {
1551 (void) fprintf(stderr, MEANINGLESS_OPT, "-F");
1552 force_update = B_FALSE;
1554 if (strip || verbose_dump) {
1555 BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
1556 strip = B_FALSE;
1557 verbose_dump = B_FALSE;
1561 if (do_getinfo) {
1562 if (write_mbr || force_mbr || do_version || force_update) {
1563 BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F");
1564 write_mbr = force_mbr = do_version = B_FALSE;
1565 force_update = B_FALSE;