6005 bootadm update-archive should create/update archive sha-1 hash Reviewed by:...
[unleashed.git] / usr / src / cmd / boot / bootadm / bootadm.c
blob6094148c92b666cb21eb8db1a3a895c47727f5c0
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright 2012 Milan Jurik. All rights reserved.
27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28 * Copyright (c) 2015 by Delphix. All rights reserved.
29 * Copyright 2016 Toomas Soome <tsoome@me.com>
33 * bootadm(1M) is a new utility for managing bootability of
34 * Solaris *Newboot* environments. It has two primary tasks:
35 * - Allow end users to manage bootability of Newboot Solaris instances
36 * - Provide services to other subsystems in Solaris (primarily Install)
39 /* Headers */
40 #include <stdio.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <alloca.h>
48 #include <stdarg.h>
49 #include <limits.h>
50 #include <signal.h>
51 #include <sys/wait.h>
52 #include <sys/mnttab.h>
53 #include <sys/mntent.h>
54 #include <sys/statvfs.h>
55 #include <libnvpair.h>
56 #include <ftw.h>
57 #include <fcntl.h>
58 #include <strings.h>
59 #include <utime.h>
60 #include <sys/systeminfo.h>
61 #include <sys/dktp/fdisk.h>
62 #include <sys/param.h>
63 #include <dirent.h>
64 #include <ctype.h>
65 #include <libgen.h>
66 #include <sys/sysmacros.h>
67 #include <sys/elf.h>
68 #include <libscf.h>
69 #include <zlib.h>
70 #include <sys/lockfs.h>
71 #include <sys/filio.h>
72 #include <libbe.h>
73 #ifdef i386
74 #include <libfdisk.h>
75 #endif
77 #if !defined(_OBP)
78 #include <sys/ucode.h>
79 #endif
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>
90 #include "message.h"
91 #include "bootadm.h"
93 #ifndef TEXT_DOMAIN
94 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
95 #endif /* TEXT_DOMAIN */
97 /* Type definitions */
99 /* Primary subcmds */
100 typedef enum {
101 BAM_MENU = 3,
102 BAM_ARCHIVE,
103 BAM_INSTALL
104 } subcmd_t;
106 typedef enum {
107 OPT_ABSENT = 0, /* No option */
108 OPT_REQ, /* option required */
109 OPT_OPTIONAL /* option may or may not be present */
110 } option_t;
112 typedef struct {
113 char *subcmd;
114 option_t option;
115 error_t (*handler)();
116 int unpriv; /* is this an unprivileged command */
117 } subcmd_defn_t;
119 #define LINE_INIT 0 /* lineNum initial value */
120 #define ENTRY_INIT -1 /* entryNum initial value */
121 #define ALL_ENTRIES -2 /* selects all boot entries */
123 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
124 #define PARTNO_EFI -2 /* EFI partition table found */
126 #define GRUB_DIR "/boot/grub"
127 #define GRUB_STAGE2 GRUB_DIR "/stage2"
128 #define GRUB_MENU "/boot/grub/menu.lst"
129 #define MENU_TMP "/boot/grub/menu.lst.tmp"
130 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
131 #define RAMDISK_SPECIAL "/dev/ramdisk/"
132 #define STUBBOOT "/stubboot"
133 #define MULTIBOOT "/platform/i86pc/multiboot"
134 #define GRUBSIGN_DIR "/boot/grub/bootsign"
135 #define GRUBSIGN_BACKUP "/etc/bootsign"
136 #define GRUBSIGN_UFS_PREFIX "rootfs"
137 #define GRUBSIGN_ZFS_PREFIX "pool_"
138 #define GRUBSIGN_LU_PREFIX "BE_"
139 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
140 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
142 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
144 /* lock related */
145 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
146 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
148 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
149 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
150 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
151 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
153 #define GRUB_slice "/etc/lu/GRUB_slice"
154 #define GRUB_root "/etc/lu/GRUB_root"
155 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
156 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
157 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
158 #define LULIB "/usr/lib/lu/lulib"
159 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
160 #define CKSUM "/usr/bin/cksum"
161 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
162 #define BOOTADM "/sbin/bootadm"
164 #define INSTALLGRUB "/sbin/installgrub"
165 #define STAGE1 "/boot/grub/stage1"
166 #define STAGE2 "/boot/grub/stage2"
168 typedef enum zfs_mnted {
169 ZFS_MNT_ERROR = -1,
170 LEGACY_MOUNTED = 1,
171 LEGACY_ALREADY,
172 ZFS_MOUNTED,
173 ZFS_ALREADY
174 } zfs_mnted_t;
177 * Default file attributes
179 #define DEFAULT_DEV_MODE 0644 /* default permissions */
180 #define DEFAULT_DEV_UID 0 /* user root */
181 #define DEFAULT_DEV_GID 3 /* group sys */
184 * Menu related
185 * menu_cmd_t and menu_cmds must be kept in sync
187 char *menu_cmds[] = {
188 "default", /* DEFAULT_CMD */
189 "timeout", /* TIMEOUT_CMD */
190 "title", /* TITLE_CMD */
191 "root", /* ROOT_CMD */
192 "kernel", /* KERNEL_CMD */
193 "kernel$", /* KERNEL_DOLLAR_CMD */
194 "module", /* MODULE_CMD */
195 "module$", /* MODULE_DOLLAR_CMD */
196 " ", /* SEP_CMD */
197 "#", /* COMMENT_CMD */
198 "chainloader", /* CHAINLOADER_CMD */
199 "args", /* ARGS_CMD */
200 "findroot", /* FINDROOT_CMD */
201 "bootfs", /* BOOTFS_CMD */
202 NULL
205 #define OPT_ENTRY_NUM "entry"
208 * exec_cmd related
210 typedef struct {
211 line_t *head;
212 line_t *tail;
213 } filelist_t;
215 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
216 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
218 #define FILE_STAT "boot/solaris/filestat.ramdisk"
219 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
220 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
221 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
223 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
225 /* Globals */
226 int bam_verbose;
227 int bam_force;
228 int bam_debug;
229 static char *prog;
230 static subcmd_t bam_cmd;
231 static char *bam_root;
232 static int bam_rootlen;
233 static int bam_root_readonly;
234 static int bam_alt_root;
235 static int bam_extend = 0;
236 static int bam_purge = 0;
237 static char *bam_subcmd;
238 static char *bam_opt;
239 static char **bam_argv;
240 static char *bam_pool;
241 static int bam_argc;
242 static int bam_check;
243 static int bam_saved_check;
244 static int bam_smf_check;
245 static int bam_lock_fd = -1;
246 static int bam_zfs;
247 static int bam_mbr;
248 static char rootbuf[PATH_MAX] = "/";
249 static int bam_update_all;
250 static int bam_alt_platform;
251 static char *bam_platform;
252 static char *bam_home_env = NULL;
254 /* function prototypes */
255 static void parse_args_internal(int, char *[]);
256 static void parse_args(int, char *argv[]);
257 static error_t bam_menu(char *, char *, int, char *[]);
258 static error_t bam_install(char *, char *);
259 static error_t bam_archive(char *, char *);
261 static void bam_lock(void);
262 static void bam_unlock(void);
264 static int exec_cmd(char *, filelist_t *);
265 static error_t read_globals(menu_t *, char *, char *, int);
266 static int menu_on_bootdisk(char *os_root, char *menu_root);
267 static menu_t *menu_read(char *);
268 static error_t menu_write(char *, menu_t *);
269 static void linelist_free(line_t *);
270 static void menu_free(menu_t *);
271 static void filelist_free(filelist_t *);
272 static error_t list2file(char *, char *, char *, line_t *);
273 static error_t list_entry(menu_t *, char *, char *);
274 static error_t list_setting(menu_t *, char *, char *);
275 static error_t delete_all_entries(menu_t *, char *, char *);
276 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
277 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
279 static error_t install_bootloader(void);
280 static error_t update_archive(char *, char *);
281 static error_t list_archive(char *, char *);
282 static error_t update_all(char *, char *);
283 static error_t read_list(char *, filelist_t *);
284 static error_t set_option(menu_t *, char *, char *);
285 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
286 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
287 static char *expand_path(const char *);
289 static long s_strtol(char *);
290 static int s_fputs(char *, FILE *);
292 static int is_zfs(char *root);
293 static int is_ufs(char *root);
294 static int is_pcfs(char *root);
295 static int is_amd64(void);
296 static char *get_machine(void);
297 static void append_to_flist(filelist_t *, char *);
298 static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted);
299 static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt);
300 static int ufs_add_to_sign_list(char *sign);
301 static error_t synchronize_BE_menu(void);
303 #if !defined(_OBP)
304 static void ucode_install();
305 #endif
307 /* Menu related sub commands */
308 static subcmd_defn_t menu_subcmds[] = {
309 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
310 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
311 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
312 "update_entry", OPT_REQ, update_entry, 0, /* menu */
313 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
314 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
315 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
316 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
317 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
318 NULL, 0, NULL, 0 /* must be last */
321 /* Archive related sub commands */
322 static subcmd_defn_t arch_subcmds[] = {
323 "update", OPT_ABSENT, update_archive, 0, /* PUB */
324 "update_all", OPT_ABSENT, update_all, 0, /* PVT */
325 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */
326 NULL, 0, NULL, 0 /* must be last */
329 /* Install related sub commands */
330 static subcmd_defn_t inst_subcmds[] = {
331 "install_bootloader", OPT_ABSENT, install_bootloader, 0, /* PUB */
332 NULL, 0, NULL, 0 /* must be last */
335 enum dircache_copy_opt {
336 FILE32 = 0,
337 FILE64,
338 CACHEDIR_NUM
342 * Directory specific flags:
343 * NEED_UPDATE : the specified archive needs to be updated
344 * NO_MULTI : don't extend the specified archive, but recreate it
346 #define NEED_UPDATE 0x00000001
347 #define NO_MULTI 0x00000002
349 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
350 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
351 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
353 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
354 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
355 #define get_count(id) (walk_arg.dirinfo[id].count)
356 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
357 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
360 * dirinfo_t (specific cache directory information):
361 * cdir_path: path to the archive cache directory
362 * update_path: path to the update directory (contains the files that will be
363 * used to extend the archive)
364 * has_dir: the specified cache directory is active
365 * count: the number of files to update
366 * flags: directory specific flags
368 typedef struct _dirinfo {
369 char cdir_path[PATH_MAX];
370 char update_path[PATH_MAX];
371 int has_dir;
372 int count;
373 int flags;
374 } dirinfo_t;
377 * Update flags:
378 * NEED_CACHE_DIR : cache directory is missing and needs to be created
379 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
380 * UPDATE_ERROR : an error occourred while traversing the list of files
381 * RDONLY_FSCHK : the target filesystem is read-only
382 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
384 #define NEED_CACHE_DIR 0x00000001
385 #define IS_SPARC_TARGET 0x00000002
386 #define UPDATE_ERROR 0x00000004
387 #define RDONLY_FSCHK 0x00000008
388 #define INVALIDATE_CACHE 0x00000010
390 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
391 #define set_flag(flag) (walk_arg.update_flags |= flag)
392 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
395 * struct walk_arg :
396 * update_flags: flags related to the current updating process
397 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
398 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
400 static struct {
401 int update_flags;
402 nvlist_t *new_nvlp;
403 nvlist_t *old_nvlp;
404 FILE *sparcfile;
405 dirinfo_t dirinfo[CACHEDIR_NUM];
406 } walk_arg;
408 struct safefile {
409 char *name;
410 struct safefile *next;
413 static struct safefile *safefiles = NULL;
416 * svc:/system/filesystem/usr:default service checks for this file and
417 * does a boot archive update and then reboot the system.
419 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
422 * svc:/system/boot-archive-update:default checks for this file and
423 * updates the boot archive.
425 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
427 /* Thanks growisofs */
428 #define CD_BLOCK ((off64_t)2048)
429 #define VOLDESC_OFF 16
430 #define DVD_BLOCK (32*1024)
431 #define MAX_IVDs 16
433 struct iso_pdesc {
434 unsigned char type [1];
435 unsigned char id [5];
436 unsigned char void1 [80-5-1];
437 unsigned char volume_space_size [8];
438 unsigned char void2 [2048-80-8];
442 * COUNT_MAX: maximum number of changed files to justify a multisession update
443 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
444 * update
446 #define COUNT_MAX 50
447 #define BA_SIZE_MAX (50 * 1024 * 1024)
449 #define bam_nowrite() (bam_check || bam_smf_check)
451 static int sync_menu = 1; /* whether we need to sync the BE menus */
453 static void
454 usage(void)
456 (void) fprintf(stderr, "USAGE:\n");
458 /* archive usage */
459 (void) fprintf(stderr,
460 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
461 (void) fprintf(stderr,
462 "\t%s list-archive [-R altroot [-p platform]]\n", prog);
463 #if defined(_OBP)
464 (void) fprintf(stderr,
465 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
466 #else
467 (void) fprintf(stderr,
468 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
469 #endif
470 #if !defined(_OBP)
471 /* x86 only */
472 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
473 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
474 #endif
478 * Best effort attempt to restore the $HOME value.
480 static void
481 restore_env()
483 char home_env[PATH_MAX];
485 if (bam_home_env) {
486 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
487 bam_home_env);
488 (void) putenv(home_env);
493 #define SLEEP_TIME 5
494 #define MAX_TRIES 4
497 * Sanitize the environment in which bootadm will execute its sub-processes
498 * (ex. mkisofs). This is done to prevent those processes from attempting
499 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
500 * or, potentially, insecure.
502 static void
503 sanitize_env()
505 int stry = 0;
507 /* don't depend on caller umask */
508 (void) umask(0022);
510 /* move away from a potential unsafe current working directory */
511 while (chdir("/") == -1) {
512 if (errno != EINTR) {
513 bam_print("WARNING: unable to chdir to /");
514 break;
518 bam_home_env = getenv("HOME");
519 while (bam_home_env != NULL && putenv("HOME=/") == -1) {
520 if (errno == ENOMEM) {
521 /* retry no more than MAX_TRIES times */
522 if (++stry > MAX_TRIES) {
523 bam_print("WARNING: unable to recover from "
524 "system memory pressure... aborting \n");
525 bam_exit(EXIT_FAILURE);
527 /* memory is tight, try to sleep */
528 bam_print("Attempting to recover from memory pressure: "
529 "sleeping for %d seconds\n", SLEEP_TIME * stry);
530 (void) sleep(SLEEP_TIME * stry);
531 } else {
532 bam_print("WARNING: unable to sanitize HOME\n");
538 main(int argc, char *argv[])
540 error_t ret;
542 (void) setlocale(LC_ALL, "");
543 (void) textdomain(TEXT_DOMAIN);
545 if ((prog = strrchr(argv[0], '/')) == NULL) {
546 prog = argv[0];
547 } else {
548 prog++;
551 INJECT_ERROR1("ASSERT_ON", assert(0))
553 sanitize_env();
555 parse_args(argc, argv);
557 switch (bam_cmd) {
558 case BAM_MENU:
559 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
560 break;
561 case BAM_ARCHIVE:
562 ret = bam_archive(bam_subcmd, bam_opt);
563 break;
564 case BAM_INSTALL:
565 ret = bam_install(bam_subcmd, bam_opt);
566 break;
567 default:
568 usage();
569 bam_exit(1);
572 if (ret != BAM_SUCCESS)
573 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
575 bam_unlock();
576 return (0);
580 * Equivalence of public and internal commands:
581 * update-archive -- -a update
582 * list-archive -- -a list
583 * set-menu -- -m set_option
584 * list-menu -- -m list_entry
585 * update-menu -- -m update_entry
586 * install-bootloader -- -i install_bootloader
588 static struct cmd_map {
589 char *bam_cmdname;
590 int bam_cmd;
591 char *bam_subcmd;
592 } cmd_map[] = {
593 { "update-archive", BAM_ARCHIVE, "update"},
594 { "list-archive", BAM_ARCHIVE, "list"},
595 { "set-menu", BAM_MENU, "set_option"},
596 { "list-menu", BAM_MENU, "list_entry"},
597 { "update-menu", BAM_MENU, "update_entry"},
598 { "install-bootloader", BAM_INSTALL, "install_bootloader"},
599 { NULL, 0, NULL}
603 * Commands syntax published in bootadm(1M) are parsed here
605 static void
606 parse_args(int argc, char *argv[])
608 struct cmd_map *cmp = cmd_map;
610 /* command conforming to the final spec */
611 if (argc > 1 && argv[1][0] != '-') {
613 * Map commands to internal table.
615 while (cmp->bam_cmdname) {
616 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
617 bam_cmd = cmp->bam_cmd;
618 bam_subcmd = cmp->bam_subcmd;
619 break;
621 cmp++;
623 if (cmp->bam_cmdname == NULL) {
624 usage();
625 bam_exit(1);
627 argc--;
628 argv++;
631 parse_args_internal(argc, argv);
635 * A combination of public and private commands are parsed here.
636 * The internal syntax and the corresponding functionality are:
637 * -a update -- update-archive
638 * -a list -- list-archive
639 * -a update-all -- (reboot to sync all mnted OS archive)
640 * -i install_bootloader -- install-bootloader
641 * -m update_entry -- update-menu
642 * -m list_entry -- list-menu
643 * -m update_temp -- (reboot -- [boot-args])
644 * -m delete_all_entries -- (called from install)
645 * -m enable_hypervisor [args] -- cvt_to_hyper
646 * -m disable_hypervisor -- cvt_to_metal
647 * -m list_setting [entry] [value] -- list_setting
649 * A set of private flags is there too:
650 * -F -- purge the cache directories and rebuild them
651 * -e -- use the (faster) archive update approach (used by
652 * reboot)
654 static void
655 parse_args_internal(int argc, char *argv[])
657 int c, error;
658 extern char *optarg;
659 extern int optind, opterr;
660 #if defined(_OBP)
661 const char *optstring = "a:d:fi:m:no:veFCR:p:P:XZ";
662 #else
663 const char *optstring = "a:d:fi:m:no:veFCMR:p:P:XZ";
664 #endif
666 /* Suppress error message from getopt */
667 opterr = 0;
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(MULT_CMDS, c);
677 bam_cmd = BAM_ARCHIVE;
678 bam_subcmd = optarg;
679 break;
680 case 'd':
681 if (bam_debug) {
682 error = 1;
683 bam_error(DUP_OPT, c);
685 bam_debug = s_strtol(optarg);
686 break;
687 case 'f':
688 bam_force = 1;
689 break;
690 case 'F':
691 bam_purge = 1;
692 break;
693 case 'i':
694 if (bam_cmd) {
695 error = 1;
696 bam_error(MULT_CMDS, c);
698 bam_cmd = BAM_INSTALL;
699 bam_subcmd = optarg;
700 break;
701 case 'm':
702 if (bam_cmd) {
703 error = 1;
704 bam_error(MULT_CMDS, c);
706 bam_cmd = BAM_MENU;
707 bam_subcmd = optarg;
708 break;
709 #if !defined(_OBP)
710 case 'M':
711 bam_mbr = 1;
712 break;
713 #endif
714 case 'n':
715 bam_check = 1;
717 * We save the original value of bam_check. The new
718 * approach in case of a read-only filesystem is to
719 * behave as a check, so we need a way to restore the
720 * original value after the evaluation of the read-only
721 * filesystem has been done.
722 * Even if we don't allow at the moment a check with
723 * update_all, this approach is more robust than
724 * simply resetting bam_check to zero.
726 bam_saved_check = 1;
727 break;
728 case 'o':
729 if (bam_opt) {
730 error = 1;
731 bam_error(DUP_OPT, c);
733 bam_opt = optarg;
734 break;
735 case 'v':
736 bam_verbose = 1;
737 break;
738 case 'C':
739 bam_smf_check = 1;
740 break;
741 case 'P':
742 if (bam_pool != NULL) {
743 error = 1;
744 bam_error(DUP_OPT, c);
746 bam_pool = optarg;
747 break;
748 case 'R':
749 if (bam_root) {
750 error = 1;
751 bam_error(DUP_OPT, c);
752 break;
753 } else if (realpath(optarg, rootbuf) == NULL) {
754 error = 1;
755 bam_error(CANT_RESOLVE, optarg,
756 strerror(errno));
757 break;
759 bam_alt_root = 1;
760 bam_root = rootbuf;
761 bam_rootlen = strlen(rootbuf);
762 break;
763 case 'p':
764 bam_alt_platform = 1;
765 bam_platform = optarg;
766 if ((strcmp(bam_platform, "i86pc") != 0) &&
767 (strcmp(bam_platform, "sun4u") != 0) &&
768 (strcmp(bam_platform, "sun4v") != 0)) {
769 error = 1;
770 bam_error(INVALID_PLAT, bam_platform);
772 break;
773 case 'X':
774 bam_is_hv = BAM_HV_PRESENT;
775 break;
776 case 'Z':
777 bam_zfs = 1;
778 break;
779 case 'e':
780 bam_extend = 1;
781 break;
782 case '?':
783 error = 1;
784 bam_error(BAD_OPT, optopt);
785 break;
786 default :
787 error = 1;
788 bam_error(BAD_OPT, c);
789 break;
794 * An alternate platform requires an alternate root
796 if (bam_alt_platform && bam_alt_root == 0) {
797 usage();
798 bam_exit(0);
802 * A command option must be specfied
804 if (!bam_cmd) {
805 if (bam_opt && strcmp(bam_opt, "all") == 0) {
806 usage();
807 bam_exit(0);
809 bam_error(NEED_CMD);
810 error = 1;
813 if (error) {
814 usage();
815 bam_exit(1);
818 if (optind > argc) {
819 bam_error(INT_ERROR, "parse_args");
820 bam_exit(1);
821 } else if (optind < argc) {
822 bam_argv = &argv[optind];
823 bam_argc = argc - optind;
827 * mbr and pool are options for install_bootloader
829 if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
830 usage();
831 bam_exit(0);
835 * -n implies verbose mode
837 if (bam_check)
838 bam_verbose = 1;
841 static error_t
842 check_subcmd_and_options(
843 char *subcmd,
844 char *opt,
845 subcmd_defn_t *table,
846 error_t (**fp)())
848 int i;
850 if (subcmd == NULL) {
851 bam_error(NEED_SUBCMD);
852 return (BAM_ERROR);
855 if (strcmp(subcmd, "set_option") == 0) {
856 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
857 bam_error(MISSING_ARG);
858 usage();
859 return (BAM_ERROR);
860 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
861 bam_error(TRAILING_ARGS);
862 usage();
863 return (BAM_ERROR);
865 } else if (strcmp(subcmd, "update_all") == 0) {
867 * The only option we accept for the "update_all"
868 * subcmd is "fastboot".
870 if (bam_argc > 1 || (bam_argc == 1 &&
871 strcmp(bam_argv[0], "fastboot") != 0)) {
872 bam_error(TRAILING_ARGS);
873 usage();
874 return (BAM_ERROR);
876 if (bam_argc == 1)
877 sync_menu = 0;
878 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
879 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
881 * Of the remaining subcommands, only "enable_hypervisor" and
882 * "list_setting" take trailing arguments.
884 bam_error(TRAILING_ARGS);
885 usage();
886 return (BAM_ERROR);
889 if (bam_root == NULL) {
890 bam_root = rootbuf;
891 bam_rootlen = 1;
894 /* verify that subcmd is valid */
895 for (i = 0; table[i].subcmd != NULL; i++) {
896 if (strcmp(table[i].subcmd, subcmd) == 0)
897 break;
900 if (table[i].subcmd == NULL) {
901 bam_error(INVALID_SUBCMD, subcmd);
902 return (BAM_ERROR);
905 if (table[i].unpriv == 0 && geteuid() != 0) {
906 bam_error(MUST_BE_ROOT);
907 return (BAM_ERROR);
911 * Currently only privileged commands need a lock
913 if (table[i].unpriv == 0)
914 bam_lock();
916 /* subcmd verifies that opt is appropriate */
917 if (table[i].option != OPT_OPTIONAL) {
918 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
919 if (opt)
920 bam_error(NO_OPT_REQ, subcmd);
921 else
922 bam_error(MISS_OPT, subcmd);
923 return (BAM_ERROR);
927 *fp = table[i].handler;
929 return (BAM_SUCCESS);
933 * NOTE: A single "/" is also considered a trailing slash and will
934 * be deleted.
936 static void
937 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
939 size_t dstlen;
941 assert(src);
942 assert(dst);
944 (void) strlcpy(dst, src, dstsize);
946 dstlen = strlen(dst);
947 if (dst[dstlen - 1] == '/') {
948 dst[dstlen - 1] = '\0';
952 static int
953 is_safe_exec(char *path)
955 struct stat sb;
957 if (lstat(path, &sb) != 0) {
958 bam_error(STAT_FAIL, path, strerror(errno));
959 return (BAM_ERROR);
962 if (!S_ISREG(sb.st_mode)) {
963 bam_error(PATH_EXEC_LINK, path);
964 return (BAM_ERROR);
967 if (sb.st_uid != getuid()) {
968 bam_error(PATH_EXEC_OWNER, path, getuid());
969 return (BAM_ERROR);
972 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
973 bam_error(PATH_EXEC_PERMS, path);
974 return (BAM_ERROR);
977 return (BAM_SUCCESS);
980 static error_t
981 list_setting(menu_t *mp, char *which, char *setting)
983 line_t *lp;
984 entry_t *ent;
986 char *p = which;
987 int entry;
989 int found;
991 assert(which);
992 assert(setting);
994 if (*which != NULL) {
996 * If "which" is not a number, assume it's a setting we want
997 * to look for and so set up the routine to look for "which"
998 * in the default entry.
1000 while (*p != NULL)
1001 if (!(isdigit((int)*p++))) {
1002 setting = which;
1003 which = mp->curdefault->arg;
1004 break;
1006 } else {
1007 which = mp->curdefault->arg;
1010 entry = atoi(which);
1012 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1013 ent = ent->next)
1016 if (!ent) {
1017 bam_error(NO_MATCH_ENTRY);
1018 return (BAM_ERROR);
1021 found = (*setting == NULL);
1023 for (lp = ent->start; lp != NULL; lp = lp->next) {
1024 if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
1025 bam_print(PRINT, lp->line);
1026 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1027 bam_print(PRINT, lp->arg);
1028 found = 1;
1031 if (lp == ent->end)
1032 break;
1035 if (!found) {
1036 bam_error(NO_MATCH_ENTRY);
1037 return (BAM_ERROR);
1040 return (BAM_SUCCESS);
1043 static error_t
1044 install_bootloader(void)
1046 nvlist_t *nvl;
1047 uint16_t flags = 0;
1048 int found = 0;
1049 struct extmnttab mnt;
1050 struct stat statbuf = {0};
1051 be_node_list_t *be_nodes, *node;
1052 FILE *fp;
1053 char *root_ds = NULL;
1054 int ret;
1056 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1057 bam_error(_("out of memory\n"));
1058 return (BAM_ERROR);
1062 * if bam_alt_root is set, the stage files are used from alt root.
1063 * if pool is set, the target devices are pool devices, stage files
1064 * are read from pool bootfs unless alt root is set.
1066 * use arguments as targets, stage files are from alt or current root
1067 * if no arguments and no pool, install on current boot pool.
1070 if (bam_alt_root) {
1071 if (stat(bam_root, &statbuf) != 0) {
1072 bam_error(STAT_FAIL, bam_root, strerror(errno));
1073 ret = BAM_ERROR;
1074 goto done;
1076 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1077 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1078 ret = BAM_ERROR;
1079 goto done;
1081 resetmnttab(fp);
1082 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1083 if (mnt.mnt_major == major(statbuf.st_dev) &&
1084 mnt.mnt_minor == minor(statbuf.st_dev)) {
1085 found = 1;
1086 root_ds = strdup(mnt.mnt_special);
1087 break;
1090 (void) fclose(fp);
1092 if (found == 0) {
1093 bam_error(NOT_IN_MNTTAB, bam_root);
1094 ret = BAM_ERROR;
1095 goto done;
1097 if (root_ds == NULL) {
1098 bam_error(_("out of memory\n"));
1099 ret = BAM_ERROR;
1100 goto done;
1103 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1104 bam_error(_("No BE's found\n"));
1105 ret = BAM_ERROR;
1106 goto done;
1108 for (node = be_nodes; node != NULL; node = node->be_next_node)
1109 if (strcmp(root_ds, node->be_root_ds) == 0)
1110 break;
1112 if (node == NULL)
1113 bam_error(_("BE (%s) does not exist\n"), root_ds);
1115 free(root_ds);
1116 root_ds = NULL;
1117 if (node == NULL) {
1118 be_free_list(be_nodes);
1119 ret = BAM_ERROR;
1120 goto done;
1122 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1123 node->be_node_name);
1124 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1125 node->be_root_ds);
1126 be_free_list(be_nodes);
1127 if (ret) {
1128 ret = BAM_ERROR;
1129 goto done;
1133 if (bam_force)
1134 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1135 if (bam_mbr)
1136 flags |= BE_INSTALLBOOT_FLAG_MBR;
1137 if (bam_verbose)
1138 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1140 if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1141 bam_error(_("out of memory\n"));
1142 goto done;
1146 * if altroot was set, we got be name and be root, only need
1147 * to set pool name as target.
1148 * if no altroot, need to find be name and root from pool.
1150 if (bam_pool != NULL) {
1151 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1152 if (ret) {
1153 ret = BAM_ERROR;
1154 goto done;
1156 if (found) {
1157 ret = be_installboot(nvl);
1158 if (ret)
1159 ret = BAM_ERROR;
1160 goto done;
1164 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1165 bam_error(_("No BE's found\n"));
1166 ret = BAM_ERROR;
1167 goto done;
1170 if (bam_pool != NULL) {
1172 * find active be_node in bam_pool
1174 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1175 if (strcmp(bam_pool, node->be_rpool) != 0)
1176 continue;
1177 if (node->be_active_on_boot)
1178 break;
1180 if (node == NULL) {
1181 bam_error(_("No active BE in %s\n"), bam_pool);
1182 be_free_list(be_nodes);
1183 ret = BAM_ERROR;
1184 goto done;
1186 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1187 node->be_node_name);
1188 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1189 node->be_root_ds);
1190 be_free_list(be_nodes);
1191 if (ret) {
1192 ret = BAM_ERROR;
1193 goto done;
1195 ret = be_installboot(nvl);
1196 if (ret)
1197 ret = BAM_ERROR;
1198 goto done;
1202 * get dataset for "/" and fill up the args.
1204 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1205 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1206 ret = BAM_ERROR;
1207 be_free_list(be_nodes);
1208 goto done;
1210 resetmnttab(fp);
1211 found = 0;
1212 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1213 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1214 found = 1;
1215 root_ds = strdup(mnt.mnt_special);
1216 break;
1219 (void) fclose(fp);
1221 if (found == 0) {
1222 bam_error(NOT_IN_MNTTAB, "/");
1223 ret = BAM_ERROR;
1224 be_free_list(be_nodes);
1225 goto done;
1227 if (root_ds == NULL) {
1228 bam_error(_("out of memory\n"));
1229 ret = BAM_ERROR;
1230 be_free_list(be_nodes);
1231 goto done;
1234 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1235 if (strcmp(root_ds, node->be_root_ds) == 0)
1236 break;
1239 if (node == NULL) {
1240 bam_error(_("No such BE: %s\n"), root_ds);
1241 free(root_ds);
1242 be_free_list(be_nodes);
1243 ret = BAM_ERROR;
1244 goto done;
1246 free(root_ds);
1248 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1249 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1250 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1251 be_free_list(be_nodes);
1253 if (ret)
1254 ret = BAM_ERROR;
1255 else
1256 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1257 done:
1258 nvlist_free(nvl);
1260 return (ret);
1263 static error_t
1264 bam_install(char *subcmd, char *opt)
1266 error_t (*f)(void);
1269 * Check arguments
1271 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1272 BAM_ERROR)
1273 return (BAM_ERROR);
1275 return (f());
1278 static error_t
1279 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1281 error_t ret;
1282 char menu_path[PATH_MAX];
1283 char clean_menu_root[PATH_MAX];
1284 char path[PATH_MAX];
1285 menu_t *menu;
1286 char menu_root[PATH_MAX];
1287 struct stat sb;
1288 error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1289 char *special;
1290 char *pool = NULL;
1291 zfs_mnted_t zmnted;
1292 char *zmntpt;
1293 char *osdev;
1294 char *osroot;
1295 const char *fcn = "bam_menu()";
1298 * Menu sub-command only applies to GRUB (i.e. x86)
1300 if (!is_grub(bam_alt_root ? bam_root : "/")) {
1301 bam_error(NOT_GRUB_BOOT);
1302 return (BAM_ERROR);
1306 * Check arguments
1308 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1309 if (ret == BAM_ERROR) {
1310 return (BAM_ERROR);
1313 assert(bam_root);
1315 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1316 osdev = osroot = NULL;
1318 if (strcmp(subcmd, "update_entry") == 0) {
1319 assert(opt);
1321 osdev = strtok(opt, ",");
1322 assert(osdev);
1323 osroot = strtok(NULL, ",");
1324 if (osroot) {
1325 /* fixup bam_root so that it points at osroot */
1326 if (realpath(osroot, rootbuf) == NULL) {
1327 bam_error(CANT_RESOLVE, osroot,
1328 strerror(errno));
1329 return (BAM_ERROR);
1331 bam_alt_root = 1;
1332 bam_root = rootbuf;
1333 bam_rootlen = strlen(rootbuf);
1338 * We support menu on PCFS (under certain conditions), but
1339 * not the OS root
1341 if (is_pcfs(bam_root)) {
1342 bam_error(PCFS_ROOT_NOTSUP, bam_root);
1343 return (BAM_ERROR);
1346 if (stat(menu_root, &sb) == -1) {
1347 bam_error(CANNOT_LOCATE_GRUB_MENU);
1348 return (BAM_ERROR);
1351 BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root));
1354 * We no longer use the GRUB slice file. If it exists, then
1355 * the user is doing something that is unsupported (such as
1356 * standard upgrading an old Live Upgrade BE). If that
1357 * happens, mimic existing behavior i.e. pretend that it is
1358 * not a BE. Emit a warning though.
1360 if (bam_alt_root) {
1361 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1362 GRUB_slice);
1363 } else {
1364 (void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1367 if (bam_verbose && stat(path, &sb) == 0)
1368 bam_error(GRUB_SLICE_FILE_EXISTS, path);
1370 if (is_zfs(menu_root)) {
1371 assert(strcmp(menu_root, bam_root) == 0);
1372 special = get_special(menu_root);
1373 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1374 if (special == NULL) {
1375 bam_error(CANT_FIND_SPECIAL, menu_root);
1376 return (BAM_ERROR);
1378 pool = strtok(special, "/");
1379 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1380 if (pool == NULL) {
1381 free(special);
1382 bam_error(CANT_FIND_POOL, menu_root);
1383 return (BAM_ERROR);
1385 BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool));
1387 zmntpt = mount_top_dataset(pool, &zmnted);
1388 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1389 if (zmntpt == NULL) {
1390 bam_error(CANT_MOUNT_POOL_DATASET, pool);
1391 free(special);
1392 return (BAM_ERROR);
1394 BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt));
1396 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1397 BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root));
1400 elide_trailing_slash(menu_root, clean_menu_root,
1401 sizeof (clean_menu_root));
1403 BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root));
1405 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1406 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1408 BAM_DPRINTF((D_MENU_PATH, fcn, menu_path));
1411 * If listing the menu, display the menu location
1413 if (strcmp(subcmd, "list_entry") == 0)
1414 bam_print(GRUB_MENU_PATH, menu_path);
1416 if ((menu = menu_read(menu_path)) == NULL) {
1417 bam_error(CANNOT_LOCATE_GRUB_MENU_FILE, menu_path);
1418 if (special != NULL)
1419 free(special);
1421 return (BAM_ERROR);
1425 * We already checked the following case in
1426 * check_subcmd_and_suboptions() above. Complete the
1427 * final step now.
1429 if (strcmp(subcmd, "set_option") == 0) {
1430 assert(largc == 1 && largv[0] && largv[1] == NULL);
1431 opt = largv[0];
1432 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1433 (strcmp(subcmd, "list_setting") != 0)) {
1434 assert(largc == 0 && largv == NULL);
1437 ret = get_boot_cap(bam_root);
1438 if (ret != BAM_SUCCESS) {
1439 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1440 goto out;
1444 * Once the sub-cmd handler has run
1445 * only the line field is guaranteed to have valid values
1447 if (strcmp(subcmd, "update_entry") == 0) {
1448 ret = f(menu, menu_root, osdev);
1449 } else if (strcmp(subcmd, "upgrade") == 0) {
1450 ret = f(menu, bam_root, menu_root);
1451 } else if (strcmp(subcmd, "list_entry") == 0) {
1452 ret = f(menu, menu_path, opt);
1453 } else if (strcmp(subcmd, "list_setting") == 0) {
1454 ret = f(menu, ((largc > 0) ? largv[0] : ""),
1455 ((largc > 1) ? largv[1] : ""));
1456 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1457 if (is_sparc()) {
1458 bam_error(NO_SPARC, subcmd);
1459 ret = BAM_ERROR;
1460 } else {
1461 ret = f(menu, bam_root, NULL);
1463 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1464 if (is_sparc()) {
1465 bam_error(NO_SPARC, subcmd);
1466 ret = BAM_ERROR;
1467 } else {
1468 char *extra_args = NULL;
1471 * Compress all arguments passed in the largv[] array
1472 * into one string that can then be appended to the
1473 * end of the kernel$ string the routine to enable the
1474 * hypervisor will build.
1476 * This allows the caller to supply arbitrary unparsed
1477 * arguments, such as dom0 memory settings or APIC
1478 * options.
1480 * This concatenation will be done without ANY syntax
1481 * checking whatsoever, so it's the responsibility of
1482 * the caller to make sure the arguments are valid and
1483 * do not duplicate arguments the conversion routines
1484 * may create.
1486 if (largc > 0) {
1487 int extra_len, i;
1489 for (extra_len = 0, i = 0; i < largc; i++)
1490 extra_len += strlen(largv[i]);
1493 * Allocate space for argument strings,
1494 * intervening spaces and terminating NULL.
1496 extra_args = alloca(extra_len + largc);
1498 (void) strcpy(extra_args, largv[0]);
1500 for (i = 1; i < largc; i++) {
1501 (void) strcat(extra_args, " ");
1502 (void) strcat(extra_args, largv[i]);
1506 ret = f(menu, bam_root, extra_args);
1508 } else
1509 ret = f(menu, NULL, opt);
1511 if (ret == BAM_WRITE) {
1512 BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root));
1513 ret = menu_write(clean_menu_root, menu);
1516 out:
1517 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1518 assert((is_zfs(menu_root)) ^ (pool == NULL));
1519 if (pool) {
1520 (void) umount_top_dataset(pool, zmnted, zmntpt);
1521 free(special);
1523 menu_free(menu);
1524 return (ret);
1528 static error_t
1529 bam_archive(
1530 char *subcmd,
1531 char *opt)
1533 error_t ret;
1534 error_t (*f)(char *root, char *opt);
1535 const char *fcn = "bam_archive()";
1538 * Add trailing / for archive subcommands
1540 if (rootbuf[strlen(rootbuf) - 1] != '/')
1541 (void) strcat(rootbuf, "/");
1542 bam_rootlen = strlen(rootbuf);
1545 * Check arguments
1547 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1548 if (ret != BAM_SUCCESS) {
1549 return (BAM_ERROR);
1552 ret = get_boot_cap(rootbuf);
1553 if (ret != BAM_SUCCESS) {
1554 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1555 return (ret);
1559 * Check archive not supported with update_all
1560 * since it is awkward to display out-of-sync
1561 * information for each BE.
1563 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1564 bam_error(CHECK_NOT_SUPPORTED, subcmd);
1565 return (BAM_ERROR);
1568 if (strcmp(subcmd, "update_all") == 0)
1569 bam_update_all = 1;
1571 #if !defined(_OBP)
1572 ucode_install(bam_root);
1573 #endif
1575 ret = f(bam_root, opt);
1577 bam_update_all = 0;
1579 return (ret);
1582 /*PRINTFLIKE1*/
1583 void
1584 bam_error(char *format, ...)
1586 va_list ap;
1588 va_start(ap, format);
1589 (void) fprintf(stderr, "%s: ", prog);
1590 (void) vfprintf(stderr, format, ap);
1591 va_end(ap);
1594 /*PRINTFLIKE1*/
1595 void
1596 bam_derror(char *format, ...)
1598 va_list ap;
1600 assert(bam_debug);
1602 va_start(ap, format);
1603 (void) fprintf(stderr, "DEBUG: ");
1604 (void) vfprintf(stderr, format, ap);
1605 va_end(ap);
1608 /*PRINTFLIKE1*/
1609 void
1610 bam_print(char *format, ...)
1612 va_list ap;
1614 va_start(ap, format);
1615 (void) vfprintf(stdout, format, ap);
1616 va_end(ap);
1619 /*PRINTFLIKE1*/
1620 void
1621 bam_print_stderr(char *format, ...)
1623 va_list ap;
1625 va_start(ap, format);
1626 (void) vfprintf(stderr, format, ap);
1627 va_end(ap);
1630 void
1631 bam_exit(int excode)
1633 restore_env();
1634 bam_unlock();
1635 exit(excode);
1638 static void
1639 bam_lock(void)
1641 struct flock lock;
1642 pid_t pid;
1644 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1645 if (bam_lock_fd < 0) {
1647 * We may be invoked early in boot for archive verification.
1648 * In this case, root is readonly and /var/run may not exist.
1649 * Proceed without the lock
1651 if (errno == EROFS || errno == ENOENT) {
1652 bam_root_readonly = 1;
1653 return;
1656 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
1657 bam_exit(1);
1660 lock.l_type = F_WRLCK;
1661 lock.l_whence = SEEK_SET;
1662 lock.l_start = 0;
1663 lock.l_len = 0;
1665 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1666 if (errno != EACCES && errno != EAGAIN) {
1667 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1668 (void) close(bam_lock_fd);
1669 bam_lock_fd = -1;
1670 bam_exit(1);
1672 pid = 0;
1673 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1674 bam_print(FILE_LOCKED, pid);
1676 lock.l_type = F_WRLCK;
1677 lock.l_whence = SEEK_SET;
1678 lock.l_start = 0;
1679 lock.l_len = 0;
1680 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1681 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1682 (void) close(bam_lock_fd);
1683 bam_lock_fd = -1;
1684 bam_exit(1);
1688 /* We own the lock now */
1689 pid = getpid();
1690 (void) write(bam_lock_fd, &pid, sizeof (pid));
1693 static void
1694 bam_unlock(void)
1696 struct flock unlock;
1699 * NOP if we don't hold the lock
1701 if (bam_lock_fd < 0) {
1702 return;
1705 unlock.l_type = F_UNLCK;
1706 unlock.l_whence = SEEK_SET;
1707 unlock.l_start = 0;
1708 unlock.l_len = 0;
1710 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1711 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1714 if (close(bam_lock_fd) == -1) {
1715 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
1717 bam_lock_fd = -1;
1720 static error_t
1721 list_archive(char *root, char *opt)
1723 filelist_t flist;
1724 filelist_t *flistp = &flist;
1725 line_t *lp;
1727 assert(root);
1728 assert(opt == NULL);
1730 flistp->head = flistp->tail = NULL;
1731 if (read_list(root, flistp) != BAM_SUCCESS) {
1732 return (BAM_ERROR);
1734 assert(flistp->head && flistp->tail);
1736 for (lp = flistp->head; lp; lp = lp->next) {
1737 bam_print(PRINT, lp->line);
1740 filelist_free(flistp);
1742 return (BAM_SUCCESS);
1746 * This routine writes a list of lines to a file.
1747 * The list is *not* freed
1749 static error_t
1750 list2file(char *root, char *tmp, char *final, line_t *start)
1752 char tmpfile[PATH_MAX];
1753 char path[PATH_MAX];
1754 FILE *fp;
1755 int ret;
1756 struct stat sb;
1757 mode_t mode;
1758 uid_t root_uid;
1759 gid_t sys_gid;
1760 struct passwd *pw;
1761 struct group *gp;
1762 const char *fcn = "list2file()";
1764 (void) snprintf(path, sizeof (path), "%s%s", root, final);
1766 if (start == NULL) {
1767 /* Empty GRUB menu */
1768 if (stat(path, &sb) != -1) {
1769 bam_print(UNLINK_EMPTY, path);
1770 if (unlink(path) != 0) {
1771 bam_error(UNLINK_FAIL, path, strerror(errno));
1772 return (BAM_ERROR);
1773 } else {
1774 return (BAM_SUCCESS);
1777 return (BAM_SUCCESS);
1781 * Preserve attributes of existing file if possible,
1782 * otherwise ask the system for uid/gid of root/sys.
1783 * If all fails, fall back on hard-coded defaults.
1785 if (stat(path, &sb) != -1) {
1786 mode = sb.st_mode;
1787 root_uid = sb.st_uid;
1788 sys_gid = sb.st_gid;
1789 } else {
1790 mode = DEFAULT_DEV_MODE;
1791 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1792 root_uid = pw->pw_uid;
1793 } else {
1794 bam_error(CANT_FIND_USER,
1795 DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1796 root_uid = (uid_t)DEFAULT_DEV_UID;
1798 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1799 sys_gid = gp->gr_gid;
1800 } else {
1801 bam_error(CANT_FIND_GROUP,
1802 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1803 sys_gid = (gid_t)DEFAULT_DEV_GID;
1807 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1809 /* Truncate tmpfile first */
1810 fp = fopen(tmpfile, "w");
1811 if (fp == NULL) {
1812 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1813 return (BAM_ERROR);
1815 ret = fclose(fp);
1816 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1817 if (ret == EOF) {
1818 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1819 return (BAM_ERROR);
1822 /* Now open it in append mode */
1823 fp = fopen(tmpfile, "a");
1824 if (fp == NULL) {
1825 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1826 return (BAM_ERROR);
1829 for (; start; start = start->next) {
1830 ret = s_fputs(start->line, fp);
1831 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1832 if (ret == EOF) {
1833 bam_error(WRITE_FAIL, tmpfile, strerror(errno));
1834 (void) fclose(fp);
1835 return (BAM_ERROR);
1839 ret = fclose(fp);
1840 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1841 if (ret == EOF) {
1842 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1843 return (BAM_ERROR);
1847 * Set up desired attributes. Ignore failures on filesystems
1848 * not supporting these operations - pcfs reports unsupported
1849 * operations as EINVAL.
1851 ret = chmod(tmpfile, mode);
1852 if (ret == -1 &&
1853 errno != EINVAL && errno != ENOTSUP) {
1854 bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
1855 return (BAM_ERROR);
1858 ret = chown(tmpfile, root_uid, sys_gid);
1859 if (ret == -1 &&
1860 errno != EINVAL && errno != ENOTSUP) {
1861 bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
1862 return (BAM_ERROR);
1866 * Do an atomic rename
1868 ret = rename(tmpfile, path);
1869 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1870 if (ret != 0) {
1871 bam_error(RENAME_FAIL, path, strerror(errno));
1872 return (BAM_ERROR);
1875 BAM_DPRINTF((D_WROTE_FILE, fcn, path));
1876 return (BAM_SUCCESS);
1880 * Checks if the path specified (without the file name at the end) exists
1881 * and creates it if not. If the path exists and is not a directory, an attempt
1882 * to unlink is made.
1884 static int
1885 setup_path(char *path)
1887 char *p;
1888 int ret;
1889 struct stat sb;
1891 p = strrchr(path, '/');
1892 if (p != NULL) {
1893 *p = '\0';
1894 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1895 /* best effort attempt, mkdirp will catch the error */
1896 (void) unlink(path);
1897 if (bam_verbose)
1898 bam_print(NEED_DIRPATH, path);
1899 ret = mkdirp(path, DIR_PERMS);
1900 if (ret == -1) {
1901 bam_error(MKDIR_FAILED, path, strerror(errno));
1902 *p = '/';
1903 return (BAM_ERROR);
1906 *p = '/';
1907 return (BAM_SUCCESS);
1909 return (BAM_SUCCESS);
1912 typedef union {
1913 gzFile gzfile;
1914 int fdfile;
1915 } outfile;
1917 typedef struct {
1918 char path[PATH_MAX];
1919 outfile out;
1920 } cachefile;
1922 static int
1923 setup_file(char *base, const char *path, cachefile *cf)
1925 int ret;
1926 char *strip;
1928 /* init gzfile or fdfile in case we fail before opening */
1929 if (bam_direct == BAM_DIRECT_DBOOT)
1930 cf->out.gzfile = NULL;
1931 else
1932 cf->out.fdfile = -1;
1934 /* strip the trailing altroot path */
1935 strip = (char *)path + strlen(rootbuf);
1937 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1938 if (ret >= sizeof (cf->path)) {
1939 bam_error(PATH_TOO_LONG, rootbuf);
1940 return (BAM_ERROR);
1943 /* Check if path is present in the archive cache directory */
1944 if (setup_path(cf->path) == BAM_ERROR)
1945 return (BAM_ERROR);
1947 if (bam_direct == BAM_DIRECT_DBOOT) {
1948 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1949 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1950 return (BAM_ERROR);
1952 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1953 Z_DEFAULT_STRATEGY);
1954 } else {
1955 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1956 == -1) {
1957 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1958 return (BAM_ERROR);
1962 return (BAM_SUCCESS);
1965 static int
1966 cache_write(cachefile cf, char *buf, int size)
1968 int err;
1970 if (bam_direct == BAM_DIRECT_DBOOT) {
1971 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
1972 bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err));
1973 if (err == Z_ERRNO && bam_verbose) {
1974 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1976 return (BAM_ERROR);
1978 } else {
1979 if (write(cf.out.fdfile, buf, size) < 1) {
1980 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1981 return (BAM_ERROR);
1984 return (BAM_SUCCESS);
1987 static int
1988 cache_close(cachefile cf)
1990 int ret;
1992 if (bam_direct == BAM_DIRECT_DBOOT) {
1993 if (cf.out.gzfile) {
1994 ret = gzclose(cf.out.gzfile);
1995 if (ret != Z_OK) {
1996 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
1997 return (BAM_ERROR);
2000 } else {
2001 if (cf.out.fdfile != -1) {
2002 ret = close(cf.out.fdfile);
2003 if (ret != 0) {
2004 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
2005 return (BAM_ERROR);
2010 return (BAM_SUCCESS);
2013 static int
2014 dircache_updatefile(const char *path, int what)
2016 int ret, exitcode;
2017 char buf[4096 * 4];
2018 FILE *infile;
2019 cachefile outfile, outupdt;
2021 if (bam_nowrite()) {
2022 set_dir_flag(what, NEED_UPDATE);
2023 return (BAM_SUCCESS);
2026 if (!has_cachedir(what))
2027 return (BAM_SUCCESS);
2029 if ((infile = fopen(path, "rb")) == NULL) {
2030 bam_error(OPEN_FAIL, path, strerror(errno));
2031 return (BAM_ERROR);
2034 ret = setup_file(get_cachedir(what), path, &outfile);
2035 if (ret == BAM_ERROR) {
2036 exitcode = BAM_ERROR;
2037 goto out;
2039 if (!is_dir_flag_on(what, NO_MULTI)) {
2040 ret = setup_file(get_updatedir(what), path, &outupdt);
2041 if (ret == BAM_ERROR)
2042 set_dir_flag(what, NO_MULTI);
2045 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2046 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2047 exitcode = BAM_ERROR;
2048 goto out;
2050 if (!is_dir_flag_on(what, NO_MULTI))
2051 if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2052 set_dir_flag(what, NO_MULTI);
2055 set_dir_flag(what, NEED_UPDATE);
2056 get_count(what)++;
2057 if (get_count(what) > COUNT_MAX)
2058 set_dir_flag(what, NO_MULTI);
2059 exitcode = BAM_SUCCESS;
2060 out:
2061 (void) fclose(infile);
2062 if (cache_close(outfile) == BAM_ERROR)
2063 exitcode = BAM_ERROR;
2064 if (!is_dir_flag_on(what, NO_MULTI) &&
2065 cache_close(outupdt) == BAM_ERROR)
2066 exitcode = BAM_ERROR;
2067 if (exitcode == BAM_ERROR)
2068 set_flag(UPDATE_ERROR);
2069 return (exitcode);
2072 static int
2073 dircache_updatedir(const char *path, int what, int updt)
2075 int ret;
2076 char dpath[PATH_MAX];
2077 char *strip;
2078 struct stat sb;
2080 strip = (char *)path + strlen(rootbuf);
2082 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2083 get_updatedir(what) : get_cachedir(what), strip);
2085 if (ret >= sizeof (dpath)) {
2086 bam_error(PATH_TOO_LONG, rootbuf);
2087 set_flag(UPDATE_ERROR);
2088 return (BAM_ERROR);
2091 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2092 return (BAM_SUCCESS);
2094 if (updt) {
2095 if (!is_dir_flag_on(what, NO_MULTI))
2096 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2097 set_dir_flag(what, NO_MULTI);
2098 } else {
2099 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2100 set_flag(UPDATE_ERROR);
2101 return (BAM_ERROR);
2105 set_dir_flag(what, NEED_UPDATE);
2106 return (BAM_SUCCESS);
2109 #define DO_CACHE_DIR 0
2110 #define DO_UPDATE_DIR 1
2112 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2113 typedef Elf64_Ehdr _elfhdr;
2114 #else
2115 typedef Elf32_Ehdr _elfhdr;
2116 #endif
2119 * This routine updates the contents of the cache directory
2121 static int
2122 update_dircache(const char *path, int flags)
2124 int rc = BAM_SUCCESS;
2126 switch (flags) {
2127 case FTW_F:
2129 int fd;
2130 _elfhdr elf;
2132 if ((fd = open(path, O_RDONLY)) < 0) {
2133 bam_error(OPEN_FAIL, path, strerror(errno));
2134 set_flag(UPDATE_ERROR);
2135 rc = BAM_ERROR;
2136 break;
2140 * libelf and gelf would be a cleaner and easier way to handle
2141 * this, but libelf fails compilation if _ILP32 is defined &&
2142 * _FILE_OFFSET_BITS is != 32 ...
2144 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2145 bam_error(READ_FAIL, path, strerror(errno));
2146 set_flag(UPDATE_ERROR);
2147 (void) close(fd);
2148 rc = BAM_ERROR;
2149 break;
2151 (void) close(fd);
2154 * If the file is not an executable and is not inside an amd64
2155 * directory, we copy it in both the cache directories,
2156 * otherwise, we only copy it inside the 64-bit one.
2158 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2159 if (strstr(path, "/amd64")) {
2160 rc = dircache_updatefile(path, FILE64);
2161 } else {
2162 rc = dircache_updatefile(path, FILE32);
2163 if (rc == BAM_SUCCESS)
2164 rc = dircache_updatefile(path, FILE64);
2166 } else {
2168 * Based on the ELF class we copy the file in the 32-bit
2169 * or the 64-bit cache directory.
2171 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
2172 rc = dircache_updatefile(path, FILE32);
2173 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
2174 rc = dircache_updatefile(path, FILE64);
2175 } else {
2176 bam_print(NO3264ELF, path);
2177 /* paranoid */
2178 rc = dircache_updatefile(path, FILE32);
2179 if (rc == BAM_SUCCESS)
2180 rc = dircache_updatefile(path, FILE64);
2183 break;
2185 case FTW_D:
2186 if (strstr(path, "/amd64") == NULL) {
2187 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
2188 if (rc == BAM_SUCCESS)
2189 rc = dircache_updatedir(path, FILE32,
2190 DO_CACHE_DIR);
2191 } else {
2192 if (has_cachedir(FILE64)) {
2193 rc = dircache_updatedir(path, FILE64,
2194 DO_UPDATE_DIR);
2195 if (rc == BAM_SUCCESS)
2196 rc = dircache_updatedir(path, FILE64,
2197 DO_CACHE_DIR);
2200 break;
2201 default:
2202 rc = BAM_ERROR;
2203 break;
2206 return (rc);
2209 /*ARGSUSED*/
2210 static int
2211 cmpstat(
2212 const char *file,
2213 const struct stat *st,
2214 int flags,
2215 struct FTW *ftw)
2217 uint_t sz;
2218 uint64_t *value;
2219 uint64_t filestat[2];
2220 int error, ret, status;
2222 struct safefile *safefilep;
2223 FILE *fp;
2224 struct stat sb;
2225 regex_t re;
2228 * On SPARC we create/update links too.
2230 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2231 !is_flag_on(IS_SPARC_TARGET)))
2232 return (0);
2235 * Ignore broken links
2237 if (flags == FTW_SL && stat(file, &sb) < 0)
2238 return (0);
2241 * new_nvlp may be NULL if there were errors earlier
2242 * but this is not fatal to update determination.
2244 if (walk_arg.new_nvlp) {
2245 filestat[0] = st->st_size;
2246 filestat[1] = st->st_mtime;
2247 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2248 file + bam_rootlen, filestat, 2);
2249 if (error)
2250 bam_error(NVADD_FAIL, file, strerror(error));
2254 * If we are invoked as part of system/filesystem/boot-archive, then
2255 * there are a number of things we should not worry about
2257 if (bam_smf_check) {
2258 /* ignore amd64 modules unless we are booted amd64. */
2259 if (!is_amd64() && strstr(file, "/amd64/") != 0)
2260 return (0);
2262 /* read in list of safe files */
2263 if (safefiles == NULL)
2264 if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
2265 safefiles = s_calloc(1,
2266 sizeof (struct safefile));
2267 safefilep = safefiles;
2268 safefilep->name = s_calloc(1, MAXPATHLEN +
2269 MAXNAMELEN);
2270 safefilep->next = NULL;
2271 while (s_fgets(safefilep->name, MAXPATHLEN +
2272 MAXNAMELEN, fp) != NULL) {
2273 safefilep->next = s_calloc(1,
2274 sizeof (struct safefile));
2275 safefilep = safefilep->next;
2276 safefilep->name = s_calloc(1,
2277 MAXPATHLEN + MAXNAMELEN);
2278 safefilep->next = NULL;
2280 (void) fclose(fp);
2285 * On SPARC we create a -path-list file for mkisofs
2287 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2288 if (flags != FTW_D) {
2289 char *strip;
2291 strip = (char *)file + strlen(rootbuf);
2292 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2293 file);
2298 * We are transitioning from the old model to the dircache or the cache
2299 * directory was removed: create the entry without further checkings.
2301 if (is_flag_on(NEED_CACHE_DIR)) {
2302 if (bam_verbose)
2303 bam_print(PARSEABLE_NEW_FILE, file);
2305 if (is_flag_on(IS_SPARC_TARGET)) {
2306 set_dir_flag(FILE64, NEED_UPDATE);
2307 return (0);
2310 ret = update_dircache(file, flags);
2311 if (ret == BAM_ERROR) {
2312 bam_error(UPDT_CACHE_FAIL, file);
2313 return (-1);
2316 return (0);
2320 * We need an update if file doesn't exist in old archive
2322 if (walk_arg.old_nvlp == NULL ||
2323 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2324 file + bam_rootlen, &value, &sz) != 0) {
2325 if (bam_smf_check) /* ignore new during smf check */
2326 return (0);
2328 if (is_flag_on(IS_SPARC_TARGET)) {
2329 set_dir_flag(FILE64, NEED_UPDATE);
2330 } else {
2331 ret = update_dircache(file, flags);
2332 if (ret == BAM_ERROR) {
2333 bam_error(UPDT_CACHE_FAIL, file);
2334 return (-1);
2338 if (bam_verbose)
2339 bam_print(PARSEABLE_NEW_FILE, file);
2340 return (0);
2344 * If we got there, the file is already listed as to be included in the
2345 * iso image. We just need to know if we are going to rebuild it or not
2347 if (is_flag_on(IS_SPARC_TARGET) &&
2348 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
2349 return (0);
2351 * File exists in old archive. Check if file has changed
2353 assert(sz == 2);
2354 bcopy(value, filestat, sizeof (filestat));
2356 if (flags != FTW_D && (filestat[0] != st->st_size ||
2357 filestat[1] != st->st_mtime)) {
2358 if (bam_smf_check) {
2359 safefilep = safefiles;
2360 while (safefilep != NULL &&
2361 safefilep->name[0] != '\0') {
2362 if (regcomp(&re, safefilep->name,
2363 REG_EXTENDED|REG_NOSUB) == 0) {
2364 status = regexec(&re,
2365 file + bam_rootlen, 0, NULL, 0);
2366 regfree(&re);
2367 if (status == 0) {
2368 (void) creat(
2369 NEED_UPDATE_SAFE_FILE,
2370 0644);
2371 return (0);
2374 safefilep = safefilep->next;
2378 if (is_flag_on(IS_SPARC_TARGET)) {
2379 set_dir_flag(FILE64, NEED_UPDATE);
2380 } else {
2381 ret = update_dircache(file, flags);
2382 if (ret == BAM_ERROR) {
2383 bam_error(UPDT_CACHE_FAIL, file);
2384 return (-1);
2388 if (bam_verbose)
2389 if (bam_smf_check)
2390 bam_print(" %s\n", file);
2391 else
2392 bam_print(PARSEABLE_OUT_DATE, file);
2395 return (0);
2399 * Remove a directory path recursively
2401 static int
2402 rmdir_r(char *path)
2404 struct dirent *d = NULL;
2405 DIR *dir = NULL;
2406 char tpath[PATH_MAX];
2407 struct stat sb;
2409 if ((dir = opendir(path)) == NULL)
2410 return (-1);
2412 while (d = readdir(dir)) {
2413 if ((strcmp(d->d_name, ".") != 0) &&
2414 (strcmp(d->d_name, "..") != 0)) {
2415 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
2416 path, d->d_name);
2417 if (stat(tpath, &sb) == 0) {
2418 if (sb.st_mode & S_IFDIR)
2419 (void) rmdir_r(tpath);
2420 else
2421 (void) remove(tpath);
2425 return (remove(path));
2429 * Check if cache directory exists and, if not, create it and update flags
2430 * accordingly. If the path exists, but it's not a directory, a best effort
2431 * attempt to remove and recreate it is made.
2432 * If the user requested a 'purge', always recreate the directory from scratch.
2434 static int
2435 set_cache_dir(char *root, int what)
2437 struct stat sb;
2438 int ret = 0;
2440 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
2441 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
2442 "/amd64" : "", CACHEDIR_SUFFIX);
2444 if (ret >= sizeof (get_cachedir(what))) {
2445 bam_error(PATH_TOO_LONG, rootbuf);
2446 return (BAM_ERROR);
2449 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2450 (void) rmdir_r(get_cachedir(what));
2452 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2453 /* best effort unlink attempt, mkdir will catch errors */
2454 (void) unlink(get_cachedir(what));
2456 if (bam_verbose)
2457 bam_print(UPDATE_CDIR_MISS, get_cachedir(what));
2458 ret = mkdir(get_cachedir(what), DIR_PERMS);
2459 if (ret < 0) {
2460 bam_error(MKDIR_FAILED, get_cachedir(what),
2461 strerror(errno));
2462 get_cachedir(what)[0] = '\0';
2463 return (ret);
2465 set_flag(NEED_CACHE_DIR);
2466 set_dir_flag(what, NO_MULTI);
2469 return (BAM_SUCCESS);
2472 static int
2473 set_update_dir(char *root, int what)
2475 struct stat sb;
2476 int ret;
2478 if (is_dir_flag_on(what, NO_MULTI))
2479 return (BAM_SUCCESS);
2481 if (!bam_extend) {
2482 set_dir_flag(what, NO_MULTI);
2483 return (BAM_SUCCESS);
2486 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2487 ret = snprintf(get_updatedir(what),
2488 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2489 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2490 else
2491 ret = snprintf(get_updatedir(what),
2492 sizeof (get_updatedir(what)), "%s%s%s%s", root,
2493 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2495 if (ret >= sizeof (get_updatedir(what))) {
2496 bam_error(PATH_TOO_LONG, rootbuf);
2497 return (BAM_ERROR);
2500 if (stat(get_updatedir(what), &sb) == 0) {
2501 if (S_ISDIR(sb.st_mode))
2502 ret = rmdir_r(get_updatedir(what));
2503 else
2504 ret = unlink(get_updatedir(what));
2506 if (ret != 0)
2507 set_dir_flag(what, NO_MULTI);
2510 if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2511 set_dir_flag(what, NO_MULTI);
2513 return (BAM_SUCCESS);
2516 static int
2517 is_valid_archive(char *root, int what)
2519 char archive_path[PATH_MAX];
2520 char timestamp_path[PATH_MAX];
2521 struct stat sb, timestamp;
2522 int ret;
2524 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2525 ret = snprintf(archive_path, sizeof (archive_path),
2526 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2527 ARCHIVE_SUFFIX);
2528 else
2529 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2530 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2532 if (ret >= sizeof (archive_path)) {
2533 bam_error(PATH_TOO_LONG, rootbuf);
2534 return (BAM_ERROR);
2537 if (stat(archive_path, &sb) != 0) {
2538 if (bam_verbose && !bam_check)
2539 bam_print(UPDATE_ARCH_MISS, archive_path);
2540 set_dir_flag(what, NEED_UPDATE);
2541 set_dir_flag(what, NO_MULTI);
2542 return (BAM_SUCCESS);
2546 * The timestamp file is used to prevent stale files in the archive
2547 * cache.
2548 * Stale files can happen if the system is booted back and forth across
2549 * the transition from bootadm-before-the-cache to
2550 * bootadm-after-the-cache, since older versions of bootadm don't know
2551 * about the existence of the archive cache.
2553 * Since only bootadm-after-the-cache versions know about about this
2554 * file, we require that the boot archive be older than this file.
2556 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2557 FILE_STAT_TIMESTAMP);
2559 if (ret >= sizeof (timestamp_path)) {
2560 bam_error(PATH_TOO_LONG, rootbuf);
2561 return (BAM_ERROR);
2564 if (stat(timestamp_path, &timestamp) != 0 ||
2565 sb.st_mtime > timestamp.st_mtime) {
2566 if (bam_verbose && !bam_check)
2567 bam_print(UPDATE_CACHE_OLD);
2569 * Don't generate a false positive for the boot-archive service
2570 * but trigger an update of the archive cache in
2571 * boot-archive-update.
2573 if (bam_smf_check) {
2574 (void) creat(NEED_UPDATE_FILE, 0644);
2575 return (BAM_SUCCESS);
2578 set_flag(INVALIDATE_CACHE);
2579 set_dir_flag(what, NEED_UPDATE);
2580 set_dir_flag(what, NO_MULTI);
2581 return (BAM_SUCCESS);
2584 if (is_flag_on(IS_SPARC_TARGET))
2585 return (BAM_SUCCESS);
2587 if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2588 if (bam_verbose && !bam_check)
2589 bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX);
2590 set_dir_flag(what, NO_MULTI);
2593 return (BAM_SUCCESS);
2597 * Check flags and presence of required files and directories.
2598 * The force flag and/or absence of files should
2599 * trigger an update.
2600 * Suppress stdout output if check (-n) option is set
2601 * (as -n should only produce parseable output.)
2603 static int
2604 check_flags_and_files(char *root)
2607 struct stat sb;
2608 int ret;
2611 * If archive is missing, create archive
2613 if (is_flag_on(IS_SPARC_TARGET)) {
2614 ret = is_valid_archive(root, FILE64);
2615 if (ret == BAM_ERROR)
2616 return (BAM_ERROR);
2617 } else {
2618 int what = FILE32;
2619 do {
2620 ret = is_valid_archive(root, what);
2621 if (ret == BAM_ERROR)
2622 return (BAM_ERROR);
2623 what++;
2624 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2627 if (bam_nowrite())
2628 return (BAM_SUCCESS);
2632 * check if cache directories exist on x86.
2633 * check (and always open) the cache file on SPARC.
2635 if (is_sparc()) {
2636 ret = snprintf(get_cachedir(FILE64),
2637 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2638 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2640 if (ret >= sizeof (get_cachedir(FILE64))) {
2641 bam_error(PATH_TOO_LONG, rootbuf);
2642 return (BAM_ERROR);
2645 if (stat(get_cachedir(FILE64), &sb) != 0) {
2646 set_flag(NEED_CACHE_DIR);
2647 set_dir_flag(FILE64, NEED_UPDATE);
2650 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2651 if (walk_arg.sparcfile == NULL) {
2652 bam_error(OPEN_FAIL, get_cachedir(FILE64),
2653 strerror(errno));
2654 return (BAM_ERROR);
2657 set_dir_present(FILE64);
2658 } else {
2659 int what = FILE32;
2661 do {
2662 if (set_cache_dir(root, what) != 0)
2663 return (BAM_ERROR);
2665 set_dir_present(what);
2667 if (set_update_dir(root, what) != 0)
2668 return (BAM_ERROR);
2669 what++;
2670 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2674 * if force, create archive unconditionally
2676 if (bam_force) {
2677 if (!is_sparc())
2678 set_dir_flag(FILE32, NEED_UPDATE);
2679 set_dir_flag(FILE64, NEED_UPDATE);
2680 if (bam_verbose)
2681 bam_print(UPDATE_FORCE);
2682 return (BAM_SUCCESS);
2685 return (BAM_SUCCESS);
2688 static error_t
2689 read_one_list(char *root, filelist_t *flistp, char *filelist)
2691 char path[PATH_MAX];
2692 FILE *fp;
2693 char buf[BAM_MAXLINE];
2694 const char *fcn = "read_one_list()";
2696 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2698 fp = fopen(path, "r");
2699 if (fp == NULL) {
2700 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2701 return (BAM_ERROR);
2703 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2704 /* skip blank lines */
2705 if (strspn(buf, " \t") == strlen(buf))
2706 continue;
2707 append_to_flist(flistp, buf);
2709 if (fclose(fp) != 0) {
2710 bam_error(CLOSE_FAIL, path, strerror(errno));
2711 return (BAM_ERROR);
2713 return (BAM_SUCCESS);
2716 static error_t
2717 read_list(char *root, filelist_t *flistp)
2719 char path[PATH_MAX];
2720 char cmd[PATH_MAX];
2721 struct stat sb;
2722 int n, rval;
2723 const char *fcn = "read_list()";
2725 flistp->head = flistp->tail = NULL;
2728 * build and check path to extract_boot_filelist.ksh
2730 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2731 if (n >= sizeof (path)) {
2732 bam_error(NO_FLIST);
2733 return (BAM_ERROR);
2736 if (is_safe_exec(path) == BAM_ERROR)
2737 return (BAM_ERROR);
2740 * If extract_boot_filelist is present, exec it, otherwise read
2741 * the filelists directly, for compatibility with older images.
2743 if (stat(path, &sb) == 0) {
2745 * build arguments to exec extract_boot_filelist.ksh
2747 char *rootarg, *platarg;
2748 int platarglen = 1, rootarglen = 1;
2749 if (strlen(root) > 1)
2750 rootarglen += strlen(root) + strlen("-R ");
2751 if (bam_alt_platform)
2752 platarglen += strlen(bam_platform) + strlen("-p ");
2753 platarg = s_calloc(1, platarglen);
2754 rootarg = s_calloc(1, rootarglen);
2755 *platarg = 0;
2756 *rootarg = 0;
2758 if (strlen(root) > 1) {
2759 (void) snprintf(rootarg, rootarglen,
2760 "-R %s", root);
2762 if (bam_alt_platform) {
2763 (void) snprintf(platarg, platarglen,
2764 "-p %s", bam_platform);
2766 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2767 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2768 free(platarg);
2769 free(rootarg);
2770 if (n >= sizeof (cmd)) {
2771 bam_error(NO_FLIST);
2772 return (BAM_ERROR);
2774 if (exec_cmd(cmd, flistp) != 0) {
2775 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2776 return (BAM_ERROR);
2778 } else {
2780 * Read current lists of files - only the first is mandatory
2782 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2783 if (rval != BAM_SUCCESS)
2784 return (rval);
2785 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2788 if (flistp->head == NULL) {
2789 bam_error(NO_FLIST);
2790 return (BAM_ERROR);
2793 return (BAM_SUCCESS);
2796 static void
2797 getoldstat(char *root)
2799 char path[PATH_MAX];
2800 int fd, error;
2801 struct stat sb;
2802 char *ostat;
2804 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2805 fd = open(path, O_RDONLY);
2806 if (fd == -1) {
2807 if (bam_verbose)
2808 bam_print(OPEN_FAIL, path, strerror(errno));
2809 goto out_err;
2812 if (fstat(fd, &sb) != 0) {
2813 bam_error(STAT_FAIL, path, strerror(errno));
2814 goto out_err;
2817 ostat = s_calloc(1, sb.st_size);
2819 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2820 bam_error(READ_FAIL, path, strerror(errno));
2821 free(ostat);
2822 goto out_err;
2825 (void) close(fd);
2826 fd = -1;
2828 walk_arg.old_nvlp = NULL;
2829 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2831 free(ostat);
2833 if (error) {
2834 bam_error(UNPACK_FAIL, path, strerror(error));
2835 walk_arg.old_nvlp = NULL;
2836 goto out_err;
2837 } else {
2838 return;
2841 out_err:
2842 if (fd != -1)
2843 (void) close(fd);
2844 if (!is_flag_on(IS_SPARC_TARGET))
2845 set_dir_flag(FILE32, NEED_UPDATE);
2846 set_dir_flag(FILE64, NEED_UPDATE);
2849 /* Best effort stale entry removal */
2850 static void
2851 delete_stale(char *file, int what)
2853 char path[PATH_MAX];
2854 struct stat sb;
2856 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2857 if (!bam_check && stat(path, &sb) == 0) {
2858 if (sb.st_mode & S_IFDIR)
2859 (void) rmdir_r(path);
2860 else
2861 (void) unlink(path);
2863 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2868 * Checks if a file in the current (old) archive has
2869 * been deleted from the root filesystem. This is needed for
2870 * software like Trusted Extensions (TX) that switch early
2871 * in boot based on presence/absence of a kernel module.
2873 static void
2874 check4stale(char *root)
2876 nvpair_t *nvp;
2877 nvlist_t *nvlp;
2878 char *file;
2879 char path[PATH_MAX];
2882 * Skip stale file check during smf check
2884 if (bam_smf_check)
2885 return;
2888 * If we need to (re)create the cache, there's no need to check for
2889 * stale files
2891 if (is_flag_on(NEED_CACHE_DIR))
2892 return;
2894 /* Nothing to do if no old stats */
2895 if ((nvlp = walk_arg.old_nvlp) == NULL)
2896 return;
2898 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2899 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2900 file = nvpair_name(nvp);
2901 if (file == NULL)
2902 continue;
2903 (void) snprintf(path, sizeof (path), "%s/%s",
2904 root, file);
2905 if (access(path, F_OK) < 0) {
2906 int what;
2908 if (bam_verbose)
2909 bam_print(PARSEABLE_STALE_FILE, path);
2911 if (is_flag_on(IS_SPARC_TARGET)) {
2912 set_dir_flag(FILE64, NEED_UPDATE);
2913 } else {
2914 for (what = FILE32; what < CACHEDIR_NUM; what++)
2915 if (has_cachedir(what))
2916 delete_stale(file, what);
2922 static void
2923 create_newstat(void)
2925 int error;
2927 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2928 if (error) {
2930 * Not fatal - we can still create archive
2932 walk_arg.new_nvlp = NULL;
2933 bam_error(NVALLOC_FAIL, strerror(error));
2937 static int
2938 walk_list(char *root, filelist_t *flistp)
2940 char path[PATH_MAX];
2941 line_t *lp;
2943 for (lp = flistp->head; lp; lp = lp->next) {
2945 * Don't follow symlinks. A symlink must refer to
2946 * a file that would appear in the archive through
2947 * a direct reference. This matches the archive
2948 * construction behavior.
2950 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
2951 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
2952 if (is_flag_on(UPDATE_ERROR))
2953 return (BAM_ERROR);
2955 * Some files may not exist.
2956 * For example: etc/rtc_config on a x86 diskless system
2957 * Emit verbose message only
2959 if (bam_verbose)
2960 bam_print(NFTW_FAIL, path, strerror(errno));
2964 return (BAM_SUCCESS);
2968 * Update the timestamp file.
2970 static void
2971 update_timestamp(char *root)
2973 char timestamp_path[PATH_MAX];
2975 /* this path length has already been checked in check_flags_and_files */
2976 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2977 FILE_STAT_TIMESTAMP);
2980 * recreate the timestamp file. Since an outdated or absent timestamp
2981 * file translates in a complete rebuild of the archive cache, notify
2982 * the user of the performance issue.
2984 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
2985 bam_error(OPEN_FAIL, timestamp_path, strerror(errno));
2986 bam_error(TIMESTAMP_FAIL);
2991 static void
2992 savenew(char *root)
2994 char path[PATH_MAX];
2995 char path2[PATH_MAX];
2996 size_t sz;
2997 char *nstat;
2998 int fd, wrote, error;
3000 nstat = NULL;
3001 sz = 0;
3002 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3003 NV_ENCODE_XDR, 0);
3004 if (error) {
3005 bam_error(PACK_FAIL, strerror(error));
3006 return;
3009 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3010 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3011 if (fd == -1) {
3012 bam_error(OPEN_FAIL, path, strerror(errno));
3013 free(nstat);
3014 return;
3016 wrote = write(fd, nstat, sz);
3017 if (wrote != sz) {
3018 bam_error(WRITE_FAIL, path, strerror(errno));
3019 (void) close(fd);
3020 free(nstat);
3021 return;
3023 (void) close(fd);
3024 free(nstat);
3026 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3027 if (rename(path, path2) != 0) {
3028 bam_error(RENAME_FAIL, path2, strerror(errno));
3032 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3034 static void
3035 clear_walk_args(void)
3037 nvlist_free(walk_arg.old_nvlp);
3038 nvlist_free(walk_arg.new_nvlp);
3039 if (walk_arg.sparcfile)
3040 (void) fclose(walk_arg.sparcfile);
3041 walk_arg.old_nvlp = NULL;
3042 walk_arg.new_nvlp = NULL;
3043 walk_arg.sparcfile = NULL;
3047 * Returns:
3048 * 0 - no update necessary
3049 * 1 - update required.
3050 * BAM_ERROR (-1) - An error occurred
3052 * Special handling for check (-n):
3053 * ================================
3054 * The check (-n) option produces parseable output.
3055 * To do this, we suppress all stdout messages unrelated
3056 * to out of sync files.
3057 * All stderr messages are still printed though.
3060 static int
3061 update_required(char *root)
3063 struct stat sb;
3064 char path[PATH_MAX];
3065 filelist_t flist;
3066 filelist_t *flistp = &flist;
3067 int ret;
3069 flistp->head = flistp->tail = NULL;
3071 if (is_sparc())
3072 set_flag(IS_SPARC_TARGET);
3075 * Check if cache directories and archives are present
3078 ret = check_flags_and_files(root);
3079 if (ret < 0)
3080 return (BAM_ERROR);
3083 * In certain deployment scenarios, filestat may not
3084 * exist. Do not stop the boot process, but trigger an update
3085 * of the archives (which will recreate filestat.ramdisk).
3087 if (bam_smf_check) {
3088 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3089 if (stat(path, &sb) != 0) {
3090 (void) creat(NEED_UPDATE_FILE, 0644);
3091 return (0);
3095 getoldstat(root);
3098 * Check if the archive contains files that are no longer
3099 * present on the root filesystem.
3101 check4stale(root);
3104 * read list of files
3106 if (read_list(root, flistp) != BAM_SUCCESS) {
3107 clear_walk_args();
3108 return (BAM_ERROR);
3111 assert(flistp->head && flistp->tail);
3114 * At this point either the update is required
3115 * or the decision is pending. In either case
3116 * we need to create new stat nvlist
3118 create_newstat();
3120 * This walk does 2 things:
3121 * - gets new stat data for every file
3122 * - (optional) compare old and new stat data
3124 ret = walk_list(root, &flist);
3126 /* done with the file list */
3127 filelist_free(flistp);
3129 /* something went wrong */
3131 if (ret == BAM_ERROR) {
3132 bam_error(CACHE_FAIL);
3133 return (BAM_ERROR);
3136 if (walk_arg.new_nvlp == NULL) {
3137 if (walk_arg.sparcfile != NULL)
3138 (void) fclose(walk_arg.sparcfile);
3139 bam_error(NO_NEW_STAT);
3142 /* If nothing was updated, discard newstat. */
3144 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
3145 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
3146 clear_walk_args();
3147 return (0);
3150 if (walk_arg.sparcfile != NULL)
3151 (void) fclose(walk_arg.sparcfile);
3153 return (1);
3156 static int
3157 flushfs(char *root)
3159 char cmd[PATH_MAX + 30];
3161 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3162 LOCKFS_PATH, root);
3164 return (exec_cmd(cmd, NULL));
3167 static int
3168 do_archive_copy(char *source, char *dest)
3171 sync();
3173 /* the equivalent of mv archive-new-$pid boot_archive */
3174 if (rename(source, dest) != 0) {
3175 (void) unlink(source);
3176 return (BAM_ERROR);
3179 if (flushfs(bam_root) != 0)
3180 sync();
3182 return (BAM_SUCCESS);
3185 static int
3186 check_cmdline(filelist_t flist)
3188 line_t *lp;
3190 for (lp = flist.head; lp; lp = lp->next) {
3191 if (strstr(lp->line, "Error:") != NULL ||
3192 strstr(lp->line, "Inode number overflow") != NULL) {
3193 (void) fprintf(stderr, "%s\n", lp->line);
3194 return (BAM_ERROR);
3198 return (BAM_SUCCESS);
3201 static void
3202 dump_errormsg(filelist_t flist)
3204 line_t *lp;
3206 for (lp = flist.head; lp; lp = lp->next)
3207 (void) fprintf(stderr, "%s\n", lp->line);
3210 static int
3211 check_archive(char *dest)
3213 struct stat sb;
3215 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3216 sb.st_size < 10000) {
3217 bam_error(ARCHIVE_BAD, dest);
3218 (void) unlink(dest);
3219 return (BAM_ERROR);
3222 return (BAM_SUCCESS);
3225 static boolean_t
3226 is_be(char *root)
3228 zfs_handle_t *zhp;
3229 libzfs_handle_t *hdl;
3230 be_node_list_t *be_nodes = NULL;
3231 be_node_list_t *cur_be;
3232 boolean_t be_exist = B_FALSE;
3233 char ds_path[ZFS_MAX_DATASET_NAME_LEN];
3235 if (!is_zfs(root))
3236 return (B_FALSE);
3238 * Get dataset for mountpoint
3240 if ((hdl = libzfs_init()) == NULL)
3241 return (B_FALSE);
3243 if ((zhp = zfs_path_to_zhandle(hdl, root,
3244 ZFS_TYPE_FILESYSTEM)) == NULL) {
3245 libzfs_fini(hdl);
3246 return (B_FALSE);
3249 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3252 * Check if the current dataset is BE
3254 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
3255 for (cur_be = be_nodes; cur_be != NULL;
3256 cur_be = cur_be->be_next_node) {
3259 * Because we guarantee that cur_be->be_root_ds
3260 * is null-terminated by internal data structure,
3261 * we can safely use strcmp()
3263 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3264 be_exist = B_TRUE;
3265 break;
3268 be_free_list(be_nodes);
3270 zfs_close(zhp);
3271 libzfs_fini(hdl);
3273 return (be_exist);
3277 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
3279 static int
3280 is_mkisofs()
3282 if (access(MKISOFS_PATH, X_OK) == 0)
3283 return (1);
3284 return (0);
3287 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3289 static int
3290 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3292 int ret;
3293 char cmdline[3 * PATH_MAX + 64];
3294 filelist_t flist = {0};
3295 const char *func = "create_sparc_archive()";
3297 if (access(bootblk, R_OK) == 1) {
3298 bam_error(BOOTBLK_FAIL, bootblk);
3299 return (BAM_ERROR);
3303 * Prepare mkisofs command line and execute it
3305 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3306 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3307 tempname, list);
3309 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3311 ret = exec_cmd(cmdline, &flist);
3312 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3313 dump_errormsg(flist);
3314 goto out_err;
3317 filelist_free(&flist);
3320 * Prepare dd command line to copy the bootblk on the new archive and
3321 * execute it
3323 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3324 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3325 bootblk, tempname);
3327 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3329 ret = exec_cmd(cmdline, &flist);
3330 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3331 goto out_err;
3333 filelist_free(&flist);
3335 /* Did we get a valid archive ? */
3336 if (check_archive(tempname) == BAM_ERROR)
3337 return (BAM_ERROR);
3339 return (do_archive_copy(tempname, archive));
3341 out_err:
3342 filelist_free(&flist);
3343 bam_error(ARCHIVE_FAIL, cmdline);
3344 (void) unlink(tempname);
3345 return (BAM_ERROR);
3348 static unsigned int
3349 from_733(unsigned char *s)
3351 int i;
3352 unsigned int ret = 0;
3354 for (i = 0; i < 4; i++)
3355 ret |= s[i] << (8 * i);
3357 return (ret);
3360 static void
3361 to_733(unsigned char *s, unsigned int val)
3363 int i;
3365 for (i = 0; i < 4; i++)
3366 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3370 * creates sha1 hash of archive
3372 static int
3373 digest_archive(const char *archive)
3375 char *archive_hash;
3376 char *hash;
3377 int ret;
3378 FILE *fp;
3380 (void) asprintf(&archive_hash, "%s.hash", archive);
3381 if (archive_hash == NULL)
3382 return (BAM_ERROR);
3384 if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
3385 free(archive_hash);
3386 return (ret);
3389 fp = fopen(archive_hash, "w");
3390 if (fp == NULL) {
3391 free(archive_hash);
3392 free(hash);
3393 return (BAM_ERROR);
3396 (void) fprintf(fp, "%s\n", hash);
3397 (void) fclose(fp);
3398 free(hash);
3399 free(archive_hash);
3400 return (BAM_SUCCESS);
3404 * Extends the current boot archive without recreating it from scratch
3406 static int
3407 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3409 int fd = -1, newfd = -1, ret, i;
3410 int next_session = 0, new_size = 0;
3411 char cmdline[3 * PATH_MAX + 64];
3412 const char *func = "extend_iso_archive()";
3413 filelist_t flist = {0};
3414 struct iso_pdesc saved_desc[MAX_IVDs];
3416 fd = open(archive, O_RDWR);
3417 if (fd == -1) {
3418 if (bam_verbose)
3419 bam_error(OPEN_FAIL, archive, strerror(errno));
3420 goto out_err;
3424 * A partial read is likely due to a corrupted file
3426 ret = pread64(fd, saved_desc, sizeof (saved_desc),
3427 VOLDESC_OFF * CD_BLOCK);
3428 if (ret != sizeof (saved_desc)) {
3429 if (bam_verbose)
3430 bam_error(READ_FAIL, archive, strerror(errno));
3431 goto out_err;
3434 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3435 if (bam_verbose)
3436 bam_error(SIGN_FAIL, archive);
3437 goto out_err;
3441 * Read primary descriptor and locate next_session offset (it should
3442 * point to the end of the archive)
3444 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3446 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3447 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3448 MKISO_PARAMS, tempname, update_dir);
3450 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3452 ret = exec_cmd(cmdline, &flist);
3453 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3454 if (bam_verbose) {
3455 bam_error(MULTI_FAIL, cmdline);
3456 dump_errormsg(flist);
3458 goto out_flist_err;
3460 filelist_free(&flist);
3462 newfd = open(tempname, O_RDONLY);
3463 if (newfd == -1) {
3464 if (bam_verbose)
3465 bam_error(OPEN_FAIL, archive, strerror(errno));
3466 goto out_err;
3469 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3470 VOLDESC_OFF * CD_BLOCK);
3471 if (ret != sizeof (saved_desc)) {
3472 if (bam_verbose)
3473 bam_error(READ_FAIL, archive, strerror(errno));
3474 goto out_err;
3477 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3478 if (bam_verbose)
3479 bam_error(SIGN_FAIL, archive);
3480 goto out_err;
3483 new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3484 to_733(saved_desc[0].volume_space_size, new_size);
3486 for (i = 1; i < MAX_IVDs; i++) {
3487 if (saved_desc[i].type[0] == (unsigned char)255)
3488 break;
3489 if (memcmp(saved_desc[i].id, "CD001", 5))
3490 break;
3492 if (bam_verbose)
3493 bam_print("%s: Updating descriptor entry [%d]\n", func,
3496 to_733(saved_desc[i].volume_space_size, new_size);
3499 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3500 if (ret != DVD_BLOCK) {
3501 if (bam_verbose)
3502 bam_error(WRITE_FAIL, archive, strerror(errno));
3503 goto out_err;
3505 (void) close(newfd);
3506 newfd = -1;
3508 ret = fsync(fd);
3509 if (ret != 0)
3510 sync();
3512 ret = close(fd);
3513 if (ret != 0) {
3514 if (bam_verbose)
3515 bam_error(CLOSE_FAIL, archive, strerror(errno));
3516 return (BAM_ERROR);
3518 fd = -1;
3520 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3521 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3522 (next_session/16));
3524 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3526 ret = exec_cmd(cmdline, &flist);
3527 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3528 if (bam_verbose)
3529 bam_error(MULTI_FAIL, cmdline);
3530 goto out_flist_err;
3532 filelist_free(&flist);
3534 (void) unlink(tempname);
3536 if (digest_archive(archive) == BAM_ERROR && bam_verbose)
3537 bam_print("boot archive hashing failed\n");
3539 if (flushfs(bam_root) != 0)
3540 sync();
3542 if (bam_verbose)
3543 bam_print("boot archive updated successfully\n");
3545 return (BAM_SUCCESS);
3547 out_flist_err:
3548 filelist_free(&flist);
3549 out_err:
3550 if (fd != -1)
3551 (void) close(fd);
3552 if (newfd != -1)
3553 (void) close(newfd);
3554 return (BAM_ERROR);
3557 static int
3558 create_x86_archive(char *archive, char *tempname, char *update_dir)
3560 int ret;
3561 char cmdline[3 * PATH_MAX + 64];
3562 filelist_t flist = {0};
3563 const char *func = "create_x86_archive()";
3565 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3566 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3568 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3570 ret = exec_cmd(cmdline, &flist);
3571 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3572 bam_error(ARCHIVE_FAIL, cmdline);
3573 dump_errormsg(flist);
3574 filelist_free(&flist);
3575 (void) unlink(tempname);
3576 return (BAM_ERROR);
3579 filelist_free(&flist);
3581 if (check_archive(tempname) == BAM_ERROR)
3582 return (BAM_ERROR);
3584 return (do_archive_copy(tempname, archive));
3587 static int
3588 mkisofs_archive(char *root, int what)
3590 int ret;
3591 char temp[PATH_MAX];
3592 char bootblk[PATH_MAX];
3593 char boot_archive[PATH_MAX];
3595 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3596 ret = snprintf(temp, sizeof (temp),
3597 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3598 get_machine(), getpid());
3599 else
3600 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3601 root, ARCHIVE_PREFIX, get_machine(), getpid());
3603 if (ret >= sizeof (temp))
3604 goto out_path_err;
3606 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3607 ret = snprintf(boot_archive, sizeof (boot_archive),
3608 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3609 ARCHIVE_SUFFIX);
3610 else
3611 ret = snprintf(boot_archive, sizeof (boot_archive),
3612 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3613 ARCHIVE_SUFFIX);
3615 if (ret >= sizeof (boot_archive))
3616 goto out_path_err;
3618 bam_print("updating %s\n", boot_archive);
3620 if (is_flag_on(IS_SPARC_TARGET)) {
3621 ret = snprintf(bootblk, sizeof (bootblk),
3622 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3623 if (ret >= sizeof (bootblk))
3624 goto out_path_err;
3626 ret = create_sparc_archive(boot_archive, temp, bootblk,
3627 get_cachedir(what));
3628 } else {
3629 if (!is_dir_flag_on(what, NO_MULTI)) {
3630 if (bam_verbose)
3631 bam_print("Attempting to extend x86 archive: "
3632 "%s\n", boot_archive);
3634 ret = extend_iso_archive(boot_archive, temp,
3635 get_updatedir(what));
3636 if (ret == BAM_SUCCESS) {
3637 if (bam_verbose)
3638 bam_print("Successfully extended %s\n",
3639 boot_archive);
3641 (void) rmdir_r(get_updatedir(what));
3642 return (BAM_SUCCESS);
3646 * The boot archive will be recreated from scratch. We get here
3647 * if at least one of these conditions is true:
3648 * - bootadm was called without the -e switch
3649 * - the archive (or the archive cache) doesn't exist
3650 * - archive size is bigger than BA_SIZE_MAX
3651 * - more than COUNT_MAX files need to be updated
3652 * - an error occourred either populating the /updates directory
3653 * or extend_iso_archive() failed
3655 if (bam_verbose)
3656 bam_print("Unable to extend %s... rebuilding archive\n",
3657 boot_archive);
3659 if (get_updatedir(what)[0] != '\0')
3660 (void) rmdir_r(get_updatedir(what));
3663 ret = create_x86_archive(boot_archive, temp,
3664 get_cachedir(what));
3667 if (digest_archive(boot_archive) == BAM_ERROR && bam_verbose)
3668 bam_print("boot archive hashing failed\n");
3670 if (ret == BAM_SUCCESS && bam_verbose)
3671 bam_print("Successfully created %s\n", boot_archive);
3673 return (ret);
3675 out_path_err:
3676 bam_error(PATH_TOO_LONG, root);
3677 return (BAM_ERROR);
3680 static error_t
3681 create_ramdisk(char *root)
3683 char *cmdline, path[PATH_MAX];
3684 size_t len;
3685 struct stat sb;
3686 int ret, what, status = BAM_SUCCESS;
3688 /* If there is mkisofs, use it to create the required archives */
3689 if (is_mkisofs()) {
3690 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3691 if (has_cachedir(what) && is_dir_flag_on(what,
3692 NEED_UPDATE)) {
3693 ret = mkisofs_archive(root, what);
3694 if (ret != 0)
3695 status = BAM_ERROR;
3698 return (status);
3702 * Else setup command args for create_ramdisk.ksh for the UFS archives
3703 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3705 if (bam_verbose)
3706 bam_print("mkisofs not found, creating UFS archive\n");
3708 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3709 if (stat(path, &sb) != 0) {
3710 bam_error(ARCH_EXEC_MISS, path, strerror(errno));
3711 return (BAM_ERROR);
3714 if (is_safe_exec(path) == BAM_ERROR)
3715 return (BAM_ERROR);
3717 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3718 if (bam_alt_platform)
3719 len += strlen(bam_platform) + strlen("-p ");
3720 cmdline = s_calloc(1, len);
3722 if (bam_alt_platform) {
3723 assert(strlen(root) > 1);
3724 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3725 path, bam_platform, root);
3726 /* chop off / at the end */
3727 cmdline[strlen(cmdline) - 1] = '\0';
3728 } else if (strlen(root) > 1) {
3729 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3730 /* chop off / at the end */
3731 cmdline[strlen(cmdline) - 1] = '\0';
3732 } else
3733 (void) snprintf(cmdline, len, "%s", path);
3735 if (exec_cmd(cmdline, NULL) != 0) {
3736 bam_error(ARCHIVE_FAIL, cmdline);
3737 free(cmdline);
3738 return (BAM_ERROR);
3740 free(cmdline);
3742 * The existence of the expected archives used to be
3743 * verified here. This check is done in create_ramdisk as
3744 * it needs to be in sync with the altroot operated upon.
3746 return (BAM_SUCCESS);
3750 * Checks if target filesystem is on a ramdisk
3751 * 1 - is miniroot
3752 * 0 - is not
3753 * When in doubt assume it is not a ramdisk.
3755 static int
3756 is_ramdisk(char *root)
3758 struct extmnttab mnt;
3759 FILE *fp;
3760 int found;
3761 char mntpt[PATH_MAX];
3762 char *cp;
3765 * There are 3 situations where creating archive is
3766 * of dubious value:
3767 * - create boot_archive on a lofi-mounted boot_archive
3768 * - create it on a ramdisk which is the root filesystem
3769 * - create it on a ramdisk mounted somewhere else
3770 * The first is not easy to detect and checking for it is not
3771 * worth it.
3772 * The other two conditions are handled here
3774 fp = fopen(MNTTAB, "r");
3775 if (fp == NULL) {
3776 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3777 return (0);
3780 resetmnttab(fp);
3783 * Remove any trailing / from the mount point
3785 (void) strlcpy(mntpt, root, sizeof (mntpt));
3786 if (strcmp(root, "/") != 0) {
3787 cp = mntpt + strlen(mntpt) - 1;
3788 if (*cp == '/')
3789 *cp = '\0';
3791 found = 0;
3792 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3793 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3794 found = 1;
3795 break;
3799 if (!found) {
3800 if (bam_verbose)
3801 bam_error(NOT_IN_MNTTAB, mntpt);
3802 (void) fclose(fp);
3803 return (0);
3806 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3807 strlen(RAMDISK_SPECIAL)) == 0) {
3808 if (bam_verbose)
3809 bam_error(IS_RAMDISK, bam_root);
3810 (void) fclose(fp);
3811 return (1);
3814 (void) fclose(fp);
3816 return (0);
3819 static int
3820 is_boot_archive(char *root)
3822 char path[PATH_MAX];
3823 struct stat sb;
3824 int error;
3825 const char *fcn = "is_boot_archive()";
3828 * We can't create an archive without the create_ramdisk script
3830 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3831 error = stat(path, &sb);
3832 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3833 if (error == -1) {
3834 if (bam_verbose)
3835 bam_print(FILE_MISS, path);
3836 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
3837 return (0);
3840 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
3841 return (1);
3845 * Need to call this for anything that operates on the GRUB menu
3846 * In the x86 live upgrade case the directory /boot/grub may be present
3847 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3848 * is to check for the presence of the stage2 binary which is present
3849 * only on GRUB targets (even on x86 boot partitions). Checking for the
3850 * presence of the multiboot binary is not correct as it is not present
3851 * on x86 boot partitions.
3854 is_grub(const char *root)
3856 char path[PATH_MAX];
3857 struct stat sb;
3858 const char *fcn = "is_grub()";
3860 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3861 if (stat(path, &sb) == -1) {
3862 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
3863 return (0);
3866 return (1);
3869 static int
3870 is_zfs(char *root)
3872 struct statvfs vfs;
3873 int ret;
3874 const char *fcn = "is_zfs()";
3876 ret = statvfs(root, &vfs);
3877 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3878 if (ret != 0) {
3879 bam_error(STATVFS_FAIL, root, strerror(errno));
3880 return (0);
3883 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3884 BAM_DPRINTF((D_IS_ZFS, fcn, root));
3885 return (1);
3886 } else {
3887 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
3888 return (0);
3892 static int
3893 is_ufs(char *root)
3895 struct statvfs vfs;
3896 int ret;
3897 const char *fcn = "is_ufs()";
3899 ret = statvfs(root, &vfs);
3900 INJECT_ERROR1("STATVFS_UFS", ret = 1);
3901 if (ret != 0) {
3902 bam_error(STATVFS_FAIL, root, strerror(errno));
3903 return (0);
3906 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
3907 BAM_DPRINTF((D_IS_UFS, fcn, root));
3908 return (1);
3909 } else {
3910 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
3911 return (0);
3915 static int
3916 is_pcfs(char *root)
3918 struct statvfs vfs;
3919 int ret;
3920 const char *fcn = "is_pcfs()";
3922 ret = statvfs(root, &vfs);
3923 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
3924 if (ret != 0) {
3925 bam_error(STATVFS_FAIL, root, strerror(errno));
3926 return (0);
3929 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
3930 BAM_DPRINTF((D_IS_PCFS, fcn, root));
3931 return (1);
3932 } else {
3933 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
3934 return (0);
3938 static int
3939 is_readonly(char *root)
3941 int fd;
3942 int error;
3943 char testfile[PATH_MAX];
3944 const char *fcn = "is_readonly()";
3947 * Using statvfs() to check for a read-only filesystem is not
3948 * reliable. The only way to reliably test is to attempt to
3949 * create a file
3951 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
3952 root, BOOTADM_RDONLY_TEST, getpid());
3954 (void) unlink(testfile);
3956 errno = 0;
3957 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
3958 error = errno;
3959 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
3960 if (fd == -1 && error == EROFS) {
3961 BAM_DPRINTF((D_RDONLY_FS, fcn, root));
3962 return (1);
3963 } else if (fd == -1) {
3964 bam_error(RDONLY_TEST_ERROR, root, strerror(error));
3967 (void) close(fd);
3968 (void) unlink(testfile);
3970 BAM_DPRINTF((D_RDWR_FS, fcn, root));
3971 return (0);
3974 static error_t
3975 update_archive(char *root, char *opt)
3977 error_t ret;
3979 assert(root);
3980 assert(opt == NULL);
3982 init_walk_args();
3983 (void) umask(022);
3986 * Never update non-BE root in update_all
3988 if (!is_be(root) && bam_update_all)
3989 return (BAM_SUCCESS);
3991 * root must belong to a boot archive based OS,
3993 if (!is_boot_archive(root)) {
3995 * Emit message only if not in context of update_all.
3996 * If in update_all, emit only if verbose flag is set.
3998 if (!bam_update_all || bam_verbose)
3999 bam_print(NOT_ARCHIVE_BOOT, root);
4000 return (BAM_ERROR);
4004 * If smf check is requested when / is writable (can happen
4005 * on first reboot following an upgrade because service
4006 * dependency is messed up), skip the check.
4008 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
4009 return (BAM_SUCCESS);
4012 * Don't generate archive on ramdisk.
4014 if (is_ramdisk(root))
4015 return (BAM_SUCCESS);
4018 * root must be writable. This check applies to alternate
4019 * root (-R option); bam_root_readonly applies to '/' only.
4020 * The behaviour translates into being the one of a 'check'.
4022 if (!bam_smf_check && !bam_check && is_readonly(root)) {
4023 set_flag(RDONLY_FSCHK);
4024 bam_check = 1;
4028 * Now check if an update is really needed.
4030 ret = update_required(root);
4033 * The check command (-n) is *not* a dry run.
4034 * It only checks if the archive is in sync.
4035 * A readonly filesystem has to be considered an error only if an update
4036 * is required.
4038 if (bam_nowrite()) {
4039 if (is_flag_on(RDONLY_FSCHK)) {
4040 bam_check = bam_saved_check;
4041 if (ret > 0)
4042 bam_error(RDONLY_FS, root);
4043 if (bam_update_all)
4044 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4047 bam_exit((ret != 0) ? 1 : 0);
4050 if (ret == 1) {
4051 /* create the ramdisk */
4052 ret = create_ramdisk(root);
4056 * if the archive is updated, save the new stat data and update the
4057 * timestamp file
4059 if (ret == 0 && walk_arg.new_nvlp != NULL) {
4060 savenew(root);
4061 update_timestamp(root);
4064 clear_walk_args();
4066 return (ret);
4069 static char *
4070 find_root_pool()
4072 char *special = get_special("/");
4073 char *p;
4075 if (special == NULL)
4076 return (NULL);
4078 if (*special == '/') {
4079 free(special);
4080 return (NULL);
4083 if ((p = strchr(special, '/')) != NULL)
4084 *p = '\0';
4086 return (special);
4089 static error_t
4090 synchronize_BE_menu(void)
4092 struct stat sb;
4093 char cmdline[PATH_MAX];
4094 char cksum_line[PATH_MAX];
4095 filelist_t flist = {0};
4096 char *old_cksum_str;
4097 char *old_size_str;
4098 char *old_file;
4099 char *curr_cksum_str;
4100 char *curr_size_str;
4101 char *curr_file;
4102 char *pool = NULL;
4103 char *mntpt = NULL;
4104 zfs_mnted_t mnted;
4105 FILE *cfp;
4106 int found;
4107 int ret;
4108 const char *fcn = "synchronize_BE_menu()";
4110 BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
4112 /* Check if findroot enabled LU BE */
4113 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4114 BAM_DPRINTF((D_NOT_LU_BE, fcn));
4115 return (BAM_SUCCESS);
4118 if (stat(LU_MENU_CKSUM, &sb) != 0) {
4119 BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4120 goto menu_sync;
4123 cfp = fopen(LU_MENU_CKSUM, "r");
4124 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4125 if (cfp == NULL) {
4126 bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
4127 goto menu_sync;
4129 BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
4131 found = 0;
4132 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4133 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4134 if (found) {
4135 bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
4136 (void) fclose(cfp);
4137 goto menu_sync;
4139 found = 1;
4141 BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
4144 old_cksum_str = strtok(cksum_line, " \t");
4145 old_size_str = strtok(NULL, " \t");
4146 old_file = strtok(NULL, " \t");
4148 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4149 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4150 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4151 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4152 bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
4153 goto menu_sync;
4155 BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
4157 /* Get checksum of current menu */
4158 pool = find_root_pool();
4159 if (pool) {
4160 mntpt = mount_top_dataset(pool, &mnted);
4161 if (mntpt == NULL) {
4162 bam_error(FAIL_MNT_TOP_DATASET, pool);
4163 free(pool);
4164 return (BAM_ERROR);
4166 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4167 CKSUM, mntpt, GRUB_MENU);
4168 } else {
4169 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4170 CKSUM, GRUB_MENU);
4172 ret = exec_cmd(cmdline, &flist);
4173 if (pool) {
4174 (void) umount_top_dataset(pool, mnted, mntpt);
4175 free(pool);
4177 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4178 if (ret != 0) {
4179 bam_error(MENU_CKSUM_FAIL);
4180 return (BAM_ERROR);
4182 BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
4184 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4185 if ((flist.head == NULL) || (flist.head != flist.tail)) {
4186 bam_error(BAD_CKSUM);
4187 filelist_free(&flist);
4188 return (BAM_ERROR);
4190 BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
4192 curr_cksum_str = strtok(flist.head->line, " \t");
4193 curr_size_str = strtok(NULL, " \t");
4194 curr_file = strtok(NULL, " \t");
4196 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4197 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4198 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4199 if (curr_cksum_str == NULL || curr_size_str == NULL ||
4200 curr_file == NULL) {
4201 bam_error(BAD_CKSUM_PARSE);
4202 filelist_free(&flist);
4203 return (BAM_ERROR);
4205 BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
4207 if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4208 strcmp(old_size_str, curr_size_str) == 0 &&
4209 strcmp(old_file, curr_file) == 0) {
4210 filelist_free(&flist);
4211 BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
4212 return (BAM_SUCCESS);
4215 filelist_free(&flist);
4217 /* cksum doesn't match - the menu has changed */
4218 BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
4220 menu_sync:
4221 bam_print(PROP_GRUB_MENU);
4223 (void) snprintf(cmdline, sizeof (cmdline),
4224 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4225 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4226 ret = exec_cmd(cmdline, NULL);
4227 INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4228 if (ret != 0) {
4229 bam_error(MENU_PROP_FAIL);
4230 return (BAM_ERROR);
4232 BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
4234 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4235 GRUB_MENU, GRUB_BACKUP_MENU);
4236 ret = exec_cmd(cmdline, NULL);
4237 INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4238 if (ret != 0) {
4239 bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
4240 return (BAM_ERROR);
4242 BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
4244 (void) snprintf(cmdline, sizeof (cmdline),
4245 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4246 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4247 ret = exec_cmd(cmdline, NULL);
4248 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4249 if (ret != 0) {
4250 bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
4251 return (BAM_ERROR);
4253 BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
4255 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4256 CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4257 ret = exec_cmd(cmdline, NULL);
4258 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4259 if (ret != 0) {
4260 bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
4261 return (BAM_ERROR);
4263 BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4265 (void) snprintf(cmdline, sizeof (cmdline),
4266 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4267 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4268 ret = exec_cmd(cmdline, NULL);
4269 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4270 if (ret != 0) {
4271 bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
4272 return (BAM_ERROR);
4274 BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4276 return (BAM_SUCCESS);
4279 static error_t
4280 update_all(char *root, char *opt)
4282 struct extmnttab mnt;
4283 struct stat sb;
4284 FILE *fp;
4285 char multibt[PATH_MAX];
4286 char creatram[PATH_MAX];
4287 error_t ret = BAM_SUCCESS;
4289 assert(root);
4290 assert(opt == NULL);
4292 if (bam_rootlen != 1 || *root != '/') {
4293 elide_trailing_slash(root, multibt, sizeof (multibt));
4294 bam_error(ALT_ROOT_INVALID, multibt);
4295 return (BAM_ERROR);
4299 * First update archive for current root
4301 if (update_archive(root, opt) != BAM_SUCCESS)
4302 ret = BAM_ERROR;
4304 if (ret == BAM_ERROR)
4305 goto out;
4308 * Now walk the mount table, performing archive update
4309 * for all mounted Newboot root filesystems
4311 fp = fopen(MNTTAB, "r");
4312 if (fp == NULL) {
4313 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
4314 ret = BAM_ERROR;
4315 goto out;
4318 resetmnttab(fp);
4320 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4321 if (mnt.mnt_special == NULL)
4322 continue;
4323 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4324 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4325 continue;
4326 if (strcmp(mnt.mnt_mountp, "/") == 0)
4327 continue;
4329 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4330 mnt.mnt_mountp, CREATE_RAMDISK);
4332 if (stat(creatram, &sb) == -1)
4333 continue;
4336 * We put a trailing slash to be consistent with root = "/"
4337 * case, such that we don't have to print // in some cases.
4339 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4340 mnt.mnt_mountp);
4341 bam_rootlen = strlen(rootbuf);
4344 * It's possible that other mounts may be an alternate boot
4345 * architecture, so check it again.
4347 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4348 (update_archive(rootbuf, opt) != BAM_SUCCESS))
4349 ret = BAM_ERROR;
4352 (void) fclose(fp);
4354 out:
4356 * We no longer use biosdev for Live Upgrade. Hence
4357 * there is no need to defer (to shutdown time) any fdisk
4358 * updates
4360 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4361 bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
4365 * If user has updated menu in current BE, propagate the
4366 * updates to all BEs.
4368 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4369 ret = BAM_ERROR;
4371 return (ret);
4374 static void
4375 append_line(menu_t *mp, line_t *lp)
4377 if (mp->start == NULL) {
4378 mp->start = lp;
4379 } else {
4380 mp->end->next = lp;
4381 lp->prev = mp->end;
4383 mp->end = lp;
4386 void
4387 unlink_line(menu_t *mp, line_t *lp)
4389 /* unlink from list */
4390 if (lp->prev)
4391 lp->prev->next = lp->next;
4392 else
4393 mp->start = lp->next;
4394 if (lp->next)
4395 lp->next->prev = lp->prev;
4396 else
4397 mp->end = lp->prev;
4400 static entry_t *
4401 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4403 entry_t *ent, *prev;
4404 const char *fcn = "boot_entry_new()";
4406 assert(mp);
4407 assert(start);
4408 assert(end);
4410 ent = s_calloc(1, sizeof (entry_t));
4411 BAM_DPRINTF((D_ENTRY_NEW, fcn));
4412 ent->start = start;
4413 ent->end = end;
4415 if (mp->entries == NULL) {
4416 mp->entries = ent;
4417 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
4418 return (ent);
4421 prev = mp->entries;
4422 while (prev->next)
4423 prev = prev->next;
4424 prev->next = ent;
4425 ent->prev = prev;
4426 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
4427 return (ent);
4430 static void
4431 boot_entry_addline(entry_t *ent, line_t *lp)
4433 if (ent)
4434 ent->end = lp;
4438 * Check whether cmd matches the one indexed by which, and whether arg matches
4439 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4440 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4441 * strstr(), so it can be a partial match.
4443 static int
4444 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4446 int ret;
4447 const char *fcn = "check_cmd()";
4449 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
4451 if (cmd != NULL) {
4452 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4453 (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4454 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
4455 fcn, cmd, menu_cmds[which]));
4456 return (0);
4458 ret = (strstr(arg, str) != NULL);
4459 } else
4460 ret = 0;
4462 if (ret) {
4463 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
4464 } else {
4465 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
4468 return (ret);
4471 static error_t
4472 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4474 const char *fcn = "kernel_parser()";
4476 assert(entry);
4477 assert(cmd);
4478 assert(arg);
4480 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4481 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4482 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
4483 return (BAM_ERROR);
4486 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4487 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
4488 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4489 } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4490 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4491 BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
4492 entry->flags |= BAM_ENTRY_DBOOT;
4493 } else if (strncmp(arg, DIRECT_BOOT_64,
4494 sizeof (DIRECT_BOOT_64) - 1) == 0) {
4495 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
4496 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4497 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4498 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4499 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
4500 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4501 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4502 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4503 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg));
4504 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4505 | BAM_ENTRY_32BIT;
4506 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4507 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4508 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg));
4509 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4510 | BAM_ENTRY_64BIT;
4511 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4512 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
4513 entry->flags |= BAM_ENTRY_MULTIBOOT;
4514 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4515 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4516 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
4517 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4518 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4519 BAM_DPRINTF((D_SET_HV, fcn, arg));
4520 entry->flags |= BAM_ENTRY_HV;
4521 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4522 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
4523 return (BAM_ERROR);
4524 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4525 strstr(arg, UNIX_SPACE)) {
4526 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4527 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4528 strstr(arg, AMD_UNIX_SPACE)) {
4529 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4530 } else {
4531 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
4532 bam_error(UNKNOWN_KERNEL_LINE, linenum);
4533 return (BAM_ERROR);
4536 return (BAM_SUCCESS);
4539 static error_t
4540 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4542 const char *fcn = "module_parser()";
4544 assert(entry);
4545 assert(cmd);
4546 assert(arg);
4548 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4549 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4550 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
4551 return (BAM_ERROR);
4554 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4555 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4556 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4557 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4558 strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4559 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4560 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4561 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4562 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4563 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
4564 return (BAM_SUCCESS);
4565 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4566 !(entry->flags & BAM_ENTRY_LU)) {
4567 /* don't emit warning for hand entries */
4568 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
4569 return (BAM_ERROR);
4570 } else {
4571 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
4572 bam_error(UNKNOWN_MODULE_LINE, linenum);
4573 return (BAM_ERROR);
4578 * A line in menu.lst looks like
4579 * [ ]*<cmd>[ \t=]*<arg>*
4581 static void
4582 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4585 * save state across calls. This is so that
4586 * header gets the right entry# after title has
4587 * been processed
4589 static line_t *prev = NULL;
4590 static entry_t *curr_ent = NULL;
4591 static int in_liveupgrade = 0;
4592 static int is_libbe_ent = 0;
4594 line_t *lp;
4595 char *cmd, *sep, *arg;
4596 char save, *cp, *line;
4597 menu_flag_t flag = BAM_INVALID;
4598 const char *fcn = "line_parser()";
4600 if (str == NULL) {
4601 return;
4605 * First save a copy of the entire line.
4606 * We use this later to set the line field.
4608 line = s_strdup(str);
4610 /* Eat up leading whitespace */
4611 while (*str == ' ' || *str == '\t')
4612 str++;
4614 if (*str == '#') { /* comment */
4615 cmd = s_strdup("#");
4616 sep = NULL;
4617 arg = s_strdup(str + 1);
4618 flag = BAM_COMMENT;
4619 if (strstr(arg, BAM_LU_HDR) != NULL) {
4620 in_liveupgrade = 1;
4621 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4622 in_liveupgrade = 0;
4623 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4624 is_libbe_ent = 1;
4626 } else if (*str == '\0') { /* blank line */
4627 cmd = sep = arg = NULL;
4628 flag = BAM_EMPTY;
4629 } else {
4631 * '=' is not a documented separator in grub syntax.
4632 * However various development bits use '=' as a
4633 * separator. In addition, external users also
4634 * use = as a separator. So we will allow that usage.
4636 cp = str;
4637 while (*str != ' ' && *str != '\t' && *str != '=') {
4638 if (*str == '\0') {
4639 cmd = s_strdup(cp);
4640 sep = arg = NULL;
4641 break;
4643 str++;
4646 if (*str != '\0') {
4647 save = *str;
4648 *str = '\0';
4649 cmd = s_strdup(cp);
4650 *str = save;
4652 str++;
4653 save = *str;
4654 *str = '\0';
4655 sep = s_strdup(str - 1);
4656 *str = save;
4658 while (*str == ' ' || *str == '\t')
4659 str++;
4660 if (*str == '\0')
4661 arg = NULL;
4662 else
4663 arg = s_strdup(str);
4667 lp = s_calloc(1, sizeof (line_t));
4669 lp->cmd = cmd;
4670 lp->sep = sep;
4671 lp->arg = arg;
4672 lp->line = line;
4673 lp->lineNum = ++(*lineNum);
4674 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4675 lp->entryNum = ++(*entryNum);
4676 lp->flags = BAM_TITLE;
4677 if (prev && prev->flags == BAM_COMMENT &&
4678 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4679 prev->entryNum = lp->entryNum;
4680 curr_ent = boot_entry_new(mp, prev, lp);
4681 curr_ent->flags |= BAM_ENTRY_BOOTADM;
4682 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
4683 } else {
4684 curr_ent = boot_entry_new(mp, lp, lp);
4685 if (in_liveupgrade) {
4686 curr_ent->flags |= BAM_ENTRY_LU;
4687 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
4690 curr_ent->entryNum = *entryNum;
4691 } else if (flag != BAM_INVALID) {
4693 * For header comments, the entry# is "fixed up"
4694 * by the subsequent title
4696 lp->entryNum = *entryNum;
4697 lp->flags = flag;
4698 } else {
4699 lp->entryNum = *entryNum;
4701 if (*entryNum == ENTRY_INIT) {
4702 lp->flags = BAM_GLOBAL;
4703 } else {
4704 lp->flags = BAM_ENTRY;
4706 if (cmd && arg) {
4707 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4708 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
4709 curr_ent->flags |= BAM_ENTRY_ROOT;
4710 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4711 == 0) {
4712 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
4713 arg));
4714 curr_ent->flags |= BAM_ENTRY_FINDROOT;
4715 } else if (strcmp(cmd,
4716 menu_cmds[CHAINLOADER_CMD]) == 0) {
4717 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
4718 arg));
4719 curr_ent->flags |=
4720 BAM_ENTRY_CHAINLOADER;
4721 } else if (kernel_parser(curr_ent, cmd, arg,
4722 lp->lineNum) != BAM_SUCCESS) {
4723 (void) module_parser(curr_ent, cmd,
4724 arg, lp->lineNum);
4730 /* record default, old default, and entry line ranges */
4731 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4732 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4733 mp->curdefault = lp;
4734 } else if (lp->flags == BAM_COMMENT &&
4735 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4736 mp->olddefault = lp;
4737 } else if (lp->flags == BAM_COMMENT &&
4738 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4739 mp->old_rc_default = lp;
4740 } else if (lp->flags == BAM_ENTRY ||
4741 (lp->flags == BAM_COMMENT &&
4742 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4743 if (is_libbe_ent) {
4744 curr_ent->flags |= BAM_ENTRY_LIBBE;
4745 is_libbe_ent = 0;
4748 boot_entry_addline(curr_ent, lp);
4750 append_line(mp, lp);
4752 prev = lp;
4755 void
4756 update_numbering(menu_t *mp)
4758 int lineNum;
4759 int entryNum;
4760 int old_default_value;
4761 line_t *lp, *prev, *default_lp, *default_entry;
4762 char buf[PATH_MAX];
4764 if (mp->start == NULL) {
4765 return;
4768 lineNum = LINE_INIT;
4769 entryNum = ENTRY_INIT;
4770 old_default_value = ENTRY_INIT;
4771 lp = default_lp = default_entry = NULL;
4773 prev = NULL;
4774 for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4775 lp->lineNum = ++lineNum;
4778 * Get the value of the default command
4780 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4781 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4782 lp->arg) {
4783 old_default_value = atoi(lp->arg);
4784 default_lp = lp;
4788 * If not a booting entry, nothing else to fix for this
4789 * entry
4791 if (lp->entryNum == ENTRY_INIT)
4792 continue;
4795 * Record the position of the default entry.
4796 * The following works because global
4797 * commands like default and timeout should precede
4798 * actual boot entries, so old_default_value
4799 * is already known (or default cmd is missing).
4801 if (default_entry == NULL &&
4802 old_default_value != ENTRY_INIT &&
4803 lp->entryNum == old_default_value) {
4804 default_entry = lp;
4808 * Now fixup the entry number
4810 if (lp->cmd != NULL &&
4811 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4812 lp->entryNum = ++entryNum;
4813 /* fixup the bootadm header */
4814 if (prev && prev->flags == BAM_COMMENT &&
4815 prev->arg &&
4816 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4817 prev->entryNum = lp->entryNum;
4819 } else {
4820 lp->entryNum = entryNum;
4825 * No default command in menu, simply return
4827 if (default_lp == NULL) {
4828 return;
4831 free(default_lp->arg);
4832 free(default_lp->line);
4834 if (default_entry == NULL) {
4835 default_lp->arg = s_strdup("0");
4836 } else {
4837 (void) snprintf(buf, sizeof (buf), "%d",
4838 default_entry->entryNum);
4839 default_lp->arg = s_strdup(buf);
4843 * The following is required since only the line field gets
4844 * written back to menu.lst
4846 (void) snprintf(buf, sizeof (buf), "%s%s%s",
4847 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4848 default_lp->line = s_strdup(buf);
4852 static menu_t *
4853 menu_read(char *menu_path)
4855 FILE *fp;
4856 char buf[BAM_MAXLINE], *cp;
4857 menu_t *mp;
4858 int line, entry, len, n;
4860 mp = s_calloc(1, sizeof (menu_t));
4862 fp = fopen(menu_path, "r");
4863 if (fp == NULL) { /* Let the caller handle this error */
4864 free(mp);
4865 return (NULL);
4868 /* Note: GRUB boot entry number starts with 0 */
4869 line = LINE_INIT;
4870 entry = ENTRY_INIT;
4871 cp = buf;
4872 len = sizeof (buf);
4873 while (s_fgets(cp, len, fp) != NULL) {
4874 n = strlen(cp);
4875 if (cp[n - 1] == '\\') {
4876 len -= n - 1;
4877 assert(len >= 2);
4878 cp += n - 1;
4879 continue;
4881 line_parser(mp, buf, &line, &entry);
4882 cp = buf;
4883 len = sizeof (buf);
4886 if (fclose(fp) == EOF) {
4887 bam_error(CLOSE_FAIL, menu_path, strerror(errno));
4890 return (mp);
4893 static error_t
4894 selector(menu_t *mp, char *opt, int *entry, char **title)
4896 char *eq;
4897 char *opt_dup;
4898 int entryNum;
4900 assert(mp);
4901 assert(mp->start);
4902 assert(opt);
4904 opt_dup = s_strdup(opt);
4906 if (entry)
4907 *entry = ENTRY_INIT;
4908 if (title)
4909 *title = NULL;
4911 eq = strchr(opt_dup, '=');
4912 if (eq == NULL) {
4913 bam_error(INVALID_OPT, opt);
4914 free(opt_dup);
4915 return (BAM_ERROR);
4918 *eq = '\0';
4919 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
4920 assert(mp->end);
4921 entryNum = s_strtol(eq + 1);
4922 if (entryNum < 0 || entryNum > mp->end->entryNum) {
4923 bam_error(INVALID_ENTRY, eq + 1);
4924 free(opt_dup);
4925 return (BAM_ERROR);
4927 *entry = entryNum;
4928 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
4929 *title = opt + (eq - opt_dup) + 1;
4930 } else {
4931 bam_error(INVALID_OPT, opt);
4932 free(opt_dup);
4933 return (BAM_ERROR);
4936 free(opt_dup);
4937 return (BAM_SUCCESS);
4941 * If invoked with no titles/entries (opt == NULL)
4942 * only title lines in file are printed.
4944 * If invoked with a title or entry #, all
4945 * lines in *every* matching entry are listed
4947 static error_t
4948 list_entry(menu_t *mp, char *menu_path, char *opt)
4950 line_t *lp;
4951 int entry = ENTRY_INIT;
4952 int found;
4953 char *title = NULL;
4955 assert(mp);
4956 assert(menu_path);
4958 /* opt is optional */
4959 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
4960 opt ? opt : "<NULL>"));
4962 if (mp->start == NULL) {
4963 bam_error(NO_MENU, menu_path);
4964 return (BAM_ERROR);
4967 if (opt != NULL) {
4968 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
4969 return (BAM_ERROR);
4971 assert((entry != ENTRY_INIT) ^ (title != NULL));
4972 } else {
4973 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
4974 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
4977 found = 0;
4978 for (lp = mp->start; lp; lp = lp->next) {
4979 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
4980 continue;
4981 if (opt == NULL && lp->flags == BAM_TITLE) {
4982 bam_print(PRINT_TITLE, lp->entryNum,
4983 lp->arg);
4984 found = 1;
4985 continue;
4987 if (entry != ENTRY_INIT && lp->entryNum == entry) {
4988 bam_print(PRINT, lp->line);
4989 found = 1;
4990 continue;
4994 * We set the entry value here so that all lines
4995 * in entry get printed. If we subsequently match
4996 * title in other entries, all lines in those
4997 * entries get printed as well.
4999 if (title && lp->flags == BAM_TITLE && lp->arg &&
5000 strncmp(title, lp->arg, strlen(title)) == 0) {
5001 bam_print(PRINT, lp->line);
5002 entry = lp->entryNum;
5003 found = 1;
5004 continue;
5008 if (!found) {
5009 bam_error(NO_MATCH_ENTRY);
5010 return (BAM_ERROR);
5013 return (BAM_SUCCESS);
5017 add_boot_entry(menu_t *mp,
5018 char *title,
5019 char *findroot,
5020 char *kernel,
5021 char *mod_kernel,
5022 char *module,
5023 char *bootfs)
5025 int lineNum;
5026 int entryNum;
5027 char linebuf[BAM_MAXLINE];
5028 menu_cmd_t k_cmd;
5029 menu_cmd_t m_cmd;
5030 const char *fcn = "add_boot_entry()";
5032 assert(mp);
5034 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
5035 if (findroot == NULL) {
5036 bam_error(NULL_FINDROOT);
5037 return (BAM_ERROR);
5040 if (title == NULL) {
5041 title = "Solaris"; /* default to Solaris */
5043 if (kernel == NULL) {
5044 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
5045 return (BAM_ERROR);
5047 if (module == NULL) {
5048 if (bam_direct != BAM_DIRECT_DBOOT) {
5049 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
5050 return (BAM_ERROR);
5053 /* Figure the commands out from the kernel line */
5054 if (strstr(kernel, "$ISADIR") != NULL) {
5055 module = DIRECT_BOOT_ARCHIVE;
5056 } else if (strstr(kernel, "amd64") != NULL) {
5057 module = DIRECT_BOOT_ARCHIVE_64;
5058 } else {
5059 module = DIRECT_BOOT_ARCHIVE_32;
5063 k_cmd = KERNEL_DOLLAR_CMD;
5064 m_cmd = MODULE_DOLLAR_CMD;
5066 if (mp->start) {
5067 lineNum = mp->end->lineNum;
5068 entryNum = mp->end->entryNum;
5069 } else {
5070 lineNum = LINE_INIT;
5071 entryNum = ENTRY_INIT;
5075 * No separator for comment (HDR/FTR) commands
5076 * The syntax for comments is #<comment>
5078 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5079 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5080 line_parser(mp, linebuf, &lineNum, &entryNum);
5082 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5083 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5084 line_parser(mp, linebuf, &lineNum, &entryNum);
5086 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5087 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5088 line_parser(mp, linebuf, &lineNum, &entryNum);
5089 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
5091 if (bootfs != NULL) {
5092 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5093 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5094 line_parser(mp, linebuf, &lineNum, &entryNum);
5097 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5098 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5099 line_parser(mp, linebuf, &lineNum, &entryNum);
5101 if (mod_kernel != NULL) {
5102 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5103 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5104 line_parser(mp, linebuf, &lineNum, &entryNum);
5107 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5108 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5109 line_parser(mp, linebuf, &lineNum, &entryNum);
5111 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5112 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5113 line_parser(mp, linebuf, &lineNum, &entryNum);
5115 return (entryNum);
5118 error_t
5119 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5121 line_t *lp;
5122 line_t *freed;
5123 entry_t *ent;
5124 entry_t *tmp;
5125 int deleted = 0;
5126 const char *fcn = "delete_boot_entry()";
5128 assert(entryNum != ENTRY_INIT);
5130 tmp = NULL;
5132 ent = mp->entries;
5133 while (ent) {
5134 lp = ent->start;
5137 * Check entry number and make sure it's a modifiable entry.
5139 * Guidelines:
5140 * + We can modify a bootadm-created entry
5141 * + We can modify a libbe-created entry
5143 if ((lp->flags != BAM_COMMENT &&
5144 (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5145 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5146 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5147 ent = ent->next;
5148 continue;
5151 /* free the entry content */
5152 do {
5153 freed = lp;
5154 lp = lp->next; /* prev stays the same */
5155 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
5156 unlink_line(mp, freed);
5157 line_free(freed);
5158 } while (freed != ent->end);
5160 /* free the entry_t structure */
5161 assert(tmp == NULL);
5162 tmp = ent;
5163 ent = ent->next;
5164 if (tmp->prev)
5165 tmp->prev->next = ent;
5166 else
5167 mp->entries = ent;
5168 if (ent)
5169 ent->prev = tmp->prev;
5170 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
5171 free(tmp);
5172 tmp = NULL;
5173 deleted = 1;
5176 assert(tmp == NULL);
5178 if (!deleted && entryNum != ALL_ENTRIES) {
5179 if (quiet == DBE_PRINTERR)
5180 bam_error(NO_BOOTADM_MATCH);
5181 return (BAM_ERROR);
5185 * Now that we have deleted an entry, update
5186 * the entry numbering and the default cmd.
5188 update_numbering(mp);
5190 return (BAM_SUCCESS);
5193 static error_t
5194 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5196 assert(mp);
5197 assert(dummy == NULL);
5198 assert(opt == NULL);
5200 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
5202 if (mp->start == NULL) {
5203 bam_print(EMPTY_MENU);
5204 return (BAM_SUCCESS);
5207 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5208 return (BAM_ERROR);
5211 return (BAM_WRITE);
5214 static FILE *
5215 create_diskmap(char *osroot)
5217 FILE *fp;
5218 char cmd[PATH_MAX + 16];
5219 char path[PATH_MAX];
5220 const char *fcn = "create_diskmap()";
5222 /* make sure we have a map file */
5223 fp = fopen(GRUBDISK_MAP, "r");
5224 if (fp == NULL) {
5225 int ret;
5227 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5228 CREATE_DISKMAP);
5229 if (ret >= sizeof (path)) {
5230 bam_error(PATH_TOO_LONG, osroot);
5231 return (NULL);
5233 if (is_safe_exec(path) == BAM_ERROR)
5234 return (NULL);
5236 (void) snprintf(cmd, sizeof (cmd),
5237 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5238 if (exec_cmd(cmd, NULL) != 0)
5239 return (NULL);
5240 fp = fopen(GRUBDISK_MAP, "r");
5241 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5242 if (fp) {
5243 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
5244 } else {
5245 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
5248 return (fp);
5251 #define SECTOR_SIZE 512
5253 static int
5254 get_partition(char *device)
5256 int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5257 struct mboot *mboot;
5258 char boot_sect[SECTOR_SIZE];
5259 char *wholedisk, *slice;
5260 #ifdef i386
5261 ext_part_t *epp;
5262 uint32_t secnum, numsec;
5263 int rval, pno, ext_partno = PARTNO_NOTFOUND;
5264 #endif
5266 /* form whole disk (p0) */
5267 slice = device + strlen(device) - 2;
5268 is_pcfs = (*slice != 's');
5269 if (!is_pcfs)
5270 *slice = '\0';
5271 wholedisk = s_calloc(1, strlen(device) + 3);
5272 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5273 if (!is_pcfs)
5274 *slice = 's';
5276 /* read boot sector */
5277 fd = open(wholedisk, O_RDONLY);
5278 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5279 return (partno);
5281 (void) close(fd);
5283 #ifdef i386
5284 /* Read/Initialize extended partition information */
5285 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5286 != FDISK_SUCCESS) {
5287 switch (rval) {
5289 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5290 * be considered as soft errors and hence
5291 * we do not return
5293 case FDISK_EBADLOGDRIVE:
5294 break;
5295 case FDISK_ENOLOGDRIVE:
5296 break;
5297 case FDISK_EBADMAGIC:
5298 /*FALLTHROUGH*/
5299 default:
5300 free(wholedisk);
5301 libfdisk_fini(&epp);
5302 return (partno);
5305 #endif
5306 free(wholedisk);
5308 /* parse fdisk table */
5309 mboot = (struct mboot *)((void *)boot_sect);
5310 for (i = 0; i < FD_NUMPART; i++) {
5311 struct ipart *part =
5312 (struct ipart *)(uintptr_t)mboot->parts + i;
5313 if (is_pcfs) { /* looking for solaris boot part */
5314 if (part->systid == 0xbe) {
5315 partno = i;
5316 break;
5318 } else { /* look for solaris partition, old and new */
5319 if (part->systid == EFI_PMBR) {
5320 partno = PARTNO_EFI;
5321 break;
5324 #ifdef i386
5325 if ((part->systid == SUNIXOS &&
5326 (fdisk_is_linux_swap(epp, part->relsect,
5327 NULL) != 0)) || part->systid == SUNIXOS2) {
5328 #else
5329 if (part->systid == SUNIXOS ||
5330 part->systid == SUNIXOS2) {
5331 #endif
5332 partno = i;
5333 break;
5336 #ifdef i386
5337 if (fdisk_is_dos_extended(part->systid))
5338 ext_partno = i;
5339 #endif
5342 #ifdef i386
5343 /* If no primary solaris partition, check extended partition */
5344 if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5345 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5346 if (rval == FDISK_SUCCESS) {
5347 partno = pno - 1;
5350 libfdisk_fini(&epp);
5351 #endif
5352 return (partno);
5355 char *
5356 get_grubroot(char *osroot, char *osdev, char *menu_root)
5358 char *grubroot; /* (hd#,#,#) */
5359 char *slice;
5360 char *grubhd;
5361 int fdiskpart;
5362 int found = 0;
5363 char *devname;
5364 char *ctdname = strstr(osdev, "dsk/");
5365 char linebuf[PATH_MAX];
5366 FILE *fp;
5368 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5369 if (ctdname == NULL) {
5370 bam_error(INVALID_DEV_DSK, osdev);
5371 return (NULL);
5374 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5375 /* menu bears no resemblance to our reality */
5376 bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev);
5377 return (NULL);
5380 ctdname += strlen("dsk/");
5381 slice = strrchr(ctdname, 's');
5382 if (slice)
5383 *slice = '\0';
5385 fp = create_diskmap(osroot);
5386 if (fp == NULL) {
5387 bam_error(DISKMAP_FAIL, osroot);
5388 return (NULL);
5391 rewind(fp);
5392 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5393 grubhd = strtok(linebuf, " \t\n");
5394 if (grubhd)
5395 devname = strtok(NULL, " \t\n");
5396 else
5397 devname = NULL;
5398 if (devname && strcmp(devname, ctdname) == 0) {
5399 found = 1;
5400 break;
5404 if (slice)
5405 *slice = 's';
5407 (void) fclose(fp);
5408 fp = NULL;
5410 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5411 if (found == 0) {
5412 bam_error(BIOSDEV_SKIP, osdev);
5413 return (NULL);
5416 fdiskpart = get_partition(osdev);
5417 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5418 if (fdiskpart == PARTNO_NOTFOUND) {
5419 bam_error(FDISKPART_FAIL, osdev);
5420 return (NULL);
5423 grubroot = s_calloc(1, 10);
5424 if (fdiskpart == PARTNO_EFI) {
5425 fdiskpart = atoi(&slice[1]);
5426 slice = NULL;
5429 if (slice) {
5430 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5431 grubhd, fdiskpart, slice[1] + 'a' - '0');
5432 } else
5433 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5434 grubhd, fdiskpart);
5436 assert(fp == NULL);
5437 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5438 return (grubroot);
5441 static char *
5442 find_primary_common(char *mntpt, char *fstype)
5444 char signdir[PATH_MAX];
5445 char tmpsign[MAXNAMELEN + 1];
5446 char *lu;
5447 char *ufs;
5448 char *zfs;
5449 DIR *dirp = NULL;
5450 struct dirent *entp;
5451 struct stat sb;
5452 const char *fcn = "find_primary_common()";
5454 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5455 mntpt, GRUBSIGN_DIR);
5457 if (stat(signdir, &sb) == -1) {
5458 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
5459 return (NULL);
5462 dirp = opendir(signdir);
5463 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5464 if (dirp == NULL) {
5465 bam_error(OPENDIR_FAILED, signdir, strerror(errno));
5466 return (NULL);
5469 ufs = zfs = lu = NULL;
5471 while (entp = readdir(dirp)) {
5472 if (strcmp(entp->d_name, ".") == 0 ||
5473 strcmp(entp->d_name, "..") == 0)
5474 continue;
5476 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5478 if (lu == NULL &&
5479 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5480 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5481 lu = s_strdup(tmpsign);
5484 if (ufs == NULL &&
5485 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5486 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5487 ufs = s_strdup(tmpsign);
5490 if (zfs == NULL &&
5491 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5492 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5493 zfs = s_strdup(tmpsign);
5497 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
5498 zfs ? zfs : "NULL",
5499 ufs ? ufs : "NULL",
5500 lu ? lu : "NULL"));
5502 if (dirp) {
5503 (void) closedir(dirp);
5504 dirp = NULL;
5507 if (strcmp(fstype, "ufs") == 0 && zfs) {
5508 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5509 free(zfs);
5510 zfs = NULL;
5511 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5512 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5513 free(ufs);
5514 ufs = NULL;
5517 assert(dirp == NULL);
5519 /* For now, we let Live Upgrade take care of its signature itself */
5520 if (lu) {
5521 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5522 free(lu);
5523 lu = NULL;
5526 return (zfs ? zfs : ufs);
5529 static char *
5530 find_backup_common(char *mntpt, char *fstype)
5532 FILE *bfp = NULL;
5533 char tmpsign[MAXNAMELEN + 1];
5534 char backup[PATH_MAX];
5535 char *ufs;
5536 char *zfs;
5537 char *lu;
5538 int error;
5539 const char *fcn = "find_backup_common()";
5542 * We didn't find it in the primary directory.
5543 * Look at the backup
5545 (void) snprintf(backup, sizeof (backup), "%s%s",
5546 mntpt, GRUBSIGN_BACKUP);
5548 bfp = fopen(backup, "r");
5549 if (bfp == NULL) {
5550 error = errno;
5551 if (bam_verbose) {
5552 bam_error(OPEN_FAIL, backup, strerror(error));
5554 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
5555 return (NULL);
5558 ufs = zfs = lu = NULL;
5560 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5562 if (lu == NULL &&
5563 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5564 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5565 lu = s_strdup(tmpsign);
5568 if (ufs == NULL &&
5569 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5570 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5571 ufs = s_strdup(tmpsign);
5574 if (zfs == NULL &&
5575 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5576 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5577 zfs = s_strdup(tmpsign);
5581 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
5582 zfs ? zfs : "NULL",
5583 ufs ? ufs : "NULL",
5584 lu ? lu : "NULL"));
5586 if (bfp) {
5587 (void) fclose(bfp);
5588 bfp = NULL;
5591 if (strcmp(fstype, "ufs") == 0 && zfs) {
5592 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5593 free(zfs);
5594 zfs = NULL;
5595 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5596 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5597 free(ufs);
5598 ufs = NULL;
5601 assert(bfp == NULL);
5603 /* For now, we let Live Upgrade take care of its signature itself */
5604 if (lu) {
5605 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5606 free(lu);
5607 lu = NULL;
5610 return (zfs ? zfs : ufs);
5613 static char *
5614 find_ufs_existing(char *osroot)
5616 char *sign;
5617 const char *fcn = "find_ufs_existing()";
5619 sign = find_primary_common(osroot, "ufs");
5620 if (sign == NULL) {
5621 sign = find_backup_common(osroot, "ufs");
5622 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
5623 } else {
5624 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
5627 return (sign);
5630 char *
5631 get_mountpoint(char *special, char *fstype)
5633 FILE *mntfp;
5634 struct mnttab mp = {0};
5635 struct mnttab mpref = {0};
5636 int error;
5637 int ret;
5638 const char *fcn = "get_mountpoint()";
5640 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
5642 mntfp = fopen(MNTTAB, "r");
5643 error = errno;
5644 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5645 if (mntfp == NULL) {
5646 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
5647 return (NULL);
5650 mpref.mnt_special = special;
5651 mpref.mnt_fstype = fstype;
5653 ret = getmntany(mntfp, &mp, &mpref);
5654 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5655 if (ret != 0) {
5656 (void) fclose(mntfp);
5657 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
5658 return (NULL);
5660 (void) fclose(mntfp);
5662 assert(mp.mnt_mountp);
5664 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
5666 return (s_strdup(mp.mnt_mountp));
5670 * Mounts a "legacy" top dataset (if needed)
5671 * Returns: The mountpoint of the legacy top dataset or NULL on error
5672 * mnted returns one of the above values defined for zfs_mnted_t
5674 static char *
5675 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5677 char cmd[PATH_MAX];
5678 char tmpmnt[PATH_MAX];
5679 filelist_t flist = {0};
5680 char *is_mounted;
5681 struct stat sb;
5682 int ret;
5683 const char *fcn = "mount_legacy_dataset()";
5685 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5687 *mnted = ZFS_MNT_ERROR;
5689 (void) snprintf(cmd, sizeof (cmd),
5690 "/sbin/zfs get -Ho value mounted %s",
5691 pool);
5693 ret = exec_cmd(cmd, &flist);
5694 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5695 if (ret != 0) {
5696 bam_error(ZFS_MNTED_FAILED, pool);
5697 return (NULL);
5700 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5701 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5702 bam_error(BAD_ZFS_MNTED, pool);
5703 filelist_free(&flist);
5704 return (NULL);
5707 is_mounted = strtok(flist.head->line, " \t\n");
5708 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5709 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5710 if (strcmp(is_mounted, "no") != 0) {
5711 filelist_free(&flist);
5712 *mnted = LEGACY_ALREADY;
5713 /* get_mountpoint returns a strdup'ed string */
5714 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
5715 return (get_mountpoint(pool, "zfs"));
5718 filelist_free(&flist);
5721 * legacy top dataset is not mounted. Mount it now
5722 * First create a mountpoint.
5724 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5725 ZFS_LEGACY_MNTPT, getpid());
5727 ret = stat(tmpmnt, &sb);
5728 if (ret == -1) {
5729 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
5730 ret = mkdirp(tmpmnt, DIR_PERMS);
5731 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5732 if (ret == -1) {
5733 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
5734 return (NULL);
5736 } else {
5737 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
5740 (void) snprintf(cmd, sizeof (cmd),
5741 "/sbin/mount -F zfs %s %s",
5742 pool, tmpmnt);
5744 ret = exec_cmd(cmd, NULL);
5745 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5746 if (ret != 0) {
5747 bam_error(ZFS_MOUNT_FAILED, pool);
5748 (void) rmdir(tmpmnt);
5749 return (NULL);
5752 *mnted = LEGACY_MOUNTED;
5753 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
5754 return (s_strdup(tmpmnt));
5758 * Mounts the top dataset (if needed)
5759 * Returns: The mountpoint of the top dataset or NULL on error
5760 * mnted returns one of the above values defined for zfs_mnted_t
5762 static char *
5763 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5765 char cmd[PATH_MAX];
5766 filelist_t flist = {0};
5767 char *is_mounted;
5768 char *mntpt;
5769 char *zmntpt;
5770 int ret;
5771 const char *fcn = "mount_top_dataset()";
5773 *mnted = ZFS_MNT_ERROR;
5775 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5778 * First check if the top dataset is a "legacy" dataset
5780 (void) snprintf(cmd, sizeof (cmd),
5781 "/sbin/zfs get -Ho value mountpoint %s",
5782 pool);
5783 ret = exec_cmd(cmd, &flist);
5784 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5785 if (ret != 0) {
5786 bam_error(ZFS_MNTPT_FAILED, pool);
5787 return (NULL);
5790 if (flist.head && (flist.head == flist.tail)) {
5791 char *legacy = strtok(flist.head->line, " \t\n");
5792 if (legacy && strcmp(legacy, "legacy") == 0) {
5793 filelist_free(&flist);
5794 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
5795 return (mount_legacy_dataset(pool, mnted));
5799 filelist_free(&flist);
5801 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
5803 (void) snprintf(cmd, sizeof (cmd),
5804 "/sbin/zfs get -Ho value mounted %s",
5805 pool);
5807 ret = exec_cmd(cmd, &flist);
5808 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5809 if (ret != 0) {
5810 bam_error(ZFS_MNTED_FAILED, pool);
5811 return (NULL);
5814 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5815 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5816 bam_error(BAD_ZFS_MNTED, pool);
5817 filelist_free(&flist);
5818 return (NULL);
5821 is_mounted = strtok(flist.head->line, " \t\n");
5822 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5823 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5824 if (strcmp(is_mounted, "no") != 0) {
5825 filelist_free(&flist);
5826 *mnted = ZFS_ALREADY;
5827 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
5828 goto mounted;
5831 filelist_free(&flist);
5832 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
5834 /* top dataset is not mounted. Mount it now */
5835 (void) snprintf(cmd, sizeof (cmd),
5836 "/sbin/zfs mount %s", pool);
5837 ret = exec_cmd(cmd, NULL);
5838 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5839 if (ret != 0) {
5840 bam_error(ZFS_MOUNT_FAILED, pool);
5841 return (NULL);
5843 *mnted = ZFS_MOUNTED;
5844 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
5845 /*FALLTHRU*/
5846 mounted:
5848 * Now get the mountpoint
5850 (void) snprintf(cmd, sizeof (cmd),
5851 "/sbin/zfs get -Ho value mountpoint %s",
5852 pool);
5854 ret = exec_cmd(cmd, &flist);
5855 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
5856 if (ret != 0) {
5857 bam_error(ZFS_MNTPT_FAILED, pool);
5858 goto error;
5861 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
5862 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5863 bam_error(NULL_ZFS_MNTPT, pool);
5864 goto error;
5867 mntpt = strtok(flist.head->line, " \t\n");
5868 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
5869 if (*mntpt != '/') {
5870 bam_error(BAD_ZFS_MNTPT, pool, mntpt);
5871 goto error;
5873 zmntpt = s_strdup(mntpt);
5875 filelist_free(&flist);
5877 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
5879 return (zmntpt);
5881 error:
5882 filelist_free(&flist);
5883 (void) umount_top_dataset(pool, *mnted, NULL);
5884 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
5885 return (NULL);
5888 static int
5889 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
5891 char cmd[PATH_MAX];
5892 int ret;
5893 const char *fcn = "umount_top_dataset()";
5895 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
5896 switch (mnted) {
5897 case LEGACY_ALREADY:
5898 case ZFS_ALREADY:
5899 /* nothing to do */
5900 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
5901 mntpt ? mntpt : "NULL"));
5902 free(mntpt);
5903 return (BAM_SUCCESS);
5904 case LEGACY_MOUNTED:
5905 (void) snprintf(cmd, sizeof (cmd),
5906 "/sbin/umount %s", pool);
5907 ret = exec_cmd(cmd, NULL);
5908 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
5909 if (ret != 0) {
5910 bam_error(UMOUNT_FAILED, pool);
5911 free(mntpt);
5912 return (BAM_ERROR);
5914 if (mntpt)
5915 (void) rmdir(mntpt);
5916 free(mntpt);
5917 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
5918 return (BAM_SUCCESS);
5919 case ZFS_MOUNTED:
5920 free(mntpt);
5921 (void) snprintf(cmd, sizeof (cmd),
5922 "/sbin/zfs unmount %s", pool);
5923 ret = exec_cmd(cmd, NULL);
5924 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
5925 if (ret != 0) {
5926 bam_error(UMOUNT_FAILED, pool);
5927 return (BAM_ERROR);
5929 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
5930 return (BAM_SUCCESS);
5931 default:
5932 bam_error(INT_BAD_MNTSTATE, pool);
5933 return (BAM_ERROR);
5935 /*NOTREACHED*/
5939 * For ZFS, osdev can be one of two forms
5940 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
5941 * It can be a /dev/[r]dsk special file. We handle both instances
5943 static char *
5944 get_pool(char *osdev)
5946 char cmd[PATH_MAX];
5947 char buf[PATH_MAX];
5948 filelist_t flist = {0};
5949 char *pool;
5950 char *cp;
5951 char *slash;
5952 int ret;
5953 const char *fcn = "get_pool()";
5955 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
5956 if (osdev == NULL) {
5957 bam_error(GET_POOL_OSDEV_NULL);
5958 return (NULL);
5961 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
5963 if (osdev[0] != '/') {
5964 (void) strlcpy(buf, osdev, sizeof (buf));
5965 slash = strchr(buf, '/');
5966 if (slash)
5967 *slash = '\0';
5968 pool = s_strdup(buf);
5969 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5970 return (pool);
5971 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
5972 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
5973 bam_error(GET_POOL_BAD_OSDEV, osdev);
5974 return (NULL);
5978 * Call the zfs fstyp directly since this is a zpool. This avoids
5979 * potential pcfs conflicts if the first block wasn't cleared.
5981 (void) snprintf(cmd, sizeof (cmd),
5982 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
5983 osdev);
5985 ret = exec_cmd(cmd, &flist);
5986 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
5987 if (ret != 0) {
5988 bam_error(FSTYP_A_FAILED, osdev);
5989 return (NULL);
5992 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
5993 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5994 bam_error(NULL_FSTYP_A, osdev);
5995 filelist_free(&flist);
5996 return (NULL);
5999 (void) strtok(flist.head->line, "'");
6000 cp = strtok(NULL, "'");
6001 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6002 if (cp == NULL) {
6003 bam_error(BAD_FSTYP_A, osdev);
6004 filelist_free(&flist);
6005 return (NULL);
6008 pool = s_strdup(cp);
6010 filelist_free(&flist);
6012 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
6014 return (pool);
6017 static char *
6018 find_zfs_existing(char *osdev)
6020 char *pool;
6021 zfs_mnted_t mnted;
6022 char *mntpt;
6023 char *sign;
6024 const char *fcn = "find_zfs_existing()";
6026 pool = get_pool(osdev);
6027 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6028 if (pool == NULL) {
6029 bam_error(ZFS_GET_POOL_FAILED, osdev);
6030 return (NULL);
6033 mntpt = mount_top_dataset(pool, &mnted);
6034 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
6035 if (mntpt == NULL) {
6036 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
6037 free(pool);
6038 return (NULL);
6041 sign = find_primary_common(mntpt, "zfs");
6042 if (sign == NULL) {
6043 sign = find_backup_common(mntpt, "zfs");
6044 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
6045 } else {
6046 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
6049 (void) umount_top_dataset(pool, mnted, mntpt);
6051 free(pool);
6053 return (sign);
6056 static char *
6057 find_existing_sign(char *osroot, char *osdev, char *fstype)
6059 const char *fcn = "find_existing_sign()";
6061 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6062 if (strcmp(fstype, "ufs") == 0) {
6063 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
6064 return (find_ufs_existing(osroot));
6065 } else if (strcmp(fstype, "zfs") == 0) {
6066 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
6067 return (find_zfs_existing(osdev));
6068 } else {
6069 bam_error(GRUBSIGN_NOTSUP, fstype);
6070 return (NULL);
6074 #define MH_HASH_SZ 16
6076 typedef enum {
6077 MH_ERROR = -1,
6078 MH_NOMATCH,
6079 MH_MATCH
6080 } mh_search_t;
6082 typedef struct mcache {
6083 char *mc_special;
6084 char *mc_mntpt;
6085 char *mc_fstype;
6086 struct mcache *mc_next;
6087 } mcache_t;
6089 typedef struct mhash {
6090 mcache_t *mh_hash[MH_HASH_SZ];
6091 } mhash_t;
6093 static int
6094 mhash_fcn(char *key)
6096 int i;
6097 uint64_t sum = 0;
6099 for (i = 0; key[i] != '\0'; i++) {
6100 sum += (uchar_t)key[i];
6103 sum %= MH_HASH_SZ;
6105 assert(sum < MH_HASH_SZ);
6107 return (sum);
6110 static mhash_t *
6111 cache_mnttab(void)
6113 FILE *mfp;
6114 struct extmnttab mnt;
6115 mcache_t *mcp;
6116 mhash_t *mhp;
6117 char *ctds;
6118 int idx;
6119 int error;
6120 char *special_dup;
6121 const char *fcn = "cache_mnttab()";
6123 mfp = fopen(MNTTAB, "r");
6124 error = errno;
6125 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6126 if (mfp == NULL) {
6127 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6128 return (NULL);
6131 mhp = s_calloc(1, sizeof (mhash_t));
6133 resetmnttab(mfp);
6135 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6136 /* only cache ufs */
6137 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6138 continue;
6140 /* basename() modifies its arg, so dup it */
6141 special_dup = s_strdup(mnt.mnt_special);
6142 ctds = basename(special_dup);
6144 mcp = s_calloc(1, sizeof (mcache_t));
6145 mcp->mc_special = s_strdup(ctds);
6146 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6147 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6148 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
6149 mnt.mnt_mountp, mnt.mnt_fstype));
6150 idx = mhash_fcn(ctds);
6151 mcp->mc_next = mhp->mh_hash[idx];
6152 mhp->mh_hash[idx] = mcp;
6153 free(special_dup);
6156 (void) fclose(mfp);
6158 return (mhp);
6161 static void
6162 free_mnttab(mhash_t *mhp)
6164 mcache_t *mcp;
6165 int i;
6167 for (i = 0; i < MH_HASH_SZ; i++) {
6168 /*LINTED*/
6169 while (mcp = mhp->mh_hash[i]) {
6170 mhp->mh_hash[i] = mcp->mc_next;
6171 free(mcp->mc_special);
6172 free(mcp->mc_mntpt);
6173 free(mcp->mc_fstype);
6174 free(mcp);
6178 for (i = 0; i < MH_HASH_SZ; i++) {
6179 assert(mhp->mh_hash[i] == NULL);
6181 free(mhp);
6184 static mh_search_t
6185 search_hash(mhash_t *mhp, char *special, char **mntpt)
6187 int idx;
6188 mcache_t *mcp;
6189 const char *fcn = "search_hash()";
6191 assert(mntpt);
6193 *mntpt = NULL;
6195 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6196 if (strchr(special, '/')) {
6197 bam_error(INVALID_MHASH_KEY, special);
6198 return (MH_ERROR);
6201 idx = mhash_fcn(special);
6203 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6204 if (strcmp(mcp->mc_special, special) == 0)
6205 break;
6208 if (mcp == NULL) {
6209 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
6210 return (MH_NOMATCH);
6213 assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6214 *mntpt = mcp->mc_mntpt;
6215 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
6216 return (MH_MATCH);
6219 static int
6220 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6222 char *sign;
6223 char *signline;
6224 char signbuf[MAXNAMELEN];
6225 int len;
6226 int error;
6227 const char *fcn = "check_add_ufs_sign_to_list()";
6229 /* safe to specify NULL as "osdev" arg for UFS */
6230 sign = find_existing_sign(mntpt, NULL, "ufs");
6231 if (sign == NULL) {
6232 /* No existing signature, nothing to add to list */
6233 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
6234 return (0);
6237 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6238 signline = signbuf;
6240 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6241 if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6242 strlen(GRUBSIGN_UFS_PREFIX))) {
6243 bam_error(INVALID_UFS_SIGNATURE, sign);
6244 free(sign);
6245 /* ignore invalid signatures */
6246 return (0);
6249 len = fputs(signline, tfp);
6250 error = errno;
6251 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6252 if (len != strlen(signline)) {
6253 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
6254 free(sign);
6255 return (-1);
6258 free(sign);
6260 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
6261 return (0);
6265 * slice is a basename not a full pathname
6267 static int
6268 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6270 int ret;
6271 char cmd[PATH_MAX];
6272 char path[PATH_MAX];
6273 struct stat sbuf;
6274 char *mntpt;
6275 filelist_t flist = {0};
6276 char *fstype;
6277 char blkslice[PATH_MAX];
6278 const char *fcn = "process_slice_common()";
6281 ret = search_hash(mhp, slice, &mntpt);
6282 switch (ret) {
6283 case MH_MATCH:
6284 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6285 return (-1);
6286 else
6287 return (0);
6288 case MH_NOMATCH:
6289 break;
6290 case MH_ERROR:
6291 default:
6292 return (-1);
6295 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6296 if (stat(path, &sbuf) == -1) {
6297 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
6298 return (0);
6301 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6302 (void) snprintf(cmd, sizeof (cmd),
6303 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6304 slice);
6306 if (exec_cmd(cmd, &flist) != 0) {
6307 if (bam_verbose)
6308 bam_print(FSTYP_FAILED, slice);
6309 return (0);
6312 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6313 if (bam_verbose)
6314 bam_print(FSTYP_BAD, slice);
6315 filelist_free(&flist);
6316 return (0);
6319 fstype = strtok(flist.head->line, " \t\n");
6320 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6321 if (bam_verbose)
6322 bam_print(NOT_UFS_SLICE, slice, fstype);
6323 filelist_free(&flist);
6324 return (0);
6327 filelist_free(&flist);
6330 * Since we are mounting the filesystem read-only, the
6331 * the last mount field of the superblock is unchanged
6332 * and does not need to be fixed up post-mount;
6335 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6336 slice);
6338 (void) snprintf(cmd, sizeof (cmd),
6339 "/usr/sbin/mount -F ufs -o ro %s %s "
6340 "> /dev/null 2>&1", blkslice, tmpmnt);
6342 if (exec_cmd(cmd, NULL) != 0) {
6343 if (bam_verbose)
6344 bam_print(MOUNT_FAILED, blkslice, "ufs");
6345 return (0);
6348 ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6350 (void) snprintf(cmd, sizeof (cmd),
6351 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6352 tmpmnt);
6354 if (exec_cmd(cmd, NULL) != 0) {
6355 bam_print(UMOUNT_FAILED, slice);
6356 return (0);
6359 return (ret);
6362 static int
6363 process_vtoc_slices(
6364 char *s0,
6365 struct vtoc *vtoc,
6366 FILE *tfp,
6367 mhash_t *mhp,
6368 char *tmpmnt)
6370 int idx;
6371 char slice[PATH_MAX];
6372 size_t len;
6373 char *cp;
6374 const char *fcn = "process_vtoc_slices()";
6376 len = strlen(s0);
6378 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6380 s0[len - 1] = '\0';
6382 (void) strlcpy(slice, s0, sizeof (slice));
6384 s0[len - 1] = '0';
6386 cp = slice + len - 1;
6388 for (idx = 0; idx < vtoc->v_nparts; idx++) {
6390 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6392 if (vtoc->v_part[idx].p_size == 0) {
6393 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
6394 continue;
6397 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6398 switch (vtoc->v_part[idx].p_tag) {
6399 case V_SWAP:
6400 case V_USR:
6401 case V_BACKUP:
6402 case V_VAR:
6403 case V_HOME:
6404 case V_ALTSCTR:
6405 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
6406 continue;
6407 default:
6408 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
6409 break;
6412 /* skip unmountable and readonly slices */
6413 switch (vtoc->v_part[idx].p_flag) {
6414 case V_UNMNT:
6415 case V_RONLY:
6416 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
6417 continue;
6418 default:
6419 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
6420 break;
6423 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6424 return (-1);
6428 return (0);
6431 static int
6432 process_efi_slices(
6433 char *s0,
6434 struct dk_gpt *efi,
6435 FILE *tfp,
6436 mhash_t *mhp,
6437 char *tmpmnt)
6439 int idx;
6440 char slice[PATH_MAX];
6441 size_t len;
6442 char *cp;
6443 const char *fcn = "process_efi_slices()";
6445 len = strlen(s0);
6447 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6449 s0[len - 1] = '\0';
6451 (void) strlcpy(slice, s0, sizeof (slice));
6453 s0[len - 1] = '0';
6455 cp = slice + len - 1;
6457 for (idx = 0; idx < efi->efi_nparts; idx++) {
6459 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6461 if (efi->efi_parts[idx].p_size == 0) {
6462 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
6463 continue;
6466 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6467 switch (efi->efi_parts[idx].p_tag) {
6468 case V_SWAP:
6469 case V_USR:
6470 case V_BACKUP:
6471 case V_VAR:
6472 case V_HOME:
6473 case V_ALTSCTR:
6474 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
6475 continue;
6476 default:
6477 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
6478 break;
6481 /* skip unmountable and readonly slices */
6482 switch (efi->efi_parts[idx].p_flag) {
6483 case V_UNMNT:
6484 case V_RONLY:
6485 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
6486 continue;
6487 default:
6488 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
6489 break;
6492 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6493 return (-1);
6497 return (0);
6501 * s0 is a basename not a full path
6503 static int
6504 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6506 struct vtoc vtoc;
6507 struct dk_gpt *efi;
6508 char s0path[PATH_MAX];
6509 struct stat sbuf;
6510 int e_flag;
6511 int v_flag;
6512 int retval;
6513 int err;
6514 int fd;
6515 const char *fcn = "process_slice0()";
6517 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6519 if (stat(s0path, &sbuf) == -1) {
6520 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
6521 return (0);
6524 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6525 if (fd == -1) {
6526 bam_error(OPEN_FAIL, s0path, strerror(errno));
6527 return (0);
6530 e_flag = v_flag = 0;
6531 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6532 switch (retval) {
6533 case VT_EIO:
6534 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
6535 break;
6536 case VT_EINVAL:
6537 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
6538 break;
6539 case VT_ERROR:
6540 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
6541 break;
6542 case VT_ENOTSUP:
6543 e_flag = 1;
6544 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
6545 break;
6546 case 0:
6547 v_flag = 1;
6548 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
6549 break;
6550 default:
6551 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
6552 break;
6556 if (e_flag) {
6557 e_flag = 0;
6558 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6559 switch (retval) {
6560 case VT_EIO:
6561 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
6562 break;
6563 case VT_EINVAL:
6564 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
6565 break;
6566 case VT_ERROR:
6567 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
6568 break;
6569 case VT_ENOTSUP:
6570 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
6571 break;
6572 case 0:
6573 e_flag = 1;
6574 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
6575 break;
6576 default:
6577 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
6578 break;
6582 (void) close(fd);
6584 if (v_flag) {
6585 retval = process_vtoc_slices(s0,
6586 &vtoc, tfp, mhp, tmpmnt);
6587 } else if (e_flag) {
6588 retval = process_efi_slices(s0,
6589 efi, tfp, mhp, tmpmnt);
6590 } else {
6591 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
6592 return (0);
6595 return (retval);
6599 * Find and create a list of all existing UFS boot signatures
6601 static int
6602 FindAllUfsSignatures(void)
6604 mhash_t *mnttab_hash;
6605 DIR *dirp = NULL;
6606 struct dirent *dp;
6607 char tmpmnt[PATH_MAX];
6608 char cmd[PATH_MAX];
6609 struct stat sb;
6610 int fd;
6611 FILE *tfp;
6612 size_t len;
6613 int ret;
6614 int error;
6615 const char *fcn = "FindAllUfsSignatures()";
6617 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) {
6618 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
6619 return (0);
6622 fd = open(UFS_SIGNATURE_LIST".tmp",
6623 O_RDWR|O_CREAT|O_TRUNC, 0644);
6624 error = errno;
6625 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6626 if (fd == -1) {
6627 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6628 return (-1);
6631 ret = close(fd);
6632 error = errno;
6633 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6634 if (ret == -1) {
6635 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6636 strerror(error));
6637 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6638 return (-1);
6641 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6642 error = errno;
6643 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6644 if (tfp == NULL) {
6645 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6646 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6647 return (-1);
6650 mnttab_hash = cache_mnttab();
6651 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6652 if (mnttab_hash == NULL) {
6653 (void) fclose(tfp);
6654 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6655 bam_error(CACHE_MNTTAB_FAIL, fcn);
6656 return (-1);
6659 (void) snprintf(tmpmnt, sizeof (tmpmnt),
6660 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6661 (void) unlink(tmpmnt);
6663 ret = mkdirp(tmpmnt, DIR_PERMS);
6664 error = errno;
6665 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6666 if (ret == -1) {
6667 bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
6668 free_mnttab(mnttab_hash);
6669 (void) fclose(tfp);
6670 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6671 return (-1);
6674 dirp = opendir("/dev/rdsk");
6675 error = errno;
6676 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6677 if (dirp == NULL) {
6678 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
6679 goto fail;
6682 while (dp = readdir(dirp)) {
6683 if (strcmp(dp->d_name, ".") == 0 ||
6684 strcmp(dp->d_name, "..") == 0)
6685 continue;
6688 * we only look for the s0 slice. This is guranteed to
6689 * have 's' at len - 2.
6691 len = strlen(dp->d_name);
6692 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6693 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
6694 continue;
6697 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6698 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6699 if (ret == -1)
6700 goto fail;
6703 (void) closedir(dirp);
6704 free_mnttab(mnttab_hash);
6705 (void) rmdir(tmpmnt);
6707 ret = fclose(tfp);
6708 error = errno;
6709 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6710 if (ret == EOF) {
6711 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6712 strerror(error));
6713 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6714 return (-1);
6717 /* We have a list of existing GRUB signatures. Sort it first */
6718 (void) snprintf(cmd, sizeof (cmd),
6719 "/usr/bin/sort -u %s.tmp > %s.sorted",
6720 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6722 ret = exec_cmd(cmd, NULL);
6723 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6724 if (ret != 0) {
6725 bam_error(GRUBSIGN_SORT_FAILED);
6726 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6727 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6728 return (-1);
6731 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6733 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6734 error = errno;
6735 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6736 if (ret == -1) {
6737 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
6738 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6739 return (-1);
6742 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6743 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
6746 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6747 return (0);
6749 fail:
6750 if (dirp)
6751 (void) closedir(dirp);
6752 free_mnttab(mnttab_hash);
6753 (void) rmdir(tmpmnt);
6754 (void) fclose(tfp);
6755 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6756 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6757 return (-1);
6760 static char *
6761 create_ufs_sign(void)
6763 struct stat sb;
6764 int signnum = -1;
6765 char tmpsign[MAXNAMELEN + 1];
6766 char *numstr;
6767 int i;
6768 FILE *tfp;
6769 int ret;
6770 int error;
6771 const char *fcn = "create_ufs_sign()";
6773 bam_print(SEARCHING_UFS_SIGN);
6775 ret = FindAllUfsSignatures();
6776 INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6777 if (ret == -1) {
6778 bam_error(ERR_FIND_UFS_SIGN);
6779 return (NULL);
6782 /* Make sure the list exists and is owned by root */
6783 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6784 (void) unlink(UFS_SIGNATURE_LIST));
6785 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6786 (void) unlink(UFS_SIGNATURE_LIST);
6787 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
6788 return (NULL);
6791 if (sb.st_size == 0) {
6792 bam_print(GRUBSIGN_UFS_NONE);
6793 i = 0;
6794 goto found;
6797 /* The signature list was sorted when it was created */
6798 tfp = fopen(UFS_SIGNATURE_LIST, "r");
6799 error = errno;
6800 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
6801 if (tfp == NULL) {
6802 bam_error(UFS_SIGNATURE_LIST_OPENERR,
6803 UFS_SIGNATURE_LIST, strerror(error));
6804 (void) unlink(UFS_SIGNATURE_LIST);
6805 return (NULL);
6808 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
6810 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
6811 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
6812 (void) fclose(tfp);
6813 (void) unlink(UFS_SIGNATURE_LIST);
6814 bam_error(UFS_BADSIGN, tmpsign);
6815 return (NULL);
6817 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
6819 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
6820 (void) fclose(tfp);
6821 (void) unlink(UFS_SIGNATURE_LIST);
6822 bam_error(UFS_BADSIGN, tmpsign);
6823 return (NULL);
6826 signnum = atoi(numstr);
6827 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
6828 if (signnum < 0) {
6829 (void) fclose(tfp);
6830 (void) unlink(UFS_SIGNATURE_LIST);
6831 bam_error(UFS_BADSIGN, tmpsign);
6832 return (NULL);
6835 if (i != signnum) {
6836 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
6837 break;
6841 (void) fclose(tfp);
6843 found:
6844 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
6846 /* add the ufs signature to the /var/run list of signatures */
6847 ret = ufs_add_to_sign_list(tmpsign);
6848 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
6849 if (ret == -1) {
6850 (void) unlink(UFS_SIGNATURE_LIST);
6851 bam_error(FAILED_ADD_SIGNLIST, tmpsign);
6852 return (NULL);
6855 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6857 return (s_strdup(tmpsign));
6860 static char *
6861 get_fstype(char *osroot)
6863 FILE *mntfp;
6864 struct mnttab mp = {0};
6865 struct mnttab mpref = {0};
6866 int error;
6867 int ret;
6868 const char *fcn = "get_fstype()";
6870 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
6871 if (osroot == NULL) {
6872 bam_error(GET_FSTYPE_ARGS);
6873 return (NULL);
6876 mntfp = fopen(MNTTAB, "r");
6877 error = errno;
6878 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
6879 if (mntfp == NULL) {
6880 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6881 return (NULL);
6884 if (*osroot == '\0')
6885 mpref.mnt_mountp = "/";
6886 else
6887 mpref.mnt_mountp = osroot;
6889 ret = getmntany(mntfp, &mp, &mpref);
6890 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
6891 if (ret != 0) {
6892 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
6893 (void) fclose(mntfp);
6894 return (NULL);
6896 (void) fclose(mntfp);
6898 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
6899 if (mp.mnt_fstype == NULL) {
6900 bam_error(MNTTAB_FSTYPE_NULL, osroot);
6901 return (NULL);
6904 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6906 return (s_strdup(mp.mnt_fstype));
6909 static char *
6910 create_zfs_sign(char *osdev)
6912 char tmpsign[PATH_MAX];
6913 char *pool;
6914 const char *fcn = "create_zfs_sign()";
6916 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
6919 * First find the pool name
6921 pool = get_pool(osdev);
6922 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
6923 if (pool == NULL) {
6924 bam_error(GET_POOL_FAILED, osdev);
6925 return (NULL);
6928 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
6930 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
6932 free(pool);
6934 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6936 return (s_strdup(tmpsign));
6939 static char *
6940 create_new_sign(char *osdev, char *fstype)
6942 char *sign;
6943 const char *fcn = "create_new_sign()";
6945 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
6947 if (strcmp(fstype, "zfs") == 0) {
6948 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
6949 sign = create_zfs_sign(osdev);
6950 } else if (strcmp(fstype, "ufs") == 0) {
6951 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
6952 sign = create_ufs_sign();
6953 } else {
6954 bam_error(GRUBSIGN_NOTSUP, fstype);
6955 sign = NULL;
6958 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
6959 return (sign);
6962 static int
6963 set_backup_common(char *mntpt, char *sign)
6965 FILE *bfp;
6966 char backup[PATH_MAX];
6967 char tmpsign[PATH_MAX];
6968 int error;
6969 char *bdir;
6970 char *backup_dup;
6971 struct stat sb;
6972 int ret;
6973 const char *fcn = "set_backup_common()";
6975 (void) snprintf(backup, sizeof (backup), "%s%s",
6976 mntpt, GRUBSIGN_BACKUP);
6978 /* First read the backup */
6979 bfp = fopen(backup, "r");
6980 if (bfp != NULL) {
6981 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
6982 if (strcmp(tmpsign, sign) == 0) {
6983 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
6984 (void) fclose(bfp);
6985 return (0);
6988 (void) fclose(bfp);
6989 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
6990 } else {
6991 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
6995 * Didn't find the correct signature. First create
6996 * the directory if necessary.
6999 /* dirname() modifies its argument so dup it */
7000 backup_dup = s_strdup(backup);
7001 bdir = dirname(backup_dup);
7002 assert(bdir);
7004 ret = stat(bdir, &sb);
7005 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
7006 if (ret == -1) {
7007 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
7008 ret = mkdirp(bdir, DIR_PERMS);
7009 error = errno;
7010 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
7011 if (ret == -1) {
7012 bam_error(GRUBSIGN_BACKUP_MKDIRERR,
7013 GRUBSIGN_BACKUP, strerror(error));
7014 free(backup_dup);
7015 return (-1);
7018 free(backup_dup);
7021 * Open the backup in append mode to add the correct
7022 * signature;
7024 bfp = fopen(backup, "a");
7025 error = errno;
7026 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
7027 if (bfp == NULL) {
7028 bam_error(GRUBSIGN_BACKUP_OPENERR,
7029 GRUBSIGN_BACKUP, strerror(error));
7030 return (-1);
7033 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
7035 ret = fputs(tmpsign, bfp);
7036 error = errno;
7037 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
7038 if (ret != strlen(tmpsign)) {
7039 bam_error(GRUBSIGN_BACKUP_WRITEERR,
7040 GRUBSIGN_BACKUP, strerror(error));
7041 (void) fclose(bfp);
7042 return (-1);
7045 (void) fclose(bfp);
7047 if (bam_verbose)
7048 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
7050 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7052 return (0);
7055 static int
7056 set_backup_ufs(char *osroot, char *sign)
7058 const char *fcn = "set_backup_ufs()";
7060 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
7061 return (set_backup_common(osroot, sign));
7064 static int
7065 set_backup_zfs(char *osdev, char *sign)
7067 char *pool;
7068 char *mntpt;
7069 zfs_mnted_t mnted;
7070 int ret;
7071 const char *fcn = "set_backup_zfs()";
7073 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
7075 pool = get_pool(osdev);
7076 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7077 if (pool == NULL) {
7078 bam_error(GET_POOL_FAILED, osdev);
7079 return (-1);
7082 mntpt = mount_top_dataset(pool, &mnted);
7083 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7084 if (mntpt == NULL) {
7085 bam_error(FAIL_MNT_TOP_DATASET, pool);
7086 free(pool);
7087 return (-1);
7090 ret = set_backup_common(mntpt, sign);
7092 (void) umount_top_dataset(pool, mnted, mntpt);
7094 free(pool);
7096 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7097 if (ret == 0) {
7098 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7099 } else {
7100 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7103 return (ret);
7106 static int
7107 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7109 const char *fcn = "set_backup()";
7110 int ret;
7112 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7114 if (strcmp(fstype, "ufs") == 0) {
7115 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
7116 ret = set_backup_ufs(osroot, sign);
7117 } else if (strcmp(fstype, "zfs") == 0) {
7118 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
7119 ret = set_backup_zfs(osdev, sign);
7120 } else {
7121 bam_error(GRUBSIGN_NOTSUP, fstype);
7122 ret = -1;
7125 if (ret == 0) {
7126 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7127 } else {
7128 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7131 return (ret);
7134 static int
7135 set_primary_common(char *mntpt, char *sign)
7137 char signfile[PATH_MAX];
7138 char signdir[PATH_MAX];
7139 struct stat sb;
7140 int fd;
7141 int error;
7142 int ret;
7143 const char *fcn = "set_primary_common()";
7145 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7146 mntpt, GRUBSIGN_DIR, sign);
7148 if (stat(signfile, &sb) != -1) {
7149 if (bam_verbose)
7150 bam_print(PRIMARY_SIGN_EXISTS, sign);
7151 return (0);
7152 } else {
7153 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
7156 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
7157 mntpt, GRUBSIGN_DIR);
7159 if (stat(signdir, &sb) == -1) {
7160 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
7161 ret = mkdirp(signdir, DIR_PERMS);
7162 error = errno;
7163 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7164 if (ret == -1) {
7165 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
7166 return (-1);
7170 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7171 error = errno;
7172 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7173 if (fd == -1) {
7174 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
7175 return (-1);
7178 ret = fsync(fd);
7179 error = errno;
7180 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7181 if (ret != 0) {
7182 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
7185 (void) close(fd);
7187 if (bam_verbose)
7188 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
7190 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7192 return (0);
7195 static int
7196 set_primary_ufs(char *osroot, char *sign)
7198 const char *fcn = "set_primary_ufs()";
7200 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
7201 return (set_primary_common(osroot, sign));
7204 static int
7205 set_primary_zfs(char *osdev, char *sign)
7207 char *pool;
7208 char *mntpt;
7209 zfs_mnted_t mnted;
7210 int ret;
7211 const char *fcn = "set_primary_zfs()";
7213 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
7215 pool = get_pool(osdev);
7216 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7217 if (pool == NULL) {
7218 bam_error(GET_POOL_FAILED, osdev);
7219 return (-1);
7222 /* Pool name must exist in the sign */
7223 ret = (strstr(sign, pool) != NULL);
7224 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7225 if (ret == 0) {
7226 bam_error(POOL_SIGN_INCOMPAT, pool, sign);
7227 free(pool);
7228 return (-1);
7231 mntpt = mount_top_dataset(pool, &mnted);
7232 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7233 if (mntpt == NULL) {
7234 bam_error(FAIL_MNT_TOP_DATASET, pool);
7235 free(pool);
7236 return (-1);
7239 ret = set_primary_common(mntpt, sign);
7241 (void) umount_top_dataset(pool, mnted, mntpt);
7243 free(pool);
7245 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7246 if (ret == 0) {
7247 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7248 } else {
7249 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7252 return (ret);
7255 static int
7256 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7258 const char *fcn = "set_primary()";
7259 int ret;
7261 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7262 if (strcmp(fstype, "ufs") == 0) {
7263 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
7264 ret = set_primary_ufs(osroot, sign);
7265 } else if (strcmp(fstype, "zfs") == 0) {
7266 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
7267 ret = set_primary_zfs(osdev, sign);
7268 } else {
7269 bam_error(GRUBSIGN_NOTSUP, fstype);
7270 ret = -1;
7273 if (ret == 0) {
7274 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7275 } else {
7276 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7279 return (ret);
7282 static int
7283 ufs_add_to_sign_list(char *sign)
7285 FILE *tfp;
7286 char signline[MAXNAMELEN];
7287 char cmd[PATH_MAX];
7288 int ret;
7289 int error;
7290 const char *fcn = "ufs_add_to_sign_list()";
7292 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7293 if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7294 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7295 bam_error(INVALID_UFS_SIGN, sign);
7296 (void) unlink(UFS_SIGNATURE_LIST);
7297 return (-1);
7301 * most failures in this routine are not a fatal error
7302 * We simply unlink the /var/run file and continue
7305 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7306 error = errno;
7307 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7308 if (ret == -1) {
7309 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
7310 strerror(error));
7311 (void) unlink(UFS_SIGNATURE_LIST);
7312 return (0);
7315 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7316 error = errno;
7317 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7318 if (tfp == NULL) {
7319 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
7320 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7321 return (0);
7324 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7326 ret = fputs(signline, tfp);
7327 error = errno;
7328 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7329 if (ret != strlen(signline)) {
7330 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
7331 (void) fclose(tfp);
7332 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7333 return (0);
7336 ret = fclose(tfp);
7337 error = errno;
7338 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7339 if (ret == EOF) {
7340 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
7341 strerror(error));
7342 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7343 return (0);
7346 /* Sort the list again */
7347 (void) snprintf(cmd, sizeof (cmd),
7348 "/usr/bin/sort -u %s.tmp > %s.sorted",
7349 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7351 ret = exec_cmd(cmd, NULL);
7352 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7353 if (ret != 0) {
7354 bam_error(GRUBSIGN_SORT_FAILED);
7355 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7356 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7357 return (0);
7360 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7362 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7363 error = errno;
7364 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7365 if (ret == -1) {
7366 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
7367 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7368 return (0);
7371 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7373 return (0);
7376 static int
7377 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7379 int ret;
7380 const char *fcn = "set_signature()";
7382 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
7384 ret = set_backup(osroot, osdev, sign, fstype);
7385 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7386 if (ret == -1) {
7387 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7388 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
7389 return (-1);
7392 ret = set_primary(osroot, osdev, sign, fstype);
7393 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7395 if (ret == 0) {
7396 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7397 } else {
7398 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7399 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
7402 return (ret);
7405 char *
7406 get_grubsign(char *osroot, char *osdev)
7408 char *grubsign; /* (<sign>,#,#) */
7409 char *slice;
7410 int fdiskpart;
7411 char *sign;
7412 char *fstype;
7413 int ret;
7414 const char *fcn = "get_grubsign()";
7416 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
7417 fstype = get_fstype(osroot);
7418 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7419 if (fstype == NULL) {
7420 bam_error(GET_FSTYPE_FAILED, osroot);
7421 return (NULL);
7424 sign = find_existing_sign(osroot, osdev, fstype);
7425 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7426 if (sign == NULL) {
7427 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
7428 sign = create_new_sign(osdev, fstype);
7429 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7430 if (sign == NULL) {
7431 bam_error(GRUBSIGN_CREATE_FAIL, osdev);
7432 free(fstype);
7433 return (NULL);
7437 ret = set_signature(osroot, osdev, sign, fstype);
7438 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7439 if (ret == -1) {
7440 bam_error(GRUBSIGN_WRITE_FAIL, osdev);
7441 free(sign);
7442 free(fstype);
7443 (void) unlink(UFS_SIGNATURE_LIST);
7444 return (NULL);
7447 free(fstype);
7449 if (bam_verbose)
7450 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
7452 fdiskpart = get_partition(osdev);
7453 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7454 if (fdiskpart == PARTNO_NOTFOUND) {
7455 bam_error(FDISKPART_FAIL, osdev);
7456 free(sign);
7457 return (NULL);
7460 slice = strrchr(osdev, 's');
7462 if (fdiskpart == PARTNO_EFI) {
7463 fdiskpart = atoi(&slice[1]);
7464 slice = NULL;
7467 grubsign = s_calloc(1, MAXNAMELEN + 10);
7468 if (slice) {
7469 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7470 sign, fdiskpart, slice[1] + 'a' - '0');
7471 } else
7472 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7473 sign, fdiskpart);
7475 free(sign);
7477 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
7479 return (grubsign);
7482 static char *
7483 get_title(char *rootdir)
7485 static char title[80];
7486 char *cp = NULL;
7487 char release[PATH_MAX];
7488 FILE *fp;
7489 const char *fcn = "get_title()";
7491 /* open the /etc/release file */
7492 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7494 fp = fopen(release, "r");
7495 if (fp == NULL) {
7496 bam_error(OPEN_FAIL, release, strerror(errno));
7497 cp = NULL;
7498 goto out;
7501 /* grab first line of /etc/release */
7502 cp = s_fgets(title, sizeof (title), fp);
7503 if (cp) {
7504 while (isspace(*cp)) /* remove leading spaces */
7505 cp++;
7508 (void) fclose(fp);
7510 out:
7511 cp = cp ? cp : "Oracle Solaris";
7513 BAM_DPRINTF((D_GET_TITLE, fcn, cp));
7515 return (cp);
7518 char *
7519 get_special(char *mountp)
7521 FILE *mntfp;
7522 struct mnttab mp = {0};
7523 struct mnttab mpref = {0};
7524 int error;
7525 int ret;
7526 const char *fcn = "get_special()";
7528 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7529 if (mountp == NULL) {
7530 bam_error(GET_SPECIAL_NULL_MNTPT);
7531 return (NULL);
7534 mntfp = fopen(MNTTAB, "r");
7535 error = errno;
7536 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7537 if (mntfp == NULL) {
7538 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
7539 return (NULL);
7542 if (*mountp == '\0')
7543 mpref.mnt_mountp = "/";
7544 else
7545 mpref.mnt_mountp = mountp;
7547 ret = getmntany(mntfp, &mp, &mpref);
7548 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7549 if (ret != 0) {
7550 (void) fclose(mntfp);
7551 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
7552 return (NULL);
7554 (void) fclose(mntfp);
7556 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
7558 return (s_strdup(mp.mnt_special));
7561 static void
7562 free_physarray(char **physarray, int n)
7564 int i;
7565 const char *fcn = "free_physarray()";
7567 assert(physarray);
7568 assert(n);
7570 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
7572 for (i = 0; i < n; i++) {
7573 free(physarray[i]);
7575 free(physarray);
7577 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7580 static int
7581 zfs_get_physical(char *special, char ***physarray, int *n)
7583 char sdup[PATH_MAX];
7584 char cmd[PATH_MAX];
7585 char dsk[PATH_MAX];
7586 char *pool;
7587 filelist_t flist = {0};
7588 line_t *lp;
7589 line_t *startlp;
7590 char *comp1;
7591 int i;
7592 int ret;
7593 const char *fcn = "zfs_get_physical()";
7595 assert(special);
7597 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7599 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7600 if (special[0] == '/') {
7601 bam_error(INVALID_ZFS_SPECIAL, special);
7602 return (-1);
7605 (void) strlcpy(sdup, special, sizeof (sdup));
7607 pool = strtok(sdup, "/");
7608 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7609 if (pool == NULL) {
7610 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
7611 return (-1);
7614 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7616 ret = exec_cmd(cmd, &flist);
7617 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7618 if (ret != 0) {
7619 bam_error(ZFS_GET_POOL_STATUS, pool);
7620 return (-1);
7623 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7624 if (flist.head == NULL) {
7625 bam_error(BAD_ZPOOL_STATUS, pool);
7626 filelist_free(&flist);
7627 return (-1);
7630 for (lp = flist.head; lp; lp = lp->next) {
7631 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
7632 comp1 = strtok(lp->line, " \t");
7633 if (comp1 == NULL) {
7634 free(lp->line);
7635 lp->line = NULL;
7636 } else {
7637 comp1 = s_strdup(comp1);
7638 free(lp->line);
7639 lp->line = comp1;
7643 for (lp = flist.head; lp; lp = lp->next) {
7644 if (lp->line == NULL)
7645 continue;
7646 if (strcmp(lp->line, pool) == 0) {
7647 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
7648 break;
7652 if (lp == NULL) {
7653 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
7654 filelist_free(&flist);
7655 return (-1);
7658 startlp = lp->next;
7659 for (i = 0, lp = startlp; lp; lp = lp->next) {
7660 if (lp->line == NULL)
7661 continue;
7662 if (strcmp(lp->line, "mirror") == 0)
7663 continue;
7664 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7665 break;
7666 i++;
7667 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
7670 if (i == 0) {
7671 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
7672 filelist_free(&flist);
7673 return (-1);
7676 *n = i;
7677 *physarray = s_calloc(*n, sizeof (char *));
7678 for (i = 0, lp = startlp; lp; lp = lp->next) {
7679 if (lp->line == NULL)
7680 continue;
7681 if (strcmp(lp->line, "mirror") == 0)
7682 continue;
7683 if (strcmp(lp->line, "errors:") == 0)
7684 break;
7685 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7686 strncmp(lp->line, "/dev/rdsk/",
7687 strlen("/dev/rdsk/")) != 0) {
7688 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7689 lp->line);
7690 } else {
7691 (void) strlcpy(dsk, lp->line, sizeof (dsk));
7693 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
7694 (*physarray)[i++] = s_strdup(dsk);
7697 assert(i == *n);
7699 filelist_free(&flist);
7701 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7702 return (0);
7706 * Certain services needed to run metastat successfully may not
7707 * be enabled. Enable them now.
7710 * Checks if the specified service is online
7711 * Returns: 1 if the service is online
7712 * 0 if the service is not online
7713 * -1 on error
7715 static int
7716 is_svc_online(char *svc)
7718 char *state;
7719 const char *fcn = "is_svc_online()";
7721 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
7723 state = smf_get_state(svc);
7724 INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
7725 if (state == NULL) {
7726 bam_error(GET_SVC_STATE_ERR, svc);
7727 return (-1);
7729 BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
7731 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
7732 BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
7733 free(state);
7734 return (1);
7737 BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
7739 free(state);
7741 return (0);
7744 static int
7745 enable_svc(char *svc)
7747 int ret;
7748 int sleeptime;
7749 const char *fcn = "enable_svc()";
7751 ret = is_svc_online(svc);
7752 if (ret == -1) {
7753 bam_error(SVC_IS_ONLINE_FAILED, svc);
7754 return (-1);
7755 } else if (ret == 1) {
7756 BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
7757 return (0);
7760 /* Service is not enabled. Enable it now. */
7761 ret = smf_enable_instance(svc, 0);
7762 INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
7763 if (ret != 0) {
7764 bam_error(ENABLE_SVC_FAILED, svc);
7765 return (-1);
7768 BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
7770 sleeptime = 0;
7771 do {
7772 ret = is_svc_online(svc);
7773 INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
7774 INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
7775 INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
7776 if (ret == -1) {
7777 bam_error(ERR_SVC_GET_ONLINE, svc);
7778 return (-1);
7779 } else if (ret == 1) {
7780 BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
7781 return (1);
7783 (void) sleep(1);
7784 } while (++sleeptime < 60);
7786 bam_error(TIMEOUT_ENABLE_SVC, svc);
7788 return (-1);
7791 static int
7792 ufs_get_physical(char *special, char ***physarray, int *n)
7794 char cmd[PATH_MAX];
7795 char *shortname;
7796 filelist_t flist = {0};
7797 char *meta;
7798 char *type;
7799 char *comp1;
7800 char *comp2;
7801 char *comp3;
7802 char *comp4;
7803 int i;
7804 line_t *lp;
7805 int ret;
7806 char *svc;
7807 const char *fcn = "ufs_get_physical()";
7809 assert(special);
7811 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7813 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
7814 bam_error(UFS_GET_PHYS_NOT_SVM, special);
7815 return (-1);
7818 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
7819 shortname = special + strlen("/dev/md/dsk/");
7820 } else if (strncmp(special, "/dev/md/rdsk/",
7821 strlen("/dev/md/rdsk/")) == 0) {
7822 shortname = special + strlen("/dev/md/rdsk");
7823 } else {
7824 bam_error(UFS_GET_PHYS_INVALID_SVM, special);
7825 return (-1);
7828 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
7830 svc = "network/rpc/meta:default";
7831 if (enable_svc(svc) == -1) {
7832 bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
7835 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
7837 ret = exec_cmd(cmd, &flist);
7838 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
7839 if (ret != 0) {
7840 bam_error(UFS_SVM_METASTAT_ERR, shortname);
7841 return (-1);
7844 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
7845 if (flist.head == NULL) {
7846 bam_error(BAD_UFS_SVM_METASTAT, shortname);
7847 filelist_free(&flist);
7848 return (-1);
7852 * Check if not a mirror. We only parse a single metadevice
7853 * if not a mirror
7855 meta = strtok(flist.head->line, " \t");
7856 type = strtok(NULL, " \t");
7857 if (meta == NULL || type == NULL) {
7858 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
7859 filelist_free(&flist);
7860 return (-1);
7862 if (strcmp(type, "-m") != 0) {
7863 comp1 = strtok(NULL, " \t");
7864 comp2 = strtok(NULL, " \t");
7865 if (comp1 == NULL || comp2 != NULL) {
7866 bam_error(INVALID_UFS_SVM_METASTAT, shortname);
7867 filelist_free(&flist);
7868 return (-1);
7870 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
7871 *physarray = s_calloc(1, sizeof (char *));
7872 (*physarray)[0] = s_strdup(comp1);
7873 *n = 1;
7874 filelist_free(&flist);
7875 return (0);
7879 * Okay we have a mirror. Everything after the first line
7880 * is a submirror
7882 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7883 if (strstr(lp->line, "/dev/dsk/") == NULL &&
7884 strstr(lp->line, "/dev/rdsk/") == NULL) {
7885 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
7886 filelist_free(&flist);
7887 return (-1);
7889 i++;
7892 *physarray = s_calloc(i, sizeof (char *));
7893 *n = i;
7895 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7896 comp1 = strtok(lp->line, " \t");
7897 comp2 = strtok(NULL, " \t");
7898 comp3 = strtok(NULL, " \t");
7899 comp4 = strtok(NULL, " \t");
7901 if (comp3 == NULL || comp4 == NULL ||
7902 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7903 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
7904 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
7905 filelist_free(&flist);
7906 free_physarray(*physarray, *n);
7907 return (-1);
7910 (*physarray)[i++] = s_strdup(comp4);
7913 assert(i == *n);
7915 filelist_free(&flist);
7917 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7918 return (0);
7921 static int
7922 get_physical(char *menu_root, char ***physarray, int *n)
7924 char *special;
7925 int ret;
7926 const char *fcn = "get_physical()";
7928 assert(menu_root);
7929 assert(physarray);
7930 assert(n);
7932 *physarray = NULL;
7933 *n = 0;
7935 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
7937 /* First get the device special file from /etc/mnttab */
7938 special = get_special(menu_root);
7939 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7940 if (special == NULL) {
7941 bam_error(GET_SPECIAL_NULL, menu_root);
7942 return (-1);
7945 /* If already a physical device nothing to do */
7946 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7947 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7948 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
7949 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7950 *physarray = s_calloc(1, sizeof (char *));
7951 (*physarray)[0] = special;
7952 *n = 1;
7953 return (0);
7956 if (is_zfs(menu_root)) {
7957 ret = zfs_get_physical(special, physarray, n);
7958 } else if (is_ufs(menu_root)) {
7959 ret = ufs_get_physical(special, physarray, n);
7960 } else {
7961 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
7962 ret = -1;
7965 free(special);
7967 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7968 if (ret == -1) {
7969 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7970 } else {
7971 int i;
7972 assert (*n > 0);
7973 for (i = 0; i < *n; i++) {
7974 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
7978 return (ret);
7981 static int
7982 is_bootdisk(char *osroot, char *physical)
7984 int ret;
7985 char *grubroot;
7986 char *bootp;
7987 const char *fcn = "is_bootdisk()";
7989 assert(osroot);
7990 assert(physical);
7992 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
7994 bootp = strstr(physical, "p0:boot");
7995 if (bootp)
7996 *bootp = '\0';
7998 * We just want the BIOS mapping for menu disk.
7999 * Don't pass menu_root to get_grubroot() as the
8000 * check that it is used for is not relevant here.
8001 * The osroot is immaterial as well - it is only used to
8002 * to find create_diskmap script. Everything hinges on
8003 * "physical"
8005 grubroot = get_grubroot(osroot, physical, NULL);
8007 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
8008 if (grubroot == NULL) {
8009 if (bam_verbose)
8010 bam_error(NO_GRUBROOT_FOR_DISK, physical);
8011 return (0);
8013 ret = grubroot[3] == '0';
8014 free(grubroot);
8016 BAM_DPRINTF((D_RETURN_RET, fcn, ret));
8018 return (ret);
8022 * Check if menu is on the boot device
8023 * Return 0 (false) on error
8025 static int
8026 menu_on_bootdisk(char *osroot, char *menu_root)
8028 char **physarray;
8029 int ret;
8030 int n;
8031 int i;
8032 int on_bootdisk;
8033 const char *fcn = "menu_on_bootdisk()";
8035 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
8037 ret = get_physical(menu_root, &physarray, &n);
8038 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
8039 if (ret != 0) {
8040 bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
8041 return (0);
8044 assert(physarray);
8045 assert(n > 0);
8047 on_bootdisk = 0;
8048 for (i = 0; i < n; i++) {
8049 assert(strncmp(physarray[i], "/dev/dsk/",
8050 strlen("/dev/dsk/")) == 0 ||
8051 strncmp(physarray[i], "/dev/rdsk/",
8052 strlen("/dev/rdsk/")) == 0);
8054 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
8055 if (is_bootdisk(osroot, physarray[i])) {
8056 on_bootdisk = 1;
8057 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
8061 free_physarray(physarray, n);
8063 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8064 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8065 if (on_bootdisk) {
8066 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8067 } else {
8068 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8071 return (on_bootdisk);
8074 void
8075 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8077 const char *fcn = "bam_add_line()";
8079 assert(mp);
8080 assert(entry);
8081 assert(prev);
8082 assert(lp);
8084 lp->next = prev->next;
8085 if (prev->next) {
8086 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
8087 prev->next->prev = lp;
8088 } else {
8089 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
8091 prev->next = lp;
8092 lp->prev = prev;
8094 if (entry->end == prev) {
8095 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
8096 entry->end = lp;
8098 if (mp->end == prev) {
8099 assert(lp->next == NULL);
8100 mp->end = lp;
8101 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
8106 * look for matching bootadm entry with specified parameters
8107 * Here are the rules (based on existing usage):
8108 * - If title is specified, match on title only
8109 * - Else, match on root/findroot, kernel, and module.
8110 * Note that, if root_opt is non-zero, the absence of
8111 * root line is considered a match.
8113 static entry_t *
8114 find_boot_entry(
8115 menu_t *mp,
8116 char *title,
8117 char *kernel,
8118 char *findroot,
8119 char *root,
8120 char *module,
8121 int root_opt,
8122 int *entry_num)
8124 int i;
8125 line_t *lp;
8126 entry_t *ent;
8127 const char *fcn = "find_boot_entry()";
8129 if (entry_num)
8130 *entry_num = BAM_ERROR;
8132 /* find matching entry */
8133 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8134 lp = ent->start;
8136 /* first line of entry must be bootadm comment */
8137 lp = ent->start;
8138 if (lp->flags != BAM_COMMENT ||
8139 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8140 continue;
8143 /* advance to title line */
8144 lp = lp->next;
8145 if (title) {
8146 if (lp->flags == BAM_TITLE && lp->arg &&
8147 strcmp(lp->arg, title) == 0) {
8148 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
8149 break;
8151 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
8152 continue; /* check title only */
8155 lp = lp->next; /* advance to root line */
8156 if (lp == NULL) {
8157 continue;
8158 } else if (lp->cmd != NULL &&
8159 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8160 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8161 findroot = NULL);
8162 if (findroot == NULL) {
8163 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
8164 fcn, lp->arg));
8165 continue;
8167 /* findroot command found, try match */
8168 if (strcmp(lp->arg, findroot) != 0) {
8169 BAM_DPRINTF((D_NOMATCH_FINDROOT,
8170 fcn, findroot, lp->arg));
8171 continue;
8173 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
8174 lp = lp->next; /* advance to kernel line */
8175 } else if (lp->cmd != NULL &&
8176 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8177 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8178 if (root == NULL) {
8179 BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
8180 fcn, lp->arg));
8181 continue;
8183 /* root cmd found, try match */
8184 if (strcmp(lp->arg, root) != 0) {
8185 BAM_DPRINTF((D_NOMATCH_ROOT,
8186 fcn, root, lp->arg));
8187 continue;
8189 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
8190 lp = lp->next; /* advance to kernel line */
8191 } else {
8192 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8193 root_opt = 0);
8194 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8195 root_opt = 1);
8196 /* no root command, see if root is optional */
8197 if (root_opt == 0) {
8198 BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
8199 continue;
8201 BAM_DPRINTF((D_ROOT_OPT, fcn));
8204 if (lp == NULL || lp->next == NULL) {
8205 continue;
8208 if (kernel &&
8209 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8210 if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8211 !(ent->flags & BAM_ENTRY_DBOOT) ||
8212 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8213 continue;
8215 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8218 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
8221 * Check for matching module entry (failsafe or normal).
8222 * If it fails to match, we go around the loop again.
8223 * For xpv entries, there are two module lines, so we
8224 * do the check twice.
8226 lp = lp->next; /* advance to module line */
8227 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8228 (((lp = lp->next) != NULL) &&
8229 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8230 /* match found */
8231 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
8232 break;
8235 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8236 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8237 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8238 ent->flags |= BAM_ENTRY_UPGFSMODULE;
8239 break;
8244 if (ent && entry_num) {
8245 *entry_num = i;
8248 if (ent) {
8249 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8250 } else {
8251 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
8253 return (ent);
8256 static int
8257 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8258 char *kernel, char *mod_kernel, char *module, int root_opt)
8260 int i;
8261 int change_kernel = 0;
8262 entry_t *ent;
8263 line_t *lp;
8264 line_t *tlp;
8265 char linebuf[BAM_MAXLINE];
8266 const char *fcn = "update_boot_entry()";
8268 /* note: don't match on title, it's updated on upgrade */
8269 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8270 root_opt, &i);
8271 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8273 * We may be upgrading a kernel from multiboot to
8274 * directboot. Look for a multiboot entry. A multiboot
8275 * entry will not have a findroot line.
8277 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8278 MULTIBOOT_ARCHIVE, root_opt, &i);
8279 if (ent != NULL) {
8280 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
8281 change_kernel = 1;
8283 } else if (ent) {
8284 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
8287 if (ent == NULL) {
8288 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
8289 return (add_boot_entry(mp, title, findroot,
8290 kernel, mod_kernel, module, NULL));
8293 /* replace title of existing entry and update findroot line */
8294 lp = ent->start;
8295 lp = lp->next; /* title line */
8296 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8297 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8298 free(lp->arg);
8299 free(lp->line);
8300 lp->arg = s_strdup(title);
8301 lp->line = s_strdup(linebuf);
8302 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
8304 tlp = lp; /* title line */
8305 lp = lp->next; /* root line */
8307 /* if no root or findroot command, create a new line_t */
8308 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8309 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8310 lp = s_calloc(1, sizeof (line_t));
8311 bam_add_line(mp, ent, tlp, lp);
8312 } else {
8313 if (lp->cmd != NULL)
8314 free(lp->cmd);
8316 free(lp->sep);
8317 free(lp->arg);
8318 free(lp->line);
8321 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8322 lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8323 lp->arg = s_strdup(findroot);
8324 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8325 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8326 lp->line = s_strdup(linebuf);
8327 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
8329 /* kernel line */
8330 lp = lp->next;
8332 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8333 char *params = NULL;
8335 params = strstr(lp->line, "-s");
8336 if (params != NULL)
8337 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8338 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8339 kernel, params+2);
8340 else
8341 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8342 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8343 kernel);
8345 if (lp->cmd != NULL)
8346 free(lp->cmd);
8348 free(lp->arg);
8349 free(lp->line);
8350 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8351 lp->arg = s_strdup(strstr(linebuf, "/"));
8352 lp->line = s_strdup(linebuf);
8353 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8354 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd));
8357 if (change_kernel) {
8359 * We're upgrading from multiboot to directboot.
8361 if (lp->cmd != NULL &&
8362 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8363 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8364 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8365 kernel);
8366 free(lp->cmd);
8367 free(lp->arg);
8368 free(lp->line);
8369 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8370 lp->arg = s_strdup(kernel);
8371 lp->line = s_strdup(linebuf);
8372 lp = lp->next;
8373 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
8375 if (lp->cmd != NULL &&
8376 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8377 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8378 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8379 module);
8380 free(lp->cmd);
8381 free(lp->arg);
8382 free(lp->line);
8383 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8384 lp->arg = s_strdup(module);
8385 lp->line = s_strdup(linebuf);
8386 lp = lp->next;
8387 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8391 /* module line */
8392 lp = lp->next;
8394 if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8395 if (lp->cmd != NULL &&
8396 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8397 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8398 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8399 module);
8400 free(lp->cmd);
8401 free(lp->arg);
8402 free(lp->line);
8403 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8404 lp->arg = s_strdup(module);
8405 lp->line = s_strdup(linebuf);
8406 lp = lp->next;
8407 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8408 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8412 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8413 return (i);
8417 root_optional(char *osroot, char *menu_root)
8419 char *ospecial;
8420 char *mspecial;
8421 char *slash;
8422 int root_opt;
8423 int ret1;
8424 int ret2;
8425 const char *fcn = "root_optional()";
8427 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
8430 * For all filesystems except ZFS, a straight compare of osroot
8431 * and menu_root will tell us if root is optional.
8432 * For ZFS, the situation is complicated by the fact that
8433 * menu_root and osroot are always different
8435 ret1 = is_zfs(osroot);
8436 ret2 = is_zfs(menu_root);
8437 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8438 if (!ret1 || !ret2) {
8439 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
8440 root_opt = (strcmp(osroot, menu_root) == 0);
8441 goto out;
8444 ospecial = get_special(osroot);
8445 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8446 if (ospecial == NULL) {
8447 bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
8448 return (0);
8450 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
8452 mspecial = get_special(menu_root);
8453 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8454 if (mspecial == NULL) {
8455 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
8456 free(ospecial);
8457 return (0);
8459 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
8461 slash = strchr(ospecial, '/');
8462 if (slash)
8463 *slash = '\0';
8464 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
8466 root_opt = (strcmp(ospecial, mspecial) == 0);
8468 free(ospecial);
8469 free(mspecial);
8471 out:
8472 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8473 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8474 if (root_opt) {
8475 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8476 } else {
8477 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8480 return (root_opt);
8483 /*ARGSUSED*/
8484 static error_t
8485 update_entry(menu_t *mp, char *menu_root, char *osdev)
8487 int entry;
8488 char *grubsign;
8489 char *grubroot;
8490 char *title;
8491 char osroot[PATH_MAX];
8492 char *failsafe_kernel = NULL;
8493 struct stat sbuf;
8494 char failsafe[256];
8495 char failsafe_64[256];
8496 int ret;
8497 const char *fcn = "update_entry()";
8499 assert(mp);
8500 assert(menu_root);
8501 assert(osdev);
8502 assert(bam_root);
8504 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
8506 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8508 title = get_title(osroot);
8509 assert(title);
8511 grubsign = get_grubsign(osroot, osdev);
8512 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8513 if (grubsign == NULL) {
8514 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
8515 return (BAM_ERROR);
8519 * It is not a fatal error if get_grubroot() fails
8520 * We no longer rely on biosdev to populate the
8521 * menu
8523 grubroot = get_grubroot(osroot, osdev, menu_root);
8524 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8525 if (grubroot) {
8526 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
8527 fcn, osroot, osdev, menu_root));
8528 } else {
8529 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
8530 fcn, osroot, osdev, menu_root));
8533 /* add the entry for normal Solaris */
8534 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8535 bam_direct = BAM_DIRECT_MULTIBOOT);
8536 if (bam_direct == BAM_DIRECT_DBOOT) {
8537 entry = update_boot_entry(mp, title, grubsign, grubroot,
8538 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8539 NULL, DIRECT_BOOT_ARCHIVE,
8540 root_optional(osroot, menu_root));
8541 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
8542 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8543 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8544 grubroot, XEN_MENU, bam_zfs ?
8545 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8546 DIRECT_BOOT_ARCHIVE,
8547 root_optional(osroot, menu_root));
8548 BAM_DPRINTF((D_UPDATED_HV_ENTRY,
8549 fcn, bam_zfs, grubsign));
8551 } else {
8552 entry = update_boot_entry(mp, title, grubsign, grubroot,
8553 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8554 root_optional(osroot, menu_root));
8556 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
8560 * Add the entry for failsafe archive. On a bfu'd system, the
8561 * failsafe may be different than the installed kernel.
8563 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8564 osroot, FAILSAFE_ARCHIVE_32);
8565 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8566 osroot, FAILSAFE_ARCHIVE_64);
8569 * Check if at least one of the two archives exists
8570 * Using $ISADIR as the default line, we have an entry which works
8571 * for both the cases.
8574 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8576 /* Figure out where the kernel line should point */
8577 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8578 DIRECT_BOOT_FAILSAFE_32);
8579 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8580 osroot, DIRECT_BOOT_FAILSAFE_64);
8581 if (stat(failsafe, &sbuf) == 0 ||
8582 stat(failsafe_64, &sbuf) == 0) {
8583 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8584 } else {
8585 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8586 osroot, MULTI_BOOT_FAILSAFE);
8587 if (stat(failsafe, &sbuf) == 0) {
8588 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8591 if (failsafe_kernel != NULL) {
8592 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8593 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8594 root_optional(osroot, menu_root));
8595 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
8596 failsafe_kernel));
8599 free(grubroot);
8601 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8602 if (entry == BAM_ERROR) {
8603 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
8604 free(grubsign);
8605 return (BAM_ERROR);
8607 free(grubsign);
8609 update_numbering(mp);
8610 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8611 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8612 if (ret == BAM_ERROR) {
8613 bam_error(SET_DEFAULT_FAILED, entry);
8615 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8616 return (BAM_WRITE);
8619 static void
8620 save_default_entry(menu_t *mp, const char *which)
8622 int lineNum;
8623 int entryNum;
8624 int entry = 0; /* default is 0 */
8625 char linebuf[BAM_MAXLINE];
8626 line_t *lp = mp->curdefault;
8627 const char *fcn = "save_default_entry()";
8629 if (mp->start) {
8630 lineNum = mp->end->lineNum;
8631 entryNum = mp->end->entryNum;
8632 } else {
8633 lineNum = LINE_INIT;
8634 entryNum = ENTRY_INIT;
8637 if (lp)
8638 entry = s_strtol(lp->arg);
8640 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8641 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
8642 line_parser(mp, linebuf, &lineNum, &entryNum);
8643 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
8646 static void
8647 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8649 int entry;
8650 char *str;
8651 const char *fcn = "restore_default_entry()";
8653 if (lp == NULL) {
8654 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
8655 return; /* nothing to restore */
8658 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
8660 str = lp->arg + strlen(which);
8661 entry = s_strtol(str);
8662 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8664 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
8666 /* delete saved old default line */
8667 unlink_line(mp, lp);
8668 line_free(lp);
8672 * This function is for supporting reboot with args.
8673 * The opt value can be:
8674 * NULL delete temp entry, if present
8675 * entry=<n> switches default entry to <n>
8676 * else treated as boot-args and setup a temperary menu entry
8677 * and make it the default
8678 * Note that we are always rebooting the current OS instance
8679 * so osroot == / always.
8681 #define REBOOT_TITLE "Solaris_reboot_transient"
8683 /*ARGSUSED*/
8684 static error_t
8685 update_temp(menu_t *mp, char *dummy, char *opt)
8687 int entry;
8688 char *osdev;
8689 char *fstype;
8690 char *sign;
8691 char *opt_ptr;
8692 char *path;
8693 char kernbuf[BUFSIZ];
8694 char args_buf[BUFSIZ];
8695 char signbuf[PATH_MAX];
8696 int ret;
8697 const char *fcn = "update_temp()";
8699 assert(mp);
8700 assert(dummy == NULL);
8702 /* opt can be NULL */
8703 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
8704 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
8706 if (bam_alt_root || bam_rootlen != 1 ||
8707 strcmp(bam_root, "/") != 0 ||
8708 strcmp(rootbuf, "/") != 0) {
8709 bam_error(ALT_ROOT_INVALID, bam_root);
8710 return (BAM_ERROR);
8713 /* If no option, delete exiting reboot menu entry */
8714 if (opt == NULL) {
8715 entry_t *ent;
8716 BAM_DPRINTF((D_OPT_NULL, fcn));
8717 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8718 NULL, NULL, 0, &entry);
8719 if (ent == NULL) { /* not found is ok */
8720 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
8721 return (BAM_SUCCESS);
8723 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8724 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8725 mp->olddefault = NULL;
8726 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
8727 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8728 return (BAM_WRITE);
8731 /* if entry= is specified, set the default entry */
8732 if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8733 int entryNum = s_strtol(opt + strlen("entry="));
8734 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
8735 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8736 /* this is entry=# option */
8737 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8738 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
8739 return (ret);
8740 } else {
8741 bam_error(SET_DEFAULT_FAILED, entryNum);
8742 return (BAM_ERROR);
8747 * add a new menu entry based on opt and make it the default
8750 fstype = get_fstype("/");
8751 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8752 if (fstype == NULL) {
8753 bam_error(REBOOT_FSTYPE_FAILED);
8754 return (BAM_ERROR);
8757 osdev = get_special("/");
8758 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8759 if (osdev == NULL) {
8760 free(fstype);
8761 bam_error(REBOOT_SPECIAL_FAILED);
8762 return (BAM_ERROR);
8765 sign = find_existing_sign("/", osdev, fstype);
8766 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8767 if (sign == NULL) {
8768 free(fstype);
8769 free(osdev);
8770 bam_error(REBOOT_SIGN_FAILED);
8771 return (BAM_ERROR);
8774 free(osdev);
8775 (void) strlcpy(signbuf, sign, sizeof (signbuf));
8776 free(sign);
8778 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8779 strchr(signbuf, ')') == NULL);
8782 * There is no alternate root while doing reboot with args
8783 * This version of bootadm is only delivered with a DBOOT
8784 * version of Solaris.
8786 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8787 if (bam_direct != BAM_DIRECT_DBOOT) {
8788 free(fstype);
8789 bam_error(REBOOT_DIRECT_FAILED);
8790 return (BAM_ERROR);
8793 /* add an entry for Solaris reboot */
8794 if (opt[0] == '-') {
8795 /* It's an option - first see if boot-file is set */
8796 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8797 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8798 if (ret != BAM_SUCCESS) {
8799 free(fstype);
8800 bam_error(REBOOT_GET_KERNEL_FAILED);
8801 return (BAM_ERROR);
8803 if (kernbuf[0] == '\0')
8804 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8805 sizeof (kernbuf));
8807 * If this is a zfs file system and kernbuf does not
8808 * have "-B $ZFS-BOOTFS" string yet, add it.
8810 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8811 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8812 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8814 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8815 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8816 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
8817 } else if (opt[0] == '/') {
8818 /* It's a full path, so write it out. */
8819 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8822 * If someone runs:
8824 * # eeprom boot-args='-kd'
8825 * # reboot /platform/i86pc/kernel/unix
8827 * we want to use the boot-args as part of the boot
8828 * line. On the other hand, if someone runs:
8830 * # reboot "/platform/i86pc/kernel/unix -kd"
8832 * we don't need to mess with boot-args. If there's
8833 * no space in the options string, assume we're in the
8834 * first case.
8836 if (strchr(opt, ' ') == NULL) {
8837 ret = get_kernel(mp, ARGS_CMD, args_buf,
8838 sizeof (args_buf));
8839 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8840 if (ret != BAM_SUCCESS) {
8841 free(fstype);
8842 bam_error(REBOOT_GET_ARGS_FAILED);
8843 return (BAM_ERROR);
8846 if (args_buf[0] != '\0') {
8847 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8848 (void) strlcat(kernbuf, args_buf,
8849 sizeof (kernbuf));
8852 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
8853 } else {
8855 * It may be a partial path, or it may be a partial
8856 * path followed by options. Assume that only options
8857 * follow a space. If someone sends us a kernel path
8858 * that includes a space, they deserve to be broken.
8860 opt_ptr = strchr(opt, ' ');
8861 if (opt_ptr != NULL) {
8862 *opt_ptr = '\0';
8865 path = expand_path(opt);
8866 if (path != NULL) {
8867 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8868 free(path);
8871 * If there were options given, use those.
8872 * Otherwise, copy over the default options.
8874 if (opt_ptr != NULL) {
8875 /* Restore the space in opt string */
8876 *opt_ptr = ' ';
8877 (void) strlcat(kernbuf, opt_ptr,
8878 sizeof (kernbuf));
8879 } else {
8880 ret = get_kernel(mp, ARGS_CMD, args_buf,
8881 sizeof (args_buf));
8882 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8883 ret = BAM_ERROR);
8884 if (ret != BAM_SUCCESS) {
8885 free(fstype);
8886 bam_error(REBOOT_GET_ARGS_FAILED);
8887 return (BAM_ERROR);
8890 if (args_buf[0] != '\0') {
8891 (void) strlcat(kernbuf, " ",
8892 sizeof (kernbuf));
8893 (void) strlcat(kernbuf,
8894 args_buf, sizeof (kernbuf));
8897 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
8898 } else {
8899 free(fstype);
8900 bam_error(UNKNOWN_KERNEL, opt);
8901 bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
8902 return (BAM_ERROR);
8905 free(fstype);
8906 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8907 NULL, NULL, NULL);
8908 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8909 if (entry == BAM_ERROR) {
8910 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
8911 return (BAM_ERROR);
8914 save_default_entry(mp, BAM_OLDDEF);
8915 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8916 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8917 if (ret == BAM_ERROR) {
8918 bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
8920 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8921 return (BAM_WRITE);
8924 error_t
8925 set_global(menu_t *mp, char *globalcmd, int val)
8927 line_t *lp;
8928 line_t *found;
8929 line_t *last;
8930 char *cp;
8931 char *str;
8932 char prefix[BAM_MAXLINE];
8933 size_t len;
8934 const char *fcn = "set_global()";
8936 assert(mp);
8937 assert(globalcmd);
8939 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
8940 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
8941 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
8942 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
8943 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
8944 (void) snprintf(prefix, sizeof (prefix), "%d", val);
8945 bam_error(INVALID_ENTRY, prefix);
8946 return (BAM_ERROR);
8950 found = last = NULL;
8951 for (lp = mp->start; lp; lp = lp->next) {
8952 if (lp->flags != BAM_GLOBAL)
8953 continue;
8955 last = lp; /* track the last global found */
8957 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
8958 if (lp->cmd == NULL) {
8959 bam_error(NO_CMD, lp->lineNum);
8960 continue;
8962 if (strcmp(globalcmd, lp->cmd) != 0)
8963 continue;
8965 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
8967 if (found) {
8968 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
8970 found = lp;
8973 if (found == NULL) {
8974 lp = s_calloc(1, sizeof (line_t));
8975 if (last == NULL) {
8976 lp->next = mp->start;
8977 mp->start = lp;
8978 mp->end = (mp->end) ? mp->end : lp;
8979 } else {
8980 lp->next = last->next;
8981 last->next = lp;
8982 if (lp->next == NULL)
8983 mp->end = lp;
8985 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
8986 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
8987 len += 10; /* val < 10 digits */
8988 lp->line = s_calloc(1, len);
8989 (void) snprintf(lp->line, len, "%s%s%d",
8990 globalcmd, menu_cmds[SEP_CMD], val);
8991 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
8992 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8993 return (BAM_WRITE);
8997 * We are changing an existing entry. Retain any prefix whitespace,
8998 * but overwrite everything else. This preserves tabs added for
8999 * readability.
9001 str = found->line;
9002 cp = prefix;
9003 while (*str == ' ' || *str == '\t')
9004 *(cp++) = *(str++);
9005 *cp = '\0'; /* Terminate prefix */
9006 len = strlen(prefix) + strlen(globalcmd);
9007 len += strlen(menu_cmds[SEP_CMD]) + 10;
9009 free(found->line);
9010 found->line = s_calloc(1, len);
9011 (void) snprintf(found->line, len,
9012 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
9014 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
9015 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9016 return (BAM_WRITE); /* need a write to menu */
9020 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
9021 * expand it to a full unix path. The calling function is expected to
9022 * output a message if an error occurs and NULL is returned.
9024 static char *
9025 expand_path(const char *partial_path)
9027 int new_path_len;
9028 char *new_path;
9029 char new_path2[PATH_MAX];
9030 struct stat sb;
9031 const char *fcn = "expand_path()";
9033 new_path_len = strlen(partial_path) + 64;
9034 new_path = s_calloc(1, new_path_len);
9036 /* First, try the simplest case - something like "kernel/unix" */
9037 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
9038 partial_path);
9039 if (stat(new_path, &sb) == 0) {
9040 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
9041 return (new_path);
9044 if (strcmp(partial_path, "kmdb") == 0) {
9045 (void) snprintf(new_path, new_path_len, "%s -k",
9046 DIRECT_BOOT_KERNEL);
9047 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
9048 return (new_path);
9052 * We've quickly reached unsupported usage. Try once more to
9053 * see if we were just given a glom name.
9055 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9056 partial_path);
9057 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9058 partial_path);
9059 if (stat(new_path, &sb) == 0) {
9060 if (stat(new_path2, &sb) == 0) {
9062 * We matched both, so we actually
9063 * want to write the $ISADIR version.
9065 (void) snprintf(new_path, new_path_len,
9066 "/platform/i86pc/kernel/%s/$ISADIR/unix",
9067 partial_path);
9069 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
9070 return (new_path);
9073 free(new_path);
9074 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9075 return (NULL);
9079 * The kernel cmd and arg have been changed, so
9080 * check whether the archive line needs to change.
9082 static void
9083 set_archive_line(entry_t *entryp, line_t *kernelp)
9085 line_t *lp = entryp->start;
9086 char *new_archive;
9087 menu_cmd_t m_cmd;
9088 const char *fcn = "set_archive_line()";
9090 for (; lp != NULL; lp = lp->next) {
9091 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9092 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9093 break;
9096 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9097 if (lp == entryp->end) {
9098 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
9099 entryp->entryNum));
9100 return;
9103 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9104 if (lp == NULL) {
9105 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
9106 return;
9109 if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9110 new_archive = DIRECT_BOOT_ARCHIVE;
9111 m_cmd = MODULE_DOLLAR_CMD;
9112 } else if (strstr(kernelp->arg, "amd64") != NULL) {
9113 new_archive = DIRECT_BOOT_ARCHIVE_64;
9114 m_cmd = MODULE_CMD;
9115 } else {
9116 new_archive = DIRECT_BOOT_ARCHIVE_32;
9117 m_cmd = MODULE_CMD;
9120 if (strcmp(lp->arg, new_archive) == 0) {
9121 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
9122 return;
9125 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9126 free(lp->cmd);
9127 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9130 free(lp->arg);
9131 lp->arg = s_strdup(new_archive);
9132 update_line(lp);
9133 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
9137 * Title for an entry to set properties that once went in bootenv.rc.
9139 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
9142 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9143 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
9144 * string, reset the value to the default. If path is a non-zero-length
9145 * string, set the kernel or arguments.
9147 static error_t
9148 get_set_kernel(
9149 menu_t *mp,
9150 menu_cmd_t optnum,
9151 char *path,
9152 char *buf,
9153 size_t bufsize)
9155 int entryNum;
9156 int rv = BAM_SUCCESS;
9157 int free_new_path = 0;
9158 entry_t *entryp;
9159 line_t *ptr;
9160 line_t *kernelp;
9161 char *new_arg;
9162 char *old_args;
9163 char *space;
9164 char *new_path;
9165 char old_space;
9166 size_t old_kernel_len;
9167 size_t new_str_len;
9168 char *fstype;
9169 char *osdev;
9170 char *sign;
9171 char signbuf[PATH_MAX];
9172 int ret;
9173 const char *fcn = "get_set_kernel()";
9175 assert(bufsize > 0);
9177 ptr = kernelp = NULL;
9178 new_arg = old_args = space = NULL;
9179 new_path = NULL;
9180 buf[0] = '\0';
9182 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9183 bam_direct = BAM_DIRECT_MULTIBOOT);
9184 if (bam_direct != BAM_DIRECT_DBOOT) {
9185 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
9186 return (BAM_ERROR);
9190 * If a user changed the default entry to a non-bootadm controlled
9191 * one, we don't want to mess with it. Just print an error and
9192 * return.
9194 if (mp->curdefault) {
9195 entryNum = s_strtol(mp->curdefault->arg);
9196 for (entryp = mp->entries; entryp; entryp = entryp->next) {
9197 if (entryp->entryNum == entryNum)
9198 break;
9200 if ((entryp != NULL) &&
9201 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9202 bam_error(DEFAULT_NOT_BAM);
9203 return (BAM_ERROR);
9207 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9208 0, &entryNum);
9210 if (entryp != NULL) {
9211 for (ptr = entryp->start; ptr && ptr != entryp->end;
9212 ptr = ptr->next) {
9213 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9214 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9215 kernelp = ptr;
9216 break;
9219 if (kernelp == NULL) {
9220 bam_error(NO_KERNEL, entryNum);
9221 return (BAM_ERROR);
9224 old_kernel_len = strcspn(kernelp->arg, " \t");
9225 space = old_args = kernelp->arg + old_kernel_len;
9226 while ((*old_args == ' ') || (*old_args == '\t'))
9227 old_args++;
9230 if (path == NULL) {
9231 if (entryp == NULL) {
9232 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
9233 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9234 return (BAM_SUCCESS);
9236 assert(kernelp);
9237 if (optnum == ARGS_CMD) {
9238 if (old_args[0] != '\0') {
9239 (void) strlcpy(buf, old_args, bufsize);
9240 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
9242 } else {
9244 * We need to print the kernel, so we just turn the
9245 * first space into a '\0' and print the beginning.
9246 * We don't print anything if it's the default kernel.
9248 old_space = *space;
9249 *space = '\0';
9250 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9251 (void) strlcpy(buf, kernelp->arg, bufsize);
9252 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
9254 *space = old_space;
9256 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9257 return (BAM_SUCCESS);
9261 * First, check if we're resetting an entry to the default.
9263 if ((path[0] == '\0') ||
9264 ((optnum == KERNEL_CMD) &&
9265 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9266 if ((entryp == NULL) || (kernelp == NULL)) {
9267 /* No previous entry, it's already the default */
9268 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
9269 return (BAM_SUCCESS);
9273 * Check if we can delete the entry. If we're resetting the
9274 * kernel command, and the args is already empty, or if we're
9275 * resetting the args command, and the kernel is already the
9276 * default, we can restore the old default and delete the entry.
9278 if (((optnum == KERNEL_CMD) &&
9279 ((old_args == NULL) || (old_args[0] == '\0'))) ||
9280 ((optnum == ARGS_CMD) &&
9281 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9282 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9283 kernelp = NULL;
9284 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9285 restore_default_entry(mp, BAM_OLD_RC_DEF,
9286 mp->old_rc_default);
9287 mp->old_rc_default = NULL;
9288 rv = BAM_WRITE;
9289 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
9290 goto done;
9293 if (optnum == KERNEL_CMD) {
9295 * At this point, we've already checked that old_args
9296 * and entryp are valid pointers. The "+ 2" is for
9297 * a space a the string termination character.
9299 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9300 strlen(old_args) + 2;
9301 new_arg = s_calloc(1, new_str_len);
9302 (void) snprintf(new_arg, new_str_len, "%s %s",
9303 DIRECT_BOOT_KERNEL, old_args);
9304 free(kernelp->arg);
9305 kernelp->arg = new_arg;
9308 * We have changed the kernel line, so we may need
9309 * to update the archive line as well.
9311 set_archive_line(entryp, kernelp);
9312 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
9313 fcn, kernelp->arg));
9314 } else {
9316 * We're resetting the boot args to nothing, so
9317 * we only need to copy the kernel. We've already
9318 * checked that the kernel is not the default.
9320 new_arg = s_calloc(1, old_kernel_len + 1);
9321 (void) snprintf(new_arg, old_kernel_len + 1, "%s",
9322 kernelp->arg);
9323 free(kernelp->arg);
9324 kernelp->arg = new_arg;
9325 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
9326 fcn, kernelp->arg));
9328 rv = BAM_WRITE;
9329 goto done;
9333 * Expand the kernel file to a full path, if necessary
9335 if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9336 new_path = expand_path(path);
9337 if (new_path == NULL) {
9338 bam_error(UNKNOWN_KERNEL, path);
9339 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9340 return (BAM_ERROR);
9342 free_new_path = 1;
9343 } else {
9344 new_path = path;
9345 free_new_path = 0;
9349 * At this point, we know we're setting a new value. First, take care
9350 * of the case where there was no previous entry.
9352 if (entryp == NULL) {
9354 /* Similar to code in update_temp */
9355 fstype = get_fstype("/");
9356 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9357 if (fstype == NULL) {
9358 bam_error(BOOTENV_FSTYPE_FAILED);
9359 rv = BAM_ERROR;
9360 goto done;
9363 osdev = get_special("/");
9364 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9365 if (osdev == NULL) {
9366 free(fstype);
9367 bam_error(BOOTENV_SPECIAL_FAILED);
9368 rv = BAM_ERROR;
9369 goto done;
9372 sign = find_existing_sign("/", osdev, fstype);
9373 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9374 if (sign == NULL) {
9375 free(fstype);
9376 free(osdev);
9377 bam_error(BOOTENV_SIGN_FAILED);
9378 rv = BAM_ERROR;
9379 goto done;
9382 free(osdev);
9383 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9384 free(sign);
9385 assert(strchr(signbuf, '(') == NULL &&
9386 strchr(signbuf, ',') == NULL &&
9387 strchr(signbuf, ')') == NULL);
9389 if (optnum == KERNEL_CMD) {
9390 if (strcmp(fstype, "zfs") == 0) {
9391 new_str_len = strlen(new_path) +
9392 strlen(ZFS_BOOT) + 8;
9393 new_arg = s_calloc(1, new_str_len);
9394 (void) snprintf(new_arg, new_str_len, "%s %s",
9395 new_path, ZFS_BOOT);
9396 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9397 new_arg));
9398 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9399 signbuf, new_arg, NULL, NULL, NULL);
9400 free(new_arg);
9401 } else {
9402 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9403 new_path));
9404 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9405 signbuf, new_path, NULL, NULL, NULL);
9407 } else {
9408 new_str_len = strlen(path) + 8;
9409 if (strcmp(fstype, "zfs") == 0) {
9410 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9411 new_arg = s_calloc(1, new_str_len);
9412 (void) snprintf(new_arg, new_str_len, "%s %s",
9413 DIRECT_BOOT_KERNEL_ZFS, path);
9414 } else {
9415 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9416 new_arg = s_calloc(1, new_str_len);
9417 (void) snprintf(new_arg, new_str_len, "%s %s",
9418 DIRECT_BOOT_KERNEL, path);
9421 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
9422 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9423 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9424 free(new_arg);
9426 free(fstype);
9427 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9428 entryNum = BAM_ERROR);
9429 if (entryNum == BAM_ERROR) {
9430 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
9431 BOOTENV_RC_TITLE);
9432 rv = BAM_ERROR;
9433 goto done;
9435 save_default_entry(mp, BAM_OLD_RC_DEF);
9436 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9437 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9438 if (ret == BAM_ERROR) {
9439 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
9441 rv = BAM_WRITE;
9442 goto done;
9446 * There was already an bootenv entry which we need to edit.
9448 if (optnum == KERNEL_CMD) {
9449 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9450 new_arg = s_calloc(1, new_str_len);
9451 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9452 old_args);
9453 free(kernelp->arg);
9454 kernelp->arg = new_arg;
9457 * If we have changed the kernel line, we may need to update
9458 * the archive line as well.
9460 set_archive_line(entryp, kernelp);
9461 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
9462 kernelp->arg));
9463 } else {
9464 new_str_len = old_kernel_len + strlen(path) + 8;
9465 new_arg = s_calloc(1, new_str_len);
9466 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9467 (void) strlcat(new_arg, " ", new_str_len);
9468 (void) strlcat(new_arg, path, new_str_len);
9469 free(kernelp->arg);
9470 kernelp->arg = new_arg;
9471 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
9472 kernelp->arg));
9474 rv = BAM_WRITE;
9476 done:
9477 if ((rv == BAM_WRITE) && kernelp)
9478 update_line(kernelp);
9479 if (free_new_path)
9480 free(new_path);
9481 if (rv == BAM_WRITE) {
9482 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9483 } else {
9484 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9486 return (rv);
9489 static error_t
9490 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9492 const char *fcn = "get_kernel()";
9493 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
9494 return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9497 static error_t
9498 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9500 const char *fcn = "set_kernel()";
9501 assert(path != NULL);
9502 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
9503 return (get_set_kernel(mp, optnum, path, buf, bufsize));
9506 /*ARGSUSED*/
9507 static error_t
9508 set_option(menu_t *mp, char *dummy, char *opt)
9510 int optnum;
9511 int optval;
9512 char *val;
9513 char buf[BUFSIZ] = "";
9514 error_t rv;
9515 const char *fcn = "set_option()";
9517 assert(mp);
9518 assert(opt);
9519 assert(dummy == NULL);
9521 /* opt is set from bam_argv[0] and is always non-NULL */
9522 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
9524 val = strchr(opt, '=');
9525 if (val != NULL) {
9526 *val = '\0';
9529 if (strcmp(opt, "default") == 0) {
9530 optnum = DEFAULT_CMD;
9531 } else if (strcmp(opt, "timeout") == 0) {
9532 optnum = TIMEOUT_CMD;
9533 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9534 optnum = KERNEL_CMD;
9535 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9536 optnum = ARGS_CMD;
9537 } else {
9538 bam_error(INVALID_OPTION, opt);
9539 return (BAM_ERROR);
9543 * kernel and args are allowed without "=new_value" strings. All
9544 * others cause errors
9546 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9547 bam_error(NO_OPTION_ARG, opt);
9548 return (BAM_ERROR);
9549 } else if (val != NULL) {
9550 *val = '=';
9553 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9554 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
9555 val ? val + 1 : "NULL"));
9557 if (val)
9558 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9559 else
9560 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9561 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9562 (void) printf("%s\n", buf);
9563 } else {
9564 optval = s_strtol(val + 1);
9565 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
9566 rv = set_global(mp, menu_cmds[optnum], optval);
9569 if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9570 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9571 } else {
9572 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9575 return (rv);
9579 * The quiet argument suppresses messages. This is used
9580 * when invoked in the context of other commands (e.g. list_entry)
9582 static error_t
9583 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9585 line_t *lp;
9586 char *arg;
9587 int done, ret = BAM_SUCCESS;
9589 assert(mp);
9590 assert(menu_path);
9591 assert(globalcmd);
9593 if (mp->start == NULL) {
9594 if (!quiet)
9595 bam_error(NO_MENU, menu_path);
9596 return (BAM_ERROR);
9599 done = 0;
9600 for (lp = mp->start; lp; lp = lp->next) {
9601 if (lp->flags != BAM_GLOBAL)
9602 continue;
9604 if (lp->cmd == NULL) {
9605 if (!quiet)
9606 bam_error(NO_CMD, lp->lineNum);
9607 continue;
9610 if (strcmp(globalcmd, lp->cmd) != 0)
9611 continue;
9613 /* Found global. Check for duplicates */
9614 if (done && !quiet) {
9615 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
9616 ret = BAM_ERROR;
9619 arg = lp->arg ? lp->arg : "";
9620 bam_print(GLOBAL_CMD, globalcmd, arg);
9621 done = 1;
9624 if (!done && bam_verbose)
9625 bam_print(NO_ENTRY, globalcmd);
9627 return (ret);
9630 static error_t
9631 menu_write(char *root, menu_t *mp)
9633 const char *fcn = "menu_write()";
9635 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
9636 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9639 void
9640 line_free(line_t *lp)
9642 if (lp == NULL)
9643 return;
9645 if (lp->cmd != NULL)
9646 free(lp->cmd);
9647 if (lp->sep)
9648 free(lp->sep);
9649 if (lp->arg)
9650 free(lp->arg);
9651 if (lp->line)
9652 free(lp->line);
9653 free(lp);
9656 static void
9657 linelist_free(line_t *start)
9659 line_t *lp;
9661 while (start) {
9662 lp = start;
9663 start = start->next;
9664 line_free(lp);
9668 static void
9669 filelist_free(filelist_t *flistp)
9671 linelist_free(flistp->head);
9672 flistp->head = NULL;
9673 flistp->tail = NULL;
9676 static void
9677 menu_free(menu_t *mp)
9679 entry_t *ent, *tmp;
9680 assert(mp);
9682 if (mp->start)
9683 linelist_free(mp->start);
9684 ent = mp->entries;
9685 while (ent) {
9686 tmp = ent;
9687 ent = tmp->next;
9688 free(tmp);
9691 free(mp);
9695 * Utility routines
9700 * Returns 0 on success
9701 * Any other value indicates an error
9703 static int
9704 exec_cmd(char *cmdline, filelist_t *flistp)
9706 char buf[BUFSIZ];
9707 int ret;
9708 FILE *ptr;
9709 sigset_t set;
9710 void (*disp)(int);
9713 * For security
9714 * - only absolute paths are allowed
9715 * - set IFS to space and tab
9717 if (*cmdline != '/') {
9718 bam_error(ABS_PATH_REQ, cmdline);
9719 return (-1);
9721 (void) putenv("IFS= \t");
9724 * We may have been exec'ed with SIGCHLD blocked
9725 * unblock it here
9727 (void) sigemptyset(&set);
9728 (void) sigaddset(&set, SIGCHLD);
9729 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9730 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
9731 return (-1);
9735 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9737 disp = sigset(SIGCHLD, SIG_DFL);
9738 if (disp == SIG_ERR) {
9739 bam_error(FAILED_SIG, strerror(errno));
9740 return (-1);
9742 if (disp == SIG_HOLD) {
9743 bam_error(BLOCKED_SIG, cmdline);
9744 return (-1);
9747 ptr = popen(cmdline, "r");
9748 if (ptr == NULL) {
9749 bam_error(POPEN_FAIL, cmdline, strerror(errno));
9750 return (-1);
9754 * If we simply do a pclose() following a popen(), pclose()
9755 * will close the reader end of the pipe immediately even
9756 * if the child process has not started/exited. pclose()
9757 * does wait for cmd to terminate before returning though.
9758 * When the executed command writes its output to the pipe
9759 * there is no reader process and the command dies with
9760 * SIGPIPE. To avoid this we read repeatedly until read
9761 * terminates with EOF. This indicates that the command
9762 * (writer) has closed the pipe and we can safely do a
9763 * pclose().
9765 * Since pclose() does wait for the command to exit,
9766 * we can safely reap the exit status of the command
9767 * from the value returned by pclose()
9769 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9770 if (flistp == NULL) {
9771 /* s_fgets strips newlines, so insert them at the end */
9772 bam_print(PRINT, buf);
9773 } else {
9774 append_to_flist(flistp, buf);
9778 ret = pclose(ptr);
9779 if (ret == -1) {
9780 bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
9781 return (-1);
9784 if (WIFEXITED(ret)) {
9785 return (WEXITSTATUS(ret));
9786 } else {
9787 bam_error(EXEC_FAIL, cmdline, ret);
9788 return (-1);
9793 * Since this function returns -1 on error
9794 * it cannot be used to convert -1. However,
9795 * that is sufficient for what we need.
9797 static long
9798 s_strtol(char *str)
9800 long l;
9801 char *res = NULL;
9803 if (str == NULL) {
9804 return (-1);
9807 errno = 0;
9808 l = strtol(str, &res, 10);
9809 if (errno || *res != '\0') {
9810 return (-1);
9813 return (l);
9817 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9819 static int
9820 s_fputs(char *str, FILE *fp)
9822 char linebuf[BAM_MAXLINE];
9824 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9825 return (fputs(linebuf, fp));
9829 * Wrapper around fgets, that strips newlines returned by fgets
9831 char *
9832 s_fgets(char *buf, int buflen, FILE *fp)
9834 int n;
9836 buf = fgets(buf, buflen, fp);
9837 if (buf) {
9838 n = strlen(buf);
9839 if (n == buflen - 1 && buf[n-1] != '\n')
9840 bam_error(TOO_LONG, buflen - 1, buf);
9841 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9844 return (buf);
9847 void *
9848 s_calloc(size_t nelem, size_t sz)
9850 void *ptr;
9852 ptr = calloc(nelem, sz);
9853 if (ptr == NULL) {
9854 bam_error(NO_MEM, nelem*sz);
9855 bam_exit(1);
9857 return (ptr);
9860 void *
9861 s_realloc(void *ptr, size_t sz)
9863 ptr = realloc(ptr, sz);
9864 if (ptr == NULL) {
9865 bam_error(NO_MEM, sz);
9866 bam_exit(1);
9868 return (ptr);
9871 char *
9872 s_strdup(char *str)
9874 char *ptr;
9876 if (str == NULL)
9877 return (NULL);
9879 ptr = strdup(str);
9880 if (ptr == NULL) {
9881 bam_error(NO_MEM, strlen(str) + 1);
9882 bam_exit(1);
9884 return (ptr);
9888 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9889 * Returns 0 otherwise
9891 static int
9892 is_amd64(void)
9894 static int amd64 = -1;
9895 char isabuf[257]; /* from sysinfo(2) manpage */
9897 if (amd64 != -1)
9898 return (amd64);
9900 if (bam_alt_platform) {
9901 if (strcmp(bam_platform, "i86pc") == 0) {
9902 amd64 = 1; /* diskless server */
9904 } else {
9905 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
9906 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
9907 amd64 = 1;
9908 } else if (strstr(isabuf, "i386") == NULL) {
9909 amd64 = 1; /* diskless server */
9912 if (amd64 == -1)
9913 amd64 = 0;
9915 return (amd64);
9918 static char *
9919 get_machine(void)
9921 static int cached = -1;
9922 static char mbuf[257]; /* from sysinfo(2) manpage */
9924 if (cached == 0)
9925 return (mbuf);
9927 if (bam_alt_platform) {
9928 return (bam_platform);
9929 } else {
9930 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
9931 cached = 1;
9934 if (cached == -1) {
9935 mbuf[0] = '\0';
9936 cached = 0;
9939 return (mbuf);
9943 is_sparc(void)
9945 static int issparc = -1;
9946 char mbuf[257]; /* from sysinfo(2) manpage */
9948 if (issparc != -1)
9949 return (issparc);
9951 if (bam_alt_platform) {
9952 if (strncmp(bam_platform, "sun4", 4) == 0) {
9953 issparc = 1;
9955 } else {
9956 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
9957 strcmp(mbuf, "sparc") == 0) {
9958 issparc = 1;
9961 if (issparc == -1)
9962 issparc = 0;
9964 return (issparc);
9967 static void
9968 append_to_flist(filelist_t *flistp, char *s)
9970 line_t *lp;
9972 lp = s_calloc(1, sizeof (line_t));
9973 lp->line = s_strdup(s);
9974 if (flistp->head == NULL)
9975 flistp->head = lp;
9976 else
9977 flistp->tail->next = lp;
9978 flistp->tail = lp;
9981 #if !defined(_OBP)
9983 UCODE_VENDORS;
9985 /*ARGSUSED*/
9986 static void
9987 ucode_install(char *root)
9989 int i;
9991 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
9992 int cmd_len = PATH_MAX + 256;
9993 char cmd[PATH_MAX + 256];
9994 char file[PATH_MAX];
9995 char timestamp[PATH_MAX];
9996 struct stat fstatus, tstatus;
9997 struct utimbuf u_times;
9999 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
10000 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
10001 ucode_vendors[i].extstr);
10003 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
10004 continue;
10006 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
10008 if (stat(timestamp, &tstatus) == 0 &&
10009 fstatus.st_mtime <= tstatus.st_mtime)
10010 continue;
10012 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
10013 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
10014 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
10015 if (system(cmd) != 0)
10016 return;
10018 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
10019 return;
10021 u_times.actime = fstatus.st_atime;
10022 u_times.modtime = fstatus.st_mtime;
10023 (void) utime(timestamp, &u_times);
10026 #endif