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