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