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