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