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