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