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