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