3027 installgrub can segfault when encountering bogus data on disk
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 bblock->extra_size = bblock->buf_size - bblock->mboot_off 256 - BBLK_DATA_RSVD_SIZE - sizeof (multiboot_header_t); 257 return (BC_SUCCESS); 258 } 259 260 static boolean_t 261 is_update_necessary(ib_data_t *data, char *updt_str) 262 { 263 bblk_einfo_t *einfo; 264 bblk_hs_t bblock_hs; 265 ib_bootblock_t bblock_disk; 266 ib_bootblock_t *bblock_file = &data->bootblock; 267 ib_device_t *device = &data->device; 268 int dev_fd = device->fd; 269 270 assert(data != NULL); 271 assert(device->fd != -1); 272 273 /* Nothing to do if we are not updating a ZFS bootblock. */ 274 if (!is_zfs(device->type)) 275 return (B_TRUE); 276 277 bzero(&bblock_disk, sizeof (ib_bootblock_t)); 278 279 if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) { 280 BOOT_DEBUG("Unable to read bootblock from %s\n", device->path); 281 return (B_TRUE); 282 } 283 284 einfo = find_einfo(bblock_disk.extra, bblock_disk.extra_size); 285 if (einfo == NULL) { 286 BOOT_DEBUG("No extended information available\n"); 287 return (B_TRUE); 288 } 289 290 if (!do_version || updt_str == NULL) { 291 (void) fprintf(stdout, "WARNING: target device %s has a " 292 "versioned bootblock that is going to be overwritten by a " 293 "non versioned one\n", device->path); 294 return (B_TRUE); 295 } 296 297 if (force_update) { 298 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path); 299 return (B_TRUE); 300 } 301 302 BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str); 303 304 bblock_hs.src_buf = (unsigned char *)bblock_file->file; 305 bblock_hs.src_size = bblock_file->file_size; 306 307 return (einfo_should_update(einfo, &bblock_hs, updt_str)); 308 } 309 310 static void 311 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str) 312 { 313 bblk_hs_t hs; 314 uint32_t avail_space; 315 316 assert(bblock != NULL); 317 318 if (updt_str == NULL) { 319 BOOT_DEBUG("WARNING: no update string passed to " 320 "add_bootblock_einfo()\n"); 321 return; 322 } 323 324 /* Fill bootblock hashing source information. */ 325 hs.src_buf = (unsigned char *)bblock->file; 326 hs.src_size = bblock->file_size; 327 /* How much space for the extended information structure? */ 328 avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8); 329 /* Place the extended information structure. */ 330 add_einfo(bblock->extra, updt_str, &hs, avail_space); 331 } 332 333 334 static int 335 prepare_bootblock(ib_data_t *data, char *updt_str) 336 { 337 ib_device_t *device = &data->device; 338 ib_bootblock_t *bblock = &data->bootblock; 339 multiboot_header_t *mboot; 340 341 assert(data != NULL); 342 343 /* Nothing to do if we are not on ZFS. */ 344 if (!is_zfs(device->type)) 345 return (BC_SUCCESS); 346 347 /* 348 * Write the fake multiboot structure followed by the extra information 349 * data. Both mboot and extra pointers have already been filled up to 350 * point to the right location in the buffer. We prepare the fake 351 * multiboot regardless if versioning was requested or not because 352 * we need it for mirroring support. 353 */ 354 assert(bblock->mboot != NULL); 355 assert(bblock->extra != NULL); 356 357 mboot = bblock->mboot; 358 359 mboot->magic = MB_HEADER_MAGIC; 360 mboot->flags = MB_HEADER_FLAGS_64; 361 mboot->checksum = -(mboot->flags + mboot->magic); 362 /* 363 * Flags include the AOUT_KLUDGE and we use the extra members to specify 364 * the size of the bootblock. 365 */ 366 mboot->header_addr = bblock->mboot_off; 367 mboot->load_addr = 0; 368 mboot->load_end_addr = bblock->file_size; 369 370 /* 371 * Now that we have the mboot header in place, we can add the extended 372 * versioning information. Since the multiboot header has been placed 373 * after the file image, the hashing will still reflect the one of the 374 * file on the disk. 375 */ 376 if (do_version) 377 add_bootblock_einfo(bblock, updt_str); 378 379 return (BC_SUCCESS); 380 } 381 382 static int 383 write_zfs_bootblock(ib_data_t *data) 384 { 385 ib_device_t *device = &data->device; 386 ib_bootblock_t *bblock = &data->bootblock; 387 char *bufptr; 388 uint32_t size; 389 390 assert(data != NULL); 391 assert(device->fd != -1); 392 393 /* 394 * In the ZFS case we actually perform two different steps: 395 * - write the first 15 blocks of the bootblock to the reserved disk 396 * blocks. 397 * - write the remaining blocks in the ZFS reserved area at offset 398 * 512K. 399 */ 400 bufptr = bblock->buf; 401 size = BBLK_DATA_RSVD_SIZE; 402 403 if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) { 404 BOOT_DEBUG("Error writing first 15 blocks of %s\n", 405 device->path); 406 perror("write"); 407 return (BC_ERROR); 408 } 409 410 bufptr += BBLK_DATA_RSVD_SIZE; 411 size = bblock->buf_size - BBLK_DATA_RSVD_SIZE; 412 413 if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF) 414 != BC_SUCCESS) { 415 BOOT_DEBUG("Error writing the second part of ZFS bootblock " 416 "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF); 417 return (BC_ERROR); 418 } 419 return (BC_SUCCESS); 420 } 421 422 static int 423 write_bootblock(ib_data_t *data) 424 { 425 ib_device_t *device = &data->device; 426 ib_bootblock_t *bblock = &data->bootblock; 427 int ret; 428 429 assert(data != NULL); 430 431 /* 432 * If we are on UFS or HSFS we simply write out to the reserved 433 * blocks (1 to 15) the boot block. 434 */ 435 if (!is_zfs(device->type)) { 436 if (write_out(device->fd, bblock->buf, bblock->buf_size, 437 SECTOR_SIZE) != BC_SUCCESS) { 438 BOOT_DEBUG("Error writing bootblock to %s\n", 439 device->path); 440 return (BC_ERROR); 441 } else { 442 return (BC_SUCCESS); 443 } 444 } else { 445 ret = write_zfs_bootblock(data); 446 return (ret); 447 } 448 } 449 450 static int 451 open_device(ib_device_t *device) 452 { 453 struct stat statbuf; 454 455 device->fd = open(device->path, O_RDWR); 456 if (device->fd == -1) { 457 BOOT_DEBUG("Unable to open %s\n", device->path); 458 perror("open"); 459 return (BC_ERROR); 460 } 461 462 if (fstat(device->fd, &statbuf) != 0) { 463 BOOT_DEBUG("Unable to stat %s\n", device->path); 464 perror("stat"); 465 (void) close(device->fd); 466 return (BC_ERROR); 467 } 468 469 if (S_ISCHR(statbuf.st_mode) == 0) { 470 (void) fprintf(stderr, gettext("%s: Not a character device\n"), 471 device->path); 472 return (BC_ERROR); 473 } 474 475 return (BC_SUCCESS); 476 } 477 478 static int 479 init_device(ib_device_t *device, char *path) 480 { 481 bzero(device, sizeof (*device)); 482 device->fd = -1; 483 484 device->path = strdup(path); 485 if (path == NULL) { 486 perror(gettext("Memory allocation failure")); 487 return (BC_ERROR); 488 } 489 490 device->type = tgt_fs_type; 491 if (open_device(device) != BC_SUCCESS) 492 return (BC_ERROR); 493 494 return (BC_SUCCESS); 495 } 496 497 static void 498 cleanup_device(ib_device_t *device) 499 { 500 free(device->path); 501 bzero(device, sizeof (*device)); 502 503 if (device->fd != -1) 504 (void) close(device->fd); 505 } 506 507 static void 508 cleanup_bootblock(ib_bootblock_t *bblock) 509 { 510 free(bblock->buf); 511 bzero(bblock, sizeof (ib_bootblock_t)); 512 } 513 514 /* 515 * Propagate the bootblock on the source disk to the destination disk and 516 * version it with 'updt_str' in the process. Since we cannot trust any data 517 * on the attaching disk, we do not perform any specific check on a potential 518 * target extended information structure and we just blindly update. 519 */ 520 static int 521 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str) 522 { 523 ib_bootblock_t *src_bblock = &src->bootblock; 524 ib_bootblock_t *dest_bblock = &dest->bootblock; 525 uint32_t buf_size; 526 527 assert(src != NULL); 528 assert(dest != NULL); 529 530 cleanup_bootblock(dest_bblock); 531 532 if (updt_str != NULL) { 533 do_version = B_TRUE; 534 } else { 535 do_version = B_FALSE; 536 } 537 538 buf_size = src_bblock->file_size + SECTOR_SIZE; 539 540 dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE); 541 dest_bblock->buf = malloc(dest_bblock->buf_size); 542 if (dest_bblock->buf == NULL) { 543 perror(gettext("Memory Allocation Failure")); 544 return (BC_ERROR); 545 } 546 dest_bblock->file = dest_bblock->buf; 547 dest_bblock->file_size = src_bblock->file_size; 548 (void) memcpy(dest_bblock->file, src_bblock->file, 549 dest_bblock->file_size); 550 551 dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file + 552 P2ROUNDUP(dest_bblock->file_size, 8)); 553 dest_bblock->extra = (char *)dest_bblock->mboot + 554 sizeof (multiboot_header_t); 555 556 (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"), 557 src->device.path, dest->device.path); 558 559 return (commit_to_disk(dest, updt_str)); 560 } 561 562 static int 563 commit_to_disk(ib_data_t *data, char *update_str) 564 { 565 assert(data != NULL); 566 567 if (prepare_bootblock(data, update_str) != BC_SUCCESS) { 568 (void) fprintf(stderr, gettext("Error updating the bootblock " 569 "image\n")); 570 return (BC_ERROR); 571 } 572 573 if (write_bootblock(data) != BC_SUCCESS) { 574 (void) fprintf(stderr, gettext("Error writing bootblock to " 575 "disk\n")); 576 return (BC_ERROR); 577 } 578 579 return (BC_SUCCESS); 580 } 581 582 583 /* 584 * Install a new bootblock on the given device. handle_install() expects argv 585 * to contain 2 parameters (the target device path and the path to the 586 * bootblock. 587 * 588 * Returns: BC_SUCCESS - if the installation is successful 589 * BC_ERROR - if the installation failed 590 * BC_NOUPDT - if no installation was performed because the 591 * version currently installed is more recent than the 592 * supplied one. 593 * 594 */ 595 static int 596 handle_install(char *progname, char **argv) 597 { 598 ib_data_t install_data; 599 char *bootblock = NULL; 600 char *device_path = NULL; 601 int ret = BC_ERROR; 602 603 bootblock = strdup(argv[0]); 604 device_path = strdup(argv[1]); 605 606 if (!device_path || !bootblock) { 607 (void) fprintf(stderr, gettext("Missing parameter")); 608 usage(progname); 609 goto out; 610 } 611 612 BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path, 613 bootblock); 614 bzero(&install_data, sizeof (ib_data_t)); 615 616 if (init_device(&install_data.device, device_path) != BC_SUCCESS) { 617 (void) fprintf(stderr, gettext("Unable to open device %s\n"), 618 device_path); 619 goto out; 620 } 621 622 if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) { 623 (void) fprintf(stderr, gettext("Error reading %s\n"), 624 bootblock); 625 goto out_dev; 626 } 627 /* Versioning is only supported for the ZFS bootblock. */ 628 if (do_version && !is_zfs(install_data.device.type)) { 629 (void) fprintf(stderr, gettext("Versioning is only supported on" 630 " ZFS... skipping.\n")); 631 do_version = B_FALSE; 632 } 633 634 /* 635 * is_update_necessary() will take care of checking if versioning and/or 636 * forcing the update have been specified. It will also emit a warning 637 * if a non-versioned update is attempted over a versioned bootblock. 638 */ 639 if (!is_update_necessary(&install_data, update_str)) { 640 (void) fprintf(stderr, gettext("bootblock version installed " 641 "on %s is more recent or identical\n" 642 "Use -F to override or install without the -u option\n"), 643 device_path); 644 ret = BC_NOUPDT; 645 goto out_dev; 646 } 647 648 BOOT_DEBUG("Ready to commit to disk\n"); 649 ret = commit_to_disk(&install_data, update_str); 650 651 out_dev: 652 cleanup_device(&install_data.device); 653 out: 654 free(bootblock); 655 free(device_path); 656 return (ret); 657 } 658 659 /* 660 * Retrieves from a device the extended information (einfo) associated to the 661 * installed bootblock. 662 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0. 663 * Returns: 664 * - BC_SUCCESS (and prints out einfo contents depending on 'flags') 665 * - BC_ERROR (on error) 666 * - BC_NOEINFO (no extended information available) 667 */ 668 static int 669 handle_getinfo(char *progname, char **argv) 670 { 671 672 ib_data_t data; 673 ib_bootblock_t *bblock = &data.bootblock; 674 ib_device_t *device = &data.device; 675 bblk_einfo_t *einfo; 676 uint8_t flags = 0; 677 uint32_t size; 678 char *device_path; 679 int retval = BC_ERROR; 680 int ret; 681 682 device_path = strdup(argv[0]); 683 if (!device_path) { 684 (void) fprintf(stderr, gettext("Missing parameter")); 685 usage(progname); 686 goto out; 687 } 688 689 bzero(&data, sizeof (ib_data_t)); 690 BOOT_DEBUG("device path: %s\n", device_path); 691 692 if (init_device(device, device_path) != BC_SUCCESS) { 693 (void) fprintf(stderr, gettext("Unable to gather device " 694 "information from %s\n"), device_path); 695 goto out_dev; 696 } 697 698 if (!is_zfs(device->type)) { 699 (void) fprintf(stderr, gettext("Versioning only supported on " 700 "ZFS\n")); 701 goto out_dev; 702 } 703 704 ret = read_bootblock_from_disk(device->fd, bblock); 705 if (ret == BC_ERROR) { 706 (void) fprintf(stderr, gettext("Error reading bootblock from " 707 "%s\n"), device_path); 708 goto out_dev; 709 } 710 711 if (ret == BC_NOEXTRA) { 712 BOOT_DEBUG("No multiboot header found on %s, unable " 713 "to locate extra information area (old/non versioned " 714 "bootblock?) \n", device_path); 715 (void) fprintf(stderr, gettext("No extended information " 716 "found\n")); 717 retval = BC_NOEINFO; 718 goto out_dev; 719 } 720 721 einfo = find_einfo(bblock->extra, bblock->extra_size); 722 if (einfo == NULL) { 723 retval = BC_NOEINFO; 724 (void) fprintf(stderr, gettext("No extended information " 725 "found\n")); 726 goto out_dev; 727 } 728 729 /* Print the extended information. */ 730 if (strip) 731 flags |= EINFO_EASY_PARSE; 732 if (verbose_dump) 733 flags |= EINFO_PRINT_HEADER; 734 735 size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) - 736 sizeof (multiboot_header_t); 737 print_einfo(flags, einfo, size); 738 retval = BC_SUCCESS; 739 740 out_dev: 741 cleanup_device(&data.device); 742 out: 743 free(device_path); 744 return (retval); 745 746 } 747 748 /* 749 * Attempt to mirror (propagate) the current bootblock over the attaching disk. 750 * 751 * Returns: 752 * - BC_SUCCESS (a successful propagation happened) 753 * - BC_ERROR (an error occurred) 754 * - BC_NOEXTRA (it is not possible to dump the current bootblock since 755 * there is no multiboot information) 756 */ 757 static int 758 handle_mirror(char *progname, char **argv) 759 { 760 ib_data_t curr_data; 761 ib_data_t attach_data; 762 ib_device_t *curr_device = &curr_data.device; 763 ib_device_t *attach_device = &attach_data.device; 764 ib_bootblock_t *bblock_curr = &curr_data.bootblock; 765 ib_bootblock_t *bblock_attach = &attach_data.bootblock; 766 bblk_einfo_t *einfo_curr = NULL; 767 char *curr_device_path; 768 char *attach_device_path; 769 char *updt_str = NULL; 770 int retval = BC_ERROR; 771 int ret; 772 773 curr_device_path = strdup(argv[0]); 774 attach_device_path = strdup(argv[1]); 775 776 if (!curr_device_path || !attach_device_path) { 777 (void) fprintf(stderr, gettext("Missing parameter")); 778 usage(progname); 779 goto out; 780 } 781 BOOT_DEBUG("Current device path is: %s, attaching device path is: " 782 " %s\n", curr_device_path, attach_device_path); 783 784 bzero(&curr_data, sizeof (ib_data_t)); 785 bzero(&attach_data, sizeof (ib_data_t)); 786 787 if (tgt_fs_type != TARGET_IS_ZFS) { 788 (void) fprintf(stderr, gettext("Mirroring is only supported on " 789 "ZFS\n")); 790 return (BC_ERROR); 791 } 792 793 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) { 794 (void) fprintf(stderr, gettext("Unable to gather device " 795 "information from %s (current device)\n"), 796 curr_device_path); 797 goto out_currdev; 798 } 799 800 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) { 801 (void) fprintf(stderr, gettext("Unable to gather device " 802 "information from %s (attaching device)\n"), 803 attach_device_path); 804 goto out_devs; 805 } 806 807 ret = read_bootblock_from_disk(curr_device->fd, bblock_curr); 808 if (ret == BC_ERROR) { 809 BOOT_DEBUG("Error reading bootblock from %s\n", 810 curr_device->path); 811 retval = BC_ERROR; 812 goto out_devs; 813 } 814 815 if (ret == BC_NOEXTRA) { 816 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve" 817 " the bootblock\n", curr_device->path); 818 retval = BC_NOEXTRA; 819 goto out_devs; 820 } 821 822 einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size); 823 if (einfo_curr != NULL) 824 updt_str = einfo_get_string(einfo_curr); 825 826 retval = propagate_bootblock(&curr_data, &attach_data, updt_str); 827 cleanup_bootblock(bblock_curr); 828 cleanup_bootblock(bblock_attach); 829 out_devs: 830 cleanup_device(attach_device); 831 out_currdev: 832 cleanup_device(curr_device); 833 out: 834 free(curr_device_path); 835 free(attach_device_path); 836 return (retval); 837 } 838 839 #define USAGE_STRING "Usage: %s [-h|-f|-F fstype|-u verstr] bootblk " \ 840 "raw-device\n" \ 841 "\t%s [-e|-V] -i -F zfs raw-device\n" \ 842 "\t%s -M -F zfs raw-device attach-raw-device\n" \ 843 "\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n" 844 845 #define CANON_USAGE_STR gettext(USAGE_STRING) 846 847 static void 848 usage(char *progname) 849 { 850 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname); 851 } 852 853 int 854 main(int argc, char **argv) 855 { 856 int opt; 857 int params = 2; 858 int ret; 859 char *progname; 860 char **handle_args; 861 862 (void) setlocale(LC_ALL, ""); 863 (void) textdomain(TEXT_DOMAIN); 864 865 while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) { 866 switch (opt) { 867 case 'F': 868 if (strcmp(optarg, "ufs") == 0) { 869 tgt_fs_type = TARGET_IS_UFS; 870 } else if (strcmp(optarg, "hsfs") == 0) { 871 tgt_fs_type = TARGET_IS_HSFS; 872 } else if (strcmp(optarg, "zfs") == 0) { 873 tgt_fs_type = TARGET_IS_ZFS; 874 } else { 875 (void) fprintf(stderr, gettext("Wrong " 876 "filesystem specified\n\n")); 877 usage(argv[0]); 878 exit(BC_ERROR); 879 } 880 break; 881 case 'e': 882 strip = B_TRUE; 883 break; 884 case 'f': 885 force_update = B_TRUE; 886 break; 887 case 'V': 888 verbose_dump = B_TRUE; 889 break; 890 case 'i': 891 do_getinfo = B_TRUE; 892 params = 1; 893 break; 894 case 'u': 895 do_version = B_TRUE; 896 897 update_str = malloc(strlen(optarg) + 1); 898 if (update_str == NULL) { 899 perror(gettext("Memory allocation failure")); 900 exit(BC_ERROR); 901 } 902 (void) strlcpy(update_str, optarg, strlen(optarg) + 1); 903 break; 904 case 'M': 905 do_mirror_bblk = B_TRUE; 906 break; 907 case 'h': 908 usage(argv[0]); 909 exit(BC_SUCCESS); 910 break; 911 case 'd': 912 boot_debug = B_TRUE; 913 break; 914 case 'n': 915 nowrite = B_TRUE; 916 break; 917 default: 918 /* fall through to process non-optional args */ 919 break; 920 } 921 } 922 923 /* check arguments */ 924 if (argc != optind + params) { 925 usage(argv[0]); 926 exit(BC_ERROR); 927 } 928 progname = argv[0]; 929 handle_args = argv + optind; 930 931 /* check options. */ 932 if (do_getinfo && do_mirror_bblk) { 933 (void) fprintf(stderr, gettext("Only one of -M and -i can be " 934 "specified at the same time\n")); 935 usage(progname); 936 exit(BC_ERROR); 937 } 938 939 if (nowrite) 940 (void) fprintf(stdout, gettext("Dry run requested. Nothing will" 941 " be written to disk.\n")); 942 943 if (do_getinfo) { 944 ret = handle_getinfo(progname, handle_args); 945 } else if (do_mirror_bblk) { 946 ret = handle_mirror(progname, handle_args); 947 } else { 948 ret = handle_install(progname, handle_args); 949 } 950 return (ret); 951 } --- EOF ---