8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / boot / installboot / i386 / installboot.c
blob2813e8b1502177e6e2c93e6305eca6c9a75bc135
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 2017 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 *);
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 *);
138 static int
139 read_stage1_from_file(char *path, ib_data_t *dest)
141 int fd;
143 assert(dest != NULL);
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);
153 (void) close(fd);
154 return (BC_SUCCESS);
157 static int
158 read_bootblock_from_file(char *file, ib_bootblock_t *bblock)
160 struct stat sb;
161 uint32_t buf_size;
162 uint32_t mboot_off;
163 int fd = -1;
164 int retval = BC_ERROR;
166 assert(bblock != NULL);
167 assert(file != NULL);
169 fd = open(file, O_RDONLY);
170 if (fd == -1) {
171 BOOT_DEBUG("Error opening %s\n", file);
172 perror("open");
173 goto out;
176 if (fstat(fd, &sb) == -1) {
177 BOOT_DEBUG("Error getting information (stat) about %s", file);
178 perror("stat");
179 goto outfd;
182 /* loader bootblock has version built in */
183 buf_size = sb.st_size;
185 bblock->buf_size = buf_size;
186 BOOT_DEBUG("bootblock in-memory buffer size is %d\n",
187 bblock->buf_size);
189 bblock->buf = malloc(buf_size);
190 if (bblock->buf == NULL) {
191 perror(gettext("Memory allocation failure"));
192 goto outbuf;
194 bblock->file = bblock->buf;
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;
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;
209 bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off);
210 bblock->mboot_off = mboot_off;
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);
216 bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8);
217 bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
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);
223 (void) close(fd);
224 return (BC_SUCCESS);
226 outbuf:
227 (void) free(bblock->buf);
228 bblock->buf = NULL;
229 outfd:
230 (void) close(fd);
231 out:
232 return (retval);
235 static int
236 read_bootblock_from_disk(ib_device_t *device, ib_bootblock_t *bblock,
237 char **path)
239 int dev_fd;
240 uint32_t size, offset;
241 uint32_t buf_size;
242 uint32_t mboot_off;
243 multiboot_header_t *mboot;
245 assert(device != NULL);
246 assert(bblock != NULL);
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;
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);
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);
271 mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
274 * make sure mboot has sane values
276 if (mboot->load_end_addr == 0 ||
277 mboot->load_end_addr < mboot->load_addr)
278 return (BC_NOEXTRA);
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.
285 size = mboot->load_end_addr - mboot->load_addr;
286 buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
287 bblock->file_size = size;
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);
296 bblock->buf_size = buf_size;
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);
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);
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);
316 return (BC_SUCCESS);
319 static boolean_t
320 is_update_necessary(ib_data_t *data, char *updt_str)
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;
331 assert(data != NULL);
333 bzero(&bblock_disk, sizeof (ib_bootblock_t));
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);
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);
347 einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size);
348 if (einfo_file == NULL) {
350 * loader bootblock is versioned. missing version means
351 * probably incompatible block. installboot can not install
352 * grub, for example.
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;
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);
372 if (force_update) {
373 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
374 return (B_TRUE);
377 BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
379 bblock_hs.src_buf = (unsigned char *)bblock_file->file;
380 bblock_hs.src_size = bblock_file->file_size;
382 return (einfo_should_update(einfo, &bblock_hs, updt_str));
385 static void
386 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
388 bblk_hs_t hs;
389 uint32_t avail_space;
391 assert(bblock != NULL);
393 if (updt_str == NULL) {
394 BOOT_DEBUG("WARNING: no update string passed to "
395 "add_bootblock_einfo()\n");
396 return;
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);
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
413 static int
414 prepare_stage1(ib_data_t *data)
416 ib_device_t *device;
418 assert(data != NULL);
419 device = &data->device;
421 /* copy BPB */
422 bcopy(device->mbr + STAGE1_BPB_OFFSET,
423 data->stage1 + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE);
426 /* copy MBR, note STAGE1_SIG == BOOTSZ */
427 bcopy(device->mbr + STAGE1_SIG, data->stage1 + STAGE1_SIG,
428 SECTOR_SIZE - STAGE1_SIG);
430 /* set stage2 size */
431 *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) =
432 (uint16_t)(data->bootblock.buf_size / SECTOR_SIZE);
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.
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;
449 * set disk uuid. we only need reasonable amount of uniqueness
450 * to allow biosdev to identify disk based on mbr differences.
452 uuid_generate(data->stage1 + STAGE1_STAGE2_UUID);
454 return (BC_SUCCESS);
457 static int
458 prepare_bootblock(ib_data_t *data, char *updt_str)
460 ib_bootblock_t *bblock;
461 ib_device_t *device;
462 uint64_t *ptr;
464 assert(data != NULL);
466 bblock = &data->bootblock;
467 device = &data->device;
469 ptr = (uint64_t *)(&bblock->mboot->bss_end_addr);
470 *ptr = device->target.start;
473 * the loader bootblock has built in version, if custom
474 * version was provided, update it.
476 if (do_version)
477 add_bootblock_einfo(bblock, updt_str);
479 return (BC_SUCCESS);
482 static int
483 write_bootblock(ib_data_t *data)
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;
492 assert(data != NULL);
495 * ZFS bootblock area is 3.5MB, make sure we can fit.
496 * buf_size is size of bootblk+EINFO.
498 if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) {
499 (void) fprintf(stderr, gettext("bootblock is too large\n"));
500 return (BC_ERROR);
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);
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);
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);
531 return (BC_SUCCESS);
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.
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.
556 static int
557 write_stage1(ib_data_t *data)
559 ib_device_t *device = &data->device;
560 uint64_t start = 0;
562 assert(data != NULL);
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.
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);
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;
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.
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);
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;
611 if (write_mbr) {
613 * If we did write partition boot block, update MBR to
614 * read partition boot block, not boot2.
616 if (start != 0) {
617 *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) = 1;
618 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) =
619 start;
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);
628 (void) fprintf(stdout,
629 gettext("stage1 written to master boot sector\n"));
632 return (BC_SUCCESS);
636 * find partition/slice start sector. will be recorded in stage2 and used
637 * by stage2 to identify partition with boot file system.
639 static int
640 get_start_sector(ib_device_t *device)
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;
650 if (device->devtype == IG_DEV_EFI) {
651 struct dk_gpt *vtoc;
653 if (efi_alloc_and_read(device->fd, &vtoc) < 0)
654 return (BC_ERROR);
656 if (device->stage.start == 0) {
657 /* zero size means the fstype must be zfs */
658 assert(device->target.fstype == IG_FS_ZFS);
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;
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;
673 /* with pcfs we always write MBR */
674 if (device->target.fstype == IG_FS_PCFS) {
675 force_mbr = 1;
676 write_mbr = 1;
679 efi_free(vtoc);
680 goto found_part;
683 mboot = (struct mboot *)device->mbr;
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;
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);
697 device->target.start = part->relsect;
698 device->target.size = part->numsect;
700 /* with pcfs we always write MBR */
701 if (device->target.fstype == IG_FS_PCFS) {
702 force_mbr = 1;
703 write_mbr = 1;
705 if (device->target.fstype == IG_FS_ZFS)
706 device->target.offset = BBLK_ZFS_BLK_OFF;
708 goto found_part;
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.
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;
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;
733 for (i = 0; i < FD_NUMPART; i++) {
734 part = (struct ipart *)mboot->parts + i;
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);
742 if (edkpi.p_start >= part->relsect &&
743 edkpi.p_start < (part->relsect + part->numsect)) {
744 /* Found the partition */
745 break;
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);
757 * We have found a Solaris fdisk partition (primary or extended)
758 * Handle the simple case first: Solaris in a primary partition
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;
772 * Solaris in a logical partition. Find that partition in the
773 * extended part.
776 if ((rval = libfdisk_init(&epp, device->path, NULL, FDISK_READ_DISK))
777 != FDISK_SUCCESS) {
778 switch (rval) {
780 * The first 3 cases are not an error per-se, just that
781 * there is no Solaris logical partition
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);
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);
818 device->stage.start = secnum;
819 device->stage.size = numsec;
820 device->stage.id = pno;
821 log_part = 1;
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);
838 * warn, if illumos in primary partition and loader not in MBR and
839 * partition is not active
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);
848 return (BC_SUCCESS);
851 static int
852 open_device(char *path)
854 struct stat statbuf = {0};
855 int fd = -1;
857 if (nowrite)
858 fd = open(path, O_RDONLY);
859 else
860 fd = open(path, O_RDWR);
862 if (fd == -1) {
863 BOOT_DEBUG("Unable to open %s\n", path);
864 perror("open");
865 return (-1);
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);
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);
882 return (fd);
885 static int
886 get_boot_partition(ib_device_t *device, struct mboot *mbr)
888 struct ipart *part;
889 char *path, *ptr;
890 int i;
892 part = (struct ipart *)mbr->parts;
893 for (i = 0; i < FD_NUMPART; i++) {
894 if (part[i].systid == X86BOOT)
895 break;
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);
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);
915 if ((path = strdup(device->path)) == NULL) {
916 perror(gettext("Memory allocation failure"));
917 return (BC_ERROR);
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);
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);
940 static int
941 get_boot_slice(ib_device_t *device, struct dk_gpt *vtoc)
943 uint_t i;
944 char *path, *ptr;
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);
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);
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);
972 return (BC_SUCCESS);
975 static int
976 init_device(ib_device_t *device, char *path)
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;
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 */
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"));
999 device->target.path = strdup(path);
1000 if (device->target.path == NULL) {
1001 perror(gettext("Memory allocation failure"));
1002 return (BC_ERROR);
1004 device->path = strdup(path);
1005 if (device->path == NULL) {
1006 perror(gettext("Memory allocation failure"));
1007 return (BC_ERROR);
1010 /* change device name to p0 */
1011 device->path[pathlen - 2] = 'p';
1012 device->path[pathlen - 1] = '0';
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);
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);
1027 if ((device->fd = open_device(device->path)) == -1)
1028 return (BC_ERROR);
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);
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') {
1051 * NOTE: we could relax there and allow zfs boot on
1052 * slice 2 for instance, but lets keep traditional limits.
1054 (void) fprintf(stderr,
1055 gettext("raw device must be a root slice (not s2)\n"));
1056 return (BC_ERROR);
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);
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);
1076 device->stage.devtype = device->devtype;
1077 device->stage.fd = open_device(device->stage.path);
1080 p = strrchr(device->target.path, 'p');
1081 if (p == NULL)
1082 p = strrchr(device->target.path, 's');
1083 device->target.id = atoi(++p);
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);
1090 if (fstyp_init(device->target.fd, 0, NULL, &fhdl) != 0)
1091 return (BC_ERROR);
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);
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);
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);
1120 fstyp_fini(fhdl);
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);
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);
1133 fstyp_fini(fhdl);
1135 return (get_start_sector(device));
1138 static void
1139 cleanup_device(ib_device_t *device)
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);
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));
1157 static void
1158 cleanup_bootblock(ib_bootblock_t *bblock)
1160 free(bblock->buf);
1161 bzero(bblock, sizeof (ib_bootblock_t));
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.
1170 static int
1171 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
1173 ib_bootblock_t *src_bblock = &src->bootblock;
1174 ib_bootblock_t *dest_bblock = &dest->bootblock;
1176 assert(src != NULL);
1177 assert(dest != NULL);
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);
1186 cleanup_bootblock(dest_bblock);
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);
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);
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;
1206 (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
1207 src->device.path, dest->device.path);
1209 return (commit_to_disk(dest, updt_str));
1212 static int
1213 commit_to_disk(ib_data_t *data, char *update_str)
1215 assert(data != NULL);
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);
1223 if (prepare_stage1(data) != BC_SUCCESS) {
1224 (void) fprintf(stderr, gettext("Error updating the stage1 "
1225 "image\n"));
1226 return (BC_ERROR);
1229 if (write_bootblock(data) != BC_SUCCESS) {
1230 (void) fprintf(stderr, gettext("Error writing bootblock to "
1231 "disk\n"));
1232 return (BC_ERROR);
1235 return (write_stage1(data));
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.
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.
1250 static int
1251 handle_install(char *progname, char **argv)
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;
1260 stage1 = strdup(argv[0]);
1261 bootblock = strdup(argv[1]);
1262 device_path = strdup(argv[2]);
1264 if (!device_path || !bootblock || !stage1) {
1265 (void) fprintf(stderr, gettext("Missing parameter"));
1266 usage(progname);
1267 goto out;
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));
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;
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;
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;
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.
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;
1305 BOOT_DEBUG("Ready to commit to disk\n");
1306 ret = commit_to_disk(&install_data, update_str);
1308 out_dev:
1309 cleanup_device(&install_data.device);
1310 out:
1311 free(stage1);
1312 free(bootblock);
1313 free(device_path);
1314 return (ret);
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)
1327 static int
1328 handle_getinfo(char *progname, char **argv)
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;
1339 device_path = strdup(argv[0]);
1340 if (!device_path) {
1341 (void) fprintf(stderr, gettext("Missing parameter"));
1342 usage(progname);
1343 goto out;
1346 if (stat(device_path, &sb) == -1) {
1347 perror("stat");
1348 goto out;
1351 bzero(&bblock, sizeof (bblock));
1352 bzero(&device, sizeof (device));
1353 BOOT_DEBUG("device path: %s\n", device_path);
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;
1364 ret = read_bootblock_from_disk(&device, &bblock, &path);
1367 if (ret == BC_ERROR) {
1368 (void) fprintf(stderr, gettext("Error reading bootblock from "
1369 "%s\n"), path);
1370 goto out_dev;
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;
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;
1391 /* Print the extended information. */
1392 if (strip)
1393 flags |= EINFO_EASY_PARSE;
1394 if (verbose_dump)
1395 flags |= EINFO_PRINT_HEADER;
1397 print_einfo(flags, einfo, bblock.extra_size);
1398 retval = BC_SUCCESS;
1400 out_dev:
1401 if (S_ISREG(sb.st_mode) == 0)
1402 cleanup_device(&device);
1403 out:
1404 free(device_path);
1405 return (retval);
1409 * Attempt to mirror (propagate) the current bootblock over the attaching disk.
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)
1417 static int
1418 handle_mirror(char *progname, char **argv)
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;
1434 curr_device_path = strdup(argv[0]);
1435 attach_device_path = strdup(argv[1]);
1437 if (!curr_device_path || !attach_device_path) {
1438 (void) fprintf(stderr, gettext("Missing parameter"));
1439 usage(progname);
1440 goto out;
1442 BOOT_DEBUG("Current device path is: %s, attaching device path is: "
1443 " %s\n", curr_device_path, attach_device_path);
1445 bzero(&curr_data, sizeof (ib_data_t));
1446 bzero(&attach_data, sizeof (ib_data_t));
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;
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;
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;
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;
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);
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);
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"
1500 #define CANON_USAGE_STR gettext(USAGE_STRING)
1502 static void
1503 usage(char *progname)
1505 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
1509 main(int argc, char **argv)
1511 int opt;
1512 int params = 3;
1513 int ret;
1514 char *progname;
1515 char **handle_args;
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);
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;
1560 update_str = malloc(strlen(optarg) + 1);
1561 if (update_str == NULL) {
1562 perror(gettext("Memory allocation failure"));
1563 exit(BC_ERROR);
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;
1576 /* check arguments */
1577 if (argc != optind + params) {
1578 usage(argv[0]);
1579 exit(BC_ERROR);
1581 progname = argv[0];
1582 check_options(progname);
1583 handle_args = argv + optind;
1585 if (nowrite)
1586 (void) fprintf(stdout, gettext("Dry run requested. Nothing will"
1587 " be written to disk.\n"));
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);
1596 return (ret);
1599 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n")
1600 static void
1601 check_options(char *progname)
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);
1610 if (do_mirror_bblk) {
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.
1618 if (do_version) {
1619 (void) fprintf(stderr, MEANINGLESS_OPT, "-u");
1620 do_version = B_FALSE;
1622 if (force_update) {
1623 (void) fprintf(stderr, MEANINGLESS_OPT, "-F");
1624 force_update = B_FALSE;
1626 if (strip || verbose_dump) {
1627 BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V");
1628 strip = B_FALSE;
1629 verbose_dump = B_FALSE;
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;