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
read_bootblock_from_file(char * file,ib_data_t * data)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
read_bootblock_from_disk(int dev_fd,ib_bootblock_t * bblock)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
is_update_necessary(ib_data_t * data,char * updt_str)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
add_bootblock_einfo(ib_bootblock_t * bblock,char * updt_str)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
prepare_bootblock(ib_data_t * data,char * updt_str)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
write_zfs_bootblock(ib_data_t * data)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
write_bootblock(ib_data_t * data)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
open_device(ib_device_t * device)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
init_device(ib_device_t * device,char * path)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
cleanup_device(ib_device_t * device)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
cleanup_bootblock(ib_bootblock_t * bblock)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
propagate_bootblock(ib_data_t * src,ib_data_t * dest,char * updt_str)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
commit_to_disk(ib_data_t * data,char * update_str)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
handle_install(char * progname,char ** argv)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
handle_getinfo(char * progname,char ** argv)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
handle_mirror(char * progname,char ** argv)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
usage(char * progname)849 usage(char *progname)
850 {
851 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname);
852 }
853
854 int
main(int argc,char ** argv)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