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