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