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