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