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