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 * Copyright 2016 Toomas Soome <tsoome@me.com> 25 */ 26 27 #include <stdio.h> 28 #include <errno.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include <assert.h> 32 #include <locale.h> 33 #include <strings.h> 34 #include <libfdisk.h> 35 36 #include <sys/dktp/fdisk.h> 37 #include <sys/dkio.h> 38 #include <sys/vtoc.h> 39 #include <sys/multiboot.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <sys/sysmacros.h> 43 #include <sys/efi_partition.h> 44 #include <libfstyp.h> 45 #include <uuid/uuid.h> 46 47 #include "installboot.h" 48 #include "../../common/bblk_einfo.h" 49 #include "../../common/boot_utils.h" 50 #include "../../common/mboot_extra.h" 51 #include "getresponse.h" 52 53 #ifndef TEXT_DOMAIN 54 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 55 #endif 56 57 /* 58 * BIOS bootblock installation: 59 * 60 * 1. MBR is first sector of the disk. If the file system on target is 61 * ufs or zfs, the same MBR code is installed on first sector of the 62 * partition as well; this will allow to have real MBR sector to be 63 * replaced by some other boot loader and have illumos chainloaded. 64 * 65 * installboot will record the start LBA and size of stage2 code in MBR code. 66 * On boot, the MBR code will read the stage2 code and executes it. 67 * 68 * 2. Stage2 location depends on file system type; 69 * In case of zfs, installboot will store stage2 to zfs bootblk area, 70 * which is 512k bytes from partition start and size is 3.5MB. 71 * 72 * In case of ufs, the stage2 location is 50 512B sectors from 73 * Solaris2 MBR partition start, within boot slice, boot slice size is 74 * one cylinder. 75 * 76 * In case of pcfs, the stage2 location is 50 512B sectors from beginning 77 * of the disk, filling the space between MBR and first partition. 78 * This location assumes no other bootloader and the space is one cylinder, 79 * as first partition is starting from cylinder 1. 80 * 81 * In case of GPT partitioning and if file system is not zfs, the boot 82 * support is only possible with dedicated boot partition. For GPT, 83 * the current implementation is using BOOT partition, which must exist. 84 * BOOT partition does only contain raw boot blocks, without any file system. 85 * 86 * Loader stage2 is created with embedded version, by using fake multiboot (MB) 87 * header within first 32k and EINFO block is at the end of the actual 88 * boot block. MB header load_addr is set to 0 and load_end_addr is set to 89 * actual block end, so the EINFO size is (file size - load_end_addr). 90 * installboot does also store the illumos boot partition LBA to MB space, 91 * starting from bss_end_addr structure member location; stage2 will 92 * detect the partition and file system based on this value. 93 * 94 * Stored location values in MBR/stage2 also mean the bootblocks must be 95 * reinstalled in case the partition content is relocated. 96 */ 97 98 static boolean_t write_mbr = B_FALSE; 99 static boolean_t force_mbr = B_FALSE; 100 static boolean_t force_update = B_FALSE; 101 static boolean_t do_getinfo = B_FALSE; 102 static boolean_t do_version = B_FALSE; 103 static boolean_t do_mirror_bblk = B_FALSE; 104 static boolean_t strip = B_FALSE; 105 static boolean_t verbose_dump = B_FALSE; 106 107 /* Versioning string, if present. */ 108 static char *update_str; 109 110 /* 111 * Temporary buffer to store the first 32K of data looking for a multiboot 112 * signature. 113 */ 114 char mboot_scan[MBOOT_SCAN_SIZE]; 115 116 /* Function prototypes. */ 117 static void check_options(char *); 118 static int get_start_sector(ib_device_t *); 119 120 static int read_stage1_from_file(char *, ib_data_t *data); 121 static int read_bootblock_from_file(char *, ib_data_t *data); 122 static int read_bootblock_from_disk(ib_device_t *device, ib_bootblock_t *, 123 char **); 124 static void add_bootblock_einfo(ib_bootblock_t *, char *); 125 static int prepare_stage1(ib_data_t *); 126 static int prepare_bootblock(ib_data_t *, char *); 127 static int write_stage1(ib_data_t *); 128 static int write_bootblock(ib_data_t *); 129 static int init_device(ib_device_t *, char *); 130 static void cleanup_device(ib_device_t *); 131 static int commit_to_disk(ib_data_t *, char *); 132 static int handle_install(char *, char **); 133 static int handle_getinfo(char *, char **); 134 static int handle_mirror(char *, char **); 135 static boolean_t is_update_necessary(ib_data_t *, char *); 136 static int propagate_bootblock(ib_data_t *, ib_data_t *, char *); 137 static void usage(char *); 138 139 static int 140 read_stage1_from_file(char *path, ib_data_t *dest) 141 { 142 int fd; 143 144 assert(dest != NULL); 145 146 /* read the stage1 file from filesystem */ 147 fd = open(path, O_RDONLY); 148 if (fd == -1 || 149 read(fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) { 150 (void) fprintf(stderr, gettext("cannot read stage1 file %s\n"), 151 path); 152 return (BC_ERROR); 153 } 154 (void) close(fd); 155 return (BC_SUCCESS); 156 } 157 158 static int 159 read_bootblock_from_file(char *file, ib_data_t *data) 160 { 161 ib_bootblock_t *bblock = &data->bootblock; 162 struct stat sb; 163 uint32_t buf_size; 164 uint32_t mboot_off; 165 int fd = -1; 166 int retval = BC_ERROR; 167 168 assert(data != NULL); 169 assert(file != NULL); 170 171 fd = open(file, O_RDONLY); 172 if (fd == -1) { 173 BOOT_DEBUG("Error opening %s\n", file); 174 perror("open"); 175 goto out; 176 } 177 178 if (fstat(fd, &sb) == -1) { 179 BOOT_DEBUG("Error getting information (stat) about %s", file); 180 perror("stat"); 181 goto outfd; 182 } 183 184 /* loader bootblock has version built in */ 185 buf_size = sb.st_size; 186 187 bblock->buf_size = buf_size; 188 BOOT_DEBUG("bootblock in-memory buffer size is %d\n", 189 bblock->buf_size); 190 191 bblock->buf = malloc(buf_size); 192 if (bblock->buf == NULL) { 193 perror(gettext("Memory allocation failure")); 194 goto outbuf; 195 } 196 bblock->file = bblock->buf; 197 198 if (read(fd, bblock->file, bblock->buf_size) != bblock->buf_size) { 199 BOOT_DEBUG("Read from %s failed\n", file); 200 perror("read"); 201 goto outfd; 202 } 203 204 if (find_multiboot(bblock->file, MBOOT_SCAN_SIZE, &mboot_off) 205 != BC_SUCCESS) { 206 (void) fprintf(stderr, 207 gettext("Unable to find multiboot header\n")); 208 goto outfd; 209 } 210 211 bblock->mboot = (multiboot_header_t *)(bblock->file + mboot_off); 212 bblock->mboot_off = mboot_off; 213 214 bblock->file_size = 215 bblock->mboot->load_end_addr - bblock->mboot->load_addr; 216 BOOT_DEBUG("bootblock file size is %d\n", bblock->file_size); 217 218 bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8); 219 bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8); 220 221 BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p " 222 "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra, 223 bblock->extra_size, bblock->buf, bblock->buf_size); 224 225 (void) close(fd); 226 return (BC_SUCCESS); 227 228 outbuf: 229 (void) free(bblock->buf); 230 bblock->buf = NULL; 231 outfd: 232 (void) close(fd); 233 out: 234 return (retval); 235 } 236 237 static int 238 read_bootblock_from_disk(ib_device_t *device, ib_bootblock_t *bblock, 239 char **path) 240 { 241 int dev_fd; 242 uint32_t size, offset; 243 uint32_t buf_size; 244 uint32_t mboot_off; 245 multiboot_header_t *mboot; 246 247 assert(device != NULL); 248 assert(bblock != NULL); 249 250 if (device->target.fstype == IG_FS_ZFS) { 251 dev_fd = device->target.fd; 252 offset = BBLK_ZFS_BLK_OFF * SECTOR_SIZE; 253 *path = device->target.path; 254 } else { 255 dev_fd = device->stage.fd; 256 offset = device->stage.offset * SECTOR_SIZE; 257 *path = device->stage.path; 258 } 259 260 if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan), offset) 261 != BC_SUCCESS) { 262 BOOT_DEBUG("Error reading bootblock area\n"); 263 perror("read"); 264 return (BC_ERROR); 265 } 266 267 /* No multiboot means no chance of knowing bootblock size */ 268 if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off) 269 != BC_SUCCESS) { 270 BOOT_DEBUG("Unable to find multiboot header\n"); 271 return (BC_NOEXTRA); 272 } 273 mboot = (multiboot_header_t *)(mboot_scan + mboot_off); 274 275 /* 276 * make sure mboot has sane values 277 */ 278 if (mboot->load_end_addr == 0 || 279 mboot->load_end_addr < mboot->load_addr) 280 return (BC_NOEXTRA); 281 282 /* 283 * Currently, the amount of space reserved for extra information 284 * is "fixed". We may have to scan for the terminating extra payload 285 * in the future. 286 */ 287 size = mboot->load_end_addr - mboot->load_addr; 288 buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE); 289 bblock->file_size = size; 290 291 bblock->buf = malloc(buf_size); 292 if (bblock->buf == NULL) { 293 BOOT_DEBUG("Unable to allocate enough memory to read" 294 " the extra bootblock from the disk\n"); 295 perror(gettext("Memory allocation failure")); 296 return (BC_ERROR); 297 } 298 bblock->buf_size = buf_size; 299 300 if (read_in(dev_fd, bblock->buf, buf_size, offset) != BC_SUCCESS) { 301 BOOT_DEBUG("Error reading the bootblock\n"); 302 (void) free(bblock->buf); 303 bblock->buf = NULL; 304 return (BC_ERROR); 305 } 306 307 /* Update pointers. */ 308 bblock->file = bblock->buf; 309 bblock->mboot_off = mboot_off; 310 bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off); 311 bblock->extra = bblock->buf + P2ROUNDUP(bblock->file_size, 8); 312 bblock->extra_size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8); 313 314 BOOT_DEBUG("mboot at %p offset %d, extra at %p size %d, buf=%p " 315 "(size=%d)\n", bblock->mboot, bblock->mboot_off, bblock->extra, 316 bblock->extra_size, bblock->buf, bblock->buf_size); 317 318 return (BC_SUCCESS); 319 } 320 321 static boolean_t 322 is_update_necessary(ib_data_t *data, char *updt_str) 323 { 324 bblk_einfo_t *einfo; 325 bblk_einfo_t *einfo_file; 326 bblk_hs_t bblock_hs; 327 ib_bootblock_t bblock_disk; 328 ib_bootblock_t *bblock_file = &data->bootblock; 329 ib_device_t *device = &data->device; 330 int ret; 331 char *path; 332 333 assert(data != NULL); 334 335 bzero(&bblock_disk, sizeof (ib_bootblock_t)); 336 337 ret = read_bootblock_from_disk(device, &bblock_disk, &path); 338 if (ret != BC_SUCCESS) { 339 BOOT_DEBUG("Unable to read bootblock from %s\n", path); 340 return (B_TRUE); 341 } 342 343 einfo = find_einfo(bblock_disk.extra, bblock_disk.extra_size); 344 if (einfo == NULL) { 345 BOOT_DEBUG("No extended information available on disk\n"); 346 return (B_TRUE); 347 } 348 349 einfo_file = find_einfo(bblock_file->extra, bblock_file->extra_size); 350 if (einfo_file == NULL) { 351 /* 352 * loader bootblock is versioned. missing version means 353 * probably incompatible block. installboot can not install 354 * grub, for example. 355 */ 356 (void) fprintf(stderr, 357 gettext("ERROR: non versioned bootblock in file\n")); 358 return (B_FALSE); 359 } else { 360 if (updt_str == NULL) { 361 updt_str = einfo_get_string(einfo_file); 362 do_version = B_TRUE; 363 } 364 } 365 366 if (!do_version || updt_str == NULL) { 367 (void) fprintf(stderr, 368 gettext("WARNING: target device %s has a " 369 "versioned bootblock that is going to be overwritten by a " 370 "non versioned one\n"), device->path); 371 return (B_TRUE); 372 } 373 374 if (force_update) { 375 BOOT_DEBUG("Forcing update of %s bootblock\n", device->path); 376 return (B_TRUE); 377 } 378 379 BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str); 380 381 bblock_hs.src_buf = (unsigned char *)bblock_file->file; 382 bblock_hs.src_size = bblock_file->file_size; 383 384 return (einfo_should_update(einfo, &bblock_hs, updt_str)); 385 } 386 387 static void 388 add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str) 389 { 390 bblk_hs_t hs; 391 uint32_t avail_space; 392 393 assert(bblock != NULL); 394 395 if (updt_str == NULL) { 396 BOOT_DEBUG("WARNING: no update string passed to " 397 "add_bootblock_einfo()\n"); 398 return; 399 } 400 401 /* Fill bootblock hashing source information. */ 402 hs.src_buf = (unsigned char *)bblock->file; 403 hs.src_size = bblock->file_size; 404 /* How much space for the extended information structure? */ 405 avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8); 406 /* Place the extended information structure. */ 407 add_einfo(bblock->extra, updt_str, &hs, avail_space); 408 } 409 410 /* 411 * set up data for case stage1 is installed as MBR 412 * set up location and size of bootblock 413 * set disk guid to provide unique information for biosdev command 414 */ 415 static int 416 prepare_stage1(ib_data_t *data) 417 { 418 ib_device_t *device; 419 420 assert(data != NULL); 421 device = &data->device; 422 423 /* copy BPB */ 424 bcopy(device->mbr + STAGE1_BPB_OFFSET, 425 data->stage1 + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); 426 427 428 /* copy MBR, note STAGE1_SIG == BOOTSZ */ 429 bcopy(device->mbr + STAGE1_SIG, data->stage1 + STAGE1_SIG, 430 SECTOR_SIZE - STAGE1_SIG); 431 432 /* set stage2 size */ 433 *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) = 434 (uint16_t)(data->bootblock.buf_size / SECTOR_SIZE); 435 436 /* 437 * set stage2 location. 438 * for zfs always use zfs embedding, for ufs/pcfs use partition_start 439 * as base for stage2 location, for ufs/pcfs in MBR partition, use 440 * free space after MBR record. 441 */ 442 if (device->target.fstype == IG_FS_ZFS) 443 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) = 444 device->target.start + device->target.offset; 445 else { 446 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) = 447 device->stage.start + device->stage.offset; 448 } 449 450 /* 451 * set disk uuid. we only need reasonable amount of uniqueness 452 * to allow biosdev to identify disk based on mbr differences. 453 */ 454 uuid_generate(data->stage1 + STAGE1_STAGE2_UUID); 455 456 return (BC_SUCCESS); 457 } 458 459 static int 460 prepare_bootblock(ib_data_t *data, char *updt_str) 461 { 462 ib_bootblock_t *bblock; 463 ib_device_t *device; 464 uint64_t *ptr; 465 466 assert(data != NULL); 467 468 bblock = &data->bootblock; 469 device = &data->device; 470 471 ptr = (uint64_t *)(&bblock->mboot->bss_end_addr); 472 *ptr = device->target.start; 473 474 /* 475 * the loader bootblock has built in version, if custom 476 * version was provided, update it. 477 */ 478 if (do_version) 479 add_bootblock_einfo(bblock, updt_str); 480 481 return (BC_SUCCESS); 482 } 483 484 static int 485 write_bootblock(ib_data_t *data) 486 { 487 ib_device_t *device = &data->device; 488 ib_bootblock_t *bblock = &data->bootblock; 489 uint64_t abs; 490 int dev_fd, ret; 491 off_t offset; 492 char *path; 493 494 assert(data != NULL); 495 496 /* 497 * ZFS bootblock area is 3.5MB, make sure we can fit. 498 * buf_size is size of bootblk+EINFO. 499 */ 500 if (bblock->buf_size > BBLK_ZFS_BLK_SIZE) { 501 (void) fprintf(stderr, gettext("bootblock is too large\n")); 502 return (BC_ERROR); 503 } 504 505 if (device->target.fstype == IG_FS_ZFS) { 506 dev_fd = device->target.fd; 507 abs = device->target.start + device->target.offset; 508 offset = BBLK_ZFS_BLK_OFF * SECTOR_SIZE; 509 path = device->target.path; 510 } else { 511 dev_fd = device->stage.fd; 512 abs = device->stage.start + device->stage.offset; 513 offset = device->stage.offset * SECTOR_SIZE; 514 path = device->stage.path; 515 if (bblock->buf_size > 516 (device->stage.size - device->stage.offset) * SECTOR_SIZE) { 517 (void) fprintf(stderr, gettext("Device %s is " 518 "too small to fit the stage2\n"), path); 519 return (BC_ERROR); 520 } 521 } 522 ret = write_out(dev_fd, bblock->buf, bblock->buf_size, offset); 523 if (ret != BC_SUCCESS) { 524 BOOT_DEBUG("Error writing the ZFS bootblock " 525 "to %s at offset %d\n", path, offset); 526 return (BC_ERROR); 527 } 528 529 (void) fprintf(stdout, gettext("bootblock written for %s," 530 " %d sectors starting at %d (abs %lld)\n"), path, 531 (bblock->buf_size / SECTOR_SIZE) + 1, offset / SECTOR_SIZE, abs); 532 533 return (BC_SUCCESS); 534 } 535 536 /* 537 * Partition boot block or volume boot record (VBR). The VBR is 538 * stored on partition relative sector 0 and allows chainloading 539 * to read boot program from partition. 540 * 541 * As the VBR will use the first sector of the partition, 542 * this means, we need to be sure the space is not used. 543 * We do support three partitioning chemes: 544 * 1. GPT: zfs and ufs have reserved space for first 8KB, but 545 * only zfs does have space for boot2. The pcfs has support 546 * for VBR, but no space for boot2. So with GPT, to support 547 * ufs or pcfs boot, we must have separate dedicated boot 548 * partition and we will store VBR on it. 549 * 2. MBR: we have almost the same situation as with GPT, except that 550 * if the partitions start from cylinder 1, we will have space 551 * between MBR and cylinder 0. If so, we do not require separate 552 * boot partition. 553 * 3. MBR+VTOC: with this combination we store VBR in sector 0 of the 554 * solaris2 MBR partition. The slice 0 will start from cylinder 1, 555 * and we do have space for boot2, so we do not require separate 556 * boot partition. 557 */ 558 static int 559 write_stage1(ib_data_t *data) 560 { 561 ib_device_t *device = &data->device; 562 uint64_t start = 0; 563 564 assert(data != NULL); 565 566 /* 567 * We have separate partition for boot programs and the stage1 568 * location is not absolute sector 0. 569 * We will write VBR and trigger MBR to read 1 sector from VBR. 570 * This case does also cover MBR+VTOC case, as the solaris 2 partition 571 * name and the root file system slice names are different. 572 */ 573 if (device->stage.start != 0 && 574 strcmp(device->target.path, device->stage.path)) { 575 /* we got separate stage area, use it */ 576 if (write_out(device->stage.fd, data->stage1, 577 sizeof (data->stage1), 0) != BC_SUCCESS) { 578 (void) fprintf(stdout, gettext("cannot write " 579 "partition boot sector\n")); 580 perror("write"); 581 return (BC_ERROR); 582 } 583 584 (void) fprintf(stdout, gettext("stage1 written to " 585 "%s %d sector 0 (abs %d)\n"), 586 device->devtype == IG_DEV_MBR? "partition":"slice", 587 device->stage.id, device->stage.start); 588 start = device->stage.start; 589 } 590 591 /* 592 * We have either GPT or MBR (without VTOC) and if the root 593 * file system is not pcfs, we can store VBR. Also trigger 594 * MBR to read 1 sector from VBR. 595 */ 596 if (device->devtype != IG_DEV_VTOC && 597 device->target.fstype != IG_FS_PCFS) { 598 if (write_out(device->target.fd, data->stage1, 599 sizeof (data->stage1), 0) != BC_SUCCESS) { 600 (void) fprintf(stdout, gettext("cannot write " 601 "partition boot sector\n")); 602 perror("write"); 603 return (BC_ERROR); 604 } 605 606 (void) fprintf(stdout, gettext("stage1 written to " 607 "%s %d sector 0 (abs %d)\n"), 608 device->devtype == IG_DEV_MBR? "partition":"slice", 609 device->target.id, device->target.start); 610 start = device->target.start; 611 } 612 613 if (write_mbr) { 614 /* 615 * If we did write partition boot block, update MBR to 616 * read partition boot block, not boot2. 617 */ 618 if (start != 0) { 619 *((uint16_t *)(data->stage1 + STAGE1_STAGE2_SIZE)) = 1; 620 *((uint64_t *)(data->stage1 + STAGE1_STAGE2_LBA)) = 621 start; 622 } 623 if (write_out(device->fd, data->stage1, 624 sizeof (data->stage1), 0) != BC_SUCCESS) { 625 (void) fprintf(stdout, 626 gettext("cannot write master boot sector\n")); 627 perror("write"); 628 return (BC_ERROR); 629 } 630 (void) fprintf(stdout, 631 gettext("stage1 written to master boot sector\n")); 632 } 633 634 return (BC_SUCCESS); 635 } 636 637 /* 638 * find partition/slice start sector. will be recorded in stage2 and used 639 * by stage2 to identify partition with boot file system. 640 */ 641 static int 642 get_start_sector(ib_device_t *device) 643 { 644 uint32_t secnum = 0, numsec = 0; 645 int i, pno, rval, log_part = 0; 646 struct mboot *mboot; 647 struct ipart *part = NULL; 648 ext_part_t *epp; 649 struct part_info dkpi; 650 struct extpart_info edkpi; 651 652 if (device->devtype == IG_DEV_EFI) { 653 struct dk_gpt *vtoc; 654 655 if (efi_alloc_and_read(device->fd, &vtoc) < 0) 656 return (BC_ERROR); 657 658 if (device->stage.start == 0) { 659 /* zero size means the fstype must be zfs */ 660 assert(device->target.fstype == IG_FS_ZFS); 661 662 device->stage.start = 663 vtoc->efi_parts[device->stage.id].p_start; 664 device->stage.size = 665 vtoc->efi_parts[device->stage.id].p_size; 666 device->stage.offset = BBLK_ZFS_BLK_OFF; 667 device->target.offset = BBLK_ZFS_BLK_OFF; 668 } 669 670 device->target.start = 671 vtoc->efi_parts[device->target.id].p_start; 672 device->target.size = 673 vtoc->efi_parts[device->target.id].p_size; 674 675 /* with pcfs we always write MBR */ 676 if (device->target.fstype == IG_FS_PCFS) { 677 force_mbr = 1; 678 write_mbr = 1; 679 } 680 681 efi_free(vtoc); 682 goto found_part; 683 } 684 685 mboot = (struct mboot *)device->mbr; 686 687 /* For MBR we have device->stage filled already. */ 688 if (device->devtype == IG_DEV_MBR) { 689 /* MBR partition starts from 0 */ 690 pno = device->target.id - 1; 691 part = (struct ipart *)mboot->parts + pno; 692 693 if (part->relsect == 0) { 694 (void) fprintf(stderr, gettext("Partition %d of the " 695 "disk has an incorrect offset\n"), 696 device->target.id); 697 return (BC_ERROR); 698 } 699 device->target.start = part->relsect; 700 device->target.size = part->numsect; 701 702 /* with pcfs we always write MBR */ 703 if (device->target.fstype == IG_FS_PCFS) { 704 force_mbr = 1; 705 write_mbr = 1; 706 } 707 if (device->target.fstype == IG_FS_ZFS) 708 device->target.offset = BBLK_ZFS_BLK_OFF; 709 710 goto found_part; 711 } 712 713 /* 714 * Search for Solaris fdisk partition 715 * Get the solaris partition information from the device 716 * and compare the offset of S2 with offset of solaris partition 717 * from fdisk partition table. 718 */ 719 if (ioctl(device->target.fd, DKIOCEXTPARTINFO, &edkpi) < 0) { 720 if (ioctl(device->target.fd, DKIOCPARTINFO, &dkpi) < 0) { 721 (void) fprintf(stderr, gettext("cannot get the " 722 "slice information of the disk\n")); 723 return (BC_ERROR); 724 } else { 725 edkpi.p_start = dkpi.p_start; 726 edkpi.p_length = dkpi.p_length; 727 } 728 } 729 730 device->target.start = edkpi.p_start; 731 device->target.size = edkpi.p_length; 732 if (device->target.fstype == IG_FS_ZFS) 733 device->target.offset = BBLK_ZFS_BLK_OFF; 734 735 for (i = 0; i < FD_NUMPART; i++) { 736 part = (struct ipart *)mboot->parts + i; 737 738 if (part->relsect == 0) { 739 (void) fprintf(stderr, gettext("Partition %d of the " 740 "disk has an incorrect offset\n"), i+1); 741 return (BC_ERROR); 742 } 743 744 if (edkpi.p_start >= part->relsect && 745 edkpi.p_start < (part->relsect + part->numsect)) { 746 /* Found the partition */ 747 break; 748 } 749 } 750 751 if (i == FD_NUMPART) { 752 /* No solaris fdisk partitions (primary or logical) */ 753 (void) fprintf(stderr, gettext("Solaris partition not found. " 754 "Aborting operation.\n")); 755 return (BC_ERROR); 756 } 757 758 /* 759 * We have found a Solaris fdisk partition (primary or extended) 760 * Handle the simple case first: Solaris in a primary partition 761 */ 762 if (!fdisk_is_dos_extended(part->systid)) { 763 device->stage.start = part->relsect; 764 device->stage.size = part->numsect; 765 if (device->target.fstype == IG_FS_ZFS) 766 device->stage.offset = BBLK_ZFS_BLK_OFF; 767 else 768 device->stage.offset = BBLK_BLKLIST_OFF; 769 device->stage.id = i + 1; 770 goto found_part; 771 } 772 773 /* 774 * Solaris in a logical partition. Find that partition in the 775 * extended part. 776 */ 777 778 if ((rval = libfdisk_init(&epp, device->path, NULL, FDISK_READ_DISK)) 779 != FDISK_SUCCESS) { 780 switch (rval) { 781 /* 782 * The first 3 cases are not an error per-se, just that 783 * there is no Solaris logical partition 784 */ 785 case FDISK_EBADLOGDRIVE: 786 case FDISK_ENOLOGDRIVE: 787 case FDISK_EBADMAGIC: 788 (void) fprintf(stderr, gettext("Solaris " 789 "partition not found. " 790 "Aborting operation.\n")); 791 return (BC_ERROR); 792 case FDISK_ENOVGEOM: 793 (void) fprintf(stderr, gettext("Could not get " 794 "virtual geometry\n")); 795 return (BC_ERROR); 796 case FDISK_ENOPGEOM: 797 (void) fprintf(stderr, gettext("Could not get " 798 "physical geometry\n")); 799 return (BC_ERROR); 800 case FDISK_ENOLGEOM: 801 (void) fprintf(stderr, gettext("Could not get " 802 "label geometry\n")); 803 return (BC_ERROR); 804 default: 805 (void) fprintf(stderr, gettext("Failed to " 806 "initialize libfdisk.\n")); 807 return (BC_ERROR); 808 } 809 } 810 811 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec); 812 libfdisk_fini(&epp); 813 if (rval != FDISK_SUCCESS) { 814 /* No solaris logical partition */ 815 (void) fprintf(stderr, gettext("Solaris partition not found. " 816 "Aborting operation.\n")); 817 return (BC_ERROR); 818 } 819 820 device->stage.start = secnum; 821 device->stage.size = numsec; 822 device->stage.id = pno; 823 log_part = 1; 824 825 found_part: 826 /* get confirmation for -m */ 827 if (write_mbr && !force_mbr) { 828 (void) fprintf(stdout, gettext("Updating master boot sector " 829 "destroys existing boot managers (if any).\n" 830 "continue (y/n)? ")); 831 if (!yes()) { 832 write_mbr = 0; 833 (void) fprintf(stdout, gettext("master boot sector " 834 "not updated\n")); 835 return (BC_ERROR); 836 } 837 } 838 839 /* 840 * warn, if illumos in primary partition and loader not in MBR and 841 * partition is not active 842 */ 843 if (device->devtype != IG_DEV_EFI) { 844 if (!log_part && part->bootid != 128 && !write_mbr) { 845 (void) fprintf(stdout, gettext("Solaris fdisk " 846 "partition is inactive.\n"), device->stage.id); 847 } 848 } 849 850 return (BC_SUCCESS); 851 } 852 853 static int 854 open_device(char *path) 855 { 856 struct stat statbuf = {0}; 857 int fd = -1; 858 859 if (nowrite) 860 fd = open(path, O_RDONLY); 861 else 862 fd = open(path, O_RDWR); 863 864 if (fd == -1) { 865 BOOT_DEBUG("Unable to open %s\n", path); 866 perror("open"); 867 return (-1); 868 } 869 870 if (fstat(fd, &statbuf) != 0) { 871 BOOT_DEBUG("Unable to stat %s\n", path); 872 perror("stat"); 873 (void) close(fd); 874 return (-1); 875 } 876 877 if (S_ISCHR(statbuf.st_mode) == 0) { 878 (void) fprintf(stderr, gettext("%s: Not a character device\n"), 879 path); 880 (void) close(fd); 881 return (-1); 882 } 883 884 return (fd); 885 } 886 887 static int 888 get_boot_partition(ib_device_t *device, struct mboot *mbr) 889 { 890 struct ipart *part; 891 char *path, *ptr; 892 int i; 893 894 part = (struct ipart *)mbr->parts; 895 for (i = 0; i < FD_NUMPART; i++) { 896 if (part[i].systid == X86BOOT) 897 break; 898 } 899 900 /* no X86BOOT, try to use space between MBR and first partition */ 901 if (i == FD_NUMPART) { 902 device->stage.path = strdup(device->path); 903 if (device->stage.path == NULL) { 904 perror(gettext("Memory allocation failure")); 905 return (BC_ERROR); 906 } 907 device->stage.fd = dup(device->fd); 908 device->stage.id = 0; 909 device->stage.devtype = IG_DEV_MBR; 910 device->stage.fstype = IG_FS_NONE; 911 device->stage.start = 0; 912 device->stage.size = part[0].relsect; 913 device->stage.offset = BBLK_BLKLIST_OFF; 914 return (BC_SUCCESS); 915 } 916 917 if ((path = strdup(device->path)) == NULL) { 918 perror(gettext("Memory allocation failure")); 919 return (BC_ERROR); 920 } 921 922 ptr = strrchr(path, 'p'); 923 ptr++; 924 *ptr = '\0'; 925 (void) asprintf(&ptr, "%s%d", path, i+1); /* partitions are p1..p4 */ 926 free(path); 927 if (ptr == NULL) { 928 perror(gettext("Memory allocation failure")); 929 return (BC_ERROR); 930 } 931 device->stage.path = ptr; 932 device->stage.fd = open_device(ptr); 933 device->stage.id = i + 1; 934 device->stage.devtype = IG_DEV_MBR; 935 device->stage.fstype = IG_FS_NONE; 936 device->stage.start = part[i].relsect; 937 device->stage.size = part[i].numsect; 938 device->stage.offset = 1; /* leave sector 0 for VBR */ 939 return (BC_SUCCESS); 940 } 941 942 static int 943 get_boot_slice(ib_device_t *device, struct dk_gpt *vtoc) 944 { 945 uint_t i; 946 char *path, *ptr; 947 948 for (i = 0; i < vtoc->efi_nparts; i++) { 949 if (vtoc->efi_parts[i].p_tag == V_BOOT) { 950 if ((path = strdup(device->target.path)) == NULL) { 951 perror(gettext("Memory allocation failure")); 952 return (BC_ERROR); 953 } 954 ptr = strrchr(path, 's'); 955 ptr++; 956 *ptr = '\0'; 957 (void) asprintf(&ptr, "%s%d", path, i); 958 free(path); 959 if (ptr == NULL) { 960 perror(gettext("Memory allocation failure")); 961 return (BC_ERROR); 962 } 963 device->stage.path = ptr; 964 device->stage.fd = open_device(ptr); 965 device->stage.id = i; 966 device->stage.devtype = IG_DEV_EFI; 967 device->stage.fstype = IG_FS_NONE; 968 device->stage.start = vtoc->efi_parts[i].p_start; 969 device->stage.size = vtoc->efi_parts[i].p_size; 970 device->stage.offset = 1; /* leave sector 0 for VBR */ 971 return (BC_SUCCESS); 972 } 973 } 974 return (BC_SUCCESS); 975 } 976 977 static int 978 init_device(ib_device_t *device, char *path) 979 { 980 struct dk_gpt *vtoc; 981 fstyp_handle_t fhdl; 982 const char *fident; 983 char *p; 984 int pathlen = strlen(path); 985 int ret; 986 987 bzero(device, sizeof (*device)); 988 device->fd = -1; /* whole disk fd */ 989 device->stage.fd = -1; /* bootblock partition fd */ 990 device->target.fd = -1; /* target fs partition fd */ 991 992 /* basic check, whole disk is not allowed */ 993 if ((p = strrchr(path, '/')) == NULL) 994 p = path; 995 if ((strrchr(p, 'p') == NULL && strrchr(p, 's') == NULL) || 996 (path[pathlen-2] == 'p' && path[pathlen-1] == '0')) { 997 (void) fprintf(stderr, gettext("installing loader to " 998 "whole disk device is not supported\n")); 999 } 1000 1001 device->target.path = strdup(path); 1002 if (device->target.path == NULL) { 1003 perror(gettext("Memory allocation failure")); 1004 return (BC_ERROR); 1005 } 1006 device->path = strdup(path); 1007 if (device->path == NULL) { 1008 perror(gettext("Memory allocation failure")); 1009 return (BC_ERROR); 1010 } 1011 1012 /* change device name to p0 */ 1013 device->path[pathlen - 2] = 'p'; 1014 device->path[pathlen - 1] = '0'; 1015 1016 if (strstr(device->target.path, "diskette")) { 1017 (void) fprintf(stderr, gettext("installing loader to a floppy " 1018 "disk is not supported\n")); 1019 return (BC_ERROR); 1020 } 1021 1022 /* Detect if the target device is a pcfs partition. */ 1023 if (strstr(device->target.path, "p0:boot")) { 1024 (void) fprintf(stderr, gettext("installing loader to x86 boot " 1025 "partition is not supported\n")); 1026 return (BC_ERROR); 1027 } 1028 1029 if ((device->fd = open_device(device->path)) == -1) 1030 return (BC_ERROR); 1031 1032 /* read in the device boot sector. */ 1033 if (read(device->fd, device->mbr, SECTOR_SIZE) != SECTOR_SIZE) { 1034 (void) fprintf(stderr, gettext("Error reading boot sector\n")); 1035 perror("read"); 1036 return (BC_ERROR); 1037 } 1038 1039 device->devtype = IG_DEV_VTOC; 1040 if (efi_alloc_and_read(device->fd, &vtoc) >= 0) { 1041 ret = get_boot_slice(device, vtoc); 1042 device->devtype = IG_DEV_EFI; 1043 efi_free(vtoc); 1044 if (ret == BC_ERROR) 1045 return (BC_ERROR); 1046 } else if (device->target.path[pathlen - 2] == 'p') { 1047 device->devtype = IG_DEV_MBR; 1048 ret = get_boot_partition(device, (struct mboot *)device->mbr); 1049 if (ret == BC_ERROR) 1050 return (BC_ERROR); 1051 } else if (device->target.path[pathlen - 1] == '2') { 1052 /* 1053 * NOTE: we could relax there and allow zfs boot on 1054 * slice 2 for instance, but lets keep traditional limits. 1055 */ 1056 (void) fprintf(stderr, 1057 gettext("raw device must be a root slice (not s2)\n")); 1058 return (BC_ERROR); 1059 } 1060 1061 /* fill stage partition for case there is no boot partition */ 1062 if (device->stage.path == NULL) { 1063 if ((device->stage.path = strdup(path)) == NULL) { 1064 perror(gettext("Memory allocation failure")); 1065 return (BC_ERROR); 1066 } 1067 if (device->devtype == IG_DEV_VTOC) { 1068 /* use slice 2 */ 1069 device->stage.path[pathlen - 2] = 's'; 1070 device->stage.path[pathlen - 1] = '2'; 1071 device->stage.id = 2; 1072 } else { 1073 p = strrchr(device->stage.path, 'p'); 1074 if (p == NULL) 1075 p = strrchr(device->stage.path, 's'); 1076 device->stage.id = atoi(++p); 1077 } 1078 device->stage.devtype = device->devtype; 1079 device->stage.fd = open_device(device->stage.path); 1080 } 1081 1082 p = strrchr(device->target.path, 'p'); 1083 if (p == NULL) 1084 p = strrchr(device->target.path, 's'); 1085 device->target.id = atoi(++p); 1086 1087 if (strcmp(device->stage.path, device->target.path) == 0) 1088 device->target.fd = dup(device->stage.fd); 1089 else 1090 device->target.fd = open_device(device->target.path); 1091 1092 if (fstyp_init(device->target.fd, 0, NULL, &fhdl) != 0) 1093 return (BC_ERROR); 1094 1095 if (fstyp_ident(fhdl, NULL, &fident) != 0) { 1096 fstyp_fini(fhdl); 1097 (void) fprintf(stderr, gettext("Failed to detect file " 1098 "system type\n")); 1099 return (BC_ERROR); 1100 } 1101 1102 /* at this moment non-boot partition has no size set, use this fact */ 1103 if (device->devtype == IG_DEV_EFI && strcmp(fident, "zfs") && 1104 device->stage.size == 0) { 1105 fstyp_fini(fhdl); 1106 (void) fprintf(stderr, gettext("Booting %s of EFI labeled " 1107 "disks requires the boot partition.\n"), fident); 1108 return (BC_ERROR); 1109 } 1110 if (strcmp(fident, "zfs") == 0) 1111 device->target.fstype = IG_FS_ZFS; 1112 else if (strcmp(fident, "ufs") == 0) { 1113 device->target.fstype = IG_FS_UFS; 1114 } else if (strcmp(fident, "pcfs") == 0) { 1115 device->target.fstype = IG_FS_PCFS; 1116 } else { 1117 (void) fprintf(stderr, gettext("File system %s is not " 1118 "supported by loader\n"), fident); 1119 fstyp_fini(fhdl); 1120 return (BC_ERROR); 1121 } 1122 fstyp_fini(fhdl); 1123 1124 /* check for boot partition content */ 1125 if (device->stage.size) { 1126 if (fstyp_init(device->stage.fd, 0, NULL, &fhdl) != 0) 1127 return (BC_ERROR); 1128 1129 if (fstyp_ident(fhdl, NULL, &fident) == 0) { 1130 (void) fprintf(stderr, gettext("Unexpected %s file " 1131 "system on boot partition\n"), fident); 1132 fstyp_fini(fhdl); 1133 return (BC_ERROR); 1134 } 1135 fstyp_fini(fhdl); 1136 } 1137 return (get_start_sector(device)); 1138 } 1139 1140 static void 1141 cleanup_device(ib_device_t *device) 1142 { 1143 if (device->path) 1144 free(device->path); 1145 if (device->stage.path) 1146 free(device->stage.path); 1147 if (device->target.path) 1148 free(device->target.path); 1149 1150 if (device->fd != -1) 1151 (void) close(device->fd); 1152 if (device->stage.fd != -1) 1153 (void) close(device->stage.fd); 1154 if (device->target.fd != -1) 1155 (void) close(device->target.fd); 1156 bzero(device, sizeof (*device)); 1157 } 1158 1159 static void 1160 cleanup_bootblock(ib_bootblock_t *bblock) 1161 { 1162 free(bblock->buf); 1163 bzero(bblock, sizeof (ib_bootblock_t)); 1164 } 1165 1166 /* 1167 * Propagate the bootblock on the source disk to the destination disk and 1168 * version it with 'updt_str' in the process. Since we cannot trust any data 1169 * on the attaching disk, we do not perform any specific check on a potential 1170 * target extended information structure and we just blindly update. 1171 */ 1172 static int 1173 propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str) 1174 { 1175 ib_bootblock_t *src_bblock = &src->bootblock; 1176 ib_bootblock_t *dest_bblock = &dest->bootblock; 1177 1178 assert(src != NULL); 1179 assert(dest != NULL); 1180 1181 /* read the stage1 file from source disk */ 1182 if (read(src->device.fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) { 1183 (void) fprintf(stderr, gettext("cannot read stage1 from %s\n"), 1184 src->device.path); 1185 return (BC_ERROR); 1186 } 1187 1188 cleanup_bootblock(dest_bblock); 1189 1190 dest_bblock->buf_size = src_bblock->buf_size; 1191 dest_bblock->buf = malloc(dest_bblock->buf_size); 1192 if (dest_bblock->buf == NULL) { 1193 perror(gettext("Memory Allocation Failure")); 1194 return (BC_ERROR); 1195 } 1196 dest_bblock->file = dest_bblock->buf; 1197 dest_bblock->file_size = src_bblock->file_size; 1198 (void) memcpy(dest_bblock->buf, src_bblock->buf, 1199 dest_bblock->buf_size); 1200 1201 dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file + 1202 src_bblock->mboot_off); 1203 dest_bblock->mboot_off = src_bblock->mboot_off; 1204 dest_bblock->extra = (char *)dest_bblock->file + 1205 P2ROUNDUP(dest_bblock->file_size, 8); 1206 dest_bblock->extra_size = src_bblock->extra_size; 1207 1208 (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"), 1209 src->device.path, dest->device.path); 1210 1211 return (commit_to_disk(dest, updt_str)); 1212 } 1213 1214 static int 1215 commit_to_disk(ib_data_t *data, char *update_str) 1216 { 1217 assert(data != NULL); 1218 1219 if (prepare_bootblock(data, update_str) != BC_SUCCESS) { 1220 (void) fprintf(stderr, gettext("Error updating the bootblock " 1221 "image\n")); 1222 return (BC_ERROR); 1223 } 1224 1225 if (prepare_stage1(data) != BC_SUCCESS) { 1226 (void) fprintf(stderr, gettext("Error updating the stage1 " 1227 "image\n")); 1228 return (BC_ERROR); 1229 } 1230 1231 if (write_bootblock(data) != BC_SUCCESS) { 1232 (void) fprintf(stderr, gettext("Error writing bootblock to " 1233 "disk\n")); 1234 return (BC_ERROR); 1235 } 1236 1237 return (write_stage1(data)); 1238 } 1239 1240 /* 1241 * Install a new bootblock on the given device. handle_install() expects argv 1242 * to contain 3 parameters (the target device path and the path to the 1243 * bootblock. 1244 * 1245 * Returns: BC_SUCCESS - if the installation is successful 1246 * BC_ERROR - if the installation failed 1247 * BC_NOUPDT - if no installation was performed because the 1248 * version currently installed is more recent than the 1249 * supplied one. 1250 * 1251 */ 1252 static int 1253 handle_install(char *progname, char **argv) 1254 { 1255 ib_data_t install_data; 1256 char *stage1 = NULL; 1257 char *bootblock = NULL; 1258 char *device_path = NULL; 1259 int ret = BC_ERROR; 1260 1261 stage1 = strdup(argv[0]); 1262 bootblock = strdup(argv[1]); 1263 device_path = strdup(argv[2]); 1264 1265 if (!device_path || !bootblock || !stage1) { 1266 (void) fprintf(stderr, gettext("Missing parameter")); 1267 usage(progname); 1268 goto out; 1269 } 1270 1271 BOOT_DEBUG("device path: %s, stage1 path: %s bootblock path: %s\n", 1272 device_path, stage1, bootblock); 1273 bzero(&install_data, sizeof (ib_data_t)); 1274 1275 if (init_device(&install_data.device, device_path) != BC_SUCCESS) { 1276 (void) fprintf(stderr, gettext("Unable to open device %s\n"), 1277 device_path); 1278 goto out; 1279 } 1280 1281 if (read_stage1_from_file(stage1, &install_data) != BC_SUCCESS) { 1282 (void) fprintf(stderr, gettext("Error opening %s\n"), stage1); 1283 goto out_dev; 1284 } 1285 1286 if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) { 1287 (void) fprintf(stderr, gettext("Error reading %s\n"), 1288 bootblock); 1289 goto out_dev; 1290 } 1291 1292 /* 1293 * is_update_necessary() will take care of checking if versioning and/or 1294 * forcing the update have been specified. It will also emit a warning 1295 * if a non-versioned update is attempted over a versioned bootblock. 1296 */ 1297 if (!is_update_necessary(&install_data, update_str)) { 1298 (void) fprintf(stderr, gettext("bootblock version installed " 1299 "on %s is more recent or identical\n" 1300 "Use -F to override or install without the -u option\n"), 1301 device_path); 1302 ret = BC_NOUPDT; 1303 goto out_dev; 1304 } 1305 1306 BOOT_DEBUG("Ready to commit to disk\n"); 1307 ret = commit_to_disk(&install_data, update_str); 1308 1309 out_dev: 1310 cleanup_device(&install_data.device); 1311 out: 1312 free(stage1); 1313 free(bootblock); 1314 free(device_path); 1315 return (ret); 1316 } 1317 1318 /* 1319 * Retrieves from a device the extended information (einfo) associated to the 1320 * installed stage2. 1321 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0. 1322 * Returns: 1323 * - BC_SUCCESS (and prints out einfo contents depending on 'flags') 1324 * - BC_ERROR (on error) 1325 * - BC_NOEINFO (no extended information available) 1326 */ 1327 static int 1328 handle_getinfo(char *progname, char **argv) 1329 { 1330 1331 ib_data_t data; 1332 ib_bootblock_t *bblock = &data.bootblock; 1333 ib_device_t *device = &data.device; 1334 bblk_einfo_t *einfo; 1335 uint8_t flags = 0; 1336 char *device_path, *path; 1337 int retval = BC_ERROR; 1338 int ret; 1339 1340 device_path = strdup(argv[0]); 1341 if (!device_path) { 1342 (void) fprintf(stderr, gettext("Missing parameter")); 1343 usage(progname); 1344 goto out; 1345 } 1346 1347 bzero(&data, sizeof (ib_data_t)); 1348 BOOT_DEBUG("device path: %s\n", device_path); 1349 1350 if (init_device(device, device_path) != BC_SUCCESS) { 1351 (void) fprintf(stderr, gettext("Unable to gather device " 1352 "information from %s\n"), device_path); 1353 goto out_dev; 1354 } 1355 1356 ret = read_bootblock_from_disk(device, bblock, &path); 1357 if (ret == BC_ERROR) { 1358 (void) fprintf(stderr, gettext("Error reading bootblock from " 1359 "%s\n"), path); 1360 goto out_dev; 1361 } 1362 1363 if (ret == BC_NOEXTRA) { 1364 BOOT_DEBUG("No multiboot header found on %s, unable " 1365 "to locate extra information area (old/non versioned " 1366 "bootblock?) \n", device_path); 1367 (void) fprintf(stderr, gettext("No extended information " 1368 "found\n")); 1369 retval = BC_NOEINFO; 1370 goto out_dev; 1371 } 1372 1373 einfo = find_einfo(bblock->extra, bblock->extra_size); 1374 if (einfo == NULL) { 1375 retval = BC_NOEINFO; 1376 (void) fprintf(stderr, gettext("No extended information " 1377 "found\n")); 1378 goto out_dev; 1379 } 1380 1381 /* Print the extended information. */ 1382 if (strip) 1383 flags |= EINFO_EASY_PARSE; 1384 if (verbose_dump) 1385 flags |= EINFO_PRINT_HEADER; 1386 1387 print_einfo(flags, einfo, bblock->extra_size); 1388 retval = BC_SUCCESS; 1389 1390 out_dev: 1391 cleanup_device(&data.device); 1392 out: 1393 free(device_path); 1394 return (retval); 1395 } 1396 1397 /* 1398 * Attempt to mirror (propagate) the current bootblock over the attaching disk. 1399 * 1400 * Returns: 1401 * - BC_SUCCESS (a successful propagation happened) 1402 * - BC_ERROR (an error occurred) 1403 * - BC_NOEXTRA (it is not possible to dump the current bootblock since 1404 * there is no multiboot information) 1405 */ 1406 static int 1407 handle_mirror(char *progname, char **argv) 1408 { 1409 ib_data_t curr_data; 1410 ib_data_t attach_data; 1411 ib_device_t *curr_device = &curr_data.device; 1412 ib_device_t *attach_device = &attach_data.device; 1413 ib_bootblock_t *bblock_curr = &curr_data.bootblock; 1414 ib_bootblock_t *bblock_attach = &attach_data.bootblock; 1415 bblk_einfo_t *einfo_curr = NULL; 1416 char *curr_device_path; 1417 char *attach_device_path; 1418 char *updt_str = NULL; 1419 char *path; 1420 int retval = BC_ERROR; 1421 int ret; 1422 1423 curr_device_path = strdup(argv[0]); 1424 attach_device_path = strdup(argv[1]); 1425 1426 if (!curr_device_path || !attach_device_path) { 1427 (void) fprintf(stderr, gettext("Missing parameter")); 1428 usage(progname); 1429 goto out; 1430 } 1431 BOOT_DEBUG("Current device path is: %s, attaching device path is: " 1432 " %s\n", curr_device_path, attach_device_path); 1433 1434 bzero(&curr_data, sizeof (ib_data_t)); 1435 bzero(&attach_data, sizeof (ib_data_t)); 1436 1437 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) { 1438 (void) fprintf(stderr, gettext("Unable to gather device " 1439 "information from %s (current device)\n"), 1440 curr_device_path); 1441 goto out_currdev; 1442 } 1443 1444 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) { 1445 (void) fprintf(stderr, gettext("Unable to gather device " 1446 "information from %s (attaching device)\n"), 1447 attach_device_path); 1448 goto out_devs; 1449 } 1450 1451 ret = read_bootblock_from_disk(curr_device, bblock_curr, &path); 1452 if (ret == BC_ERROR) { 1453 BOOT_DEBUG("Error reading bootblock from %s\n", path); 1454 retval = BC_ERROR; 1455 goto out_devs; 1456 } 1457 1458 if (ret == BC_NOEXTRA) { 1459 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve" 1460 " the bootblock\n", path); 1461 retval = BC_NOEXTRA; 1462 goto out_devs; 1463 } 1464 1465 write_mbr = B_TRUE; 1466 force_mbr = B_TRUE; 1467 einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size); 1468 if (einfo_curr != NULL) 1469 updt_str = einfo_get_string(einfo_curr); 1470 1471 retval = propagate_bootblock(&curr_data, &attach_data, updt_str); 1472 cleanup_bootblock(bblock_curr); 1473 cleanup_bootblock(bblock_attach); 1474 out_devs: 1475 cleanup_device(attach_device); 1476 out_currdev: 1477 cleanup_device(curr_device); 1478 out: 1479 free(curr_device_path); 1480 free(attach_device_path); 1481 return (retval); 1482 } 1483 1484 #define USAGE_STRING "Usage:\t%s [-h|-m|-f|-n|-F|-u verstr] stage1 stage2 " \ 1485 "raw-device\n" \ 1486 "\t%s -M [-n] raw-device attach-raw-device\n" \ 1487 "\t%s [-e|-V] -i raw-device\n" 1488 1489 #define CANON_USAGE_STR gettext(USAGE_STRING) 1490 1491 static void 1492 usage(char *progname) 1493 { 1494 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname); 1495 } 1496 1497 int 1498 main(int argc, char **argv) 1499 { 1500 int opt; 1501 int params = 3; 1502 int ret; 1503 char *progname; 1504 char **handle_args; 1505 1506 (void) setlocale(LC_ALL, ""); 1507 (void) textdomain(TEXT_DOMAIN); 1508 if (init_yes() < 0) { 1509 (void) fprintf(stderr, gettext(ERR_MSG_INIT_YES), 1510 strerror(errno)); 1511 exit(BC_ERROR); 1512 } 1513 1514 while ((opt = getopt(argc, argv, "deFfhiMmnu:V")) != EOF) { 1515 switch (opt) { 1516 case 'd': 1517 boot_debug = B_TRUE; 1518 break; 1519 case 'e': 1520 strip = B_TRUE; 1521 break; 1522 case 'F': 1523 force_update = B_TRUE; 1524 break; 1525 case 'f': 1526 force_mbr = B_TRUE; 1527 break; 1528 case 'h': 1529 usage(argv[0]); 1530 exit(BC_SUCCESS); 1531 break; 1532 case 'i': 1533 do_getinfo = B_TRUE; 1534 params = 1; 1535 break; 1536 case 'M': 1537 do_mirror_bblk = B_TRUE; 1538 params = 2; 1539 break; 1540 case 'm': 1541 write_mbr = B_TRUE; 1542 break; 1543 case 'n': 1544 nowrite = B_TRUE; 1545 break; 1546 case 'u': 1547 do_version = B_TRUE; 1548 1549 update_str = malloc(strlen(optarg) + 1); 1550 if (update_str == NULL) { 1551 perror(gettext("Memory allocation failure")); 1552 exit(BC_ERROR); 1553 } 1554 (void) strlcpy(update_str, optarg, strlen(optarg) + 1); 1555 break; 1556 case 'V': 1557 verbose_dump = B_TRUE; 1558 break; 1559 default: 1560 /* fall through to process non-optional args */ 1561 break; 1562 } 1563 } 1564 1565 /* check arguments */ 1566 if (argc != optind + params) { 1567 usage(argv[0]); 1568 exit(BC_ERROR); 1569 } 1570 progname = argv[0]; 1571 check_options(progname); 1572 handle_args = argv + optind; 1573 1574 if (nowrite) 1575 (void) fprintf(stdout, gettext("Dry run requested. Nothing will" 1576 " be written to disk.\n")); 1577 1578 if (do_getinfo) { 1579 ret = handle_getinfo(progname, handle_args); 1580 } else if (do_mirror_bblk) { 1581 ret = handle_mirror(progname, handle_args); 1582 } else { 1583 ret = handle_install(progname, handle_args); 1584 } 1585 return (ret); 1586 } 1587 1588 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n") 1589 static void 1590 check_options(char *progname) 1591 { 1592 if (do_getinfo && do_mirror_bblk) { 1593 (void) fprintf(stderr, gettext("Only one of -M and -i can be " 1594 "specified at the same time\n")); 1595 usage(progname); 1596 exit(BC_ERROR); 1597 } 1598 1599 if (do_mirror_bblk) { 1600 /* 1601 * -u and -F may actually reflect a user intent that is not 1602 * correct with this command (mirror can be interpreted 1603 * "similar" to install. Emit a message and continue. 1604 * -e and -V have no meaning, be quiet here and only report the 1605 * incongruence if a debug output is requested. 1606 */ 1607 if (do_version) { 1608 (void) fprintf(stderr, MEANINGLESS_OPT, "-u"); 1609 do_version = B_FALSE; 1610 } 1611 if (force_update) { 1612 (void) fprintf(stderr, MEANINGLESS_OPT, "-F"); 1613 force_update = B_FALSE; 1614 } 1615 if (strip || verbose_dump) { 1616 BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V"); 1617 strip = B_FALSE; 1618 verbose_dump = B_FALSE; 1619 } 1620 } 1621 1622 if (do_getinfo) { 1623 if (write_mbr || force_mbr || do_version || force_update) { 1624 BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F"); 1625 write_mbr = force_mbr = do_version = B_FALSE; 1626 force_update = B_FALSE; 1627 } 1628 } 1629 } 1630