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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2017 Toomas Soome <tsoome@me.com> 25 */ 26 27 #include <stdio.h> 28 #include <errno.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <limits.h> 35 #include <fcntl.h> 36 #include <strings.h> 37 38 #include <sys/mman.h> 39 #include <sys/elf.h> 40 #include <sys/multiboot.h> 41 42 #include "message.h" 43 #include "bootadm.h" 44 45 direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET; 46 hv_t bam_is_hv = BAM_HV_UNKNOWN; 47 findroot_t bam_is_findroot = BAM_FINDROOT_UNKNOWN; 48 49 static void 50 get_findroot_cap(const char *osroot) 51 { 52 FILE *fp; 53 char path[PATH_MAX]; 54 char buf[BAM_MAXLINE]; 55 struct stat sb; 56 int dboot; 57 int error; 58 int ret; 59 const char *fcn = "get_findroot_cap()"; 60 61 (void) snprintf(path, sizeof (path), "%s/%s", 62 osroot, "boot/grub/capability"); 63 64 if (stat(path, &sb) == -1) { 65 bam_is_findroot = BAM_FINDROOT_ABSENT; 66 BAM_DPRINTF((D_FINDROOT_ABSENT, fcn)); 67 return; 68 } 69 70 fp = fopen(path, "r"); 71 error = errno; 72 INJECT_ERROR1("GET_CAP_FINDROOT_FOPEN", fp = NULL); 73 if (fp == NULL) { 74 bam_error(OPEN_FAIL, path, strerror(error)); 75 return; 76 } 77 78 dboot = 0; 79 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 80 if (strcmp(buf, "findroot") == 0) { 81 BAM_DPRINTF((D_FINDROOT_PRESENT, fcn)); 82 bam_is_findroot = BAM_FINDROOT_PRESENT; 83 } 84 if (strcmp(buf, "dboot") == 0) { 85 BAM_DPRINTF((D_DBOOT_PRESENT, fcn)); 86 dboot = 1; 87 } 88 } 89 90 assert(dboot); 91 92 if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) { 93 bam_is_findroot = BAM_FINDROOT_ABSENT; 94 BAM_DPRINTF((D_FINDROOT_ABSENT, fcn)); 95 } 96 out: 97 ret = fclose(fp); 98 error = errno; 99 INJECT_ERROR1("GET_CAP_FINDROOT_FCLOSE", ret = 1); 100 if (ret != 0) { 101 bam_error(CLOSE_FAIL, path, strerror(error)); 102 } 103 } 104 105 error_t 106 get_boot_cap(const char *osroot) 107 { 108 char fname[PATH_MAX]; 109 char *image; 110 uchar_t *ident; 111 uchar_t class; 112 int fd; 113 int m; 114 multiboot_header_t *mbh; 115 struct stat sb; 116 int error; 117 const char *fcn = "get_boot_cap()"; 118 119 if (is_sparc()) { 120 /* there is no non dboot sparc new-boot */ 121 bam_direct = BAM_DIRECT_DBOOT; 122 BAM_DPRINTF((D_IS_SPARC_DBOOT, fcn)); 123 return (BAM_SUCCESS); 124 } 125 126 /* 127 * The install media can support both 64 and 32 bit boot 128 * by using boot archive as ramdisk image. However, to save 129 * the memory, the ramdisk may only have either 32 or 64 130 * bit kernel files. To avoid error message about missing unix, 131 * we should try both variants here and only complain if neither 132 * is found. Since the 64-bit systems are more common, we start 133 * from amd64. 134 */ 135 class = ELFCLASS64; 136 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot, 137 "platform/i86pc/kernel/amd64/unix"); 138 fd = open(fname, O_RDONLY); 139 if (fd < 0) { 140 class = ELFCLASS32; 141 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot, 142 "platform/i86pc/kernel/unix"); 143 fd = open(fname, O_RDONLY); 144 } 145 error = errno; 146 INJECT_ERROR1("GET_CAP_UNIX_OPEN", fd = -1); 147 if (fd < 0) { 148 bam_error(OPEN_FAIL, fname, strerror(error)); 149 return (BAM_ERROR); 150 } 151 152 /* 153 * Verify that this is a sane unix at least 8192 bytes in length 154 */ 155 if (fstat(fd, &sb) == -1 || sb.st_size < 8192) { 156 (void) close(fd); 157 bam_error(INVALID_BINARY, fname); 158 return (BAM_ERROR); 159 } 160 161 /* 162 * mmap the first 8K 163 */ 164 image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0); 165 error = errno; 166 INJECT_ERROR1("GET_CAP_MMAP", image = MAP_FAILED); 167 if (image == MAP_FAILED) { 168 bam_error(MMAP_FAIL, fname, strerror(error)); 169 return (BAM_ERROR); 170 } 171 172 ident = (uchar_t *)image; 173 if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || 174 ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { 175 bam_error(NOT_ELF_FILE, fname); 176 return (BAM_ERROR); 177 } 178 if (ident[EI_CLASS] != class) { 179 bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]); 180 return (BAM_ERROR); 181 } 182 183 /* 184 * The GRUB multiboot header must be 32-bit aligned and completely 185 * contained in the 1st 8K of the file. If the unix binary has 186 * a multiboot header, then it is a 'dboot' kernel. Otherwise, 187 * this kernel must be booted via multiboot -- we call this a 188 * 'multiboot' kernel. 189 */ 190 bam_direct = BAM_DIRECT_MULTIBOOT; 191 for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) { 192 mbh = (void *)(image + m); 193 if (mbh->magic == MB_HEADER_MAGIC) { 194 BAM_DPRINTF((D_IS_DBOOT, fcn)); 195 bam_direct = BAM_DIRECT_DBOOT; 196 break; 197 } 198 } 199 (void) munmap(image, 8192); 200 (void) close(fd); 201 202 INJECT_ERROR1("GET_CAP_MULTIBOOT", bam_direct = BAM_DIRECT_MULTIBOOT); 203 if (bam_direct == BAM_DIRECT_DBOOT) { 204 if (bam_is_hv == BAM_HV_PRESENT) { 205 BAM_DPRINTF((D_IS_XVM, fcn)); 206 } else { 207 BAM_DPRINTF((D_IS_NOT_XVM, fcn)); 208 } 209 } else { 210 BAM_DPRINTF((D_IS_MULTIBOOT, fcn)); 211 } 212 213 /* Not a fatal error if this fails */ 214 get_findroot_cap(osroot); 215 216 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 217 return (BAM_SUCCESS); 218 } 219 220 #define INST_RELEASE "var/sadm/system/admin/INST_RELEASE" 221 222 /* 223 * Return true if root has been bfu'ed. bfu will blow away 224 * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can 225 * assume the system has not been bfu'ed. 226 */ 227 static int 228 is_bfu_system(const char *root) 229 { 230 static int is_bfu = -1; 231 char path[PATH_MAX]; 232 struct stat sb; 233 const char *fcn = "is_bfu_system()"; 234 235 if (is_bfu != -1) { 236 BAM_DPRINTF((D_ALREADY_BFU_TEST, fcn, is_bfu ? "" : "NOT")); 237 return (is_bfu); 238 } 239 240 (void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE); 241 if (stat(path, &sb) != 0) { 242 is_bfu = 1; 243 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 244 } else { 245 is_bfu = 0; 246 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 247 } 248 return (is_bfu); 249 } 250 251 #define MENU_URL(root) (is_bfu_system(root) ? \ 252 "http://illumos.org/msg/SUNOS-8000-CF" : \ 253 "http://illumos.org/msg/SUNOS-8000-AK") 254 255 /* 256 * Simply allocate a new line and copy in cmd + sep + arg 257 */ 258 void 259 update_line(line_t *linep) 260 { 261 size_t size; 262 const char *fcn = "update_line()"; 263 264 BAM_DPRINTF((D_UPDATE_LINE_BEFORE, fcn, linep->line)); 265 free(linep->line); 266 size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1; 267 linep->line = s_calloc(1, size); 268 (void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep, 269 linep->arg); 270 BAM_DPRINTF((D_UPDATE_LINE_AFTER, fcn, linep->line)); 271 } 272 273 static char * 274 skip_wspace(char *ptr) 275 { 276 const char *fcn = "skip_wspace()"; 277 278 INJECT_ERROR1("SKIP_WSPACE", ptr = NULL); 279 if (ptr == NULL) { 280 BAM_DPRINTF((D_SKIP_WSPACE_PTR_NULL, fcn)); 281 return (NULL); 282 } 283 284 BAM_DPRINTF((D_SKIP_WSPACE_ENTRY_PTR, fcn, ptr)); 285 for (; *ptr != '\0'; ptr++) { 286 if ((*ptr != ' ') && (*ptr != '\t') && 287 (*ptr != '\n')) 288 break; 289 } 290 291 ptr = (*ptr == '\0' ? NULL : ptr); 292 293 BAM_DPRINTF((D_SKIP_WSPACE_EXIT_PTR, fcn, ptr ? ptr : "NULL")); 294 295 return (ptr); 296 } 297 298 static char * 299 rskip_bspace(char *bound, char *ptr) 300 { 301 const char *fcn = "rskip_bspace()"; 302 assert(bound); 303 assert(ptr); 304 assert(bound <= ptr); 305 assert(*bound != ' ' && *bound != '\t' && *bound != '\n'); 306 307 BAM_DPRINTF((D_RSKIP_BSPACE_ENTRY, fcn, ptr)); 308 for (; ptr > bound; ptr--) { 309 if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') 310 break; 311 } 312 313 BAM_DPRINTF((D_RSKIP_BSPACE_EXIT, fcn, ptr)); 314 return (ptr); 315 } 316 317 /* 318 * The parse_kernel_line function examines a menu.lst kernel line. For 319 * multiboot, this is: 320 * 321 * kernel <multiboot path> <flags1> <kernel path> <flags2> 322 * 323 * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot 324 * 325 * <kernel path> may be missing, or may be any full or relative path to unix. 326 * We check for it by looking for a word ending in "/unix". If it ends 327 * in "kernel/unix", we upgrade it to a 32-bit entry. If it ends in 328 * "kernel/amd64/unix", we upgrade it to the default entry. Otherwise, 329 * it's a custom kernel, and we skip it. 330 * 331 * <flags*> are anything that doesn't fit either of the above - these will be 332 * copied over. 333 * 334 * For direct boot, the defaults are 335 * 336 * kernel$ <kernel path> <flags> 337 * 338 * <kernel path> is one of: 339 * /platform/i86pc/kernel/$ISADIR/unix 340 * /boot/platform/i86pc/kernel/$ISADIR/unix 341 * /platform/i86pc/kernel/unix 342 * /platform/i86pc/kernel/amd64/unix 343 * /boot/platform/i86pc/kernel/unix 344 * /boot/platform/i86pc/kernel/amd64/unix 345 * 346 * If <kernel path> is any of the last four, the command may also be "kernel". 347 * 348 * <flags> is anything that isn't <kernel path>. 349 * 350 * This function is only called to convert a multiboot entry to a dboot entry 351 * 352 * For safety, we do one more check: if the kernel path starts with /boot, 353 * we verify that the new kernel exists before changing it. This is mainly 354 * done for bfu, as it may cause the failsafe archives to be a different 355 * boot architecture from the newly bfu'ed system. 356 */ 357 static error_t 358 cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry) 359 { 360 char path[PATH_MAX], path_64[PATH_MAX]; 361 char linebuf[PATH_MAX]; 362 char new_arg[PATH_MAX]; 363 struct stat sb, sb_64; 364 char *old_ptr; 365 char *unix_ptr; 366 char *flags1_ptr; 367 char *flags2_ptr; 368 const char *fcn = "cvt_kernel_line()"; 369 370 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, line->line, osroot)); 371 372 /* 373 * We only convert multiboot to dboot and nothing else. 374 */ 375 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) { 376 BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn)); 377 return (BAM_SUCCESS); 378 } 379 380 if (entry->flags & BAM_ENTRY_FAILSAFE) { 381 /* 382 * We're attempting to change failsafe to dboot. 383 * In the bfu case, we may not have a dboot failsafe 384 * kernel i.e. a "unix" under the "/boot" hierarchy. 385 * If so, just emit a message in verbose mode and 386 * return success. 387 */ 388 BAM_DPRINTF((D_TRYING_FAILSAFE_CVT_TO_DBOOT, fcn)); 389 (void) snprintf(path, PATH_MAX, "%s%s", osroot, 390 DIRECT_BOOT_FAILSAFE_32); 391 (void) snprintf(path_64, PATH_MAX, "%s%s", osroot, 392 DIRECT_BOOT_FAILSAFE_64); 393 if (stat(path, &sb) != 0 && stat(path_64, &sb_64) != 0) { 394 if (bam_verbose) { 395 bam_error(FAILSAFE_MISSING, line->lineNum); 396 } 397 BAM_DPRINTF((D_NO_FAILSAFE_UNIX_CONVERT, fcn)); 398 return (BAM_SUCCESS); 399 } 400 } 401 402 /* 403 * Make sure we have the correct cmd 404 */ 405 406 free(line->cmd); 407 line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); 408 BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, line->cmd)); 409 410 assert(sizeof (linebuf) > strlen(line->arg) + 32); 411 (void) strlcpy(linebuf, line->arg, sizeof (linebuf)); 412 413 old_ptr = strpbrk(linebuf, " \t\n"); 414 old_ptr = skip_wspace(old_ptr); 415 if (old_ptr == NULL) { 416 /* 417 * only multiboot and nothing else 418 * i.e. flags1 = unix = flags2 = NULL 419 */ 420 flags1_ptr = unix_ptr = flags2_ptr = NULL; 421 BAM_DPRINTF((D_FLAGS1_UNIX_FLAGS2_NULL, fcn)) 422 goto create; 423 } 424 425 /* 426 * 427 * old_ptr is either at "flags1" or "unix" 428 */ 429 if (unix_ptr = strstr(old_ptr, "/unix")) { 430 431 /* 432 * There is a unix. 433 */ 434 BAM_DPRINTF((D_UNIX_PRESENT, fcn)); 435 436 /* See if there's a flags2 past unix */ 437 flags2_ptr = unix_ptr + strlen("/unix"); 438 flags2_ptr = skip_wspace(flags2_ptr); 439 if (flags2_ptr) { 440 BAM_DPRINTF((D_FLAGS2_PRESENT, fcn, flags2_ptr)); 441 } else { 442 BAM_DPRINTF((D_FLAGS2_ABSENT, fcn)); 443 } 444 445 /* see if there is a flags1 before unix */ 446 unix_ptr = rskip_bspace(old_ptr, unix_ptr); 447 448 if (unix_ptr == old_ptr) { 449 flags1_ptr = NULL; 450 BAM_DPRINTF((D_FLAGS1_ABSENT, fcn)); 451 } else { 452 flags1_ptr = old_ptr; 453 *unix_ptr = '\0'; 454 unix_ptr++; 455 BAM_DPRINTF((D_FLAGS1_PRESENT, fcn, flags1_ptr)); 456 } 457 458 } else { 459 /* There is no unix, there is only a bunch of flags */ 460 flags1_ptr = old_ptr; 461 unix_ptr = flags2_ptr = NULL; 462 BAM_DPRINTF((D_FLAGS1_ONLY, fcn, flags1_ptr)); 463 } 464 465 /* 466 * With dboot, unix is fixed and is at the beginning. We need to 467 * migrate flags1 and flags2 468 */ 469 create: 470 if (entry->flags & BAM_ENTRY_FAILSAFE) { 471 (void) snprintf(new_arg, sizeof (new_arg), "%s", 472 DIRECT_BOOT_FAILSAFE_KERNEL); 473 } else { 474 (void) snprintf(new_arg, sizeof (new_arg), "%s", 475 DIRECT_BOOT_KERNEL); 476 } 477 BAM_DPRINTF((D_CVTED_UNIX, fcn, new_arg)); 478 479 if (flags1_ptr != NULL) { 480 (void) strlcat(new_arg, " ", sizeof (new_arg)); 481 (void) strlcat(new_arg, flags1_ptr, sizeof (new_arg)); 482 } 483 484 if (flags2_ptr != NULL) { 485 (void) strlcat(new_arg, " ", sizeof (new_arg)); 486 (void) strlcat(new_arg, flags2_ptr, sizeof (new_arg)); 487 } 488 489 BAM_DPRINTF((D_CVTED_UNIX_AND_FLAGS, fcn, new_arg)); 490 491 free(line->arg); 492 line->arg = s_strdup(new_arg); 493 update_line(line); 494 BAM_DPRINTF((D_CVTED_KERNEL_LINE, fcn, line->line)); 495 return (BAM_SUCCESS); 496 } 497 498 /* 499 * Similar to above, except this time we're looking at a module line, 500 * which is quite a bit simpler. 501 * 502 * Under multiboot, the archive line is: 503 * 504 * module /platform/i86pc/boot_archive 505 * 506 * Under directboot, the archive line is: 507 * 508 * module$ /platform/i86pc/$ISADIR/boot_archive 509 * 510 * which may be specified exactly as either of: 511 * 512 * module /platform/i86pc/boot_archive 513 * module /platform/i86pc/amd64/boot_archive 514 * 515 * Under multiboot, the failsafe is: 516 * 517 * module /boot/x86.miniroot-safe 518 * 519 * Under dboot, the failsafe is: 520 * 521 * module$ /boot/$ISADIR/x86.miniroot-safe 522 * 523 * which may be specified exactly as either of: 524 * 525 * module /boot/x86.miniroot-safe 526 * module /boot/amd64/x86.miniroot-safe 527 */ 528 static error_t 529 cvt_module_line(line_t *line, entry_t *entry) 530 { 531 const char *fcn = "cvt_module_line()"; 532 533 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, line->line)); 534 535 /* 536 * We only convert multiboot to dboot and nothing else 537 */ 538 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) { 539 BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn)); 540 return (BAM_SUCCESS); 541 } 542 543 if (entry->flags & BAM_ENTRY_FAILSAFE) { 544 if (strcmp(line->arg, FAILSAFE_ARCHIVE) == 0) { 545 BAM_DPRINTF((D_FAILSAFE_NO_CVT_NEEDED, fcn, line->arg)); 546 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 547 return (BAM_SUCCESS); 548 } 549 } else if (strcmp(line->arg, MULTIBOOT_ARCHIVE) != 0) { 550 bam_error(UNKNOWN_MODULE_LINE, line->lineNum); 551 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 552 return (BAM_MSG); 553 } 554 555 free(line->cmd); 556 free(line->arg); 557 line->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 558 559 line->arg = s_strdup(entry->flags & BAM_ENTRY_FAILSAFE ? 560 FAILSAFE_ARCHIVE : DIRECT_BOOT_ARCHIVE); 561 562 update_line(line); 563 BAM_DPRINTF((D_CVTED_MODULE, fcn, line->line)); 564 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 565 return (BAM_SUCCESS); 566 } 567 568 static void 569 bam_warn_hand_entries(menu_t *mp, char *osroot) 570 { 571 int hand_num; 572 int hand_max; 573 int *hand_list; 574 int i; 575 entry_t *entry; 576 const char *fcn = "bam_warn_hand_entries()"; 577 578 if (bam_force) { 579 /* 580 * No warning needed, we are automatically converting 581 * the "hand" entries 582 */ 583 BAM_DPRINTF((D_FORCE_HAND_CVT, fcn)); 584 return; 585 } 586 587 hand_num = 0; 588 hand_max = BAM_ENTRY_NUM; 589 hand_list = s_calloc(1, hand_max); 590 591 for (entry = mp->entries; entry; entry = entry->next) { 592 if (entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) 593 continue; 594 BAM_DPRINTF((D_FOUND_HAND, fcn, entry->entryNum)); 595 if (++hand_num > hand_max) { 596 hand_max *= 2; 597 hand_list = s_realloc(hand_list, 598 hand_max * sizeof (int)); 599 } 600 hand_list[hand_num - 1] = entry->entryNum; 601 } 602 603 bam_error(HAND_ADDED_ENTRIES, osroot, MENU_URL(osroot)); 604 bam_print_stderr("Entry Number%s: ", (hand_num > 1) ? 605 "s" : ""); 606 for (i = 0; i < hand_num; i++) { 607 bam_print_stderr("%d ", hand_list[i]); 608 } 609 bam_print_stderr("\n"); 610 } 611 612 static entry_t * 613 find_matching_entry( 614 entry_t *estart, 615 char *grubsign, 616 char *grubroot, 617 int root_opt) 618 { 619 entry_t *entry; 620 line_t *line; 621 char opt[10]; 622 const char *fcn = "find_matching_entry()"; 623 624 assert(grubsign); 625 assert(root_opt == 0 || root_opt == 1); 626 627 (void) snprintf(opt, sizeof (opt), "%d", root_opt); 628 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, grubsign, grubroot, opt)); 629 630 for (entry = estart; entry; entry = entry->next) { 631 632 if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) && 633 !bam_force) { 634 BAM_DPRINTF((D_SKIP_ENTRY, fcn, entry->entryNum)); 635 continue; 636 } 637 638 if (entry->flags & BAM_ENTRY_ROOT) { 639 for (line = entry->start; line; line = line->next) { 640 if (line->cmd == NULL || line->arg == NULL) { 641 if (line == entry->end) { 642 BAM_DPRINTF((D_ENTRY_END, fcn)); 643 break; 644 } else { 645 BAM_DPRINTF((D_SKIP_NULL, fcn)); 646 continue; 647 } 648 } 649 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) 650 == 0 && strcmp(line->arg, grubroot) == 0) { 651 BAM_DPRINTF((D_ROOT_MATCH, fcn, 652 line->line, grubsign)); 653 return (entry); 654 } 655 if (line == entry->end) { 656 BAM_DPRINTF((D_ENTRY_END, fcn)); 657 break; 658 } 659 } 660 } else if (entry->flags & BAM_ENTRY_FINDROOT) { 661 for (line = entry->start; line; line = line->next) { 662 if (line->cmd == NULL || line->arg == NULL) { 663 if (line == entry->end) { 664 BAM_DPRINTF((D_ENTRY_END, fcn)); 665 break; 666 } else { 667 BAM_DPRINTF((D_SKIP_NULL, fcn)); 668 continue; 669 } 670 } 671 if (strcmp(line->cmd, menu_cmds[FINDROOT_CMD]) 672 == 0 && strcmp(line->arg, grubsign) == 0) { 673 BAM_DPRINTF((D_FINDROOT_MATCH, fcn, 674 line->line, grubsign)); 675 return (entry); 676 } 677 if (line == entry->end) { 678 BAM_DPRINTF((D_ENTRY_END, fcn)); 679 break; 680 } 681 } 682 } else if (root_opt) { 683 /* Neither root nor findroot */ 684 BAM_DPRINTF((D_NO_ROOT_FINDROOT, fcn, entry->entryNum)); 685 return (entry); 686 } 687 } 688 689 BAM_DPRINTF((D_NO_MATCH, fcn)); 690 return (NULL); 691 } 692 693 /* 694 * The following is a set of routines that attempt to convert the 695 * menu entries for the supplied osroot into a format compatible 696 * with the GRUB installation on osroot. 697 * 698 * Each of these conversion routines make no assumptions about 699 * the current state of the menu entry, it does its best to 700 * convert the menu entry to the new state. In the process 701 * we may either upgrade or downgrade. 702 * 703 * We don't make any heroic efforts at conversion. It is better 704 * to be conservative and bail out at the first sign of error. We will 705 * in such cases, point the user at the knowledge-base article 706 * so that they can upgrade manually. 707 */ 708 static error_t 709 bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 710 { 711 entry_t *entry; 712 line_t *line; 713 line_t *newlp; 714 int update_num; 715 char linebuf[PATH_MAX]; 716 const char *fcn = "bam_add_findroot()"; 717 718 update_num = 0; 719 720 bam_print(CVT_FINDROOT); 721 722 entry = mp->entries; 723 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 724 entry = entry->next) { 725 if (entry->flags & BAM_ENTRY_FINDROOT) { 726 /* already converted */ 727 BAM_DPRINTF((D_ALREADY_FINDROOT, fcn, entry->entryNum)); 728 continue; 729 } 730 for (line = entry->start; line; line = line->next) { 731 if (line->cmd == NULL || line->arg == NULL) { 732 if (line == entry->end) { 733 BAM_DPRINTF((D_ENTRY_END, fcn)); 734 break; 735 } else { 736 BAM_DPRINTF((D_SKIP_NULL, fcn)); 737 continue; 738 } 739 } 740 if (strcmp(line->cmd, menu_cmds[TITLE_CMD]) == 0) { 741 newlp = s_calloc(1, sizeof (line_t)); 742 newlp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]); 743 newlp->sep = s_strdup(" "); 744 newlp->arg = s_strdup(grubsign); 745 (void) snprintf(linebuf, sizeof (linebuf), 746 "%s%s%s", newlp->cmd, newlp->sep, 747 newlp->arg); 748 newlp->line = s_strdup(linebuf); 749 bam_add_line(mp, entry, line, newlp); 750 update_num = 1; 751 entry->flags &= ~BAM_ENTRY_ROOT; 752 entry->flags |= BAM_ENTRY_FINDROOT; 753 BAM_DPRINTF((D_ADDED_FINDROOT, fcn, 754 newlp->line)); 755 line = newlp; 756 } 757 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) == 0) { 758 BAM_DPRINTF((D_FREEING_ROOT, fcn, line->line)); 759 unlink_line(mp, line); 760 line_free(line); 761 } 762 if (line == entry->end) { 763 BAM_DPRINTF((D_ENTRY_END, fcn)); 764 break; 765 } 766 } 767 } 768 769 if (update_num) { 770 BAM_DPRINTF((D_UPDATED_NUMBERING, fcn)); 771 update_numbering(mp); 772 } 773 774 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 775 return (BAM_SUCCESS); 776 } 777 778 static error_t 779 bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt) 780 { 781 entry_t *entry; 782 const char *fcn = "bam_add_hv()"; 783 784 bam_print(CVT_HV); 785 786 entry = mp->entries; 787 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 788 entry = entry->next) { 789 if (entry->flags & BAM_ENTRY_HV) { 790 BAM_DPRINTF((D_ALREADY_HV, fcn, entry->entryNum)); 791 return (BAM_SUCCESS); 792 } 793 } 794 795 (void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU, 796 XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE, NULL); 797 798 BAM_DPRINTF((D_ADDED_XVM_ENTRY, fcn)); 799 800 update_numbering(mp); 801 802 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 803 804 return (BAM_SUCCESS); 805 } 806 807 static error_t 808 bam_add_dboot( 809 menu_t *mp, 810 char *osroot, 811 char *grubsign, 812 char *grubroot, 813 int root_opt) 814 { 815 int msg = 0; 816 entry_t *entry; 817 line_t *line; 818 error_t ret; 819 const char *fcn = "bam_add_dboot()"; 820 821 bam_print(CVT_DBOOT); 822 823 entry = mp->entries; 824 for (; entry = find_matching_entry(entry, grubsign, grubroot, root_opt); 825 entry = entry->next) { 826 for (line = entry->start; line; line = line->next) { 827 if (line->cmd == NULL || line->arg == NULL) { 828 if (line == entry->end) { 829 BAM_DPRINTF((D_ENTRY_END, fcn)); 830 break; 831 } else { 832 BAM_DPRINTF((D_SKIP_NULL, fcn)); 833 continue; 834 } 835 } 836 837 /* 838 * If we have a kernel$ command, assume it 839 * is dboot already. If it is not a dboot 840 * entry, something funny is going on and 841 * we will leave it alone 842 */ 843 if (strcmp(line->cmd, menu_cmds[KERNEL_CMD]) == 0) { 844 ret = cvt_kernel_line(line, osroot, entry); 845 INJECT_ERROR1("ADD_DBOOT_KERN_ERR", 846 ret = BAM_ERROR); 847 INJECT_ERROR1("ADD_DBOOT_KERN_MSG", 848 ret = BAM_MSG); 849 if (ret == BAM_ERROR) { 850 BAM_DPRINTF((D_CVT_KERNEL_FAIL, fcn)); 851 return (ret); 852 } else if (ret == BAM_MSG) { 853 msg = 1; 854 BAM_DPRINTF((D_CVT_KERNEL_MSG, fcn)); 855 } 856 } 857 if (strcmp(line->cmd, menu_cmds[MODULE_CMD]) == 0) { 858 ret = cvt_module_line(line, entry); 859 INJECT_ERROR1("ADD_DBOOT_MOD_ERR", 860 ret = BAM_ERROR); 861 INJECT_ERROR1("ADD_DBOOT_MOD_MSG", 862 ret = BAM_MSG); 863 if (ret == BAM_ERROR) { 864 BAM_DPRINTF((D_CVT_MODULE_FAIL, fcn)); 865 return (ret); 866 } else if (ret == BAM_MSG) { 867 BAM_DPRINTF((D_CVT_MODULE_MSG, fcn)); 868 msg = 1; 869 } 870 } 871 872 if (line == entry->end) { 873 BAM_DPRINTF((D_ENTRY_END, fcn)); 874 break; 875 } 876 } 877 } 878 879 ret = msg ? BAM_MSG : BAM_SUCCESS; 880 BAM_DPRINTF((D_RETURN_RET, fcn, ret)); 881 return (ret); 882 } 883 884 /*ARGSUSED*/ 885 error_t 886 upgrade_menu(menu_t *mp, char *osroot, char *menu_root) 887 { 888 char *osdev; 889 char *grubsign; 890 char *grubroot; 891 int ret1; 892 int ret2; 893 int ret3; 894 const char *fcn = "upgrade_menu()"; 895 896 assert(osroot); 897 assert(menu_root); 898 899 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 900 901 /* 902 * We only support upgrades. Xen may not be present 903 * on smaller metaclusters so we don't check for that. 904 */ 905 if (bam_is_findroot != BAM_FINDROOT_PRESENT || 906 bam_direct != BAM_DIRECT_DBOOT) { 907 bam_error(DOWNGRADE_NOTSUP); 908 return (BAM_ERROR); 909 } 910 911 /* 912 * First get the GRUB signature 913 */ 914 osdev = get_special(osroot); 915 INJECT_ERROR1("UPGRADE_OSDEV", osdev = NULL); 916 if (osdev == NULL) { 917 bam_error(CANT_FIND_SPECIAL, osroot); 918 return (BAM_ERROR); 919 } 920 921 grubsign = get_grubsign(osroot, osdev); 922 INJECT_ERROR1("UPGRADE_GRUBSIGN", grubsign = NULL); 923 if (grubsign == NULL) { 924 free(osdev); 925 bam_error(CANT_FIND_GRUBSIGN, osroot); 926 return (BAM_ERROR); 927 } 928 929 /* not fatal if we can't get grubroot */ 930 grubroot = get_grubroot(osroot, osdev, menu_root); 931 INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL); 932 933 free(osdev); 934 935 ret1 = bam_add_findroot(mp, grubsign, 936 grubroot, root_optional(osroot, menu_root)); 937 INJECT_ERROR1("UPGRADE_ADD_FINDROOT", ret1 = BAM_ERROR); 938 if (ret1 == BAM_ERROR) 939 goto abort; 940 941 if (bam_is_hv == BAM_HV_PRESENT) { 942 ret2 = bam_add_hv(mp, grubsign, grubroot, 943 root_optional(osroot, menu_root)); 944 INJECT_ERROR1("UPGRADE_ADD_HV", ret2 = BAM_ERROR); 945 if (ret2 == BAM_ERROR) 946 goto abort; 947 } else 948 ret2 = BAM_SUCCESS; 949 950 ret3 = bam_add_dboot(mp, osroot, grubsign, 951 grubroot, root_optional(osroot, menu_root)); 952 INJECT_ERROR1("UPGRADE_ADD_DBOOT", ret3 = BAM_ERROR); 953 if (ret3 == BAM_ERROR) 954 goto abort; 955 956 if (ret1 == BAM_MSG || ret2 == BAM_MSG || ret3 == BAM_MSG) { 957 bam_error(CVT_TODO, MENU_URL(osroot)); 958 } else { 959 bam_warn_hand_entries(mp, osroot); 960 } 961 962 free(grubsign); 963 964 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_WRITE)); 965 return (BAM_WRITE); 966 967 abort: 968 free(grubsign); 969 bam_error(CVT_ABORT, osroot, MENU_URL(osroot)); 970 return (BAM_ERROR); 971 } 972