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