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