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 2017 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 #include <err.h> 36 37 #include <sys/dktp/fdisk.h> 38 #include <sys/dkio.h> 39 #include <sys/vtoc.h> 40 #include <sys/multiboot.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/sysmacros.h> 44 #include <sys/efi_partition.h> 45 #include <libfstyp.h> 46 #include <libgen.h> 47 #include <uuid/uuid.h> 48 49 #include "installboot.h" 50 #include "bblk_einfo.h" 51 #include "boot_utils.h" 52 #include "mboot_extra.h" 53 #include "getresponse.h" 54 55 #ifndef TEXT_DOMAIN 56 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 57 #endif 58 59 /* 60 * BIOS bootblock installation: 61 * 62 * 1. MBR is first sector of the disk. If the file system on target is 63 * ufs or zfs, the same MBR code is installed on first sector of the 64 * partition as well; this will allow to have real MBR sector to be 65 * replaced by some other boot loader and have illumos chainloaded. 66 * 67 * installboot will record the start LBA and size of stage2 code in MBR code. 68 * On boot, the MBR code will read the stage2 code and executes it. 69 * 70 * 2. Stage2 location depends on file system type; 71 * In case of zfs, installboot will store stage2 to zfs bootblk area, 72 * which is 512k bytes from partition start and size is 3.5MB. 73 * 74 * In case of ufs, the stage2 location is 50 512B sectors from 75 * Solaris2 MBR partition start, within boot slice, boot slice size is 76 * one cylinder. 77 * 78 * In case of pcfs, the stage2 location is 50 512B sectors from beginning 79 * of the disk, filling the space between MBR and first partition. 80 * This location assumes no other bootloader and the space is one cylinder, 81 * as first partition is starting from cylinder 1. 82 * 83 * In case of GPT partitioning and if file system is not zfs, the boot 84 * support is only possible with dedicated boot partition. For GPT, 85 * the current implementation is using BOOT partition, which must exist. 86 * BOOT partition does only contain raw boot blocks, without any file system. 87 * 88 * Loader stage2 is created with embedded version, by using fake multiboot (MB) 89 * header within first 32k and EINFO block is at the end of the actual 90 * boot block. MB header load_addr is set to 0 and load_end_addr is set to 91 * actual block end, so the EINFO size is (file size - load_end_addr). 92 * installboot does also store the illumos boot partition LBA to MB space, 93 * starting from bss_end_addr structure member location; stage2 will 94 * detect the partition and file system based on this value. 95 * 96 * Stored location values in MBR/stage2 also mean the bootblocks must be 97 * reinstalled in case the partition content is relocated. 98 */ 99 100 static boolean_t write_mbr = B_FALSE; 101 static boolean_t force_mbr = B_FALSE; 102 static boolean_t force_update = B_FALSE; 103 static boolean_t do_getinfo = B_FALSE; 104 static boolean_t do_version = B_FALSE; 105 static boolean_t do_mirror_bblk = B_FALSE; 106 static boolean_t strip = B_FALSE; 107 static boolean_t verbose_dump = B_FALSE; 108 109 /* Versioning string, if present. */ 110 static char *update_str; 111 112 /* 113 * Temporary buffer to store the first 32K of data looking for a multiboot 114 * signature. 115 */ 116 char mboot_scan[MBOOT_SCAN_SIZE]; 117 118 /* Function prototypes. */ 119 static void check_options(char *); 120 static int get_start_sector(ib_device_t *); 121 122 static int read_stage1_from_file(char *, ib_data_t *); 123 static int read_bootblock_from_file(char *, ib_bootblock_t *); 124 static int read_bootblock_from_disk(ib_device_t *, ib_bootblock_t *, char **); 125 static void add_bootblock_einfo(ib_bootblock_t *, char *); 126 static int prepare_stage1(ib_data_t *); 127 static int prepare_bootblock(ib_data_t *, char *); 128 static int write_stage1(ib_data_t *); 129 static int write_bootblock(ib_data_t *); 130 static int init_device(ib_device_t *, char *); 131 static void cleanup_device(ib_device_t *); 132 static int commit_to_disk(ib_data_t *, char *); 133 static int handle_install(char *, char **); 134 static int handle_getinfo(char *, char **); 135 static int handle_mirror(char *, char **); 136 static boolean_t is_update_necessary(ib_data_t *, char *); 137 static int propagate_bootblock(ib_data_t *, ib_data_t *, char *); 138 static void usage(char *, int) __NORETURN; 139 140 static int 141 read_stage1_from_file(char *path, ib_data_t *dest) 142 { 143 int fd; 144 145 assert(dest != NULL); 146 147 /* read the stage1 file from filesystem */ 148 fd = open(path, O_RDONLY); 149 if (fd == -1 || 150 read(fd, dest->stage1, SECTOR_SIZE) != SECTOR_SIZE) { 151 (void) fprintf(stderr, gettext("cannot read stage1 file %s\n"), 152 path); 153 return (BC_ERROR); 154 } 155 (void) close(fd); 156 return (BC_SUCCESS); 157 } 158 159 static int 160 read_bootblock_from_file(char *file, ib_bootblock_t *bblock) 161 { 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(bblock != 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 ib_bootblock_t *bblock = &install_data.bootblock; 1257 char *stage1 = NULL; 1258 char *bootblock = NULL; 1259 char *device_path = NULL; 1260 int ret = BC_ERROR; 1261 1262 stage1 = strdup(argv[0]); 1263 bootblock = strdup(argv[1]); 1264 device_path = strdup(argv[2]); 1265 1266 if (!device_path || !bootblock || !stage1) { 1267 (void) fprintf(stderr, gettext("Missing parameter")); 1268 usage(progname, BC_ERROR); 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, bblock) != 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 * file or installed stage2. 1321 * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0 1322 * or file name. 1323 * Returns: 1324 * - BC_SUCCESS (and prints out einfo contents depending on 'flags') 1325 * - BC_ERROR (on error) 1326 * - BC_NOEINFO (no extended information available) 1327 */ 1328 static int 1329 handle_getinfo(char *progname, char **argv) 1330 { 1331 struct stat sb; 1332 ib_bootblock_t bblock; 1333 ib_device_t 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, BC_ERROR); 1344 } 1345 1346 if (stat(device_path, &sb) == -1) { 1347 perror("stat"); 1348 goto out; 1349 } 1350 1351 bzero(&bblock, sizeof (bblock)); 1352 bzero(&device, sizeof (device)); 1353 BOOT_DEBUG("device path: %s\n", device_path); 1354 1355 if (S_ISREG(sb.st_mode) != 0) { 1356 path = device_path; 1357 ret = read_bootblock_from_file(device_path, &bblock); 1358 } else { 1359 if (init_device(&device, device_path) != BC_SUCCESS) { 1360 (void) fprintf(stderr, gettext("Unable to gather " 1361 "device information from %s\n"), device_path); 1362 goto out_dev; 1363 } 1364 ret = read_bootblock_from_disk(&device, &bblock, &path); 1365 } 1366 1367 if (ret == BC_ERROR) { 1368 (void) fprintf(stderr, gettext("Error reading bootblock from " 1369 "%s\n"), path); 1370 goto out_dev; 1371 } 1372 1373 if (ret == BC_NOEXTRA) { 1374 BOOT_DEBUG("No multiboot header found on %s, unable " 1375 "to locate extra information area (old/non versioned " 1376 "bootblock?) \n", device_path); 1377 (void) fprintf(stderr, gettext("No extended information " 1378 "found\n")); 1379 retval = BC_NOEINFO; 1380 goto out_dev; 1381 } 1382 1383 einfo = find_einfo(bblock.extra, bblock.extra_size); 1384 if (einfo == NULL) { 1385 retval = BC_NOEINFO; 1386 (void) fprintf(stderr, gettext("No extended information " 1387 "found\n")); 1388 goto out_dev; 1389 } 1390 1391 /* Print the extended information. */ 1392 if (strip) 1393 flags |= EINFO_EASY_PARSE; 1394 if (verbose_dump) 1395 flags |= EINFO_PRINT_HEADER; 1396 1397 print_einfo(flags, einfo, bblock.extra_size); 1398 retval = BC_SUCCESS; 1399 1400 out_dev: 1401 if (S_ISREG(sb.st_mode) == 0) 1402 cleanup_device(&device); 1403 out: 1404 free(device_path); 1405 return (retval); 1406 } 1407 1408 /* 1409 * Attempt to mirror (propagate) the current bootblock over the attaching disk. 1410 * 1411 * Returns: 1412 * - BC_SUCCESS (a successful propagation happened) 1413 * - BC_ERROR (an error occurred) 1414 * - BC_NOEXTRA (it is not possible to dump the current bootblock since 1415 * there is no multiboot information) 1416 */ 1417 static int 1418 handle_mirror(char *progname, char **argv) 1419 { 1420 ib_data_t curr_data; 1421 ib_data_t attach_data; 1422 ib_device_t *curr_device = &curr_data.device; 1423 ib_device_t *attach_device = &attach_data.device; 1424 ib_bootblock_t *bblock_curr = &curr_data.bootblock; 1425 ib_bootblock_t *bblock_attach = &attach_data.bootblock; 1426 bblk_einfo_t *einfo_curr = NULL; 1427 char *curr_device_path; 1428 char *attach_device_path; 1429 char *updt_str = NULL; 1430 char *path; 1431 int retval = BC_ERROR; 1432 int ret; 1433 1434 curr_device_path = strdup(argv[0]); 1435 attach_device_path = strdup(argv[1]); 1436 1437 if (!curr_device_path || !attach_device_path) { 1438 (void) fprintf(stderr, gettext("Missing parameter")); 1439 usage(progname, BC_ERROR); 1440 } 1441 BOOT_DEBUG("Current device path is: %s, attaching device path is: " 1442 " %s\n", curr_device_path, attach_device_path); 1443 1444 bzero(&curr_data, sizeof (ib_data_t)); 1445 bzero(&attach_data, sizeof (ib_data_t)); 1446 1447 if (init_device(curr_device, curr_device_path) != BC_SUCCESS) { 1448 (void) fprintf(stderr, gettext("Unable to gather device " 1449 "information from %s (current device)\n"), 1450 curr_device_path); 1451 goto out_currdev; 1452 } 1453 1454 if (init_device(attach_device, attach_device_path) != BC_SUCCESS) { 1455 (void) fprintf(stderr, gettext("Unable to gather device " 1456 "information from %s (attaching device)\n"), 1457 attach_device_path); 1458 goto out_devs; 1459 } 1460 1461 ret = read_bootblock_from_disk(curr_device, bblock_curr, &path); 1462 if (ret == BC_ERROR) { 1463 BOOT_DEBUG("Error reading bootblock from %s\n", path); 1464 retval = BC_ERROR; 1465 goto out_devs; 1466 } 1467 1468 if (ret == BC_NOEXTRA) { 1469 BOOT_DEBUG("No multiboot header found on %s, unable to retrieve" 1470 " the bootblock\n", path); 1471 retval = BC_NOEXTRA; 1472 goto out_devs; 1473 } 1474 1475 write_mbr = B_TRUE; 1476 force_mbr = B_TRUE; 1477 einfo_curr = find_einfo(bblock_curr->extra, bblock_curr->extra_size); 1478 if (einfo_curr != NULL) 1479 updt_str = einfo_get_string(einfo_curr); 1480 1481 retval = propagate_bootblock(&curr_data, &attach_data, updt_str); 1482 cleanup_bootblock(bblock_curr); 1483 cleanup_bootblock(bblock_attach); 1484 out_devs: 1485 cleanup_device(attach_device); 1486 out_currdev: 1487 cleanup_device(curr_device); 1488 free(curr_device_path); 1489 free(attach_device_path); 1490 return (retval); 1491 } 1492 1493 #define USAGE_STRING "Usage:\t%s [-h|-m|-f|-n|-F|-u verstr] stage1 stage2 " \ 1494 "raw-device\n" \ 1495 "\t%s -M [-n] raw-device attach-raw-device\n" \ 1496 "\t%s [-e|-V] -i raw-device | file\n" 1497 1498 #define CANON_USAGE_STR gettext(USAGE_STRING) 1499 1500 static void 1501 usage(char *progname, int rc) 1502 { 1503 (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname); 1504 exit(rc); 1505 } 1506 1507 int 1508 main(int argc, char **argv) 1509 { 1510 int opt; 1511 int params = 3; 1512 int ret; 1513 char *progname; 1514 char **handle_args; 1515 1516 (void) setlocale(LC_ALL, ""); 1517 (void) textdomain(TEXT_DOMAIN); 1518 if (init_yes() < 0) 1519 errx(BC_ERROR, gettext(ERR_MSG_INIT_YES), strerror(errno)); 1520 1521 /* Determine our name */ 1522 progname = basename(argv[0]); 1523 1524 while ((opt = getopt(argc, argv, "deFfhiMmnu:V")) != EOF) { 1525 switch (opt) { 1526 case 'd': 1527 boot_debug = B_TRUE; 1528 break; 1529 case 'e': 1530 strip = B_TRUE; 1531 break; 1532 case 'F': 1533 force_update = B_TRUE; 1534 break; 1535 case 'f': 1536 force_mbr = B_TRUE; 1537 break; 1538 case 'h': 1539 usage(progname, BC_SUCCESS); 1540 break; 1541 case 'i': 1542 do_getinfo = B_TRUE; 1543 params = 1; 1544 break; 1545 case 'M': 1546 do_mirror_bblk = B_TRUE; 1547 params = 2; 1548 break; 1549 case 'm': 1550 write_mbr = B_TRUE; 1551 break; 1552 case 'n': 1553 nowrite = B_TRUE; 1554 break; 1555 case 'u': 1556 do_version = B_TRUE; 1557 1558 update_str = strdup(optarg); 1559 if (update_str == NULL) { 1560 perror(gettext("Memory allocation failure")); 1561 exit(BC_ERROR); 1562 } 1563 break; 1564 case 'V': 1565 verbose_dump = B_TRUE; 1566 break; 1567 default: 1568 /* fall through to process non-optional args */ 1569 break; 1570 } 1571 } 1572 1573 /* check arguments */ 1574 if (argc != optind + params) { 1575 usage(progname, BC_ERROR); 1576 } 1577 check_options(progname); 1578 handle_args = argv + optind; 1579 1580 if (nowrite) 1581 (void) fprintf(stdout, gettext("Dry run requested. Nothing will" 1582 " be written to disk.\n")); 1583 1584 if (do_getinfo) { 1585 ret = handle_getinfo(progname, handle_args); 1586 } else if (do_mirror_bblk) { 1587 ret = handle_mirror(progname, handle_args); 1588 } else { 1589 ret = handle_install(progname, handle_args); 1590 } 1591 return (ret); 1592 } 1593 1594 #define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n") 1595 static void 1596 check_options(char *progname) 1597 { 1598 if (do_getinfo && do_mirror_bblk) { 1599 (void) fprintf(stderr, gettext("Only one of -M and -i can be " 1600 "specified at the same time\n")); 1601 usage(progname, BC_ERROR); 1602 } 1603 1604 if (do_mirror_bblk) { 1605 /* 1606 * -u and -F may actually reflect a user intent that is not 1607 * correct with this command (mirror can be interpreted 1608 * "similar" to install. Emit a message and continue. 1609 * -e and -V have no meaning, be quiet here and only report the 1610 * incongruence if a debug output is requested. 1611 */ 1612 if (do_version) { 1613 (void) fprintf(stderr, MEANINGLESS_OPT, "-u"); 1614 do_version = B_FALSE; 1615 } 1616 if (force_update) { 1617 (void) fprintf(stderr, MEANINGLESS_OPT, "-F"); 1618 force_update = B_FALSE; 1619 } 1620 if (strip || verbose_dump) { 1621 BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V"); 1622 strip = B_FALSE; 1623 verbose_dump = B_FALSE; 1624 } 1625 } 1626 1627 if (do_getinfo) { 1628 if (write_mbr || force_mbr || do_version || force_update) { 1629 BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F"); 1630 write_mbr = force_mbr = do_version = B_FALSE; 1631 force_update = B_FALSE; 1632 } 1633 } 1634 } 1635