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