xref: /titanic_41/usr/src/cmd/boot/installboot/installboot.c (revision b3697b90e692e3e5d859fb77d285d4c056d99eda)
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  */
24 
25 #include <stdio.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <assert.h>
30 #include <locale.h>
31 #include <strings.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/multiboot.h>
35 #include <sys/sysmacros.h>
36 
37 #include "installboot.h"
38 #include "./../common/bblk_einfo.h"
39 #include "./../common/boot_utils.h"
40 #include "./../common/mboot_extra.h"
41 
42 #ifndef	TEXT_DOMAIN
43 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
44 #endif
45 
46 /*
47  * SPARC bootblock installation:
48  *
49  * The bootblock resides in blocks 1 to 15 (disk label is at block 0).
50  * The ZFS boot block is larger than what will fit into these first 7.5K so we
51  * break it up and write the remaining portion into the ZFS provided boot block
52  * region at offset 512K. If versioning is requested, we add a multiboot
53  * header at the end of the bootblock, followed by the extra payload area and
54  * place the extended information structure within the latter.
55  */
56 
57 static boolean_t	force_update = B_FALSE;
58 static boolean_t	do_getinfo = B_FALSE;
59 static boolean_t	do_version = B_FALSE;
60 static boolean_t	do_mirror_bblk = B_FALSE;
61 static boolean_t	strip = B_FALSE;
62 static boolean_t	verbose_dump = B_FALSE;
63 
64 static char		*update_str;
65 static int		tgt_fs_type = TARGET_IS_UFS;
66 char			mboot_scan[MBOOT_SCAN_SIZE];
67 
68 /* Function prototypes. */
69 static int read_bootblock_from_file(char *, ib_data_t *data);
70 static int read_bootblock_from_disk(int, ib_bootblock_t *);
71 static void add_bootblock_einfo(ib_bootblock_t *, char *);
72 static int prepare_bootblock(ib_data_t *, char *);
73 static int write_zfs_bootblock(ib_data_t *);
74 static int write_bootblock(ib_data_t *);
75 static int open_device(ib_device_t *);
76 static int init_device(ib_device_t *, char *);
77 static void cleanup_device(ib_device_t *);
78 static int commit_to_disk(ib_data_t *, char *);
79 static int handle_install(char *, char **);
80 static int handle_getinfo(char *, char **);
81 static int handle_mirror(char *, char **);
82 static boolean_t is_update_necessary(ib_data_t *, char *);
83 static int propagate_bootblock(ib_data_t *, ib_data_t *, char *);
84 static void usage(char *);
85 
86 static int
87 read_bootblock_from_file(char *file, ib_data_t *data)
88 {
89 	ib_device_t	*device = &data->device;
90 	ib_bootblock_t	*bblock = &data->bootblock;
91 	struct stat 	sb;
92 	uint32_t	buf_size;
93 	int		fd = -1;
94 	int		retval = BC_ERROR;
95 
96 	assert(data != NULL);
97 	assert(file != NULL);
98 
99 	fd = open(file, O_RDONLY);
100 	if (fd == -1) {
101 		BOOT_DEBUG("Error opening %s\n", file);
102 		perror("open");
103 		goto out;
104 	}
105 
106 	if (fstat(fd, &sb) == -1) {
107 		BOOT_DEBUG("Error getting information (stat) about %s", file);
108 		perror("stat");
109 		goto outfd;
110 	}
111 
112 	bblock->file_size = sb.st_size;
113 	BOOT_DEBUG("bootblock file size is %x\n", bblock->file_size);
114 
115 	/* UFS and HSFS bootblocks need to fit in the reserved 7.5K. */
116 	if (!is_zfs(device->type)) {
117 		buf_size = P2ROUNDUP(bblock->file_size, SECTOR_SIZE);
118 		if (buf_size > BBLK_DATA_RSVD_SIZE) {
119 			BOOT_DEBUG("boot block size is bigger than allowed\n");
120 			goto outfd;
121 		}
122 	} else {
123 		buf_size = P2ROUNDUP(bblock->file_size + SECTOR_SIZE,
124 		    SECTOR_SIZE);
125 		if (buf_size > BBLK_DATA_RSVD_SIZE + MBOOT_SCAN_SIZE) {
126 			(void) fprintf(stderr, gettext("WARNING, bootblock size"
127 			    " does not allow to place extended versioning "
128 			    "information.. skipping\n"));
129 			do_version = B_FALSE;
130 		}
131 	}
132 
133 	bblock->buf_size = buf_size;
134 	BOOT_DEBUG("bootblock in-memory buffer size is %x\n",
135 	    bblock->buf_size);
136 
137 	bblock->buf = malloc(buf_size);
138 	if (bblock->buf == NULL) {
139 		perror(gettext("Memory allocation failure"));
140 		goto outbuf;
141 	}
142 	bblock->file = bblock->buf;
143 
144 	if (read(fd, bblock->file, bblock->file_size) != bblock->file_size) {
145 		BOOT_DEBUG("Read from %s failed\n", file);
146 		perror("read");
147 		goto outfd;
148 	}
149 
150 	/* If not on ZFS, we are done here. */
151 	if (!is_zfs(device->type)) {
152 		BOOT_DEBUG("Reading of the bootblock done\n");
153 		retval = BC_SUCCESS;
154 		goto outfd;
155 	}
156 	/*
157 	 * We place the multiboot header right after the file, followed by
158 	 * the extended information structure.
159 	 */
160 	bblock->mboot = (multiboot_header_t *)(bblock->file +
161 	    P2ROUNDUP(bblock->file_size, 8));
162 	bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
163 	BOOT_DEBUG("mboot at %p, extra at %p, buf=%p (size=%d)\n",
164 	    bblock->mboot, bblock->extra, bblock->buf, bblock->buf_size);
165 
166 	(void) close(fd);
167 	return (BC_SUCCESS);
168 
169 outbuf:
170 	(void) free(bblock->buf);
171 	bblock->buf = NULL;
172 outfd:
173 	(void) close(fd);
174 out:
175 	return (retval);
176 }
177 
178 static int
179 read_bootblock_from_disk(int dev_fd, ib_bootblock_t *bblock)
180 {
181 	char			*dest;
182 	uint32_t		size;
183 	uint32_t		buf_size;
184 	uint32_t		mboot_off;
185 	multiboot_header_t	*mboot;
186 
187 	assert(bblock != NULL);
188 	assert(dev_fd != -1);
189 
190 	/*
191 	 * The ZFS bootblock is divided in two parts, but the fake multiboot
192 	 * header can only be in the second part (the one contained in the ZFS
193 	 * reserved area).
194 	 */
195 	if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan),
196 	    BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
197 		BOOT_DEBUG("Error reading ZFS reserved area\n");
198 		perror("read");
199 		return (BC_ERROR);
200 	}
201 
202 	/* No multiboot means no chance of knowing bootblock size */
203 	if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off)
204 	    != BC_SUCCESS) {
205 		BOOT_DEBUG("Unable to find multiboot header\n");
206 		return (BC_NOEXTRA);
207 	}
208 	mboot = (multiboot_header_t *)(mboot_scan + mboot_off);
209 
210 	/*
211 	 * Currently, the amount of space reserved for extra information
212 	 * is "fixed". We may have to scan for the terminating extra payload
213 	 * in the future.
214 	 */
215 	size = mboot->load_end_addr - mboot->load_addr;
216 	buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE);
217 	bblock->file_size = size;
218 
219 	bblock->buf = malloc(buf_size);
220 	if (bblock->buf == NULL) {
221 		BOOT_DEBUG("Unable to allocate enough memory to read"
222 		    " the extra bootblock from the disk\n");
223 		perror(gettext("Memory allocation failure"));
224 		return (BC_ERROR);
225 	}
226 	bblock->buf_size = buf_size;
227 
228 	dest = bblock->buf;
229 	size = BBLK_DATA_RSVD_SIZE;
230 
231 	if (read_in(dev_fd, dest, size, SECTOR_SIZE) != BC_SUCCESS) {
232 		BOOT_DEBUG("Error reading first %d bytes of the bootblock\n",
233 		    size);
234 		(void) free(bblock->buf);
235 		bblock->buf = NULL;
236 		return (BC_ERROR);
237 	}
238 
239 	dest += BBLK_DATA_RSVD_SIZE;
240 	size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
241 
242 	if (read_in(dev_fd, dest, size, BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) {
243 		BOOT_DEBUG("Error reading ZFS reserved area the second time\n");
244 		(void) free(bblock->buf);
245 		bblock->buf = NULL;
246 		return (BC_ERROR);
247 	}
248 
249 	/* Update pointers. */
250 	bblock->file = bblock->buf;
251 	bblock->mboot_off = mboot_off;
252 	bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off
253 	    + BBLK_DATA_RSVD_SIZE);
254 	bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t);
255 	return (BC_SUCCESS);
256 }
257 
258 static boolean_t
259 is_update_necessary(ib_data_t *data, char *updt_str)
260 {
261 	bblk_einfo_t	*einfo;
262 	bblk_hs_t	bblock_hs;
263 	ib_bootblock_t	bblock_disk;
264 	ib_bootblock_t	*bblock_file = &data->bootblock;
265 	ib_device_t	*device = &data->device;
266 	int		dev_fd = device->fd;
267 
268 	assert(data != NULL);
269 	assert(device->fd != -1);
270 
271 	/* Nothing to do if we are not updating a ZFS bootblock. */
272 	if (!is_zfs(device->type))
273 		return (B_TRUE);
274 
275 	bzero(&bblock_disk, sizeof (ib_bootblock_t));
276 
277 	if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) {
278 		BOOT_DEBUG("Unable to read bootblock from %s\n", device->path);
279 		return (B_TRUE);
280 	}
281 
282 	einfo = find_einfo(bblock_disk.extra);
283 	if (einfo == NULL) {
284 		BOOT_DEBUG("No extended information available\n");
285 		return (B_TRUE);
286 	}
287 
288 	if (!do_version || updt_str == NULL) {
289 		(void) fprintf(stdout, "WARNING: target device %s has a "
290 		    "versioned bootblock that is going to be overwritten by a "
291 		    "non versioned one\n", device->path);
292 		return (B_TRUE);
293 	}
294 
295 	if (force_update) {
296 		BOOT_DEBUG("Forcing update of %s bootblock\n", device->path);
297 		return (B_TRUE);
298 	}
299 
300 	BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str);
301 
302 	bblock_hs.src_buf = (unsigned char *)bblock_file->file;
303 	bblock_hs.src_size = bblock_file->file_size;
304 
305 	return (einfo_should_update(einfo, &bblock_hs, updt_str));
306 }
307 
308 static void
309 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str)
310 {
311 	bblk_hs_t	hs;
312 	uint32_t	avail_space;
313 
314 	assert(bblock != NULL);
315 
316 	if (updt_str == NULL) {
317 		BOOT_DEBUG("WARNING: no update string passed to "
318 		    "add_bootblock_einfo()\n");
319 		return;
320 	}
321 
322 	/* Fill bootblock hashing source information. */
323 	hs.src_buf = (unsigned char *)bblock->file;
324 	hs.src_size = bblock->file_size;
325 	/* How much space for the extended information structure? */
326 	avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8);
327 	/* Place the extended information structure. */
328 	add_einfo(bblock->extra, updt_str, &hs, avail_space);
329 }
330 
331 
332 static int
333 prepare_bootblock(ib_data_t *data, char *updt_str)
334 {
335 	ib_device_t		*device = &data->device;
336 	ib_bootblock_t		*bblock = &data->bootblock;
337 	multiboot_header_t	*mboot;
338 
339 	assert(data != NULL);
340 
341 	/* Nothing to do if we are not on ZFS. */
342 	if (!is_zfs(device->type))
343 		return (BC_SUCCESS);
344 
345 	/*
346 	 * Write the fake multiboot structure followed by the extra information
347 	 * data. Both mboot and extra pointers have already been filled up to
348 	 * point to the right location in the buffer. We prepare the fake
349 	 * multiboot regardless if versioning was requested or not because
350 	 * we need it for mirroring support.
351 	 */
352 	assert(bblock->mboot != NULL);
353 	assert(bblock->extra != NULL);
354 
355 	mboot = bblock->mboot;
356 
357 	mboot->magic = MB_HEADER_MAGIC;
358 	mboot->flags = MB_HEADER_FLAGS_64;
359 	mboot->checksum = -(mboot->flags + mboot->magic);
360 	/*
361 	 * Flags include the AOUT_KLUDGE and we use the extra members to specify
362 	 * the size of the bootblock.
363 	 */
364 	mboot->header_addr = bblock->mboot_off;
365 	mboot->load_addr = 0;
366 	mboot->load_end_addr = bblock->file_size;
367 
368 	/*
369 	 * Now that we have the mboot header in place, we can add the extended
370 	 * versioning information. Since the multiboot header has been placed
371 	 * after the file image, the hashing will still reflect the one of the
372 	 * file on the disk.
373 	 */
374 	if (do_version)
375 		add_bootblock_einfo(bblock, updt_str);
376 
377 	return (BC_SUCCESS);
378 }
379 
380 static int
381 write_zfs_bootblock(ib_data_t *data)
382 {
383 	ib_device_t	*device = &data->device;
384 	ib_bootblock_t	*bblock = &data->bootblock;
385 	char		*bufptr;
386 	uint32_t	size;
387 
388 	assert(data != NULL);
389 	assert(device->fd != -1);
390 
391 	/*
392 	 * In the ZFS case we actually perform two different steps:
393 	 * - write the first 15 blocks of the bootblock to the reserved disk
394 	 *   blocks.
395 	 * - write the remaining blocks in the ZFS reserved area at offset
396 	 *   512K.
397 	 */
398 	bufptr = bblock->buf;
399 	size = BBLK_DATA_RSVD_SIZE;
400 
401 	if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) {
402 		BOOT_DEBUG("Error writing first 15 blocks of %s\n",
403 		    device->path);
404 		perror("write");
405 		return (BC_ERROR);
406 	}
407 
408 	bufptr += BBLK_DATA_RSVD_SIZE;
409 	size = bblock->buf_size - BBLK_DATA_RSVD_SIZE;
410 
411 	if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF)
412 	    != BC_SUCCESS) {
413 		BOOT_DEBUG("Error writing the second part of ZFS bootblock "
414 		    "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF);
415 		return (BC_ERROR);
416 	}
417 	return (BC_SUCCESS);
418 }
419 
420 static int
421 write_bootblock(ib_data_t *data)
422 {
423 	ib_device_t	*device = &data->device;
424 	ib_bootblock_t	*bblock = &data->bootblock;
425 	int		ret;
426 
427 	assert(data != NULL);
428 
429 	/*
430 	 * If we are on UFS or HSFS we simply write out to the reserved
431 	 * blocks (1 to 15) the boot block.
432 	 */
433 	if (!is_zfs(device->type)) {
434 		if (write_out(device->fd, bblock->buf, bblock->buf_size,
435 		    SECTOR_SIZE) != BC_SUCCESS) {
436 			BOOT_DEBUG("Error writing bootblock to %s\n",
437 			    device->path);
438 			return (BC_ERROR);
439 		} else {
440 			return (BC_SUCCESS);
441 		}
442 	} else {
443 		ret = write_zfs_bootblock(data);
444 		return (ret);
445 	}
446 }
447 
448 static int
449 open_device(ib_device_t *device)
450 {
451 	struct stat	statbuf;
452 
453 	device->fd = open(device->path, O_RDWR);
454 	if (device->fd == -1) {
455 		BOOT_DEBUG("Unable to open %s\n", device->path);
456 		perror("open");
457 		return (BC_ERROR);
458 	}
459 
460 	if (fstat(device->fd, &statbuf) != 0) {
461 		BOOT_DEBUG("Unable to stat %s\n", device->path);
462 		perror("stat");
463 		(void) close(device->fd);
464 		return (BC_ERROR);
465 	}
466 
467 	if (S_ISCHR(statbuf.st_mode) == 0) {
468 		(void) fprintf(stderr, gettext("%s: Not a character device\n"),
469 		    device->path);
470 		return (BC_ERROR);
471 	}
472 
473 	return (BC_SUCCESS);
474 }
475 
476 static int
477 init_device(ib_device_t *device, char *path)
478 {
479 	bzero(device, sizeof (*device));
480 	device->fd = -1;
481 
482 	device->path = strdup(path);
483 	if (path == NULL) {
484 		perror(gettext("Memory allocation failure"));
485 		return (BC_ERROR);
486 	}
487 
488 	device->type = tgt_fs_type;
489 	if (open_device(device) != BC_SUCCESS)
490 		return (BC_ERROR);
491 
492 	return (BC_SUCCESS);
493 }
494 
495 static void
496 cleanup_device(ib_device_t *device)
497 {
498 	free(device->path);
499 	bzero(device, sizeof (*device));
500 
501 	if (device->fd != -1)
502 		(void) close(device->fd);
503 }
504 
505 static void
506 cleanup_bootblock(ib_bootblock_t *bblock)
507 {
508 	free(bblock->buf);
509 	bzero(bblock, sizeof (ib_bootblock_t));
510 }
511 
512 /*
513  * Propagate the bootblock on the source disk to the destination disk and
514  * version it with 'updt_str' in the process. Since we cannot trust any data
515  * on the attaching disk, we do not perform any specific check on a potential
516  * target extended information structure and we just blindly update.
517  */
518 static int
519 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str)
520 {
521 	ib_bootblock_t	*src_bblock = &src->bootblock;
522 	ib_bootblock_t	*dest_bblock = &dest->bootblock;
523 	uint32_t	buf_size;
524 
525 	assert(src != NULL);
526 	assert(dest != NULL);
527 
528 	cleanup_bootblock(dest_bblock);
529 
530 	if (updt_str != NULL) {
531 		do_version = B_TRUE;
532 	} else {
533 		do_version = B_FALSE;
534 	}
535 
536 	buf_size = src_bblock->file_size + SECTOR_SIZE;
537 
538 	dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE);
539 	dest_bblock->buf = malloc(dest_bblock->buf_size);
540 	if (dest_bblock->buf == NULL) {
541 		perror(gettext("Memory Allocation Failure"));
542 		return (BC_ERROR);
543 	}
544 	dest_bblock->file = dest_bblock->buf;
545 	dest_bblock->file_size = src_bblock->file_size;
546 	(void) memcpy(dest_bblock->file, src_bblock->file,
547 	    dest_bblock->file_size);
548 
549 	dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file +
550 	    P2ROUNDUP(dest_bblock->file_size, 8));
551 	dest_bblock->extra = (char *)dest_bblock->mboot +
552 	    sizeof (multiboot_header_t);
553 
554 	(void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"),
555 	    src->device.path, dest->device.path);
556 
557 	return (commit_to_disk(dest, updt_str));
558 }
559 
560 static int
561 commit_to_disk(ib_data_t *data, char *update_str)
562 {
563 	assert(data != NULL);
564 
565 	if (prepare_bootblock(data, update_str) != BC_SUCCESS) {
566 		(void) fprintf(stderr, gettext("Error updating the bootblock "
567 		    "image\n"));
568 		return (BC_ERROR);
569 	}
570 
571 	if (write_bootblock(data) != BC_SUCCESS) {
572 		(void) fprintf(stderr, gettext("Error writing bootblock to "
573 		    "disk\n"));
574 		return (BC_ERROR);
575 	}
576 
577 	return (BC_SUCCESS);
578 }
579 
580 
581 /*
582  * Install a new bootblock on the given device. handle_install() expects argv
583  * to contain 2 parameters (the target device path and the path to the
584  * bootblock.
585  *
586  * Returns:	BC_SUCCESS - if the installation is successful
587  *		BC_ERROR   - if the installation failed
588  *		BC_NOUPDT  - if no installation was performed because the
589  *		             version currently installed is more recent than the
590  *			     supplied one.
591  *
592  */
593 static int
594 handle_install(char *progname, char **argv)
595 {
596 	ib_data_t	install_data;
597 	char		*bootblock = NULL;
598 	char		*device_path = NULL;
599 	int		ret = BC_ERROR;
600 
601 	bootblock = strdup(argv[0]);
602 	device_path = strdup(argv[1]);
603 
604 	if (!device_path || !bootblock) {
605 		(void) fprintf(stderr, gettext("Missing parameter"));
606 		usage(progname);
607 		goto out;
608 	}
609 
610 	BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path,
611 	    bootblock);
612 	bzero(&install_data, sizeof (ib_data_t));
613 
614 	if (init_device(&install_data.device, device_path) != BC_SUCCESS) {
615 		(void) fprintf(stderr, gettext("Unable to open device %s\n"),
616 		    device_path);
617 		goto out;
618 	}
619 
620 	if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) {
621 		(void) fprintf(stderr, gettext("Error reading %s\n"),
622 		    bootblock);
623 		goto out_dev;
624 	}
625 	/* Versioning is only supported for the ZFS bootblock. */
626 	if (do_version && !is_zfs(install_data.device.type)) {
627 		(void) fprintf(stderr, gettext("Versioning is only supported on"
628 		    " ZFS... skipping.\n"));
629 		do_version = B_FALSE;
630 	}
631 
632 	/*
633 	 * is_update_necessary() will take care of checking if versioning and/or
634 	 * forcing the update have been specified. It will also emit a warning
635 	 * if a non-versioned update is attempted over a versioned bootblock.
636 	 */
637 	if (!is_update_necessary(&install_data, update_str)) {
638 		(void) fprintf(stderr, gettext("bootblock version installed "
639 		    "on %s is more recent or identical\n"
640 		    "Use -F to override or install without the -u option\n"),
641 		    device_path);
642 		ret = BC_NOUPDT;
643 		goto out_dev;
644 	}
645 
646 	BOOT_DEBUG("Ready to commit to disk\n");
647 	ret = commit_to_disk(&install_data, update_str);
648 
649 out_dev:
650 	cleanup_device(&install_data.device);
651 out:
652 	free(bootblock);
653 	free(device_path);
654 	return (ret);
655 }
656 
657 /*
658  * Retrieves from a device the extended information (einfo) associated to the
659  * installed bootblock.
660  * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0.
661  * Returns:
662  *        - BC_SUCCESS (and prints out einfo contents depending on 'flags')
663  *	  - BC_ERROR (on error)
664  *        - BC_NOEINFO (no extended information available)
665  */
666 static int
667 handle_getinfo(char *progname, char **argv)
668 {
669 
670 	ib_data_t	data;
671 	ib_bootblock_t	*bblock = &data.bootblock;
672 	ib_device_t	*device = &data.device;
673 	bblk_einfo_t	*einfo;
674 	uint8_t		flags = 0;
675 	uint32_t	size;
676 	char		*device_path;
677 	int		retval = BC_ERROR;
678 	int		ret;
679 
680 	device_path = strdup(argv[0]);
681 	if (!device_path) {
682 		(void) fprintf(stderr, gettext("Missing parameter"));
683 		usage(progname);
684 		goto out;
685 	}
686 
687 	bzero(&data, sizeof (ib_data_t));
688 	BOOT_DEBUG("device path: %s\n", device_path);
689 
690 	if (init_device(device, device_path) != BC_SUCCESS) {
691 		(void) fprintf(stderr, gettext("Unable to gather device "
692 		    "information from %s\n"), device_path);
693 		goto out_dev;
694 	}
695 
696 	if (!is_zfs(device->type)) {
697 		(void) fprintf(stderr, gettext("Versioning only supported on "
698 		    "ZFS\n"));
699 		goto out_dev;
700 	}
701 
702 	ret = read_bootblock_from_disk(device->fd, bblock);
703 	if (ret == BC_ERROR) {
704 		(void) fprintf(stderr, gettext("Error reading bootblock from "
705 		    "%s\n"), device_path);
706 		goto out_dev;
707 	}
708 
709 	if (ret == BC_NOEXTRA) {
710 		BOOT_DEBUG("No multiboot header found on %s, unable "
711 		    "to locate extra information area (old/non versioned "
712 		    "bootblock?) \n", device_path);
713 		(void) fprintf(stderr, gettext("No extended information "
714 		    "found\n"));
715 		retval = BC_NOEINFO;
716 		goto out_dev;
717 	}
718 
719 	einfo = find_einfo(bblock->extra);
720 	if (einfo == NULL) {
721 		retval = BC_NOEINFO;
722 		(void) fprintf(stderr, gettext("No extended information "
723 		    "found\n"));
724 		goto out_dev;
725 	}
726 
727 	/* Print the extended information. */
728 	if (strip)
729 		flags |= EINFO_EASY_PARSE;
730 	if (verbose_dump)
731 		flags |= EINFO_PRINT_HEADER;
732 
733 	size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) -
734 	    sizeof (multiboot_header_t);
735 	print_einfo(flags, einfo, size);
736 	retval = BC_SUCCESS;
737 
738 out_dev:
739 	cleanup_device(&data.device);
740 out:
741 	free(device_path);
742 	return (retval);
743 
744 }
745 
746 /*
747  * Attempt to mirror (propagate) the current bootblock over the attaching disk.
748  *
749  * Returns:
750  *	- BC_SUCCESS (a successful propagation happened)
751  *	- BC_ERROR (an error occurred)
752  *	- BC_NOEXTRA (it is not possible to dump the current bootblock since
753  *			there is no multiboot information)
754  */
755 static int
756 handle_mirror(char *progname, char **argv)
757 {
758 	ib_data_t	curr_data;
759 	ib_data_t	attach_data;
760 	ib_device_t	*curr_device = &curr_data.device;
761 	ib_device_t	*attach_device = &attach_data.device;
762 	ib_bootblock_t	*bblock_curr = &curr_data.bootblock;
763 	ib_bootblock_t	*bblock_attach = &attach_data.bootblock;
764 	bblk_einfo_t	*einfo_curr = NULL;
765 	char		*curr_device_path;
766 	char		*attach_device_path;
767 	char		*updt_str = NULL;
768 	int		retval = BC_ERROR;
769 	int		ret;
770 
771 	curr_device_path = strdup(argv[0]);
772 	attach_device_path = strdup(argv[1]);
773 
774 	if (!curr_device_path || !attach_device_path) {
775 		(void) fprintf(stderr, gettext("Missing parameter"));
776 		usage(progname);
777 		goto out;
778 	}
779 	BOOT_DEBUG("Current device path is: %s, attaching device path is: "
780 	    " %s\n", curr_device_path, attach_device_path);
781 
782 	bzero(&curr_data, sizeof (ib_data_t));
783 	bzero(&attach_data, sizeof (ib_data_t));
784 
785 	if (tgt_fs_type != TARGET_IS_ZFS) {
786 		(void) fprintf(stderr, gettext("Mirroring is only supported on "
787 		    "ZFS\n"));
788 		return (BC_ERROR);
789 	}
790 
791 	if (init_device(curr_device, curr_device_path) != BC_SUCCESS) {
792 		(void) fprintf(stderr, gettext("Unable to gather device "
793 		    "information from %s (current device)\n"),
794 		    curr_device_path);
795 		goto out_currdev;
796 	}
797 
798 	if (init_device(attach_device, attach_device_path) != BC_SUCCESS) {
799 		(void) fprintf(stderr, gettext("Unable to gather device "
800 		    "information from %s (attaching device)\n"),
801 		    attach_device_path);
802 		goto out_devs;
803 	}
804 
805 	ret = read_bootblock_from_disk(curr_device->fd, bblock_curr);
806 	if (ret == BC_ERROR) {
807 		BOOT_DEBUG("Error reading bootblock from %s\n",
808 		    curr_device->path);
809 		retval = BC_ERROR;
810 		goto out_devs;
811 	}
812 
813 	if (ret == BC_NOEXTRA) {
814 		BOOT_DEBUG("No multiboot header found on %s, unable to retrieve"
815 		    " the bootblock\n", curr_device->path);
816 		retval = BC_NOEXTRA;
817 		goto out_devs;
818 	}
819 
820 	einfo_curr = find_einfo(bblock_curr->extra);
821 	if (einfo_curr != NULL)
822 		updt_str = einfo_get_string(einfo_curr);
823 
824 	retval = propagate_bootblock(&curr_data, &attach_data, updt_str);
825 	cleanup_bootblock(bblock_curr);
826 	cleanup_bootblock(bblock_attach);
827 out_devs:
828 	cleanup_device(attach_device);
829 out_currdev:
830 	cleanup_device(curr_device);
831 out:
832 	free(curr_device_path);
833 	free(attach_device_path);
834 	return (retval);
835 }
836 
837 #define	USAGE_STRING	"Usage: %s [-h|-f|-F fstype|-u verstr] bootblk "       \
838 			"raw-device\n"					       \
839 			"\t%s [-e|-V] -i -F zfs raw-device\n"	               \
840 			"\t%s -M -F zfs raw-device attach-raw-device\n"        \
841 			"\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n"
842 
843 #define	CANON_USAGE_STR	gettext(USAGE_STRING)
844 
845 static void
846 usage(char *progname)
847 {
848 	(void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
849 }
850 
851 int
852 main(int argc, char **argv)
853 {
854 	int	opt;
855 	int	params = 2;
856 	int	ret;
857 	char	*progname;
858 	char	**handle_args;
859 
860 	(void) setlocale(LC_ALL, "");
861 	(void) textdomain(TEXT_DOMAIN);
862 
863 	while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) {
864 		switch (opt) {
865 		case 'F':
866 			if (strcmp(optarg, "ufs") == 0) {
867 				tgt_fs_type = TARGET_IS_UFS;
868 			} else if (strcmp(optarg, "hsfs") == 0) {
869 				tgt_fs_type = TARGET_IS_HSFS;
870 			} else if (strcmp(optarg, "zfs") == 0) {
871 				tgt_fs_type = TARGET_IS_ZFS;
872 			} else {
873 				(void) fprintf(stderr, gettext("Wrong "
874 				    "filesystem specified\n\n"));
875 				usage(argv[0]);
876 				exit(BC_ERROR);
877 			}
878 			break;
879 		case 'e':
880 			strip = B_TRUE;
881 			break;
882 		case 'f':
883 			force_update = B_TRUE;
884 			break;
885 		case 'V':
886 			verbose_dump = B_TRUE;
887 			break;
888 		case 'i':
889 			do_getinfo = B_TRUE;
890 			params = 1;
891 			break;
892 		case 'u':
893 			do_version = B_TRUE;
894 
895 			update_str = malloc(strlen(optarg) + 1);
896 			if (update_str == NULL) {
897 				perror(gettext("Memory allocation failure"));
898 				exit(BC_ERROR);
899 			}
900 			(void) strlcpy(update_str, optarg, strlen(optarg) + 1);
901 			break;
902 		case 'M':
903 			do_mirror_bblk = B_TRUE;
904 			break;
905 		case 'h':
906 			usage(argv[0]);
907 			exit(BC_SUCCESS);
908 			break;
909 		case 'd':
910 			boot_debug = B_TRUE;
911 			break;
912 		case 'n':
913 			nowrite = B_TRUE;
914 			break;
915 		default:
916 			/* fall through to process non-optional args */
917 			break;
918 		}
919 	}
920 
921 	/* check arguments */
922 	if (argc != optind + params) {
923 		usage(argv[0]);
924 		exit(BC_ERROR);
925 	}
926 	progname = argv[0];
927 	handle_args = argv + optind;
928 
929 	/* check options. */
930 	if (do_getinfo && do_mirror_bblk) {
931 		(void) fprintf(stderr, gettext("Only one of -M and -i can be "
932 		    "specified at the same time\n"));
933 		usage(progname);
934 		exit(BC_ERROR);
935 	}
936 
937 	if (nowrite)
938 		(void) fprintf(stdout, gettext("Dry run requested. Nothing will"
939 		    " be written to disk.\n"));
940 
941 	if (do_getinfo) {
942 		ret = handle_getinfo(progname, handle_args);
943 	} else if (do_mirror_bblk) {
944 		ret = handle_mirror(progname, handle_args);
945 	} else {
946 		ret = handle_install(progname, handle_args);
947 	}
948 	return (ret);
949 }
950