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) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2012 Milan Jurik. All rights reserved. 24 */ 25 26 /* 27 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 28 */ 29 30 /* 31 * bootadm(1M) is a new utility for managing bootability of 32 * Solaris *Newboot* environments. It has two primary tasks: 33 * - Allow end users to manage bootability of Newboot Solaris instances 34 * - Provide services to other subsystems in Solaris (primarily Install) 35 */ 36 37 /* Headers */ 38 #include <stdio.h> 39 #include <errno.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <alloca.h> 46 #include <stdarg.h> 47 #include <limits.h> 48 #include <signal.h> 49 #include <sys/wait.h> 50 #include <sys/mnttab.h> 51 #include <sys/mntent.h> 52 #include <sys/statvfs.h> 53 #include <libnvpair.h> 54 #include <ftw.h> 55 #include <fcntl.h> 56 #include <strings.h> 57 #include <utime.h> 58 #include <sys/systeminfo.h> 59 #include <sys/dktp/fdisk.h> 60 #include <sys/param.h> 61 #include <dirent.h> 62 #include <ctype.h> 63 #include <libgen.h> 64 #include <sys/sysmacros.h> 65 #include <sys/elf.h> 66 #include <libscf.h> 67 #include <zlib.h> 68 #include <sys/lockfs.h> 69 #include <sys/filio.h> 70 #include <libbe.h> 71 #ifdef i386 72 #include <libfdisk.h> 73 #endif 74 75 #if !defined(_OPB) 76 #include <sys/ucode.h> 77 #endif 78 79 #include <pwd.h> 80 #include <grp.h> 81 #include <device_info.h> 82 #include <sys/vtoc.h> 83 #include <sys/efi_partition.h> 84 #include <regex.h> 85 #include <locale.h> 86 87 #include "message.h" 88 #include "bootadm.h" 89 90 #ifndef TEXT_DOMAIN 91 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 92 #endif /* TEXT_DOMAIN */ 93 94 /* Type definitions */ 95 96 /* Primary subcmds */ 97 typedef enum { 98 BAM_MENU = 3, 99 BAM_ARCHIVE 100 } subcmd_t; 101 102 typedef enum { 103 OPT_ABSENT = 0, /* No option */ 104 OPT_REQ, /* option required */ 105 OPT_OPTIONAL /* option may or may not be present */ 106 } option_t; 107 108 typedef struct { 109 char *subcmd; 110 option_t option; 111 error_t (*handler)(); 112 int unpriv; /* is this an unprivileged command */ 113 } subcmd_defn_t; 114 115 #define LINE_INIT 0 /* lineNum initial value */ 116 #define ENTRY_INIT -1 /* entryNum initial value */ 117 #define ALL_ENTRIES -2 /* selects all boot entries */ 118 119 #define GRUB_DIR "/boot/grub" 120 #define GRUB_STAGE2 GRUB_DIR "/stage2" 121 #define GRUB_MENU "/boot/grub/menu.lst" 122 #define MENU_TMP "/boot/grub/menu.lst.tmp" 123 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu" 124 #define RAMDISK_SPECIAL "/dev/ramdisk/" 125 #define STUBBOOT "/stubboot" 126 #define MULTIBOOT "/platform/i86pc/multiboot" 127 #define GRUBSIGN_DIR "/boot/grub/bootsign" 128 #define GRUBSIGN_BACKUP "/etc/bootsign" 129 #define GRUBSIGN_UFS_PREFIX "rootfs" 130 #define GRUBSIGN_ZFS_PREFIX "pool_" 131 #define GRUBSIGN_LU_PREFIX "BE_" 132 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures" 133 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy" 134 135 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST" 136 137 /* lock related */ 138 #define BAM_LOCK_FILE "/var/run/bootadm.lock" 139 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 140 141 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk" 142 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap" 143 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist" 144 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map" 145 146 #define GRUB_slice "/etc/lu/GRUB_slice" 147 #define GRUB_root "/etc/lu/GRUB_root" 148 #define GRUB_fdisk "/etc/lu/GRUB_fdisk" 149 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target" 150 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot" 151 #define LULIB "/usr/lib/lu/lulib" 152 #define LULIB_PROPAGATE_FILE "lulib_propagate_file" 153 #define CKSUM "/usr/bin/cksum" 154 #define LU_MENU_CKSUM "/etc/lu/menu.cksum" 155 #define BOOTADM "/sbin/bootadm" 156 157 #define INSTALLGRUB "/sbin/installgrub" 158 #define STAGE1 "/boot/grub/stage1" 159 #define STAGE2 "/boot/grub/stage2" 160 161 typedef enum zfs_mnted { 162 ZFS_MNT_ERROR = -1, 163 LEGACY_MOUNTED = 1, 164 LEGACY_ALREADY, 165 ZFS_MOUNTED, 166 ZFS_ALREADY 167 } zfs_mnted_t; 168 169 /* 170 * Default file attributes 171 */ 172 #define DEFAULT_DEV_MODE 0644 /* default permissions */ 173 #define DEFAULT_DEV_UID 0 /* user root */ 174 #define DEFAULT_DEV_GID 3 /* group sys */ 175 176 /* 177 * Menu related 178 * menu_cmd_t and menu_cmds must be kept in sync 179 */ 180 char *menu_cmds[] = { 181 "default", /* DEFAULT_CMD */ 182 "timeout", /* TIMEOUT_CMD */ 183 "title", /* TITLE_CMD */ 184 "root", /* ROOT_CMD */ 185 "kernel", /* KERNEL_CMD */ 186 "kernel$", /* KERNEL_DOLLAR_CMD */ 187 "module", /* MODULE_CMD */ 188 "module$", /* MODULE_DOLLAR_CMD */ 189 " ", /* SEP_CMD */ 190 "#", /* COMMENT_CMD */ 191 "chainloader", /* CHAINLOADER_CMD */ 192 "args", /* ARGS_CMD */ 193 "findroot", /* FINDROOT_CMD */ 194 "bootfs", /* BOOTFS_CMD */ 195 NULL 196 }; 197 198 #define OPT_ENTRY_NUM "entry" 199 200 /* 201 * exec_cmd related 202 */ 203 typedef struct { 204 line_t *head; 205 line_t *tail; 206 } filelist_t; 207 208 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk" 209 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk" 210 211 #define FILE_STAT "boot/solaris/filestat.ramdisk" 212 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp" 213 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 214 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 215 216 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache" 217 218 /* Globals */ 219 int bam_verbose; 220 int bam_force; 221 int bam_debug; 222 static char *prog; 223 static subcmd_t bam_cmd; 224 static char *bam_root; 225 static int bam_rootlen; 226 static int bam_root_readonly; 227 static int bam_alt_root; 228 static int bam_extend = 0; 229 static int bam_purge = 0; 230 static char *bam_subcmd; 231 static char *bam_opt; 232 static char **bam_argv; 233 static int bam_argc; 234 static int bam_check; 235 static int bam_saved_check; 236 static int bam_smf_check; 237 static int bam_lock_fd = -1; 238 static int bam_zfs; 239 static char rootbuf[PATH_MAX] = "/"; 240 static int bam_update_all; 241 static int bam_alt_platform; 242 static char *bam_platform; 243 static char *bam_home_env = NULL; 244 245 /* function prototypes */ 246 static void parse_args_internal(int, char *[]); 247 static void parse_args(int, char *argv[]); 248 static error_t bam_menu(char *, char *, int, char *[]); 249 static error_t bam_archive(char *, char *); 250 251 static void bam_lock(void); 252 static void bam_unlock(void); 253 254 static int exec_cmd(char *, filelist_t *); 255 static error_t read_globals(menu_t *, char *, char *, int); 256 static int menu_on_bootdisk(char *os_root, char *menu_root); 257 static menu_t *menu_read(char *); 258 static error_t menu_write(char *, menu_t *); 259 static void linelist_free(line_t *); 260 static void menu_free(menu_t *); 261 static void filelist_free(filelist_t *); 262 static error_t list2file(char *, char *, char *, line_t *); 263 static error_t list_entry(menu_t *, char *, char *); 264 static error_t list_setting(menu_t *, char *, char *); 265 static error_t delete_all_entries(menu_t *, char *, char *); 266 static error_t update_entry(menu_t *mp, char *menu_root, char *opt); 267 static error_t update_temp(menu_t *mp, char *dummy, char *opt); 268 269 static error_t update_archive(char *, char *); 270 static error_t list_archive(char *, char *); 271 static error_t update_all(char *, char *); 272 static error_t read_list(char *, filelist_t *); 273 static error_t set_option(menu_t *, char *, char *); 274 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t); 275 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t); 276 static char *expand_path(const char *); 277 278 static long s_strtol(char *); 279 static int s_fputs(char *, FILE *); 280 281 static int is_zfs(char *root); 282 static int is_ufs(char *root); 283 static int is_pcfs(char *root); 284 static int is_amd64(void); 285 static char *get_machine(void); 286 static void append_to_flist(filelist_t *, char *); 287 static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted); 288 static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt); 289 static int ufs_add_to_sign_list(char *sign); 290 static error_t synchronize_BE_menu(void); 291 292 #if !defined(_OPB) 293 static void ucode_install(); 294 #endif 295 296 /* Menu related sub commands */ 297 static subcmd_defn_t menu_subcmds[] = { 298 "set_option", OPT_ABSENT, set_option, 0, /* PUB */ 299 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */ 300 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */ 301 "update_entry", OPT_REQ, update_entry, 0, /* menu */ 302 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */ 303 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */ 304 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */ 305 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */ 306 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */ 307 NULL, 0, NULL, 0 /* must be last */ 308 }; 309 310 /* Archive related sub commands */ 311 static subcmd_defn_t arch_subcmds[] = { 312 "update", OPT_ABSENT, update_archive, 0, /* PUB */ 313 "update_all", OPT_ABSENT, update_all, 0, /* PVT */ 314 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */ 315 NULL, 0, NULL, 0 /* must be last */ 316 }; 317 318 enum dircache_copy_opt { 319 FILE32 = 0, 320 FILE64, 321 CACHEDIR_NUM 322 }; 323 324 /* 325 * Directory specific flags: 326 * NEED_UPDATE : the specified archive needs to be updated 327 * NO_MULTI : don't extend the specified archive, but recreate it 328 */ 329 #define NEED_UPDATE 0x00000001 330 #define NO_MULTI 0x00000002 331 332 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f) 333 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f) 334 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0) 335 336 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path) 337 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path) 338 #define get_count(id) (walk_arg.dirinfo[id].count) 339 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir) 340 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1) 341 342 /* 343 * dirinfo_t (specific cache directory information): 344 * cdir_path: path to the archive cache directory 345 * update_path: path to the update directory (contains the files that will be 346 * used to extend the archive) 347 * has_dir: the specified cache directory is active 348 * count: the number of files to update 349 * flags: directory specific flags 350 */ 351 typedef struct _dirinfo { 352 char cdir_path[PATH_MAX]; 353 char update_path[PATH_MAX]; 354 int has_dir; 355 int count; 356 int flags; 357 } dirinfo_t; 358 359 /* 360 * Update flags: 361 * NEED_CACHE_DIR : cache directory is missing and needs to be created 362 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment 363 * UPDATE_ERROR : an error occourred while traversing the list of files 364 * RDONLY_FSCHK : the target filesystem is read-only 365 * RAMDSK_FSCHK : the target filesystem is on a ramdisk 366 */ 367 #define NEED_CACHE_DIR 0x00000001 368 #define IS_SPARC_TARGET 0x00000002 369 #define UPDATE_ERROR 0x00000004 370 #define RDONLY_FSCHK 0x00000008 371 #define INVALIDATE_CACHE 0x00000010 372 373 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0) 374 #define set_flag(flag) (walk_arg.update_flags |= flag) 375 #define unset_flag(flag) (walk_arg.update_flags &= ~flag) 376 377 /* 378 * struct walk_arg : 379 * update_flags: flags related to the current updating process 380 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs 381 * sparcfile: list of file paths for mkisofs -path-list (SPARC only) 382 */ 383 static struct { 384 int update_flags; 385 nvlist_t *new_nvlp; 386 nvlist_t *old_nvlp; 387 FILE *sparcfile; 388 dirinfo_t dirinfo[CACHEDIR_NUM]; 389 } walk_arg; 390 391 struct safefile { 392 char *name; 393 struct safefile *next; 394 }; 395 396 static struct safefile *safefiles = NULL; 397 398 /* 399 * svc:/system/filesystem/usr:default service checks for this file and 400 * does a boot archive update and then reboot the system. 401 */ 402 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update" 403 404 /* 405 * svc:/system/boot-archive-update:default checks for this file and 406 * updates the boot archive. 407 */ 408 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update" 409 410 /* Thanks growisofs */ 411 #define CD_BLOCK ((off64_t)2048) 412 #define VOLDESC_OFF 16 413 #define DVD_BLOCK (32*1024) 414 #define MAX_IVDs 16 415 416 struct iso_pdesc { 417 unsigned char type [1]; 418 unsigned char id [5]; 419 unsigned char void1 [80-5-1]; 420 unsigned char volume_space_size [8]; 421 unsigned char void2 [2048-80-8]; 422 }; 423 424 /* 425 * COUNT_MAX: maximum number of changed files to justify a multisession update 426 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession 427 * update 428 */ 429 #define COUNT_MAX 50 430 #define BA_SIZE_MAX (50 * 1024 * 1024) 431 432 #define bam_nowrite() (bam_check || bam_smf_check) 433 434 static int sync_menu = 1; /* whether we need to sync the BE menus */ 435 436 static void 437 usage(void) 438 { 439 (void) fprintf(stderr, "USAGE:\n"); 440 441 /* archive usage */ 442 (void) fprintf(stderr, 443 "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog); 444 (void) fprintf(stderr, 445 "\t%s list-archive [-R altroot [-p platform>]]\n", prog); 446 #if !defined(_OPB) 447 /* x86 only */ 448 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog); 449 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog); 450 #endif 451 } 452 453 /* 454 * Best effort attempt to restore the $HOME value. 455 */ 456 static void 457 restore_env() 458 { 459 char home_env[PATH_MAX]; 460 461 if (bam_home_env) { 462 (void) snprintf(home_env, sizeof (home_env), "HOME=%s", 463 bam_home_env); 464 (void) putenv(home_env); 465 } 466 } 467 468 469 #define SLEEP_TIME 5 470 #define MAX_TRIES 4 471 472 /* 473 * Sanitize the environment in which bootadm will execute its sub-processes 474 * (ex. mkisofs). This is done to prevent those processes from attempting 475 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS 476 * or, potentially, insecure. 477 */ 478 static void 479 sanitize_env() 480 { 481 int stry = 0; 482 483 /* don't depend on caller umask */ 484 (void) umask(0022); 485 486 /* move away from a potential unsafe current working directory */ 487 while (chdir("/") == -1) { 488 if (errno != EINTR) { 489 bam_print("WARNING: unable to chdir to /"); 490 break; 491 } 492 } 493 494 bam_home_env = getenv("HOME"); 495 while (bam_home_env != NULL && putenv("HOME=/") == -1) { 496 if (errno == ENOMEM) { 497 /* retry no more than MAX_TRIES times */ 498 if (++stry > MAX_TRIES) { 499 bam_print("WARNING: unable to recover from " 500 "system memory pressure... aborting \n"); 501 bam_exit(EXIT_FAILURE); 502 } 503 /* memory is tight, try to sleep */ 504 bam_print("Attempting to recover from memory pressure: " 505 "sleeping for %d seconds\n", SLEEP_TIME * stry); 506 (void) sleep(SLEEP_TIME * stry); 507 } else { 508 bam_print("WARNING: unable to sanitize HOME\n"); 509 } 510 } 511 } 512 513 int 514 main(int argc, char *argv[]) 515 { 516 error_t ret; 517 518 (void) setlocale(LC_ALL, ""); 519 (void) textdomain(TEXT_DOMAIN); 520 521 if ((prog = strrchr(argv[0], '/')) == NULL) { 522 prog = argv[0]; 523 } else { 524 prog++; 525 } 526 527 INJECT_ERROR1("ASSERT_ON", assert(0)) 528 529 sanitize_env(); 530 531 parse_args(argc, argv); 532 533 switch (bam_cmd) { 534 case BAM_MENU: 535 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv); 536 break; 537 case BAM_ARCHIVE: 538 ret = bam_archive(bam_subcmd, bam_opt); 539 break; 540 default: 541 usage(); 542 bam_exit(1); 543 } 544 545 if (ret != BAM_SUCCESS) 546 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1); 547 548 bam_unlock(); 549 return (0); 550 } 551 552 /* 553 * Equivalence of public and internal commands: 554 * update-archive -- -a update 555 * list-archive -- -a list 556 * set-menu -- -m set_option 557 * list-menu -- -m list_entry 558 * update-menu -- -m update_entry 559 */ 560 static struct cmd_map { 561 char *bam_cmdname; 562 int bam_cmd; 563 char *bam_subcmd; 564 } cmd_map[] = { 565 { "update-archive", BAM_ARCHIVE, "update"}, 566 { "list-archive", BAM_ARCHIVE, "list"}, 567 { "set-menu", BAM_MENU, "set_option"}, 568 { "list-menu", BAM_MENU, "list_entry"}, 569 { "update-menu", BAM_MENU, "update_entry"}, 570 { NULL, 0, NULL} 571 }; 572 573 /* 574 * Commands syntax published in bootadm(1M) are parsed here 575 */ 576 static void 577 parse_args(int argc, char *argv[]) 578 { 579 struct cmd_map *cmp = cmd_map; 580 581 /* command conforming to the final spec */ 582 if (argc > 1 && argv[1][0] != '-') { 583 /* 584 * Map commands to internal table. 585 */ 586 while (cmp->bam_cmdname) { 587 if (strcmp(argv[1], cmp->bam_cmdname) == 0) { 588 bam_cmd = cmp->bam_cmd; 589 bam_subcmd = cmp->bam_subcmd; 590 break; 591 } 592 cmp++; 593 } 594 if (cmp->bam_cmdname == NULL) { 595 usage(); 596 bam_exit(1); 597 } 598 argc--; 599 argv++; 600 } 601 602 parse_args_internal(argc, argv); 603 } 604 605 /* 606 * A combination of public and private commands are parsed here. 607 * The internal syntax and the corresponding functionality are: 608 * -a update -- update-archive 609 * -a list -- list-archive 610 * -a update-all -- (reboot to sync all mnted OS archive) 611 * -m update_entry -- update-menu 612 * -m list_entry -- list-menu 613 * -m update_temp -- (reboot -- [boot-args]) 614 * -m delete_all_entries -- (called from install) 615 * -m enable_hypervisor [args] -- cvt_to_hyper 616 * -m disable_hypervisor -- cvt_to_metal 617 * -m list_setting [entry] [value] -- list_setting 618 * 619 * A set of private flags is there too: 620 * -F -- purge the cache directories and rebuild them 621 * -e -- use the (faster) archive update approach (used by 622 * reboot) 623 */ 624 static void 625 parse_args_internal(int argc, char *argv[]) 626 { 627 int c, error; 628 extern char *optarg; 629 extern int optind, opterr; 630 631 /* Suppress error message from getopt */ 632 opterr = 0; 633 634 error = 0; 635 while ((c = getopt(argc, argv, "a:d:fm:no:veFCR:p:XZ")) != -1) { 636 switch (c) { 637 case 'a': 638 if (bam_cmd) { 639 error = 1; 640 bam_error(MULT_CMDS, c); 641 } 642 bam_cmd = BAM_ARCHIVE; 643 bam_subcmd = optarg; 644 break; 645 case 'd': 646 if (bam_debug) { 647 error = 1; 648 bam_error(DUP_OPT, c); 649 } 650 bam_debug = s_strtol(optarg); 651 break; 652 case 'f': 653 bam_force = 1; 654 break; 655 case 'F': 656 bam_purge = 1; 657 break; 658 case 'm': 659 if (bam_cmd) { 660 error = 1; 661 bam_error(MULT_CMDS, c); 662 } 663 bam_cmd = BAM_MENU; 664 bam_subcmd = optarg; 665 break; 666 case 'n': 667 bam_check = 1; 668 /* 669 * We save the original value of bam_check. The new 670 * approach in case of a read-only filesystem is to 671 * behave as a check, so we need a way to restore the 672 * original value after the evaluation of the read-only 673 * filesystem has been done. 674 * Even if we don't allow at the moment a check with 675 * update_all, this approach is more robust than 676 * simply resetting bam_check to zero. 677 */ 678 bam_saved_check = 1; 679 break; 680 case 'o': 681 if (bam_opt) { 682 error = 1; 683 bam_error(DUP_OPT, c); 684 } 685 bam_opt = optarg; 686 break; 687 case 'v': 688 bam_verbose = 1; 689 break; 690 case 'C': 691 bam_smf_check = 1; 692 break; 693 case 'R': 694 if (bam_root) { 695 error = 1; 696 bam_error(DUP_OPT, c); 697 break; 698 } else if (realpath(optarg, rootbuf) == NULL) { 699 error = 1; 700 bam_error(CANT_RESOLVE, optarg, 701 strerror(errno)); 702 break; 703 } 704 bam_alt_root = 1; 705 bam_root = rootbuf; 706 bam_rootlen = strlen(rootbuf); 707 break; 708 case 'p': 709 bam_alt_platform = 1; 710 bam_platform = optarg; 711 if ((strcmp(bam_platform, "i86pc") != 0) && 712 (strcmp(bam_platform, "sun4u") != 0) && 713 (strcmp(bam_platform, "sun4v") != 0)) { 714 error = 1; 715 bam_error(INVALID_PLAT, bam_platform); 716 } 717 break; 718 case 'X': 719 bam_is_hv = BAM_HV_PRESENT; 720 break; 721 case 'Z': 722 bam_zfs = 1; 723 break; 724 case 'e': 725 bam_extend = 1; 726 break; 727 case '?': 728 error = 1; 729 bam_error(BAD_OPT, optopt); 730 break; 731 default : 732 error = 1; 733 bam_error(BAD_OPT, c); 734 break; 735 } 736 } 737 738 /* 739 * An alternate platform requires an alternate root 740 */ 741 if (bam_alt_platform && bam_alt_root == 0) { 742 usage(); 743 bam_exit(0); 744 } 745 746 /* 747 * A command option must be specfied 748 */ 749 if (!bam_cmd) { 750 if (bam_opt && strcmp(bam_opt, "all") == 0) { 751 usage(); 752 bam_exit(0); 753 } 754 bam_error(NEED_CMD); 755 error = 1; 756 } 757 758 if (error) { 759 usage(); 760 bam_exit(1); 761 } 762 763 if (optind > argc) { 764 bam_error(INT_ERROR, "parse_args"); 765 bam_exit(1); 766 } else if (optind < argc) { 767 bam_argv = &argv[optind]; 768 bam_argc = argc - optind; 769 } 770 771 /* 772 * -n implies verbose mode 773 */ 774 if (bam_check) 775 bam_verbose = 1; 776 } 777 778 static error_t 779 check_subcmd_and_options( 780 char *subcmd, 781 char *opt, 782 subcmd_defn_t *table, 783 error_t (**fp)()) 784 { 785 int i; 786 787 if (subcmd == NULL) { 788 bam_error(NEED_SUBCMD); 789 return (BAM_ERROR); 790 } 791 792 if (strcmp(subcmd, "set_option") == 0) { 793 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) { 794 bam_error(MISSING_ARG); 795 usage(); 796 return (BAM_ERROR); 797 } else if (bam_argc > 1 || bam_argv[1] != NULL) { 798 bam_error(TRAILING_ARGS); 799 usage(); 800 return (BAM_ERROR); 801 } 802 } else if (strcmp(subcmd, "update_all") == 0) { 803 /* 804 * The only option we accept for the "update_all" 805 * subcmd is "fastboot". 806 */ 807 if (bam_argc > 1 || (bam_argc == 1 && 808 strcmp(bam_argv[0], "fastboot") != 0)) { 809 bam_error(TRAILING_ARGS); 810 usage(); 811 return (BAM_ERROR); 812 } 813 if (bam_argc == 1) 814 sync_menu = 0; 815 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) && 816 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) { 817 /* 818 * Of the remaining subcommands, only "enable_hypervisor" and 819 * "list_setting" take trailing arguments. 820 */ 821 bam_error(TRAILING_ARGS); 822 usage(); 823 return (BAM_ERROR); 824 } 825 826 if (bam_root == NULL) { 827 bam_root = rootbuf; 828 bam_rootlen = 1; 829 } 830 831 /* verify that subcmd is valid */ 832 for (i = 0; table[i].subcmd != NULL; i++) { 833 if (strcmp(table[i].subcmd, subcmd) == 0) 834 break; 835 } 836 837 if (table[i].subcmd == NULL) { 838 bam_error(INVALID_SUBCMD, subcmd); 839 return (BAM_ERROR); 840 } 841 842 if (table[i].unpriv == 0 && geteuid() != 0) { 843 bam_error(MUST_BE_ROOT); 844 return (BAM_ERROR); 845 } 846 847 /* 848 * Currently only privileged commands need a lock 849 */ 850 if (table[i].unpriv == 0) 851 bam_lock(); 852 853 /* subcmd verifies that opt is appropriate */ 854 if (table[i].option != OPT_OPTIONAL) { 855 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) { 856 if (opt) 857 bam_error(NO_OPT_REQ, subcmd); 858 else 859 bam_error(MISS_OPT, subcmd); 860 return (BAM_ERROR); 861 } 862 } 863 864 *fp = table[i].handler; 865 866 return (BAM_SUCCESS); 867 } 868 869 /* 870 * NOTE: A single "/" is also considered a trailing slash and will 871 * be deleted. 872 */ 873 static void 874 elide_trailing_slash(const char *src, char *dst, size_t dstsize) 875 { 876 size_t dstlen; 877 878 assert(src); 879 assert(dst); 880 881 (void) strlcpy(dst, src, dstsize); 882 883 dstlen = strlen(dst); 884 if (dst[dstlen - 1] == '/') { 885 dst[dstlen - 1] = '\0'; 886 } 887 } 888 889 static int 890 is_safe_exec(char *path) 891 { 892 struct stat sb; 893 894 if (lstat(path, &sb) != 0) { 895 bam_error(STAT_FAIL, path, strerror(errno)); 896 return (BAM_ERROR); 897 } 898 899 if (!S_ISREG(sb.st_mode)) { 900 bam_error(PATH_EXEC_LINK, path); 901 return (BAM_ERROR); 902 } 903 904 if (sb.st_uid != getuid()) { 905 bam_error(PATH_EXEC_OWNER, path, getuid()); 906 return (BAM_ERROR); 907 } 908 909 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) { 910 bam_error(PATH_EXEC_PERMS, path); 911 return (BAM_ERROR); 912 } 913 914 return (BAM_SUCCESS); 915 } 916 917 static error_t 918 list_setting(menu_t *mp, char *which, char *setting) 919 { 920 line_t *lp; 921 entry_t *ent; 922 923 char *p = which; 924 int entry; 925 926 int found; 927 928 assert(which); 929 assert(setting); 930 931 if (*which != NULL) { 932 /* 933 * If "which" is not a number, assume it's a setting we want 934 * to look for and so set up the routine to look for "which" 935 * in the default entry. 936 */ 937 while (*p != NULL) 938 if (!(isdigit((int)*p++))) { 939 setting = which; 940 which = mp->curdefault->arg; 941 break; 942 } 943 } else { 944 which = mp->curdefault->arg; 945 } 946 947 entry = atoi(which); 948 949 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry)); 950 ent = ent->next) 951 ; 952 953 if (!ent) { 954 bam_error(NO_MATCH_ENTRY); 955 return (BAM_ERROR); 956 } 957 958 found = (*setting == NULL); 959 960 for (lp = ent->start; lp != NULL; lp = lp->next) { 961 if ((*setting == NULL) && (lp->flags != BAM_COMMENT)) 962 bam_print(PRINT, lp->line); 963 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) { 964 bam_print(PRINT, lp->arg); 965 found = 1; 966 } 967 968 if (lp == ent->end) 969 break; 970 } 971 972 if (!found) { 973 bam_error(NO_MATCH_ENTRY); 974 return (BAM_ERROR); 975 } 976 977 return (BAM_SUCCESS); 978 } 979 980 static error_t 981 bam_menu(char *subcmd, char *opt, int largc, char *largv[]) 982 { 983 error_t ret; 984 char menu_path[PATH_MAX]; 985 char clean_menu_root[PATH_MAX]; 986 char path[PATH_MAX]; 987 menu_t *menu; 988 char menu_root[PATH_MAX]; 989 struct stat sb; 990 error_t (*f)(menu_t *mp, char *menu_path, char *opt); 991 char *special; 992 char *pool = NULL; 993 zfs_mnted_t zmnted; 994 char *zmntpt; 995 char *osdev; 996 char *osroot; 997 const char *fcn = "bam_menu()"; 998 999 /* 1000 * Menu sub-command only applies to GRUB (i.e. x86) 1001 */ 1002 if (!is_grub(bam_alt_root ? bam_root : "/")) { 1003 bam_error(NOT_GRUB_BOOT); 1004 return (BAM_ERROR); 1005 } 1006 1007 /* 1008 * Check arguments 1009 */ 1010 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f); 1011 if (ret == BAM_ERROR) { 1012 return (BAM_ERROR); 1013 } 1014 1015 assert(bam_root); 1016 1017 (void) strlcpy(menu_root, bam_root, sizeof (menu_root)); 1018 osdev = osroot = NULL; 1019 1020 if (strcmp(subcmd, "update_entry") == 0) { 1021 assert(opt); 1022 1023 osdev = strtok(opt, ","); 1024 assert(osdev); 1025 osroot = strtok(NULL, ","); 1026 if (osroot) { 1027 /* fixup bam_root so that it points at osroot */ 1028 if (realpath(osroot, rootbuf) == NULL) { 1029 bam_error(CANT_RESOLVE, osroot, 1030 strerror(errno)); 1031 return (BAM_ERROR); 1032 } 1033 bam_alt_root = 1; 1034 bam_root = rootbuf; 1035 bam_rootlen = strlen(rootbuf); 1036 } 1037 } 1038 1039 /* 1040 * We support menu on PCFS (under certain conditions), but 1041 * not the OS root 1042 */ 1043 if (is_pcfs(bam_root)) { 1044 bam_error(PCFS_ROOT_NOTSUP, bam_root); 1045 return (BAM_ERROR); 1046 } 1047 1048 if (stat(menu_root, &sb) == -1) { 1049 bam_error(CANNOT_LOCATE_GRUB_MENU); 1050 return (BAM_ERROR); 1051 } 1052 1053 BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root)); 1054 1055 /* 1056 * We no longer use the GRUB slice file. If it exists, then 1057 * the user is doing something that is unsupported (such as 1058 * standard upgrading an old Live Upgrade BE). If that 1059 * happens, mimic existing behavior i.e. pretend that it is 1060 * not a BE. Emit a warning though. 1061 */ 1062 if (bam_alt_root) { 1063 (void) snprintf(path, sizeof (path), "%s%s", bam_root, 1064 GRUB_slice); 1065 } else { 1066 (void) snprintf(path, sizeof (path), "%s", GRUB_slice); 1067 } 1068 1069 if (bam_verbose && stat(path, &sb) == 0) 1070 bam_error(GRUB_SLICE_FILE_EXISTS, path); 1071 1072 if (is_zfs(menu_root)) { 1073 assert(strcmp(menu_root, bam_root) == 0); 1074 special = get_special(menu_root); 1075 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL); 1076 if (special == NULL) { 1077 bam_error(CANT_FIND_SPECIAL, menu_root); 1078 return (BAM_ERROR); 1079 } 1080 pool = strtok(special, "/"); 1081 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL); 1082 if (pool == NULL) { 1083 free(special); 1084 bam_error(CANT_FIND_POOL, menu_root); 1085 return (BAM_ERROR); 1086 } 1087 BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool)); 1088 1089 zmntpt = mount_top_dataset(pool, &zmnted); 1090 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL); 1091 if (zmntpt == NULL) { 1092 bam_error(CANT_MOUNT_POOL_DATASET, pool); 1093 free(special); 1094 return (BAM_ERROR); 1095 } 1096 BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt)); 1097 1098 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root)); 1099 BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root)); 1100 } 1101 1102 elide_trailing_slash(menu_root, clean_menu_root, 1103 sizeof (clean_menu_root)); 1104 1105 BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root)); 1106 1107 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path)); 1108 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path)); 1109 1110 BAM_DPRINTF((D_MENU_PATH, fcn, menu_path)); 1111 1112 /* 1113 * If listing the menu, display the menu location 1114 */ 1115 if (strcmp(subcmd, "list_entry") == 0) 1116 bam_print(GRUB_MENU_PATH, menu_path); 1117 1118 if ((menu = menu_read(menu_path)) == NULL) { 1119 bam_error(CANNOT_LOCATE_GRUB_MENU_FILE, menu_path); 1120 if (special != NULL) 1121 free(special); 1122 1123 return (BAM_ERROR); 1124 } 1125 1126 /* 1127 * We already checked the following case in 1128 * check_subcmd_and_suboptions() above. Complete the 1129 * final step now. 1130 */ 1131 if (strcmp(subcmd, "set_option") == 0) { 1132 assert(largc == 1 && largv[0] && largv[1] == NULL); 1133 opt = largv[0]; 1134 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) && 1135 (strcmp(subcmd, "list_setting") != 0)) { 1136 assert(largc == 0 && largv == NULL); 1137 } 1138 1139 ret = get_boot_cap(bam_root); 1140 if (ret != BAM_SUCCESS) { 1141 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn)); 1142 goto out; 1143 } 1144 1145 /* 1146 * Once the sub-cmd handler has run 1147 * only the line field is guaranteed to have valid values 1148 */ 1149 if (strcmp(subcmd, "update_entry") == 0) { 1150 ret = f(menu, menu_root, osdev); 1151 } else if (strcmp(subcmd, "upgrade") == 0) { 1152 ret = f(menu, bam_root, menu_root); 1153 } else if (strcmp(subcmd, "list_entry") == 0) { 1154 ret = f(menu, menu_path, opt); 1155 } else if (strcmp(subcmd, "list_setting") == 0) { 1156 ret = f(menu, ((largc > 0) ? largv[0] : ""), 1157 ((largc > 1) ? largv[1] : "")); 1158 } else if (strcmp(subcmd, "disable_hypervisor") == 0) { 1159 if (is_sparc()) { 1160 bam_error(NO_SPARC, subcmd); 1161 ret = BAM_ERROR; 1162 } else { 1163 ret = f(menu, bam_root, NULL); 1164 } 1165 } else if (strcmp(subcmd, "enable_hypervisor") == 0) { 1166 if (is_sparc()) { 1167 bam_error(NO_SPARC, subcmd); 1168 ret = BAM_ERROR; 1169 } else { 1170 char *extra_args = NULL; 1171 1172 /* 1173 * Compress all arguments passed in the largv[] array 1174 * into one string that can then be appended to the 1175 * end of the kernel$ string the routine to enable the 1176 * hypervisor will build. 1177 * 1178 * This allows the caller to supply arbitrary unparsed 1179 * arguments, such as dom0 memory settings or APIC 1180 * options. 1181 * 1182 * This concatenation will be done without ANY syntax 1183 * checking whatsoever, so it's the responsibility of 1184 * the caller to make sure the arguments are valid and 1185 * do not duplicate arguments the conversion routines 1186 * may create. 1187 */ 1188 if (largc > 0) { 1189 int extra_len, i; 1190 1191 for (extra_len = 0, i = 0; i < largc; i++) 1192 extra_len += strlen(largv[i]); 1193 1194 /* 1195 * Allocate space for argument strings, 1196 * intervening spaces and terminating NULL. 1197 */ 1198 extra_args = alloca(extra_len + largc); 1199 1200 (void) strcpy(extra_args, largv[0]); 1201 1202 for (i = 1; i < largc; i++) { 1203 (void) strcat(extra_args, " "); 1204 (void) strcat(extra_args, largv[i]); 1205 } 1206 } 1207 1208 ret = f(menu, bam_root, extra_args); 1209 } 1210 } else 1211 ret = f(menu, NULL, opt); 1212 1213 if (ret == BAM_WRITE) { 1214 BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root)); 1215 ret = menu_write(clean_menu_root, menu); 1216 } 1217 1218 out: 1219 INJECT_ERROR1("POOL_SET", pool = "/pooldata"); 1220 assert((is_zfs(menu_root)) ^ (pool == NULL)); 1221 if (pool) { 1222 (void) umount_top_dataset(pool, zmnted, zmntpt); 1223 free(special); 1224 } 1225 menu_free(menu); 1226 return (ret); 1227 } 1228 1229 1230 static error_t 1231 bam_archive( 1232 char *subcmd, 1233 char *opt) 1234 { 1235 error_t ret; 1236 error_t (*f)(char *root, char *opt); 1237 const char *fcn = "bam_archive()"; 1238 1239 /* 1240 * Add trailing / for archive subcommands 1241 */ 1242 if (rootbuf[strlen(rootbuf) - 1] != '/') 1243 (void) strcat(rootbuf, "/"); 1244 bam_rootlen = strlen(rootbuf); 1245 1246 /* 1247 * Check arguments 1248 */ 1249 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f); 1250 if (ret != BAM_SUCCESS) { 1251 return (BAM_ERROR); 1252 } 1253 1254 ret = get_boot_cap(rootbuf); 1255 if (ret != BAM_SUCCESS) { 1256 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn)); 1257 return (ret); 1258 } 1259 1260 /* 1261 * Check archive not supported with update_all 1262 * since it is awkward to display out-of-sync 1263 * information for each BE. 1264 */ 1265 if (bam_check && strcmp(subcmd, "update_all") == 0) { 1266 bam_error(CHECK_NOT_SUPPORTED, subcmd); 1267 return (BAM_ERROR); 1268 } 1269 1270 if (strcmp(subcmd, "update_all") == 0) 1271 bam_update_all = 1; 1272 1273 #if !defined(_OPB) 1274 ucode_install(bam_root); 1275 #endif 1276 1277 ret = f(bam_root, opt); 1278 1279 bam_update_all = 0; 1280 1281 return (ret); 1282 } 1283 1284 /*PRINTFLIKE1*/ 1285 void 1286 bam_error(char *format, ...) 1287 { 1288 va_list ap; 1289 1290 va_start(ap, format); 1291 (void) fprintf(stderr, "%s: ", prog); 1292 (void) vfprintf(stderr, format, ap); 1293 va_end(ap); 1294 } 1295 1296 /*PRINTFLIKE1*/ 1297 void 1298 bam_derror(char *format, ...) 1299 { 1300 va_list ap; 1301 1302 assert(bam_debug); 1303 1304 va_start(ap, format); 1305 (void) fprintf(stderr, "DEBUG: "); 1306 (void) vfprintf(stderr, format, ap); 1307 va_end(ap); 1308 } 1309 1310 /*PRINTFLIKE1*/ 1311 void 1312 bam_print(char *format, ...) 1313 { 1314 va_list ap; 1315 1316 va_start(ap, format); 1317 (void) vfprintf(stdout, format, ap); 1318 va_end(ap); 1319 } 1320 1321 /*PRINTFLIKE1*/ 1322 void 1323 bam_print_stderr(char *format, ...) 1324 { 1325 va_list ap; 1326 1327 va_start(ap, format); 1328 (void) vfprintf(stderr, format, ap); 1329 va_end(ap); 1330 } 1331 1332 void 1333 bam_exit(int excode) 1334 { 1335 restore_env(); 1336 bam_unlock(); 1337 exit(excode); 1338 } 1339 1340 static void 1341 bam_lock(void) 1342 { 1343 struct flock lock; 1344 pid_t pid; 1345 1346 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS); 1347 if (bam_lock_fd < 0) { 1348 /* 1349 * We may be invoked early in boot for archive verification. 1350 * In this case, root is readonly and /var/run may not exist. 1351 * Proceed without the lock 1352 */ 1353 if (errno == EROFS || errno == ENOENT) { 1354 bam_root_readonly = 1; 1355 return; 1356 } 1357 1358 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno)); 1359 bam_exit(1); 1360 } 1361 1362 lock.l_type = F_WRLCK; 1363 lock.l_whence = SEEK_SET; 1364 lock.l_start = 0; 1365 lock.l_len = 0; 1366 1367 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) { 1368 if (errno != EACCES && errno != EAGAIN) { 1369 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1370 (void) close(bam_lock_fd); 1371 bam_lock_fd = -1; 1372 bam_exit(1); 1373 } 1374 pid = 0; 1375 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0); 1376 bam_print(FILE_LOCKED, pid); 1377 1378 lock.l_type = F_WRLCK; 1379 lock.l_whence = SEEK_SET; 1380 lock.l_start = 0; 1381 lock.l_len = 0; 1382 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) { 1383 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1384 (void) close(bam_lock_fd); 1385 bam_lock_fd = -1; 1386 bam_exit(1); 1387 } 1388 } 1389 1390 /* We own the lock now */ 1391 pid = getpid(); 1392 (void) write(bam_lock_fd, &pid, sizeof (pid)); 1393 } 1394 1395 static void 1396 bam_unlock(void) 1397 { 1398 struct flock unlock; 1399 1400 /* 1401 * NOP if we don't hold the lock 1402 */ 1403 if (bam_lock_fd < 0) { 1404 return; 1405 } 1406 1407 unlock.l_type = F_UNLCK; 1408 unlock.l_whence = SEEK_SET; 1409 unlock.l_start = 0; 1410 unlock.l_len = 0; 1411 1412 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) { 1413 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1414 } 1415 1416 if (close(bam_lock_fd) == -1) { 1417 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno)); 1418 } 1419 bam_lock_fd = -1; 1420 } 1421 1422 static error_t 1423 list_archive(char *root, char *opt) 1424 { 1425 filelist_t flist; 1426 filelist_t *flistp = &flist; 1427 line_t *lp; 1428 1429 assert(root); 1430 assert(opt == NULL); 1431 1432 flistp->head = flistp->tail = NULL; 1433 if (read_list(root, flistp) != BAM_SUCCESS) { 1434 return (BAM_ERROR); 1435 } 1436 assert(flistp->head && flistp->tail); 1437 1438 for (lp = flistp->head; lp; lp = lp->next) { 1439 bam_print(PRINT, lp->line); 1440 } 1441 1442 filelist_free(flistp); 1443 1444 return (BAM_SUCCESS); 1445 } 1446 1447 /* 1448 * This routine writes a list of lines to a file. 1449 * The list is *not* freed 1450 */ 1451 static error_t 1452 list2file(char *root, char *tmp, char *final, line_t *start) 1453 { 1454 char tmpfile[PATH_MAX]; 1455 char path[PATH_MAX]; 1456 FILE *fp; 1457 int ret; 1458 struct stat sb; 1459 mode_t mode; 1460 uid_t root_uid; 1461 gid_t sys_gid; 1462 struct passwd *pw; 1463 struct group *gp; 1464 const char *fcn = "list2file()"; 1465 1466 (void) snprintf(path, sizeof (path), "%s%s", root, final); 1467 1468 if (start == NULL) { 1469 /* Empty GRUB menu */ 1470 if (stat(path, &sb) != -1) { 1471 bam_print(UNLINK_EMPTY, path); 1472 if (unlink(path) != 0) { 1473 bam_error(UNLINK_FAIL, path, strerror(errno)); 1474 return (BAM_ERROR); 1475 } else { 1476 return (BAM_SUCCESS); 1477 } 1478 } 1479 return (BAM_SUCCESS); 1480 } 1481 1482 /* 1483 * Preserve attributes of existing file if possible, 1484 * otherwise ask the system for uid/gid of root/sys. 1485 * If all fails, fall back on hard-coded defaults. 1486 */ 1487 if (stat(path, &sb) != -1) { 1488 mode = sb.st_mode; 1489 root_uid = sb.st_uid; 1490 sys_gid = sb.st_gid; 1491 } else { 1492 mode = DEFAULT_DEV_MODE; 1493 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) { 1494 root_uid = pw->pw_uid; 1495 } else { 1496 bam_error(CANT_FIND_USER, 1497 DEFAULT_DEV_USER, DEFAULT_DEV_UID); 1498 root_uid = (uid_t)DEFAULT_DEV_UID; 1499 } 1500 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) { 1501 sys_gid = gp->gr_gid; 1502 } else { 1503 bam_error(CANT_FIND_GROUP, 1504 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID); 1505 sys_gid = (gid_t)DEFAULT_DEV_GID; 1506 } 1507 } 1508 1509 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp); 1510 1511 /* Truncate tmpfile first */ 1512 fp = fopen(tmpfile, "w"); 1513 if (fp == NULL) { 1514 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1515 return (BAM_ERROR); 1516 } 1517 ret = fclose(fp); 1518 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF); 1519 if (ret == EOF) { 1520 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1521 return (BAM_ERROR); 1522 } 1523 1524 /* Now open it in append mode */ 1525 fp = fopen(tmpfile, "a"); 1526 if (fp == NULL) { 1527 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1528 return (BAM_ERROR); 1529 } 1530 1531 for (; start; start = start->next) { 1532 ret = s_fputs(start->line, fp); 1533 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF); 1534 if (ret == EOF) { 1535 bam_error(WRITE_FAIL, tmpfile, strerror(errno)); 1536 (void) fclose(fp); 1537 return (BAM_ERROR); 1538 } 1539 } 1540 1541 ret = fclose(fp); 1542 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF); 1543 if (ret == EOF) { 1544 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1545 return (BAM_ERROR); 1546 } 1547 1548 /* 1549 * Set up desired attributes. Ignore failures on filesystems 1550 * not supporting these operations - pcfs reports unsupported 1551 * operations as EINVAL. 1552 */ 1553 ret = chmod(tmpfile, mode); 1554 if (ret == -1 && 1555 errno != EINVAL && errno != ENOTSUP) { 1556 bam_error(CHMOD_FAIL, tmpfile, strerror(errno)); 1557 return (BAM_ERROR); 1558 } 1559 1560 ret = chown(tmpfile, root_uid, sys_gid); 1561 if (ret == -1 && 1562 errno != EINVAL && errno != ENOTSUP) { 1563 bam_error(CHOWN_FAIL, tmpfile, strerror(errno)); 1564 return (BAM_ERROR); 1565 } 1566 1567 /* 1568 * Do an atomic rename 1569 */ 1570 ret = rename(tmpfile, path); 1571 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1); 1572 if (ret != 0) { 1573 bam_error(RENAME_FAIL, path, strerror(errno)); 1574 return (BAM_ERROR); 1575 } 1576 1577 BAM_DPRINTF((D_WROTE_FILE, fcn, path)); 1578 return (BAM_SUCCESS); 1579 } 1580 1581 /* 1582 * Checks if the path specified (without the file name at the end) exists 1583 * and creates it if not. If the path exists and is not a directory, an attempt 1584 * to unlink is made. 1585 */ 1586 static int 1587 setup_path(char *path) 1588 { 1589 char *p; 1590 int ret; 1591 struct stat sb; 1592 1593 p = strrchr(path, '/'); 1594 if (p != NULL) { 1595 *p = '\0'; 1596 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) { 1597 /* best effort attempt, mkdirp will catch the error */ 1598 (void) unlink(path); 1599 if (bam_verbose) 1600 bam_print(NEED_DIRPATH, path); 1601 ret = mkdirp(path, DIR_PERMS); 1602 if (ret == -1) { 1603 bam_error(MKDIR_FAILED, path, strerror(errno)); 1604 *p = '/'; 1605 return (BAM_ERROR); 1606 } 1607 } 1608 *p = '/'; 1609 return (BAM_SUCCESS); 1610 } 1611 return (BAM_SUCCESS); 1612 } 1613 1614 typedef union { 1615 gzFile gzfile; 1616 int fdfile; 1617 } outfile; 1618 1619 typedef struct { 1620 char path[PATH_MAX]; 1621 outfile out; 1622 } cachefile; 1623 1624 static int 1625 setup_file(char *base, const char *path, cachefile *cf) 1626 { 1627 int ret; 1628 char *strip; 1629 1630 /* init gzfile or fdfile in case we fail before opening */ 1631 if (bam_direct == BAM_DIRECT_DBOOT) 1632 cf->out.gzfile = NULL; 1633 else 1634 cf->out.fdfile = -1; 1635 1636 /* strip the trailing altroot path */ 1637 strip = (char *)path + strlen(rootbuf); 1638 1639 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip); 1640 if (ret >= sizeof (cf->path)) { 1641 bam_error(PATH_TOO_LONG, rootbuf); 1642 return (BAM_ERROR); 1643 } 1644 1645 /* Check if path is present in the archive cache directory */ 1646 if (setup_path(cf->path) == BAM_ERROR) 1647 return (BAM_ERROR); 1648 1649 if (bam_direct == BAM_DIRECT_DBOOT) { 1650 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) { 1651 bam_error(OPEN_FAIL, cf->path, strerror(errno)); 1652 return (BAM_ERROR); 1653 } 1654 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED, 1655 Z_DEFAULT_STRATEGY); 1656 } else { 1657 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644)) 1658 == -1) { 1659 bam_error(OPEN_FAIL, cf->path, strerror(errno)); 1660 return (BAM_ERROR); 1661 } 1662 } 1663 1664 return (BAM_SUCCESS); 1665 } 1666 1667 static int 1668 cache_write(cachefile cf, char *buf, int size) 1669 { 1670 int err; 1671 1672 if (bam_direct == BAM_DIRECT_DBOOT) { 1673 if (gzwrite(cf.out.gzfile, buf, size) < 1) { 1674 bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err)); 1675 if (err == Z_ERRNO && bam_verbose) { 1676 bam_error(WRITE_FAIL, cf.path, strerror(errno)); 1677 } 1678 return (BAM_ERROR); 1679 } 1680 } else { 1681 if (write(cf.out.fdfile, buf, size) < 1) { 1682 bam_error(WRITE_FAIL, cf.path, strerror(errno)); 1683 return (BAM_ERROR); 1684 } 1685 } 1686 return (BAM_SUCCESS); 1687 } 1688 1689 static int 1690 cache_close(cachefile cf) 1691 { 1692 int ret; 1693 1694 if (bam_direct == BAM_DIRECT_DBOOT) { 1695 if (cf.out.gzfile) { 1696 ret = gzclose(cf.out.gzfile); 1697 if (ret != Z_OK) { 1698 bam_error(CLOSE_FAIL, cf.path, strerror(errno)); 1699 return (BAM_ERROR); 1700 } 1701 } 1702 } else { 1703 if (cf.out.fdfile != -1) { 1704 ret = close(cf.out.fdfile); 1705 if (ret != 0) { 1706 bam_error(CLOSE_FAIL, cf.path, strerror(errno)); 1707 return (BAM_ERROR); 1708 } 1709 } 1710 } 1711 1712 return (BAM_SUCCESS); 1713 } 1714 1715 static int 1716 dircache_updatefile(const char *path, int what) 1717 { 1718 int ret, exitcode; 1719 char buf[4096 * 4]; 1720 FILE *infile; 1721 cachefile outfile, outupdt; 1722 1723 if (bam_nowrite()) { 1724 set_dir_flag(what, NEED_UPDATE); 1725 return (BAM_SUCCESS); 1726 } 1727 1728 if (!has_cachedir(what)) 1729 return (BAM_SUCCESS); 1730 1731 if ((infile = fopen(path, "rb")) == NULL) { 1732 bam_error(OPEN_FAIL, path, strerror(errno)); 1733 return (BAM_ERROR); 1734 } 1735 1736 ret = setup_file(get_cachedir(what), path, &outfile); 1737 if (ret == BAM_ERROR) { 1738 exitcode = BAM_ERROR; 1739 goto out; 1740 } 1741 if (!is_dir_flag_on(what, NO_MULTI)) { 1742 ret = setup_file(get_updatedir(what), path, &outupdt); 1743 if (ret == BAM_ERROR) 1744 set_dir_flag(what, NO_MULTI); 1745 } 1746 1747 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) { 1748 if (cache_write(outfile, buf, ret) == BAM_ERROR) { 1749 exitcode = BAM_ERROR; 1750 goto out; 1751 } 1752 if (!is_dir_flag_on(what, NO_MULTI)) 1753 if (cache_write(outupdt, buf, ret) == BAM_ERROR) 1754 set_dir_flag(what, NO_MULTI); 1755 } 1756 1757 set_dir_flag(what, NEED_UPDATE); 1758 get_count(what)++; 1759 if (get_count(what) > COUNT_MAX) 1760 set_dir_flag(what, NO_MULTI); 1761 exitcode = BAM_SUCCESS; 1762 out: 1763 (void) fclose(infile); 1764 if (cache_close(outfile) == BAM_ERROR) 1765 exitcode = BAM_ERROR; 1766 if (!is_dir_flag_on(what, NO_MULTI) && 1767 cache_close(outupdt) == BAM_ERROR) 1768 exitcode = BAM_ERROR; 1769 if (exitcode == BAM_ERROR) 1770 set_flag(UPDATE_ERROR); 1771 return (exitcode); 1772 } 1773 1774 static int 1775 dircache_updatedir(const char *path, int what, int updt) 1776 { 1777 int ret; 1778 char dpath[PATH_MAX]; 1779 char *strip; 1780 struct stat sb; 1781 1782 strip = (char *)path + strlen(rootbuf); 1783 1784 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ? 1785 get_updatedir(what) : get_cachedir(what), strip); 1786 1787 if (ret >= sizeof (dpath)) { 1788 bam_error(PATH_TOO_LONG, rootbuf); 1789 set_flag(UPDATE_ERROR); 1790 return (BAM_ERROR); 1791 } 1792 1793 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode)) 1794 return (BAM_SUCCESS); 1795 1796 if (updt) { 1797 if (!is_dir_flag_on(what, NO_MULTI)) 1798 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) 1799 set_dir_flag(what, NO_MULTI); 1800 } else { 1801 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) { 1802 set_flag(UPDATE_ERROR); 1803 return (BAM_ERROR); 1804 } 1805 } 1806 1807 set_dir_flag(what, NEED_UPDATE); 1808 return (BAM_SUCCESS); 1809 } 1810 1811 #define DO_CACHE_DIR 0 1812 #define DO_UPDATE_DIR 1 1813 1814 #if defined(_LP64) || defined(_LONGLONG_TYPE) 1815 typedef Elf64_Ehdr _elfhdr; 1816 #else 1817 typedef Elf32_Ehdr _elfhdr; 1818 #endif 1819 1820 /* 1821 * This routine updates the contents of the cache directory 1822 */ 1823 static int 1824 update_dircache(const char *path, int flags) 1825 { 1826 int rc = BAM_SUCCESS; 1827 1828 switch (flags) { 1829 case FTW_F: 1830 { 1831 int fd; 1832 _elfhdr elf; 1833 1834 if ((fd = open(path, O_RDONLY)) < 0) { 1835 bam_error(OPEN_FAIL, path, strerror(errno)); 1836 set_flag(UPDATE_ERROR); 1837 rc = BAM_ERROR; 1838 break; 1839 } 1840 1841 /* 1842 * libelf and gelf would be a cleaner and easier way to handle 1843 * this, but libelf fails compilation if _ILP32 is defined && 1844 * _FILE_OFFSET_BITS is != 32 ... 1845 */ 1846 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) { 1847 bam_error(READ_FAIL, path, strerror(errno)); 1848 set_flag(UPDATE_ERROR); 1849 (void) close(fd); 1850 rc = BAM_ERROR; 1851 break; 1852 } 1853 (void) close(fd); 1854 1855 /* 1856 * If the file is not an executable and is not inside an amd64 1857 * directory, we copy it in both the cache directories, 1858 * otherwise, we only copy it inside the 64-bit one. 1859 */ 1860 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) { 1861 if (strstr(path, "/amd64")) { 1862 rc = dircache_updatefile(path, FILE64); 1863 } else { 1864 rc = dircache_updatefile(path, FILE32); 1865 if (rc == BAM_SUCCESS) 1866 rc = dircache_updatefile(path, FILE64); 1867 } 1868 } else { 1869 /* 1870 * Based on the ELF class we copy the file in the 32-bit 1871 * or the 64-bit cache directory. 1872 */ 1873 if (elf.e_ident[EI_CLASS] == ELFCLASS32) { 1874 rc = dircache_updatefile(path, FILE32); 1875 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) { 1876 rc = dircache_updatefile(path, FILE64); 1877 } else { 1878 bam_print(NO3264ELF, path); 1879 /* paranoid */ 1880 rc = dircache_updatefile(path, FILE32); 1881 if (rc == BAM_SUCCESS) 1882 rc = dircache_updatefile(path, FILE64); 1883 } 1884 } 1885 break; 1886 } 1887 case FTW_D: 1888 if (strstr(path, "/amd64") == NULL) { 1889 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR); 1890 if (rc == BAM_SUCCESS) 1891 rc = dircache_updatedir(path, FILE32, 1892 DO_CACHE_DIR); 1893 } else { 1894 if (has_cachedir(FILE64)) { 1895 rc = dircache_updatedir(path, FILE64, 1896 DO_UPDATE_DIR); 1897 if (rc == BAM_SUCCESS) 1898 rc = dircache_updatedir(path, FILE64, 1899 DO_CACHE_DIR); 1900 } 1901 } 1902 break; 1903 default: 1904 rc = BAM_ERROR; 1905 break; 1906 } 1907 1908 return (rc); 1909 } 1910 1911 /*ARGSUSED*/ 1912 static int 1913 cmpstat( 1914 const char *file, 1915 const struct stat *st, 1916 int flags, 1917 struct FTW *ftw) 1918 { 1919 uint_t sz; 1920 uint64_t *value; 1921 uint64_t filestat[2]; 1922 int error, ret, status; 1923 1924 struct safefile *safefilep; 1925 FILE *fp; 1926 struct stat sb; 1927 regex_t re; 1928 1929 /* 1930 * On SPARC we create/update links too. 1931 */ 1932 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL && 1933 !is_flag_on(IS_SPARC_TARGET))) 1934 return (0); 1935 1936 /* 1937 * Ignore broken links 1938 */ 1939 if (flags == FTW_SL && stat(file, &sb) < 0) 1940 return (0); 1941 1942 /* 1943 * new_nvlp may be NULL if there were errors earlier 1944 * but this is not fatal to update determination. 1945 */ 1946 if (walk_arg.new_nvlp) { 1947 filestat[0] = st->st_size; 1948 filestat[1] = st->st_mtime; 1949 error = nvlist_add_uint64_array(walk_arg.new_nvlp, 1950 file + bam_rootlen, filestat, 2); 1951 if (error) 1952 bam_error(NVADD_FAIL, file, strerror(error)); 1953 } 1954 1955 /* 1956 * If we are invoked as part of system/filesystem/boot-archive, then 1957 * there are a number of things we should not worry about 1958 */ 1959 if (bam_smf_check) { 1960 /* ignore amd64 modules unless we are booted amd64. */ 1961 if (!is_amd64() && strstr(file, "/amd64/") != 0) 1962 return (0); 1963 1964 /* read in list of safe files */ 1965 if (safefiles == NULL) 1966 if (fp = fopen("/boot/solaris/filelist.safe", "r")) { 1967 safefiles = s_calloc(1, 1968 sizeof (struct safefile)); 1969 safefilep = safefiles; 1970 safefilep->name = s_calloc(1, MAXPATHLEN + 1971 MAXNAMELEN); 1972 safefilep->next = NULL; 1973 while (s_fgets(safefilep->name, MAXPATHLEN + 1974 MAXNAMELEN, fp) != NULL) { 1975 safefilep->next = s_calloc(1, 1976 sizeof (struct safefile)); 1977 safefilep = safefilep->next; 1978 safefilep->name = s_calloc(1, 1979 MAXPATHLEN + MAXNAMELEN); 1980 safefilep->next = NULL; 1981 } 1982 (void) fclose(fp); 1983 } 1984 } 1985 1986 /* 1987 * On SPARC we create a -path-list file for mkisofs 1988 */ 1989 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) { 1990 if (flags != FTW_D) { 1991 char *strip; 1992 1993 strip = (char *)file + strlen(rootbuf); 1994 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip, 1995 file); 1996 } 1997 } 1998 1999 /* 2000 * We are transitioning from the old model to the dircache or the cache 2001 * directory was removed: create the entry without further checkings. 2002 */ 2003 if (is_flag_on(NEED_CACHE_DIR)) { 2004 if (bam_verbose) 2005 bam_print(PARSEABLE_NEW_FILE, file); 2006 2007 if (is_flag_on(IS_SPARC_TARGET)) { 2008 set_dir_flag(FILE64, NEED_UPDATE); 2009 return (0); 2010 } 2011 2012 ret = update_dircache(file, flags); 2013 if (ret == BAM_ERROR) { 2014 bam_error(UPDT_CACHE_FAIL, file); 2015 return (-1); 2016 } 2017 2018 return (0); 2019 } 2020 2021 /* 2022 * We need an update if file doesn't exist in old archive 2023 */ 2024 if (walk_arg.old_nvlp == NULL || 2025 nvlist_lookup_uint64_array(walk_arg.old_nvlp, 2026 file + bam_rootlen, &value, &sz) != 0) { 2027 if (bam_smf_check) /* ignore new during smf check */ 2028 return (0); 2029 2030 if (is_flag_on(IS_SPARC_TARGET)) { 2031 set_dir_flag(FILE64, NEED_UPDATE); 2032 } else { 2033 ret = update_dircache(file, flags); 2034 if (ret == BAM_ERROR) { 2035 bam_error(UPDT_CACHE_FAIL, file); 2036 return (-1); 2037 } 2038 } 2039 2040 if (bam_verbose) 2041 bam_print(PARSEABLE_NEW_FILE, file); 2042 return (0); 2043 } 2044 2045 /* 2046 * If we got there, the file is already listed as to be included in the 2047 * iso image. We just need to know if we are going to rebuild it or not 2048 */ 2049 if (is_flag_on(IS_SPARC_TARGET) && 2050 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite()) 2051 return (0); 2052 /* 2053 * File exists in old archive. Check if file has changed 2054 */ 2055 assert(sz == 2); 2056 bcopy(value, filestat, sizeof (filestat)); 2057 2058 if (flags != FTW_D && (filestat[0] != st->st_size || 2059 filestat[1] != st->st_mtime)) { 2060 if (bam_smf_check) { 2061 safefilep = safefiles; 2062 while (safefilep != NULL && 2063 safefilep->name[0] != '\0') { 2064 if (regcomp(&re, safefilep->name, 2065 REG_EXTENDED|REG_NOSUB) == 0) { 2066 status = regexec(&re, 2067 file + bam_rootlen, 0, NULL, 0); 2068 regfree(&re); 2069 if (status == 0) { 2070 (void) creat( 2071 NEED_UPDATE_SAFE_FILE, 2072 0644); 2073 return (0); 2074 } 2075 } 2076 safefilep = safefilep->next; 2077 } 2078 } 2079 2080 if (is_flag_on(IS_SPARC_TARGET)) { 2081 set_dir_flag(FILE64, NEED_UPDATE); 2082 } else { 2083 ret = update_dircache(file, flags); 2084 if (ret == BAM_ERROR) { 2085 bam_error(UPDT_CACHE_FAIL, file); 2086 return (-1); 2087 } 2088 } 2089 2090 if (bam_verbose) 2091 if (bam_smf_check) 2092 bam_print(" %s\n", file); 2093 else 2094 bam_print(PARSEABLE_OUT_DATE, file); 2095 } 2096 2097 return (0); 2098 } 2099 2100 /* 2101 * Remove a directory path recursively 2102 */ 2103 static int 2104 rmdir_r(char *path) 2105 { 2106 struct dirent *d = NULL; 2107 DIR *dir = NULL; 2108 char tpath[PATH_MAX]; 2109 struct stat sb; 2110 2111 if ((dir = opendir(path)) == NULL) 2112 return (-1); 2113 2114 while (d = readdir(dir)) { 2115 if ((strcmp(d->d_name, ".") != 0) && 2116 (strcmp(d->d_name, "..") != 0)) { 2117 (void) snprintf(tpath, sizeof (tpath), "%s/%s", 2118 path, d->d_name); 2119 if (stat(tpath, &sb) == 0) { 2120 if (sb.st_mode & S_IFDIR) 2121 (void) rmdir_r(tpath); 2122 else 2123 (void) remove(tpath); 2124 } 2125 } 2126 } 2127 return (remove(path)); 2128 } 2129 2130 /* 2131 * Check if cache directory exists and, if not, create it and update flags 2132 * accordingly. If the path exists, but it's not a directory, a best effort 2133 * attempt to remove and recreate it is made. 2134 * If the user requested a 'purge', always recreate the directory from scratch. 2135 */ 2136 static int 2137 set_cache_dir(char *root, int what) 2138 { 2139 struct stat sb; 2140 int ret = 0; 2141 2142 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)), 2143 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ? 2144 "/amd64" : "", CACHEDIR_SUFFIX); 2145 2146 if (ret >= sizeof (get_cachedir(what))) { 2147 bam_error(PATH_TOO_LONG, rootbuf); 2148 return (BAM_ERROR); 2149 } 2150 2151 if (bam_purge || is_flag_on(INVALIDATE_CACHE)) 2152 (void) rmdir_r(get_cachedir(what)); 2153 2154 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) { 2155 /* best effort unlink attempt, mkdir will catch errors */ 2156 (void) unlink(get_cachedir(what)); 2157 2158 if (bam_verbose) 2159 bam_print(UPDATE_CDIR_MISS, get_cachedir(what)); 2160 ret = mkdir(get_cachedir(what), DIR_PERMS); 2161 if (ret < 0) { 2162 bam_error(MKDIR_FAILED, get_cachedir(what), 2163 strerror(errno)); 2164 get_cachedir(what)[0] = '\0'; 2165 return (ret); 2166 } 2167 set_flag(NEED_CACHE_DIR); 2168 set_dir_flag(what, NO_MULTI); 2169 } 2170 2171 return (BAM_SUCCESS); 2172 } 2173 2174 static int 2175 set_update_dir(char *root, int what) 2176 { 2177 struct stat sb; 2178 int ret; 2179 2180 if (is_dir_flag_on(what, NO_MULTI)) 2181 return (BAM_SUCCESS); 2182 2183 if (!bam_extend) { 2184 set_dir_flag(what, NO_MULTI); 2185 return (BAM_SUCCESS); 2186 } 2187 2188 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 2189 ret = snprintf(get_updatedir(what), 2190 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root, 2191 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX); 2192 else 2193 ret = snprintf(get_updatedir(what), 2194 sizeof (get_updatedir(what)), "%s%s%s%s", root, 2195 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX); 2196 2197 if (ret >= sizeof (get_updatedir(what))) { 2198 bam_error(PATH_TOO_LONG, rootbuf); 2199 return (BAM_ERROR); 2200 } 2201 2202 if (stat(get_updatedir(what), &sb) == 0) { 2203 if (S_ISDIR(sb.st_mode)) 2204 ret = rmdir_r(get_updatedir(what)); 2205 else 2206 ret = unlink(get_updatedir(what)); 2207 2208 if (ret != 0) 2209 set_dir_flag(what, NO_MULTI); 2210 } 2211 2212 if (mkdir(get_updatedir(what), DIR_PERMS) < 0) 2213 set_dir_flag(what, NO_MULTI); 2214 2215 return (BAM_SUCCESS); 2216 } 2217 2218 static int 2219 is_valid_archive(char *root, int what) 2220 { 2221 char archive_path[PATH_MAX]; 2222 char timestamp_path[PATH_MAX]; 2223 struct stat sb, timestamp; 2224 int ret; 2225 2226 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 2227 ret = snprintf(archive_path, sizeof (archive_path), 2228 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(), 2229 ARCHIVE_SUFFIX); 2230 else 2231 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s", 2232 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX); 2233 2234 if (ret >= sizeof (archive_path)) { 2235 bam_error(PATH_TOO_LONG, rootbuf); 2236 return (BAM_ERROR); 2237 } 2238 2239 if (stat(archive_path, &sb) != 0) { 2240 if (bam_verbose && !bam_check) 2241 bam_print(UPDATE_ARCH_MISS, archive_path); 2242 set_dir_flag(what, NEED_UPDATE); 2243 set_dir_flag(what, NO_MULTI); 2244 return (BAM_SUCCESS); 2245 } 2246 2247 /* 2248 * The timestamp file is used to prevent stale files in the archive 2249 * cache. 2250 * Stale files can happen if the system is booted back and forth across 2251 * the transition from bootadm-before-the-cache to 2252 * bootadm-after-the-cache, since older versions of bootadm don't know 2253 * about the existence of the archive cache. 2254 * 2255 * Since only bootadm-after-the-cache versions know about about this 2256 * file, we require that the boot archive be older than this file. 2257 */ 2258 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root, 2259 FILE_STAT_TIMESTAMP); 2260 2261 if (ret >= sizeof (timestamp_path)) { 2262 bam_error(PATH_TOO_LONG, rootbuf); 2263 return (BAM_ERROR); 2264 } 2265 2266 if (stat(timestamp_path, ×tamp) != 0 || 2267 sb.st_mtime > timestamp.st_mtime) { 2268 if (bam_verbose && !bam_check) 2269 bam_print(UPDATE_CACHE_OLD, timestamp); 2270 /* 2271 * Don't generate a false positive for the boot-archive service 2272 * but trigger an update of the archive cache in 2273 * boot-archive-update. 2274 */ 2275 if (bam_smf_check) { 2276 (void) creat(NEED_UPDATE_FILE, 0644); 2277 return (BAM_SUCCESS); 2278 } 2279 2280 set_flag(INVALIDATE_CACHE); 2281 set_dir_flag(what, NEED_UPDATE); 2282 set_dir_flag(what, NO_MULTI); 2283 return (BAM_SUCCESS); 2284 } 2285 2286 if (is_flag_on(IS_SPARC_TARGET)) 2287 return (BAM_SUCCESS); 2288 2289 if (bam_extend && sb.st_size > BA_SIZE_MAX) { 2290 if (bam_verbose && !bam_check) 2291 bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX); 2292 set_dir_flag(what, NO_MULTI); 2293 } 2294 2295 return (BAM_SUCCESS); 2296 } 2297 2298 /* 2299 * Check flags and presence of required files and directories. 2300 * The force flag and/or absence of files should 2301 * trigger an update. 2302 * Suppress stdout output if check (-n) option is set 2303 * (as -n should only produce parseable output.) 2304 */ 2305 static int 2306 check_flags_and_files(char *root) 2307 { 2308 2309 struct stat sb; 2310 int ret; 2311 2312 /* 2313 * If archive is missing, create archive 2314 */ 2315 if (is_flag_on(IS_SPARC_TARGET)) { 2316 ret = is_valid_archive(root, FILE64); 2317 if (ret == BAM_ERROR) 2318 return (BAM_ERROR); 2319 } else { 2320 int what = FILE32; 2321 do { 2322 ret = is_valid_archive(root, what); 2323 if (ret == BAM_ERROR) 2324 return (BAM_ERROR); 2325 what++; 2326 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM); 2327 } 2328 2329 if (bam_nowrite()) 2330 return (BAM_SUCCESS); 2331 2332 2333 /* 2334 * check if cache directories exist on x86. 2335 * check (and always open) the cache file on SPARC. 2336 */ 2337 if (is_sparc()) { 2338 ret = snprintf(get_cachedir(FILE64), 2339 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root, 2340 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX); 2341 2342 if (ret >= sizeof (get_cachedir(FILE64))) { 2343 bam_error(PATH_TOO_LONG, rootbuf); 2344 return (BAM_ERROR); 2345 } 2346 2347 if (stat(get_cachedir(FILE64), &sb) != 0) { 2348 set_flag(NEED_CACHE_DIR); 2349 set_dir_flag(FILE64, NEED_UPDATE); 2350 } 2351 2352 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w"); 2353 if (walk_arg.sparcfile == NULL) { 2354 bam_error(OPEN_FAIL, get_cachedir(FILE64), 2355 strerror(errno)); 2356 return (BAM_ERROR); 2357 } 2358 2359 set_dir_present(FILE64); 2360 } else { 2361 int what = FILE32; 2362 2363 do { 2364 if (set_cache_dir(root, what) != 0) 2365 return (BAM_ERROR); 2366 2367 set_dir_present(what); 2368 2369 if (set_update_dir(root, what) != 0) 2370 return (BAM_ERROR); 2371 what++; 2372 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM); 2373 } 2374 2375 /* 2376 * if force, create archive unconditionally 2377 */ 2378 if (bam_force) { 2379 if (!is_sparc()) 2380 set_dir_flag(FILE32, NEED_UPDATE); 2381 set_dir_flag(FILE64, NEED_UPDATE); 2382 if (bam_verbose) 2383 bam_print(UPDATE_FORCE); 2384 return (BAM_SUCCESS); 2385 } 2386 2387 return (BAM_SUCCESS); 2388 } 2389 2390 static error_t 2391 read_one_list(char *root, filelist_t *flistp, char *filelist) 2392 { 2393 char path[PATH_MAX]; 2394 FILE *fp; 2395 char buf[BAM_MAXLINE]; 2396 const char *fcn = "read_one_list()"; 2397 2398 (void) snprintf(path, sizeof (path), "%s%s", root, filelist); 2399 2400 fp = fopen(path, "r"); 2401 if (fp == NULL) { 2402 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno))); 2403 return (BAM_ERROR); 2404 } 2405 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 2406 /* skip blank lines */ 2407 if (strspn(buf, " \t") == strlen(buf)) 2408 continue; 2409 append_to_flist(flistp, buf); 2410 } 2411 if (fclose(fp) != 0) { 2412 bam_error(CLOSE_FAIL, path, strerror(errno)); 2413 return (BAM_ERROR); 2414 } 2415 return (BAM_SUCCESS); 2416 } 2417 2418 static error_t 2419 read_list(char *root, filelist_t *flistp) 2420 { 2421 char path[PATH_MAX]; 2422 char cmd[PATH_MAX]; 2423 struct stat sb; 2424 int n, rval; 2425 const char *fcn = "read_list()"; 2426 2427 flistp->head = flistp->tail = NULL; 2428 2429 /* 2430 * build and check path to extract_boot_filelist.ksh 2431 */ 2432 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST); 2433 if (n >= sizeof (path)) { 2434 bam_error(NO_FLIST); 2435 return (BAM_ERROR); 2436 } 2437 2438 if (is_safe_exec(path) == BAM_ERROR) 2439 return (BAM_ERROR); 2440 2441 /* 2442 * If extract_boot_filelist is present, exec it, otherwise read 2443 * the filelists directly, for compatibility with older images. 2444 */ 2445 if (stat(path, &sb) == 0) { 2446 /* 2447 * build arguments to exec extract_boot_filelist.ksh 2448 */ 2449 char *rootarg, *platarg; 2450 int platarglen = 1, rootarglen = 1; 2451 if (strlen(root) > 1) 2452 rootarglen += strlen(root) + strlen("-R "); 2453 if (bam_alt_platform) 2454 platarglen += strlen(bam_platform) + strlen("-p "); 2455 platarg = s_calloc(1, platarglen); 2456 rootarg = s_calloc(1, rootarglen); 2457 *platarg = 0; 2458 *rootarg = 0; 2459 2460 if (strlen(root) > 1) { 2461 (void) snprintf(rootarg, rootarglen, 2462 "-R %s", root); 2463 } 2464 if (bam_alt_platform) { 2465 (void) snprintf(platarg, platarglen, 2466 "-p %s", bam_platform); 2467 } 2468 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s", 2469 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST); 2470 free(platarg); 2471 free(rootarg); 2472 if (n >= sizeof (cmd)) { 2473 bam_error(NO_FLIST); 2474 return (BAM_ERROR); 2475 } 2476 if (exec_cmd(cmd, flistp) != 0) { 2477 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno))); 2478 return (BAM_ERROR); 2479 } 2480 } else { 2481 /* 2482 * Read current lists of files - only the first is mandatory 2483 */ 2484 rval = read_one_list(root, flistp, BOOT_FILE_LIST); 2485 if (rval != BAM_SUCCESS) 2486 return (rval); 2487 (void) read_one_list(root, flistp, ETC_FILE_LIST); 2488 } 2489 2490 if (flistp->head == NULL) { 2491 bam_error(NO_FLIST); 2492 return (BAM_ERROR); 2493 } 2494 2495 return (BAM_SUCCESS); 2496 } 2497 2498 static void 2499 getoldstat(char *root) 2500 { 2501 char path[PATH_MAX]; 2502 int fd, error; 2503 struct stat sb; 2504 char *ostat; 2505 2506 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 2507 fd = open(path, O_RDONLY); 2508 if (fd == -1) { 2509 if (bam_verbose) 2510 bam_print(OPEN_FAIL, path, strerror(errno)); 2511 goto out_err; 2512 } 2513 2514 if (fstat(fd, &sb) != 0) { 2515 bam_error(STAT_FAIL, path, strerror(errno)); 2516 goto out_err; 2517 } 2518 2519 ostat = s_calloc(1, sb.st_size); 2520 2521 if (read(fd, ostat, sb.st_size) != sb.st_size) { 2522 bam_error(READ_FAIL, path, strerror(errno)); 2523 free(ostat); 2524 goto out_err; 2525 } 2526 2527 (void) close(fd); 2528 fd = -1; 2529 2530 walk_arg.old_nvlp = NULL; 2531 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0); 2532 2533 free(ostat); 2534 2535 if (error) { 2536 bam_error(UNPACK_FAIL, path, strerror(error)); 2537 walk_arg.old_nvlp = NULL; 2538 goto out_err; 2539 } else { 2540 return; 2541 } 2542 2543 out_err: 2544 if (fd != -1) 2545 (void) close(fd); 2546 if (!is_flag_on(IS_SPARC_TARGET)) 2547 set_dir_flag(FILE32, NEED_UPDATE); 2548 set_dir_flag(FILE64, NEED_UPDATE); 2549 } 2550 2551 /* Best effort stale entry removal */ 2552 static void 2553 delete_stale(char *file, int what) 2554 { 2555 char path[PATH_MAX]; 2556 struct stat sb; 2557 2558 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file); 2559 if (!bam_check && stat(path, &sb) == 0) { 2560 if (sb.st_mode & S_IFDIR) 2561 (void) rmdir_r(path); 2562 else 2563 (void) unlink(path); 2564 2565 set_dir_flag(what, (NEED_UPDATE | NO_MULTI)); 2566 } 2567 } 2568 2569 /* 2570 * Checks if a file in the current (old) archive has 2571 * been deleted from the root filesystem. This is needed for 2572 * software like Trusted Extensions (TX) that switch early 2573 * in boot based on presence/absence of a kernel module. 2574 */ 2575 static void 2576 check4stale(char *root) 2577 { 2578 nvpair_t *nvp; 2579 nvlist_t *nvlp; 2580 char *file; 2581 char path[PATH_MAX]; 2582 2583 /* 2584 * Skip stale file check during smf check 2585 */ 2586 if (bam_smf_check) 2587 return; 2588 2589 /* 2590 * If we need to (re)create the cache, there's no need to check for 2591 * stale files 2592 */ 2593 if (is_flag_on(NEED_CACHE_DIR)) 2594 return; 2595 2596 /* Nothing to do if no old stats */ 2597 if ((nvlp = walk_arg.old_nvlp) == NULL) 2598 return; 2599 2600 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp; 2601 nvp = nvlist_next_nvpair(nvlp, nvp)) { 2602 file = nvpair_name(nvp); 2603 if (file == NULL) 2604 continue; 2605 (void) snprintf(path, sizeof (path), "%s/%s", 2606 root, file); 2607 if (access(path, F_OK) < 0) { 2608 int what; 2609 2610 if (bam_verbose) 2611 bam_print(PARSEABLE_STALE_FILE, path); 2612 2613 if (is_flag_on(IS_SPARC_TARGET)) { 2614 set_dir_flag(FILE64, NEED_UPDATE); 2615 } else { 2616 for (what = FILE32; what < CACHEDIR_NUM; what++) 2617 if (has_cachedir(what)) 2618 delete_stale(file, what); 2619 } 2620 } 2621 } 2622 } 2623 2624 static void 2625 create_newstat(void) 2626 { 2627 int error; 2628 2629 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0); 2630 if (error) { 2631 /* 2632 * Not fatal - we can still create archive 2633 */ 2634 walk_arg.new_nvlp = NULL; 2635 bam_error(NVALLOC_FAIL, strerror(error)); 2636 } 2637 } 2638 2639 static int 2640 walk_list(char *root, filelist_t *flistp) 2641 { 2642 char path[PATH_MAX]; 2643 line_t *lp; 2644 2645 for (lp = flistp->head; lp; lp = lp->next) { 2646 /* 2647 * Don't follow symlinks. A symlink must refer to 2648 * a file that would appear in the archive through 2649 * a direct reference. This matches the archive 2650 * construction behavior. 2651 */ 2652 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line); 2653 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) { 2654 if (is_flag_on(UPDATE_ERROR)) 2655 return (BAM_ERROR); 2656 /* 2657 * Some files may not exist. 2658 * For example: etc/rtc_config on a x86 diskless system 2659 * Emit verbose message only 2660 */ 2661 if (bam_verbose) 2662 bam_print(NFTW_FAIL, path, strerror(errno)); 2663 } 2664 } 2665 2666 return (BAM_SUCCESS); 2667 } 2668 2669 /* 2670 * Update the timestamp file. 2671 */ 2672 static void 2673 update_timestamp(char *root) 2674 { 2675 char timestamp_path[PATH_MAX]; 2676 2677 /* this path length has already been checked in check_flags_and_files */ 2678 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root, 2679 FILE_STAT_TIMESTAMP); 2680 2681 /* 2682 * recreate the timestamp file. Since an outdated or absent timestamp 2683 * file translates in a complete rebuild of the archive cache, notify 2684 * the user of the performance issue. 2685 */ 2686 if (creat(timestamp_path, FILE_STAT_MODE) < 0) { 2687 bam_error(OPEN_FAIL, timestamp_path, strerror(errno)); 2688 bam_error(TIMESTAMP_FAIL, rootbuf); 2689 } 2690 } 2691 2692 2693 static void 2694 savenew(char *root) 2695 { 2696 char path[PATH_MAX]; 2697 char path2[PATH_MAX]; 2698 size_t sz; 2699 char *nstat; 2700 int fd, wrote, error; 2701 2702 nstat = NULL; 2703 sz = 0; 2704 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz, 2705 NV_ENCODE_XDR, 0); 2706 if (error) { 2707 bam_error(PACK_FAIL, strerror(error)); 2708 return; 2709 } 2710 2711 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP); 2712 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE); 2713 if (fd == -1) { 2714 bam_error(OPEN_FAIL, path, strerror(errno)); 2715 free(nstat); 2716 return; 2717 } 2718 wrote = write(fd, nstat, sz); 2719 if (wrote != sz) { 2720 bam_error(WRITE_FAIL, path, strerror(errno)); 2721 (void) close(fd); 2722 free(nstat); 2723 return; 2724 } 2725 (void) close(fd); 2726 free(nstat); 2727 2728 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT); 2729 if (rename(path, path2) != 0) { 2730 bam_error(RENAME_FAIL, path2, strerror(errno)); 2731 } 2732 } 2733 2734 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg)) 2735 2736 static void 2737 clear_walk_args(void) 2738 { 2739 if (walk_arg.old_nvlp) 2740 nvlist_free(walk_arg.old_nvlp); 2741 if (walk_arg.new_nvlp) 2742 nvlist_free(walk_arg.new_nvlp); 2743 if (walk_arg.sparcfile) 2744 (void) fclose(walk_arg.sparcfile); 2745 walk_arg.old_nvlp = NULL; 2746 walk_arg.new_nvlp = NULL; 2747 walk_arg.sparcfile = NULL; 2748 } 2749 2750 /* 2751 * Returns: 2752 * 0 - no update necessary 2753 * 1 - update required. 2754 * BAM_ERROR (-1) - An error occurred 2755 * 2756 * Special handling for check (-n): 2757 * ================================ 2758 * The check (-n) option produces parseable output. 2759 * To do this, we suppress all stdout messages unrelated 2760 * to out of sync files. 2761 * All stderr messages are still printed though. 2762 * 2763 */ 2764 static int 2765 update_required(char *root) 2766 { 2767 struct stat sb; 2768 char path[PATH_MAX]; 2769 filelist_t flist; 2770 filelist_t *flistp = &flist; 2771 int ret; 2772 2773 flistp->head = flistp->tail = NULL; 2774 2775 if (is_sparc()) 2776 set_flag(IS_SPARC_TARGET); 2777 2778 /* 2779 * Check if cache directories and archives are present 2780 */ 2781 2782 ret = check_flags_and_files(root); 2783 if (ret < 0) 2784 return (BAM_ERROR); 2785 2786 /* 2787 * In certain deployment scenarios, filestat may not 2788 * exist. Do not stop the boot process, but trigger an update 2789 * of the archives (which will recreate filestat.ramdisk). 2790 */ 2791 if (bam_smf_check) { 2792 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 2793 if (stat(path, &sb) != 0) { 2794 (void) creat(NEED_UPDATE_FILE, 0644); 2795 return (0); 2796 } 2797 } 2798 2799 getoldstat(root); 2800 2801 /* 2802 * Check if the archive contains files that are no longer 2803 * present on the root filesystem. 2804 */ 2805 check4stale(root); 2806 2807 /* 2808 * read list of files 2809 */ 2810 if (read_list(root, flistp) != BAM_SUCCESS) { 2811 clear_walk_args(); 2812 return (BAM_ERROR); 2813 } 2814 2815 assert(flistp->head && flistp->tail); 2816 2817 /* 2818 * At this point either the update is required 2819 * or the decision is pending. In either case 2820 * we need to create new stat nvlist 2821 */ 2822 create_newstat(); 2823 /* 2824 * This walk does 2 things: 2825 * - gets new stat data for every file 2826 * - (optional) compare old and new stat data 2827 */ 2828 ret = walk_list(root, &flist); 2829 2830 /* done with the file list */ 2831 filelist_free(flistp); 2832 2833 /* something went wrong */ 2834 2835 if (ret == BAM_ERROR) { 2836 bam_error(CACHE_FAIL); 2837 return (BAM_ERROR); 2838 } 2839 2840 if (walk_arg.new_nvlp == NULL) { 2841 if (walk_arg.sparcfile != NULL) 2842 (void) fclose(walk_arg.sparcfile); 2843 bam_error(NO_NEW_STAT); 2844 } 2845 2846 /* If nothing was updated, discard newstat. */ 2847 2848 if (!is_dir_flag_on(FILE32, NEED_UPDATE) && 2849 !is_dir_flag_on(FILE64, NEED_UPDATE)) { 2850 clear_walk_args(); 2851 return (0); 2852 } 2853 2854 if (walk_arg.sparcfile != NULL) 2855 (void) fclose(walk_arg.sparcfile); 2856 2857 return (1); 2858 } 2859 2860 static int 2861 flushfs(char *root) 2862 { 2863 char cmd[PATH_MAX + 30]; 2864 2865 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null", 2866 LOCKFS_PATH, root); 2867 2868 return (exec_cmd(cmd, NULL)); 2869 } 2870 2871 static int 2872 do_archive_copy(char *source, char *dest) 2873 { 2874 2875 sync(); 2876 2877 /* the equivalent of mv archive-new-$pid boot_archive */ 2878 if (rename(source, dest) != 0) { 2879 (void) unlink(source); 2880 return (BAM_ERROR); 2881 } 2882 2883 if (flushfs(bam_root) != 0) 2884 sync(); 2885 2886 return (BAM_SUCCESS); 2887 } 2888 2889 static int 2890 check_cmdline(filelist_t flist) 2891 { 2892 line_t *lp; 2893 2894 for (lp = flist.head; lp; lp = lp->next) { 2895 if (strstr(lp->line, "Error:") != NULL || 2896 strstr(lp->line, "Inode number overflow") != NULL) { 2897 (void) fprintf(stderr, "%s\n", lp->line); 2898 return (BAM_ERROR); 2899 } 2900 } 2901 2902 return (BAM_SUCCESS); 2903 } 2904 2905 static void 2906 dump_errormsg(filelist_t flist) 2907 { 2908 line_t *lp; 2909 2910 for (lp = flist.head; lp; lp = lp->next) 2911 (void) fprintf(stderr, "%s\n", lp->line); 2912 } 2913 2914 static int 2915 check_archive(char *dest) 2916 { 2917 struct stat sb; 2918 2919 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) || 2920 sb.st_size < 10000) { 2921 bam_error(ARCHIVE_BAD, dest); 2922 (void) unlink(dest); 2923 return (BAM_ERROR); 2924 } 2925 2926 return (BAM_SUCCESS); 2927 } 2928 2929 static boolean_t 2930 is_be(char *root) 2931 { 2932 zfs_handle_t *zhp; 2933 libzfs_handle_t *hdl; 2934 be_node_list_t *be_nodes = NULL; 2935 be_node_list_t *cur_be; 2936 boolean_t be_exist = B_FALSE; 2937 char ds_path[ZFS_MAXNAMELEN]; 2938 2939 if (!is_zfs(root)) 2940 return (B_FALSE); 2941 /* 2942 * Get dataset for mountpoint 2943 */ 2944 if ((hdl = libzfs_init()) == NULL) 2945 return (B_FALSE); 2946 2947 if ((zhp = zfs_path_to_zhandle(hdl, root, 2948 ZFS_TYPE_FILESYSTEM)) == NULL) { 2949 libzfs_fini(hdl); 2950 return (B_FALSE); 2951 } 2952 2953 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path)); 2954 2955 /* 2956 * Check if the current dataset is BE 2957 */ 2958 if (be_list(NULL, &be_nodes) == BE_SUCCESS) { 2959 for (cur_be = be_nodes; cur_be != NULL; 2960 cur_be = cur_be->be_next_node) { 2961 2962 /* 2963 * Because we guarantee that cur_be->be_root_ds 2964 * is null-terminated by internal data structure, 2965 * we can safely use strcmp() 2966 */ 2967 if (strcmp(ds_path, cur_be->be_root_ds) == 0) { 2968 be_exist = B_TRUE; 2969 break; 2970 } 2971 } 2972 be_free_list(be_nodes); 2973 } 2974 zfs_close(zhp); 2975 libzfs_fini(hdl); 2976 2977 return (be_exist); 2978 } 2979 2980 /* 2981 * Returns 1 if mkiso is in the expected PATH, 0 otherwise 2982 */ 2983 static int 2984 is_mkisofs() 2985 { 2986 if (access(MKISOFS_PATH, X_OK) == 0) 2987 return (1); 2988 return (0); 2989 } 2990 2991 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames " 2992 2993 static int 2994 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list) 2995 { 2996 int ret; 2997 char cmdline[3 * PATH_MAX + 64]; 2998 filelist_t flist = {0}; 2999 const char *func = "create_sparc_archive()"; 3000 3001 if (access(bootblk, R_OK) == 1) { 3002 bam_error(BOOTBLK_FAIL, bootblk); 3003 return (BAM_ERROR); 3004 } 3005 3006 /* 3007 * Prepare mkisofs command line and execute it 3008 */ 3009 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" " 3010 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk, 3011 tempname, list); 3012 3013 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3014 3015 ret = exec_cmd(cmdline, &flist); 3016 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 3017 dump_errormsg(flist); 3018 goto out_err; 3019 } 3020 3021 filelist_free(&flist); 3022 3023 /* 3024 * Prepare dd command line to copy the bootblk on the new archive and 3025 * execute it 3026 */ 3027 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\"" 3028 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR, 3029 bootblk, tempname); 3030 3031 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3032 3033 ret = exec_cmd(cmdline, &flist); 3034 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) 3035 goto out_err; 3036 3037 filelist_free(&flist); 3038 3039 /* Did we get a valid archive ? */ 3040 if (check_archive(tempname) == BAM_ERROR) 3041 return (BAM_ERROR); 3042 3043 return (do_archive_copy(tempname, archive)); 3044 3045 out_err: 3046 filelist_free(&flist); 3047 bam_error(ARCHIVE_FAIL, cmdline); 3048 (void) unlink(tempname); 3049 return (BAM_ERROR); 3050 } 3051 3052 static unsigned int 3053 from_733(unsigned char *s) 3054 { 3055 int i; 3056 unsigned int ret = 0; 3057 3058 for (i = 0; i < 4; i++) 3059 ret |= s[i] << (8 * i); 3060 3061 return (ret); 3062 } 3063 3064 static void 3065 to_733(unsigned char *s, unsigned int val) 3066 { 3067 int i; 3068 3069 for (i = 0; i < 4; i++) 3070 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF; 3071 } 3072 3073 /* 3074 * Extends the current boot archive without recreating it from scratch 3075 */ 3076 static int 3077 extend_iso_archive(char *archive, char *tempname, char *update_dir) 3078 { 3079 int fd = -1, newfd = -1, ret, i; 3080 int next_session = 0, new_size = 0; 3081 char cmdline[3 * PATH_MAX + 64]; 3082 const char *func = "extend_iso_archive()"; 3083 filelist_t flist = {0}; 3084 struct iso_pdesc saved_desc[MAX_IVDs]; 3085 3086 fd = open(archive, O_RDWR); 3087 if (fd == -1) { 3088 if (bam_verbose) 3089 bam_error(OPEN_FAIL, archive, strerror(errno)); 3090 goto out_err; 3091 } 3092 3093 /* 3094 * A partial read is likely due to a corrupted file 3095 */ 3096 ret = pread64(fd, saved_desc, sizeof (saved_desc), 3097 VOLDESC_OFF * CD_BLOCK); 3098 if (ret != sizeof (saved_desc)) { 3099 if (bam_verbose) 3100 bam_error(READ_FAIL, archive, strerror(errno)); 3101 goto out_err; 3102 } 3103 3104 if (memcmp(saved_desc[0].type, "\1CD001", 6)) { 3105 if (bam_verbose) 3106 bam_error(SIGN_FAIL, archive); 3107 goto out_err; 3108 } 3109 3110 /* 3111 * Read primary descriptor and locate next_session offset (it should 3112 * point to the end of the archive) 3113 */ 3114 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16); 3115 3116 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \"" 3117 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive, 3118 MKISO_PARAMS, tempname, update_dir); 3119 3120 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3121 3122 ret = exec_cmd(cmdline, &flist); 3123 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 3124 if (bam_verbose) { 3125 bam_error(MULTI_FAIL, cmdline); 3126 dump_errormsg(flist); 3127 } 3128 goto out_flist_err; 3129 } 3130 filelist_free(&flist); 3131 3132 newfd = open(tempname, O_RDONLY); 3133 if (newfd == -1) { 3134 if (bam_verbose) 3135 bam_error(OPEN_FAIL, archive, strerror(errno)); 3136 goto out_err; 3137 } 3138 3139 ret = pread64(newfd, saved_desc, sizeof (saved_desc), 3140 VOLDESC_OFF * CD_BLOCK); 3141 if (ret != sizeof (saved_desc)) { 3142 if (bam_verbose) 3143 bam_error(READ_FAIL, archive, strerror(errno)); 3144 goto out_err; 3145 } 3146 3147 if (memcmp(saved_desc[0].type, "\1CD001", 6)) { 3148 if (bam_verbose) 3149 bam_error(SIGN_FAIL, archive); 3150 goto out_err; 3151 } 3152 3153 new_size = from_733(saved_desc[0].volume_space_size) + next_session; 3154 to_733(saved_desc[0].volume_space_size, new_size); 3155 3156 for (i = 1; i < MAX_IVDs; i++) { 3157 if (saved_desc[i].type[0] == (unsigned char)255) 3158 break; 3159 if (memcmp(saved_desc[i].id, "CD001", 5)) 3160 break; 3161 3162 if (bam_verbose) 3163 bam_print("%s: Updating descriptor entry [%d]\n", func, 3164 i); 3165 3166 to_733(saved_desc[i].volume_space_size, new_size); 3167 } 3168 3169 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK); 3170 if (ret != DVD_BLOCK) { 3171 if (bam_verbose) 3172 bam_error(WRITE_FAIL, archive, strerror(errno)); 3173 goto out_err; 3174 } 3175 (void) close(newfd); 3176 newfd = -1; 3177 3178 ret = fsync(fd); 3179 if (ret != 0) 3180 sync(); 3181 3182 ret = close(fd); 3183 if (ret != 0) { 3184 if (bam_verbose) 3185 bam_error(CLOSE_FAIL, archive, strerror(errno)); 3186 return (BAM_ERROR); 3187 } 3188 fd = -1; 3189 3190 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k " 3191 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive, 3192 (next_session/16)); 3193 3194 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3195 3196 ret = exec_cmd(cmdline, &flist); 3197 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 3198 if (bam_verbose) 3199 bam_error(MULTI_FAIL, cmdline); 3200 goto out_flist_err; 3201 } 3202 filelist_free(&flist); 3203 3204 (void) unlink(tempname); 3205 3206 if (flushfs(bam_root) != 0) 3207 sync(); 3208 3209 if (bam_verbose) 3210 bam_print("boot archive updated successfully\n"); 3211 3212 return (BAM_SUCCESS); 3213 3214 out_flist_err: 3215 filelist_free(&flist); 3216 out_err: 3217 if (fd != -1) 3218 (void) close(fd); 3219 if (newfd != -1) 3220 (void) close(newfd); 3221 return (BAM_ERROR); 3222 } 3223 3224 static int 3225 create_x86_archive(char *archive, char *tempname, char *update_dir) 3226 { 3227 int ret; 3228 char cmdline[3 * PATH_MAX + 64]; 3229 filelist_t flist = {0}; 3230 const char *func = "create_x86_archive()"; 3231 3232 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" " 3233 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir); 3234 3235 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3236 3237 ret = exec_cmd(cmdline, &flist); 3238 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 3239 bam_error(ARCHIVE_FAIL, cmdline); 3240 dump_errormsg(flist); 3241 filelist_free(&flist); 3242 (void) unlink(tempname); 3243 return (BAM_ERROR); 3244 } 3245 3246 filelist_free(&flist); 3247 3248 if (check_archive(tempname) == BAM_ERROR) 3249 return (BAM_ERROR); 3250 3251 return (do_archive_copy(tempname, archive)); 3252 } 3253 3254 static int 3255 mkisofs_archive(char *root, int what) 3256 { 3257 int ret; 3258 char temp[PATH_MAX]; 3259 char bootblk[PATH_MAX]; 3260 char boot_archive[PATH_MAX]; 3261 3262 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 3263 ret = snprintf(temp, sizeof (temp), 3264 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX, 3265 get_machine(), getpid()); 3266 else 3267 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d", 3268 root, ARCHIVE_PREFIX, get_machine(), getpid()); 3269 3270 if (ret >= sizeof (temp)) 3271 goto out_path_err; 3272 3273 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 3274 ret = snprintf(boot_archive, sizeof (boot_archive), 3275 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(), 3276 ARCHIVE_SUFFIX); 3277 else 3278 ret = snprintf(boot_archive, sizeof (boot_archive), 3279 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), 3280 ARCHIVE_SUFFIX); 3281 3282 if (ret >= sizeof (boot_archive)) 3283 goto out_path_err; 3284 3285 bam_print("updating %s\n", boot_archive); 3286 3287 if (is_flag_on(IS_SPARC_TARGET)) { 3288 ret = snprintf(bootblk, sizeof (bootblk), 3289 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine()); 3290 if (ret >= sizeof (bootblk)) 3291 goto out_path_err; 3292 3293 ret = create_sparc_archive(boot_archive, temp, bootblk, 3294 get_cachedir(what)); 3295 } else { 3296 if (!is_dir_flag_on(what, NO_MULTI)) { 3297 if (bam_verbose) 3298 bam_print("Attempting to extend x86 archive: " 3299 "%s\n", boot_archive); 3300 3301 ret = extend_iso_archive(boot_archive, temp, 3302 get_updatedir(what)); 3303 if (ret == BAM_SUCCESS) { 3304 if (bam_verbose) 3305 bam_print("Successfully extended %s\n", 3306 boot_archive); 3307 3308 (void) rmdir_r(get_updatedir(what)); 3309 return (BAM_SUCCESS); 3310 } 3311 } 3312 /* 3313 * The boot archive will be recreated from scratch. We get here 3314 * if at least one of these conditions is true: 3315 * - bootadm was called without the -e switch 3316 * - the archive (or the archive cache) doesn't exist 3317 * - archive size is bigger than BA_SIZE_MAX 3318 * - more than COUNT_MAX files need to be updated 3319 * - an error occourred either populating the /updates directory 3320 * or extend_iso_archive() failed 3321 */ 3322 if (bam_verbose) 3323 bam_print("Unable to extend %s... rebuilding archive\n", 3324 boot_archive); 3325 3326 if (get_updatedir(what)[0] != '\0') 3327 (void) rmdir_r(get_updatedir(what)); 3328 3329 3330 ret = create_x86_archive(boot_archive, temp, 3331 get_cachedir(what)); 3332 } 3333 3334 if (ret == BAM_SUCCESS && bam_verbose) 3335 bam_print("Successfully created %s\n", boot_archive); 3336 3337 return (ret); 3338 3339 out_path_err: 3340 bam_error(PATH_TOO_LONG, root); 3341 return (BAM_ERROR); 3342 } 3343 3344 static error_t 3345 create_ramdisk(char *root) 3346 { 3347 char *cmdline, path[PATH_MAX]; 3348 size_t len; 3349 struct stat sb; 3350 int ret, what, status = BAM_SUCCESS; 3351 3352 /* If there is mkisofs, use it to create the required archives */ 3353 if (is_mkisofs()) { 3354 for (what = FILE32; what < CACHEDIR_NUM; what++) { 3355 if (has_cachedir(what) && is_dir_flag_on(what, 3356 NEED_UPDATE)) { 3357 ret = mkisofs_archive(root, what); 3358 if (ret != 0) 3359 status = BAM_ERROR; 3360 } 3361 } 3362 return (status); 3363 } 3364 3365 /* 3366 * Else setup command args for create_ramdisk.ksh for the UFS archives 3367 */ 3368 if (bam_verbose) 3369 bam_print("mkisofs not found, creating UFS archive\n"); 3370 3371 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 3372 if (stat(path, &sb) != 0) { 3373 bam_error(ARCH_EXEC_MISS, path, strerror(errno)); 3374 return (BAM_ERROR); 3375 } 3376 3377 if (is_safe_exec(path) == BAM_ERROR) 3378 return (BAM_ERROR); 3379 3380 len = strlen(path) + strlen(root) + 10; /* room for space + -R */ 3381 if (bam_alt_platform) 3382 len += strlen(bam_platform) + strlen("-p "); 3383 cmdline = s_calloc(1, len); 3384 3385 if (bam_alt_platform) { 3386 assert(strlen(root) > 1); 3387 (void) snprintf(cmdline, len, "%s -p %s -R %s", 3388 path, bam_platform, root); 3389 /* chop off / at the end */ 3390 cmdline[strlen(cmdline) - 1] = '\0'; 3391 } else if (strlen(root) > 1) { 3392 (void) snprintf(cmdline, len, "%s -R %s", path, root); 3393 /* chop off / at the end */ 3394 cmdline[strlen(cmdline) - 1] = '\0'; 3395 } else 3396 (void) snprintf(cmdline, len, "%s", path); 3397 3398 if (exec_cmd(cmdline, NULL) != 0) { 3399 bam_error(ARCHIVE_FAIL, cmdline); 3400 free(cmdline); 3401 return (BAM_ERROR); 3402 } 3403 free(cmdline); 3404 /* 3405 * The existence of the expected archives used to be 3406 * verified here. This check is done in create_ramdisk as 3407 * it needs to be in sync with the altroot operated upon. 3408 */ 3409 return (BAM_SUCCESS); 3410 } 3411 3412 /* 3413 * Checks if target filesystem is on a ramdisk 3414 * 1 - is miniroot 3415 * 0 - is not 3416 * When in doubt assume it is not a ramdisk. 3417 */ 3418 static int 3419 is_ramdisk(char *root) 3420 { 3421 struct extmnttab mnt; 3422 FILE *fp; 3423 int found; 3424 char mntpt[PATH_MAX]; 3425 char *cp; 3426 3427 /* 3428 * There are 3 situations where creating archive is 3429 * of dubious value: 3430 * - create boot_archive on a lofi-mounted boot_archive 3431 * - create it on a ramdisk which is the root filesystem 3432 * - create it on a ramdisk mounted somewhere else 3433 * The first is not easy to detect and checking for it is not 3434 * worth it. 3435 * The other two conditions are handled here 3436 */ 3437 fp = fopen(MNTTAB, "r"); 3438 if (fp == NULL) { 3439 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 3440 return (0); 3441 } 3442 3443 resetmnttab(fp); 3444 3445 /* 3446 * Remove any trailing / from the mount point 3447 */ 3448 (void) strlcpy(mntpt, root, sizeof (mntpt)); 3449 if (strcmp(root, "/") != 0) { 3450 cp = mntpt + strlen(mntpt) - 1; 3451 if (*cp == '/') 3452 *cp = '\0'; 3453 } 3454 found = 0; 3455 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 3456 if (strcmp(mnt.mnt_mountp, mntpt) == 0) { 3457 found = 1; 3458 break; 3459 } 3460 } 3461 3462 if (!found) { 3463 if (bam_verbose) 3464 bam_error(NOT_IN_MNTTAB, mntpt); 3465 (void) fclose(fp); 3466 return (0); 3467 } 3468 3469 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL, 3470 strlen(RAMDISK_SPECIAL)) == 0) { 3471 if (bam_verbose) 3472 bam_error(IS_RAMDISK, bam_root); 3473 (void) fclose(fp); 3474 return (1); 3475 } 3476 3477 (void) fclose(fp); 3478 3479 return (0); 3480 } 3481 3482 static int 3483 is_boot_archive(char *root) 3484 { 3485 char path[PATH_MAX]; 3486 struct stat sb; 3487 int error; 3488 const char *fcn = "is_boot_archive()"; 3489 3490 /* 3491 * We can't create an archive without the create_ramdisk script 3492 */ 3493 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 3494 error = stat(path, &sb); 3495 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1); 3496 if (error == -1) { 3497 if (bam_verbose) 3498 bam_print(FILE_MISS, path); 3499 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root)); 3500 return (0); 3501 } 3502 3503 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root)); 3504 return (1); 3505 } 3506 3507 /* 3508 * Need to call this for anything that operates on the GRUB menu 3509 * In the x86 live upgrade case the directory /boot/grub may be present 3510 * even on pre-newboot BEs. The authoritative way to check for a GRUB target 3511 * is to check for the presence of the stage2 binary which is present 3512 * only on GRUB targets (even on x86 boot partitions). Checking for the 3513 * presence of the multiboot binary is not correct as it is not present 3514 * on x86 boot partitions. 3515 */ 3516 int 3517 is_grub(const char *root) 3518 { 3519 char path[PATH_MAX]; 3520 struct stat sb; 3521 const char *fcn = "is_grub()"; 3522 3523 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2); 3524 if (stat(path, &sb) == -1) { 3525 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path)); 3526 return (0); 3527 } 3528 3529 return (1); 3530 } 3531 3532 static int 3533 is_zfs(char *root) 3534 { 3535 struct statvfs vfs; 3536 int ret; 3537 const char *fcn = "is_zfs()"; 3538 3539 ret = statvfs(root, &vfs); 3540 INJECT_ERROR1("STATVFS_ZFS", ret = 1); 3541 if (ret != 0) { 3542 bam_error(STATVFS_FAIL, root, strerror(errno)); 3543 return (0); 3544 } 3545 3546 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) { 3547 BAM_DPRINTF((D_IS_ZFS, fcn, root)); 3548 return (1); 3549 } else { 3550 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root)); 3551 return (0); 3552 } 3553 } 3554 3555 static int 3556 is_ufs(char *root) 3557 { 3558 struct statvfs vfs; 3559 int ret; 3560 const char *fcn = "is_ufs()"; 3561 3562 ret = statvfs(root, &vfs); 3563 INJECT_ERROR1("STATVFS_UFS", ret = 1); 3564 if (ret != 0) { 3565 bam_error(STATVFS_FAIL, root, strerror(errno)); 3566 return (0); 3567 } 3568 3569 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) { 3570 BAM_DPRINTF((D_IS_UFS, fcn, root)); 3571 return (1); 3572 } else { 3573 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root)); 3574 return (0); 3575 } 3576 } 3577 3578 static int 3579 is_pcfs(char *root) 3580 { 3581 struct statvfs vfs; 3582 int ret; 3583 const char *fcn = "is_pcfs()"; 3584 3585 ret = statvfs(root, &vfs); 3586 INJECT_ERROR1("STATVFS_PCFS", ret = 1); 3587 if (ret != 0) { 3588 bam_error(STATVFS_FAIL, root, strerror(errno)); 3589 return (0); 3590 } 3591 3592 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) { 3593 BAM_DPRINTF((D_IS_PCFS, fcn, root)); 3594 return (1); 3595 } else { 3596 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root)); 3597 return (0); 3598 } 3599 } 3600 3601 static int 3602 is_readonly(char *root) 3603 { 3604 int fd; 3605 int error; 3606 char testfile[PATH_MAX]; 3607 const char *fcn = "is_readonly()"; 3608 3609 /* 3610 * Using statvfs() to check for a read-only filesystem is not 3611 * reliable. The only way to reliably test is to attempt to 3612 * create a file 3613 */ 3614 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d", 3615 root, BOOTADM_RDONLY_TEST, getpid()); 3616 3617 (void) unlink(testfile); 3618 3619 errno = 0; 3620 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644); 3621 error = errno; 3622 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES); 3623 if (fd == -1 && error == EROFS) { 3624 BAM_DPRINTF((D_RDONLY_FS, fcn, root)); 3625 return (1); 3626 } else if (fd == -1) { 3627 bam_error(RDONLY_TEST_ERROR, root, strerror(error)); 3628 } 3629 3630 (void) close(fd); 3631 (void) unlink(testfile); 3632 3633 BAM_DPRINTF((D_RDWR_FS, fcn, root)); 3634 return (0); 3635 } 3636 3637 static error_t 3638 update_archive(char *root, char *opt) 3639 { 3640 error_t ret; 3641 3642 assert(root); 3643 assert(opt == NULL); 3644 3645 init_walk_args(); 3646 (void) umask(022); 3647 3648 /* 3649 * Never update non-BE root in update_all 3650 */ 3651 if (!is_be(root) && bam_update_all) 3652 return (BAM_SUCCESS); 3653 /* 3654 * root must belong to a boot archive based OS, 3655 */ 3656 if (!is_boot_archive(root)) { 3657 /* 3658 * Emit message only if not in context of update_all. 3659 * If in update_all, emit only if verbose flag is set. 3660 */ 3661 if (!bam_update_all || bam_verbose) 3662 bam_print(NOT_ARCHIVE_BOOT, root); 3663 return (BAM_ERROR); 3664 } 3665 3666 /* 3667 * If smf check is requested when / is writable (can happen 3668 * on first reboot following an upgrade because service 3669 * dependency is messed up), skip the check. 3670 */ 3671 if (bam_smf_check && !bam_root_readonly && !is_zfs(root)) 3672 return (BAM_SUCCESS); 3673 3674 /* 3675 * Don't generate archive on ramdisk. 3676 */ 3677 if (is_ramdisk(root)) 3678 return (BAM_SUCCESS); 3679 3680 /* 3681 * root must be writable. This check applies to alternate 3682 * root (-R option); bam_root_readonly applies to '/' only. 3683 * The behaviour translates into being the one of a 'check'. 3684 */ 3685 if (!bam_smf_check && !bam_check && is_readonly(root)) { 3686 set_flag(RDONLY_FSCHK); 3687 bam_check = 1; 3688 } 3689 3690 /* 3691 * Now check if an update is really needed. 3692 */ 3693 ret = update_required(root); 3694 3695 /* 3696 * The check command (-n) is *not* a dry run. 3697 * It only checks if the archive is in sync. 3698 * A readonly filesystem has to be considered an error only if an update 3699 * is required. 3700 */ 3701 if (bam_nowrite()) { 3702 if (is_flag_on(RDONLY_FSCHK)) { 3703 bam_check = bam_saved_check; 3704 if (ret > 0) 3705 bam_error(RDONLY_FS, root); 3706 if (bam_update_all) 3707 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS); 3708 } 3709 3710 bam_exit((ret != 0) ? 1 : 0); 3711 } 3712 3713 if (ret == 1) { 3714 /* create the ramdisk */ 3715 ret = create_ramdisk(root); 3716 } 3717 3718 /* 3719 * if the archive is updated, save the new stat data and update the 3720 * timestamp file 3721 */ 3722 if (ret == 0 && walk_arg.new_nvlp != NULL) { 3723 savenew(root); 3724 update_timestamp(root); 3725 } 3726 3727 clear_walk_args(); 3728 3729 return (ret); 3730 } 3731 3732 static char * 3733 find_root_pool() 3734 { 3735 char *special = get_special("/"); 3736 char *p; 3737 3738 if (special == NULL) 3739 return (NULL); 3740 3741 if (*special == '/') { 3742 free(special); 3743 return (NULL); 3744 } 3745 3746 if ((p = strchr(special, '/')) != NULL) 3747 *p = '\0'; 3748 3749 return (special); 3750 } 3751 3752 static error_t 3753 synchronize_BE_menu(void) 3754 { 3755 struct stat sb; 3756 char cmdline[PATH_MAX]; 3757 char cksum_line[PATH_MAX]; 3758 filelist_t flist = {0}; 3759 char *old_cksum_str; 3760 char *old_size_str; 3761 char *old_file; 3762 char *curr_cksum_str; 3763 char *curr_size_str; 3764 char *curr_file; 3765 char *pool = NULL; 3766 char *mntpt = NULL; 3767 zfs_mnted_t mnted; 3768 FILE *cfp; 3769 int found; 3770 int ret; 3771 const char *fcn = "synchronize_BE_menu()"; 3772 3773 BAM_DPRINTF((D_FUNC_ENTRY0, fcn)); 3774 3775 /* Check if findroot enabled LU BE */ 3776 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) { 3777 BAM_DPRINTF((D_NOT_LU_BE, fcn)); 3778 return (BAM_SUCCESS); 3779 } 3780 3781 if (stat(LU_MENU_CKSUM, &sb) != 0) { 3782 BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM)); 3783 goto menu_sync; 3784 } 3785 3786 cfp = fopen(LU_MENU_CKSUM, "r"); 3787 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL); 3788 if (cfp == NULL) { 3789 bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM); 3790 goto menu_sync; 3791 } 3792 BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM)); 3793 3794 found = 0; 3795 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) { 3796 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1); 3797 if (found) { 3798 bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM); 3799 (void) fclose(cfp); 3800 goto menu_sync; 3801 } 3802 found = 1; 3803 } 3804 BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM)); 3805 3806 3807 old_cksum_str = strtok(cksum_line, " \t"); 3808 old_size_str = strtok(NULL, " \t"); 3809 old_file = strtok(NULL, " \t"); 3810 3811 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL); 3812 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL); 3813 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL); 3814 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) { 3815 bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM); 3816 goto menu_sync; 3817 } 3818 BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM)); 3819 3820 /* Get checksum of current menu */ 3821 pool = find_root_pool(); 3822 if (pool) { 3823 mntpt = mount_top_dataset(pool, &mnted); 3824 if (mntpt == NULL) { 3825 bam_error(FAIL_MNT_TOP_DATASET, pool); 3826 free(pool); 3827 return (BAM_ERROR); 3828 } 3829 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s", 3830 CKSUM, mntpt, GRUB_MENU); 3831 } else { 3832 (void) snprintf(cmdline, sizeof (cmdline), "%s %s", 3833 CKSUM, GRUB_MENU); 3834 } 3835 ret = exec_cmd(cmdline, &flist); 3836 if (pool) { 3837 (void) umount_top_dataset(pool, mnted, mntpt); 3838 free(pool); 3839 } 3840 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1); 3841 if (ret != 0) { 3842 bam_error(MENU_CKSUM_FAIL); 3843 return (BAM_ERROR); 3844 } 3845 BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn)); 3846 3847 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL); 3848 if ((flist.head == NULL) || (flist.head != flist.tail)) { 3849 bam_error(BAD_CKSUM); 3850 filelist_free(&flist); 3851 return (BAM_ERROR); 3852 } 3853 BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn)); 3854 3855 curr_cksum_str = strtok(flist.head->line, " \t"); 3856 curr_size_str = strtok(NULL, " \t"); 3857 curr_file = strtok(NULL, " \t"); 3858 3859 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL); 3860 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL); 3861 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL); 3862 if (curr_cksum_str == NULL || curr_size_str == NULL || 3863 curr_file == NULL) { 3864 bam_error(BAD_CKSUM_PARSE); 3865 filelist_free(&flist); 3866 return (BAM_ERROR); 3867 } 3868 BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn)); 3869 3870 if (strcmp(old_cksum_str, curr_cksum_str) == 0 && 3871 strcmp(old_size_str, curr_size_str) == 0 && 3872 strcmp(old_file, curr_file) == 0) { 3873 filelist_free(&flist); 3874 BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn)); 3875 return (BAM_SUCCESS); 3876 } 3877 3878 filelist_free(&flist); 3879 3880 /* cksum doesn't match - the menu has changed */ 3881 BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn)); 3882 3883 menu_sync: 3884 bam_print(PROP_GRUB_MENU); 3885 3886 (void) snprintf(cmdline, sizeof (cmdline), 3887 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'", 3888 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU); 3889 ret = exec_cmd(cmdline, NULL); 3890 INJECT_ERROR1("PROPAGATE_MENU", ret = 1); 3891 if (ret != 0) { 3892 bam_error(MENU_PROP_FAIL); 3893 return (BAM_ERROR); 3894 } 3895 BAM_DPRINTF((D_PROPAGATED_MENU, fcn)); 3896 3897 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null", 3898 GRUB_MENU, GRUB_BACKUP_MENU); 3899 ret = exec_cmd(cmdline, NULL); 3900 INJECT_ERROR1("CREATE_BACKUP", ret = 1); 3901 if (ret != 0) { 3902 bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU); 3903 return (BAM_ERROR); 3904 } 3905 BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU)); 3906 3907 (void) snprintf(cmdline, sizeof (cmdline), 3908 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'", 3909 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU); 3910 ret = exec_cmd(cmdline, NULL); 3911 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1); 3912 if (ret != 0) { 3913 bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU); 3914 return (BAM_ERROR); 3915 } 3916 BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU)); 3917 3918 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s", 3919 CKSUM, GRUB_MENU, LU_MENU_CKSUM); 3920 ret = exec_cmd(cmdline, NULL); 3921 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1); 3922 if (ret != 0) { 3923 bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM); 3924 return (BAM_ERROR); 3925 } 3926 BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM)); 3927 3928 (void) snprintf(cmdline, sizeof (cmdline), 3929 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'", 3930 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM); 3931 ret = exec_cmd(cmdline, NULL); 3932 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1); 3933 if (ret != 0) { 3934 bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM); 3935 return (BAM_ERROR); 3936 } 3937 BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM)); 3938 3939 return (BAM_SUCCESS); 3940 } 3941 3942 static error_t 3943 update_all(char *root, char *opt) 3944 { 3945 struct extmnttab mnt; 3946 struct stat sb; 3947 FILE *fp; 3948 char multibt[PATH_MAX]; 3949 char creatram[PATH_MAX]; 3950 error_t ret = BAM_SUCCESS; 3951 3952 assert(root); 3953 assert(opt == NULL); 3954 3955 if (bam_rootlen != 1 || *root != '/') { 3956 elide_trailing_slash(root, multibt, sizeof (multibt)); 3957 bam_error(ALT_ROOT_INVALID, multibt); 3958 return (BAM_ERROR); 3959 } 3960 3961 /* 3962 * First update archive for current root 3963 */ 3964 if (update_archive(root, opt) != BAM_SUCCESS) 3965 ret = BAM_ERROR; 3966 3967 if (ret == BAM_ERROR) 3968 goto out; 3969 3970 /* 3971 * Now walk the mount table, performing archive update 3972 * for all mounted Newboot root filesystems 3973 */ 3974 fp = fopen(MNTTAB, "r"); 3975 if (fp == NULL) { 3976 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 3977 ret = BAM_ERROR; 3978 goto out; 3979 } 3980 3981 resetmnttab(fp); 3982 3983 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 3984 if (mnt.mnt_special == NULL) 3985 continue; 3986 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) && 3987 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)) 3988 continue; 3989 if (strcmp(mnt.mnt_mountp, "/") == 0) 3990 continue; 3991 3992 (void) snprintf(creatram, sizeof (creatram), "%s/%s", 3993 mnt.mnt_mountp, CREATE_RAMDISK); 3994 3995 if (stat(creatram, &sb) == -1) 3996 continue; 3997 3998 /* 3999 * We put a trailing slash to be consistent with root = "/" 4000 * case, such that we don't have to print // in some cases. 4001 */ 4002 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 4003 mnt.mnt_mountp); 4004 bam_rootlen = strlen(rootbuf); 4005 4006 /* 4007 * It's possible that other mounts may be an alternate boot 4008 * architecture, so check it again. 4009 */ 4010 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) || 4011 (update_archive(rootbuf, opt) != BAM_SUCCESS)) 4012 ret = BAM_ERROR; 4013 } 4014 4015 (void) fclose(fp); 4016 4017 out: 4018 /* 4019 * We no longer use biosdev for Live Upgrade. Hence 4020 * there is no need to defer (to shutdown time) any fdisk 4021 * updates 4022 */ 4023 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) { 4024 bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target); 4025 } 4026 4027 /* 4028 * If user has updated menu in current BE, propagate the 4029 * updates to all BEs. 4030 */ 4031 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS) 4032 ret = BAM_ERROR; 4033 4034 return (ret); 4035 } 4036 4037 static void 4038 append_line(menu_t *mp, line_t *lp) 4039 { 4040 if (mp->start == NULL) { 4041 mp->start = lp; 4042 } else { 4043 mp->end->next = lp; 4044 lp->prev = mp->end; 4045 } 4046 mp->end = lp; 4047 } 4048 4049 void 4050 unlink_line(menu_t *mp, line_t *lp) 4051 { 4052 /* unlink from list */ 4053 if (lp->prev) 4054 lp->prev->next = lp->next; 4055 else 4056 mp->start = lp->next; 4057 if (lp->next) 4058 lp->next->prev = lp->prev; 4059 else 4060 mp->end = lp->prev; 4061 } 4062 4063 static entry_t * 4064 boot_entry_new(menu_t *mp, line_t *start, line_t *end) 4065 { 4066 entry_t *ent, *prev; 4067 const char *fcn = "boot_entry_new()"; 4068 4069 assert(mp); 4070 assert(start); 4071 assert(end); 4072 4073 ent = s_calloc(1, sizeof (entry_t)); 4074 BAM_DPRINTF((D_ENTRY_NEW, fcn)); 4075 ent->start = start; 4076 ent->end = end; 4077 4078 if (mp->entries == NULL) { 4079 mp->entries = ent; 4080 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn)); 4081 return (ent); 4082 } 4083 4084 prev = mp->entries; 4085 while (prev->next) 4086 prev = prev->next; 4087 prev->next = ent; 4088 ent->prev = prev; 4089 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn)); 4090 return (ent); 4091 } 4092 4093 static void 4094 boot_entry_addline(entry_t *ent, line_t *lp) 4095 { 4096 if (ent) 4097 ent->end = lp; 4098 } 4099 4100 /* 4101 * Check whether cmd matches the one indexed by which, and whether arg matches 4102 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the 4103 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using 4104 * strstr(), so it can be a partial match. 4105 */ 4106 static int 4107 check_cmd(const char *cmd, const int which, const char *arg, const char *str) 4108 { 4109 int ret; 4110 const char *fcn = "check_cmd()"; 4111 4112 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str)); 4113 4114 if (cmd != NULL) { 4115 if ((strcmp(cmd, menu_cmds[which]) != 0) && 4116 (strcmp(cmd, menu_cmds[which + 1]) != 0)) { 4117 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH, 4118 fcn, cmd, menu_cmds[which])); 4119 return (0); 4120 } 4121 ret = (strstr(arg, str) != NULL); 4122 } else 4123 ret = 0; 4124 4125 if (ret) { 4126 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4127 } else { 4128 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 4129 } 4130 4131 return (ret); 4132 } 4133 4134 static error_t 4135 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum) 4136 { 4137 const char *fcn = "kernel_parser()"; 4138 4139 assert(entry); 4140 assert(cmd); 4141 assert(arg); 4142 4143 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 && 4144 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) { 4145 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd)); 4146 return (BAM_ERROR); 4147 } 4148 4149 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) { 4150 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg)); 4151 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT; 4152 } else if (strncmp(arg, DIRECT_BOOT_KERNEL, 4153 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) { 4154 BAM_DPRINTF((D_SET_DBOOT, fcn, arg)); 4155 entry->flags |= BAM_ENTRY_DBOOT; 4156 } else if (strncmp(arg, DIRECT_BOOT_64, 4157 sizeof (DIRECT_BOOT_64) - 1) == 0) { 4158 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg)); 4159 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT; 4160 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL, 4161 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) { 4162 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg)); 4163 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE; 4164 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32, 4165 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) { 4166 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg)); 4167 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE 4168 | BAM_ENTRY_32BIT; 4169 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64, 4170 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) { 4171 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg)); 4172 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE 4173 | BAM_ENTRY_64BIT; 4174 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) { 4175 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg)); 4176 entry->flags |= BAM_ENTRY_MULTIBOOT; 4177 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE, 4178 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) { 4179 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg)); 4180 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE; 4181 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) { 4182 BAM_DPRINTF((D_SET_HV, fcn, arg)); 4183 entry->flags |= BAM_ENTRY_HV; 4184 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) { 4185 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg)); 4186 return (BAM_ERROR); 4187 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 && 4188 strstr(arg, UNIX_SPACE)) { 4189 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT; 4190 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 && 4191 strstr(arg, AMD_UNIX_SPACE)) { 4192 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT; 4193 } else { 4194 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg)); 4195 bam_error(UNKNOWN_KERNEL_LINE, linenum); 4196 return (BAM_ERROR); 4197 } 4198 4199 return (BAM_SUCCESS); 4200 } 4201 4202 static error_t 4203 module_parser(entry_t *entry, char *cmd, char *arg, int linenum) 4204 { 4205 const char *fcn = "module_parser()"; 4206 4207 assert(entry); 4208 assert(cmd); 4209 assert(arg); 4210 4211 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 && 4212 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) { 4213 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd)); 4214 return (BAM_ERROR); 4215 } 4216 4217 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 || 4218 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 || 4219 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 || 4220 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 || 4221 strcmp(arg, FAILSAFE_ARCHIVE) == 0 || 4222 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 || 4223 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 || 4224 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 || 4225 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) { 4226 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg)); 4227 return (BAM_SUCCESS); 4228 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) && 4229 !(entry->flags & BAM_ENTRY_LU)) { 4230 /* don't emit warning for hand entries */ 4231 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg)); 4232 return (BAM_ERROR); 4233 } else { 4234 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg)); 4235 bam_error(UNKNOWN_MODULE_LINE, linenum); 4236 return (BAM_ERROR); 4237 } 4238 } 4239 4240 /* 4241 * A line in menu.lst looks like 4242 * [ ]*<cmd>[ \t=]*<arg>* 4243 */ 4244 static void 4245 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum) 4246 { 4247 /* 4248 * save state across calls. This is so that 4249 * header gets the right entry# after title has 4250 * been processed 4251 */ 4252 static line_t *prev = NULL; 4253 static entry_t *curr_ent = NULL; 4254 static int in_liveupgrade = 0; 4255 static int is_libbe_ent = 0; 4256 4257 line_t *lp; 4258 char *cmd, *sep, *arg; 4259 char save, *cp, *line; 4260 menu_flag_t flag = BAM_INVALID; 4261 const char *fcn = "line_parser()"; 4262 4263 if (str == NULL) { 4264 return; 4265 } 4266 4267 /* 4268 * First save a copy of the entire line. 4269 * We use this later to set the line field. 4270 */ 4271 line = s_strdup(str); 4272 4273 /* Eat up leading whitespace */ 4274 while (*str == ' ' || *str == '\t') 4275 str++; 4276 4277 if (*str == '#') { /* comment */ 4278 cmd = s_strdup("#"); 4279 sep = NULL; 4280 arg = s_strdup(str + 1); 4281 flag = BAM_COMMENT; 4282 if (strstr(arg, BAM_LU_HDR) != NULL) { 4283 in_liveupgrade = 1; 4284 } else if (strstr(arg, BAM_LU_FTR) != NULL) { 4285 in_liveupgrade = 0; 4286 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) { 4287 is_libbe_ent = 1; 4288 } 4289 } else if (*str == '\0') { /* blank line */ 4290 cmd = sep = arg = NULL; 4291 flag = BAM_EMPTY; 4292 } else { 4293 /* 4294 * '=' is not a documented separator in grub syntax. 4295 * However various development bits use '=' as a 4296 * separator. In addition, external users also 4297 * use = as a separator. So we will allow that usage. 4298 */ 4299 cp = str; 4300 while (*str != ' ' && *str != '\t' && *str != '=') { 4301 if (*str == '\0') { 4302 cmd = s_strdup(cp); 4303 sep = arg = NULL; 4304 break; 4305 } 4306 str++; 4307 } 4308 4309 if (*str != '\0') { 4310 save = *str; 4311 *str = '\0'; 4312 cmd = s_strdup(cp); 4313 *str = save; 4314 4315 str++; 4316 save = *str; 4317 *str = '\0'; 4318 sep = s_strdup(str - 1); 4319 *str = save; 4320 4321 while (*str == ' ' || *str == '\t') 4322 str++; 4323 if (*str == '\0') 4324 arg = NULL; 4325 else 4326 arg = s_strdup(str); 4327 } 4328 } 4329 4330 lp = s_calloc(1, sizeof (line_t)); 4331 4332 lp->cmd = cmd; 4333 lp->sep = sep; 4334 lp->arg = arg; 4335 lp->line = line; 4336 lp->lineNum = ++(*lineNum); 4337 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) { 4338 lp->entryNum = ++(*entryNum); 4339 lp->flags = BAM_TITLE; 4340 if (prev && prev->flags == BAM_COMMENT && 4341 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 4342 prev->entryNum = lp->entryNum; 4343 curr_ent = boot_entry_new(mp, prev, lp); 4344 curr_ent->flags |= BAM_ENTRY_BOOTADM; 4345 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg)); 4346 } else { 4347 curr_ent = boot_entry_new(mp, lp, lp); 4348 if (in_liveupgrade) { 4349 curr_ent->flags |= BAM_ENTRY_LU; 4350 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg)); 4351 } 4352 } 4353 curr_ent->entryNum = *entryNum; 4354 } else if (flag != BAM_INVALID) { 4355 /* 4356 * For header comments, the entry# is "fixed up" 4357 * by the subsequent title 4358 */ 4359 lp->entryNum = *entryNum; 4360 lp->flags = flag; 4361 } else { 4362 lp->entryNum = *entryNum; 4363 4364 if (*entryNum == ENTRY_INIT) { 4365 lp->flags = BAM_GLOBAL; 4366 } else { 4367 lp->flags = BAM_ENTRY; 4368 4369 if (cmd && arg) { 4370 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) { 4371 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg)); 4372 curr_ent->flags |= BAM_ENTRY_ROOT; 4373 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD]) 4374 == 0) { 4375 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn, 4376 arg)); 4377 curr_ent->flags |= BAM_ENTRY_FINDROOT; 4378 } else if (strcmp(cmd, 4379 menu_cmds[CHAINLOADER_CMD]) == 0) { 4380 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn, 4381 arg)); 4382 curr_ent->flags |= 4383 BAM_ENTRY_CHAINLOADER; 4384 } else if (kernel_parser(curr_ent, cmd, arg, 4385 lp->lineNum) != BAM_SUCCESS) { 4386 (void) module_parser(curr_ent, cmd, 4387 arg, lp->lineNum); 4388 } 4389 } 4390 } 4391 } 4392 4393 /* record default, old default, and entry line ranges */ 4394 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL && 4395 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) { 4396 mp->curdefault = lp; 4397 } else if (lp->flags == BAM_COMMENT && 4398 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) { 4399 mp->olddefault = lp; 4400 } else if (lp->flags == BAM_COMMENT && 4401 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) { 4402 mp->old_rc_default = lp; 4403 } else if (lp->flags == BAM_ENTRY || 4404 (lp->flags == BAM_COMMENT && 4405 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) { 4406 if (is_libbe_ent) { 4407 curr_ent->flags |= BAM_ENTRY_LIBBE; 4408 is_libbe_ent = 0; 4409 } 4410 4411 boot_entry_addline(curr_ent, lp); 4412 } 4413 append_line(mp, lp); 4414 4415 prev = lp; 4416 } 4417 4418 void 4419 update_numbering(menu_t *mp) 4420 { 4421 int lineNum; 4422 int entryNum; 4423 int old_default_value; 4424 line_t *lp, *prev, *default_lp, *default_entry; 4425 char buf[PATH_MAX]; 4426 4427 if (mp->start == NULL) { 4428 return; 4429 } 4430 4431 lineNum = LINE_INIT; 4432 entryNum = ENTRY_INIT; 4433 old_default_value = ENTRY_INIT; 4434 lp = default_lp = default_entry = NULL; 4435 4436 prev = NULL; 4437 for (lp = mp->start; lp; prev = lp, lp = lp->next) { 4438 lp->lineNum = ++lineNum; 4439 4440 /* 4441 * Get the value of the default command 4442 */ 4443 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL && 4444 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 && 4445 lp->arg) { 4446 old_default_value = atoi(lp->arg); 4447 default_lp = lp; 4448 } 4449 4450 /* 4451 * If not a booting entry, nothing else to fix for this 4452 * entry 4453 */ 4454 if (lp->entryNum == ENTRY_INIT) 4455 continue; 4456 4457 /* 4458 * Record the position of the default entry. 4459 * The following works because global 4460 * commands like default and timeout should precede 4461 * actual boot entries, so old_default_value 4462 * is already known (or default cmd is missing). 4463 */ 4464 if (default_entry == NULL && 4465 old_default_value != ENTRY_INIT && 4466 lp->entryNum == old_default_value) { 4467 default_entry = lp; 4468 } 4469 4470 /* 4471 * Now fixup the entry number 4472 */ 4473 if (lp->cmd != NULL && 4474 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) { 4475 lp->entryNum = ++entryNum; 4476 /* fixup the bootadm header */ 4477 if (prev && prev->flags == BAM_COMMENT && 4478 prev->arg && 4479 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 4480 prev->entryNum = lp->entryNum; 4481 } 4482 } else { 4483 lp->entryNum = entryNum; 4484 } 4485 } 4486 4487 /* 4488 * No default command in menu, simply return 4489 */ 4490 if (default_lp == NULL) { 4491 return; 4492 } 4493 4494 free(default_lp->arg); 4495 free(default_lp->line); 4496 4497 if (default_entry == NULL) { 4498 default_lp->arg = s_strdup("0"); 4499 } else { 4500 (void) snprintf(buf, sizeof (buf), "%d", 4501 default_entry->entryNum); 4502 default_lp->arg = s_strdup(buf); 4503 } 4504 4505 /* 4506 * The following is required since only the line field gets 4507 * written back to menu.lst 4508 */ 4509 (void) snprintf(buf, sizeof (buf), "%s%s%s", 4510 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg); 4511 default_lp->line = s_strdup(buf); 4512 } 4513 4514 4515 static menu_t * 4516 menu_read(char *menu_path) 4517 { 4518 FILE *fp; 4519 char buf[BAM_MAXLINE], *cp; 4520 menu_t *mp; 4521 int line, entry, len, n; 4522 4523 mp = s_calloc(1, sizeof (menu_t)); 4524 4525 fp = fopen(menu_path, "r"); 4526 if (fp == NULL) { /* Let the caller handle this error */ 4527 free(mp); 4528 return (NULL); 4529 } 4530 4531 /* Note: GRUB boot entry number starts with 0 */ 4532 line = LINE_INIT; 4533 entry = ENTRY_INIT; 4534 cp = buf; 4535 len = sizeof (buf); 4536 while (s_fgets(cp, len, fp) != NULL) { 4537 n = strlen(cp); 4538 if (cp[n - 1] == '\\') { 4539 len -= n - 1; 4540 assert(len >= 2); 4541 cp += n - 1; 4542 continue; 4543 } 4544 line_parser(mp, buf, &line, &entry); 4545 cp = buf; 4546 len = sizeof (buf); 4547 } 4548 4549 if (fclose(fp) == EOF) { 4550 bam_error(CLOSE_FAIL, menu_path, strerror(errno)); 4551 } 4552 4553 return (mp); 4554 } 4555 4556 static error_t 4557 selector(menu_t *mp, char *opt, int *entry, char **title) 4558 { 4559 char *eq; 4560 char *opt_dup; 4561 int entryNum; 4562 4563 assert(mp); 4564 assert(mp->start); 4565 assert(opt); 4566 4567 opt_dup = s_strdup(opt); 4568 4569 if (entry) 4570 *entry = ENTRY_INIT; 4571 if (title) 4572 *title = NULL; 4573 4574 eq = strchr(opt_dup, '='); 4575 if (eq == NULL) { 4576 bam_error(INVALID_OPT, opt); 4577 free(opt_dup); 4578 return (BAM_ERROR); 4579 } 4580 4581 *eq = '\0'; 4582 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) { 4583 assert(mp->end); 4584 entryNum = s_strtol(eq + 1); 4585 if (entryNum < 0 || entryNum > mp->end->entryNum) { 4586 bam_error(INVALID_ENTRY, eq + 1); 4587 free(opt_dup); 4588 return (BAM_ERROR); 4589 } 4590 *entry = entryNum; 4591 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) { 4592 *title = opt + (eq - opt_dup) + 1; 4593 } else { 4594 bam_error(INVALID_OPT, opt); 4595 free(opt_dup); 4596 return (BAM_ERROR); 4597 } 4598 4599 free(opt_dup); 4600 return (BAM_SUCCESS); 4601 } 4602 4603 /* 4604 * If invoked with no titles/entries (opt == NULL) 4605 * only title lines in file are printed. 4606 * 4607 * If invoked with a title or entry #, all 4608 * lines in *every* matching entry are listed 4609 */ 4610 static error_t 4611 list_entry(menu_t *mp, char *menu_path, char *opt) 4612 { 4613 line_t *lp; 4614 int entry = ENTRY_INIT; 4615 int found; 4616 char *title = NULL; 4617 4618 assert(mp); 4619 assert(menu_path); 4620 4621 /* opt is optional */ 4622 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path, 4623 opt ? opt : "<NULL>")); 4624 4625 if (mp->start == NULL) { 4626 bam_error(NO_MENU, menu_path); 4627 return (BAM_ERROR); 4628 } 4629 4630 if (opt != NULL) { 4631 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) { 4632 return (BAM_ERROR); 4633 } 4634 assert((entry != ENTRY_INIT) ^ (title != NULL)); 4635 } else { 4636 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0); 4637 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0); 4638 } 4639 4640 found = 0; 4641 for (lp = mp->start; lp; lp = lp->next) { 4642 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY) 4643 continue; 4644 if (opt == NULL && lp->flags == BAM_TITLE) { 4645 bam_print(PRINT_TITLE, lp->entryNum, 4646 lp->arg); 4647 found = 1; 4648 continue; 4649 } 4650 if (entry != ENTRY_INIT && lp->entryNum == entry) { 4651 bam_print(PRINT, lp->line); 4652 found = 1; 4653 continue; 4654 } 4655 4656 /* 4657 * We set the entry value here so that all lines 4658 * in entry get printed. If we subsequently match 4659 * title in other entries, all lines in those 4660 * entries get printed as well. 4661 */ 4662 if (title && lp->flags == BAM_TITLE && lp->arg && 4663 strncmp(title, lp->arg, strlen(title)) == 0) { 4664 bam_print(PRINT, lp->line); 4665 entry = lp->entryNum; 4666 found = 1; 4667 continue; 4668 } 4669 } 4670 4671 if (!found) { 4672 bam_error(NO_MATCH_ENTRY); 4673 return (BAM_ERROR); 4674 } 4675 4676 return (BAM_SUCCESS); 4677 } 4678 4679 int 4680 add_boot_entry(menu_t *mp, 4681 char *title, 4682 char *findroot, 4683 char *kernel, 4684 char *mod_kernel, 4685 char *module, 4686 char *bootfs) 4687 { 4688 int lineNum; 4689 int entryNum; 4690 char linebuf[BAM_MAXLINE]; 4691 menu_cmd_t k_cmd; 4692 menu_cmd_t m_cmd; 4693 const char *fcn = "add_boot_entry()"; 4694 4695 assert(mp); 4696 4697 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL); 4698 if (findroot == NULL) { 4699 bam_error(NULL_FINDROOT); 4700 return (BAM_ERROR); 4701 } 4702 4703 if (title == NULL) { 4704 title = "Solaris"; /* default to Solaris */ 4705 } 4706 if (kernel == NULL) { 4707 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]); 4708 return (BAM_ERROR); 4709 } 4710 if (module == NULL) { 4711 if (bam_direct != BAM_DIRECT_DBOOT) { 4712 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]); 4713 return (BAM_ERROR); 4714 } 4715 4716 /* Figure the commands out from the kernel line */ 4717 if (strstr(kernel, "$ISADIR") != NULL) { 4718 module = DIRECT_BOOT_ARCHIVE; 4719 } else if (strstr(kernel, "amd64") != NULL) { 4720 module = DIRECT_BOOT_ARCHIVE_64; 4721 } else { 4722 module = DIRECT_BOOT_ARCHIVE_32; 4723 } 4724 } 4725 4726 k_cmd = KERNEL_DOLLAR_CMD; 4727 m_cmd = MODULE_DOLLAR_CMD; 4728 4729 if (mp->start) { 4730 lineNum = mp->end->lineNum; 4731 entryNum = mp->end->entryNum; 4732 } else { 4733 lineNum = LINE_INIT; 4734 entryNum = ENTRY_INIT; 4735 } 4736 4737 /* 4738 * No separator for comment (HDR/FTR) commands 4739 * The syntax for comments is #<comment> 4740 */ 4741 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 4742 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR); 4743 line_parser(mp, linebuf, &lineNum, &entryNum); 4744 4745 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4746 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 4747 line_parser(mp, linebuf, &lineNum, &entryNum); 4748 4749 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4750 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot); 4751 line_parser(mp, linebuf, &lineNum, &entryNum); 4752 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum)); 4753 4754 if (bootfs != NULL) { 4755 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4756 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs); 4757 line_parser(mp, linebuf, &lineNum, &entryNum); 4758 } 4759 4760 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4761 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel); 4762 line_parser(mp, linebuf, &lineNum, &entryNum); 4763 4764 if (mod_kernel != NULL) { 4765 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4766 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel); 4767 line_parser(mp, linebuf, &lineNum, &entryNum); 4768 } 4769 4770 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4771 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module); 4772 line_parser(mp, linebuf, &lineNum, &entryNum); 4773 4774 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 4775 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR); 4776 line_parser(mp, linebuf, &lineNum, &entryNum); 4777 4778 return (entryNum); 4779 } 4780 4781 error_t 4782 delete_boot_entry(menu_t *mp, int entryNum, int quiet) 4783 { 4784 line_t *lp; 4785 line_t *freed; 4786 entry_t *ent; 4787 entry_t *tmp; 4788 int deleted = 0; 4789 const char *fcn = "delete_boot_entry()"; 4790 4791 assert(entryNum != ENTRY_INIT); 4792 4793 tmp = NULL; 4794 4795 ent = mp->entries; 4796 while (ent) { 4797 lp = ent->start; 4798 4799 /* 4800 * Check entry number and make sure it's a modifiable entry. 4801 * 4802 * Guidelines: 4803 * + We can modify a bootadm-created entry 4804 * + We can modify a libbe-created entry 4805 */ 4806 if ((lp->flags != BAM_COMMENT && 4807 (((ent->flags & BAM_ENTRY_LIBBE) == 0) && 4808 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) || 4809 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) { 4810 ent = ent->next; 4811 continue; 4812 } 4813 4814 /* free the entry content */ 4815 do { 4816 freed = lp; 4817 lp = lp->next; /* prev stays the same */ 4818 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum)); 4819 unlink_line(mp, freed); 4820 line_free(freed); 4821 } while (freed != ent->end); 4822 4823 /* free the entry_t structure */ 4824 assert(tmp == NULL); 4825 tmp = ent; 4826 ent = ent->next; 4827 if (tmp->prev) 4828 tmp->prev->next = ent; 4829 else 4830 mp->entries = ent; 4831 if (ent) 4832 ent->prev = tmp->prev; 4833 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum)); 4834 free(tmp); 4835 tmp = NULL; 4836 deleted = 1; 4837 } 4838 4839 assert(tmp == NULL); 4840 4841 if (!deleted && entryNum != ALL_ENTRIES) { 4842 if (quiet == DBE_PRINTERR) 4843 bam_error(NO_BOOTADM_MATCH); 4844 return (BAM_ERROR); 4845 } 4846 4847 /* 4848 * Now that we have deleted an entry, update 4849 * the entry numbering and the default cmd. 4850 */ 4851 update_numbering(mp); 4852 4853 return (BAM_SUCCESS); 4854 } 4855 4856 static error_t 4857 delete_all_entries(menu_t *mp, char *dummy, char *opt) 4858 { 4859 assert(mp); 4860 assert(dummy == NULL); 4861 assert(opt == NULL); 4862 4863 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries")); 4864 4865 if (mp->start == NULL) { 4866 bam_print(EMPTY_MENU); 4867 return (BAM_SUCCESS); 4868 } 4869 4870 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) { 4871 return (BAM_ERROR); 4872 } 4873 4874 return (BAM_WRITE); 4875 } 4876 4877 static FILE * 4878 create_diskmap(char *osroot) 4879 { 4880 FILE *fp; 4881 char cmd[PATH_MAX + 16]; 4882 char path[PATH_MAX]; 4883 const char *fcn = "create_diskmap()"; 4884 4885 /* make sure we have a map file */ 4886 fp = fopen(GRUBDISK_MAP, "r"); 4887 if (fp == NULL) { 4888 int ret; 4889 4890 ret = snprintf(path, sizeof (path), "%s/%s", osroot, 4891 CREATE_DISKMAP); 4892 if (ret >= sizeof (path)) { 4893 bam_error(PATH_TOO_LONG, osroot); 4894 return (NULL); 4895 } 4896 if (is_safe_exec(path) == BAM_ERROR) 4897 return (NULL); 4898 4899 (void) snprintf(cmd, sizeof (cmd), 4900 "%s/%s > /dev/null", osroot, CREATE_DISKMAP); 4901 if (exec_cmd(cmd, NULL) != 0) 4902 return (NULL); 4903 fp = fopen(GRUBDISK_MAP, "r"); 4904 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL); 4905 if (fp) { 4906 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP)); 4907 } else { 4908 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP)); 4909 } 4910 } 4911 return (fp); 4912 } 4913 4914 #define SECTOR_SIZE 512 4915 4916 static int 4917 get_partition(char *device) 4918 { 4919 int i, fd, is_pcfs, partno = -1; 4920 struct mboot *mboot; 4921 char boot_sect[SECTOR_SIZE]; 4922 char *wholedisk, *slice; 4923 #ifdef i386 4924 ext_part_t *epp; 4925 uint32_t secnum, numsec; 4926 int rval, pno, ext_partno = -1; 4927 #endif 4928 4929 /* form whole disk (p0) */ 4930 slice = device + strlen(device) - 2; 4931 is_pcfs = (*slice != 's'); 4932 if (!is_pcfs) 4933 *slice = '\0'; 4934 wholedisk = s_calloc(1, strlen(device) + 3); 4935 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device); 4936 if (!is_pcfs) 4937 *slice = 's'; 4938 4939 /* read boot sector */ 4940 fd = open(wholedisk, O_RDONLY); 4941 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 4942 return (partno); 4943 } 4944 (void) close(fd); 4945 4946 #ifdef i386 4947 /* Read/Initialize extended partition information */ 4948 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK)) 4949 != FDISK_SUCCESS) { 4950 switch (rval) { 4951 /* 4952 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can 4953 * be considered as soft errors and hence 4954 * we do not return 4955 */ 4956 case FDISK_EBADLOGDRIVE: 4957 break; 4958 case FDISK_ENOLOGDRIVE: 4959 break; 4960 case FDISK_EBADMAGIC: 4961 /*FALLTHROUGH*/ 4962 default: 4963 free(wholedisk); 4964 libfdisk_fini(&epp); 4965 return (partno); 4966 } 4967 } 4968 #endif 4969 free(wholedisk); 4970 4971 /* parse fdisk table */ 4972 mboot = (struct mboot *)((void *)boot_sect); 4973 for (i = 0; i < FD_NUMPART; i++) { 4974 struct ipart *part = 4975 (struct ipart *)(uintptr_t)mboot->parts + i; 4976 if (is_pcfs) { /* looking for solaris boot part */ 4977 if (part->systid == 0xbe) { 4978 partno = i; 4979 break; 4980 } 4981 } else { /* look for solaris partition, old and new */ 4982 #ifdef i386 4983 if ((part->systid == SUNIXOS && 4984 (fdisk_is_linux_swap(epp, part->relsect, 4985 NULL) != 0)) || part->systid == SUNIXOS2) { 4986 #else 4987 if (part->systid == SUNIXOS || 4988 part->systid == SUNIXOS2) { 4989 #endif 4990 partno = i; 4991 break; 4992 } 4993 4994 #ifdef i386 4995 if (fdisk_is_dos_extended(part->systid)) 4996 ext_partno = i; 4997 #endif 4998 } 4999 } 5000 #ifdef i386 5001 /* If no primary solaris partition, check extended partition */ 5002 if ((partno == -1) && (ext_partno != -1)) { 5003 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec); 5004 if (rval == FDISK_SUCCESS) { 5005 partno = pno - 1; 5006 } 5007 } 5008 libfdisk_fini(&epp); 5009 #endif 5010 return (partno); 5011 } 5012 5013 char * 5014 get_grubroot(char *osroot, char *osdev, char *menu_root) 5015 { 5016 char *grubroot; /* (hd#,#,#) */ 5017 char *slice; 5018 char *grubhd; 5019 int fdiskpart; 5020 int found = 0; 5021 char *devname; 5022 char *ctdname = strstr(osdev, "dsk/"); 5023 char linebuf[PATH_MAX]; 5024 FILE *fp; 5025 5026 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL); 5027 if (ctdname == NULL) { 5028 bam_error(INVALID_DEV_DSK, osdev); 5029 return (NULL); 5030 } 5031 5032 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) { 5033 /* menu bears no resemblance to our reality */ 5034 bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev); 5035 return (NULL); 5036 } 5037 5038 ctdname += strlen("dsk/"); 5039 slice = strrchr(ctdname, 's'); 5040 if (slice) 5041 *slice = '\0'; 5042 5043 fp = create_diskmap(osroot); 5044 if (fp == NULL) { 5045 bam_error(DISKMAP_FAIL, osroot); 5046 return (NULL); 5047 } 5048 5049 rewind(fp); 5050 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) { 5051 grubhd = strtok(linebuf, " \t\n"); 5052 if (grubhd) 5053 devname = strtok(NULL, " \t\n"); 5054 else 5055 devname = NULL; 5056 if (devname && strcmp(devname, ctdname) == 0) { 5057 found = 1; 5058 break; 5059 } 5060 } 5061 5062 if (slice) 5063 *slice = 's'; 5064 5065 (void) fclose(fp); 5066 fp = NULL; 5067 5068 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0); 5069 if (found == 0) { 5070 bam_error(BIOSDEV_SKIP, osdev); 5071 return (NULL); 5072 } 5073 5074 fdiskpart = get_partition(osdev); 5075 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1); 5076 if (fdiskpart == -1) { 5077 bam_error(FDISKPART_FAIL, osdev); 5078 return (NULL); 5079 } 5080 5081 grubroot = s_calloc(1, 10); 5082 if (slice) { 5083 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)", 5084 grubhd, fdiskpart, slice[1] + 'a' - '0'); 5085 } else 5086 (void) snprintf(grubroot, 10, "(hd%s,%d)", 5087 grubhd, fdiskpart); 5088 5089 assert(fp == NULL); 5090 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0); 5091 return (grubroot); 5092 } 5093 5094 static char * 5095 find_primary_common(char *mntpt, char *fstype) 5096 { 5097 char signdir[PATH_MAX]; 5098 char tmpsign[MAXNAMELEN + 1]; 5099 char *lu; 5100 char *ufs; 5101 char *zfs; 5102 DIR *dirp = NULL; 5103 struct dirent *entp; 5104 struct stat sb; 5105 const char *fcn = "find_primary_common()"; 5106 5107 (void) snprintf(signdir, sizeof (signdir), "%s/%s", 5108 mntpt, GRUBSIGN_DIR); 5109 5110 if (stat(signdir, &sb) == -1) { 5111 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir)); 5112 return (NULL); 5113 } 5114 5115 dirp = opendir(signdir); 5116 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL); 5117 if (dirp == NULL) { 5118 bam_error(OPENDIR_FAILED, signdir, strerror(errno)); 5119 return (NULL); 5120 } 5121 5122 ufs = zfs = lu = NULL; 5123 5124 while (entp = readdir(dirp)) { 5125 if (strcmp(entp->d_name, ".") == 0 || 5126 strcmp(entp->d_name, "..") == 0) 5127 continue; 5128 5129 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name); 5130 5131 if (lu == NULL && 5132 strncmp(tmpsign, GRUBSIGN_LU_PREFIX, 5133 strlen(GRUBSIGN_LU_PREFIX)) == 0) { 5134 lu = s_strdup(tmpsign); 5135 } 5136 5137 if (ufs == NULL && 5138 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 5139 strlen(GRUBSIGN_UFS_PREFIX)) == 0) { 5140 ufs = s_strdup(tmpsign); 5141 } 5142 5143 if (zfs == NULL && 5144 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX, 5145 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) { 5146 zfs = s_strdup(tmpsign); 5147 } 5148 } 5149 5150 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn, 5151 zfs ? zfs : "NULL", 5152 ufs ? ufs : "NULL", 5153 lu ? lu : "NULL")); 5154 5155 if (dirp) { 5156 (void) closedir(dirp); 5157 dirp = NULL; 5158 } 5159 5160 if (strcmp(fstype, "ufs") == 0 && zfs) { 5161 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs"); 5162 free(zfs); 5163 zfs = NULL; 5164 } else if (strcmp(fstype, "zfs") == 0 && ufs) { 5165 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs"); 5166 free(ufs); 5167 ufs = NULL; 5168 } 5169 5170 assert(dirp == NULL); 5171 5172 /* For now, we let Live Upgrade take care of its signature itself */ 5173 if (lu) { 5174 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu)); 5175 free(lu); 5176 lu = NULL; 5177 } 5178 5179 return (zfs ? zfs : ufs); 5180 } 5181 5182 static char * 5183 find_backup_common(char *mntpt, char *fstype) 5184 { 5185 FILE *bfp = NULL; 5186 char tmpsign[MAXNAMELEN + 1]; 5187 char backup[PATH_MAX]; 5188 char *ufs; 5189 char *zfs; 5190 char *lu; 5191 int error; 5192 const char *fcn = "find_backup_common()"; 5193 5194 /* 5195 * We didn't find it in the primary directory. 5196 * Look at the backup 5197 */ 5198 (void) snprintf(backup, sizeof (backup), "%s%s", 5199 mntpt, GRUBSIGN_BACKUP); 5200 5201 bfp = fopen(backup, "r"); 5202 if (bfp == NULL) { 5203 error = errno; 5204 if (bam_verbose) { 5205 bam_error(OPEN_FAIL, backup, strerror(error)); 5206 } 5207 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error))); 5208 return (NULL); 5209 } 5210 5211 ufs = zfs = lu = NULL; 5212 5213 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) { 5214 5215 if (lu == NULL && 5216 strncmp(tmpsign, GRUBSIGN_LU_PREFIX, 5217 strlen(GRUBSIGN_LU_PREFIX)) == 0) { 5218 lu = s_strdup(tmpsign); 5219 } 5220 5221 if (ufs == NULL && 5222 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 5223 strlen(GRUBSIGN_UFS_PREFIX)) == 0) { 5224 ufs = s_strdup(tmpsign); 5225 } 5226 5227 if (zfs == NULL && 5228 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX, 5229 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) { 5230 zfs = s_strdup(tmpsign); 5231 } 5232 } 5233 5234 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn, 5235 zfs ? zfs : "NULL", 5236 ufs ? ufs : "NULL", 5237 lu ? lu : "NULL")); 5238 5239 if (bfp) { 5240 (void) fclose(bfp); 5241 bfp = NULL; 5242 } 5243 5244 if (strcmp(fstype, "ufs") == 0 && zfs) { 5245 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs"); 5246 free(zfs); 5247 zfs = NULL; 5248 } else if (strcmp(fstype, "zfs") == 0 && ufs) { 5249 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs"); 5250 free(ufs); 5251 ufs = NULL; 5252 } 5253 5254 assert(bfp == NULL); 5255 5256 /* For now, we let Live Upgrade take care of its signature itself */ 5257 if (lu) { 5258 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu)); 5259 free(lu); 5260 lu = NULL; 5261 } 5262 5263 return (zfs ? zfs : ufs); 5264 } 5265 5266 static char * 5267 find_ufs_existing(char *osroot) 5268 { 5269 char *sign; 5270 const char *fcn = "find_ufs_existing()"; 5271 5272 sign = find_primary_common(osroot, "ufs"); 5273 if (sign == NULL) { 5274 sign = find_backup_common(osroot, "ufs"); 5275 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL")); 5276 } else { 5277 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign)); 5278 } 5279 5280 return (sign); 5281 } 5282 5283 char * 5284 get_mountpoint(char *special, char *fstype) 5285 { 5286 FILE *mntfp; 5287 struct mnttab mp = {0}; 5288 struct mnttab mpref = {0}; 5289 int error; 5290 int ret; 5291 const char *fcn = "get_mountpoint()"; 5292 5293 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype)); 5294 5295 mntfp = fopen(MNTTAB, "r"); 5296 error = errno; 5297 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL); 5298 if (mntfp == NULL) { 5299 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 5300 return (NULL); 5301 } 5302 5303 mpref.mnt_special = special; 5304 mpref.mnt_fstype = fstype; 5305 5306 ret = getmntany(mntfp, &mp, &mpref); 5307 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1); 5308 if (ret != 0) { 5309 (void) fclose(mntfp); 5310 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype)); 5311 return (NULL); 5312 } 5313 (void) fclose(mntfp); 5314 5315 assert(mp.mnt_mountp); 5316 5317 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp)); 5318 5319 return (s_strdup(mp.mnt_mountp)); 5320 } 5321 5322 /* 5323 * Mounts a "legacy" top dataset (if needed) 5324 * Returns: The mountpoint of the legacy top dataset or NULL on error 5325 * mnted returns one of the above values defined for zfs_mnted_t 5326 */ 5327 static char * 5328 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted) 5329 { 5330 char cmd[PATH_MAX]; 5331 char tmpmnt[PATH_MAX]; 5332 filelist_t flist = {0}; 5333 char *is_mounted; 5334 struct stat sb; 5335 int ret; 5336 const char *fcn = "mount_legacy_dataset()"; 5337 5338 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool)); 5339 5340 *mnted = ZFS_MNT_ERROR; 5341 5342 (void) snprintf(cmd, sizeof (cmd), 5343 "/sbin/zfs get -Ho value mounted %s", 5344 pool); 5345 5346 ret = exec_cmd(cmd, &flist); 5347 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1); 5348 if (ret != 0) { 5349 bam_error(ZFS_MNTED_FAILED, pool); 5350 return (NULL); 5351 } 5352 5353 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL); 5354 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5355 bam_error(BAD_ZFS_MNTED, pool); 5356 filelist_free(&flist); 5357 return (NULL); 5358 } 5359 5360 is_mounted = strtok(flist.head->line, " \t\n"); 5361 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes"); 5362 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no"); 5363 if (strcmp(is_mounted, "no") != 0) { 5364 filelist_free(&flist); 5365 *mnted = LEGACY_ALREADY; 5366 /* get_mountpoint returns a strdup'ed string */ 5367 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool)); 5368 return (get_mountpoint(pool, "zfs")); 5369 } 5370 5371 filelist_free(&flist); 5372 5373 /* 5374 * legacy top dataset is not mounted. Mount it now 5375 * First create a mountpoint. 5376 */ 5377 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d", 5378 ZFS_LEGACY_MNTPT, getpid()); 5379 5380 ret = stat(tmpmnt, &sb); 5381 if (ret == -1) { 5382 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt)); 5383 ret = mkdirp(tmpmnt, DIR_PERMS); 5384 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1); 5385 if (ret == -1) { 5386 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno)); 5387 return (NULL); 5388 } 5389 } else { 5390 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt)); 5391 } 5392 5393 (void) snprintf(cmd, sizeof (cmd), 5394 "/sbin/mount -F zfs %s %s", 5395 pool, tmpmnt); 5396 5397 ret = exec_cmd(cmd, NULL); 5398 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1); 5399 if (ret != 0) { 5400 bam_error(ZFS_MOUNT_FAILED, pool); 5401 (void) rmdir(tmpmnt); 5402 return (NULL); 5403 } 5404 5405 *mnted = LEGACY_MOUNTED; 5406 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt)); 5407 return (s_strdup(tmpmnt)); 5408 } 5409 5410 /* 5411 * Mounts the top dataset (if needed) 5412 * Returns: The mountpoint of the top dataset or NULL on error 5413 * mnted returns one of the above values defined for zfs_mnted_t 5414 */ 5415 static char * 5416 mount_top_dataset(char *pool, zfs_mnted_t *mnted) 5417 { 5418 char cmd[PATH_MAX]; 5419 filelist_t flist = {0}; 5420 char *is_mounted; 5421 char *mntpt; 5422 char *zmntpt; 5423 int ret; 5424 const char *fcn = "mount_top_dataset()"; 5425 5426 *mnted = ZFS_MNT_ERROR; 5427 5428 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool)); 5429 5430 /* 5431 * First check if the top dataset is a "legacy" dataset 5432 */ 5433 (void) snprintf(cmd, sizeof (cmd), 5434 "/sbin/zfs get -Ho value mountpoint %s", 5435 pool); 5436 ret = exec_cmd(cmd, &flist); 5437 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1); 5438 if (ret != 0) { 5439 bam_error(ZFS_MNTPT_FAILED, pool); 5440 return (NULL); 5441 } 5442 5443 if (flist.head && (flist.head == flist.tail)) { 5444 char *legacy = strtok(flist.head->line, " \t\n"); 5445 if (legacy && strcmp(legacy, "legacy") == 0) { 5446 filelist_free(&flist); 5447 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool)); 5448 return (mount_legacy_dataset(pool, mnted)); 5449 } 5450 } 5451 5452 filelist_free(&flist); 5453 5454 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool)); 5455 5456 (void) snprintf(cmd, sizeof (cmd), 5457 "/sbin/zfs get -Ho value mounted %s", 5458 pool); 5459 5460 ret = exec_cmd(cmd, &flist); 5461 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1); 5462 if (ret != 0) { 5463 bam_error(ZFS_MNTED_FAILED, pool); 5464 return (NULL); 5465 } 5466 5467 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL); 5468 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5469 bam_error(BAD_ZFS_MNTED, pool); 5470 filelist_free(&flist); 5471 return (NULL); 5472 } 5473 5474 is_mounted = strtok(flist.head->line, " \t\n"); 5475 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes"); 5476 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no"); 5477 if (strcmp(is_mounted, "no") != 0) { 5478 filelist_free(&flist); 5479 *mnted = ZFS_ALREADY; 5480 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool)); 5481 goto mounted; 5482 } 5483 5484 filelist_free(&flist); 5485 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool)); 5486 5487 /* top dataset is not mounted. Mount it now */ 5488 (void) snprintf(cmd, sizeof (cmd), 5489 "/sbin/zfs mount %s", pool); 5490 ret = exec_cmd(cmd, NULL); 5491 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1); 5492 if (ret != 0) { 5493 bam_error(ZFS_MOUNT_FAILED, pool); 5494 return (NULL); 5495 } 5496 *mnted = ZFS_MOUNTED; 5497 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool)); 5498 /*FALLTHRU*/ 5499 mounted: 5500 /* 5501 * Now get the mountpoint 5502 */ 5503 (void) snprintf(cmd, sizeof (cmd), 5504 "/sbin/zfs get -Ho value mountpoint %s", 5505 pool); 5506 5507 ret = exec_cmd(cmd, &flist); 5508 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1); 5509 if (ret != 0) { 5510 bam_error(ZFS_MNTPT_FAILED, pool); 5511 goto error; 5512 } 5513 5514 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL); 5515 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5516 bam_error(NULL_ZFS_MNTPT, pool); 5517 goto error; 5518 } 5519 5520 mntpt = strtok(flist.head->line, " \t\n"); 5521 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo"); 5522 if (*mntpt != '/') { 5523 bam_error(BAD_ZFS_MNTPT, pool, mntpt); 5524 goto error; 5525 } 5526 zmntpt = s_strdup(mntpt); 5527 5528 filelist_free(&flist); 5529 5530 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt)); 5531 5532 return (zmntpt); 5533 5534 error: 5535 filelist_free(&flist); 5536 (void) umount_top_dataset(pool, *mnted, NULL); 5537 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5538 return (NULL); 5539 } 5540 5541 static int 5542 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt) 5543 { 5544 char cmd[PATH_MAX]; 5545 int ret; 5546 const char *fcn = "umount_top_dataset()"; 5547 5548 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR); 5549 switch (mnted) { 5550 case LEGACY_ALREADY: 5551 case ZFS_ALREADY: 5552 /* nothing to do */ 5553 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool, 5554 mntpt ? mntpt : "NULL")); 5555 free(mntpt); 5556 return (BAM_SUCCESS); 5557 case LEGACY_MOUNTED: 5558 (void) snprintf(cmd, sizeof (cmd), 5559 "/sbin/umount %s", pool); 5560 ret = exec_cmd(cmd, NULL); 5561 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1); 5562 if (ret != 0) { 5563 bam_error(UMOUNT_FAILED, pool); 5564 free(mntpt); 5565 return (BAM_ERROR); 5566 } 5567 if (mntpt) 5568 (void) rmdir(mntpt); 5569 free(mntpt); 5570 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool)); 5571 return (BAM_SUCCESS); 5572 case ZFS_MOUNTED: 5573 free(mntpt); 5574 (void) snprintf(cmd, sizeof (cmd), 5575 "/sbin/zfs unmount %s", pool); 5576 ret = exec_cmd(cmd, NULL); 5577 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1); 5578 if (ret != 0) { 5579 bam_error(UMOUNT_FAILED, pool); 5580 return (BAM_ERROR); 5581 } 5582 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool)); 5583 return (BAM_SUCCESS); 5584 default: 5585 bam_error(INT_BAD_MNTSTATE, pool); 5586 return (BAM_ERROR); 5587 } 5588 /*NOTREACHED*/ 5589 } 5590 5591 /* 5592 * For ZFS, osdev can be one of two forms 5593 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402 5594 * It can be a /dev/[r]dsk special file. We handle both instances 5595 */ 5596 static char * 5597 get_pool(char *osdev) 5598 { 5599 char cmd[PATH_MAX]; 5600 char buf[PATH_MAX]; 5601 filelist_t flist = {0}; 5602 char *pool; 5603 char *cp; 5604 char *slash; 5605 int ret; 5606 const char *fcn = "get_pool()"; 5607 5608 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL); 5609 if (osdev == NULL) { 5610 bam_error(GET_POOL_OSDEV_NULL); 5611 return (NULL); 5612 } 5613 5614 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev)); 5615 5616 if (osdev[0] != '/') { 5617 (void) strlcpy(buf, osdev, sizeof (buf)); 5618 slash = strchr(buf, '/'); 5619 if (slash) 5620 *slash = '\0'; 5621 pool = s_strdup(buf); 5622 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool)); 5623 return (pool); 5624 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 5625 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) { 5626 bam_error(GET_POOL_BAD_OSDEV, osdev); 5627 return (NULL); 5628 } 5629 5630 /* 5631 * Call the zfs fstyp directly since this is a zpool. This avoids 5632 * potential pcfs conflicts if the first block wasn't cleared. 5633 */ 5634 (void) snprintf(cmd, sizeof (cmd), 5635 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'", 5636 osdev); 5637 5638 ret = exec_cmd(cmd, &flist); 5639 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1); 5640 if (ret != 0) { 5641 bam_error(FSTYP_A_FAILED, osdev); 5642 return (NULL); 5643 } 5644 5645 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL); 5646 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5647 bam_error(NULL_FSTYP_A, osdev); 5648 filelist_free(&flist); 5649 return (NULL); 5650 } 5651 5652 (void) strtok(flist.head->line, "'"); 5653 cp = strtok(NULL, "'"); 5654 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL); 5655 if (cp == NULL) { 5656 bam_error(BAD_FSTYP_A, osdev); 5657 filelist_free(&flist); 5658 return (NULL); 5659 } 5660 5661 pool = s_strdup(cp); 5662 5663 filelist_free(&flist); 5664 5665 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool)); 5666 5667 return (pool); 5668 } 5669 5670 static char * 5671 find_zfs_existing(char *osdev) 5672 { 5673 char *pool; 5674 zfs_mnted_t mnted; 5675 char *mntpt; 5676 char *sign; 5677 const char *fcn = "find_zfs_existing()"; 5678 5679 pool = get_pool(osdev); 5680 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL); 5681 if (pool == NULL) { 5682 bam_error(ZFS_GET_POOL_FAILED, osdev); 5683 return (NULL); 5684 } 5685 5686 mntpt = mount_top_dataset(pool, &mnted); 5687 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL); 5688 if (mntpt == NULL) { 5689 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool); 5690 free(pool); 5691 return (NULL); 5692 } 5693 5694 sign = find_primary_common(mntpt, "zfs"); 5695 if (sign == NULL) { 5696 sign = find_backup_common(mntpt, "zfs"); 5697 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL")); 5698 } else { 5699 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign)); 5700 } 5701 5702 (void) umount_top_dataset(pool, mnted, mntpt); 5703 5704 free(pool); 5705 5706 return (sign); 5707 } 5708 5709 static char * 5710 find_existing_sign(char *osroot, char *osdev, char *fstype) 5711 { 5712 const char *fcn = "find_existing_sign()"; 5713 5714 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs"); 5715 if (strcmp(fstype, "ufs") == 0) { 5716 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn)); 5717 return (find_ufs_existing(osroot)); 5718 } else if (strcmp(fstype, "zfs") == 0) { 5719 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn)); 5720 return (find_zfs_existing(osdev)); 5721 } else { 5722 bam_error(GRUBSIGN_NOTSUP, fstype); 5723 return (NULL); 5724 } 5725 } 5726 5727 #define MH_HASH_SZ 16 5728 5729 typedef enum { 5730 MH_ERROR = -1, 5731 MH_NOMATCH, 5732 MH_MATCH 5733 } mh_search_t; 5734 5735 typedef struct mcache { 5736 char *mc_special; 5737 char *mc_mntpt; 5738 char *mc_fstype; 5739 struct mcache *mc_next; 5740 } mcache_t; 5741 5742 typedef struct mhash { 5743 mcache_t *mh_hash[MH_HASH_SZ]; 5744 } mhash_t; 5745 5746 static int 5747 mhash_fcn(char *key) 5748 { 5749 int i; 5750 uint64_t sum = 0; 5751 5752 for (i = 0; key[i] != '\0'; i++) { 5753 sum += (uchar_t)key[i]; 5754 } 5755 5756 sum %= MH_HASH_SZ; 5757 5758 assert(sum < MH_HASH_SZ); 5759 5760 return (sum); 5761 } 5762 5763 static mhash_t * 5764 cache_mnttab(void) 5765 { 5766 FILE *mfp; 5767 struct extmnttab mnt; 5768 mcache_t *mcp; 5769 mhash_t *mhp; 5770 char *ctds; 5771 int idx; 5772 int error; 5773 char *special_dup; 5774 const char *fcn = "cache_mnttab()"; 5775 5776 mfp = fopen(MNTTAB, "r"); 5777 error = errno; 5778 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL); 5779 if (mfp == NULL) { 5780 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 5781 return (NULL); 5782 } 5783 5784 mhp = s_calloc(1, sizeof (mhash_t)); 5785 5786 resetmnttab(mfp); 5787 5788 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) { 5789 /* only cache ufs */ 5790 if (strcmp(mnt.mnt_fstype, "ufs") != 0) 5791 continue; 5792 5793 /* basename() modifies its arg, so dup it */ 5794 special_dup = s_strdup(mnt.mnt_special); 5795 ctds = basename(special_dup); 5796 5797 mcp = s_calloc(1, sizeof (mcache_t)); 5798 mcp->mc_special = s_strdup(ctds); 5799 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp); 5800 mcp->mc_fstype = s_strdup(mnt.mnt_fstype); 5801 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds, 5802 mnt.mnt_mountp, mnt.mnt_fstype)); 5803 idx = mhash_fcn(ctds); 5804 mcp->mc_next = mhp->mh_hash[idx]; 5805 mhp->mh_hash[idx] = mcp; 5806 free(special_dup); 5807 } 5808 5809 (void) fclose(mfp); 5810 5811 return (mhp); 5812 } 5813 5814 static void 5815 free_mnttab(mhash_t *mhp) 5816 { 5817 mcache_t *mcp; 5818 int i; 5819 5820 for (i = 0; i < MH_HASH_SZ; i++) { 5821 /*LINTED*/ 5822 while (mcp = mhp->mh_hash[i]) { 5823 mhp->mh_hash[i] = mcp->mc_next; 5824 free(mcp->mc_special); 5825 free(mcp->mc_mntpt); 5826 free(mcp->mc_fstype); 5827 free(mcp); 5828 } 5829 } 5830 5831 for (i = 0; i < MH_HASH_SZ; i++) { 5832 assert(mhp->mh_hash[i] == NULL); 5833 } 5834 free(mhp); 5835 } 5836 5837 static mh_search_t 5838 search_hash(mhash_t *mhp, char *special, char **mntpt) 5839 { 5840 int idx; 5841 mcache_t *mcp; 5842 const char *fcn = "search_hash()"; 5843 5844 assert(mntpt); 5845 5846 *mntpt = NULL; 5847 5848 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo"); 5849 if (strchr(special, '/')) { 5850 bam_error(INVALID_MHASH_KEY, special); 5851 return (MH_ERROR); 5852 } 5853 5854 idx = mhash_fcn(special); 5855 5856 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) { 5857 if (strcmp(mcp->mc_special, special) == 0) 5858 break; 5859 } 5860 5861 if (mcp == NULL) { 5862 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special)); 5863 return (MH_NOMATCH); 5864 } 5865 5866 assert(strcmp(mcp->mc_fstype, "ufs") == 0); 5867 *mntpt = mcp->mc_mntpt; 5868 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special)); 5869 return (MH_MATCH); 5870 } 5871 5872 static int 5873 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt) 5874 { 5875 char *sign; 5876 char *signline; 5877 char signbuf[MAXNAMELEN]; 5878 int len; 5879 int error; 5880 const char *fcn = "check_add_ufs_sign_to_list()"; 5881 5882 /* safe to specify NULL as "osdev" arg for UFS */ 5883 sign = find_existing_sign(mntpt, NULL, "ufs"); 5884 if (sign == NULL) { 5885 /* No existing signature, nothing to add to list */ 5886 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt)); 5887 return (0); 5888 } 5889 5890 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign); 5891 signline = signbuf; 5892 5893 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n"); 5894 if (strncmp(signline, GRUBSIGN_UFS_PREFIX, 5895 strlen(GRUBSIGN_UFS_PREFIX))) { 5896 bam_error(INVALID_UFS_SIGNATURE, sign); 5897 free(sign); 5898 /* ignore invalid signatures */ 5899 return (0); 5900 } 5901 5902 len = fputs(signline, tfp); 5903 error = errno; 5904 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0); 5905 if (len != strlen(signline)) { 5906 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error)); 5907 free(sign); 5908 return (-1); 5909 } 5910 5911 free(sign); 5912 5913 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt)); 5914 return (0); 5915 } 5916 5917 /* 5918 * slice is a basename not a full pathname 5919 */ 5920 static int 5921 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt) 5922 { 5923 int ret; 5924 char cmd[PATH_MAX]; 5925 char path[PATH_MAX]; 5926 struct stat sbuf; 5927 char *mntpt; 5928 filelist_t flist = {0}; 5929 char *fstype; 5930 char blkslice[PATH_MAX]; 5931 const char *fcn = "process_slice_common()"; 5932 5933 5934 ret = search_hash(mhp, slice, &mntpt); 5935 switch (ret) { 5936 case MH_MATCH: 5937 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1) 5938 return (-1); 5939 else 5940 return (0); 5941 case MH_NOMATCH: 5942 break; 5943 case MH_ERROR: 5944 default: 5945 return (-1); 5946 } 5947 5948 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice); 5949 if (stat(path, &sbuf) == -1) { 5950 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path)); 5951 return (0); 5952 } 5953 5954 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */ 5955 (void) snprintf(cmd, sizeof (cmd), 5956 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null", 5957 slice); 5958 5959 if (exec_cmd(cmd, &flist) != 0) { 5960 if (bam_verbose) 5961 bam_print(FSTYP_FAILED, slice); 5962 return (0); 5963 } 5964 5965 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5966 if (bam_verbose) 5967 bam_print(FSTYP_BAD, slice); 5968 filelist_free(&flist); 5969 return (0); 5970 } 5971 5972 fstype = strtok(flist.head->line, " \t\n"); 5973 if (fstype == NULL || strcmp(fstype, "ufs") != 0) { 5974 if (bam_verbose) 5975 bam_print(NOT_UFS_SLICE, slice, fstype); 5976 filelist_free(&flist); 5977 return (0); 5978 } 5979 5980 filelist_free(&flist); 5981 5982 /* 5983 * Since we are mounting the filesystem read-only, the 5984 * the last mount field of the superblock is unchanged 5985 * and does not need to be fixed up post-mount; 5986 */ 5987 5988 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s", 5989 slice); 5990 5991 (void) snprintf(cmd, sizeof (cmd), 5992 "/usr/sbin/mount -F ufs -o ro %s %s " 5993 "> /dev/null 2>&1", blkslice, tmpmnt); 5994 5995 if (exec_cmd(cmd, NULL) != 0) { 5996 if (bam_verbose) 5997 bam_print(MOUNT_FAILED, blkslice, "ufs"); 5998 return (0); 5999 } 6000 6001 ret = check_add_ufs_sign_to_list(tfp, tmpmnt); 6002 6003 (void) snprintf(cmd, sizeof (cmd), 6004 "/usr/sbin/umount -f %s > /dev/null 2>&1", 6005 tmpmnt); 6006 6007 if (exec_cmd(cmd, NULL) != 0) { 6008 bam_print(UMOUNT_FAILED, slice); 6009 return (0); 6010 } 6011 6012 return (ret); 6013 } 6014 6015 static int 6016 process_vtoc_slices( 6017 char *s0, 6018 struct vtoc *vtoc, 6019 FILE *tfp, 6020 mhash_t *mhp, 6021 char *tmpmnt) 6022 { 6023 int idx; 6024 char slice[PATH_MAX]; 6025 size_t len; 6026 char *cp; 6027 const char *fcn = "process_vtoc_slices()"; 6028 6029 len = strlen(s0); 6030 6031 assert(s0[len - 2] == 's' && s0[len - 1] == '0'); 6032 6033 s0[len - 1] = '\0'; 6034 6035 (void) strlcpy(slice, s0, sizeof (slice)); 6036 6037 s0[len - 1] = '0'; 6038 6039 cp = slice + len - 1; 6040 6041 for (idx = 0; idx < vtoc->v_nparts; idx++) { 6042 6043 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx); 6044 6045 if (vtoc->v_part[idx].p_size == 0) { 6046 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice)); 6047 continue; 6048 } 6049 6050 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */ 6051 switch (vtoc->v_part[idx].p_tag) { 6052 case V_SWAP: 6053 case V_USR: 6054 case V_BACKUP: 6055 case V_VAR: 6056 case V_HOME: 6057 case V_ALTSCTR: 6058 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice)); 6059 continue; 6060 default: 6061 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice)); 6062 break; 6063 } 6064 6065 /* skip unmountable and readonly slices */ 6066 switch (vtoc->v_part[idx].p_flag) { 6067 case V_UNMNT: 6068 case V_RONLY: 6069 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice)); 6070 continue; 6071 default: 6072 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice)); 6073 break; 6074 } 6075 6076 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) { 6077 return (-1); 6078 } 6079 } 6080 6081 return (0); 6082 } 6083 6084 static int 6085 process_efi_slices( 6086 char *s0, 6087 struct dk_gpt *efi, 6088 FILE *tfp, 6089 mhash_t *mhp, 6090 char *tmpmnt) 6091 { 6092 int idx; 6093 char slice[PATH_MAX]; 6094 size_t len; 6095 char *cp; 6096 const char *fcn = "process_efi_slices()"; 6097 6098 len = strlen(s0); 6099 6100 assert(s0[len - 2] == 's' && s0[len - 1] == '0'); 6101 6102 s0[len - 1] = '\0'; 6103 6104 (void) strlcpy(slice, s0, sizeof (slice)); 6105 6106 s0[len - 1] = '0'; 6107 6108 cp = slice + len - 1; 6109 6110 for (idx = 0; idx < efi->efi_nparts; idx++) { 6111 6112 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx); 6113 6114 if (efi->efi_parts[idx].p_size == 0) { 6115 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice)); 6116 continue; 6117 } 6118 6119 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */ 6120 switch (efi->efi_parts[idx].p_tag) { 6121 case V_SWAP: 6122 case V_USR: 6123 case V_BACKUP: 6124 case V_VAR: 6125 case V_HOME: 6126 case V_ALTSCTR: 6127 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice)); 6128 continue; 6129 default: 6130 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice)); 6131 break; 6132 } 6133 6134 /* skip unmountable and readonly slices */ 6135 switch (efi->efi_parts[idx].p_flag) { 6136 case V_UNMNT: 6137 case V_RONLY: 6138 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice)); 6139 continue; 6140 default: 6141 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice)); 6142 break; 6143 } 6144 6145 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) { 6146 return (-1); 6147 } 6148 } 6149 6150 return (0); 6151 } 6152 6153 /* 6154 * s0 is a basename not a full path 6155 */ 6156 static int 6157 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt) 6158 { 6159 struct vtoc vtoc; 6160 struct dk_gpt *efi; 6161 char s0path[PATH_MAX]; 6162 struct stat sbuf; 6163 int e_flag; 6164 int v_flag; 6165 int retval; 6166 int err; 6167 int fd; 6168 const char *fcn = "process_slice0()"; 6169 6170 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0); 6171 6172 if (stat(s0path, &sbuf) == -1) { 6173 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path)); 6174 return (0); 6175 } 6176 6177 fd = open(s0path, O_NONBLOCK|O_RDONLY); 6178 if (fd == -1) { 6179 bam_error(OPEN_FAIL, s0path, strerror(errno)); 6180 return (0); 6181 } 6182 6183 e_flag = v_flag = 0; 6184 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err; 6185 switch (retval) { 6186 case VT_EIO: 6187 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path)); 6188 break; 6189 case VT_EINVAL: 6190 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path)); 6191 break; 6192 case VT_ERROR: 6193 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path)); 6194 break; 6195 case VT_ENOTSUP: 6196 e_flag = 1; 6197 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path)); 6198 break; 6199 case 0: 6200 v_flag = 1; 6201 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path)); 6202 break; 6203 default: 6204 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path)); 6205 break; 6206 } 6207 6208 6209 if (e_flag) { 6210 e_flag = 0; 6211 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err; 6212 switch (retval) { 6213 case VT_EIO: 6214 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path)); 6215 break; 6216 case VT_EINVAL: 6217 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path)); 6218 break; 6219 case VT_ERROR: 6220 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path)); 6221 break; 6222 case VT_ENOTSUP: 6223 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path)); 6224 break; 6225 case 0: 6226 e_flag = 1; 6227 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path)); 6228 break; 6229 default: 6230 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path)); 6231 break; 6232 } 6233 } 6234 6235 (void) close(fd); 6236 6237 if (v_flag) { 6238 retval = process_vtoc_slices(s0, 6239 &vtoc, tfp, mhp, tmpmnt); 6240 } else if (e_flag) { 6241 retval = process_efi_slices(s0, 6242 efi, tfp, mhp, tmpmnt); 6243 } else { 6244 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path)); 6245 return (0); 6246 } 6247 6248 return (retval); 6249 } 6250 6251 /* 6252 * Find and create a list of all existing UFS boot signatures 6253 */ 6254 static int 6255 FindAllUfsSignatures(void) 6256 { 6257 mhash_t *mnttab_hash; 6258 DIR *dirp = NULL; 6259 struct dirent *dp; 6260 char tmpmnt[PATH_MAX]; 6261 char cmd[PATH_MAX]; 6262 struct stat sb; 6263 int fd; 6264 FILE *tfp; 6265 size_t len; 6266 int ret; 6267 int error; 6268 const char *fcn = "FindAllUfsSignatures()"; 6269 6270 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) { 6271 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST); 6272 return (0); 6273 } 6274 6275 fd = open(UFS_SIGNATURE_LIST".tmp", 6276 O_RDWR|O_CREAT|O_TRUNC, 0644); 6277 error = errno; 6278 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1); 6279 if (fd == -1) { 6280 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 6281 return (-1); 6282 } 6283 6284 ret = close(fd); 6285 error = errno; 6286 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1); 6287 if (ret == -1) { 6288 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 6289 strerror(error)); 6290 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6291 return (-1); 6292 } 6293 6294 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a"); 6295 error = errno; 6296 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL); 6297 if (tfp == NULL) { 6298 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 6299 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6300 return (-1); 6301 } 6302 6303 mnttab_hash = cache_mnttab(); 6304 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL); 6305 if (mnttab_hash == NULL) { 6306 (void) fclose(tfp); 6307 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6308 bam_error(CACHE_MNTTAB_FAIL, fcn); 6309 return (-1); 6310 } 6311 6312 (void) snprintf(tmpmnt, sizeof (tmpmnt), 6313 "/tmp/bootadm_ufs_sign_mnt.%d", getpid()); 6314 (void) unlink(tmpmnt); 6315 6316 ret = mkdirp(tmpmnt, DIR_PERMS); 6317 error = errno; 6318 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1); 6319 if (ret == -1) { 6320 bam_error(MKDIR_FAILED, tmpmnt, strerror(error)); 6321 free_mnttab(mnttab_hash); 6322 (void) fclose(tfp); 6323 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6324 return (-1); 6325 } 6326 6327 dirp = opendir("/dev/rdsk"); 6328 error = errno; 6329 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL); 6330 if (dirp == NULL) { 6331 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error)); 6332 goto fail; 6333 } 6334 6335 while (dp = readdir(dirp)) { 6336 if (strcmp(dp->d_name, ".") == 0 || 6337 strcmp(dp->d_name, "..") == 0) 6338 continue; 6339 6340 /* 6341 * we only look for the s0 slice. This is guranteed to 6342 * have 's' at len - 2. 6343 */ 6344 len = strlen(dp->d_name); 6345 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') { 6346 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name)); 6347 continue; 6348 } 6349 6350 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt); 6351 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1); 6352 if (ret == -1) 6353 goto fail; 6354 } 6355 6356 (void) closedir(dirp); 6357 free_mnttab(mnttab_hash); 6358 (void) rmdir(tmpmnt); 6359 6360 ret = fclose(tfp); 6361 error = errno; 6362 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF); 6363 if (ret == EOF) { 6364 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 6365 strerror(error)); 6366 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6367 return (-1); 6368 } 6369 6370 /* We have a list of existing GRUB signatures. Sort it first */ 6371 (void) snprintf(cmd, sizeof (cmd), 6372 "/usr/bin/sort -u %s.tmp > %s.sorted", 6373 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST); 6374 6375 ret = exec_cmd(cmd, NULL); 6376 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1); 6377 if (ret != 0) { 6378 bam_error(GRUBSIGN_SORT_FAILED); 6379 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 6380 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6381 return (-1); 6382 } 6383 6384 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6385 6386 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST); 6387 error = errno; 6388 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1); 6389 if (ret == -1) { 6390 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error)); 6391 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 6392 return (-1); 6393 } 6394 6395 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) { 6396 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST)); 6397 } 6398 6399 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6400 return (0); 6401 6402 fail: 6403 if (dirp) 6404 (void) closedir(dirp); 6405 free_mnttab(mnttab_hash); 6406 (void) rmdir(tmpmnt); 6407 (void) fclose(tfp); 6408 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6409 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6410 return (-1); 6411 } 6412 6413 static char * 6414 create_ufs_sign(void) 6415 { 6416 struct stat sb; 6417 int signnum = -1; 6418 char tmpsign[MAXNAMELEN + 1]; 6419 char *numstr; 6420 int i; 6421 FILE *tfp; 6422 int ret; 6423 int error; 6424 const char *fcn = "create_ufs_sign()"; 6425 6426 bam_print(SEARCHING_UFS_SIGN); 6427 6428 ret = FindAllUfsSignatures(); 6429 INJECT_ERROR1("FIND_ALL_UFS", ret = -1); 6430 if (ret == -1) { 6431 bam_error(ERR_FIND_UFS_SIGN); 6432 return (NULL); 6433 } 6434 6435 /* Make sure the list exists and is owned by root */ 6436 INJECT_ERROR1("SIGNLIST_NOT_CREATED", 6437 (void) unlink(UFS_SIGNATURE_LIST)); 6438 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) { 6439 (void) unlink(UFS_SIGNATURE_LIST); 6440 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST); 6441 return (NULL); 6442 } 6443 6444 if (sb.st_size == 0) { 6445 bam_print(GRUBSIGN_UFS_NONE); 6446 i = 0; 6447 goto found; 6448 } 6449 6450 /* The signature list was sorted when it was created */ 6451 tfp = fopen(UFS_SIGNATURE_LIST, "r"); 6452 error = errno; 6453 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL); 6454 if (tfp == NULL) { 6455 bam_error(UFS_SIGNATURE_LIST_OPENERR, 6456 UFS_SIGNATURE_LIST, strerror(error)); 6457 (void) unlink(UFS_SIGNATURE_LIST); 6458 return (NULL); 6459 } 6460 6461 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) { 6462 6463 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 6464 strlen(GRUBSIGN_UFS_PREFIX)) != 0) { 6465 (void) fclose(tfp); 6466 (void) unlink(UFS_SIGNATURE_LIST); 6467 bam_error(UFS_BADSIGN, tmpsign); 6468 return (NULL); 6469 } 6470 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX); 6471 6472 if (numstr[0] == '\0' || !isdigit(numstr[0])) { 6473 (void) fclose(tfp); 6474 (void) unlink(UFS_SIGNATURE_LIST); 6475 bam_error(UFS_BADSIGN, tmpsign); 6476 return (NULL); 6477 } 6478 6479 signnum = atoi(numstr); 6480 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1); 6481 if (signnum < 0) { 6482 (void) fclose(tfp); 6483 (void) unlink(UFS_SIGNATURE_LIST); 6484 bam_error(UFS_BADSIGN, tmpsign); 6485 return (NULL); 6486 } 6487 6488 if (i != signnum) { 6489 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i)); 6490 break; 6491 } 6492 } 6493 6494 (void) fclose(tfp); 6495 6496 found: 6497 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i); 6498 6499 /* add the ufs signature to the /var/run list of signatures */ 6500 ret = ufs_add_to_sign_list(tmpsign); 6501 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1); 6502 if (ret == -1) { 6503 (void) unlink(UFS_SIGNATURE_LIST); 6504 bam_error(FAILED_ADD_SIGNLIST, tmpsign); 6505 return (NULL); 6506 } 6507 6508 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6509 6510 return (s_strdup(tmpsign)); 6511 } 6512 6513 static char * 6514 get_fstype(char *osroot) 6515 { 6516 FILE *mntfp; 6517 struct mnttab mp = {0}; 6518 struct mnttab mpref = {0}; 6519 int error; 6520 int ret; 6521 const char *fcn = "get_fstype()"; 6522 6523 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL); 6524 if (osroot == NULL) { 6525 bam_error(GET_FSTYPE_ARGS); 6526 return (NULL); 6527 } 6528 6529 mntfp = fopen(MNTTAB, "r"); 6530 error = errno; 6531 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL); 6532 if (mntfp == NULL) { 6533 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 6534 return (NULL); 6535 } 6536 6537 if (*osroot == '\0') 6538 mpref.mnt_mountp = "/"; 6539 else 6540 mpref.mnt_mountp = osroot; 6541 6542 ret = getmntany(mntfp, &mp, &mpref); 6543 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1); 6544 if (ret != 0) { 6545 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB); 6546 (void) fclose(mntfp); 6547 return (NULL); 6548 } 6549 (void) fclose(mntfp); 6550 6551 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL); 6552 if (mp.mnt_fstype == NULL) { 6553 bam_error(MNTTAB_FSTYPE_NULL, osroot); 6554 return (NULL); 6555 } 6556 6557 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6558 6559 return (s_strdup(mp.mnt_fstype)); 6560 } 6561 6562 static char * 6563 create_zfs_sign(char *osdev) 6564 { 6565 char tmpsign[PATH_MAX]; 6566 char *pool; 6567 const char *fcn = "create_zfs_sign()"; 6568 6569 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev)); 6570 6571 /* 6572 * First find the pool name 6573 */ 6574 pool = get_pool(osdev); 6575 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL); 6576 if (pool == NULL) { 6577 bam_error(GET_POOL_FAILED, osdev); 6578 return (NULL); 6579 } 6580 6581 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool); 6582 6583 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign)); 6584 6585 free(pool); 6586 6587 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6588 6589 return (s_strdup(tmpsign)); 6590 } 6591 6592 static char * 6593 create_new_sign(char *osdev, char *fstype) 6594 { 6595 char *sign; 6596 const char *fcn = "create_new_sign()"; 6597 6598 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs"); 6599 6600 if (strcmp(fstype, "zfs") == 0) { 6601 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn)); 6602 sign = create_zfs_sign(osdev); 6603 } else if (strcmp(fstype, "ufs") == 0) { 6604 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn)); 6605 sign = create_ufs_sign(); 6606 } else { 6607 bam_error(GRUBSIGN_NOTSUP, fstype); 6608 sign = NULL; 6609 } 6610 6611 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>")); 6612 return (sign); 6613 } 6614 6615 static int 6616 set_backup_common(char *mntpt, char *sign) 6617 { 6618 FILE *bfp; 6619 char backup[PATH_MAX]; 6620 char tmpsign[PATH_MAX]; 6621 int error; 6622 char *bdir; 6623 char *backup_dup; 6624 struct stat sb; 6625 int ret; 6626 const char *fcn = "set_backup_common()"; 6627 6628 (void) snprintf(backup, sizeof (backup), "%s%s", 6629 mntpt, GRUBSIGN_BACKUP); 6630 6631 /* First read the backup */ 6632 bfp = fopen(backup, "r"); 6633 if (bfp != NULL) { 6634 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) { 6635 if (strcmp(tmpsign, sign) == 0) { 6636 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign)); 6637 (void) fclose(bfp); 6638 return (0); 6639 } 6640 } 6641 (void) fclose(bfp); 6642 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign)); 6643 } else { 6644 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup)); 6645 } 6646 6647 /* 6648 * Didn't find the correct signature. First create 6649 * the directory if necessary. 6650 */ 6651 6652 /* dirname() modifies its argument so dup it */ 6653 backup_dup = s_strdup(backup); 6654 bdir = dirname(backup_dup); 6655 assert(bdir); 6656 6657 ret = stat(bdir, &sb); 6658 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1); 6659 if (ret == -1) { 6660 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir)); 6661 ret = mkdirp(bdir, DIR_PERMS); 6662 error = errno; 6663 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1); 6664 if (ret == -1) { 6665 bam_error(GRUBSIGN_BACKUP_MKDIRERR, 6666 GRUBSIGN_BACKUP, strerror(error)); 6667 free(backup_dup); 6668 return (-1); 6669 } 6670 } 6671 free(backup_dup); 6672 6673 /* 6674 * Open the backup in append mode to add the correct 6675 * signature; 6676 */ 6677 bfp = fopen(backup, "a"); 6678 error = errno; 6679 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL); 6680 if (bfp == NULL) { 6681 bam_error(GRUBSIGN_BACKUP_OPENERR, 6682 GRUBSIGN_BACKUP, strerror(error)); 6683 return (-1); 6684 } 6685 6686 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign); 6687 6688 ret = fputs(tmpsign, bfp); 6689 error = errno; 6690 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0); 6691 if (ret != strlen(tmpsign)) { 6692 bam_error(GRUBSIGN_BACKUP_WRITEERR, 6693 GRUBSIGN_BACKUP, strerror(error)); 6694 (void) fclose(bfp); 6695 return (-1); 6696 } 6697 6698 (void) fclose(bfp); 6699 6700 if (bam_verbose) 6701 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP); 6702 6703 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6704 6705 return (0); 6706 } 6707 6708 static int 6709 set_backup_ufs(char *osroot, char *sign) 6710 { 6711 const char *fcn = "set_backup_ufs()"; 6712 6713 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign)); 6714 return (set_backup_common(osroot, sign)); 6715 } 6716 6717 static int 6718 set_backup_zfs(char *osdev, char *sign) 6719 { 6720 char *pool; 6721 char *mntpt; 6722 zfs_mnted_t mnted; 6723 int ret; 6724 const char *fcn = "set_backup_zfs()"; 6725 6726 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign)); 6727 6728 pool = get_pool(osdev); 6729 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL); 6730 if (pool == NULL) { 6731 bam_error(GET_POOL_FAILED, osdev); 6732 return (-1); 6733 } 6734 6735 mntpt = mount_top_dataset(pool, &mnted); 6736 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL); 6737 if (mntpt == NULL) { 6738 bam_error(FAIL_MNT_TOP_DATASET, pool); 6739 free(pool); 6740 return (-1); 6741 } 6742 6743 ret = set_backup_common(mntpt, sign); 6744 6745 (void) umount_top_dataset(pool, mnted, mntpt); 6746 6747 free(pool); 6748 6749 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1); 6750 if (ret == 0) { 6751 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6752 } else { 6753 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6754 } 6755 6756 return (ret); 6757 } 6758 6759 static int 6760 set_backup(char *osroot, char *osdev, char *sign, char *fstype) 6761 { 6762 const char *fcn = "set_backup()"; 6763 int ret; 6764 6765 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs"); 6766 6767 if (strcmp(fstype, "ufs") == 0) { 6768 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn)); 6769 ret = set_backup_ufs(osroot, sign); 6770 } else if (strcmp(fstype, "zfs") == 0) { 6771 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn)); 6772 ret = set_backup_zfs(osdev, sign); 6773 } else { 6774 bam_error(GRUBSIGN_NOTSUP, fstype); 6775 ret = -1; 6776 } 6777 6778 if (ret == 0) { 6779 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6780 } else { 6781 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6782 } 6783 6784 return (ret); 6785 } 6786 6787 static int 6788 set_primary_common(char *mntpt, char *sign) 6789 { 6790 char signfile[PATH_MAX]; 6791 char signdir[PATH_MAX]; 6792 struct stat sb; 6793 int fd; 6794 int error; 6795 int ret; 6796 const char *fcn = "set_primary_common()"; 6797 6798 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s", 6799 mntpt, GRUBSIGN_DIR, sign); 6800 6801 if (stat(signfile, &sb) != -1) { 6802 if (bam_verbose) 6803 bam_print(PRIMARY_SIGN_EXISTS, sign); 6804 return (0); 6805 } else { 6806 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile)); 6807 } 6808 6809 (void) snprintf(signdir, sizeof (signdir), "%s/%s", 6810 mntpt, GRUBSIGN_DIR); 6811 6812 if (stat(signdir, &sb) == -1) { 6813 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir)); 6814 ret = mkdirp(signdir, DIR_PERMS); 6815 error = errno; 6816 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1); 6817 if (ret == -1) { 6818 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno)); 6819 return (-1); 6820 } 6821 } 6822 6823 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444); 6824 error = errno; 6825 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1); 6826 if (fd == -1) { 6827 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error)); 6828 return (-1); 6829 } 6830 6831 ret = fsync(fd); 6832 error = errno; 6833 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1); 6834 if (ret != 0) { 6835 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error)); 6836 } 6837 6838 (void) close(fd); 6839 6840 if (bam_verbose) 6841 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile); 6842 6843 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6844 6845 return (0); 6846 } 6847 6848 static int 6849 set_primary_ufs(char *osroot, char *sign) 6850 { 6851 const char *fcn = "set_primary_ufs()"; 6852 6853 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign)); 6854 return (set_primary_common(osroot, sign)); 6855 } 6856 6857 static int 6858 set_primary_zfs(char *osdev, char *sign) 6859 { 6860 char *pool; 6861 char *mntpt; 6862 zfs_mnted_t mnted; 6863 int ret; 6864 const char *fcn = "set_primary_zfs()"; 6865 6866 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign)); 6867 6868 pool = get_pool(osdev); 6869 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL); 6870 if (pool == NULL) { 6871 bam_error(GET_POOL_FAILED, osdev); 6872 return (-1); 6873 } 6874 6875 /* Pool name must exist in the sign */ 6876 ret = (strstr(sign, pool) != NULL); 6877 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0); 6878 if (ret == 0) { 6879 bam_error(POOL_SIGN_INCOMPAT, pool, sign); 6880 free(pool); 6881 return (-1); 6882 } 6883 6884 mntpt = mount_top_dataset(pool, &mnted); 6885 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL); 6886 if (mntpt == NULL) { 6887 bam_error(FAIL_MNT_TOP_DATASET, pool); 6888 free(pool); 6889 return (-1); 6890 } 6891 6892 ret = set_primary_common(mntpt, sign); 6893 6894 (void) umount_top_dataset(pool, mnted, mntpt); 6895 6896 free(pool); 6897 6898 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1); 6899 if (ret == 0) { 6900 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6901 } else { 6902 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6903 } 6904 6905 return (ret); 6906 } 6907 6908 static int 6909 set_primary(char *osroot, char *osdev, char *sign, char *fstype) 6910 { 6911 const char *fcn = "set_primary()"; 6912 int ret; 6913 6914 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs"); 6915 if (strcmp(fstype, "ufs") == 0) { 6916 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn)); 6917 ret = set_primary_ufs(osroot, sign); 6918 } else if (strcmp(fstype, "zfs") == 0) { 6919 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn)); 6920 ret = set_primary_zfs(osdev, sign); 6921 } else { 6922 bam_error(GRUBSIGN_NOTSUP, fstype); 6923 ret = -1; 6924 } 6925 6926 if (ret == 0) { 6927 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6928 } else { 6929 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6930 } 6931 6932 return (ret); 6933 } 6934 6935 static int 6936 ufs_add_to_sign_list(char *sign) 6937 { 6938 FILE *tfp; 6939 char signline[MAXNAMELEN]; 6940 char cmd[PATH_MAX]; 6941 int ret; 6942 int error; 6943 const char *fcn = "ufs_add_to_sign_list()"; 6944 6945 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5"); 6946 if (strncmp(sign, GRUBSIGN_UFS_PREFIX, 6947 strlen(GRUBSIGN_UFS_PREFIX)) != 0) { 6948 bam_error(INVALID_UFS_SIGN, sign); 6949 (void) unlink(UFS_SIGNATURE_LIST); 6950 return (-1); 6951 } 6952 6953 /* 6954 * most failures in this routine are not a fatal error 6955 * We simply unlink the /var/run file and continue 6956 */ 6957 6958 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp"); 6959 error = errno; 6960 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1); 6961 if (ret == -1) { 6962 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp", 6963 strerror(error)); 6964 (void) unlink(UFS_SIGNATURE_LIST); 6965 return (0); 6966 } 6967 6968 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a"); 6969 error = errno; 6970 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL); 6971 if (tfp == NULL) { 6972 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 6973 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6974 return (0); 6975 } 6976 6977 (void) snprintf(signline, sizeof (signline), "%s\n", sign); 6978 6979 ret = fputs(signline, tfp); 6980 error = errno; 6981 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0); 6982 if (ret != strlen(signline)) { 6983 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error)); 6984 (void) fclose(tfp); 6985 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6986 return (0); 6987 } 6988 6989 ret = fclose(tfp); 6990 error = errno; 6991 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF); 6992 if (ret == EOF) { 6993 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 6994 strerror(error)); 6995 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6996 return (0); 6997 } 6998 6999 /* Sort the list again */ 7000 (void) snprintf(cmd, sizeof (cmd), 7001 "/usr/bin/sort -u %s.tmp > %s.sorted", 7002 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST); 7003 7004 ret = exec_cmd(cmd, NULL); 7005 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1); 7006 if (ret != 0) { 7007 bam_error(GRUBSIGN_SORT_FAILED); 7008 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 7009 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 7010 return (0); 7011 } 7012 7013 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 7014 7015 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST); 7016 error = errno; 7017 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1); 7018 if (ret == -1) { 7019 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error)); 7020 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 7021 return (0); 7022 } 7023 7024 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7025 7026 return (0); 7027 } 7028 7029 static int 7030 set_signature(char *osroot, char *osdev, char *sign, char *fstype) 7031 { 7032 int ret; 7033 const char *fcn = "set_signature()"; 7034 7035 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype)); 7036 7037 ret = set_backup(osroot, osdev, sign, fstype); 7038 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1); 7039 if (ret == -1) { 7040 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7041 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev); 7042 return (-1); 7043 } 7044 7045 ret = set_primary(osroot, osdev, sign, fstype); 7046 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1); 7047 7048 if (ret == 0) { 7049 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7050 } else { 7051 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7052 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev); 7053 7054 } 7055 return (ret); 7056 } 7057 7058 char * 7059 get_grubsign(char *osroot, char *osdev) 7060 { 7061 char *grubsign; /* (<sign>,#,#) */ 7062 char *slice; 7063 int fdiskpart; 7064 char *sign; 7065 char *fstype; 7066 int ret; 7067 const char *fcn = "get_grubsign()"; 7068 7069 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev)); 7070 fstype = get_fstype(osroot); 7071 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL); 7072 if (fstype == NULL) { 7073 bam_error(GET_FSTYPE_FAILED, osroot); 7074 return (NULL); 7075 } 7076 7077 sign = find_existing_sign(osroot, osdev, fstype); 7078 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL); 7079 if (sign == NULL) { 7080 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev)); 7081 sign = create_new_sign(osdev, fstype); 7082 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL); 7083 if (sign == NULL) { 7084 bam_error(GRUBSIGN_CREATE_FAIL, osdev); 7085 free(fstype); 7086 return (NULL); 7087 } 7088 } 7089 7090 ret = set_signature(osroot, osdev, sign, fstype); 7091 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1); 7092 if (ret == -1) { 7093 bam_error(GRUBSIGN_WRITE_FAIL, osdev); 7094 free(sign); 7095 free(fstype); 7096 (void) unlink(UFS_SIGNATURE_LIST); 7097 return (NULL); 7098 } 7099 7100 free(fstype); 7101 7102 if (bam_verbose) 7103 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev); 7104 7105 fdiskpart = get_partition(osdev); 7106 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1); 7107 if (fdiskpart == -1) { 7108 bam_error(FDISKPART_FAIL, osdev); 7109 free(sign); 7110 return (NULL); 7111 } 7112 7113 slice = strrchr(osdev, 's'); 7114 7115 grubsign = s_calloc(1, MAXNAMELEN + 10); 7116 if (slice) { 7117 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)", 7118 sign, fdiskpart, slice[1] + 'a' - '0'); 7119 } else 7120 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)", 7121 sign, fdiskpart); 7122 7123 free(sign); 7124 7125 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign)); 7126 7127 return (grubsign); 7128 } 7129 7130 static char * 7131 get_title(char *rootdir) 7132 { 7133 static char title[80]; 7134 char *cp = NULL; 7135 char release[PATH_MAX]; 7136 FILE *fp; 7137 const char *fcn = "get_title()"; 7138 7139 /* open the /etc/release file */ 7140 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir); 7141 7142 fp = fopen(release, "r"); 7143 if (fp == NULL) { 7144 bam_error(OPEN_FAIL, release, strerror(errno)); 7145 cp = NULL; 7146 goto out; 7147 } 7148 7149 /* grab first line of /etc/release */ 7150 cp = s_fgets(title, sizeof (title), fp); 7151 if (cp) { 7152 while (isspace(*cp)) /* remove leading spaces */ 7153 cp++; 7154 } 7155 7156 (void) fclose(fp); 7157 7158 out: 7159 cp = cp ? cp : "Oracle Solaris"; 7160 7161 BAM_DPRINTF((D_GET_TITLE, fcn, cp)); 7162 7163 return (cp); 7164 } 7165 7166 char * 7167 get_special(char *mountp) 7168 { 7169 FILE *mntfp; 7170 struct mnttab mp = {0}; 7171 struct mnttab mpref = {0}; 7172 int error; 7173 int ret; 7174 const char *fcn = "get_special()"; 7175 7176 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL); 7177 if (mountp == NULL) { 7178 bam_error(GET_SPECIAL_NULL_MNTPT); 7179 return (NULL); 7180 } 7181 7182 mntfp = fopen(MNTTAB, "r"); 7183 error = errno; 7184 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL); 7185 if (mntfp == NULL) { 7186 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 7187 return (NULL); 7188 } 7189 7190 if (*mountp == '\0') 7191 mpref.mnt_mountp = "/"; 7192 else 7193 mpref.mnt_mountp = mountp; 7194 7195 ret = getmntany(mntfp, &mp, &mpref); 7196 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1); 7197 if (ret != 0) { 7198 (void) fclose(mntfp); 7199 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp)); 7200 return (NULL); 7201 } 7202 (void) fclose(mntfp); 7203 7204 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special)); 7205 7206 return (s_strdup(mp.mnt_special)); 7207 } 7208 7209 static void 7210 free_physarray(char **physarray, int n) 7211 { 7212 int i; 7213 const char *fcn = "free_physarray()"; 7214 7215 assert(physarray); 7216 assert(n); 7217 7218 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n)); 7219 7220 for (i = 0; i < n; i++) { 7221 free(physarray[i]); 7222 } 7223 free(physarray); 7224 7225 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7226 } 7227 7228 static int 7229 zfs_get_physical(char *special, char ***physarray, int *n) 7230 { 7231 char sdup[PATH_MAX]; 7232 char cmd[PATH_MAX]; 7233 char dsk[PATH_MAX]; 7234 char *pool; 7235 filelist_t flist = {0}; 7236 line_t *lp; 7237 line_t *startlp; 7238 char *comp1; 7239 int i; 7240 int ret; 7241 const char *fcn = "zfs_get_physical()"; 7242 7243 assert(special); 7244 7245 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special)); 7246 7247 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo"); 7248 if (special[0] == '/') { 7249 bam_error(INVALID_ZFS_SPECIAL, special); 7250 return (-1); 7251 } 7252 7253 (void) strlcpy(sdup, special, sizeof (sdup)); 7254 7255 pool = strtok(sdup, "/"); 7256 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL); 7257 if (pool == NULL) { 7258 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special); 7259 return (-1); 7260 } 7261 7262 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool); 7263 7264 ret = exec_cmd(cmd, &flist); 7265 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1); 7266 if (ret != 0) { 7267 bam_error(ZFS_GET_POOL_STATUS, pool); 7268 return (-1); 7269 } 7270 7271 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL); 7272 if (flist.head == NULL) { 7273 bam_error(BAD_ZPOOL_STATUS, pool); 7274 filelist_free(&flist); 7275 return (-1); 7276 } 7277 7278 for (lp = flist.head; lp; lp = lp->next) { 7279 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line)); 7280 comp1 = strtok(lp->line, " \t"); 7281 if (comp1 == NULL) { 7282 free(lp->line); 7283 lp->line = NULL; 7284 } else { 7285 comp1 = s_strdup(comp1); 7286 free(lp->line); 7287 lp->line = comp1; 7288 } 7289 } 7290 7291 for (lp = flist.head; lp; lp = lp->next) { 7292 if (lp->line == NULL) 7293 continue; 7294 if (strcmp(lp->line, pool) == 0) { 7295 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool)); 7296 break; 7297 } 7298 } 7299 7300 if (lp == NULL) { 7301 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool); 7302 filelist_free(&flist); 7303 return (-1); 7304 } 7305 7306 startlp = lp->next; 7307 for (i = 0, lp = startlp; lp; lp = lp->next) { 7308 if (lp->line == NULL) 7309 continue; 7310 if (strcmp(lp->line, "mirror") == 0) 7311 continue; 7312 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0) 7313 break; 7314 i++; 7315 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i)); 7316 } 7317 7318 if (i == 0) { 7319 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool); 7320 filelist_free(&flist); 7321 return (-1); 7322 } 7323 7324 *n = i; 7325 *physarray = s_calloc(*n, sizeof (char *)); 7326 for (i = 0, lp = startlp; lp; lp = lp->next) { 7327 if (lp->line == NULL) 7328 continue; 7329 if (strcmp(lp->line, "mirror") == 0) 7330 continue; 7331 if (strcmp(lp->line, "errors:") == 0) 7332 break; 7333 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 7334 strncmp(lp->line, "/dev/rdsk/", 7335 strlen("/dev/rdsk/")) != 0) { 7336 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s", 7337 lp->line); 7338 } else { 7339 (void) strlcpy(dsk, lp->line, sizeof (dsk)); 7340 } 7341 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool)); 7342 (*physarray)[i++] = s_strdup(dsk); 7343 } 7344 7345 assert(i == *n); 7346 7347 filelist_free(&flist); 7348 7349 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7350 return (0); 7351 } 7352 7353 /* 7354 * Certain services needed to run metastat successfully may not 7355 * be enabled. Enable them now. 7356 */ 7357 /* 7358 * Checks if the specified service is online 7359 * Returns: 1 if the service is online 7360 * 0 if the service is not online 7361 * -1 on error 7362 */ 7363 static int 7364 is_svc_online(char *svc) 7365 { 7366 char *state; 7367 const char *fcn = "is_svc_online()"; 7368 7369 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc)); 7370 7371 state = smf_get_state(svc); 7372 INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL); 7373 if (state == NULL) { 7374 bam_error(GET_SVC_STATE_ERR, svc); 7375 return (-1); 7376 } 7377 BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc)); 7378 7379 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) { 7380 BAM_DPRINTF((D_SVC_ONLINE, fcn, svc)); 7381 free(state); 7382 return (1); 7383 } 7384 7385 BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc)); 7386 7387 free(state); 7388 7389 return (0); 7390 } 7391 7392 static int 7393 enable_svc(char *svc) 7394 { 7395 int ret; 7396 int sleeptime; 7397 const char *fcn = "enable_svc()"; 7398 7399 ret = is_svc_online(svc); 7400 if (ret == -1) { 7401 bam_error(SVC_IS_ONLINE_FAILED, svc); 7402 return (-1); 7403 } else if (ret == 1) { 7404 BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc)); 7405 return (0); 7406 } 7407 7408 /* Service is not enabled. Enable it now. */ 7409 ret = smf_enable_instance(svc, 0); 7410 INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1); 7411 if (ret != 0) { 7412 bam_error(ENABLE_SVC_FAILED, svc); 7413 return (-1); 7414 } 7415 7416 BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc)); 7417 7418 sleeptime = 0; 7419 do { 7420 ret = is_svc_online(svc); 7421 INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1); 7422 INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1); 7423 INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0); 7424 if (ret == -1) { 7425 bam_error(ERR_SVC_GET_ONLINE, svc); 7426 return (-1); 7427 } else if (ret == 1) { 7428 BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc)); 7429 return (1); 7430 } 7431 (void) sleep(1); 7432 } while (++sleeptime < 60); 7433 7434 bam_error(TIMEOUT_ENABLE_SVC, svc); 7435 7436 return (-1); 7437 } 7438 7439 static int 7440 ufs_get_physical(char *special, char ***physarray, int *n) 7441 { 7442 char cmd[PATH_MAX]; 7443 char *shortname; 7444 filelist_t flist = {0}; 7445 char *meta; 7446 char *type; 7447 char *comp1; 7448 char *comp2; 7449 char *comp3; 7450 char *comp4; 7451 int i; 7452 line_t *lp; 7453 int ret; 7454 char *svc; 7455 const char *fcn = "ufs_get_physical()"; 7456 7457 assert(special); 7458 7459 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special)); 7460 7461 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) { 7462 bam_error(UFS_GET_PHYS_NOT_SVM, special); 7463 return (-1); 7464 } 7465 7466 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) { 7467 shortname = special + strlen("/dev/md/dsk/"); 7468 } else if (strncmp(special, "/dev/md/rdsk/", 7469 strlen("/dev/md/rdsk/")) == 0) { 7470 shortname = special + strlen("/dev/md/rdsk"); 7471 } else { 7472 bam_error(UFS_GET_PHYS_INVALID_SVM, special); 7473 return (-1); 7474 } 7475 7476 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname)); 7477 7478 svc = "network/rpc/meta:default"; 7479 if (enable_svc(svc) == -1) { 7480 bam_error(UFS_SVM_METASTAT_SVC_ERR, svc); 7481 } 7482 7483 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname); 7484 7485 ret = exec_cmd(cmd, &flist); 7486 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1); 7487 if (ret != 0) { 7488 bam_error(UFS_SVM_METASTAT_ERR, shortname); 7489 return (-1); 7490 } 7491 7492 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL); 7493 if (flist.head == NULL) { 7494 bam_error(BAD_UFS_SVM_METASTAT, shortname); 7495 filelist_free(&flist); 7496 return (-1); 7497 } 7498 7499 /* 7500 * Check if not a mirror. We only parse a single metadevice 7501 * if not a mirror 7502 */ 7503 meta = strtok(flist.head->line, " \t"); 7504 type = strtok(NULL, " \t"); 7505 if (meta == NULL || type == NULL) { 7506 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname); 7507 filelist_free(&flist); 7508 return (-1); 7509 } 7510 if (strcmp(type, "-m") != 0) { 7511 comp1 = strtok(NULL, " \t"); 7512 comp2 = strtok(NULL, " \t"); 7513 if (comp1 == NULL || comp2 != NULL) { 7514 bam_error(INVALID_UFS_SVM_METASTAT, shortname); 7515 filelist_free(&flist); 7516 return (-1); 7517 } 7518 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname)); 7519 *physarray = s_calloc(1, sizeof (char *)); 7520 (*physarray)[0] = s_strdup(comp1); 7521 *n = 1; 7522 filelist_free(&flist); 7523 return (0); 7524 } 7525 7526 /* 7527 * Okay we have a mirror. Everything after the first line 7528 * is a submirror 7529 */ 7530 for (i = 0, lp = flist.head->next; lp; lp = lp->next) { 7531 if (strstr(lp->line, "/dev/dsk/") == NULL && 7532 strstr(lp->line, "/dev/rdsk/") == NULL) { 7533 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname); 7534 filelist_free(&flist); 7535 return (-1); 7536 } 7537 i++; 7538 } 7539 7540 *physarray = s_calloc(i, sizeof (char *)); 7541 *n = i; 7542 7543 for (i = 0, lp = flist.head->next; lp; lp = lp->next) { 7544 comp1 = strtok(lp->line, " \t"); 7545 comp2 = strtok(NULL, " \t"); 7546 comp3 = strtok(NULL, " \t"); 7547 comp4 = strtok(NULL, " \t"); 7548 7549 if (comp3 == NULL || comp4 == NULL || 7550 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 7551 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) { 7552 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname); 7553 filelist_free(&flist); 7554 free_physarray(*physarray, *n); 7555 return (-1); 7556 } 7557 7558 (*physarray)[i++] = s_strdup(comp4); 7559 } 7560 7561 assert(i == *n); 7562 7563 filelist_free(&flist); 7564 7565 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7566 return (0); 7567 } 7568 7569 static int 7570 get_physical(char *menu_root, char ***physarray, int *n) 7571 { 7572 char *special; 7573 int ret; 7574 const char *fcn = "get_physical()"; 7575 7576 assert(menu_root); 7577 assert(physarray); 7578 assert(n); 7579 7580 *physarray = NULL; 7581 *n = 0; 7582 7583 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root)); 7584 7585 /* First get the device special file from /etc/mnttab */ 7586 special = get_special(menu_root); 7587 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL); 7588 if (special == NULL) { 7589 bam_error(GET_SPECIAL_NULL, menu_root); 7590 return (-1); 7591 } 7592 7593 /* If already a physical device nothing to do */ 7594 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 || 7595 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) { 7596 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special)); 7597 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7598 *physarray = s_calloc(1, sizeof (char *)); 7599 (*physarray)[0] = special; 7600 *n = 1; 7601 return (0); 7602 } 7603 7604 if (is_zfs(menu_root)) { 7605 ret = zfs_get_physical(special, physarray, n); 7606 } else if (is_ufs(menu_root)) { 7607 ret = ufs_get_physical(special, physarray, n); 7608 } else { 7609 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special); 7610 ret = -1; 7611 } 7612 7613 free(special); 7614 7615 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1); 7616 if (ret == -1) { 7617 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7618 } else { 7619 int i; 7620 assert (*n > 0); 7621 for (i = 0; i < *n; i++) { 7622 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i])); 7623 } 7624 } 7625 7626 return (ret); 7627 } 7628 7629 static int 7630 is_bootdisk(char *osroot, char *physical) 7631 { 7632 int ret; 7633 char *grubroot; 7634 char *bootp; 7635 const char *fcn = "is_bootdisk()"; 7636 7637 assert(osroot); 7638 assert(physical); 7639 7640 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical)); 7641 7642 bootp = strstr(physical, "p0:boot"); 7643 if (bootp) 7644 *bootp = '\0'; 7645 /* 7646 * We just want the BIOS mapping for menu disk. 7647 * Don't pass menu_root to get_grubroot() as the 7648 * check that it is used for is not relevant here. 7649 * The osroot is immaterial as well - it is only used to 7650 * to find create_diskmap script. Everything hinges on 7651 * "physical" 7652 */ 7653 grubroot = get_grubroot(osroot, physical, NULL); 7654 7655 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL); 7656 if (grubroot == NULL) { 7657 if (bam_verbose) 7658 bam_error(NO_GRUBROOT_FOR_DISK, physical); 7659 return (0); 7660 } 7661 ret = grubroot[3] == '0'; 7662 free(grubroot); 7663 7664 BAM_DPRINTF((D_RETURN_RET, fcn, ret)); 7665 7666 return (ret); 7667 } 7668 7669 /* 7670 * Check if menu is on the boot device 7671 * Return 0 (false) on error 7672 */ 7673 static int 7674 menu_on_bootdisk(char *osroot, char *menu_root) 7675 { 7676 char **physarray; 7677 int ret; 7678 int n; 7679 int i; 7680 int on_bootdisk; 7681 const char *fcn = "menu_on_bootdisk()"; 7682 7683 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 7684 7685 ret = get_physical(menu_root, &physarray, &n); 7686 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1); 7687 if (ret != 0) { 7688 bam_error(GET_PHYSICAL_MENU_NULL, menu_root); 7689 return (0); 7690 } 7691 7692 assert(physarray); 7693 assert(n > 0); 7694 7695 on_bootdisk = 0; 7696 for (i = 0; i < n; i++) { 7697 assert(strncmp(physarray[i], "/dev/dsk/", 7698 strlen("/dev/dsk/")) == 0 || 7699 strncmp(physarray[i], "/dev/rdsk/", 7700 strlen("/dev/rdsk/")) == 0); 7701 7702 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i])); 7703 if (is_bootdisk(osroot, physarray[i])) { 7704 on_bootdisk = 1; 7705 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i])); 7706 } 7707 } 7708 7709 free_physarray(physarray, n); 7710 7711 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1); 7712 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0); 7713 if (on_bootdisk) { 7714 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7715 } else { 7716 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7717 } 7718 7719 return (on_bootdisk); 7720 } 7721 7722 void 7723 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp) 7724 { 7725 const char *fcn = "bam_add_line()"; 7726 7727 assert(mp); 7728 assert(entry); 7729 assert(prev); 7730 assert(lp); 7731 7732 lp->next = prev->next; 7733 if (prev->next) { 7734 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn)); 7735 prev->next->prev = lp; 7736 } else { 7737 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn)); 7738 } 7739 prev->next = lp; 7740 lp->prev = prev; 7741 7742 if (entry->end == prev) { 7743 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn)); 7744 entry->end = lp; 7745 } 7746 if (mp->end == prev) { 7747 assert(lp->next == NULL); 7748 mp->end = lp; 7749 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn)); 7750 } 7751 } 7752 7753 /* 7754 * look for matching bootadm entry with specified parameters 7755 * Here are the rules (based on existing usage): 7756 * - If title is specified, match on title only 7757 * - Else, match on root/findroot, kernel, and module. 7758 * Note that, if root_opt is non-zero, the absence of 7759 * root line is considered a match. 7760 */ 7761 static entry_t * 7762 find_boot_entry( 7763 menu_t *mp, 7764 char *title, 7765 char *kernel, 7766 char *findroot, 7767 char *root, 7768 char *module, 7769 int root_opt, 7770 int *entry_num) 7771 { 7772 int i; 7773 line_t *lp; 7774 entry_t *ent; 7775 const char *fcn = "find_boot_entry()"; 7776 7777 if (entry_num) 7778 *entry_num = BAM_ERROR; 7779 7780 /* find matching entry */ 7781 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) { 7782 lp = ent->start; 7783 7784 /* first line of entry must be bootadm comment */ 7785 lp = ent->start; 7786 if (lp->flags != BAM_COMMENT || 7787 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) { 7788 continue; 7789 } 7790 7791 /* advance to title line */ 7792 lp = lp->next; 7793 if (title) { 7794 if (lp->flags == BAM_TITLE && lp->arg && 7795 strcmp(lp->arg, title) == 0) { 7796 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title)); 7797 break; 7798 } 7799 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg)); 7800 continue; /* check title only */ 7801 } 7802 7803 lp = lp->next; /* advance to root line */ 7804 if (lp == NULL) { 7805 continue; 7806 } else if (lp->cmd != NULL && 7807 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) { 7808 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT", 7809 findroot = NULL); 7810 if (findroot == NULL) { 7811 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL, 7812 fcn, lp->arg)); 7813 continue; 7814 } 7815 /* findroot command found, try match */ 7816 if (strcmp(lp->arg, findroot) != 0) { 7817 BAM_DPRINTF((D_NOMATCH_FINDROOT, 7818 fcn, findroot, lp->arg)); 7819 continue; 7820 } 7821 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot)); 7822 lp = lp->next; /* advance to kernel line */ 7823 } else if (lp->cmd != NULL && 7824 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 7825 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL); 7826 if (root == NULL) { 7827 BAM_DPRINTF((D_NOMATCH_ROOT_NULL, 7828 fcn, lp->arg)); 7829 continue; 7830 } 7831 /* root cmd found, try match */ 7832 if (strcmp(lp->arg, root) != 0) { 7833 BAM_DPRINTF((D_NOMATCH_ROOT, 7834 fcn, root, lp->arg)); 7835 continue; 7836 } 7837 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root)); 7838 lp = lp->next; /* advance to kernel line */ 7839 } else { 7840 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO", 7841 root_opt = 0); 7842 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES", 7843 root_opt = 1); 7844 /* no root command, see if root is optional */ 7845 if (root_opt == 0) { 7846 BAM_DPRINTF((D_NO_ROOT_OPT, fcn)); 7847 continue; 7848 } 7849 BAM_DPRINTF((D_ROOT_OPT, fcn)); 7850 } 7851 7852 if (lp == NULL || lp->next == NULL) { 7853 continue; 7854 } 7855 7856 if (kernel && 7857 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) { 7858 if (!(ent->flags & BAM_ENTRY_FAILSAFE) || 7859 !(ent->flags & BAM_ENTRY_DBOOT) || 7860 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0) 7861 continue; 7862 7863 ent->flags |= BAM_ENTRY_UPGFSKERNEL; 7864 7865 } 7866 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg)); 7867 7868 /* 7869 * Check for matching module entry (failsafe or normal). 7870 * If it fails to match, we go around the loop again. 7871 * For xpv entries, there are two module lines, so we 7872 * do the check twice. 7873 */ 7874 lp = lp->next; /* advance to module line */ 7875 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) || 7876 (((lp = lp->next) != NULL) && 7877 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) { 7878 /* match found */ 7879 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg)); 7880 break; 7881 } 7882 7883 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 && 7884 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 || 7885 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) { 7886 ent->flags |= BAM_ENTRY_UPGFSMODULE; 7887 break; 7888 } 7889 7890 } 7891 7892 if (ent && entry_num) { 7893 *entry_num = i; 7894 } 7895 7896 if (ent) { 7897 BAM_DPRINTF((D_RETURN_RET, fcn, i)); 7898 } else { 7899 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR)); 7900 } 7901 return (ent); 7902 } 7903 7904 static int 7905 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root, 7906 char *kernel, char *mod_kernel, char *module, int root_opt) 7907 { 7908 int i; 7909 int change_kernel = 0; 7910 entry_t *ent; 7911 line_t *lp; 7912 line_t *tlp; 7913 char linebuf[BAM_MAXLINE]; 7914 const char *fcn = "update_boot_entry()"; 7915 7916 /* note: don't match on title, it's updated on upgrade */ 7917 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module, 7918 root_opt, &i); 7919 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) { 7920 /* 7921 * We may be upgrading a kernel from multiboot to 7922 * directboot. Look for a multiboot entry. A multiboot 7923 * entry will not have a findroot line. 7924 */ 7925 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root, 7926 MULTIBOOT_ARCHIVE, root_opt, &i); 7927 if (ent != NULL) { 7928 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root)); 7929 change_kernel = 1; 7930 } 7931 } else if (ent) { 7932 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot)); 7933 } 7934 7935 if (ent == NULL) { 7936 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot)); 7937 return (add_boot_entry(mp, title, findroot, 7938 kernel, mod_kernel, module, NULL)); 7939 } 7940 7941 /* replace title of existing entry and update findroot line */ 7942 lp = ent->start; 7943 lp = lp->next; /* title line */ 7944 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 7945 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 7946 free(lp->arg); 7947 free(lp->line); 7948 lp->arg = s_strdup(title); 7949 lp->line = s_strdup(linebuf); 7950 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title)); 7951 7952 tlp = lp; /* title line */ 7953 lp = lp->next; /* root line */ 7954 7955 /* if no root or findroot command, create a new line_t */ 7956 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 && 7957 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) { 7958 lp = s_calloc(1, sizeof (line_t)); 7959 bam_add_line(mp, ent, tlp, lp); 7960 } else { 7961 if (lp->cmd != NULL) 7962 free(lp->cmd); 7963 7964 free(lp->sep); 7965 free(lp->arg); 7966 free(lp->line); 7967 } 7968 7969 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]); 7970 lp->sep = s_strdup(menu_cmds[SEP_CMD]); 7971 lp->arg = s_strdup(findroot); 7972 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 7973 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot); 7974 lp->line = s_strdup(linebuf); 7975 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot)); 7976 7977 /* kernel line */ 7978 lp = lp->next; 7979 7980 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) { 7981 char *params = NULL; 7982 7983 params = strstr(lp->line, "-s"); 7984 if (params != NULL) 7985 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s", 7986 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 7987 kernel, params+2); 7988 else 7989 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 7990 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 7991 kernel); 7992 7993 if (lp->cmd != NULL) 7994 free(lp->cmd); 7995 7996 free(lp->arg); 7997 free(lp->line); 7998 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); 7999 lp->arg = s_strdup(strstr(linebuf, "/")); 8000 lp->line = s_strdup(linebuf); 8001 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL; 8002 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd)); 8003 } 8004 8005 if (change_kernel) { 8006 /* 8007 * We're upgrading from multiboot to directboot. 8008 */ 8009 if (lp->cmd != NULL && 8010 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) { 8011 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 8012 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 8013 kernel); 8014 free(lp->cmd); 8015 free(lp->arg); 8016 free(lp->line); 8017 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); 8018 lp->arg = s_strdup(kernel); 8019 lp->line = s_strdup(linebuf); 8020 lp = lp->next; 8021 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel)); 8022 } 8023 if (lp->cmd != NULL && 8024 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 8025 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 8026 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 8027 module); 8028 free(lp->cmd); 8029 free(lp->arg); 8030 free(lp->line); 8031 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 8032 lp->arg = s_strdup(module); 8033 lp->line = s_strdup(linebuf); 8034 lp = lp->next; 8035 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module)); 8036 } 8037 } 8038 8039 /* module line */ 8040 lp = lp->next; 8041 8042 if (ent->flags & BAM_ENTRY_UPGFSMODULE) { 8043 if (lp->cmd != NULL && 8044 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 8045 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 8046 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 8047 module); 8048 free(lp->cmd); 8049 free(lp->arg); 8050 free(lp->line); 8051 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 8052 lp->arg = s_strdup(module); 8053 lp->line = s_strdup(linebuf); 8054 lp = lp->next; 8055 ent->flags &= ~BAM_ENTRY_UPGFSMODULE; 8056 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module)); 8057 } 8058 } 8059 8060 BAM_DPRINTF((D_RETURN_RET, fcn, i)); 8061 return (i); 8062 } 8063 8064 int 8065 root_optional(char *osroot, char *menu_root) 8066 { 8067 char *ospecial; 8068 char *mspecial; 8069 char *slash; 8070 int root_opt; 8071 int ret1; 8072 int ret2; 8073 const char *fcn = "root_optional()"; 8074 8075 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 8076 8077 /* 8078 * For all filesystems except ZFS, a straight compare of osroot 8079 * and menu_root will tell us if root is optional. 8080 * For ZFS, the situation is complicated by the fact that 8081 * menu_root and osroot are always different 8082 */ 8083 ret1 = is_zfs(osroot); 8084 ret2 = is_zfs(menu_root); 8085 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0); 8086 if (!ret1 || !ret2) { 8087 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root)); 8088 root_opt = (strcmp(osroot, menu_root) == 0); 8089 goto out; 8090 } 8091 8092 ospecial = get_special(osroot); 8093 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL); 8094 if (ospecial == NULL) { 8095 bam_error(GET_OSROOT_SPECIAL_ERR, osroot); 8096 return (0); 8097 } 8098 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot)); 8099 8100 mspecial = get_special(menu_root); 8101 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL); 8102 if (mspecial == NULL) { 8103 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root); 8104 free(ospecial); 8105 return (0); 8106 } 8107 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root)); 8108 8109 slash = strchr(ospecial, '/'); 8110 if (slash) 8111 *slash = '\0'; 8112 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot)); 8113 8114 root_opt = (strcmp(ospecial, mspecial) == 0); 8115 8116 free(ospecial); 8117 free(mspecial); 8118 8119 out: 8120 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0); 8121 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1); 8122 if (root_opt) { 8123 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8124 } else { 8125 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 8126 } 8127 8128 return (root_opt); 8129 } 8130 8131 /*ARGSUSED*/ 8132 static error_t 8133 update_entry(menu_t *mp, char *menu_root, char *osdev) 8134 { 8135 int entry; 8136 char *grubsign; 8137 char *grubroot; 8138 char *title; 8139 char osroot[PATH_MAX]; 8140 char *failsafe_kernel = NULL; 8141 struct stat sbuf; 8142 char failsafe[256]; 8143 char failsafe_64[256]; 8144 int ret; 8145 const char *fcn = "update_entry()"; 8146 8147 assert(mp); 8148 assert(menu_root); 8149 assert(osdev); 8150 assert(bam_root); 8151 8152 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root)); 8153 8154 (void) strlcpy(osroot, bam_root, sizeof (osroot)); 8155 8156 title = get_title(osroot); 8157 assert(title); 8158 8159 grubsign = get_grubsign(osroot, osdev); 8160 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL); 8161 if (grubsign == NULL) { 8162 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev); 8163 return (BAM_ERROR); 8164 } 8165 8166 /* 8167 * It is not a fatal error if get_grubroot() fails 8168 * We no longer rely on biosdev to populate the 8169 * menu 8170 */ 8171 grubroot = get_grubroot(osroot, osdev, menu_root); 8172 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL); 8173 if (grubroot) { 8174 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS, 8175 fcn, osroot, osdev, menu_root)); 8176 } else { 8177 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE, 8178 fcn, osroot, osdev, menu_root)); 8179 } 8180 8181 /* add the entry for normal Solaris */ 8182 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT", 8183 bam_direct = BAM_DIRECT_MULTIBOOT); 8184 if (bam_direct == BAM_DIRECT_DBOOT) { 8185 entry = update_boot_entry(mp, title, grubsign, grubroot, 8186 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL), 8187 NULL, DIRECT_BOOT_ARCHIVE, 8188 root_optional(osroot, menu_root)); 8189 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign)); 8190 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) { 8191 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign, 8192 grubroot, XEN_MENU, bam_zfs ? 8193 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE, 8194 DIRECT_BOOT_ARCHIVE, 8195 root_optional(osroot, menu_root)); 8196 BAM_DPRINTF((D_UPDATED_HV_ENTRY, 8197 fcn, bam_zfs, grubsign)); 8198 } 8199 } else { 8200 entry = update_boot_entry(mp, title, grubsign, grubroot, 8201 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE, 8202 root_optional(osroot, menu_root)); 8203 8204 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign)); 8205 } 8206 8207 /* 8208 * Add the entry for failsafe archive. On a bfu'd system, the 8209 * failsafe may be different than the installed kernel. 8210 */ 8211 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 8212 osroot, FAILSAFE_ARCHIVE_32); 8213 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s", 8214 osroot, FAILSAFE_ARCHIVE_64); 8215 8216 /* 8217 * Check if at least one of the two archives exists 8218 * Using $ISADIR as the default line, we have an entry which works 8219 * for both the cases. 8220 */ 8221 8222 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) { 8223 8224 /* Figure out where the kernel line should point */ 8225 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, 8226 DIRECT_BOOT_FAILSAFE_32); 8227 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s", 8228 osroot, DIRECT_BOOT_FAILSAFE_64); 8229 if (stat(failsafe, &sbuf) == 0 || 8230 stat(failsafe_64, &sbuf) == 0) { 8231 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE; 8232 } else { 8233 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 8234 osroot, MULTI_BOOT_FAILSAFE); 8235 if (stat(failsafe, &sbuf) == 0) { 8236 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE; 8237 } 8238 } 8239 if (failsafe_kernel != NULL) { 8240 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign, 8241 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE, 8242 root_optional(osroot, menu_root)); 8243 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn, 8244 failsafe_kernel)); 8245 } 8246 } 8247 free(grubroot); 8248 8249 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR); 8250 if (entry == BAM_ERROR) { 8251 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign); 8252 free(grubsign); 8253 return (BAM_ERROR); 8254 } 8255 free(grubsign); 8256 8257 update_numbering(mp); 8258 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8259 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR); 8260 if (ret == BAM_ERROR) { 8261 bam_error(SET_DEFAULT_FAILED, entry); 8262 } 8263 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8264 return (BAM_WRITE); 8265 } 8266 8267 static void 8268 save_default_entry(menu_t *mp, const char *which) 8269 { 8270 int lineNum; 8271 int entryNum; 8272 int entry = 0; /* default is 0 */ 8273 char linebuf[BAM_MAXLINE]; 8274 line_t *lp = mp->curdefault; 8275 const char *fcn = "save_default_entry()"; 8276 8277 if (mp->start) { 8278 lineNum = mp->end->lineNum; 8279 entryNum = mp->end->entryNum; 8280 } else { 8281 lineNum = LINE_INIT; 8282 entryNum = ENTRY_INIT; 8283 } 8284 8285 if (lp) 8286 entry = s_strtol(lp->arg); 8287 8288 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry); 8289 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf)); 8290 line_parser(mp, linebuf, &lineNum, &entryNum); 8291 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum)); 8292 } 8293 8294 static void 8295 restore_default_entry(menu_t *mp, const char *which, line_t *lp) 8296 { 8297 int entry; 8298 char *str; 8299 const char *fcn = "restore_default_entry()"; 8300 8301 if (lp == NULL) { 8302 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn)); 8303 return; /* nothing to restore */ 8304 } 8305 8306 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which)); 8307 8308 str = lp->arg + strlen(which); 8309 entry = s_strtol(str); 8310 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8311 8312 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry)); 8313 8314 /* delete saved old default line */ 8315 unlink_line(mp, lp); 8316 line_free(lp); 8317 } 8318 8319 /* 8320 * This function is for supporting reboot with args. 8321 * The opt value can be: 8322 * NULL delete temp entry, if present 8323 * entry=<n> switches default entry to <n> 8324 * else treated as boot-args and setup a temperary menu entry 8325 * and make it the default 8326 * Note that we are always rebooting the current OS instance 8327 * so osroot == / always. 8328 */ 8329 #define REBOOT_TITLE "Solaris_reboot_transient" 8330 8331 /*ARGSUSED*/ 8332 static error_t 8333 update_temp(menu_t *mp, char *dummy, char *opt) 8334 { 8335 int entry; 8336 char *osdev; 8337 char *fstype; 8338 char *sign; 8339 char *opt_ptr; 8340 char *path; 8341 char kernbuf[BUFSIZ]; 8342 char args_buf[BUFSIZ]; 8343 char signbuf[PATH_MAX]; 8344 int ret; 8345 const char *fcn = "update_temp()"; 8346 8347 assert(mp); 8348 assert(dummy == NULL); 8349 8350 /* opt can be NULL */ 8351 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>")); 8352 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root)); 8353 8354 if (bam_alt_root || bam_rootlen != 1 || 8355 strcmp(bam_root, "/") != 0 || 8356 strcmp(rootbuf, "/") != 0) { 8357 bam_error(ALT_ROOT_INVALID, bam_root); 8358 return (BAM_ERROR); 8359 } 8360 8361 /* If no option, delete exiting reboot menu entry */ 8362 if (opt == NULL) { 8363 entry_t *ent; 8364 BAM_DPRINTF((D_OPT_NULL, fcn)); 8365 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL, 8366 NULL, NULL, 0, &entry); 8367 if (ent == NULL) { /* not found is ok */ 8368 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn)); 8369 return (BAM_SUCCESS); 8370 } 8371 (void) delete_boot_entry(mp, entry, DBE_PRINTERR); 8372 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault); 8373 mp->olddefault = NULL; 8374 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn)); 8375 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8376 return (BAM_WRITE); 8377 } 8378 8379 /* if entry= is specified, set the default entry */ 8380 if (strncmp(opt, "entry=", strlen("entry=")) == 0) { 8381 int entryNum = s_strtol(opt + strlen("entry=")); 8382 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt)); 8383 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) { 8384 /* this is entry=# option */ 8385 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8386 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret)); 8387 return (ret); 8388 } else { 8389 bam_error(SET_DEFAULT_FAILED, entryNum); 8390 return (BAM_ERROR); 8391 } 8392 } 8393 8394 /* 8395 * add a new menu entry based on opt and make it the default 8396 */ 8397 8398 fstype = get_fstype("/"); 8399 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL); 8400 if (fstype == NULL) { 8401 bam_error(REBOOT_FSTYPE_FAILED); 8402 return (BAM_ERROR); 8403 } 8404 8405 osdev = get_special("/"); 8406 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL); 8407 if (osdev == NULL) { 8408 free(fstype); 8409 bam_error(REBOOT_SPECIAL_FAILED); 8410 return (BAM_ERROR); 8411 } 8412 8413 sign = find_existing_sign("/", osdev, fstype); 8414 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL); 8415 if (sign == NULL) { 8416 free(fstype); 8417 free(osdev); 8418 bam_error(REBOOT_SIGN_FAILED); 8419 return (BAM_ERROR); 8420 } 8421 8422 free(osdev); 8423 (void) strlcpy(signbuf, sign, sizeof (signbuf)); 8424 free(sign); 8425 8426 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL && 8427 strchr(signbuf, ')') == NULL); 8428 8429 /* 8430 * There is no alternate root while doing reboot with args 8431 * This version of bootadm is only delivered with a DBOOT 8432 * version of Solaris. 8433 */ 8434 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT); 8435 if (bam_direct != BAM_DIRECT_DBOOT) { 8436 free(fstype); 8437 bam_error(REBOOT_DIRECT_FAILED); 8438 return (BAM_ERROR); 8439 } 8440 8441 /* add an entry for Solaris reboot */ 8442 if (opt[0] == '-') { 8443 /* It's an option - first see if boot-file is set */ 8444 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf)); 8445 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR); 8446 if (ret != BAM_SUCCESS) { 8447 free(fstype); 8448 bam_error(REBOOT_GET_KERNEL_FAILED); 8449 return (BAM_ERROR); 8450 } 8451 if (kernbuf[0] == '\0') 8452 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL, 8453 sizeof (kernbuf)); 8454 /* 8455 * If this is a zfs file system and kernbuf does not 8456 * have "-B $ZFS-BOOTFS" string yet, add it. 8457 */ 8458 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) { 8459 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 8460 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf)); 8461 } 8462 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 8463 (void) strlcat(kernbuf, opt, sizeof (kernbuf)); 8464 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf)); 8465 } else if (opt[0] == '/') { 8466 /* It's a full path, so write it out. */ 8467 (void) strlcpy(kernbuf, opt, sizeof (kernbuf)); 8468 8469 /* 8470 * If someone runs: 8471 * 8472 * # eeprom boot-args='-kd' 8473 * # reboot /platform/i86pc/kernel/unix 8474 * 8475 * we want to use the boot-args as part of the boot 8476 * line. On the other hand, if someone runs: 8477 * 8478 * # reboot "/platform/i86pc/kernel/unix -kd" 8479 * 8480 * we don't need to mess with boot-args. If there's 8481 * no space in the options string, assume we're in the 8482 * first case. 8483 */ 8484 if (strchr(opt, ' ') == NULL) { 8485 ret = get_kernel(mp, ARGS_CMD, args_buf, 8486 sizeof (args_buf)); 8487 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR); 8488 if (ret != BAM_SUCCESS) { 8489 free(fstype); 8490 bam_error(REBOOT_GET_ARGS_FAILED); 8491 return (BAM_ERROR); 8492 } 8493 8494 if (args_buf[0] != '\0') { 8495 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 8496 (void) strlcat(kernbuf, args_buf, 8497 sizeof (kernbuf)); 8498 } 8499 } 8500 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf)); 8501 } else { 8502 /* 8503 * It may be a partial path, or it may be a partial 8504 * path followed by options. Assume that only options 8505 * follow a space. If someone sends us a kernel path 8506 * that includes a space, they deserve to be broken. 8507 */ 8508 opt_ptr = strchr(opt, ' '); 8509 if (opt_ptr != NULL) { 8510 *opt_ptr = '\0'; 8511 } 8512 8513 path = expand_path(opt); 8514 if (path != NULL) { 8515 (void) strlcpy(kernbuf, path, sizeof (kernbuf)); 8516 free(path); 8517 8518 /* 8519 * If there were options given, use those. 8520 * Otherwise, copy over the default options. 8521 */ 8522 if (opt_ptr != NULL) { 8523 /* Restore the space in opt string */ 8524 *opt_ptr = ' '; 8525 (void) strlcat(kernbuf, opt_ptr, 8526 sizeof (kernbuf)); 8527 } else { 8528 ret = get_kernel(mp, ARGS_CMD, args_buf, 8529 sizeof (args_buf)); 8530 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS", 8531 ret = BAM_ERROR); 8532 if (ret != BAM_SUCCESS) { 8533 free(fstype); 8534 bam_error(REBOOT_GET_ARGS_FAILED); 8535 return (BAM_ERROR); 8536 } 8537 8538 if (args_buf[0] != '\0') { 8539 (void) strlcat(kernbuf, " ", 8540 sizeof (kernbuf)); 8541 (void) strlcat(kernbuf, 8542 args_buf, sizeof (kernbuf)); 8543 } 8544 } 8545 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf)); 8546 } else { 8547 free(fstype); 8548 bam_error(UNKNOWN_KERNEL, opt); 8549 bam_print_stderr(UNKNOWN_KERNEL_REBOOT); 8550 return (BAM_ERROR); 8551 } 8552 } 8553 free(fstype); 8554 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf, 8555 NULL, NULL, NULL); 8556 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR); 8557 if (entry == BAM_ERROR) { 8558 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED); 8559 return (BAM_ERROR); 8560 } 8561 8562 save_default_entry(mp, BAM_OLDDEF); 8563 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8564 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR); 8565 if (ret == BAM_ERROR) { 8566 bam_error(REBOOT_SET_DEFAULT_FAILED, entry); 8567 } 8568 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8569 return (BAM_WRITE); 8570 } 8571 8572 error_t 8573 set_global(menu_t *mp, char *globalcmd, int val) 8574 { 8575 line_t *lp; 8576 line_t *found; 8577 line_t *last; 8578 char *cp; 8579 char *str; 8580 char prefix[BAM_MAXLINE]; 8581 size_t len; 8582 const char *fcn = "set_global()"; 8583 8584 assert(mp); 8585 assert(globalcmd); 8586 8587 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) { 8588 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1); 8589 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL); 8590 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100); 8591 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) { 8592 (void) snprintf(prefix, sizeof (prefix), "%d", val); 8593 bam_error(INVALID_ENTRY, prefix); 8594 return (BAM_ERROR); 8595 } 8596 } 8597 8598 found = last = NULL; 8599 for (lp = mp->start; lp; lp = lp->next) { 8600 if (lp->flags != BAM_GLOBAL) 8601 continue; 8602 8603 last = lp; /* track the last global found */ 8604 8605 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL); 8606 if (lp->cmd == NULL) { 8607 bam_error(NO_CMD, lp->lineNum); 8608 continue; 8609 } 8610 if (strcmp(globalcmd, lp->cmd) != 0) 8611 continue; 8612 8613 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd)); 8614 8615 if (found) { 8616 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 8617 } 8618 found = lp; 8619 } 8620 8621 if (found == NULL) { 8622 lp = s_calloc(1, sizeof (line_t)); 8623 if (last == NULL) { 8624 lp->next = mp->start; 8625 mp->start = lp; 8626 mp->end = (mp->end) ? mp->end : lp; 8627 } else { 8628 lp->next = last->next; 8629 last->next = lp; 8630 if (lp->next == NULL) 8631 mp->end = lp; 8632 } 8633 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */ 8634 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]); 8635 len += 10; /* val < 10 digits */ 8636 lp->line = s_calloc(1, len); 8637 (void) snprintf(lp->line, len, "%s%s%d", 8638 globalcmd, menu_cmds[SEP_CMD], val); 8639 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line)); 8640 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8641 return (BAM_WRITE); 8642 } 8643 8644 /* 8645 * We are changing an existing entry. Retain any prefix whitespace, 8646 * but overwrite everything else. This preserves tabs added for 8647 * readability. 8648 */ 8649 str = found->line; 8650 cp = prefix; 8651 while (*str == ' ' || *str == '\t') 8652 *(cp++) = *(str++); 8653 *cp = '\0'; /* Terminate prefix */ 8654 len = strlen(prefix) + strlen(globalcmd); 8655 len += strlen(menu_cmds[SEP_CMD]) + 10; 8656 8657 free(found->line); 8658 found->line = s_calloc(1, len); 8659 (void) snprintf(found->line, len, 8660 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val); 8661 8662 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line)); 8663 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8664 return (BAM_WRITE); /* need a write to menu */ 8665 } 8666 8667 /* 8668 * partial_path may be anything like "kernel/unix" or "kmdb". Try to 8669 * expand it to a full unix path. The calling function is expected to 8670 * output a message if an error occurs and NULL is returned. 8671 */ 8672 static char * 8673 expand_path(const char *partial_path) 8674 { 8675 int new_path_len; 8676 char *new_path; 8677 char new_path2[PATH_MAX]; 8678 struct stat sb; 8679 const char *fcn = "expand_path()"; 8680 8681 new_path_len = strlen(partial_path) + 64; 8682 new_path = s_calloc(1, new_path_len); 8683 8684 /* First, try the simplest case - something like "kernel/unix" */ 8685 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s", 8686 partial_path); 8687 if (stat(new_path, &sb) == 0) { 8688 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 8689 return (new_path); 8690 } 8691 8692 if (strcmp(partial_path, "kmdb") == 0) { 8693 (void) snprintf(new_path, new_path_len, "%s -k", 8694 DIRECT_BOOT_KERNEL); 8695 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 8696 return (new_path); 8697 } 8698 8699 /* 8700 * We've quickly reached unsupported usage. Try once more to 8701 * see if we were just given a glom name. 8702 */ 8703 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix", 8704 partial_path); 8705 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix", 8706 partial_path); 8707 if (stat(new_path, &sb) == 0) { 8708 if (stat(new_path2, &sb) == 0) { 8709 /* 8710 * We matched both, so we actually 8711 * want to write the $ISADIR version. 8712 */ 8713 (void) snprintf(new_path, new_path_len, 8714 "/platform/i86pc/kernel/%s/$ISADIR/unix", 8715 partial_path); 8716 } 8717 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 8718 return (new_path); 8719 } 8720 8721 free(new_path); 8722 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 8723 return (NULL); 8724 } 8725 8726 /* 8727 * The kernel cmd and arg have been changed, so 8728 * check whether the archive line needs to change. 8729 */ 8730 static void 8731 set_archive_line(entry_t *entryp, line_t *kernelp) 8732 { 8733 line_t *lp = entryp->start; 8734 char *new_archive; 8735 menu_cmd_t m_cmd; 8736 const char *fcn = "set_archive_line()"; 8737 8738 for (; lp != NULL; lp = lp->next) { 8739 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD], 8740 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) { 8741 break; 8742 } 8743 8744 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end); 8745 if (lp == entryp->end) { 8746 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, 8747 entryp->entryNum)); 8748 return; 8749 } 8750 } 8751 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL); 8752 if (lp == NULL) { 8753 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum)); 8754 return; 8755 } 8756 8757 if (strstr(kernelp->arg, "$ISADIR") != NULL) { 8758 new_archive = DIRECT_BOOT_ARCHIVE; 8759 m_cmd = MODULE_DOLLAR_CMD; 8760 } else if (strstr(kernelp->arg, "amd64") != NULL) { 8761 new_archive = DIRECT_BOOT_ARCHIVE_64; 8762 m_cmd = MODULE_CMD; 8763 } else { 8764 new_archive = DIRECT_BOOT_ARCHIVE_32; 8765 m_cmd = MODULE_CMD; 8766 } 8767 8768 if (strcmp(lp->arg, new_archive) == 0) { 8769 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg)); 8770 return; 8771 } 8772 8773 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) { 8774 free(lp->cmd); 8775 lp->cmd = s_strdup(menu_cmds[m_cmd]); 8776 } 8777 8778 free(lp->arg); 8779 lp->arg = s_strdup(new_archive); 8780 update_line(lp); 8781 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line)); 8782 } 8783 8784 /* 8785 * Title for an entry to set properties that once went in bootenv.rc. 8786 */ 8787 #define BOOTENV_RC_TITLE "Solaris bootenv rc" 8788 8789 /* 8790 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments 8791 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length 8792 * string, reset the value to the default. If path is a non-zero-length 8793 * string, set the kernel or arguments. 8794 */ 8795 static error_t 8796 get_set_kernel( 8797 menu_t *mp, 8798 menu_cmd_t optnum, 8799 char *path, 8800 char *buf, 8801 size_t bufsize) 8802 { 8803 int entryNum; 8804 int rv = BAM_SUCCESS; 8805 int free_new_path = 0; 8806 entry_t *entryp; 8807 line_t *ptr; 8808 line_t *kernelp; 8809 char *new_arg; 8810 char *old_args; 8811 char *space; 8812 char *new_path; 8813 char old_space; 8814 size_t old_kernel_len; 8815 size_t new_str_len; 8816 char *fstype; 8817 char *osdev; 8818 char *sign; 8819 char signbuf[PATH_MAX]; 8820 int ret; 8821 const char *fcn = "get_set_kernel()"; 8822 8823 assert(bufsize > 0); 8824 8825 ptr = kernelp = NULL; 8826 new_arg = old_args = space = NULL; 8827 new_path = NULL; 8828 buf[0] = '\0'; 8829 8830 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT", 8831 bam_direct = BAM_DIRECT_MULTIBOOT); 8832 if (bam_direct != BAM_DIRECT_DBOOT) { 8833 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args"); 8834 return (BAM_ERROR); 8835 } 8836 8837 /* 8838 * If a user changed the default entry to a non-bootadm controlled 8839 * one, we don't want to mess with it. Just print an error and 8840 * return. 8841 */ 8842 if (mp->curdefault) { 8843 entryNum = s_strtol(mp->curdefault->arg); 8844 for (entryp = mp->entries; entryp; entryp = entryp->next) { 8845 if (entryp->entryNum == entryNum) 8846 break; 8847 } 8848 if ((entryp != NULL) && 8849 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) { 8850 bam_error(DEFAULT_NOT_BAM); 8851 return (BAM_ERROR); 8852 } 8853 } 8854 8855 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL, 8856 0, &entryNum); 8857 8858 if (entryp != NULL) { 8859 for (ptr = entryp->start; ptr && ptr != entryp->end; 8860 ptr = ptr->next) { 8861 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD], 8862 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) { 8863 kernelp = ptr; 8864 break; 8865 } 8866 } 8867 if (kernelp == NULL) { 8868 bam_error(NO_KERNEL, entryNum); 8869 return (BAM_ERROR); 8870 } 8871 8872 old_kernel_len = strcspn(kernelp->arg, " \t"); 8873 space = old_args = kernelp->arg + old_kernel_len; 8874 while ((*old_args == ' ') || (*old_args == '\t')) 8875 old_args++; 8876 } 8877 8878 if (path == NULL) { 8879 if (entryp == NULL) { 8880 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn)); 8881 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8882 return (BAM_SUCCESS); 8883 } 8884 assert(kernelp); 8885 if (optnum == ARGS_CMD) { 8886 if (old_args[0] != '\0') { 8887 (void) strlcpy(buf, old_args, bufsize); 8888 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf)); 8889 } 8890 } else { 8891 /* 8892 * We need to print the kernel, so we just turn the 8893 * first space into a '\0' and print the beginning. 8894 * We don't print anything if it's the default kernel. 8895 */ 8896 old_space = *space; 8897 *space = '\0'; 8898 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) { 8899 (void) strlcpy(buf, kernelp->arg, bufsize); 8900 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf)); 8901 } 8902 *space = old_space; 8903 } 8904 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8905 return (BAM_SUCCESS); 8906 } 8907 8908 /* 8909 * First, check if we're resetting an entry to the default. 8910 */ 8911 if ((path[0] == '\0') || 8912 ((optnum == KERNEL_CMD) && 8913 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) { 8914 if ((entryp == NULL) || (kernelp == NULL)) { 8915 /* No previous entry, it's already the default */ 8916 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn)); 8917 return (BAM_SUCCESS); 8918 } 8919 8920 /* 8921 * Check if we can delete the entry. If we're resetting the 8922 * kernel command, and the args is already empty, or if we're 8923 * resetting the args command, and the kernel is already the 8924 * default, we can restore the old default and delete the entry. 8925 */ 8926 if (((optnum == KERNEL_CMD) && 8927 ((old_args == NULL) || (old_args[0] == '\0'))) || 8928 ((optnum == ARGS_CMD) && 8929 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL, 8930 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) { 8931 kernelp = NULL; 8932 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR); 8933 restore_default_entry(mp, BAM_OLD_RC_DEF, 8934 mp->old_rc_default); 8935 mp->old_rc_default = NULL; 8936 rv = BAM_WRITE; 8937 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn)); 8938 goto done; 8939 } 8940 8941 if (optnum == KERNEL_CMD) { 8942 /* 8943 * At this point, we've already checked that old_args 8944 * and entryp are valid pointers. The "+ 2" is for 8945 * a space a the string termination character. 8946 */ 8947 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) + 8948 strlen(old_args) + 2; 8949 new_arg = s_calloc(1, new_str_len); 8950 (void) snprintf(new_arg, new_str_len, "%s %s", 8951 DIRECT_BOOT_KERNEL, old_args); 8952 free(kernelp->arg); 8953 kernelp->arg = new_arg; 8954 8955 /* 8956 * We have changed the kernel line, so we may need 8957 * to update the archive line as well. 8958 */ 8959 set_archive_line(entryp, kernelp); 8960 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG, 8961 fcn, kernelp->arg)); 8962 } else { 8963 /* 8964 * We're resetting the boot args to nothing, so 8965 * we only need to copy the kernel. We've already 8966 * checked that the kernel is not the default. 8967 */ 8968 new_arg = s_calloc(1, old_kernel_len + 1); 8969 (void) snprintf(new_arg, old_kernel_len + 1, "%s", 8970 kernelp->arg); 8971 free(kernelp->arg); 8972 kernelp->arg = new_arg; 8973 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL, 8974 fcn, kernelp->arg)); 8975 } 8976 rv = BAM_WRITE; 8977 goto done; 8978 } 8979 8980 /* 8981 * Expand the kernel file to a full path, if necessary 8982 */ 8983 if ((optnum == KERNEL_CMD) && (path[0] != '/')) { 8984 new_path = expand_path(path); 8985 if (new_path == NULL) { 8986 bam_error(UNKNOWN_KERNEL, path); 8987 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 8988 return (BAM_ERROR); 8989 } 8990 free_new_path = 1; 8991 } else { 8992 new_path = path; 8993 free_new_path = 0; 8994 } 8995 8996 /* 8997 * At this point, we know we're setting a new value. First, take care 8998 * of the case where there was no previous entry. 8999 */ 9000 if (entryp == NULL) { 9001 9002 /* Similar to code in update_temp */ 9003 fstype = get_fstype("/"); 9004 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL); 9005 if (fstype == NULL) { 9006 bam_error(BOOTENV_FSTYPE_FAILED); 9007 rv = BAM_ERROR; 9008 goto done; 9009 } 9010 9011 osdev = get_special("/"); 9012 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL); 9013 if (osdev == NULL) { 9014 free(fstype); 9015 bam_error(BOOTENV_SPECIAL_FAILED); 9016 rv = BAM_ERROR; 9017 goto done; 9018 } 9019 9020 sign = find_existing_sign("/", osdev, fstype); 9021 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL); 9022 if (sign == NULL) { 9023 free(fstype); 9024 free(osdev); 9025 bam_error(BOOTENV_SIGN_FAILED); 9026 rv = BAM_ERROR; 9027 goto done; 9028 } 9029 9030 free(osdev); 9031 (void) strlcpy(signbuf, sign, sizeof (signbuf)); 9032 free(sign); 9033 assert(strchr(signbuf, '(') == NULL && 9034 strchr(signbuf, ',') == NULL && 9035 strchr(signbuf, ')') == NULL); 9036 9037 if (optnum == KERNEL_CMD) { 9038 if (strcmp(fstype, "zfs") == 0) { 9039 new_str_len = strlen(new_path) + 9040 strlen(ZFS_BOOT) + 8; 9041 new_arg = s_calloc(1, new_str_len); 9042 (void) snprintf(new_arg, new_str_len, "%s %s", 9043 new_path, ZFS_BOOT); 9044 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, 9045 new_arg)); 9046 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 9047 signbuf, new_arg, NULL, NULL, NULL); 9048 free(new_arg); 9049 } else { 9050 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, 9051 new_path)); 9052 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 9053 signbuf, new_path, NULL, NULL, NULL); 9054 } 9055 } else { 9056 new_str_len = strlen(path) + 8; 9057 if (strcmp(fstype, "zfs") == 0) { 9058 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS); 9059 new_arg = s_calloc(1, new_str_len); 9060 (void) snprintf(new_arg, new_str_len, "%s %s", 9061 DIRECT_BOOT_KERNEL_ZFS, path); 9062 } else { 9063 new_str_len += strlen(DIRECT_BOOT_KERNEL); 9064 new_arg = s_calloc(1, new_str_len); 9065 (void) snprintf(new_arg, new_str_len, "%s %s", 9066 DIRECT_BOOT_KERNEL, path); 9067 } 9068 9069 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg)); 9070 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 9071 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL); 9072 free(new_arg); 9073 } 9074 free(fstype); 9075 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY", 9076 entryNum = BAM_ERROR); 9077 if (entryNum == BAM_ERROR) { 9078 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY, 9079 BOOTENV_RC_TITLE); 9080 rv = BAM_ERROR; 9081 goto done; 9082 } 9083 save_default_entry(mp, BAM_OLD_RC_DEF); 9084 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum); 9085 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR); 9086 if (ret == BAM_ERROR) { 9087 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum); 9088 } 9089 rv = BAM_WRITE; 9090 goto done; 9091 } 9092 9093 /* 9094 * There was already an bootenv entry which we need to edit. 9095 */ 9096 if (optnum == KERNEL_CMD) { 9097 new_str_len = strlen(new_path) + strlen(old_args) + 2; 9098 new_arg = s_calloc(1, new_str_len); 9099 (void) snprintf(new_arg, new_str_len, "%s %s", new_path, 9100 old_args); 9101 free(kernelp->arg); 9102 kernelp->arg = new_arg; 9103 9104 /* 9105 * If we have changed the kernel line, we may need to update 9106 * the archive line as well. 9107 */ 9108 set_archive_line(entryp, kernelp); 9109 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn, 9110 kernelp->arg)); 9111 } else { 9112 new_str_len = old_kernel_len + strlen(path) + 8; 9113 new_arg = s_calloc(1, new_str_len); 9114 (void) strncpy(new_arg, kernelp->arg, old_kernel_len); 9115 (void) strlcat(new_arg, " ", new_str_len); 9116 (void) strlcat(new_arg, path, new_str_len); 9117 free(kernelp->arg); 9118 kernelp->arg = new_arg; 9119 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn, 9120 kernelp->arg)); 9121 } 9122 rv = BAM_WRITE; 9123 9124 done: 9125 if ((rv == BAM_WRITE) && kernelp) 9126 update_line(kernelp); 9127 if (free_new_path) 9128 free(new_path); 9129 if (rv == BAM_WRITE) { 9130 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 9131 } else { 9132 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 9133 } 9134 return (rv); 9135 } 9136 9137 static error_t 9138 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize) 9139 { 9140 const char *fcn = "get_kernel()"; 9141 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum])); 9142 return (get_set_kernel(mp, optnum, NULL, buf, bufsize)); 9143 } 9144 9145 static error_t 9146 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize) 9147 { 9148 const char *fcn = "set_kernel()"; 9149 assert(path != NULL); 9150 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path)); 9151 return (get_set_kernel(mp, optnum, path, buf, bufsize)); 9152 } 9153 9154 /*ARGSUSED*/ 9155 static error_t 9156 set_option(menu_t *mp, char *dummy, char *opt) 9157 { 9158 int optnum; 9159 int optval; 9160 char *val; 9161 char buf[BUFSIZ] = ""; 9162 error_t rv; 9163 const char *fcn = "set_option()"; 9164 9165 assert(mp); 9166 assert(opt); 9167 assert(dummy == NULL); 9168 9169 /* opt is set from bam_argv[0] and is always non-NULL */ 9170 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt)); 9171 9172 val = strchr(opt, '='); 9173 if (val != NULL) { 9174 *val = '\0'; 9175 } 9176 9177 if (strcmp(opt, "default") == 0) { 9178 optnum = DEFAULT_CMD; 9179 } else if (strcmp(opt, "timeout") == 0) { 9180 optnum = TIMEOUT_CMD; 9181 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) { 9182 optnum = KERNEL_CMD; 9183 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) { 9184 optnum = ARGS_CMD; 9185 } else { 9186 bam_error(INVALID_OPTION, opt); 9187 return (BAM_ERROR); 9188 } 9189 9190 /* 9191 * kernel and args are allowed without "=new_value" strings. All 9192 * others cause errors 9193 */ 9194 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) { 9195 bam_error(NO_OPTION_ARG, opt); 9196 return (BAM_ERROR); 9197 } else if (val != NULL) { 9198 *val = '='; 9199 } 9200 9201 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) { 9202 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], 9203 val ? val + 1 : "NULL")); 9204 9205 if (val) 9206 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf)); 9207 else 9208 rv = get_kernel(mp, optnum, buf, sizeof (buf)); 9209 if ((rv == BAM_SUCCESS) && (buf[0] != '\0')) 9210 (void) printf("%s\n", buf); 9211 } else { 9212 optval = s_strtol(val + 1); 9213 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1)); 9214 rv = set_global(mp, menu_cmds[optnum], optval); 9215 } 9216 9217 if (rv == BAM_WRITE || rv == BAM_SUCCESS) { 9218 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 9219 } else { 9220 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 9221 } 9222 9223 return (rv); 9224 } 9225 9226 /* 9227 * The quiet argument suppresses messages. This is used 9228 * when invoked in the context of other commands (e.g. list_entry) 9229 */ 9230 static error_t 9231 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet) 9232 { 9233 line_t *lp; 9234 char *arg; 9235 int done, ret = BAM_SUCCESS; 9236 9237 assert(mp); 9238 assert(menu_path); 9239 assert(globalcmd); 9240 9241 if (mp->start == NULL) { 9242 if (!quiet) 9243 bam_error(NO_MENU, menu_path); 9244 return (BAM_ERROR); 9245 } 9246 9247 done = 0; 9248 for (lp = mp->start; lp; lp = lp->next) { 9249 if (lp->flags != BAM_GLOBAL) 9250 continue; 9251 9252 if (lp->cmd == NULL) { 9253 if (!quiet) 9254 bam_error(NO_CMD, lp->lineNum); 9255 continue; 9256 } 9257 9258 if (strcmp(globalcmd, lp->cmd) != 0) 9259 continue; 9260 9261 /* Found global. Check for duplicates */ 9262 if (done && !quiet) { 9263 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 9264 ret = BAM_ERROR; 9265 } 9266 9267 arg = lp->arg ? lp->arg : ""; 9268 bam_print(GLOBAL_CMD, globalcmd, arg); 9269 done = 1; 9270 } 9271 9272 if (!done && bam_verbose) 9273 bam_print(NO_ENTRY, globalcmd); 9274 9275 return (ret); 9276 } 9277 9278 static error_t 9279 menu_write(char *root, menu_t *mp) 9280 { 9281 const char *fcn = "menu_write()"; 9282 9283 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root)); 9284 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start)); 9285 } 9286 9287 void 9288 line_free(line_t *lp) 9289 { 9290 if (lp == NULL) 9291 return; 9292 9293 if (lp->cmd != NULL) 9294 free(lp->cmd); 9295 if (lp->sep) 9296 free(lp->sep); 9297 if (lp->arg) 9298 free(lp->arg); 9299 if (lp->line) 9300 free(lp->line); 9301 free(lp); 9302 } 9303 9304 static void 9305 linelist_free(line_t *start) 9306 { 9307 line_t *lp; 9308 9309 while (start) { 9310 lp = start; 9311 start = start->next; 9312 line_free(lp); 9313 } 9314 } 9315 9316 static void 9317 filelist_free(filelist_t *flistp) 9318 { 9319 linelist_free(flistp->head); 9320 flistp->head = NULL; 9321 flistp->tail = NULL; 9322 } 9323 9324 static void 9325 menu_free(menu_t *mp) 9326 { 9327 entry_t *ent, *tmp; 9328 assert(mp); 9329 9330 if (mp->start) 9331 linelist_free(mp->start); 9332 ent = mp->entries; 9333 while (ent) { 9334 tmp = ent; 9335 ent = tmp->next; 9336 free(tmp); 9337 } 9338 9339 free(mp); 9340 } 9341 9342 /* 9343 * Utility routines 9344 */ 9345 9346 9347 /* 9348 * Returns 0 on success 9349 * Any other value indicates an error 9350 */ 9351 static int 9352 exec_cmd(char *cmdline, filelist_t *flistp) 9353 { 9354 char buf[BUFSIZ]; 9355 int ret; 9356 FILE *ptr; 9357 sigset_t set; 9358 void (*disp)(int); 9359 9360 /* 9361 * For security 9362 * - only absolute paths are allowed 9363 * - set IFS to space and tab 9364 */ 9365 if (*cmdline != '/') { 9366 bam_error(ABS_PATH_REQ, cmdline); 9367 return (-1); 9368 } 9369 (void) putenv("IFS= \t"); 9370 9371 /* 9372 * We may have been exec'ed with SIGCHLD blocked 9373 * unblock it here 9374 */ 9375 (void) sigemptyset(&set); 9376 (void) sigaddset(&set, SIGCHLD); 9377 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 9378 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno)); 9379 return (-1); 9380 } 9381 9382 /* 9383 * Set SIGCHLD disposition to SIG_DFL for popen/pclose 9384 */ 9385 disp = sigset(SIGCHLD, SIG_DFL); 9386 if (disp == SIG_ERR) { 9387 bam_error(FAILED_SIG, strerror(errno)); 9388 return (-1); 9389 } 9390 if (disp == SIG_HOLD) { 9391 bam_error(BLOCKED_SIG, cmdline); 9392 return (-1); 9393 } 9394 9395 ptr = popen(cmdline, "r"); 9396 if (ptr == NULL) { 9397 bam_error(POPEN_FAIL, cmdline, strerror(errno)); 9398 return (-1); 9399 } 9400 9401 /* 9402 * If we simply do a pclose() following a popen(), pclose() 9403 * will close the reader end of the pipe immediately even 9404 * if the child process has not started/exited. pclose() 9405 * does wait for cmd to terminate before returning though. 9406 * When the executed command writes its output to the pipe 9407 * there is no reader process and the command dies with 9408 * SIGPIPE. To avoid this we read repeatedly until read 9409 * terminates with EOF. This indicates that the command 9410 * (writer) has closed the pipe and we can safely do a 9411 * pclose(). 9412 * 9413 * Since pclose() does wait for the command to exit, 9414 * we can safely reap the exit status of the command 9415 * from the value returned by pclose() 9416 */ 9417 while (s_fgets(buf, sizeof (buf), ptr) != NULL) { 9418 if (flistp == NULL) { 9419 /* s_fgets strips newlines, so insert them at the end */ 9420 bam_print(PRINT, buf); 9421 } else { 9422 append_to_flist(flistp, buf); 9423 } 9424 } 9425 9426 ret = pclose(ptr); 9427 if (ret == -1) { 9428 bam_error(PCLOSE_FAIL, cmdline, strerror(errno)); 9429 return (-1); 9430 } 9431 9432 if (WIFEXITED(ret)) { 9433 return (WEXITSTATUS(ret)); 9434 } else { 9435 bam_error(EXEC_FAIL, cmdline, ret); 9436 return (-1); 9437 } 9438 } 9439 9440 /* 9441 * Since this function returns -1 on error 9442 * it cannot be used to convert -1. However, 9443 * that is sufficient for what we need. 9444 */ 9445 static long 9446 s_strtol(char *str) 9447 { 9448 long l; 9449 char *res = NULL; 9450 9451 if (str == NULL) { 9452 return (-1); 9453 } 9454 9455 errno = 0; 9456 l = strtol(str, &res, 10); 9457 if (errno || *res != '\0') { 9458 return (-1); 9459 } 9460 9461 return (l); 9462 } 9463 9464 /* 9465 * Wrapper around fputs, that adds a newline (since fputs doesn't) 9466 */ 9467 static int 9468 s_fputs(char *str, FILE *fp) 9469 { 9470 char linebuf[BAM_MAXLINE]; 9471 9472 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str); 9473 return (fputs(linebuf, fp)); 9474 } 9475 9476 /* 9477 * Wrapper around fgets, that strips newlines returned by fgets 9478 */ 9479 char * 9480 s_fgets(char *buf, int buflen, FILE *fp) 9481 { 9482 int n; 9483 9484 buf = fgets(buf, buflen, fp); 9485 if (buf) { 9486 n = strlen(buf); 9487 if (n == buflen - 1 && buf[n-1] != '\n') 9488 bam_error(TOO_LONG, buflen - 1, buf); 9489 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1]; 9490 } 9491 9492 return (buf); 9493 } 9494 9495 void * 9496 s_calloc(size_t nelem, size_t sz) 9497 { 9498 void *ptr; 9499 9500 ptr = calloc(nelem, sz); 9501 if (ptr == NULL) { 9502 bam_error(NO_MEM, nelem*sz); 9503 bam_exit(1); 9504 } 9505 return (ptr); 9506 } 9507 9508 void * 9509 s_realloc(void *ptr, size_t sz) 9510 { 9511 ptr = realloc(ptr, sz); 9512 if (ptr == NULL) { 9513 bam_error(NO_MEM, sz); 9514 bam_exit(1); 9515 } 9516 return (ptr); 9517 } 9518 9519 char * 9520 s_strdup(char *str) 9521 { 9522 char *ptr; 9523 9524 if (str == NULL) 9525 return (NULL); 9526 9527 ptr = strdup(str); 9528 if (ptr == NULL) { 9529 bam_error(NO_MEM, strlen(str) + 1); 9530 bam_exit(1); 9531 } 9532 return (ptr); 9533 } 9534 9535 /* 9536 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients) 9537 * Returns 0 otherwise 9538 */ 9539 static int 9540 is_amd64(void) 9541 { 9542 static int amd64 = -1; 9543 char isabuf[257]; /* from sysinfo(2) manpage */ 9544 9545 if (amd64 != -1) 9546 return (amd64); 9547 9548 if (bam_alt_platform) { 9549 if (strcmp(bam_platform, "i86pc") == 0) { 9550 amd64 = 1; /* diskless server */ 9551 } 9552 } else { 9553 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 && 9554 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) { 9555 amd64 = 1; 9556 } else if (strstr(isabuf, "i386") == NULL) { 9557 amd64 = 1; /* diskless server */ 9558 } 9559 } 9560 if (amd64 == -1) 9561 amd64 = 0; 9562 9563 return (amd64); 9564 } 9565 9566 static char * 9567 get_machine(void) 9568 { 9569 static int cached = -1; 9570 static char mbuf[257]; /* from sysinfo(2) manpage */ 9571 9572 if (cached == 0) 9573 return (mbuf); 9574 9575 if (bam_alt_platform) { 9576 return (bam_platform); 9577 } else { 9578 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) { 9579 cached = 1; 9580 } 9581 } 9582 if (cached == -1) { 9583 mbuf[0] = '\0'; 9584 cached = 0; 9585 } 9586 9587 return (mbuf); 9588 } 9589 9590 int 9591 is_sparc(void) 9592 { 9593 static int issparc = -1; 9594 char mbuf[257]; /* from sysinfo(2) manpage */ 9595 9596 if (issparc != -1) 9597 return (issparc); 9598 9599 if (bam_alt_platform) { 9600 if (strncmp(bam_platform, "sun4", 4) == 0) { 9601 issparc = 1; 9602 } 9603 } else { 9604 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 && 9605 strcmp(mbuf, "sparc") == 0) { 9606 issparc = 1; 9607 } 9608 } 9609 if (issparc == -1) 9610 issparc = 0; 9611 9612 return (issparc); 9613 } 9614 9615 static void 9616 append_to_flist(filelist_t *flistp, char *s) 9617 { 9618 line_t *lp; 9619 9620 lp = s_calloc(1, sizeof (line_t)); 9621 lp->line = s_strdup(s); 9622 if (flistp->head == NULL) 9623 flistp->head = lp; 9624 else 9625 flistp->tail->next = lp; 9626 flistp->tail = lp; 9627 } 9628 9629 #if !defined(_OPB) 9630 9631 UCODE_VENDORS; 9632 9633 /*ARGSUSED*/ 9634 static void 9635 ucode_install(char *root) 9636 { 9637 int i; 9638 9639 for (i = 0; ucode_vendors[i].filestr != NULL; i++) { 9640 int cmd_len = PATH_MAX + 256; 9641 char cmd[PATH_MAX + 256]; 9642 char file[PATH_MAX]; 9643 char timestamp[PATH_MAX]; 9644 struct stat fstatus, tstatus; 9645 struct utimbuf u_times; 9646 9647 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s", 9648 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr, 9649 ucode_vendors[i].extstr); 9650 9651 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode))) 9652 continue; 9653 9654 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file); 9655 9656 if (stat(timestamp, &tstatus) == 0 && 9657 fstatus.st_mtime <= tstatus.st_mtime) 9658 continue; 9659 9660 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R " 9661 "%s/%s/%s %s > /dev/null 2>&1", bam_root, 9662 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file); 9663 if (system(cmd) != 0) 9664 return; 9665 9666 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1) 9667 return; 9668 9669 u_times.actime = fstatus.st_atime; 9670 u_times.modtime = fstatus.st_mtime; 9671 (void) utime(timestamp, &u_times); 9672 } 9673 } 9674 #endif