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