1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 * Copyright (c) 2015 by Delphix. All rights reserved.
26 * Copyright 2016 Toomas Soome <tsoome@me.com>
27 * Copyright 2017 Nexenta Systems, Inc.
28 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
29 */
30
31/*
32 * bootadm(1M) is a new utility for managing bootability of
33 * Solaris *Newboot* environments. It has two primary tasks:
34 *	- Allow end users to manage bootability of Newboot Solaris instances
35 *	- Provide services to other subsystems in Solaris (primarily Install)
36 */
37
38/* Headers */
39#include <stdio.h>
40#include <errno.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <alloca.h>
47#include <stdarg.h>
48#include <limits.h>
49#include <signal.h>
50#include <sys/wait.h>
51#include <sys/mnttab.h>
52#include <sys/mntent.h>
53#include <sys/statvfs.h>
54#include <libnvpair.h>
55#include <ftw.h>
56#include <fcntl.h>
57#include <strings.h>
58#include <utime.h>
59#include <sys/systeminfo.h>
60#include <sys/dktp/fdisk.h>
61#include <sys/param.h>
62#include <dirent.h>
63#include <ctype.h>
64#include <libgen.h>
65#include <sys/sysmacros.h>
66#include <sys/elf.h>
67#include <libscf.h>
68#include <zlib.h>
69#include <sys/lockfs.h>
70#include <sys/filio.h>
71#include <libbe.h>
72#include <deflt.h>
73#ifdef i386
74#include <libfdisk.h>
75#endif
76
77#if !defined(_OBP)
78#include <sys/ucode.h>
79#endif
80
81#include <pwd.h>
82#include <grp.h>
83#include <device_info.h>
84#include <sys/vtoc.h>
85#include <sys/efi_partition.h>
86#include <regex.h>
87#include <locale.h>
88#include <sys/mkdev.h>
89
90#include "bootadm.h"
91
92#ifndef TEXT_DOMAIN
93#define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
94#endif	/* TEXT_DOMAIN */
95
96/* Type definitions */
97
98/* Primary subcmds */
99typedef enum {
100	BAM_MENU = 3,
101	BAM_ARCHIVE,
102	BAM_INSTALL
103} subcmd_t;
104
105#define	LINE_INIT	0	/* lineNum initial value */
106#define	ENTRY_INIT	-1	/* entryNum initial value */
107#define	ALL_ENTRIES	-2	/* selects all boot entries */
108
109#define	PARTNO_NOTFOUND -1	/* Solaris partition not found */
110#define	PARTNO_EFI	-2	/* EFI partition table found */
111
112#define	GRUB_DIR		"/boot/grub"
113#define	GRUB_STAGE2		GRUB_DIR "/stage2"
114#define	GRUB_MENU		"/boot/grub/menu.lst"
115#define	MENU_TMP		"/boot/grub/menu.lst.tmp"
116#define	GRUB_BACKUP_MENU	"/etc/lu/GRUB_backup_menu"
117#define	RAMDISK_SPECIAL		"/devices/ramdisk"
118#define	STUBBOOT		"/stubboot"
119#define	MULTIBOOT		"/platform/i86pc/multiboot"
120#define	GRUBSIGN_DIR		"/boot/grub/bootsign"
121#define	GRUBSIGN_BACKUP		"/etc/bootsign"
122#define	GRUBSIGN_UFS_PREFIX	"rootfs"
123#define	GRUBSIGN_ZFS_PREFIX	"pool_"
124#define	GRUBSIGN_LU_PREFIX	"BE_"
125#define	UFS_SIGNATURE_LIST	"/var/run/grub_ufs_signatures"
126#define	ZFS_LEGACY_MNTPT	"/tmp/bootadm_mnt_zfs_legacy"
127
128/* SMF */
129#define	BOOT_ARCHIVE_FMRI	"system/boot-archive:default"
130#define	SCF_PG_CONFIG		"config"
131#define	SCF_PROPERTY_FORMAT	"format"
132
133/* BE defaults */
134#define	BE_DEFAULTS		"/etc/default/be"
135#define	BE_DFLT_BE_HAS_GRUB	"BE_HAS_GRUB="
136
137#define	BOOTADM_RDONLY_TEST	"BOOTADM_RDONLY_TEST"
138
139/* lock related */
140#define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
141#define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
142
143#define	CREATE_RAMDISK		"boot/solaris/bin/create_ramdisk"
144#define	CREATE_DISKMAP		"boot/solaris/bin/create_diskmap"
145#define	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
146#define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
147
148#define	GRUB_slice		"/etc/lu/GRUB_slice"
149#define	GRUB_root		"/etc/lu/GRUB_root"
150#define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
151#define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
152#define	FINDROOT_INSTALLGRUB	"/etc/lu/installgrub.findroot"
153#define	LULIB			"/usr/lib/lu/lulib"
154#define	LULIB_PROPAGATE_FILE	"lulib_propagate_file"
155#define	CKSUM			"/usr/bin/cksum"
156#define	LU_MENU_CKSUM		"/etc/lu/menu.cksum"
157#define	BOOTADM			"/sbin/bootadm"
158
159#define	INSTALLGRUB		"/sbin/installgrub"
160#define	STAGE1			"/boot/grub/stage1"
161#define	STAGE2			"/boot/grub/stage2"
162
163#define	ETC_SYSTEM_DIR		"etc/system.d"
164#define	SELF_ASSEMBLY		"etc/system.d/.self-assembly"
165
166/*
167 * Default file attributes
168 */
169#define	DEFAULT_DEV_MODE	0644	/* default permissions */
170#define	DEFAULT_DEV_UID		0	/* user root */
171#define	DEFAULT_DEV_GID		3	/* group sys */
172
173/*
174 * Menu related
175 * menu_cmd_t and menu_cmds must be kept in sync
176 */
177char *menu_cmds[] = {
178	"default",	/* DEFAULT_CMD */
179	"timeout",	/* TIMEOUT_CMD */
180	"title",	/* TITLE_CMD */
181	"root",		/* ROOT_CMD */
182	"kernel",	/* KERNEL_CMD */
183	"kernel$",	/* KERNEL_DOLLAR_CMD */
184	"module",	/* MODULE_CMD */
185	"module$",	/* MODULE_DOLLAR_CMD */
186	" ",		/* SEP_CMD */
187	"#",		/* COMMENT_CMD */
188	"chainloader",	/* CHAINLOADER_CMD */
189	"args",		/* ARGS_CMD */
190	"findroot",	/* FINDROOT_CMD */
191	"bootfs",	/* BOOTFS_CMD */
192	NULL
193};
194
195char *bam_formats[] = {
196	"hsfs",
197	"ufs",
198	"cpio",
199	"ufs-nocompress",
200	NULL
201};
202#define	BAM_FORMAT_UNSET -1
203#define	BAM_FORMAT_HSFS 0
204short bam_format = BAM_FORMAT_UNSET;
205
206#define	OPT_ENTRY_NUM	"entry"
207
208/*
209 * exec_cmd related
210 */
211typedef struct {
212	line_t *head;
213	line_t *tail;
214} filelist_t;
215
216#define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
217#define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
218
219#define	FILE_STAT	"boot/solaris/filestat.ramdisk"
220#define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
221#define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
222#define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
223
224#define	FILE_STAT_TIMESTAMP	"boot/solaris/timestamp.cache"
225
226/* Globals */
227int bam_verbose;
228int bam_force;
229int bam_debug;
230int bam_skip_lock;
231static char *prog;
232static subcmd_t bam_cmd;
233char *bam_root;
234int bam_rootlen;
235static int bam_root_readonly;
236int bam_alt_root;
237static int bam_extend = 0;
238static int bam_purge = 0;
239static char *bam_subcmd;
240static char *bam_opt;
241static char **bam_argv;
242static char *bam_pool;
243static int bam_argc;
244static int bam_check;
245static int bam_saved_check;
246static int bam_smf_check;
247static int bam_lock_fd = -1;
248static int bam_zfs;
249static int bam_mbr;
250char rootbuf[PATH_MAX] = "/";
251static char self_assembly[PATH_MAX];
252static int bam_update_all;
253static int bam_alt_platform;
254static char *bam_platform;
255static char *bam_home_env = NULL;
256
257/* function prototypes */
258static void parse_args_internal(int, char *[]);
259static void parse_args(int, char *argv[]);
260static error_t bam_menu(char *, char *, int, char *[]);
261static error_t bam_install(char *, char *);
262static error_t bam_archive(char *, char *);
263
264static void bam_lock(void);
265static void bam_unlock(void);
266
267static int exec_cmd(char *, filelist_t *);
268static error_t read_globals(menu_t *, char *, char *, int);
269static int menu_on_bootdisk(char *os_root, char *menu_root);
270static menu_t *menu_read(char *);
271static error_t menu_write(char *, menu_t *);
272static void linelist_free(line_t *);
273static void menu_free(menu_t *);
274static void filelist_free(filelist_t *);
275static error_t list2file(char *, char *, char *, line_t *);
276static error_t list_entry(menu_t *, char *, char *);
277static error_t list_setting(menu_t *, char *, char *);
278static error_t delete_all_entries(menu_t *, char *, char *);
279static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
280static error_t update_temp(menu_t *mp, char *dummy, char *opt);
281
282static error_t install_bootloader(void);
283static error_t update_archive(char *, char *);
284static error_t list_archive(char *, char *);
285static error_t update_all(char *, char *);
286static error_t read_list(char *, filelist_t *);
287static error_t set_option(menu_t *, char *, char *);
288static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
289static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
290static error_t build_etc_system_dir(char *);
291static char *expand_path(const char *);
292
293static long s_strtol(char *);
294static int s_fputs(char *, FILE *);
295
296static int is_amd64(void);
297static char *get_machine(void);
298static void append_to_flist(filelist_t *, char *);
299static int ufs_add_to_sign_list(char *sign);
300static error_t synchronize_BE_menu(void);
301
302#if !defined(_OBP)
303static void ucode_install();
304#endif
305
306/* Menu related sub commands */
307static subcmd_defn_t menu_subcmds[] = {
308	"set_option",		OPT_ABSENT,	set_option, 0,	/* PUB */
309	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
310	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
311	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
312	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
313	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
314	"list_setting",		OPT_OPTIONAL,	list_setting, 1, /* menu */
315	"disable_hypervisor",	OPT_ABSENT,	cvt_to_metal, 0, /* menu */
316	"enable_hypervisor",	OPT_ABSENT,	cvt_to_hyper, 0, /* menu */
317	NULL,			0,		NULL, 0	/* must be last */
318};
319
320/* Archive related sub commands */
321static subcmd_defn_t arch_subcmds[] = {
322	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
323	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
324	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
325	NULL,			0,		NULL, 0	/* must be last */
326};
327
328/* Install related sub commands */
329static subcmd_defn_t inst_subcmds[] = {
330	"install_bootloader",	OPT_ABSENT,	install_bootloader, 0, /* PUB */
331	NULL,			0,		NULL, 0	/* must be last */
332};
333
334#define	build_path(buf, len, root, prefix, suffix) \
335    snprintf((buf), (len), "%s%s%s%s%s", (root), (prefix), get_machine(), \
336    is_flag_on(IS_SPARC_TARGET) ? "" : "/amd64", (suffix))
337
338/*
339 * Directory specific flags:
340 * NEED_UPDATE : the specified archive needs to be updated
341 * NO_EXTEND   : don't extend the specified archive, but recreate it
342 */
343#define	NEED_UPDATE		0x00000001
344#define	NO_EXTEND		0x00000002
345
346#define	set_dir_flag(f)		(walk_arg.dirinfo.flags |= (f))
347#define	unset_dir_flag(f)	(walk_arg.dirinfo.flags &= ~(f))
348#define	is_dir_flag_on(f)	(walk_arg.dirinfo.flags & (f) ? 1 : 0)
349
350#define	get_cachedir()		(walk_arg.dirinfo.cdir_path)
351#define	get_updatedir()		(walk_arg.dirinfo.update_path)
352#define	get_count()		(walk_arg.dirinfo.count)
353#define	has_cachedir()		(walk_arg.dirinfo.has_dir)
354#define	set_dir_present()	(walk_arg.dirinfo.has_dir = 1)
355
356/*
357 * dirinfo_t (specific cache directory information):
358 * cdir_path:   path to the archive cache directory
359 * update_path: path to the update directory (contains the files that will be
360 *              used to extend the archive)
361 * has_dir:	the specified cache directory is active
362 * count:	the number of files to update
363 * flags:	directory specific flags
364 */
365typedef struct _dirinfo {
366	char	cdir_path[PATH_MAX];
367	char	update_path[PATH_MAX];
368	int	has_dir;
369	int	count;
370	int	flags;
371} dirinfo_t;
372
373/*
374 * Update flags:
375 * NEED_CACHE_DIR : cache directory is missing and needs to be created
376 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
377 * UPDATE_ERROR : an error occourred while traversing the list of files
378 * RDONLY_FSCHK : the target filesystem is read-only
379 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
380 */
381#define	NEED_CACHE_DIR		0x00000001
382#define	IS_SPARC_TARGET		0x00000002
383#define	UPDATE_ERROR		0x00000004
384#define	RDONLY_FSCHK		0x00000008
385#define	INVALIDATE_CACHE	0x00000010
386
387#define	is_flag_on(flag)	(walk_arg.update_flags & flag ? 1 : 0)
388#define	set_flag(flag)		(walk_arg.update_flags |= flag)
389#define	unset_flag(flag)	(walk_arg.update_flags &= ~flag)
390
391/*
392 * struct walk_arg :
393 * update_flags: flags related to the current updating process
394 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
395 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
396 */
397static struct {
398	int		update_flags;
399	nvlist_t	*new_nvlp;
400	nvlist_t	*old_nvlp;
401	FILE		*sparcfile;
402	dirinfo_t	dirinfo;
403} walk_arg;
404
405struct safefile {
406	char *name;
407	struct safefile *next;
408};
409
410static struct safefile *safefiles = NULL;
411
412/*
413 * svc:/system/filesystem/usr:default service checks for this file and
414 * does a boot archive update and then reboot the system.
415 */
416#define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
417
418/*
419 * svc:/system/boot-archive-update:default checks for this file and
420 * updates the boot archive.
421 */
422#define	NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
423
424/* Thanks growisofs */
425#define	CD_BLOCK	((off64_t)2048)
426#define	VOLDESC_OFF	16
427#define	DVD_BLOCK	(32*1024)
428#define	MAX_IVDs	16
429
430struct iso_pdesc {
431    unsigned char type	[1];
432    unsigned char id	[5];
433    unsigned char void1	[80-5-1];
434    unsigned char volume_space_size [8];
435    unsigned char void2	[2048-80-8];
436};
437
438/*
439 * COUNT_MAX:	maximum number of changed files to justify a multisession update
440 * BA_SIZE_MAX:	maximum size of the boot_archive to justify a multisession
441 *		update
442 */
443#define	COUNT_MAX		50
444#define	BA_SIZE_MAX		(50 * 1024 * 1024)
445
446#define	bam_nowrite()		(bam_check || bam_smf_check)
447
448static int sync_menu = 1;	/* whether we need to sync the BE menus */
449
450static void
451usage(void)
452{
453	(void) fprintf(stderr, "USAGE:\n");
454
455	/* archive usage */
456	(void) fprintf(stderr,
457	    "\t%s update-archive [-vnf] [-R altroot [-p platform]] "
458	    "[-F format]\n", prog);
459	(void) fprintf(stderr,
460	    "\t%s list-archive [-R altroot [-p platform]]\n", prog);
461#if defined(_OBP)
462	(void) fprintf(stderr,
463	    "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
464#else
465	(void) fprintf(stderr,
466	    "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
467#endif
468#if !defined(_OBP)
469	/* x86 only */
470	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
471	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
472#endif
473}
474
475/*
476 * Best effort attempt to restore the $HOME value.
477 */
478static void
479restore_env()
480{
481	char	home_env[PATH_MAX];
482
483	if (bam_home_env) {
484		(void) snprintf(home_env, sizeof (home_env), "HOME=%s",
485		    bam_home_env);
486		(void) putenv(home_env);
487	}
488}
489
490
491#define		SLEEP_TIME	5
492#define		MAX_TRIES	4
493
494/*
495 * Sanitize the environment in which bootadm will execute its sub-processes
496 * (ex. mkisofs). This is done to prevent those processes from attempting
497 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
498 * or, potentially, insecure.
499 */
500static void
501sanitize_env()
502{
503	int	stry = 0;
504
505	/* don't depend on caller umask */
506	(void) umask(0022);
507
508	/* move away from a potential unsafe current working directory */
509	while (chdir("/") == -1) {
510		if (errno != EINTR) {
511			bam_print("WARNING: unable to chdir to /");
512			break;
513		}
514	}
515
516	bam_home_env = getenv("HOME");
517	while (bam_home_env != NULL && putenv("HOME=/") == -1) {
518		if (errno == ENOMEM) {
519			/* retry no more than MAX_TRIES times */
520			if (++stry > MAX_TRIES) {
521				bam_print("WARNING: unable to recover from "
522				    "system memory pressure... aborting \n");
523				bam_exit(EXIT_FAILURE);
524			}
525			/* memory is tight, try to sleep */
526			bam_print("Attempting to recover from memory pressure: "
527			    "sleeping for %d seconds\n", SLEEP_TIME * stry);
528			(void) sleep(SLEEP_TIME * stry);
529		} else {
530			bam_print("WARNING: unable to sanitize HOME\n");
531		}
532	}
533}
534
535int
536main(int argc, char *argv[])
537{
538	error_t ret = BAM_SUCCESS;
539
540	(void) setlocale(LC_ALL, "");
541	(void) textdomain(TEXT_DOMAIN);
542
543	if ((prog = strrchr(argv[0], '/')) == NULL) {
544		prog = argv[0];
545	} else {
546		prog++;
547	}
548
549	INJECT_ERROR1("ASSERT_ON", assert(0))
550
551	sanitize_env();
552
553	parse_args(argc, argv);
554
555	switch (bam_cmd) {
556		case BAM_MENU:
557			if (is_grub(bam_alt_root ? bam_root : "/")) {
558				ret = bam_menu(bam_subcmd, bam_opt,
559				    bam_argc, bam_argv);
560			} else {
561				ret = bam_loader_menu(bam_subcmd, bam_opt,
562				    bam_argc, bam_argv);
563			}
564			break;
565		case BAM_ARCHIVE:
566			ret = bam_archive(bam_subcmd, bam_opt);
567			break;
568		case BAM_INSTALL:
569			ret = bam_install(bam_subcmd, bam_opt);
570			break;
571		default:
572			usage();
573			bam_exit(1);
574	}
575
576	if (ret != BAM_SUCCESS)
577		bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
578
579	bam_unlock();
580	return (0);
581}
582
583/*
584 * Equivalence of public and internal commands:
585 *	update-archive  -- -a update
586 *	list-archive	-- -a list
587 *	set-menu	-- -m set_option
588 *	list-menu	-- -m list_entry
589 *	update-menu	-- -m update_entry
590 *	install-bootloader	-- -i install_bootloader
591 */
592static struct cmd_map {
593	char *bam_cmdname;
594	int bam_cmd;
595	char *bam_subcmd;
596} cmd_map[] = {
597	{ "update-archive",	BAM_ARCHIVE,	"update"},
598	{ "list-archive",	BAM_ARCHIVE,	"list"},
599	{ "set-menu",		BAM_MENU,	"set_option"},
600	{ "list-menu",		BAM_MENU,	"list_entry"},
601	{ "update-menu",	BAM_MENU,	"update_entry"},
602	{ "install-bootloader",	BAM_INSTALL,	"install_bootloader"},
603	{ NULL,			0,		NULL}
604};
605
606/*
607 * Commands syntax published in bootadm(1M) are parsed here
608 */
609static void
610parse_args(int argc, char *argv[])
611{
612	struct cmd_map *cmp = cmd_map;
613
614	/* command conforming to the final spec */
615	if (argc > 1 && argv[1][0] != '-') {
616		/*
617		 * Map commands to internal table.
618		 */
619		while (cmp->bam_cmdname) {
620			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
621				bam_cmd = cmp->bam_cmd;
622				bam_subcmd = cmp->bam_subcmd;
623				break;
624			}
625			cmp++;
626		}
627		if (cmp->bam_cmdname == NULL) {
628			usage();
629			bam_exit(1);
630		}
631		argc--;
632		argv++;
633	}
634
635	parse_args_internal(argc, argv);
636}
637
638/*
639 * A combination of public and private commands are parsed here.
640 * The internal syntax and the corresponding functionality are:
641 *	-a update			-- update-archive
642 *	-a list				-- list-archive
643 *	-a update_all			-- (reboot to sync all mnted OS archive)
644 *	-i install_bootloader		-- install-bootloader
645 *	-m update_entry			-- update-menu
646 *	-m list_entry			-- list-menu
647 *	-m update_temp			-- (reboot -- [boot-args])
648 *	-m delete_all_entries		-- (called from install)
649 *	-m enable_hypervisor [args]	-- cvt_to_hyper
650 *	-m disable_hypervisor		-- cvt_to_metal
651 *	-m list_setting [entry] [value]	-- list_setting
652 *
653 * A set of private flags is there too:
654 *	-Q		-- purge the cache directories and rebuild them
655 *	-e		-- use the (faster) archive update approach (used by
656 *			   reboot)
657 *	-L		-- skip locking
658 */
659static void
660parse_args_internal(int argc, char *argv[])
661{
662	int c, i, error;
663	extern char *optarg;
664	extern int optind, opterr;
665#if defined(_OBP)
666	const char *optstring = "a:d:fF:i:m:no:veQCLR:p:P:XZ";
667#else
668	const char *optstring = "a:d:fF:i:m:no:veQCMLR:p:P:XZ";
669#endif
670
671	/* Suppress error message from getopt */
672	opterr = 0;
673
674	error = 0;
675	while ((c = getopt(argc, argv, optstring)) != -1) {
676		switch (c) {
677		case 'a':
678			if (bam_cmd) {
679				error = 1;
680				bam_error(
681				    _("multiple commands specified: -%c\n"), c);
682			}
683			bam_cmd = BAM_ARCHIVE;
684			bam_subcmd = optarg;
685			break;
686		case 'd':
687			if (bam_debug) {
688				error = 1;
689				bam_error(
690				    _("duplicate options specified: -%c\n"), c);
691			}
692			bam_debug = s_strtol(optarg);
693			break;
694		case 'f':
695			bam_force = 1;
696			break;
697		case 'F':
698			if (bam_format != BAM_FORMAT_UNSET) {
699				error = 1;
700				bam_error(
701				    _("multiple formats specified: -%c\n"), c);
702			}
703			for (i = 0; bam_formats[i] != NULL; i++) {
704				if (strcmp(bam_formats[i], optarg) == 0) {
705					bam_format = i;
706					break;
707				}
708			}
709			if (bam_format == BAM_FORMAT_UNSET) {
710				error = 1;
711				bam_error(
712				    _("unknown format specified: -%c %s\n"),
713				    c, optarg);
714			}
715			break;
716		case 'Q':
717			bam_purge = 1;
718			break;
719		case 'L':
720			bam_skip_lock = 1;
721			break;
722		case 'i':
723			if (bam_cmd) {
724				error = 1;
725				bam_error(
726				    _("multiple commands specified: -%c\n"), c);
727			}
728			bam_cmd = BAM_INSTALL;
729			bam_subcmd = optarg;
730			break;
731		case 'm':
732			if (bam_cmd) {
733				error = 1;
734				bam_error(
735				    _("multiple commands specified: -%c\n"), c);
736			}
737			bam_cmd = BAM_MENU;
738			bam_subcmd = optarg;
739			break;
740#if !defined(_OBP)
741		case 'M':
742			bam_mbr = 1;
743			break;
744#endif
745		case 'n':
746			bam_check = 1;
747			/*
748			 * We save the original value of bam_check. The new
749			 * approach in case of a read-only filesystem is to
750			 * behave as a check, so we need a way to restore the
751			 * original value after the evaluation of the read-only
752			 * filesystem has been done.
753			 * Even if we don't allow at the moment a check with
754			 * update_all, this approach is more robust than
755			 * simply resetting bam_check to zero.
756			 */
757			bam_saved_check = 1;
758			break;
759		case 'o':
760			if (bam_opt) {
761				error = 1;
762				bam_error(
763				    _("duplicate options specified: -%c\n"), c);
764			}
765			bam_opt = optarg;
766			break;
767		case 'v':
768			bam_verbose = 1;
769			break;
770		case 'C':
771			bam_smf_check = 1;
772			break;
773		case 'P':
774			if (bam_pool != NULL) {
775				error = 1;
776				bam_error(
777				    _("duplicate options specified: -%c\n"), c);
778			}
779			bam_pool = optarg;
780			break;
781		case 'R':
782			if (bam_root) {
783				error = 1;
784				bam_error(
785				    _("duplicate options specified: -%c\n"), c);
786				break;
787			} else if (realpath(optarg, rootbuf) == NULL) {
788				error = 1;
789				bam_error(_("cannot resolve path %s: %s\n"),
790				    optarg, strerror(errno));
791				break;
792			}
793			bam_alt_root = 1;
794			bam_root = rootbuf;
795			bam_rootlen = strlen(rootbuf);
796			break;
797		case 'p':
798			bam_alt_platform = 1;
799			bam_platform = optarg;
800			if ((strcmp(bam_platform, "i86pc") != 0) &&
801			    (strcmp(bam_platform, "sun4u") != 0) &&
802			    (strcmp(bam_platform, "sun4v") != 0)) {
803				error = 1;
804				bam_error(_("invalid platform %s - must be "
805				    "one of sun4u, sun4v or i86pc\n"),
806				    bam_platform);
807			}
808			break;
809		case 'X':
810			bam_is_hv = BAM_HV_PRESENT;
811			break;
812		case 'Z':
813			bam_zfs = 1;
814			break;
815		case 'e':
816			bam_extend = 1;
817			break;
818		case '?':
819			error = 1;
820			bam_error(_("invalid option or missing option "
821			    "argument: -%c\n"), optopt);
822			break;
823		default :
824			error = 1;
825			bam_error(_("invalid option or missing option "
826			    "argument: -%c\n"), c);
827			break;
828		}
829	}
830
831	/*
832	 * An alternate platform requires an alternate root
833	 */
834	if (bam_alt_platform && bam_alt_root == 0) {
835		usage();
836		bam_exit(0);
837	}
838
839	/*
840	 * A command option must be specfied
841	 */
842	if (!bam_cmd) {
843		if (bam_opt && strcmp(bam_opt, "all") == 0) {
844			usage();
845			bam_exit(0);
846		}
847		bam_error(_("a command option must be specified\n"));
848		error = 1;
849	}
850
851	if (error) {
852		usage();
853		bam_exit(1);
854	}
855
856	if (optind > argc) {
857		bam_error(_("Internal error: %s\n"), "parse_args");
858		bam_exit(1);
859	} else if (optind < argc) {
860		bam_argv = &argv[optind];
861		bam_argc = argc - optind;
862	}
863
864	/*
865	 * mbr and pool are options for install_bootloader
866	 */
867	if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
868		usage();
869		bam_exit(0);
870	}
871
872	/*
873	 * -n implies verbose mode
874	 */
875	if (bam_check)
876		bam_verbose = 1;
877}
878
879error_t
880check_subcmd_and_options(
881	char *subcmd,
882	char *opt,
883	subcmd_defn_t *table,
884	error_t (**fp)())
885{
886	int i;
887
888	if (subcmd == NULL) {
889		bam_error(_("this command requires a sub-command\n"));
890		return (BAM_ERROR);
891	}
892
893	if (strcmp(subcmd, "set_option") == 0) {
894		if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
895			bam_error(_("missing argument for sub-command\n"));
896			usage();
897			return (BAM_ERROR);
898		} else if (bam_argc > 1 || bam_argv[1] != NULL) {
899			bam_error(_("invalid trailing arguments\n"));
900			usage();
901			return (BAM_ERROR);
902		}
903	} else if (strcmp(subcmd, "update_all") == 0) {
904		/*
905		 * The only option we accept for the "update_all"
906		 * subcmd is "fastboot".
907		 */
908		if (bam_argc > 1 || (bam_argc == 1 &&
909		    strcmp(bam_argv[0], "fastboot") != 0)) {
910			bam_error(_("invalid trailing arguments\n"));
911			usage();
912			return (BAM_ERROR);
913		}
914		if (bam_argc == 1)
915			sync_menu = 0;
916	} else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
917	    (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
918		/*
919		 * Of the remaining subcommands, only "enable_hypervisor" and
920		 * "list_setting" take trailing arguments.
921		 */
922		bam_error(_("invalid trailing arguments\n"));
923		usage();
924		return (BAM_ERROR);
925	}
926
927	if (bam_root == NULL) {
928		bam_root = rootbuf;
929		bam_rootlen = 1;
930	}
931
932	/* verify that subcmd is valid */
933	for (i = 0; table[i].subcmd != NULL; i++) {
934		if (strcmp(table[i].subcmd, subcmd) == 0)
935			break;
936	}
937
938	if (table[i].subcmd == NULL) {
939		bam_error(_("invalid sub-command specified: %s\n"), subcmd);
940		return (BAM_ERROR);
941	}
942
943	if (table[i].unpriv == 0 && geteuid() != 0) {
944		bam_error(_("you must be root to run this command\n"));
945		return (BAM_ERROR);
946	}
947
948	/*
949	 * Currently only privileged commands need a lock
950	 */
951	if (table[i].unpriv == 0)
952		bam_lock();
953
954	/* subcmd verifies that opt is appropriate */
955	if (table[i].option != OPT_OPTIONAL) {
956		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
957			if (opt)
958				bam_error(_("this sub-command (%s) does not "
959				    "take options\n"), subcmd);
960			else
961				bam_error(_("an option is required for this "
962				    "sub-command: %s\n"), subcmd);
963			return (BAM_ERROR);
964		}
965	}
966
967	*fp = table[i].handler;
968
969	return (BAM_SUCCESS);
970}
971
972/*
973 * NOTE: A single "/" is also considered a trailing slash and will
974 * be deleted.
975 */
976void
977elide_trailing_slash(const char *src, char *dst, size_t dstsize)
978{
979	size_t dstlen;
980
981	assert(src);
982	assert(dst);
983
984	(void) strlcpy(dst, src, dstsize);
985
986	dstlen = strlen(dst);
987	if (dst[dstlen - 1] == '/') {
988		dst[dstlen - 1] = '\0';
989	}
990}
991
992static int
993is_safe_exec(char *path)
994{
995	struct stat	sb;
996
997	if (lstat(path, &sb) != 0) {
998		bam_error(_("stat of file failed: %s: %s\n"), path,
999		    strerror(errno));
1000		return (BAM_ERROR);
1001	}
1002
1003	if (!S_ISREG(sb.st_mode)) {
1004		bam_error(_("%s is not a regular file, skipping\n"), path);
1005		return (BAM_ERROR);
1006	}
1007
1008	if (sb.st_uid != getuid()) {
1009		bam_error(_("%s is not owned by %d, skipping\n"),
1010		    path, getuid());
1011		return (BAM_ERROR);
1012	}
1013
1014	if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
1015		bam_error(_("%s is others or group writable, skipping\n"),
1016		    path);
1017		return (BAM_ERROR);
1018	}
1019
1020	return (BAM_SUCCESS);
1021}
1022
1023static error_t
1024list_setting(menu_t *mp, char *which, char *setting)
1025{
1026	line_t	*lp;
1027	entry_t	*ent;
1028
1029	char	*p = which;
1030	int	entry;
1031
1032	int	found;
1033
1034	assert(which);
1035	assert(setting);
1036
1037	if (*which != '\0') {
1038		/*
1039		 * If "which" is not a number, assume it's a setting we want
1040		 * to look for and so set up the routine to look for "which"
1041		 * in the default entry.
1042		 */
1043		while (*p != '\0')
1044			if (!(isdigit((int)*p++))) {
1045				setting = which;
1046				which = mp->curdefault->arg;
1047				break;
1048			}
1049	} else {
1050		which = mp->curdefault->arg;
1051	}
1052
1053	entry = atoi(which);
1054
1055	for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1056	    ent = ent->next)
1057		;
1058
1059	if (!ent) {
1060		bam_error(_("no matching entry found\n"));
1061		return (BAM_ERROR);
1062	}
1063
1064	found = (*setting == '\0');
1065
1066	for (lp = ent->start; lp != NULL; lp = lp->next) {
1067		if ((*setting == '\0') && (lp->flags != BAM_COMMENT))
1068			bam_print("%s\n", lp->line);
1069		else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1070			bam_print("%s\n", lp->arg);
1071			found = 1;
1072		}
1073
1074		if (lp == ent->end)
1075			break;
1076	}
1077
1078	if (!found) {
1079		bam_error(_("no matching entry found\n"));
1080		return (BAM_ERROR);
1081	}
1082
1083	return (BAM_SUCCESS);
1084}
1085
1086static error_t
1087install_bootloader(void)
1088{
1089	nvlist_t	*nvl;
1090	uint16_t	flags = 0;
1091	int		found = 0;
1092	struct extmnttab mnt;
1093	struct stat	statbuf = {0};
1094	be_node_list_t	*be_nodes, *node;
1095	FILE		*fp;
1096	char		*root_ds = NULL;
1097	int		ret = BAM_ERROR;
1098
1099	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1100		bam_error(_("out of memory\n"));
1101		return (ret);
1102	}
1103
1104	/*
1105	 * if bam_alt_root is set, the stage files are used from alt root.
1106	 * if pool is set, the target devices are pool devices, stage files
1107	 * are read from pool bootfs unless alt root is set.
1108	 *
1109	 * use arguments as targets, stage files are from alt or current root
1110	 * if no arguments and no pool, install on current boot pool.
1111	 */
1112
1113	if (bam_alt_root) {
1114		if (stat(bam_root, &statbuf) != 0) {
1115			bam_error(_("stat of file failed: %s: %s\n"), bam_root,
1116			    strerror(errno));
1117			goto done;
1118		}
1119		if ((fp = fopen(MNTTAB, "r")) == NULL) {
1120			bam_error(_("failed to open file: %s: %s\n"),
1121			    MNTTAB, strerror(errno));
1122			goto done;
1123		}
1124		resetmnttab(fp);
1125		while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1126			if (mnt.mnt_major == major(statbuf.st_dev) &&
1127			    mnt.mnt_minor == minor(statbuf.st_dev)) {
1128				found = 1;
1129				root_ds = strdup(mnt.mnt_special);
1130				break;
1131			}
1132		}
1133		(void) fclose(fp);
1134
1135		if (found == 0) {
1136			bam_error(_("alternate root %s not in mnttab\n"),
1137			    bam_root);
1138			goto done;
1139		}
1140		if (root_ds == NULL) {
1141			bam_error(_("out of memory\n"));
1142			goto done;
1143		}
1144
1145		if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
1146			bam_error(_("No BE's found\n"));
1147			goto done;
1148		}
1149		for (node = be_nodes; node != NULL; node = node->be_next_node)
1150			if (strcmp(root_ds, node->be_root_ds) == 0)
1151				break;
1152
1153		if (node == NULL)
1154			bam_error(_("BE (%s) does not exist\n"), root_ds);
1155
1156		free(root_ds);
1157		root_ds = NULL;
1158		if (node == NULL) {
1159			be_free_list(be_nodes);
1160			goto done;
1161		}
1162		ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1163		    node->be_node_name);
1164		ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1165		    node->be_root_ds);
1166		be_free_list(be_nodes);
1167		if (ret != 0) {
1168			ret = BAM_ERROR;
1169			goto done;
1170		}
1171	}
1172
1173	if (bam_force)
1174		flags |= BE_INSTALLBOOT_FLAG_FORCE;
1175	if (bam_mbr)
1176		flags |= BE_INSTALLBOOT_FLAG_MBR;
1177	if (bam_verbose)
1178		flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1179
1180	if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1181		bam_error(_("out of memory\n"));
1182		ret = BAM_ERROR;
1183		goto done;
1184	}
1185
1186	/*
1187	 * if altroot was set, we got be name and be root, only need
1188	 * to set pool name as target.
1189	 * if no altroot, need to find be name and root from pool.
1190	 */
1191	if (bam_pool != NULL) {
1192		ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1193		if (ret != 0) {
1194			ret = BAM_ERROR;
1195			goto done;
1196		}
1197		if (found) {
1198			ret = be_installboot(nvl);
1199			if (ret != 0)
1200				ret = BAM_ERROR;
1201			goto done;
1202		}
1203	}
1204
1205	if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
1206		bam_error(_("No BE's found\n"));
1207		ret = BAM_ERROR;
1208		goto done;
1209	}
1210
1211	if (bam_pool != NULL) {
1212		/*
1213		 * find active be_node in bam_pool
1214		 */
1215		for (node = be_nodes; node != NULL; node = node->be_next_node) {
1216			if (strcmp(bam_pool, node->be_rpool) != 0)
1217				continue;
1218			if (node->be_active_on_boot)
1219				break;
1220		}
1221		if (node == NULL) {
1222			bam_error(_("No active BE in %s\n"), bam_pool);
1223			be_free_list(be_nodes);
1224			ret = BAM_ERROR;
1225			goto done;
1226		}
1227		ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1228		    node->be_node_name);
1229		ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1230		    node->be_root_ds);
1231		be_free_list(be_nodes);
1232		if (ret != 0) {
1233			ret = BAM_ERROR;
1234			goto done;
1235		}
1236		ret = be_installboot(nvl);
1237		if (ret != 0)
1238			ret = BAM_ERROR;
1239		goto done;
1240	}
1241
1242	/*
1243	 * get dataset for "/" and fill up the args.
1244	 */
1245	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1246		bam_error(_("failed to open file: %s: %s\n"),
1247		    MNTTAB, strerror(errno));
1248		ret = BAM_ERROR;
1249		be_free_list(be_nodes);
1250		goto done;
1251	}
1252	resetmnttab(fp);
1253	found = 0;
1254	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1255		if (strcmp(mnt.mnt_mountp, "/") == 0) {
1256			found = 1;
1257			root_ds = strdup(mnt.mnt_special);
1258			break;
1259		}
1260	}
1261	(void) fclose(fp);
1262
1263	if (found == 0) {
1264		bam_error(_("alternate root %s not in mnttab\n"), "/");
1265		ret = BAM_ERROR;
1266		be_free_list(be_nodes);
1267		goto done;
1268	}
1269	if (root_ds == NULL) {
1270		bam_error(_("out of memory\n"));
1271		ret = BAM_ERROR;
1272		be_free_list(be_nodes);
1273		goto done;
1274	}
1275
1276	for (node = be_nodes; node != NULL; node = node->be_next_node) {
1277		if (strcmp(root_ds, node->be_root_ds) == 0)
1278			break;
1279	}
1280
1281	if (node == NULL) {
1282		bam_error(_("No such BE: %s\n"), root_ds);
1283		free(root_ds);
1284		be_free_list(be_nodes);
1285		ret = BAM_ERROR;
1286		goto done;
1287	}
1288	free(root_ds);
1289
1290	ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1291	ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1292	ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1293	be_free_list(be_nodes);
1294
1295	if (ret != 0)
1296		ret = BAM_ERROR;
1297	else
1298		ret = be_installboot(nvl) ? BAM_ERROR : 0;
1299done:
1300	nvlist_free(nvl);
1301
1302	return (ret);
1303}
1304
1305static error_t
1306bam_install(char *subcmd, char *opt)
1307{
1308	error_t (*f)(void);
1309
1310	/*
1311	 * Check arguments
1312	 */
1313	if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1314	    BAM_ERROR)
1315		return (BAM_ERROR);
1316
1317	return (f());
1318}
1319
1320static error_t
1321bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1322{
1323	error_t			ret;
1324	char			menu_path[PATH_MAX];
1325	char			clean_menu_root[PATH_MAX];
1326	char			path[PATH_MAX];
1327	menu_t			*menu;
1328	char			menu_root[PATH_MAX];
1329	struct stat		sb;
1330	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1331	char			*special = NULL;
1332	char			*pool = NULL;
1333	zfs_mnted_t		zmnted;
1334	char			*zmntpt = NULL;
1335	char			*osdev;
1336	char			*osroot;
1337	const char		*fcn = "bam_menu()";
1338
1339	/*
1340	 * Menu sub-command only applies to GRUB (i.e. x86)
1341	 */
1342	if (!is_grub(bam_alt_root ? bam_root : "/")) {
1343		bam_error(_("not a GRUB 0.97 based Illumos instance. "
1344		    "Operation not supported\n"));
1345		return (BAM_ERROR);
1346	}
1347
1348	/*
1349	 * Check arguments
1350	 */
1351	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1352	if (ret == BAM_ERROR) {
1353		return (BAM_ERROR);
1354	}
1355
1356	assert(bam_root);
1357
1358	(void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1359	osdev = osroot = NULL;
1360
1361	if (strcmp(subcmd, "update_entry") == 0) {
1362		assert(opt);
1363
1364		osdev = strtok(opt, ",");
1365		assert(osdev);
1366		osroot = strtok(NULL, ",");
1367		if (osroot) {
1368			/* fixup bam_root so that it points at osroot */
1369			if (realpath(osroot, rootbuf) == NULL) {
1370				bam_error(_("cannot resolve path %s: %s\n"),
1371				    osroot, strerror(errno));
1372				return (BAM_ERROR);
1373			}
1374			bam_alt_root = 1;
1375			bam_root  = rootbuf;
1376			bam_rootlen = strlen(rootbuf);
1377		}
1378	}
1379
1380	/*
1381	 * We support menu on PCFS (under certain conditions), but
1382	 * not the OS root
1383	 */
1384	if (is_pcfs(bam_root)) {
1385		bam_error(_("root <%s> on PCFS is not supported\n"), bam_root);
1386		return (BAM_ERROR);
1387	}
1388
1389	if (stat(menu_root, &sb) == -1) {
1390		bam_error(_("cannot find GRUB menu\n"));
1391		return (BAM_ERROR);
1392	}
1393
1394	BAM_DPRINTF(("%s: menu root is %s\n", fcn, menu_root));
1395
1396	/*
1397	 * We no longer use the GRUB slice file. If it exists, then
1398	 * the user is doing something that is unsupported (such as
1399	 * standard upgrading an old Live Upgrade BE). If that
1400	 * happens, mimic existing behavior i.e. pretend that it is
1401	 * not a BE. Emit a warning though.
1402	 */
1403	if (bam_alt_root) {
1404		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
1405		    GRUB_slice);
1406	} else {
1407		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1408	}
1409
1410	if (bam_verbose && stat(path, &sb) == 0)
1411		bam_error(_("unsupported GRUB slice file (%s) exists - "
1412		    "ignoring.\n"), path);
1413
1414	if (is_zfs(menu_root)) {
1415		assert(strcmp(menu_root, bam_root) == 0);
1416		special = get_special(menu_root);
1417		INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1418		if (special == NULL) {
1419			bam_error(_("cant find special file for "
1420			    "mount-point %s\n"), menu_root);
1421			return (BAM_ERROR);
1422		}
1423		pool = strtok(special, "/");
1424		INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1425		if (pool == NULL) {
1426			free(special);
1427			bam_error(_("cant find pool for mount-point %s\n"),
1428			    menu_root);
1429			return (BAM_ERROR);
1430		}
1431		BAM_DPRINTF(("%s: derived pool=%s from special\n", fcn, pool));
1432
1433		zmntpt = mount_top_dataset(pool, &zmnted);
1434		INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1435		if (zmntpt == NULL) {
1436			bam_error(_("cannot mount pool dataset for pool: %s\n"),
1437			    pool);
1438			free(special);
1439			return (BAM_ERROR);
1440		}
1441		BAM_DPRINTF(("%s: top dataset mountpoint=%s\n", fcn, zmntpt));
1442
1443		(void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1444		BAM_DPRINTF(("%s: zfs menu_root=%s\n", fcn, menu_root));
1445	}
1446
1447	elide_trailing_slash(menu_root, clean_menu_root,
1448	    sizeof (clean_menu_root));
1449
1450	BAM_DPRINTF(("%s: cleaned menu root is <%s>\n", fcn, clean_menu_root));
1451
1452	(void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1453	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1454
1455	BAM_DPRINTF(("%s: menu path is: %s\n", fcn, menu_path));
1456
1457	/*
1458	 * If listing the menu, display the menu location
1459	 */
1460	if (strcmp(subcmd, "list_entry") == 0)
1461		bam_print(_("the location for the active GRUB menu is: %s\n"),
1462		    menu_path);
1463
1464	if ((menu = menu_read(menu_path)) == NULL) {
1465		bam_error(_("cannot find GRUB menu file: %s\n"), menu_path);
1466		free(special);
1467
1468		return (BAM_ERROR);
1469	}
1470
1471	/*
1472	 * We already checked the following case in
1473	 * check_subcmd_and_suboptions() above. Complete the
1474	 * final step now.
1475	 */
1476	if (strcmp(subcmd, "set_option") == 0) {
1477		assert(largc == 1 && largv[0] && largv[1] == NULL);
1478		opt = largv[0];
1479	} else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1480	    (strcmp(subcmd, "list_setting") != 0)) {
1481		assert(largc == 0 && largv == NULL);
1482	}
1483
1484	ret = get_boot_cap(bam_root);
1485	if (ret != BAM_SUCCESS) {
1486		BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1487		goto out;
1488	}
1489
1490	/*
1491	 * Once the sub-cmd handler has run
1492	 * only the line field is guaranteed to have valid values
1493	 */
1494	if (strcmp(subcmd, "update_entry") == 0) {
1495		ret = f(menu, menu_root, osdev);
1496	} else if (strcmp(subcmd, "upgrade") == 0) {
1497		ret = f(menu, bam_root, menu_root);
1498	} else if (strcmp(subcmd, "list_entry") == 0) {
1499		ret = f(menu, menu_path, opt);
1500	} else if (strcmp(subcmd, "list_setting") == 0) {
1501		ret = f(menu, ((largc > 0) ? largv[0] : ""),
1502		    ((largc > 1) ? largv[1] : ""));
1503	} else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1504		if (is_sparc()) {
1505			bam_error(_("%s operation unsupported on SPARC "
1506			    "machines\n"), subcmd);
1507			ret = BAM_ERROR;
1508		} else {
1509			ret = f(menu, bam_root, NULL);
1510		}
1511	} else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1512		if (is_sparc()) {
1513			bam_error(_("%s operation unsupported on SPARC "
1514			    "machines\n"), subcmd);
1515			ret = BAM_ERROR;
1516		} else {
1517			char *extra_args = NULL;
1518
1519			/*
1520			 * Compress all arguments passed in the largv[] array
1521			 * into one string that can then be appended to the
1522			 * end of the kernel$ string the routine to enable the
1523			 * hypervisor will build.
1524			 *
1525			 * This allows the caller to supply arbitrary unparsed
1526			 * arguments, such as dom0 memory settings or APIC
1527			 * options.
1528			 *
1529			 * This concatenation will be done without ANY syntax
1530			 * checking whatsoever, so it's the responsibility of
1531			 * the caller to make sure the arguments are valid and
1532			 * do not duplicate arguments the conversion routines
1533			 * may create.
1534			 */
1535			if (largc > 0) {
1536				int extra_len, i;
1537
1538				for (extra_len = 0, i = 0; i < largc; i++)
1539					extra_len += strlen(largv[i]);
1540
1541				/*
1542				 * Allocate space for argument strings,
1543				 * intervening spaces and terminating NULL.
1544				 */
1545				extra_args = alloca(extra_len + largc);
1546
1547				(void) strcpy(extra_args, largv[0]);
1548
1549				for (i = 1; i < largc; i++) {
1550					(void) strcat(extra_args, " ");
1551					(void) strcat(extra_args, largv[i]);
1552				}
1553			}
1554
1555			ret = f(menu, bam_root, extra_args);
1556		}
1557	} else
1558		ret = f(menu, NULL, opt);
1559
1560	if (ret == BAM_WRITE) {
1561		BAM_DPRINTF(("%s: writing menu to clean-menu-root: <%s>\n",
1562		    fcn, clean_menu_root));
1563		ret = menu_write(clean_menu_root, menu);
1564	}
1565
1566out:
1567	INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1568	assert((is_zfs(menu_root)) ^ (pool == NULL));
1569	if (pool) {
1570		(void) umount_top_dataset(pool, zmnted, zmntpt);
1571		free(special);
1572	}
1573	menu_free(menu);
1574	return (ret);
1575}
1576
1577
1578static error_t
1579bam_archive(
1580	char *subcmd,
1581	char *opt)
1582{
1583	error_t			ret;
1584	error_t			(*f)(char *root, char *opt);
1585	const char		*fcn = "bam_archive()";
1586
1587	/*
1588	 * Add trailing / for archive subcommands
1589	 */
1590	if (rootbuf[strlen(rootbuf) - 1] != '/')
1591		(void) strcat(rootbuf, "/");
1592	bam_rootlen = strlen(rootbuf);
1593
1594	/*
1595	 * Check arguments
1596	 */
1597	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1598	if (ret != BAM_SUCCESS) {
1599		return (BAM_ERROR);
1600	}
1601
1602	ret = get_boot_cap(rootbuf);
1603	if (ret != BAM_SUCCESS) {
1604		BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1605		return (ret);
1606	}
1607
1608	/*
1609	 * Check archive not supported with update_all
1610	 * since it is awkward to display out-of-sync
1611	 * information for each BE.
1612	 */
1613	if (bam_check && strcmp(subcmd, "update_all") == 0) {
1614		bam_error(_("the check option is not supported with "
1615		    "subcmd: %s\n"), subcmd);
1616		return (BAM_ERROR);
1617	}
1618
1619	if (strcmp(subcmd, "update_all") == 0)
1620		bam_update_all = 1;
1621
1622#if !defined(_OBP)
1623	ucode_install(bam_root);
1624#endif
1625
1626	ret = f(bam_root, opt);
1627
1628	bam_update_all = 0;
1629
1630	return (ret);
1631}
1632
1633/*PRINTFLIKE1*/
1634void
1635bam_error(char *format, ...)
1636{
1637	va_list ap;
1638
1639	va_start(ap, format);
1640	(void) fprintf(stderr, "%s: ", prog);
1641	(void) vfprintf(stderr, format, ap);
1642	va_end(ap);
1643}
1644
1645/*PRINTFLIKE1*/
1646void
1647bam_derror(char *format, ...)
1648{
1649	va_list ap;
1650
1651	assert(bam_debug);
1652
1653	va_start(ap, format);
1654	(void) fprintf(stderr, "DEBUG: ");
1655	(void) vfprintf(stderr, format, ap);
1656	va_end(ap);
1657}
1658
1659/*PRINTFLIKE1*/
1660void
1661bam_print(char *format, ...)
1662{
1663	va_list ap;
1664
1665	va_start(ap, format);
1666	(void) vfprintf(stdout, format, ap);
1667	va_end(ap);
1668}
1669
1670/*PRINTFLIKE1*/
1671void
1672bam_print_stderr(char *format, ...)
1673{
1674	va_list ap;
1675
1676	va_start(ap, format);
1677	(void) vfprintf(stderr, format, ap);
1678	va_end(ap);
1679}
1680
1681void
1682bam_exit(int excode)
1683{
1684	restore_env();
1685	bam_unlock();
1686	exit(excode);
1687}
1688
1689static void
1690bam_lock(void)
1691{
1692	struct flock lock;
1693	pid_t pid;
1694
1695	if (bam_skip_lock)
1696		return;
1697
1698	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1699	if (bam_lock_fd < 0) {
1700		/*
1701		 * We may be invoked early in boot for archive verification.
1702		 * In this case, root is readonly and /var/run may not exist.
1703		 * Proceed without the lock
1704		 */
1705		if (errno == EROFS || errno == ENOENT) {
1706			bam_root_readonly = 1;
1707			return;
1708		}
1709
1710		bam_error(_("failed to open file: %s: %s\n"),
1711		    BAM_LOCK_FILE, strerror(errno));
1712		bam_exit(1);
1713	}
1714
1715	lock.l_type = F_WRLCK;
1716	lock.l_whence = SEEK_SET;
1717	lock.l_start = 0;
1718	lock.l_len = 0;
1719
1720	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1721		if (errno != EACCES && errno != EAGAIN) {
1722			bam_error(_("failed to lock file: %s: %s\n"),
1723			    BAM_LOCK_FILE, strerror(errno));
1724			(void) close(bam_lock_fd);
1725			bam_lock_fd = -1;
1726			bam_exit(1);
1727		}
1728		pid = 0;
1729		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1730		bam_print(
1731		    _("another instance of bootadm (pid %lu) is running\n"),
1732		    pid);
1733
1734		lock.l_type = F_WRLCK;
1735		lock.l_whence = SEEK_SET;
1736		lock.l_start = 0;
1737		lock.l_len = 0;
1738		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1739			bam_error(_("failed to lock file: %s: %s\n"),
1740			    BAM_LOCK_FILE, strerror(errno));
1741			(void) close(bam_lock_fd);
1742			bam_lock_fd = -1;
1743			bam_exit(1);
1744		}
1745	}
1746
1747	/* We own the lock now */
1748	pid = getpid();
1749	(void) write(bam_lock_fd, &pid, sizeof (pid));
1750}
1751
1752static void
1753bam_unlock(void)
1754{
1755	struct flock unlock;
1756
1757	if (bam_skip_lock)
1758		return;
1759
1760	/*
1761	 * NOP if we don't hold the lock
1762	 */
1763	if (bam_lock_fd < 0) {
1764		return;
1765	}
1766
1767	unlock.l_type = F_UNLCK;
1768	unlock.l_whence = SEEK_SET;
1769	unlock.l_start = 0;
1770	unlock.l_len = 0;
1771
1772	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1773		bam_error(_("failed to unlock file: %s: %s\n"),
1774		    BAM_LOCK_FILE, strerror(errno));
1775	}
1776
1777	if (close(bam_lock_fd) == -1) {
1778		bam_error(_("failed to close file: %s: %s\n"),
1779		    BAM_LOCK_FILE, strerror(errno));
1780	}
1781	bam_lock_fd = -1;
1782}
1783
1784static error_t
1785list_archive(char *root, char *opt)
1786{
1787	filelist_t flist;
1788	filelist_t *flistp = &flist;
1789	line_t *lp;
1790
1791	assert(root);
1792	assert(opt == NULL);
1793
1794	flistp->head = flistp->tail = NULL;
1795	if (read_list(root, flistp) != BAM_SUCCESS) {
1796		return (BAM_ERROR);
1797	}
1798	assert(flistp->head && flistp->tail);
1799
1800	for (lp = flistp->head; lp; lp = lp->next) {
1801		bam_print(_("%s\n"), lp->line);
1802	}
1803
1804	filelist_free(flistp);
1805
1806	return (BAM_SUCCESS);
1807}
1808
1809/*
1810 * This routine writes a list of lines to a file.
1811 * The list is *not* freed
1812 */
1813static error_t
1814list2file(char *root, char *tmp, char *final, line_t *start)
1815{
1816	char		tmpfile[PATH_MAX];
1817	char		path[PATH_MAX];
1818	FILE		*fp;
1819	int		ret;
1820	struct stat	sb;
1821	mode_t		mode;
1822	uid_t		root_uid;
1823	gid_t		sys_gid;
1824	struct passwd	*pw;
1825	struct group	*gp;
1826	const char	*fcn = "list2file()";
1827
1828	(void) snprintf(path, sizeof (path), "%s%s", root, final);
1829
1830	if (start == NULL) {
1831		/* Empty GRUB menu */
1832		if (stat(path, &sb) != -1) {
1833			bam_print(_("file is empty, deleting file: %s\n"),
1834			    path);
1835			if (unlink(path) != 0) {
1836				bam_error(_("failed to unlink file: %s: %s\n"),
1837				    path, strerror(errno));
1838				return (BAM_ERROR);
1839			} else {
1840				return (BAM_SUCCESS);
1841			}
1842		}
1843		return (BAM_SUCCESS);
1844	}
1845
1846	/*
1847	 * Preserve attributes of existing file if possible,
1848	 * otherwise ask the system for uid/gid of root/sys.
1849	 * If all fails, fall back on hard-coded defaults.
1850	 */
1851	if (stat(path, &sb) != -1) {
1852		mode = sb.st_mode;
1853		root_uid = sb.st_uid;
1854		sys_gid = sb.st_gid;
1855	} else {
1856		mode = DEFAULT_DEV_MODE;
1857		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1858			root_uid = pw->pw_uid;
1859		} else {
1860			bam_error(_("getpwnam: uid for %s failed, "
1861			    "defaulting to %d\n"),
1862			    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1863			root_uid = (uid_t)DEFAULT_DEV_UID;
1864		}
1865		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1866			sys_gid = gp->gr_gid;
1867		} else {
1868			bam_error(_("getgrnam: gid for %s failed, "
1869			    "defaulting to %d\n"),
1870			    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1871			sys_gid = (gid_t)DEFAULT_DEV_GID;
1872		}
1873	}
1874
1875	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1876
1877	/* Truncate tmpfile first */
1878	fp = fopen(tmpfile, "w");
1879	if (fp == NULL) {
1880		bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1881		    strerror(errno));
1882		return (BAM_ERROR);
1883	}
1884	ret = fclose(fp);
1885	INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1886	if (ret == EOF) {
1887		bam_error(_("failed to close file: %s: %s\n"),
1888		    tmpfile, strerror(errno));
1889		return (BAM_ERROR);
1890	}
1891
1892	/* Now open it in append mode */
1893	fp = fopen(tmpfile, "a");
1894	if (fp == NULL) {
1895		bam_error(_("failed to open file: %s: %s\n"), tmpfile,
1896		    strerror(errno));
1897		return (BAM_ERROR);
1898	}
1899
1900	for (; start; start = start->next) {
1901		ret = s_fputs(start->line, fp);
1902		INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1903		if (ret == EOF) {
1904			bam_error(_("write to file failed: %s: %s\n"),
1905			    tmpfile, strerror(errno));
1906			(void) fclose(fp);
1907			return (BAM_ERROR);
1908		}
1909	}
1910
1911	ret = fclose(fp);
1912	INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1913	if (ret == EOF) {
1914		bam_error(_("failed to close file: %s: %s\n"),
1915		    tmpfile, strerror(errno));
1916		return (BAM_ERROR);
1917	}
1918
1919	/*
1920	 * Set up desired attributes.  Ignore failures on filesystems
1921	 * not supporting these operations - pcfs reports unsupported
1922	 * operations as EINVAL.
1923	 */
1924	ret = chmod(tmpfile, mode);
1925	if (ret == -1 &&
1926	    errno != EINVAL && errno != ENOTSUP) {
1927		bam_error(_("chmod operation on %s failed - %s\n"),
1928		    tmpfile, strerror(errno));
1929		return (BAM_ERROR);
1930	}
1931
1932	ret = chown(tmpfile, root_uid, sys_gid);
1933	if (ret == -1 &&
1934	    errno != EINVAL && errno != ENOTSUP) {
1935		bam_error(_("chgrp operation on %s failed - %s\n"),
1936		    tmpfile, strerror(errno));
1937		return (BAM_ERROR);
1938	}
1939
1940	/*
1941	 * Do an atomic rename
1942	 */
1943	ret = rename(tmpfile, path);
1944	INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1945	if (ret != 0) {
1946		bam_error(_("rename to file failed: %s: %s\n"), path,
1947		    strerror(errno));
1948		return (BAM_ERROR);
1949	}
1950
1951	BAM_DPRINTF(("%s: wrote file successfully: %s\n", fcn, path));
1952	return (BAM_SUCCESS);
1953}
1954
1955/*
1956 * Checks if the path specified (without the file name at the end) exists
1957 * and creates it if not. If the path exists and is not a directory, an attempt
1958 * to unlink is made.
1959 */
1960static int
1961setup_path(char *path)
1962{
1963	char		*p;
1964	int		ret;
1965	struct stat	sb;
1966
1967	p = strrchr(path, '/');
1968	if (p != NULL) {
1969		*p = '\0';
1970		if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1971			/* best effort attempt, mkdirp will catch the error */
1972			(void) unlink(path);
1973			if (bam_verbose)
1974				bam_print(_("need to create directory "
1975				    "path for %s\n"), path);
1976			ret = mkdirp(path, DIR_PERMS);
1977			if (ret == -1) {
1978				bam_error(_("mkdir of %s failed: %s\n"),
1979				    path, strerror(errno));
1980				*p = '/';
1981				return (BAM_ERROR);
1982			}
1983		}
1984		*p = '/';
1985		return (BAM_SUCCESS);
1986	}
1987	return (BAM_SUCCESS);
1988}
1989
1990typedef union {
1991	gzFile	gzfile;
1992	int	fdfile;
1993} outfile;
1994
1995typedef struct {
1996	char		path[PATH_MAX];
1997	outfile		out;
1998} cachefile;
1999
2000static int
2001setup_file(char *base, const char *path, cachefile *cf)
2002{
2003	int	ret;
2004	char	*strip;
2005
2006	/* init gzfile or fdfile in case we fail before opening */
2007	if (bam_direct == BAM_DIRECT_DBOOT)
2008		cf->out.gzfile = NULL;
2009	else
2010		cf->out.fdfile = -1;
2011
2012	/* strip the trailing altroot path */
2013	strip = (char *)path + strlen(rootbuf);
2014
2015	ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
2016	if (ret >= sizeof (cf->path)) {
2017		bam_error(_("unable to create path on mountpoint %s, "
2018		    "path too long\n"), rootbuf);
2019		return (BAM_ERROR);
2020	}
2021
2022	/* Check if path is present in the archive cache directory */
2023	if (setup_path(cf->path) == BAM_ERROR)
2024		return (BAM_ERROR);
2025
2026	if (bam_direct == BAM_DIRECT_DBOOT) {
2027		if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
2028			bam_error(_("failed to open file: %s: %s\n"),
2029			    cf->path, strerror(errno));
2030			return (BAM_ERROR);
2031		}
2032		(void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
2033		    Z_DEFAULT_STRATEGY);
2034	} else {
2035		if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
2036		    == -1) {
2037			bam_error(_("failed to open file: %s: %s\n"),
2038			    cf->path, strerror(errno));
2039			return (BAM_ERROR);
2040		}
2041	}
2042
2043	return (BAM_SUCCESS);
2044}
2045
2046static int
2047cache_write(cachefile cf, char *buf, int size)
2048{
2049	int	err;
2050
2051	if (bam_direct == BAM_DIRECT_DBOOT) {
2052		if (gzwrite(cf.out.gzfile, buf, size) < 1) {
2053			bam_error(_("failed to write to %s\n"),
2054			    gzerror(cf.out.gzfile, &err));
2055			if (err == Z_ERRNO && bam_verbose) {
2056				bam_error(_("write to file failed: %s: %s\n"),
2057				    cf.path, strerror(errno));
2058			}
2059			return (BAM_ERROR);
2060		}
2061	} else {
2062		if (write(cf.out.fdfile, buf, size) < 1) {
2063			bam_error(_("write to file failed: %s: %s\n"),
2064			    cf.path, strerror(errno));
2065			return (BAM_ERROR);
2066		}
2067	}
2068	return (BAM_SUCCESS);
2069}
2070
2071static int
2072cache_close(cachefile cf)
2073{
2074	int	ret;
2075
2076	if (bam_direct == BAM_DIRECT_DBOOT) {
2077		if (cf.out.gzfile) {
2078			ret = gzclose(cf.out.gzfile);
2079			if (ret != Z_OK) {
2080				bam_error(_("failed to close file: %s: %s\n"),
2081				    cf.path, strerror(errno));
2082				return (BAM_ERROR);
2083			}
2084		}
2085	} else {
2086		if (cf.out.fdfile != -1) {
2087			ret = close(cf.out.fdfile);
2088			if (ret != 0) {
2089				bam_error(_("failed to close file: %s: %s\n"),
2090				    cf.path, strerror(errno));
2091				return (BAM_ERROR);
2092			}
2093		}
2094	}
2095
2096	return (BAM_SUCCESS);
2097}
2098
2099static int
2100dircache_updatefile(const char *path)
2101{
2102	int		ret, exitcode;
2103	char		buf[4096 * 4];
2104	FILE		*infile;
2105	cachefile	outfile, outupdt;
2106
2107	if (bam_nowrite()) {
2108		set_dir_flag(NEED_UPDATE);
2109		return (BAM_SUCCESS);
2110	}
2111
2112	if (!has_cachedir())
2113		return (BAM_SUCCESS);
2114
2115	if ((infile = fopen(path, "rb")) == NULL) {
2116		bam_error(_("failed to open file: %s: %s\n"), path,
2117		    strerror(errno));
2118		return (BAM_ERROR);
2119	}
2120
2121	ret = setup_file(get_cachedir(), path, &outfile);
2122	if (ret == BAM_ERROR) {
2123		exitcode = BAM_ERROR;
2124		goto out;
2125	}
2126	if (!is_dir_flag_on(NO_EXTEND)) {
2127		ret = setup_file(get_updatedir(), path, &outupdt);
2128		if (ret == BAM_ERROR)
2129			set_dir_flag(NO_EXTEND);
2130	}
2131
2132	while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2133		if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2134			exitcode = BAM_ERROR;
2135			goto out;
2136		}
2137		if (!is_dir_flag_on(NO_EXTEND))
2138			if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2139				set_dir_flag(NO_EXTEND);
2140	}
2141
2142	set_dir_flag(NEED_UPDATE);
2143	get_count()++;
2144	if (get_count() > COUNT_MAX)
2145		set_dir_flag(NO_EXTEND);
2146	exitcode = BAM_SUCCESS;
2147out:
2148	(void) fclose(infile);
2149	if (cache_close(outfile) == BAM_ERROR)
2150		exitcode = BAM_ERROR;
2151	if (!is_dir_flag_on(NO_EXTEND) &&
2152	    cache_close(outupdt) == BAM_ERROR)
2153		exitcode = BAM_ERROR;
2154	if (exitcode == BAM_ERROR)
2155		set_flag(UPDATE_ERROR);
2156	return (exitcode);
2157}
2158
2159static int
2160dircache_updatedir(const char *path, int updt)
2161{
2162	int		ret;
2163	char		dpath[PATH_MAX];
2164	char		*strip;
2165	struct stat	sb;
2166
2167	strip = (char *)path + strlen(rootbuf);
2168
2169	ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2170	    get_updatedir() : get_cachedir(), strip);
2171
2172	if (ret >= sizeof (dpath)) {
2173		bam_error(_("unable to create path on mountpoint %s, "
2174		    "path too long\n"), rootbuf);
2175		set_flag(UPDATE_ERROR);
2176		return (BAM_ERROR);
2177	}
2178
2179	if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2180		return (BAM_SUCCESS);
2181
2182	if (updt) {
2183		if (!is_dir_flag_on(NO_EXTEND))
2184			if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2185				set_dir_flag(NO_EXTEND);
2186	} else {
2187		if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2188			set_flag(UPDATE_ERROR);
2189			return (BAM_ERROR);
2190		}
2191	}
2192
2193	set_dir_flag(NEED_UPDATE);
2194	return (BAM_SUCCESS);
2195}
2196
2197#define	DO_CACHE_DIR	0
2198#define	DO_UPDATE_DIR	1
2199
2200#if defined(_LP64) || defined(_LONGLONG_TYPE)
2201typedef		Elf64_Ehdr	_elfhdr;
2202#else
2203typedef		Elf32_Ehdr	_elfhdr;
2204#endif
2205
2206/*
2207 * This routine updates the contents of the cache directory
2208 */
2209static int
2210update_dircache(const char *path, int flags)
2211{
2212	int rc = BAM_SUCCESS;
2213
2214	switch (flags) {
2215	case FTW_F:
2216		{
2217		int	fd;
2218		_elfhdr	elf;
2219
2220		if ((fd = open(path, O_RDONLY)) < 0) {
2221			bam_error(_("failed to open file: %s: %s\n"),
2222			    path, strerror(errno));
2223			set_flag(UPDATE_ERROR);
2224			rc = BAM_ERROR;
2225			break;
2226		}
2227
2228		/*
2229		 * libelf and gelf would be a cleaner and easier way to handle
2230		 * this, but libelf fails compilation if _ILP32 is defined &&
2231		 * _FILE_OFFSET_BITS is != 32 ...
2232		 */
2233		if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2234			bam_error(_("read failed for file: %s: %s\n"),
2235			    path, strerror(errno));
2236			set_flag(UPDATE_ERROR);
2237			(void) close(fd);
2238			rc = BAM_ERROR;
2239			break;
2240		}
2241		(void) close(fd);
2242
2243		if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2244			/* Not an ELF file, include in archive */
2245			rc = dircache_updatefile(path);
2246		} else {
2247			/* Include 64-bit ELF files only */
2248			switch (elf.e_ident[EI_CLASS]) {
2249			case ELFCLASS32:
2250				bam_print(_("WARNING: ELF file %s is 32-bit "
2251				    "and will be excluded\n"), path);
2252				break;
2253			case ELFCLASS64:
2254				rc = dircache_updatefile(path);
2255				break;
2256			default:
2257				bam_print(_("WARNING: ELF file %s is neither "
2258				    "32-bit nor 64-bit\n"), path);
2259				break;
2260			}
2261		}
2262		break;
2263		}
2264	case FTW_D:
2265		if (strstr(path, "/amd64") != NULL) {
2266			if (has_cachedir()) {
2267				rc = dircache_updatedir(path, DO_UPDATE_DIR);
2268				if (rc == BAM_SUCCESS)
2269					rc = dircache_updatedir(path,
2270					    DO_CACHE_DIR);
2271			}
2272		}
2273		break;
2274	default:
2275		rc = BAM_ERROR;
2276		break;
2277	}
2278
2279	return (rc);
2280}
2281
2282/*ARGSUSED*/
2283static int
2284cmpstat(
2285	const char *file,
2286	const struct stat *st,
2287	int flags,
2288	struct FTW *ftw)
2289{
2290	uint_t		sz;
2291	uint64_t	*value;
2292	uint64_t	filestat[2];
2293	int		error, ret, status;
2294
2295	struct safefile *safefilep;
2296	FILE		*fp;
2297	struct stat	sb;
2298	regex_t re;
2299
2300	/*
2301	 * On SPARC we create/update links too.
2302	 */
2303	if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2304	    !is_flag_on(IS_SPARC_TARGET)))
2305		return (0);
2306
2307	/*
2308	 * Ignore broken links
2309	 */
2310	if (flags == FTW_SL && stat(file, &sb) < 0)
2311		return (0);
2312
2313	/*
2314	 * new_nvlp may be NULL if there were errors earlier
2315	 * but this is not fatal to update determination.
2316	 */
2317	if (walk_arg.new_nvlp) {
2318		filestat[0] = st->st_size;
2319		filestat[1] = st->st_mtime;
2320		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2321		    file + bam_rootlen, filestat, 2);
2322		if (error)
2323			bam_error(_("failed to update stat data for: %s: %s\n"),
2324			    file, strerror(error));
2325	}
2326
2327	/*
2328	 * If we are invoked as part of system/filesystem/boot-archive, then
2329	 * there are a number of things we should not worry about
2330	 */
2331	if (bam_smf_check) {
2332		/* ignore amd64 modules unless we are booted amd64. */
2333		if (!is_amd64() && strstr(file, "/amd64/") != 0)
2334			return (0);
2335
2336		/* read in list of safe files */
2337		if (safefiles == NULL) {
2338			fp = fopen("/boot/solaris/filelist.safe", "r");
2339			if (fp != NULL) {
2340				safefiles = s_calloc(1,
2341				    sizeof (struct safefile));
2342				safefilep = safefiles;
2343				safefilep->name = s_calloc(1, MAXPATHLEN +
2344				    MAXNAMELEN);
2345				safefilep->next = NULL;
2346				while (s_fgets(safefilep->name, MAXPATHLEN +
2347				    MAXNAMELEN, fp) != NULL) {
2348					safefilep->next = s_calloc(1,
2349					    sizeof (struct safefile));
2350					safefilep = safefilep->next;
2351					safefilep->name = s_calloc(1,
2352					    MAXPATHLEN + MAXNAMELEN);
2353					safefilep->next = NULL;
2354				}
2355				(void) fclose(fp);
2356			}
2357		}
2358	}
2359
2360	/*
2361	 * On SPARC we create a -path-list file for mkisofs
2362	 */
2363	if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2364		if (flags != FTW_D) {
2365			char	*strip;
2366
2367			strip = (char *)file + strlen(rootbuf);
2368			(void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2369			    file);
2370		}
2371	}
2372
2373	/*
2374	 * We are transitioning from the old model to the dircache or the cache
2375	 * directory was removed: create the entry without further checkings.
2376	 */
2377	if (is_flag_on(NEED_CACHE_DIR)) {
2378		if (bam_verbose)
2379			bam_print(_("    new     %s\n"), file);
2380
2381		if (is_flag_on(IS_SPARC_TARGET)) {
2382			set_dir_flag(NEED_UPDATE);
2383			return (0);
2384		}
2385
2386		ret = update_dircache(file, flags);
2387		if (ret == BAM_ERROR) {
2388			bam_error(_("directory cache update failed for %s\n"),
2389			    file);
2390			return (-1);
2391		}
2392
2393		return (0);
2394	}
2395
2396	/*
2397	 * We need an update if file doesn't exist in old archive
2398	 */
2399	if (walk_arg.old_nvlp == NULL ||
2400	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2401	    file + bam_rootlen, &value, &sz) != 0) {
2402		if (bam_smf_check)	/* ignore new during smf check */
2403			return (0);
2404
2405		if (is_flag_on(IS_SPARC_TARGET)) {
2406			set_dir_flag(NEED_UPDATE);
2407		} else {
2408			ret = update_dircache(file, flags);
2409			if (ret == BAM_ERROR) {
2410				bam_error(_("directory cache update "
2411				    "failed for %s\n"), file);
2412				return (-1);
2413			}
2414		}
2415
2416		if (bam_verbose)
2417			bam_print(_("    new     %s\n"), file);
2418		return (0);
2419	}
2420
2421	/*
2422	 * If we got there, the file is already listed as to be included in the
2423	 * iso image. We just need to know if we are going to rebuild it or not
2424	 */
2425	if (is_flag_on(IS_SPARC_TARGET) &&
2426	    is_dir_flag_on(NEED_UPDATE) && !bam_nowrite())
2427		return (0);
2428	/*
2429	 * File exists in old archive. Check if file has changed
2430	 */
2431	assert(sz == 2);
2432	bcopy(value, filestat, sizeof (filestat));
2433
2434	if (flags != FTW_D && (filestat[0] != st->st_size ||
2435	    filestat[1] != st->st_mtime)) {
2436		if (bam_smf_check) {
2437			safefilep = safefiles;
2438			while (safefilep != NULL &&
2439			    safefilep->name[0] != '\0') {
2440				if (regcomp(&re, safefilep->name,
2441				    REG_EXTENDED|REG_NOSUB) == 0) {
2442					status = regexec(&re,
2443					    file + bam_rootlen, 0, NULL, 0);
2444					regfree(&re);
2445					if (status == 0) {
2446						(void) creat(
2447						    NEED_UPDATE_SAFE_FILE,
2448						    0644);
2449						return (0);
2450					}
2451				}
2452				safefilep = safefilep->next;
2453			}
2454		}
2455
2456		if (is_flag_on(IS_SPARC_TARGET)) {
2457			set_dir_flag(NEED_UPDATE);
2458		} else {
2459			ret = update_dircache(file, flags);
2460			if (ret == BAM_ERROR) {
2461				bam_error(_("directory cache update failed "
2462				    "for %s\n"), file);
2463				return (-1);
2464			}
2465		}
2466
2467		/*
2468		 * Update self-assembly file if there are changes in
2469		 * /etc/system.d directory
2470		 */
2471		if (strstr(file, ETC_SYSTEM_DIR)) {
2472			ret = update_dircache(self_assembly, flags);
2473			if (ret == BAM_ERROR) {
2474				bam_error(_("directory cache update failed "
2475				    "for %s\n"), file);
2476				return (-1);
2477			}
2478		}
2479
2480		if (bam_verbose) {
2481			if (bam_smf_check)
2482				bam_print("    %s\n", file);
2483			else
2484				bam_print(_("    changed %s\n"), file);
2485		}
2486	}
2487
2488	return (0);
2489}
2490
2491/*
2492 * Remove a directory path recursively
2493 */
2494static int
2495rmdir_r(char *path)
2496{
2497	struct dirent	*d = NULL;
2498	DIR		*dir = NULL;
2499	char		tpath[PATH_MAX];
2500	struct stat	sb;
2501
2502	if ((dir = opendir(path)) == NULL)
2503		return (-1);
2504
2505	while ((d = readdir(dir)) != NULL) {
2506		if ((strcmp(d->d_name, ".") != 0) &&
2507		    (strcmp(d->d_name, "..") != 0)) {
2508			(void) snprintf(tpath, sizeof (tpath), "%s/%s",
2509			    path, d->d_name);
2510			if (stat(tpath, &sb) == 0) {
2511				if (sb.st_mode & S_IFDIR)
2512					(void) rmdir_r(tpath);
2513				else
2514					(void) remove(tpath);
2515			}
2516		}
2517	}
2518	return (remove(path));
2519}
2520
2521/*
2522 * Check if cache directory exists and, if not, create it and update flags
2523 * accordingly. If the path exists, but it's not a directory, a best effort
2524 * attempt to remove and recreate it is made.
2525 * If the user requested a 'purge', always recreate the directory from scratch.
2526 */
2527static int
2528set_cache_dir(char *root)
2529{
2530	struct stat	sb;
2531	int		ret = 0;
2532
2533	ret = build_path(get_cachedir(), sizeof (get_cachedir()),
2534	    root, ARCHIVE_PREFIX, CACHEDIR_SUFFIX);
2535
2536	if (ret >= sizeof (get_cachedir())) {
2537		bam_error(_("unable to create path on mountpoint %s, "
2538		    "path too long\n"), rootbuf);
2539		return (BAM_ERROR);
2540	}
2541
2542	if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2543		(void) rmdir_r(get_cachedir());
2544
2545	if (stat(get_cachedir(), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2546		/* best effort unlink attempt, mkdir will catch errors */
2547		(void) unlink(get_cachedir());
2548
2549		if (bam_verbose)
2550			bam_print(_("archive cache directory not found: %s\n"),
2551			    get_cachedir());
2552		ret = mkdir(get_cachedir(), DIR_PERMS);
2553		if (ret < 0) {
2554			bam_error(_("mkdir of %s failed: %s\n"),
2555			    get_cachedir(), strerror(errno));
2556			get_cachedir()[0] = '\0';
2557			return (ret);
2558		}
2559		set_flag(NEED_CACHE_DIR);
2560		set_dir_flag(NO_EXTEND);
2561	}
2562
2563	return (BAM_SUCCESS);
2564}
2565
2566static int
2567set_update_dir(char *root)
2568{
2569	struct stat	sb;
2570	int		ret;
2571
2572	if (is_dir_flag_on(NO_EXTEND))
2573		return (BAM_SUCCESS);
2574
2575	if (!bam_extend) {
2576		set_dir_flag(NO_EXTEND);
2577		return (BAM_SUCCESS);
2578	}
2579
2580	ret = build_path(get_updatedir(), sizeof (get_updatedir()),
2581	    root, ARCHIVE_PREFIX, UPDATEDIR_SUFFIX);
2582
2583	if (ret >= sizeof (get_updatedir())) {
2584		bam_error(_("unable to create path on mountpoint %s, "
2585		    "path too long\n"), rootbuf);
2586		return (BAM_ERROR);
2587	}
2588
2589	if (stat(get_updatedir(), &sb) == 0) {
2590		if (S_ISDIR(sb.st_mode))
2591			ret = rmdir_r(get_updatedir());
2592		else
2593			ret = unlink(get_updatedir());
2594
2595		if (ret != 0)
2596			set_dir_flag(NO_EXTEND);
2597	}
2598
2599	if (mkdir(get_updatedir(), DIR_PERMS) < 0)
2600		set_dir_flag(NO_EXTEND);
2601
2602	return (BAM_SUCCESS);
2603}
2604
2605static int
2606is_valid_archive(char *root)
2607{
2608	char		archive_path[PATH_MAX];
2609	char		timestamp_path[PATH_MAX];
2610	struct stat	sb, timestamp;
2611	int		ret;
2612
2613	ret = build_path(archive_path, sizeof (archive_path),
2614	    root, ARCHIVE_PREFIX, ARCHIVE_SUFFIX);
2615
2616	if (ret >= sizeof (archive_path)) {
2617		bam_error(_("unable to create path on mountpoint %s, "
2618		    "path too long\n"), rootbuf);
2619		return (BAM_ERROR);
2620	}
2621
2622	if (stat(archive_path, &sb) != 0) {
2623		if (bam_verbose && !bam_check)
2624			bam_print(_("archive not found: %s\n"), archive_path);
2625		set_dir_flag(NEED_UPDATE | NO_EXTEND);
2626		return (BAM_SUCCESS);
2627	}
2628
2629	/*
2630	 * The timestamp file is used to prevent stale files in the archive
2631	 * cache.
2632	 * Stale files can happen if the system is booted back and forth across
2633	 * the transition from bootadm-before-the-cache to
2634	 * bootadm-after-the-cache, since older versions of bootadm don't know
2635	 * about the existence of the archive cache.
2636	 *
2637	 * Since only bootadm-after-the-cache versions know about about this
2638	 * file, we require that the boot archive be older than this file.
2639	 */
2640	ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2641	    FILE_STAT_TIMESTAMP);
2642
2643	if (ret >= sizeof (timestamp_path)) {
2644		bam_error(_("unable to create path on mountpoint %s, "
2645		    "path too long\n"), rootbuf);
2646		return (BAM_ERROR);
2647	}
2648
2649	if (stat(timestamp_path, &timestamp) != 0 ||
2650	    sb.st_mtime > timestamp.st_mtime) {
2651		if (bam_verbose && !bam_check)
2652			bam_print(
2653			    _("archive cache is out of sync. Rebuilding.\n"));
2654		/*
2655		 * Don't generate a false positive for the boot-archive service
2656		 * but trigger an update of the archive cache in
2657		 * boot-archive-update.
2658		 */
2659		if (bam_smf_check) {
2660			(void) creat(NEED_UPDATE_FILE, 0644);
2661			return (BAM_SUCCESS);
2662		}
2663
2664		set_flag(INVALIDATE_CACHE);
2665		set_dir_flag(NEED_UPDATE | NO_EXTEND);
2666		return (BAM_SUCCESS);
2667	}
2668
2669	if (is_flag_on(IS_SPARC_TARGET))
2670		return (BAM_SUCCESS);
2671
2672	if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2673		if (bam_verbose && !bam_check)
2674			bam_print(_("archive %s is bigger than %d bytes and "
2675			    "will be rebuilt\n"), archive_path, BA_SIZE_MAX);
2676		set_dir_flag(NO_EXTEND);
2677	}
2678
2679	return (BAM_SUCCESS);
2680}
2681
2682/*
2683 * Check flags and presence of required files and directories.
2684 * The force flag and/or absence of files should
2685 * trigger an update.
2686 * Suppress stdout output if check (-n) option is set
2687 * (as -n should only produce parseable output.)
2688 */
2689static int
2690check_flags_and_files(char *root)
2691{
2692
2693	struct stat	sb;
2694	int		ret;
2695
2696	/*
2697	 * If archive is missing, create archive
2698	 */
2699	ret = is_valid_archive(root);
2700	if (ret == BAM_ERROR)
2701		return (BAM_ERROR);
2702
2703	if (bam_nowrite())
2704		return (BAM_SUCCESS);
2705
2706	/*
2707	 * check if cache directories exist on x86.
2708	 * check (and always open) the cache file on SPARC.
2709	 */
2710	if (is_sparc()) {
2711		ret = snprintf(get_cachedir(),
2712		    sizeof (get_cachedir()), "%s%s%s/%s", root,
2713		    ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2714
2715		if (ret >= sizeof (get_cachedir())) {
2716			bam_error(_("unable to create path on mountpoint %s, "
2717			    "path too long\n"), rootbuf);
2718			return (BAM_ERROR);
2719		}
2720
2721		if (stat(get_cachedir(), &sb) != 0) {
2722			set_flag(NEED_CACHE_DIR);
2723			set_dir_flag(NEED_UPDATE);
2724		}
2725
2726		walk_arg.sparcfile = fopen(get_cachedir(), "w");
2727		if (walk_arg.sparcfile == NULL) {
2728			bam_error(_("failed to open file: %s: %s\n"),
2729			    get_cachedir(), strerror(errno));
2730			return (BAM_ERROR);
2731		}
2732
2733		set_dir_present();
2734	} else {
2735		if (set_cache_dir(root) != 0)
2736			return (BAM_ERROR);
2737
2738		set_dir_present();
2739
2740		if (set_update_dir(root) != 0)
2741			return (BAM_ERROR);
2742	}
2743
2744	/*
2745	 * if force, create archive unconditionally
2746	 */
2747	if (bam_force) {
2748		set_dir_flag(NEED_UPDATE);
2749		if (bam_verbose)
2750			bam_print(_("forced update of archive requested\n"));
2751		return (BAM_SUCCESS);
2752	}
2753
2754	return (BAM_SUCCESS);
2755}
2756
2757static error_t
2758read_one_list(char *root, filelist_t  *flistp, char *filelist)
2759{
2760	char		path[PATH_MAX];
2761	FILE		*fp;
2762	char		buf[BAM_MAXLINE];
2763	const char	*fcn = "read_one_list()";
2764
2765	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2766
2767	fp = fopen(path, "r");
2768	if (fp == NULL) {
2769		BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2770		    fcn, path, strerror(errno)));
2771		return (BAM_ERROR);
2772	}
2773	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2774		/* skip blank lines */
2775		if (strspn(buf, " \t") == strlen(buf))
2776			continue;
2777		append_to_flist(flistp, buf);
2778	}
2779	if (fclose(fp) != 0) {
2780		bam_error(_("failed to close file: %s: %s\n"),
2781		    path, strerror(errno));
2782		return (BAM_ERROR);
2783	}
2784	return (BAM_SUCCESS);
2785}
2786
2787static error_t
2788read_list(char *root, filelist_t  *flistp)
2789{
2790	char		path[PATH_MAX];
2791	char		cmd[PATH_MAX];
2792	struct stat	sb;
2793	int		n, rval;
2794	const char	*fcn = "read_list()";
2795
2796	flistp->head = flistp->tail = NULL;
2797
2798	/*
2799	 * build and check path to extract_boot_filelist.ksh
2800	 */
2801	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2802	if (n >= sizeof (path)) {
2803		bam_error(_("archive filelist is empty\n"));
2804		return (BAM_ERROR);
2805	}
2806
2807	if (is_safe_exec(path) == BAM_ERROR)
2808		return (BAM_ERROR);
2809
2810	/*
2811	 * If extract_boot_filelist is present, exec it, otherwise read
2812	 * the filelists directly, for compatibility with older images.
2813	 */
2814	if (stat(path, &sb) == 0) {
2815		/*
2816		 * build arguments to exec extract_boot_filelist.ksh
2817		 */
2818		char *rootarg, *platarg;
2819		int platarglen = 1, rootarglen = 1;
2820		if (strlen(root) > 1)
2821			rootarglen += strlen(root) + strlen("-R ");
2822		if (bam_alt_platform)
2823			platarglen += strlen(bam_platform) + strlen("-p ");
2824		platarg = s_calloc(1, platarglen);
2825		rootarg = s_calloc(1, rootarglen);
2826		*platarg = 0;
2827		*rootarg = 0;
2828
2829		if (strlen(root) > 1) {
2830			(void) snprintf(rootarg, rootarglen,
2831			    "-R %s", root);
2832		}
2833		if (bam_alt_platform) {
2834			(void) snprintf(platarg, platarglen,
2835			    "-p %s", bam_platform);
2836		}
2837		n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2838		    path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2839		free(platarg);
2840		free(rootarg);
2841		if (n >= sizeof (cmd)) {
2842			bam_error(_("archive filelist is empty\n"));
2843			return (BAM_ERROR);
2844		}
2845		if (exec_cmd(cmd, flistp) != 0) {
2846			BAM_DPRINTF(("%s: failed to open archive "
2847			    "filelist: %s: %s\n", fcn, path, strerror(errno)));
2848			return (BAM_ERROR);
2849		}
2850	} else {
2851		/*
2852		 * Read current lists of files - only the first is mandatory
2853		 */
2854		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2855		if (rval != BAM_SUCCESS)
2856			return (rval);
2857		(void) read_one_list(root, flistp, ETC_FILE_LIST);
2858	}
2859
2860	if (flistp->head == NULL) {
2861		bam_error(_("archive filelist is empty\n"));
2862		return (BAM_ERROR);
2863	}
2864
2865	return (BAM_SUCCESS);
2866}
2867
2868static void
2869getoldstat(char *root)
2870{
2871	char		path[PATH_MAX];
2872	int		fd, error;
2873	struct stat	sb;
2874	char		*ostat;
2875
2876	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2877	fd = open(path, O_RDONLY);
2878	if (fd == -1) {
2879		if (bam_verbose)
2880			bam_print(_("failed to open file: %s: %s\n"),
2881			    path, strerror(errno));
2882		goto out_err;
2883	}
2884
2885	if (fstat(fd, &sb) != 0) {
2886		bam_error(_("stat of file failed: %s: %s\n"), path,
2887		    strerror(errno));
2888		goto out_err;
2889	}
2890
2891	ostat = s_calloc(1, sb.st_size);
2892
2893	if (read(fd, ostat, sb.st_size) != sb.st_size) {
2894		bam_error(_("read failed for file: %s: %s\n"), path,
2895		    strerror(errno));
2896		free(ostat);
2897		goto out_err;
2898	}
2899
2900	(void) close(fd);
2901	fd = -1;
2902
2903	walk_arg.old_nvlp = NULL;
2904	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2905
2906	free(ostat);
2907
2908	if (error) {
2909		bam_error(_("failed to unpack stat data: %s: %s\n"),
2910		    path, strerror(error));
2911		walk_arg.old_nvlp = NULL;
2912		goto out_err;
2913	} else {
2914		return;
2915	}
2916
2917out_err:
2918	if (fd != -1)
2919		(void) close(fd);
2920	set_dir_flag(NEED_UPDATE);
2921}
2922
2923/* Best effort stale entry removal */
2924static void
2925delete_stale(char *file)
2926{
2927	char		path[PATH_MAX];
2928	struct stat	sb;
2929
2930	(void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(), file);
2931	if (!bam_check && stat(path, &sb) == 0) {
2932		if (sb.st_mode & S_IFDIR)
2933			(void) rmdir_r(path);
2934		else
2935			(void) unlink(path);
2936
2937		set_dir_flag(NEED_UPDATE | NO_EXTEND);
2938	}
2939}
2940
2941/*
2942 * Checks if a file in the current (old) archive has
2943 * been deleted from the root filesystem. This is needed for
2944 * software like Trusted Extensions (TX) that switch early
2945 * in boot based on presence/absence of a kernel module.
2946 */
2947static void
2948check4stale(char *root)
2949{
2950	nvpair_t	*nvp;
2951	nvlist_t	*nvlp;
2952	char		*file;
2953	char		path[PATH_MAX];
2954
2955	/*
2956	 * Skip stale file check during smf check
2957	 */
2958	if (bam_smf_check)
2959		return;
2960
2961	/*
2962	 * If we need to (re)create the cache, there's no need to check for
2963	 * stale files
2964	 */
2965	if (is_flag_on(NEED_CACHE_DIR))
2966		return;
2967
2968	/* Nothing to do if no old stats */
2969	if ((nvlp = walk_arg.old_nvlp) == NULL)
2970		return;
2971
2972	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2973	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
2974		file = nvpair_name(nvp);
2975		if (file == NULL)
2976			continue;
2977		(void) snprintf(path, sizeof (path), "%s/%s",
2978		    root, file);
2979		if (access(path, F_OK) < 0) {
2980			if (bam_verbose)
2981				bam_print(_("    stale %s\n"), path);
2982
2983			if (is_flag_on(IS_SPARC_TARGET)) {
2984				set_dir_flag(NEED_UPDATE);
2985			} else {
2986				if (has_cachedir())
2987					delete_stale(file);
2988			}
2989		}
2990	}
2991}
2992
2993static void
2994create_newstat(void)
2995{
2996	int error;
2997
2998	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2999	if (error) {
3000		/*
3001		 * Not fatal - we can still create archive
3002		 */
3003		walk_arg.new_nvlp = NULL;
3004		bam_error(_("failed to create stat data: %s\n"),
3005		    strerror(error));
3006	}
3007}
3008
3009static int
3010walk_list(char *root, filelist_t *flistp)
3011{
3012	char path[PATH_MAX];
3013	line_t *lp;
3014
3015	for (lp = flistp->head; lp; lp = lp->next) {
3016		/*
3017		 * Don't follow symlinks.  A symlink must refer to
3018		 * a file that would appear in the archive through
3019		 * a direct reference.  This matches the archive
3020		 * construction behavior.
3021		 */
3022		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
3023		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
3024			if (is_flag_on(UPDATE_ERROR))
3025				return (BAM_ERROR);
3026			/*
3027			 * Some files may not exist.
3028			 * For example: etc/rtc_config on a x86 diskless system
3029			 * Emit verbose message only
3030			 */
3031			if (bam_verbose)
3032				bam_print(_("cannot find: %s: %s\n"),
3033				    path, strerror(errno));
3034		}
3035	}
3036
3037	return (BAM_SUCCESS);
3038}
3039
3040/*
3041 * Update the timestamp file.
3042 */
3043static void
3044update_timestamp(char *root)
3045{
3046	char	timestamp_path[PATH_MAX];
3047
3048	/* this path length has already been checked in check_flags_and_files */
3049	(void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
3050	    FILE_STAT_TIMESTAMP);
3051
3052	/*
3053	 * recreate the timestamp file. Since an outdated or absent timestamp
3054	 * file translates in a complete rebuild of the archive cache, notify
3055	 * the user of the performance issue.
3056	 */
3057	if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
3058		bam_error(_("failed to open file: %s: %s\n"), timestamp_path,
3059		    strerror(errno));
3060		bam_error(_("failed to update the timestamp file, next"
3061		    " archive update may experience reduced performance\n"));
3062	}
3063}
3064
3065
3066static void
3067savenew(char *root)
3068{
3069	char	path[PATH_MAX];
3070	char	path2[PATH_MAX];
3071	size_t	sz;
3072	char	*nstat;
3073	int	fd, wrote, error;
3074
3075	nstat = NULL;
3076	sz = 0;
3077	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3078	    NV_ENCODE_XDR, 0);
3079	if (error) {
3080		bam_error(_("failed to pack stat data: %s\n"),
3081		    strerror(error));
3082		return;
3083	}
3084
3085	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3086	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3087	if (fd == -1) {
3088		bam_error(_("failed to open file: %s: %s\n"), path,
3089		    strerror(errno));
3090		free(nstat);
3091		return;
3092	}
3093	wrote = write(fd, nstat, sz);
3094	if (wrote != sz) {
3095		bam_error(_("write to file failed: %s: %s\n"), path,
3096		    strerror(errno));
3097		(void) close(fd);
3098		free(nstat);
3099		return;
3100	}
3101	(void) close(fd);
3102	free(nstat);
3103
3104	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3105	if (rename(path, path2) != 0) {
3106		bam_error(_("rename to file failed: %s: %s\n"), path2,
3107		    strerror(errno));
3108	}
3109}
3110
3111#define	init_walk_args()	bzero(&walk_arg, sizeof (walk_arg))
3112
3113static void
3114clear_walk_args(void)
3115{
3116	nvlist_free(walk_arg.old_nvlp);
3117	nvlist_free(walk_arg.new_nvlp);
3118	if (walk_arg.sparcfile)
3119		(void) fclose(walk_arg.sparcfile);
3120	walk_arg.old_nvlp = NULL;
3121	walk_arg.new_nvlp = NULL;
3122	walk_arg.sparcfile = NULL;
3123}
3124
3125/*
3126 * Returns:
3127 *	0 - no update necessary
3128 *	1 - update required.
3129 *	BAM_ERROR (-1) - An error occurred
3130 *
3131 * Special handling for check (-n):
3132 * ================================
3133 * The check (-n) option produces parseable output.
3134 * To do this, we suppress all stdout messages unrelated
3135 * to out of sync files.
3136 * All stderr messages are still printed though.
3137 *
3138 */
3139static int
3140update_required(char *root)
3141{
3142	struct stat	sb;
3143	char		path[PATH_MAX];
3144	filelist_t	flist;
3145	filelist_t	*flistp = &flist;
3146	int		ret;
3147
3148	flistp->head = flistp->tail = NULL;
3149
3150	if (is_sparc())
3151		set_flag(IS_SPARC_TARGET);
3152
3153	/*
3154	 * Check if cache directories and archives are present
3155	 */
3156
3157	ret = check_flags_and_files(root);
3158	if (ret < 0)
3159		return (BAM_ERROR);
3160
3161	/*
3162	 * In certain deployment scenarios, filestat may not
3163	 * exist. Do not stop the boot process, but trigger an update
3164	 * of the archives (which will recreate filestat.ramdisk).
3165	 */
3166	if (bam_smf_check) {
3167		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3168		if (stat(path, &sb) != 0) {
3169			(void) creat(NEED_UPDATE_FILE, 0644);
3170			return (0);
3171		}
3172	}
3173
3174	getoldstat(root);
3175
3176	/*
3177	 * Check if the archive contains files that are no longer
3178	 * present on the root filesystem.
3179	 */
3180	check4stale(root);
3181
3182	/*
3183	 * read list of files
3184	 */
3185	if (read_list(root, flistp) != BAM_SUCCESS) {
3186		clear_walk_args();
3187		return (BAM_ERROR);
3188	}
3189
3190	assert(flistp->head && flistp->tail);
3191
3192	/*
3193	 * At this point either the update is required
3194	 * or the decision is pending. In either case
3195	 * we need to create new stat nvlist
3196	 */
3197	create_newstat();
3198	/*
3199	 * This walk does 2 things:
3200	 *	- gets new stat data for every file
3201	 *	- (optional) compare old and new stat data
3202	 */
3203	ret = walk_list(root, &flist);
3204
3205	/* done with the file list */
3206	filelist_free(flistp);
3207
3208	/* something went wrong */
3209
3210	if (ret == BAM_ERROR) {
3211		bam_error(_("Failed to gather cache files, archives "
3212		    "generation aborted\n"));
3213		return (BAM_ERROR);
3214	}
3215
3216	if (walk_arg.new_nvlp == NULL) {
3217		if (walk_arg.sparcfile != NULL)
3218			(void) fclose(walk_arg.sparcfile);
3219		bam_error(_("cannot create new stat data\n"));
3220	}
3221
3222	/* If nothing was updated, discard newstat. */
3223
3224	if (!is_dir_flag_on(NEED_UPDATE)) {
3225		clear_walk_args();
3226		return (0);
3227	}
3228
3229	if (walk_arg.sparcfile != NULL)
3230		(void) fclose(walk_arg.sparcfile);
3231
3232	return (1);
3233}
3234
3235static int
3236flushfs(char *root)
3237{
3238	char	cmd[PATH_MAX + 30];
3239
3240	(void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3241	    LOCKFS_PATH, root);
3242
3243	return (exec_cmd(cmd, NULL));
3244}
3245
3246static int
3247do_archive_copy(char *source, char *dest)
3248{
3249
3250	sync();
3251
3252	/* the equivalent of mv archive-new-$pid boot_archive */
3253	if (rename(source, dest) != 0) {
3254		(void) unlink(source);
3255		return (BAM_ERROR);
3256	}
3257
3258	if (flushfs(bam_root) != 0)
3259		sync();
3260
3261	return (BAM_SUCCESS);
3262}
3263
3264static int
3265check_cmdline(filelist_t flist)
3266{
3267	line_t	*lp;
3268
3269	for (lp = flist.head; lp; lp = lp->next) {
3270		if (strstr(lp->line, "Error:") != NULL ||
3271		    strstr(lp->line, "Inode number overflow") != NULL) {
3272			(void) fprintf(stderr, "%s\n", lp->line);
3273			return (BAM_ERROR);
3274		}
3275	}
3276
3277	return (BAM_SUCCESS);
3278}
3279
3280static void
3281dump_errormsg(filelist_t flist)
3282{
3283	line_t	*lp;
3284
3285	for (lp = flist.head; lp; lp = lp->next)
3286		(void) fprintf(stderr, "%s\n", lp->line);
3287}
3288
3289static int
3290check_archive(char *dest)
3291{
3292	struct stat	sb;
3293
3294	if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3295	    sb.st_size < 10000) {
3296		bam_error(_("archive file %s not generated correctly\n"), dest);
3297		(void) unlink(dest);
3298		return (BAM_ERROR);
3299	}
3300
3301	return (BAM_SUCCESS);
3302}
3303
3304static boolean_t
3305is_be(char *root)
3306{
3307	zfs_handle_t	*zhp;
3308	libzfs_handle_t	*hdl;
3309	be_node_list_t	*be_nodes = NULL;
3310	be_node_list_t	*cur_be;
3311	boolean_t	be_exist = B_FALSE;
3312	char		ds_path[ZFS_MAX_DATASET_NAME_LEN];
3313
3314	if (!is_zfs(root))
3315		return (B_FALSE);
3316	/*
3317	 * Get dataset for mountpoint
3318	 */
3319	if ((hdl = libzfs_init()) == NULL)
3320		return (B_FALSE);
3321
3322	if ((zhp = zfs_path_to_zhandle(hdl, root,
3323	    ZFS_TYPE_FILESYSTEM)) == NULL) {
3324		libzfs_fini(hdl);
3325		return (B_FALSE);
3326	}
3327
3328	(void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3329
3330	/*
3331	 * Check if the current dataset is BE
3332	 */
3333	if (be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == BE_SUCCESS) {
3334		for (cur_be = be_nodes; cur_be != NULL;
3335		    cur_be = cur_be->be_next_node) {
3336
3337			/*
3338			 * Because we guarantee that cur_be->be_root_ds
3339			 * is null-terminated by internal data structure,
3340			 * we can safely use strcmp()
3341			 */
3342			if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3343				be_exist = B_TRUE;
3344				break;
3345			}
3346		}
3347		be_free_list(be_nodes);
3348	}
3349	zfs_close(zhp);
3350	libzfs_fini(hdl);
3351
3352	return (be_exist);
3353}
3354
3355/*
3356 * Returns B_TRUE if mkiso is in the expected PATH and should be used,
3357 * B_FALSE otherwise
3358 */
3359static boolean_t
3360use_mkisofs()
3361{
3362	scf_simple_prop_t *prop;
3363	char *format = NULL;
3364	boolean_t ret;
3365
3366	/* Check whether the mkisofs binary is in the expected location */
3367	if (access(MKISOFS_PATH, X_OK) != 0) {
3368		if (bam_verbose)
3369			bam_print("mkisofs not found\n");
3370		return (B_FALSE);
3371	}
3372
3373	if (bam_format == BAM_FORMAT_HSFS) {
3374		if (bam_verbose)
3375			bam_print("-F specified HSFS");
3376		return (B_TRUE);
3377	}
3378
3379	/* If working on an alt-root, do not use HSFS unless asked via -F */
3380	if (bam_alt_root)
3381		return (B_FALSE);
3382
3383	/*
3384	 * Then check that the system/boot-archive config/format property
3385	 * is "hsfs" or empty.
3386	 */
3387	if ((prop = scf_simple_prop_get(NULL, BOOT_ARCHIVE_FMRI, SCF_PG_CONFIG,
3388	    SCF_PROPERTY_FORMAT)) == NULL) {
3389		/* Could not find property, use mkisofs */
3390		if (bam_verbose) {
3391			bam_print(
3392			    "%s does not have %s/%s property, using mkisofs\n",
3393			    BOOT_ARCHIVE_FMRI, SCF_PG_CONFIG,
3394			    SCF_PROPERTY_FORMAT);
3395		}
3396		return (B_TRUE);
3397	}
3398	if (scf_simple_prop_numvalues(prop) < 0 ||
3399	    (format = scf_simple_prop_next_astring(prop)) == NULL)
3400		ret = B_TRUE;
3401	else
3402		ret = strcmp(format, "hsfs") == 0 ? B_TRUE : B_FALSE;
3403	if (bam_verbose) {
3404		if (ret)
3405			bam_print("Creating hsfs boot archive\n");
3406		else
3407			bam_print("Creating %s boot archive\n", format);
3408	}
3409	scf_simple_prop_free(prop);
3410	return (ret);
3411}
3412
3413#define	MKISO_PARAMS	" -quiet -graft-points -dlrDJN -relaxed-filenames "
3414
3415static int
3416create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3417{
3418	int		ret;
3419	char		cmdline[3 * PATH_MAX + 64];
3420	filelist_t	flist = {0};
3421	const char	*func = "create_sparc_archive()";
3422
3423	if (access(bootblk, R_OK) == 1) {
3424		bam_error(_("unable to access bootblk file : %s\n"), bootblk);
3425		return (BAM_ERROR);
3426	}
3427
3428	/*
3429	 * Prepare mkisofs command line and execute it
3430	 */
3431	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3432	    "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3433	    tempname, list);
3434
3435	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3436
3437	ret = exec_cmd(cmdline, &flist);
3438	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3439		dump_errormsg(flist);
3440		goto out_err;
3441	}
3442
3443	filelist_free(&flist);
3444
3445	/*
3446	 * Prepare dd command line to copy the bootblk on the new archive and
3447	 * execute it
3448	 */
3449	(void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3450	    " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3451	    bootblk, tempname);
3452
3453	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3454
3455	ret = exec_cmd(cmdline, &flist);
3456	if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3457		goto out_err;
3458
3459	filelist_free(&flist);
3460
3461	/* Did we get a valid archive ? */
3462	if (check_archive(tempname) == BAM_ERROR)
3463		return (BAM_ERROR);
3464
3465	return (do_archive_copy(tempname, archive));
3466
3467out_err:
3468	filelist_free(&flist);
3469	bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline);
3470	(void) unlink(tempname);
3471	return (BAM_ERROR);
3472}
3473
3474static unsigned int
3475from_733(unsigned char *s)
3476{
3477	int		i;
3478	unsigned int	ret = 0;
3479
3480	for (i = 0; i < 4; i++)
3481		ret |= s[i] << (8 * i);
3482
3483	return (ret);
3484}
3485
3486static void
3487to_733(unsigned char *s, unsigned int val)
3488{
3489	int	i;
3490
3491	for (i = 0; i < 4; i++)
3492		s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3493}
3494
3495/*
3496 * creates sha1 hash of archive
3497 */
3498static int
3499digest_archive(const char *archive)
3500{
3501	char *archive_hash;
3502	char *hash;
3503	int ret;
3504	FILE *fp;
3505
3506	(void) asprintf(&archive_hash, "%s.hash", archive);
3507	if (archive_hash == NULL)
3508		return (BAM_ERROR);
3509
3510	if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
3511		free(archive_hash);
3512		return (ret);
3513	}
3514
3515	fp = fopen(archive_hash, "w");
3516	if (fp == NULL) {
3517		free(archive_hash);
3518		free(hash);
3519		return (BAM_ERROR);
3520	}
3521
3522	(void) fprintf(fp, "%s\n", hash);
3523	(void) fclose(fp);
3524	free(hash);
3525	free(archive_hash);
3526	return (BAM_SUCCESS);
3527}
3528
3529/*
3530 * Extends the current boot archive without recreating it from scratch
3531 */
3532static int
3533extend_iso_archive(char *archive, char *tempname, char *update_dir)
3534{
3535	int			fd = -1, newfd = -1, ret, i;
3536	int			next_session = 0, new_size = 0;
3537	char			cmdline[3 * PATH_MAX + 64];
3538	const char		*func = "extend_iso_archive()";
3539	filelist_t		flist = {0};
3540	struct iso_pdesc	saved_desc[MAX_IVDs];
3541
3542	fd = open(archive, O_RDWR);
3543	if (fd == -1) {
3544		if (bam_verbose)
3545			bam_error(_("failed to open file: %s: %s\n"),
3546			    archive, strerror(errno));
3547		goto out_err;
3548	}
3549
3550	/*
3551	 * A partial read is likely due to a corrupted file
3552	 */
3553	ret = pread64(fd, saved_desc, sizeof (saved_desc),
3554	    VOLDESC_OFF * CD_BLOCK);
3555	if (ret != sizeof (saved_desc)) {
3556		if (bam_verbose)
3557			bam_error(_("read failed for file: %s: %s\n"),
3558			    archive, strerror(errno));
3559		goto out_err;
3560	}
3561
3562	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3563		if (bam_verbose)
3564			bam_error(_("iso descriptor signature for %s is "
3565			    "invalid\n"), archive);
3566		goto out_err;
3567	}
3568
3569	/*
3570	 * Read primary descriptor and locate next_session offset (it should
3571	 * point to the end of the archive)
3572	 */
3573	next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3574
3575	(void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3576	    "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3577	    MKISO_PARAMS, tempname, update_dir);
3578
3579	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3580
3581	ret = exec_cmd(cmdline, &flist);
3582	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3583		if (bam_verbose) {
3584			bam_error(_("Command '%s' failed while generating "
3585			    "multisession archive\n"), cmdline);
3586			dump_errormsg(flist);
3587		}
3588		goto out_flist_err;
3589	}
3590	filelist_free(&flist);
3591
3592	newfd = open(tempname, O_RDONLY);
3593	if (newfd == -1) {
3594		if (bam_verbose)
3595			bam_error(_("failed to open file: %s: %s\n"),
3596			    archive, strerror(errno));
3597		goto out_err;
3598	}
3599
3600	ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3601	    VOLDESC_OFF * CD_BLOCK);
3602	if (ret != sizeof (saved_desc)) {
3603		if (bam_verbose)
3604			bam_error(_("read failed for file: %s: %s\n"),
3605			    archive, strerror(errno));
3606		goto out_err;
3607	}
3608
3609	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3610		if (bam_verbose)
3611			bam_error(_("iso descriptor signature for %s is "
3612			    "invalid\n"), archive);
3613		goto out_err;
3614	}
3615
3616	new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3617	to_733(saved_desc[0].volume_space_size, new_size);
3618
3619	for (i = 1; i < MAX_IVDs; i++) {
3620		if (saved_desc[i].type[0] == (unsigned char)255)
3621			break;
3622		if (memcmp(saved_desc[i].id, "CD001", 5))
3623			break;
3624
3625		if (bam_verbose)
3626			bam_print("%s: Updating descriptor entry [%d]\n", func,
3627			    i);
3628
3629		to_733(saved_desc[i].volume_space_size, new_size);
3630	}
3631
3632	ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3633	if (ret != DVD_BLOCK) {
3634		if (bam_verbose)
3635			bam_error(_("write to file failed: %s: %s\n"),
3636			    archive, strerror(errno));
3637		goto out_err;
3638	}
3639	(void) close(newfd);
3640	newfd = -1;
3641
3642	ret = fsync(fd);
3643	if (ret != 0)
3644		sync();
3645
3646	ret = close(fd);
3647	if (ret != 0) {
3648		if (bam_verbose)
3649			bam_error(_("failed to close file: %s: %s\n"),
3650			    archive, strerror(errno));
3651		return (BAM_ERROR);
3652	}
3653	fd = -1;
3654
3655	(void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3656	    "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3657	    (next_session/16));
3658
3659	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3660
3661	ret = exec_cmd(cmdline, &flist);
3662	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3663		if (bam_verbose)
3664			bam_error(_("Command '%s' failed while generating "
3665			    "multisession archive\n"), cmdline);
3666		goto out_flist_err;
3667	}
3668	filelist_free(&flist);
3669
3670	(void) unlink(tempname);
3671
3672	if (digest_archive(archive) == BAM_ERROR && bam_verbose)
3673		bam_print("boot archive hashing failed\n");
3674
3675	if (flushfs(bam_root) != 0)
3676		sync();
3677
3678	if (bam_verbose)
3679		bam_print("boot archive updated successfully\n");
3680
3681	return (BAM_SUCCESS);
3682
3683out_flist_err:
3684	filelist_free(&flist);
3685out_err:
3686	if (fd != -1)
3687		(void) close(fd);
3688	if (newfd != -1)
3689		(void) close(newfd);
3690	return (BAM_ERROR);
3691}
3692
3693static int
3694create_x86_archive(char *archive, char *tempname, char *update_dir)
3695{
3696	int		ret;
3697	char		cmdline[3 * PATH_MAX + 64];
3698	filelist_t	flist = {0};
3699	const char	*func = "create_x86_archive()";
3700
3701	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3702	    "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3703
3704	BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3705
3706	ret = exec_cmd(cmdline, &flist);
3707	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3708		bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3709		    cmdline);
3710		dump_errormsg(flist);
3711		filelist_free(&flist);
3712		(void) unlink(tempname);
3713		return (BAM_ERROR);
3714	}
3715
3716	filelist_free(&flist);
3717
3718	if (check_archive(tempname) == BAM_ERROR)
3719		return (BAM_ERROR);
3720
3721	return (do_archive_copy(tempname, archive));
3722}
3723
3724static int
3725mkisofs_archive(char *root)
3726{
3727	int		ret;
3728	char		suffix[20];
3729	char		temp[PATH_MAX];
3730	char		bootblk[PATH_MAX];
3731	char		boot_archive[PATH_MAX];
3732
3733	ret = snprintf(suffix, sizeof (suffix), "/archive-new-%d", getpid());
3734	if (ret >= sizeof (suffix))
3735		goto out_path_err;
3736
3737	ret = build_path(temp, sizeof (temp), root, ARCHIVE_PREFIX, suffix);
3738
3739	if (ret >= sizeof (temp))
3740		goto out_path_err;
3741
3742	ret = build_path(boot_archive, sizeof (boot_archive), root,
3743	    ARCHIVE_PREFIX, ARCHIVE_SUFFIX);
3744
3745	if (ret >= sizeof (boot_archive))
3746		goto out_path_err;
3747
3748	bam_print("updating %s (HSFS)\n",
3749	    boot_archive[1] == '/' ? boot_archive + 1 : boot_archive);
3750
3751	if (is_flag_on(IS_SPARC_TARGET)) {
3752		ret = snprintf(bootblk, sizeof (bootblk),
3753		    "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3754		if (ret >= sizeof (bootblk))
3755			goto out_path_err;
3756
3757		ret = create_sparc_archive(boot_archive, temp, bootblk,
3758		    get_cachedir());
3759	} else {
3760		if (!is_dir_flag_on(NO_EXTEND)) {
3761			if (bam_verbose)
3762				bam_print("Attempting to extend x86 archive: "
3763				    "%s\n", boot_archive);
3764
3765			ret = extend_iso_archive(boot_archive, temp,
3766			    get_updatedir());
3767			if (ret == BAM_SUCCESS) {
3768				if (bam_verbose)
3769					bam_print("Successfully extended %s\n",
3770					    boot_archive);
3771
3772				(void) rmdir_r(get_updatedir());
3773				return (BAM_SUCCESS);
3774			}
3775		}
3776		/*
3777		 * The boot archive will be recreated from scratch. We get here
3778		 * if at least one of these conditions is true:
3779		 * - bootadm was called without the -e switch
3780		 * - the archive (or the archive cache) doesn't exist
3781		 * - archive size is bigger than BA_SIZE_MAX
3782		 * - more than COUNT_MAX files need to be updated
3783		 * - an error occourred either populating the /updates directory
3784		 *   or extend_iso_archive() failed
3785		 */
3786		if (bam_verbose)
3787			bam_print("Unable to extend %s... rebuilding archive\n",
3788			    boot_archive);
3789
3790		if (get_updatedir()[0] != '\0')
3791			(void) rmdir_r(get_updatedir());
3792
3793
3794		ret = create_x86_archive(boot_archive, temp,
3795		    get_cachedir());
3796	}
3797
3798	if (digest_archive(boot_archive) == BAM_ERROR && bam_verbose)
3799		bam_print("boot archive hashing failed\n");
3800
3801	if (ret == BAM_SUCCESS && bam_verbose)
3802		bam_print("Successfully created %s\n", boot_archive);
3803
3804	return (ret);
3805
3806out_path_err:
3807	bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3808	    root);
3809	return (BAM_ERROR);
3810}
3811
3812static int
3813assemble_systemfile(char *infilename, char *outfilename)
3814{
3815	char buf[BUFSIZ];
3816	FILE *infile, *outfile;
3817	size_t n;
3818
3819	if ((infile = fopen(infilename, "r")) == NULL) {
3820		bam_error(_("failed to open file: %s: %s\n"), infilename,
3821		    strerror(errno));
3822		return (BAM_ERROR);
3823	}
3824
3825	if ((outfile = fopen(outfilename, "a")) == NULL) {
3826		bam_error(_("failed to open file: %s: %s\n"), outfilename,
3827		    strerror(errno));
3828		(void) fclose(infile);
3829		return (BAM_ERROR);
3830	}
3831
3832	while ((n = fread(buf, 1, sizeof (buf), infile)) > 0) {
3833		if (fwrite(buf, 1, n, outfile) != n) {
3834			bam_error(_("failed to write file: %s: %s\n"),
3835			    outfilename, strerror(errno));
3836			(void) fclose(infile);
3837			(void) fclose(outfile);
3838			return (BAM_ERROR);
3839		}
3840	}
3841
3842	(void) fclose(infile);
3843	(void) fclose(outfile);
3844
3845	return (BAM_SUCCESS);
3846}
3847
3848/*
3849 * Concatenate all files (except those starting with a dot)
3850 * from /etc/system.d directory into a single /etc/system.d/.self-assembly
3851 * file. The kernel reads it before /etc/system file.
3852 */
3853static error_t
3854build_etc_system_dir(char *root)
3855{
3856	struct dirent **filelist;
3857	char path[PATH_MAX], tmpfile[PATH_MAX];
3858	int i, files, sysfiles = 0;
3859	int ret = BAM_SUCCESS;
3860	struct stat st;
3861	timespec_t times[2];
3862
3863	(void) snprintf(path, sizeof (path), "%s/%s", root, ETC_SYSTEM_DIR);
3864	(void) snprintf(self_assembly, sizeof (self_assembly),
3865	    "%s%s", root, SELF_ASSEMBLY);
3866	(void) snprintf(tmpfile, sizeof (tmpfile), "%s.%ld",
3867	    self_assembly, (long)getpid());
3868
3869	if (stat(self_assembly, &st) >= 0 && (st.st_mode & S_IFMT) == S_IFREG) {
3870		times[0] = times[1] = st.st_mtim;
3871	} else {
3872		times[1].tv_nsec = 0;
3873	}
3874
3875	if ((files = scandir(path, &filelist, NULL, alphasort)) < 0) {
3876		/* Don't fail the update if <ROOT>/etc/system.d doesn't exist */
3877		if (errno == ENOENT)
3878			return (BAM_SUCCESS);
3879		bam_error(_("can't read %s: %s\n"), path, strerror(errno));
3880		return (BAM_ERROR);
3881	}
3882
3883	(void) unlink(tmpfile);
3884
3885	for (i = 0; i < files; i++) {
3886		char	filepath[PATH_MAX];
3887		char	*fname;
3888
3889		fname = filelist[i]->d_name;
3890
3891		/* skip anything that starts with a dot */
3892		if (strncmp(fname, ".", 1) == 0) {
3893			free(filelist[i]);
3894			continue;
3895		}
3896
3897		if (bam_verbose)
3898			bam_print(_("/etc/system.d adding %s/%s\n"),
3899			    path, fname);
3900
3901		(void) snprintf(filepath, sizeof (filepath), "%s/%s",
3902		    path, fname);
3903
3904		if ((assemble_systemfile(filepath, tmpfile)) < 0) {
3905			bam_error(_("failed to append file: %s: %s\n"),
3906			    filepath, strerror(errno));
3907			ret = BAM_ERROR;
3908			break;
3909		}
3910		sysfiles++;
3911	}
3912
3913	if (sysfiles > 0) {
3914		if (rename(tmpfile, self_assembly) < 0) {
3915			bam_error(_("failed to rename file: %s: %s\n"), tmpfile,
3916			    strerror(errno));
3917			return (BAM_ERROR);
3918		}
3919
3920		/*
3921		 * Use previous attribute times to avoid
3922		 * boot archive recreation.
3923		 */
3924		if (times[1].tv_nsec != 0 &&
3925		    utimensat(AT_FDCWD, self_assembly, times, 0) != 0) {
3926			bam_error(_("failed to change times: %s\n"),
3927			    strerror(errno));
3928			return (BAM_ERROR);
3929		}
3930	} else {
3931		(void) unlink(tmpfile);
3932		(void) unlink(self_assembly);
3933	}
3934	return (ret);
3935}
3936
3937static error_t
3938create_ramdisk(char *root)
3939{
3940	char *cmdline, path[PATH_MAX];
3941	size_t len;
3942	struct stat sb;
3943	int ret, status = BAM_SUCCESS;
3944
3945	/* If mkisofs should be used, use it to create the required archives */
3946	if (use_mkisofs()) {
3947		if (has_cachedir() && is_dir_flag_on(NEED_UPDATE)) {
3948			ret = mkisofs_archive(root);
3949			if (ret != 0)
3950				status = BAM_ERROR;
3951		}
3952		return (status);
3953	} else if (bam_format == BAM_FORMAT_HSFS) {
3954		bam_error(_("cannot create hsfs archive\n"));
3955		return (BAM_ERROR);
3956	}
3957
3958	/*
3959	 * Else setup command args for create_ramdisk.ksh for the archive
3960	 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3961	 */
3962
3963	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3964	if (stat(path, &sb) != 0) {
3965		bam_error(_("archive creation file not found: %s: %s\n"),
3966		    path, strerror(errno));
3967		return (BAM_ERROR);
3968	}
3969
3970	if (is_safe_exec(path) == BAM_ERROR)
3971		return (BAM_ERROR);
3972
3973	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
3974	if (bam_alt_platform)
3975		len += strlen(bam_platform) + strlen(" -p ");
3976	if (bam_format != BAM_FORMAT_UNSET)
3977		len += strlen(bam_formats[bam_format]) + strlen(" -f ");
3978	cmdline = s_calloc(1, len);
3979
3980	if (bam_alt_platform) {
3981		assert(strlen(root) > 1);
3982		(void) snprintf(cmdline, len, "%s -p %s -R %s",
3983		    path, bam_platform, root);
3984		/* chop off / at the end */
3985		cmdline[strlen(cmdline) - 1] = '\0';
3986	} else if (strlen(root) > 1) {
3987		(void) snprintf(cmdline, len, "%s -R %s", path, root);
3988		/* chop off / at the end */
3989		cmdline[strlen(cmdline) - 1] = '\0';
3990	} else
3991		(void) snprintf(cmdline, len, "%s", path);
3992
3993	if (bam_format != BAM_FORMAT_UNSET) {
3994		if (strlcat(cmdline, " -f ", len) >= len ||
3995		    strlcat(cmdline, bam_formats[bam_format], len) >= len) {
3996			bam_error(_("boot-archive command line too long\n"));
3997			free(cmdline);
3998			return (BAM_ERROR);
3999		}
4000	}
4001
4002	if (exec_cmd(cmdline, NULL) != 0) {
4003		bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
4004		    cmdline);
4005		free(cmdline);
4006		return (BAM_ERROR);
4007	}
4008	free(cmdline);
4009	/*
4010	 * The existence of the expected archives used to be
4011	 * verified here. This check is done in create_ramdisk as
4012	 * it needs to be in sync with the altroot operated upon.
4013	 */
4014	return (BAM_SUCCESS);
4015}
4016
4017/*
4018 * Checks if target filesystem is on a ramdisk
4019 * 1 - is miniroot
4020 * 0 - is not
4021 * When in doubt assume it is not a ramdisk.
4022 */
4023static int
4024is_ramdisk(char *root)
4025{
4026	struct extmnttab mnt;
4027	FILE *fp;
4028	int found;
4029	char mntpt[PATH_MAX];
4030	char *cp;
4031
4032	/*
4033	 * There are 3 situations where creating archive is
4034	 * of dubious value:
4035	 *	- create boot_archive on a lofi-mounted boot_archive
4036	 *	- create it on a ramdisk which is the root filesystem
4037	 *	- create it on a ramdisk mounted somewhere else
4038	 * The first is not easy to detect and checking for it is not
4039	 * worth it.
4040	 * The other two conditions are handled here
4041	 */
4042	fp = fopen(MNTTAB, "r");
4043	if (fp == NULL) {
4044		bam_error(_("failed to open file: %s: %s\n"),
4045		    MNTTAB, strerror(errno));
4046		return (0);
4047	}
4048
4049	resetmnttab(fp);
4050
4051	/*
4052	 * Remove any trailing / from the mount point
4053	 */
4054	(void) strlcpy(mntpt, root, sizeof (mntpt));
4055	if (strcmp(root, "/") != 0) {
4056		cp = mntpt + strlen(mntpt) - 1;
4057		if (*cp == '/')
4058			*cp = '\0';
4059	}
4060	found = 0;
4061	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4062		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
4063			found = 1;
4064			break;
4065		}
4066	}
4067
4068	if (!found) {
4069		if (bam_verbose)
4070			bam_error(_("alternate root %s not in mnttab\n"),
4071			    mntpt);
4072		(void) fclose(fp);
4073		return (0);
4074	}
4075
4076	if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
4077	    strlen(RAMDISK_SPECIAL)) == 0) {
4078		if (bam_verbose)
4079			bam_error(_("%s is on a ramdisk device\n"), bam_root);
4080		(void) fclose(fp);
4081		return (1);
4082	}
4083
4084	(void) fclose(fp);
4085
4086	return (0);
4087}
4088
4089static int
4090is_boot_archive(char *root)
4091{
4092	char		path[PATH_MAX];
4093	struct stat	sb;
4094	int		error;
4095	const char	*fcn = "is_boot_archive()";
4096
4097	/*
4098	 * We can't create an archive without the create_ramdisk script
4099	 */
4100	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
4101	error = stat(path, &sb);
4102	INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
4103	if (error == -1) {
4104		if (bam_verbose)
4105			bam_print(_("file not found: %s\n"), path);
4106		BAM_DPRINTF(("%s: not a boot archive based Solaris "
4107		    "instance: %s\n", fcn, root));
4108		return (0);
4109	}
4110
4111	BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
4112	    fcn, root));
4113	return (1);
4114}
4115
4116/*
4117 * Need to call this for anything that operates on the GRUB menu
4118 * In the x86 live upgrade case the directory /boot/grub may be present
4119 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
4120 * is to check for the presence of the stage2 binary which is present
4121 * only on GRUB targets (even on x86 boot partitions). Checking for the
4122 * presence of the multiboot binary is not correct as it is not present
4123 * on x86 boot partitions.
4124 */
4125int
4126is_grub(const char *root)
4127{
4128	char path[PATH_MAX];
4129	struct stat sb;
4130	void *defp;
4131	boolean_t grub = B_FALSE;
4132	const char *res = NULL;
4133	const char *fcn = "is_grub()";
4134
4135	/* grub is disabled by default */
4136	if ((defp = defopen_r(BE_DEFAULTS)) == NULL) {
4137		return (0);
4138	} else {
4139		res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
4140		if (res != NULL && res[0] != '\0') {
4141			if (strcasecmp(res, "true") == 0)
4142				grub = B_TRUE;
4143		}
4144		defclose_r(defp);
4145	}
4146
4147	if (grub == B_TRUE) {
4148		(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
4149		if (stat(path, &sb) == -1) {
4150			BAM_DPRINTF(("%s: Missing GRUB directory: %s\n",
4151			    fcn, path));
4152			return (0);
4153		} else
4154			return (1);
4155	}
4156
4157	return (0);
4158}
4159
4160int
4161is_zfs(char *root)
4162{
4163	struct statvfs		vfs;
4164	int			ret;
4165	const char		*fcn = "is_zfs()";
4166
4167	ret = statvfs(root, &vfs);
4168	INJECT_ERROR1("STATVFS_ZFS", ret = 1);
4169	if (ret != 0) {
4170		bam_error(_("statvfs failed for %s: %s\n"), root,
4171		    strerror(errno));
4172		return (0);
4173	}
4174
4175	if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
4176		BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
4177		return (1);
4178	} else {
4179		BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
4180		return (0);
4181	}
4182}
4183
4184int
4185is_pcfs(char *root)
4186{
4187	struct statvfs		vfs;
4188	int			ret;
4189	const char		*fcn = "is_pcfs()";
4190
4191	ret = statvfs(root, &vfs);
4192	INJECT_ERROR1("STATVFS_PCFS", ret = 1);
4193	if (ret != 0) {
4194		bam_error(_("statvfs failed for %s: %s\n"), root,
4195		    strerror(errno));
4196		return (0);
4197	}
4198
4199	if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
4200		BAM_DPRINTF(("%s: is a PCFS filesystem: %s\n", fcn, root));
4201		return (1);
4202	} else {
4203		BAM_DPRINTF(("%s: is *NOT* a PCFS filesystem: %s\n",
4204		    fcn, root));
4205		return (0);
4206	}
4207}
4208
4209static int
4210is_readonly(char *root)
4211{
4212	int		fd;
4213	int		error;
4214	char		testfile[PATH_MAX];
4215	const char	*fcn = "is_readonly()";
4216
4217	/*
4218	 * Using statvfs() to check for a read-only filesystem is not
4219	 * reliable. The only way to reliably test is to attempt to
4220	 * create a file
4221	 */
4222	(void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
4223	    root, BOOTADM_RDONLY_TEST, getpid());
4224
4225	(void) unlink(testfile);
4226
4227	errno = 0;
4228	fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
4229	error = errno;
4230	INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
4231	if (fd == -1 && error == EROFS) {
4232		BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
4233		return (1);
4234	} else if (fd == -1) {
4235		bam_error(_("error during read-only test on %s: %s\n"),
4236		    root, strerror(error));
4237	}
4238
4239	(void) close(fd);
4240	(void) unlink(testfile);
4241
4242	BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
4243	return (0);
4244}
4245
4246static error_t
4247update_archive(char *root, char *opt)
4248{
4249	error_t ret;
4250
4251	assert(root);
4252	assert(opt == NULL);
4253
4254	init_walk_args();
4255	(void) umask(022);
4256
4257	/*
4258	 * Never update non-BE root in update_all
4259	 */
4260	if (bam_update_all && !is_be(root))
4261		return (BAM_SUCCESS);
4262	/*
4263	 * root must belong to a boot archive based OS,
4264	 */
4265	if (!is_boot_archive(root)) {
4266		/*
4267		 * Emit message only if not in context of update_all.
4268		 * If in update_all, emit only if verbose flag is set.
4269		 */
4270		if (!bam_update_all || bam_verbose)
4271			bam_print(_("%s: not a boot archive based Solaris "
4272			    "instance\n"), root);
4273		return (BAM_ERROR);
4274	}
4275
4276	/*
4277	 * If smf check is requested when / is writable (can happen
4278	 * on first reboot following an upgrade because service
4279	 * dependency is messed up), skip the check.
4280	 */
4281	if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
4282		return (BAM_SUCCESS);
4283
4284	/*
4285	 * Don't generate archive on ramdisk.
4286	 */
4287	if (is_ramdisk(root))
4288		return (BAM_SUCCESS);
4289
4290	/*
4291	 * root must be writable. This check applies to alternate
4292	 * root (-R option); bam_root_readonly applies to '/' only.
4293	 * The behaviour translates into being the one of a 'check'.
4294	 */
4295	if (!bam_smf_check && !bam_check && is_readonly(root)) {
4296		set_flag(RDONLY_FSCHK);
4297		bam_check = 1;
4298	}
4299
4300	/*
4301	 * Process the /etc/system.d/.self-assembly file.
4302	 */
4303	if (build_etc_system_dir(bam_root) == BAM_ERROR)
4304		return (BAM_ERROR);
4305
4306	/*
4307	 * Now check if an update is really needed.
4308	 */
4309	ret = update_required(root);
4310
4311	/*
4312	 * The check command (-n) is *not* a dry run.
4313	 * It only checks if the archive is in sync.
4314	 * A readonly filesystem has to be considered an error only if an update
4315	 * is required.
4316	 */
4317	if (bam_nowrite()) {
4318		if (is_flag_on(RDONLY_FSCHK)) {
4319			bam_check = bam_saved_check;
4320			if (ret > 0)
4321				bam_error(_("%s filesystem is read-only, "
4322				    "skipping archives update\n"), root);
4323			if (bam_update_all)
4324				return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4325		}
4326
4327		bam_exit((ret != 0) ? 1 : 0);
4328	}
4329
4330	if (ret == 1) {
4331		/* create the ramdisk */
4332		ret = create_ramdisk(root);
4333	}
4334
4335	/*
4336	 * if the archive is updated, save the new stat data and update the
4337	 * timestamp file
4338	 */
4339	if (ret == 0 && walk_arg.new_nvlp != NULL) {
4340		savenew(root);
4341		update_timestamp(root);
4342	}
4343
4344	clear_walk_args();
4345
4346	return (ret);
4347}
4348
4349static char *
4350find_root_pool()
4351{
4352	char *special = get_special("/");
4353	char *p;
4354
4355	if (special == NULL)
4356		return (NULL);
4357
4358	if (*special == '/') {
4359		free(special);
4360		return (NULL);
4361	}
4362
4363	if ((p = strchr(special, '/')) != NULL)
4364		*p = '\0';
4365
4366	return (special);
4367}
4368
4369static error_t
4370synchronize_BE_menu(void)
4371{
4372	struct stat	sb;
4373	char		cmdline[PATH_MAX];
4374	char		cksum_line[PATH_MAX];
4375	filelist_t	flist = {0};
4376	char		*old_cksum_str;
4377	char		*old_size_str;
4378	char		*old_file;
4379	char		*curr_cksum_str;
4380	char		*curr_size_str;
4381	char		*curr_file;
4382	char		*pool = NULL;
4383	char		*mntpt = NULL;
4384	zfs_mnted_t	mnted;
4385	FILE		*cfp;
4386	int		found;
4387	int		ret;
4388	const char	*fcn = "synchronize_BE_menu()";
4389
4390	BAM_DPRINTF(("%s: entered. No args\n", fcn));
4391
4392	/* Check if findroot enabled LU BE */
4393	if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4394		BAM_DPRINTF(("%s: not a Live Upgrade BE\n", fcn));
4395		return (BAM_SUCCESS);
4396	}
4397
4398	if (stat(LU_MENU_CKSUM, &sb) != 0) {
4399		BAM_DPRINTF(("%s: checksum file absent: %s\n",
4400		    fcn, LU_MENU_CKSUM));
4401		goto menu_sync;
4402	}
4403
4404	cfp = fopen(LU_MENU_CKSUM, "r");
4405	INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4406	if (cfp == NULL) {
4407		bam_error(_("failed to read GRUB menu checksum file: %s\n"),
4408		    LU_MENU_CKSUM);
4409		goto menu_sync;
4410	}
4411	BAM_DPRINTF(("%s: opened checksum file: %s\n", fcn, LU_MENU_CKSUM));
4412
4413	found = 0;
4414	while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4415		INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4416		if (found) {
4417			bam_error(_("multiple checksums for GRUB menu in "
4418			    "checksum file: %s\n"), LU_MENU_CKSUM);
4419			(void) fclose(cfp);
4420			goto menu_sync;
4421		}
4422		found = 1;
4423	}
4424	BAM_DPRINTF(("%s: read checksum file: %s\n", fcn, LU_MENU_CKSUM));
4425
4426
4427	old_cksum_str = strtok(cksum_line, " \t");
4428	old_size_str = strtok(NULL, " \t");
4429	old_file = strtok(NULL, " \t");
4430
4431	INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4432	INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4433	INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4434	if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4435		bam_error(_("error parsing GRUB menu checksum file: %s\n"),
4436		    LU_MENU_CKSUM);
4437		goto menu_sync;
4438	}
4439	BAM_DPRINTF(("%s: parsed checksum file: %s\n", fcn, LU_MENU_CKSUM));
4440
4441	/* Get checksum of current menu */
4442	pool = find_root_pool();
4443	if (pool) {
4444		mntpt = mount_top_dataset(pool, &mnted);
4445		if (mntpt == NULL) {
4446			bam_error(_("failed to mount top dataset for %s\n"),
4447			    pool);
4448			free(pool);
4449			return (BAM_ERROR);
4450		}
4451		(void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4452		    CKSUM, mntpt, GRUB_MENU);
4453	} else {
4454		(void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4455		    CKSUM, GRUB_MENU);
4456	}
4457	ret = exec_cmd(cmdline, &flist);
4458	if (pool) {
4459		(void) umount_top_dataset(pool, mnted, mntpt);
4460		free(pool);
4461	}
4462	INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4463	if (ret != 0) {
4464		bam_error(_("error generating checksum of GRUB menu\n"));
4465		return (BAM_ERROR);
4466	}
4467	BAM_DPRINTF(("%s: successfully generated checksum\n", fcn));
4468
4469	INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4470	if ((flist.head == NULL) || (flist.head != flist.tail)) {
4471		bam_error(_("bad checksum generated for GRUB menu\n"));
4472		filelist_free(&flist);
4473		return (BAM_ERROR);
4474	}
4475	BAM_DPRINTF(("%s: generated checksum output valid\n", fcn));
4476
4477	curr_cksum_str = strtok(flist.head->line, " \t");
4478	curr_size_str = strtok(NULL, " \t");
4479	curr_file = strtok(NULL, " \t");
4480
4481	INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4482	INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4483	INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4484	if (curr_cksum_str == NULL || curr_size_str == NULL ||
4485	    curr_file == NULL) {
4486		bam_error(_("error parsing checksum generated "
4487		    "for GRUB menu\n"));
4488		filelist_free(&flist);
4489		return (BAM_ERROR);
4490	}
4491	BAM_DPRINTF(("%s: successfully parsed generated checksum\n", fcn));
4492
4493	if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4494	    strcmp(old_size_str, curr_size_str) == 0 &&
4495	    strcmp(old_file, curr_file) == 0) {
4496		filelist_free(&flist);
4497		BAM_DPRINTF(("%s: no change in checksum of GRUB menu\n", fcn));
4498		return (BAM_SUCCESS);
4499	}
4500
4501	filelist_free(&flist);
4502
4503	/* cksum doesn't match - the menu has changed */
4504	BAM_DPRINTF(("%s: checksum of GRUB menu has changed\n", fcn));
4505
4506menu_sync:
4507	bam_print(_("propagating updated GRUB menu\n"));
4508
4509	(void) snprintf(cmdline, sizeof (cmdline),
4510	    "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4511	    LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4512	ret = exec_cmd(cmdline, NULL);
4513	INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4514	if (ret != 0) {
4515		bam_error(_("error propagating updated GRUB menu\n"));
4516		return (BAM_ERROR);
4517	}
4518	BAM_DPRINTF(("%s: successfully propagated GRUB menu\n", fcn));
4519
4520	(void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4521	    GRUB_MENU, GRUB_BACKUP_MENU);
4522	ret = exec_cmd(cmdline, NULL);
4523	INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4524	if (ret != 0) {
4525		bam_error(_("failed to create backup for GRUB menu: %s\n"),
4526		    GRUB_BACKUP_MENU);
4527		return (BAM_ERROR);
4528	}
4529	BAM_DPRINTF(("%s: successfully created backup GRUB menu: %s\n",
4530	    fcn, GRUB_BACKUP_MENU));
4531
4532	(void) snprintf(cmdline, sizeof (cmdline),
4533	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4534	    LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4535	ret = exec_cmd(cmdline, NULL);
4536	INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4537	if (ret != 0) {
4538		bam_error(_("error propagating backup GRUB menu: %s\n"),
4539		    GRUB_BACKUP_MENU);
4540		return (BAM_ERROR);
4541	}
4542	BAM_DPRINTF(("%s: successfully propagated backup GRUB menu: %s\n",
4543	    fcn, GRUB_BACKUP_MENU));
4544
4545	(void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4546	    CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4547	ret = exec_cmd(cmdline, NULL);
4548	INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4549	if (ret != 0) {
4550		bam_error(_("failed to write GRUB menu checksum file: %s\n"),
4551		    LU_MENU_CKSUM);
4552		return (BAM_ERROR);
4553	}
4554	BAM_DPRINTF(("%s: successfully created checksum file: %s\n",
4555	    fcn, LU_MENU_CKSUM));
4556
4557	(void) snprintf(cmdline, sizeof (cmdline),
4558	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4559	    LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4560	ret = exec_cmd(cmdline, NULL);
4561	INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4562	if (ret != 0) {
4563		bam_error(_("error propagating GRUB menu checksum file: %s\n"),
4564		    LU_MENU_CKSUM);
4565		return (BAM_ERROR);
4566	}
4567	BAM_DPRINTF(("%s: successfully propagated checksum file: %s\n",
4568	    fcn, LU_MENU_CKSUM));
4569
4570	return (BAM_SUCCESS);
4571}
4572
4573static error_t
4574update_all(char *root, char *opt)
4575{
4576	struct extmnttab mnt;
4577	struct stat sb;
4578	FILE *fp;
4579	char multibt[PATH_MAX];
4580	char creatram[PATH_MAX];
4581	error_t ret = BAM_SUCCESS;
4582
4583	assert(root);
4584	assert(opt == NULL);
4585
4586	if (bam_rootlen != 1 || *root != '/') {
4587		elide_trailing_slash(root, multibt, sizeof (multibt));
4588		bam_error(_("an alternate root (%s) cannot be used with this "
4589		    "sub-command\n"), multibt);
4590		return (BAM_ERROR);
4591	}
4592
4593	/*
4594	 * First update archive for current root
4595	 */
4596	if (update_archive(root, opt) != BAM_SUCCESS)
4597		ret = BAM_ERROR;
4598
4599	if (ret == BAM_ERROR)
4600		goto out;
4601
4602	/*
4603	 * Now walk the mount table, performing archive update
4604	 * for all mounted Newboot root filesystems
4605	 */
4606	fp = fopen(MNTTAB, "r");
4607	if (fp == NULL) {
4608		bam_error(_("failed to open file: %s: %s\n"),
4609		    MNTTAB, strerror(errno));
4610		ret = BAM_ERROR;
4611		goto out;
4612	}
4613
4614	resetmnttab(fp);
4615
4616	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4617		if (mnt.mnt_special == NULL)
4618			continue;
4619		if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4620		    (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4621			continue;
4622		if (strcmp(mnt.mnt_mountp, "/") == 0)
4623			continue;
4624
4625		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
4626		    mnt.mnt_mountp, CREATE_RAMDISK);
4627
4628		if (stat(creatram, &sb) == -1)
4629			continue;
4630
4631		/*
4632		 * We put a trailing slash to be consistent with root = "/"
4633		 * case, such that we don't have to print // in some cases.
4634		 */
4635		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4636		    mnt.mnt_mountp);
4637		bam_rootlen = strlen(rootbuf);
4638
4639		/*
4640		 * It's possible that other mounts may be an alternate boot
4641		 * architecture, so check it again.
4642		 */
4643		if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4644		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
4645			ret = BAM_ERROR;
4646	}
4647
4648	(void) fclose(fp);
4649
4650out:
4651	/*
4652	 * We no longer use biosdev for Live Upgrade. Hence
4653	 * there is no need to defer (to shutdown time) any fdisk
4654	 * updates
4655	 */
4656	if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4657		bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
4658		    "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
4659	}
4660
4661	/*
4662	 * If user has updated menu in current BE, propagate the
4663	 * updates to all BEs.
4664	 */
4665	if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4666		ret = BAM_ERROR;
4667
4668	return (ret);
4669}
4670
4671static void
4672append_line(menu_t *mp, line_t *lp)
4673{
4674	if (mp->start == NULL) {
4675		mp->start = lp;
4676	} else {
4677		mp->end->next = lp;
4678		lp->prev = mp->end;
4679	}
4680	mp->end = lp;
4681}
4682
4683void
4684unlink_line(menu_t *mp, line_t *lp)
4685{
4686	/* unlink from list */
4687	if (lp->prev)
4688		lp->prev->next = lp->next;
4689	else
4690		mp->start = lp->next;
4691	if (lp->next)
4692		lp->next->prev = lp->prev;
4693	else
4694		mp->end = lp->prev;
4695}
4696
4697static entry_t *
4698boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4699{
4700	entry_t *ent, *prev;
4701	const char *fcn = "boot_entry_new()";
4702
4703	assert(mp);
4704	assert(start);
4705	assert(end);
4706
4707	ent = s_calloc(1, sizeof (entry_t));
4708	BAM_DPRINTF(("%s: new boot entry alloced\n", fcn));
4709	ent->start = start;
4710	ent->end = end;
4711
4712	if (mp->entries == NULL) {
4713		mp->entries = ent;
4714		BAM_DPRINTF(("%s: (first) new boot entry created\n", fcn));
4715		return (ent);
4716	}
4717
4718	prev = mp->entries;
4719	while (prev->next)
4720		prev = prev->next;
4721	prev->next = ent;
4722	ent->prev = prev;
4723	BAM_DPRINTF(("%s: new boot entry linked in\n", fcn));
4724	return (ent);
4725}
4726
4727static void
4728boot_entry_addline(entry_t *ent, line_t *lp)
4729{
4730	if (ent)
4731		ent->end = lp;
4732}
4733
4734/*
4735 * Check whether cmd matches the one indexed by which, and whether arg matches
4736 * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4737 * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
4738 * strstr(), so it can be a partial match.
4739 */
4740static int
4741check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4742{
4743	int			ret;
4744	const char		*fcn = "check_cmd()";
4745
4746	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, arg, str));
4747
4748	if (cmd != NULL) {
4749		if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4750		    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4751			BAM_DPRINTF(("%s: command %s does not match %s\n",
4752			    fcn, cmd, menu_cmds[which]));
4753			return (0);
4754		}
4755		ret = (strstr(arg, str) != NULL);
4756	} else
4757		ret = 0;
4758
4759	if (ret) {
4760		BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
4761	} else {
4762		BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
4763	}
4764
4765	return (ret);
4766}
4767
4768static error_t
4769kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4770{
4771	const char		*fcn  = "kernel_parser()";
4772
4773	assert(entry);
4774	assert(cmd);
4775	assert(arg);
4776
4777	if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4778	    strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4779		BAM_DPRINTF(("%s: not a kernel command: %s\n", fcn, cmd));
4780		return (BAM_ERROR);
4781	}
4782
4783	if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4784		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_32 flag: %s\n",
4785		    fcn, arg));
4786		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4787	} else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4788	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4789		BAM_DPRINTF(("%s: setting DBOOT flag: %s\n", fcn, arg));
4790		entry->flags |= BAM_ENTRY_DBOOT;
4791	} else if (strncmp(arg, DIRECT_BOOT_64,
4792	    sizeof (DIRECT_BOOT_64) - 1) == 0) {
4793		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_64 flag: %s\n",
4794		    fcn, arg));
4795		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4796	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4797	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4798		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE flag: %s\n",
4799		    fcn, arg));
4800		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4801	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4802	    sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4803		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_32 "
4804		    "flag: %s\n", fcn, arg));
4805		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4806		    | BAM_ENTRY_32BIT;
4807	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4808	    sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4809		BAM_DPRINTF(("%s: setting DBOOT|DBOOT_FAILSAFE|DBOOT_64 "
4810		    "flag: %s\n", fcn, arg));
4811		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4812		    | BAM_ENTRY_64BIT;
4813	} else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4814		BAM_DPRINTF(("%s: setting MULTIBOOT flag: %s\n", fcn, arg));
4815		entry->flags |= BAM_ENTRY_MULTIBOOT;
4816	} else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4817	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4818		BAM_DPRINTF(("%s: setting MULTIBOOT|MULTIBOOT_FAILSAFE "
4819		    "flag: %s\n", fcn, arg));
4820		entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4821	} else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4822		BAM_DPRINTF(("%s: setting XEN HV flag: %s\n", fcn, arg));
4823		entry->flags |= BAM_ENTRY_HV;
4824	} else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4825		BAM_DPRINTF(("%s: is HAND kernel flag: %s\n", fcn, arg));
4826		return (BAM_ERROR);
4827	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4828	    strstr(arg, UNIX_SPACE)) {
4829		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4830	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4831	    strstr(arg, AMD_UNIX_SPACE)) {
4832		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4833	} else {
4834		BAM_DPRINTF(("%s: is UNKNOWN kernel entry: %s\n", fcn, arg));
4835		bam_error(_("kernel command on line %d not recognized.\n"),
4836		    linenum);
4837		return (BAM_ERROR);
4838	}
4839
4840	return (BAM_SUCCESS);
4841}
4842
4843static error_t
4844module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4845{
4846	const char		*fcn = "module_parser()";
4847
4848	assert(entry);
4849	assert(cmd);
4850	assert(arg);
4851
4852	if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4853	    strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4854		BAM_DPRINTF(("%s: not module cmd: %s\n", fcn, cmd));
4855		return (BAM_ERROR);
4856	}
4857
4858	if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4859	    strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4860	    strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4861	    strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4862	    strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4863	    strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4864	    strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4865	    strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4866	    strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4867		BAM_DPRINTF(("%s: bootadm or LU module cmd: %s\n", fcn, arg));
4868		return (BAM_SUCCESS);
4869	} else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4870	    !(entry->flags & BAM_ENTRY_LU)) {
4871		/* don't emit warning for hand entries */
4872		BAM_DPRINTF(("%s: is HAND module: %s\n", fcn, arg));
4873		return (BAM_ERROR);
4874	} else {
4875		BAM_DPRINTF(("%s: is UNKNOWN module: %s\n", fcn, arg));
4876		bam_error(_("module command on line %d not recognized.\n"),
4877		    linenum);
4878		return (BAM_ERROR);
4879	}
4880}
4881
4882/*
4883 * A line in menu.lst looks like
4884 * [ ]*<cmd>[ \t=]*<arg>*
4885 */
4886static void
4887line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4888{
4889	/*
4890	 * save state across calls. This is so that
4891	 * header gets the right entry# after title has
4892	 * been processed
4893	 */
4894	static line_t *prev = NULL;
4895	static entry_t *curr_ent = NULL;
4896	static int in_liveupgrade = 0;
4897	static int is_libbe_ent = 0;
4898
4899	line_t	*lp;
4900	char *cmd, *sep, *arg;
4901	char save, *cp, *line;
4902	menu_flag_t flag = BAM_INVALID;
4903	const char *fcn = "line_parser()";
4904
4905	cmd = NULL;
4906	if (str == NULL) {
4907		return;
4908	}
4909
4910	/*
4911	 * First save a copy of the entire line.
4912	 * We use this later to set the line field.
4913	 */
4914	line = s_strdup(str);
4915
4916	/* Eat up leading whitespace */
4917	while (*str == ' ' || *str == '\t')
4918		str++;
4919
4920	if (*str == '#') {		/* comment */
4921		cmd = s_strdup("#");
4922		sep = NULL;
4923		arg = s_strdup(str + 1);
4924		flag = BAM_COMMENT;
4925		if (strstr(arg, BAM_LU_HDR) != NULL) {
4926			in_liveupgrade = 1;
4927		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
4928			in_liveupgrade = 0;
4929		} else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4930			is_libbe_ent = 1;
4931		}
4932	} else if (*str == '\0') {	/* blank line */
4933		cmd = sep = arg = NULL;
4934		flag = BAM_EMPTY;
4935	} else {
4936		/*
4937		 * '=' is not a documented separator in grub syntax.
4938		 * However various development bits use '=' as a
4939		 * separator. In addition, external users also
4940		 * use = as a separator. So we will allow that usage.
4941		 */
4942		cp = str;
4943		while (*str != ' ' && *str != '\t' && *str != '=') {
4944			if (*str == '\0') {
4945				cmd = s_strdup(cp);
4946				sep = arg = NULL;
4947				break;
4948			}
4949			str++;
4950		}
4951
4952		if (*str != '\0') {
4953			save = *str;
4954			*str = '\0';
4955			cmd = s_strdup(cp);
4956			*str = save;
4957
4958			str++;
4959			save = *str;
4960			*str = '\0';
4961			sep = s_strdup(str - 1);
4962			*str = save;
4963
4964			while (*str == ' ' || *str == '\t')
4965				str++;
4966			if (*str == '\0')
4967				arg = NULL;
4968			else
4969				arg = s_strdup(str);
4970		}
4971	}
4972
4973	lp = s_calloc(1, sizeof (line_t));
4974
4975	lp->cmd = cmd;
4976	lp->sep = sep;
4977	lp->arg = arg;
4978	lp->line = line;
4979	lp->lineNum = ++(*lineNum);
4980	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4981		lp->entryNum = ++(*entryNum);
4982		lp->flags = BAM_TITLE;
4983		if (prev && prev->flags == BAM_COMMENT &&
4984		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4985			prev->entryNum = lp->entryNum;
4986			curr_ent = boot_entry_new(mp, prev, lp);
4987			curr_ent->flags |= BAM_ENTRY_BOOTADM;
4988			BAM_DPRINTF(("%s: is bootadm(1M) entry: %s\n",
4989			    fcn, arg));
4990		} else {
4991			curr_ent = boot_entry_new(mp, lp, lp);
4992			if (in_liveupgrade) {
4993				curr_ent->flags |= BAM_ENTRY_LU;
4994				BAM_DPRINTF(("%s: is LU entry: %s\n",
4995				    fcn, arg));
4996			}
4997		}
4998		curr_ent->entryNum = *entryNum;
4999	} else if (flag != BAM_INVALID) {
5000		/*
5001		 * For header comments, the entry# is "fixed up"
5002		 * by the subsequent title
5003		 */
5004		lp->entryNum = *entryNum;
5005		lp->flags = flag;
5006	} else {
5007		lp->entryNum = *entryNum;
5008
5009		if (*entryNum == ENTRY_INIT) {
5010			lp->flags = BAM_GLOBAL;
5011		} else {
5012			lp->flags = BAM_ENTRY;
5013
5014			if (cmd && arg) {
5015				if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
5016					BAM_DPRINTF(("%s: setting ROOT: %s\n",
5017					    fcn, arg));
5018					curr_ent->flags |= BAM_ENTRY_ROOT;
5019				} else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
5020				    == 0) {
5021					BAM_DPRINTF(("%s: setting "
5022					    "FINDROOT: %s\n", fcn, arg));
5023					curr_ent->flags |= BAM_ENTRY_FINDROOT;
5024				} else if (strcmp(cmd,
5025				    menu_cmds[CHAINLOADER_CMD]) == 0) {
5026					BAM_DPRINTF(("%s: setting "
5027					    "CHAINLOADER: %s\n", fcn, arg));
5028					curr_ent->flags |=
5029					    BAM_ENTRY_CHAINLOADER;
5030				} else if (kernel_parser(curr_ent, cmd, arg,
5031				    lp->lineNum) != BAM_SUCCESS) {
5032					(void) module_parser(curr_ent, cmd,
5033					    arg, lp->lineNum);
5034				}
5035			}
5036		}
5037	}
5038
5039	/* record default, old default, and entry line ranges */
5040	if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
5041	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
5042		mp->curdefault = lp;
5043	} else if (lp->flags == BAM_COMMENT &&
5044	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
5045		mp->olddefault = lp;
5046	} else if (lp->flags == BAM_COMMENT &&
5047	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
5048		mp->old_rc_default = lp;
5049	} else if (lp->flags == BAM_ENTRY ||
5050	    (lp->flags == BAM_COMMENT &&
5051	    ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
5052		if (is_libbe_ent) {
5053			curr_ent->flags |= BAM_ENTRY_LIBBE;
5054			is_libbe_ent = 0;
5055		}
5056
5057		boot_entry_addline(curr_ent, lp);
5058	}
5059	append_line(mp, lp);
5060
5061	prev = lp;
5062}
5063
5064void
5065update_numbering(menu_t *mp)
5066{
5067	int lineNum;
5068	int entryNum;
5069	int old_default_value;
5070	line_t *lp, *prev, *default_lp, *default_entry;
5071	char buf[PATH_MAX];
5072
5073	if (mp->start == NULL) {
5074		return;
5075	}
5076
5077	lineNum = LINE_INIT;
5078	entryNum = ENTRY_INIT;
5079	old_default_value = ENTRY_INIT;
5080	lp = default_lp = default_entry = NULL;
5081
5082	prev = NULL;
5083	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
5084		lp->lineNum = ++lineNum;
5085
5086		/*
5087		 * Get the value of the default command
5088		 */
5089		if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
5090		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
5091		    lp->arg) {
5092			old_default_value = atoi(lp->arg);
5093			default_lp = lp;
5094		}
5095
5096		/*
5097		 * If not a booting entry, nothing else to fix for this
5098		 * entry
5099		 */
5100		if (lp->entryNum == ENTRY_INIT)
5101			continue;
5102
5103		/*
5104		 * Record the position of the default entry.
5105		 * The following works because global
5106		 * commands like default and timeout should precede
5107		 * actual boot entries, so old_default_value
5108		 * is already known (or default cmd is missing).
5109		 */
5110		if (default_entry == NULL &&
5111		    old_default_value != ENTRY_INIT &&
5112		    lp->entryNum == old_default_value) {
5113			default_entry = lp;
5114		}
5115
5116		/*
5117		 * Now fixup the entry number
5118		 */
5119		if (lp->cmd != NULL &&
5120		    strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
5121			lp->entryNum = ++entryNum;
5122			/* fixup the bootadm header */
5123			if (prev && prev->flags == BAM_COMMENT &&
5124			    prev->arg &&
5125			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
5126				prev->entryNum = lp->entryNum;
5127			}
5128		} else {
5129			lp->entryNum = entryNum;
5130		}
5131	}
5132
5133	/*
5134	 * No default command in menu, simply return
5135	 */
5136	if (default_lp == NULL) {
5137		return;
5138	}
5139
5140	free(default_lp->arg);
5141	free(default_lp->line);
5142
5143	if (default_entry == NULL) {
5144		default_lp->arg = s_strdup("0");
5145	} else {
5146		(void) snprintf(buf, sizeof (buf), "%d",
5147		    default_entry->entryNum);
5148		default_lp->arg = s_strdup(buf);
5149	}
5150
5151	/*
5152	 * The following is required since only the line field gets
5153	 * written back to menu.lst
5154	 */
5155	(void) snprintf(buf, sizeof (buf), "%s%s%s",
5156	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
5157	default_lp->line = s_strdup(buf);
5158}
5159
5160
5161static menu_t *
5162menu_read(char *menu_path)
5163{
5164	FILE *fp;
5165	char buf[BAM_MAXLINE], *cp;
5166	menu_t *mp;
5167	int line, entry, len, n;
5168
5169	mp = s_calloc(1, sizeof (menu_t));
5170
5171	fp = fopen(menu_path, "r");
5172	if (fp == NULL) { /* Let the caller handle this error */
5173		free(mp);
5174		return (NULL);
5175	}
5176
5177	/* Note: GRUB boot entry number starts with 0 */
5178	line = LINE_INIT;
5179	entry = ENTRY_INIT;
5180	cp = buf;
5181	len = sizeof (buf);
5182	while (s_fgets(cp, len, fp) != NULL) {
5183		n = strlen(cp);
5184		if (cp[n - 1] == '\\') {
5185			len -= n - 1;
5186			assert(len >= 2);
5187			cp += n - 1;
5188			continue;
5189		}
5190		line_parser(mp, buf, &line, &entry);
5191		cp = buf;
5192		len = sizeof (buf);
5193	}
5194
5195	if (fclose(fp) == EOF) {
5196		bam_error(_("failed to close file: %s: %s\n"), menu_path,
5197		    strerror(errno));
5198	}
5199
5200	return (mp);
5201}
5202
5203static error_t
5204selector(menu_t *mp, char *opt, int *entry, char **title)
5205{
5206	char *eq;
5207	char *opt_dup;
5208	int entryNum;
5209
5210	assert(mp);
5211	assert(mp->start);
5212	assert(opt);
5213
5214	opt_dup = s_strdup(opt);
5215
5216	if (entry)
5217		*entry = ENTRY_INIT;
5218	if (title)
5219		*title = NULL;
5220
5221	eq = strchr(opt_dup, '=');
5222	if (eq == NULL) {
5223		bam_error(_("invalid option: %s\n"), opt);
5224		free(opt_dup);
5225		return (BAM_ERROR);
5226	}
5227
5228	*eq = '\0';
5229	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
5230		assert(mp->end);
5231		entryNum = s_strtol(eq + 1);
5232		if (entryNum < 0 || entryNum > mp->end->entryNum) {
5233			bam_error(_("invalid boot entry number: %s\n"), eq + 1);
5234			free(opt_dup);
5235			return (BAM_ERROR);
5236		}
5237		*entry = entryNum;
5238	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
5239		*title = opt + (eq - opt_dup) + 1;
5240	} else {
5241		bam_error(_("invalid option: %s\n"), opt);
5242		free(opt_dup);
5243		return (BAM_ERROR);
5244	}
5245
5246	free(opt_dup);
5247	return (BAM_SUCCESS);
5248}
5249
5250/*
5251 * If invoked with no titles/entries (opt == NULL)
5252 * only title lines in file are printed.
5253 *
5254 * If invoked with a title or entry #, all
5255 * lines in *every* matching entry are listed
5256 */
5257static error_t
5258list_entry(menu_t *mp, char *menu_path, char *opt)
5259{
5260	line_t *lp;
5261	int entry = ENTRY_INIT;
5262	int found;
5263	char *title = NULL;
5264
5265	assert(mp);
5266	assert(menu_path);
5267
5268	/* opt is optional */
5269	BAM_DPRINTF(("%s: entered. args: %s %s\n", "list_entry", menu_path,
5270	    opt ? opt : "<NULL>"));
5271
5272	if (mp->start == NULL) {
5273		bam_error(_("menu file not found: %s\n"), menu_path);
5274		return (BAM_ERROR);
5275	}
5276
5277	if (opt != NULL) {
5278		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
5279			return (BAM_ERROR);
5280		}
5281		assert((entry != ENTRY_INIT) ^ (title != NULL));
5282	} else {
5283		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
5284		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
5285	}
5286
5287	found = 0;
5288	for (lp = mp->start; lp; lp = lp->next) {
5289		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
5290			continue;
5291		if (opt == NULL && lp->flags == BAM_TITLE) {
5292			bam_print(_("%d %s\n"), lp->entryNum,
5293			    lp->arg);
5294			found = 1;
5295			continue;
5296		}
5297		if (entry != ENTRY_INIT && lp->entryNum == entry) {
5298			bam_print(_("%s\n"), lp->line);
5299			found = 1;
5300			continue;
5301		}
5302
5303		/*
5304		 * We set the entry value here so that all lines
5305		 * in entry get printed. If we subsequently match
5306		 * title in other entries, all lines in those
5307		 * entries get printed as well.
5308		 */
5309		if (title && lp->flags == BAM_TITLE && lp->arg &&
5310		    strncmp(title, lp->arg, strlen(title)) == 0) {
5311			bam_print(_("%s\n"), lp->line);
5312			entry = lp->entryNum;
5313			found = 1;
5314			continue;
5315		}
5316	}
5317
5318	if (!found) {
5319		bam_error(_("no matching entry found\n"));
5320		return (BAM_ERROR);
5321	}
5322
5323	return (BAM_SUCCESS);
5324}
5325
5326int
5327add_boot_entry(menu_t *mp,
5328    char *title,
5329    char *findroot,
5330    char *kernel,
5331    char *mod_kernel,
5332    char *module,
5333    char *bootfs)
5334{
5335	int		lineNum;
5336	int		entryNum;
5337	char		linebuf[BAM_MAXLINE];
5338	menu_cmd_t	k_cmd;
5339	menu_cmd_t	m_cmd;
5340	const char	*fcn = "add_boot_entry()";
5341
5342	assert(mp);
5343
5344	INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
5345	if (findroot == NULL) {
5346		bam_error(_("can't find argument for findroot command\n"));
5347		return (BAM_ERROR);
5348	}
5349
5350	if (title == NULL) {
5351		title = "Solaris";	/* default to Solaris */
5352	}
5353	if (kernel == NULL) {
5354		bam_error(_("missing suboption: %s\n"), menu_cmds[KERNEL_CMD]);
5355		return (BAM_ERROR);
5356	}
5357	if (module == NULL) {
5358		if (bam_direct != BAM_DIRECT_DBOOT) {
5359			bam_error(_("missing suboption: %s\n"),
5360			    menu_cmds[MODULE_CMD]);
5361			return (BAM_ERROR);
5362		}
5363
5364		/* Figure the commands out from the kernel line */
5365		if (strstr(kernel, "$ISADIR") != NULL) {
5366			module = DIRECT_BOOT_ARCHIVE;
5367		} else if (strstr(kernel, "amd64") != NULL) {
5368			module = DIRECT_BOOT_ARCHIVE_64;
5369		} else {
5370			module = DIRECT_BOOT_ARCHIVE_32;
5371		}
5372	}
5373
5374	k_cmd = KERNEL_DOLLAR_CMD;
5375	m_cmd = MODULE_DOLLAR_CMD;
5376
5377	if (mp->start) {
5378		lineNum = mp->end->lineNum;
5379		entryNum = mp->end->entryNum;
5380	} else {
5381		lineNum = LINE_INIT;
5382		entryNum = ENTRY_INIT;
5383	}
5384
5385	/*
5386	 * No separator for comment (HDR/FTR) commands
5387	 * The syntax for comments is #<comment>
5388	 */
5389	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5390	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5391	line_parser(mp, linebuf, &lineNum, &entryNum);
5392
5393	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5394	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5395	line_parser(mp, linebuf, &lineNum, &entryNum);
5396
5397	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5398	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5399	line_parser(mp, linebuf, &lineNum, &entryNum);
5400	BAM_DPRINTF(("%s: findroot added: line#: %d: entry#: %d\n",
5401	    fcn, lineNum, entryNum));
5402
5403	if (bootfs != NULL) {
5404		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5405		    menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5406		line_parser(mp, linebuf, &lineNum, &entryNum);
5407	}
5408
5409	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5410	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5411	line_parser(mp, linebuf, &lineNum, &entryNum);
5412
5413	if (mod_kernel != NULL) {
5414		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5415		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5416		line_parser(mp, linebuf, &lineNum, &entryNum);
5417	}
5418
5419	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5420	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5421	line_parser(mp, linebuf, &lineNum, &entryNum);
5422
5423	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5424	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5425	line_parser(mp, linebuf, &lineNum, &entryNum);
5426
5427	return (entryNum);
5428}
5429
5430error_t
5431delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5432{
5433	line_t		*lp;
5434	line_t		*freed;
5435	entry_t		*ent;
5436	entry_t		*tmp;
5437	int		deleted = 0;
5438	const char	*fcn = "delete_boot_entry()";
5439
5440	assert(entryNum != ENTRY_INIT);
5441
5442	tmp = NULL;
5443
5444	ent = mp->entries;
5445	while (ent) {
5446		lp = ent->start;
5447
5448		/*
5449		 * Check entry number and make sure it's a modifiable entry.
5450		 *
5451		 * Guidelines:
5452		 *	+ We can modify a bootadm-created entry
5453		 *	+ We can modify a libbe-created entry
5454		 */
5455		if ((lp->flags != BAM_COMMENT &&
5456		    (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5457		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5458		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5459			ent = ent->next;
5460			continue;
5461		}
5462
5463		/* free the entry content */
5464		do {
5465			freed = lp;
5466			lp = lp->next;	/* prev stays the same */
5467			BAM_DPRINTF(("%s: freeing line: %d\n",
5468			    fcn, freed->lineNum));
5469			unlink_line(mp, freed);
5470			line_free(freed);
5471		} while (freed != ent->end);
5472
5473		/* free the entry_t structure */
5474		assert(tmp == NULL);
5475		tmp = ent;
5476		ent = ent->next;
5477		if (tmp->prev)
5478			tmp->prev->next = ent;
5479		else
5480			mp->entries = ent;
5481		if (ent)
5482			ent->prev = tmp->prev;
5483		BAM_DPRINTF(("%s: freeing entry: %d\n", fcn, tmp->entryNum));
5484		free(tmp);
5485		tmp = NULL;
5486		deleted = 1;
5487	}
5488
5489	assert(tmp == NULL);
5490
5491	if (!deleted && entryNum != ALL_ENTRIES) {
5492		if (quiet == DBE_PRINTERR)
5493			bam_error(_("no matching bootadm entry found\n"));
5494		return (BAM_ERROR);
5495	}
5496
5497	/*
5498	 * Now that we have deleted an entry, update
5499	 * the entry numbering and the default cmd.
5500	 */
5501	update_numbering(mp);
5502
5503	return (BAM_SUCCESS);
5504}
5505
5506static error_t
5507delete_all_entries(menu_t *mp, char *dummy, char *opt)
5508{
5509	assert(mp);
5510	assert(dummy == NULL);
5511	assert(opt == NULL);
5512
5513	BAM_DPRINTF(("%s: entered. No args\n", "delete_all_entries"));
5514
5515	if (mp->start == NULL) {
5516		bam_print(_("the GRUB menu is empty\n"));
5517		return (BAM_SUCCESS);
5518	}
5519
5520	if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5521		return (BAM_ERROR);
5522	}
5523
5524	return (BAM_WRITE);
5525}
5526
5527static FILE *
5528create_diskmap(char *osroot)
5529{
5530	FILE *fp;
5531	char cmd[PATH_MAX + 16];
5532	char path[PATH_MAX];
5533	const char *fcn = "create_diskmap()";
5534
5535	/* make sure we have a map file */
5536	fp = fopen(GRUBDISK_MAP, "r");
5537	if (fp == NULL) {
5538		int	ret;
5539
5540		ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5541		    CREATE_DISKMAP);
5542		if (ret >= sizeof (path)) {
5543			bam_error(_("unable to create path on mountpoint %s, "
5544			    "path too long\n"), osroot);
5545			return (NULL);
5546		}
5547		if (is_safe_exec(path) == BAM_ERROR)
5548			return (NULL);
5549
5550		(void) snprintf(cmd, sizeof (cmd),
5551		    "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5552		if (exec_cmd(cmd, NULL) != 0)
5553			return (NULL);
5554		fp = fopen(GRUBDISK_MAP, "r");
5555		INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5556		if (fp) {
5557			BAM_DPRINTF(("%s: created diskmap file: %s\n",
5558			    fcn, GRUBDISK_MAP));
5559		} else {
5560			BAM_DPRINTF(("%s: FAILED to create diskmap file: %s\n",
5561			    fcn, GRUBDISK_MAP));
5562		}
5563	}
5564	return (fp);
5565}
5566
5567#define	SECTOR_SIZE	512
5568
5569static int
5570get_partition(char *device)
5571{
5572	int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5573	struct mboot *mboot;
5574	char boot_sect[SECTOR_SIZE];
5575	char *wholedisk, *slice;
5576#ifdef i386
5577	ext_part_t *epp;
5578	uint32_t secnum, numsec;
5579	int rval, pno, ext_partno = PARTNO_NOTFOUND;
5580#endif
5581
5582	/* form whole disk (p0) */
5583	slice = device + strlen(device) - 2;
5584	is_pcfs = (*slice != 's');
5585	if (!is_pcfs)
5586		*slice = '\0';
5587	wholedisk = s_calloc(1, strlen(device) + 3);
5588	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5589	if (!is_pcfs)
5590		*slice = 's';
5591
5592	/* read boot sector */
5593	fd = open(wholedisk, O_RDONLY);
5594	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5595		return (partno);
5596	}
5597	(void) close(fd);
5598
5599#ifdef i386
5600	/* Read/Initialize extended partition information */
5601	if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5602	    != FDISK_SUCCESS) {
5603		switch (rval) {
5604			/*
5605			 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5606			 * be considered as soft errors and hence
5607			 * we do not return
5608			 */
5609			case FDISK_EBADLOGDRIVE:
5610				break;
5611			case FDISK_ENOLOGDRIVE:
5612				break;
5613			case FDISK_EBADMAGIC:
5614				/*FALLTHROUGH*/
5615			default:
5616				free(wholedisk);
5617				libfdisk_fini(&epp);
5618				return (partno);
5619		}
5620	}
5621#endif
5622	free(wholedisk);
5623
5624	/* parse fdisk table */
5625	mboot = (struct mboot *)((void *)boot_sect);
5626	for (i = 0; i < FD_NUMPART; i++) {
5627		struct ipart *part =
5628		    (struct ipart *)(uintptr_t)mboot->parts + i;
5629		if (is_pcfs) {	/* looking for solaris boot part */
5630			if (part->systid == 0xbe) {
5631				partno = i;
5632				break;
5633			}
5634		} else {	/* look for solaris partition, old and new */
5635			if (part->systid == EFI_PMBR) {
5636				partno = PARTNO_EFI;
5637				break;
5638			}
5639
5640#ifdef i386
5641			if ((part->systid == SUNIXOS &&
5642			    (fdisk_is_linux_swap(epp, part->relsect,
5643			    NULL) != 0)) || part->systid == SUNIXOS2) {
5644#else
5645			if (part->systid == SUNIXOS ||
5646			    part->systid == SUNIXOS2) {
5647#endif
5648				partno = i;
5649				break;
5650			}
5651
5652#ifdef i386
5653			if (fdisk_is_dos_extended(part->systid))
5654				ext_partno = i;
5655#endif
5656		}
5657	}
5658#ifdef i386
5659	/* If no primary solaris partition, check extended partition */
5660	if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5661		rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5662		if (rval == FDISK_SUCCESS) {
5663			partno = pno - 1;
5664		}
5665	}
5666	libfdisk_fini(&epp);
5667#endif
5668	return (partno);
5669}
5670
5671char *
5672get_grubroot(char *osroot, char *osdev, char *menu_root)
5673{
5674	char		*grubroot;	/* (hd#,#,#) */
5675	char		*slice;
5676	char		*grubhd = NULL;
5677	int		fdiskpart;
5678	int		found = 0;
5679	char		*devname;
5680	char		*ctdname = strstr(osdev, "dsk/");
5681	char		linebuf[PATH_MAX];
5682	FILE		*fp;
5683
5684	INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5685	if (ctdname == NULL) {
5686		bam_error(_("not a /dev/[r]dsk name: %s\n"), osdev);
5687		return (NULL);
5688	}
5689
5690	if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5691		/* menu bears no resemblance to our reality */
5692		bam_error(_("cannot get (hd?,?,?) for menu. menu not on "
5693		    "bootdisk: %s\n"), osdev);
5694		return (NULL);
5695	}
5696
5697	ctdname += strlen("dsk/");
5698	slice = strrchr(ctdname, 's');
5699	if (slice)
5700		*slice = '\0';
5701
5702	fp = create_diskmap(osroot);
5703	if (fp == NULL) {
5704		bam_error(_("create_diskmap command failed for OS root: %s.\n"),
5705		    osroot);
5706		return (NULL);
5707	}
5708
5709	rewind(fp);
5710	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5711		grubhd = strtok(linebuf, " \t\n");
5712		if (grubhd)
5713			devname = strtok(NULL, " \t\n");
5714		else
5715			devname = NULL;
5716		if (devname && strcmp(devname, ctdname) == 0) {
5717			found = 1;
5718			break;
5719		}
5720	}
5721
5722	if (slice)
5723		*slice = 's';
5724
5725	(void) fclose(fp);
5726	fp = NULL;
5727
5728	INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5729	if (found == 0) {
5730		bam_error(_("not using biosdev command for disk: %s.\n"),
5731		    osdev);
5732		return (NULL);
5733	}
5734
5735	fdiskpart = get_partition(osdev);
5736	INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5737	if (fdiskpart == PARTNO_NOTFOUND) {
5738		bam_error(_("failed to determine fdisk partition: %s\n"),
5739		    osdev);
5740		return (NULL);
5741	}
5742
5743	grubroot = s_calloc(1, 10);
5744	if (fdiskpart == PARTNO_EFI) {
5745		fdiskpart = atoi(&slice[1]);
5746		slice = NULL;
5747	}
5748
5749	if (slice) {
5750		(void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5751		    grubhd, fdiskpart, slice[1] + 'a' - '0');
5752	} else
5753		(void) snprintf(grubroot, 10, "(hd%s,%d)",
5754		    grubhd, fdiskpart);
5755
5756	assert(fp == NULL);
5757	assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5758	return (grubroot);
5759}
5760
5761static char *
5762find_primary_common(char *mntpt, char *fstype)
5763{
5764	char		signdir[PATH_MAX];
5765	char		tmpsign[MAXNAMELEN + 1];
5766	char		*lu;
5767	char		*ufs;
5768	char		*zfs;
5769	DIR		*dirp = NULL;
5770	struct dirent	*entp;
5771	struct stat	sb;
5772	const char	*fcn = "find_primary_common()";
5773
5774	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
5775	    mntpt, GRUBSIGN_DIR);
5776
5777	if (stat(signdir, &sb) == -1) {
5778		BAM_DPRINTF(("%s: no sign dir: %s\n", fcn, signdir));
5779		return (NULL);
5780	}
5781
5782	dirp = opendir(signdir);
5783	INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5784	if (dirp == NULL) {
5785		bam_error(_("opendir of %s failed: %s\n"), signdir,
5786		    strerror(errno));
5787		return (NULL);
5788	}
5789
5790	ufs = zfs = lu = NULL;
5791
5792	while ((entp = readdir(dirp)) != NULL) {
5793		if (strcmp(entp->d_name, ".") == 0 ||
5794		    strcmp(entp->d_name, "..") == 0)
5795			continue;
5796
5797		(void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5798
5799		if (lu == NULL &&
5800		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5801		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5802			lu = s_strdup(tmpsign);
5803		}
5804
5805		if (ufs == NULL &&
5806		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5807		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5808			ufs = s_strdup(tmpsign);
5809		}
5810
5811		if (zfs == NULL &&
5812		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5813		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5814			zfs = s_strdup(tmpsign);
5815		}
5816	}
5817
5818	BAM_DPRINTF(("%s: existing primary signs: zfs=%s ufs=%s lu=%s\n", fcn,
5819	    zfs ? zfs : "NULL",
5820	    ufs ? ufs : "NULL",
5821	    lu ? lu : "NULL"));
5822
5823	if (dirp) {
5824		(void) closedir(dirp);
5825		dirp = NULL;
5826	}
5827
5828	if (strcmp(fstype, "ufs") == 0 && zfs) {
5829		bam_error(_("found mismatched boot signature %s for "
5830		    "filesystem type: %s.\n"), zfs, "ufs");
5831		free(zfs);
5832		zfs = NULL;
5833	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
5834		bam_error(_("found mismatched boot signature %s for "
5835		    "filesystem type: %s.\n"), ufs, "zfs");
5836		free(ufs);
5837		ufs = NULL;
5838	}
5839
5840	assert(dirp == NULL);
5841
5842	/* For now, we let Live Upgrade take care of its signature itself */
5843	if (lu) {
5844		BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5845		free(lu);
5846		lu = NULL;
5847	}
5848
5849	return (zfs ? zfs : ufs);
5850}
5851
5852static char *
5853find_backup_common(char *mntpt, char *fstype)
5854{
5855	FILE		*bfp = NULL;
5856	char		tmpsign[MAXNAMELEN + 1];
5857	char		backup[PATH_MAX];
5858	char		*ufs;
5859	char		*zfs;
5860	char		*lu;
5861	int		error;
5862	const char	*fcn = "find_backup_common()";
5863
5864	/*
5865	 * We didn't find it in the primary directory.
5866	 * Look at the backup
5867	 */
5868	(void) snprintf(backup, sizeof (backup), "%s%s",
5869	    mntpt, GRUBSIGN_BACKUP);
5870
5871	bfp = fopen(backup, "r");
5872	if (bfp == NULL) {
5873		error = errno;
5874		if (bam_verbose) {
5875			bam_error(_("failed to open file: %s: %s\n"),
5876			    backup, strerror(error));
5877		}
5878		BAM_DPRINTF(("%s: failed to open %s: %s\n",
5879		    fcn, backup, strerror(error)));
5880		return (NULL);
5881	}
5882
5883	ufs = zfs = lu = NULL;
5884
5885	while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5886
5887		if (lu == NULL &&
5888		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5889		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5890			lu = s_strdup(tmpsign);
5891		}
5892
5893		if (ufs == NULL &&
5894		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5895		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5896			ufs = s_strdup(tmpsign);
5897		}
5898
5899		if (zfs == NULL &&
5900		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5901		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5902			zfs = s_strdup(tmpsign);
5903		}
5904	}
5905
5906	BAM_DPRINTF(("%s: existing backup signs: zfs=%s ufs=%s lu=%s\n", fcn,
5907	    zfs ? zfs : "NULL",
5908	    ufs ? ufs : "NULL",
5909	    lu ? lu : "NULL"));
5910
5911	if (bfp) {
5912		(void) fclose(bfp);
5913		bfp = NULL;
5914	}
5915
5916	if (strcmp(fstype, "ufs") == 0 && zfs) {
5917		bam_error(_("found mismatched boot signature %s for "
5918		    "filesystem type: %s.\n"), zfs, "ufs");
5919		free(zfs);
5920		zfs = NULL;
5921	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
5922		bam_error(_("found mismatched boot signature %s for "
5923		    "filesystem type: %s.\n"), ufs, "zfs");
5924		free(ufs);
5925		ufs = NULL;
5926	}
5927
5928	assert(bfp == NULL);
5929
5930	/* For now, we let Live Upgrade take care of its signature itself */
5931	if (lu) {
5932		BAM_DPRINTF(("%s: feeing LU sign: %s\n", fcn, lu));
5933		free(lu);
5934		lu = NULL;
5935	}
5936
5937	return (zfs ? zfs : ufs);
5938}
5939
5940static char *
5941find_ufs_existing(char *osroot)
5942{
5943	char		*sign;
5944	const char	*fcn = "find_ufs_existing()";
5945
5946	sign = find_primary_common(osroot, "ufs");
5947	if (sign == NULL) {
5948		sign = find_backup_common(osroot, "ufs");
5949		BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
5950		    sign ? sign : "NULL"));
5951	} else {
5952		BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
5953	}
5954
5955	return (sign);
5956}
5957
5958char *
5959get_mountpoint(char *special, char *fstype)
5960{
5961	FILE		*mntfp;
5962	struct mnttab	mp = {0};
5963	struct mnttab	mpref = {0};
5964	int		error;
5965	int		ret;
5966	const char	*fcn = "get_mountpoint()";
5967
5968	BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
5969
5970	mntfp = fopen(MNTTAB, "r");
5971	error = errno;
5972	INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5973	if (mntfp == NULL) {
5974		bam_error(_("failed to open file: %s: %s\n"),
5975		    MNTTAB, strerror(error));
5976		return (NULL);
5977	}
5978
5979	mpref.mnt_special = special;
5980	mpref.mnt_fstype = fstype;
5981
5982	ret = getmntany(mntfp, &mp, &mpref);
5983	INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5984	if (ret != 0) {
5985		(void) fclose(mntfp);
5986		BAM_DPRINTF(("%s: no mount-point for special=%s and "
5987		    "fstype=%s\n", fcn, special, fstype));
5988		return (NULL);
5989	}
5990	(void) fclose(mntfp);
5991
5992	assert(mp.mnt_mountp);
5993
5994	BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
5995	    fcn, special, mp.mnt_mountp));
5996
5997	return (s_strdup(mp.mnt_mountp));
5998}
5999
6000/*
6001 * Mounts a "legacy" top dataset (if needed)
6002 * Returns:	The mountpoint of the legacy top dataset or NULL on error
6003 *		mnted returns one of the above values defined for zfs_mnted_t
6004 */
6005static char *
6006mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
6007{
6008	char		cmd[PATH_MAX];
6009	char		tmpmnt[PATH_MAX];
6010	filelist_t	flist = {0};
6011	char		*is_mounted;
6012	struct stat	sb;
6013	int		ret;
6014	const char	*fcn = "mount_legacy_dataset()";
6015
6016	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
6017
6018	*mnted = ZFS_MNT_ERROR;
6019
6020	(void) snprintf(cmd, sizeof (cmd),
6021	    "/sbin/zfs get -Ho value mounted %s",
6022	    pool);
6023
6024	ret = exec_cmd(cmd, &flist);
6025	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
6026	if (ret != 0) {
6027		bam_error(_("failed to determine mount status of ZFS "
6028		    "pool %s\n"), pool);
6029		return (NULL);
6030	}
6031
6032	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
6033	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6034		bam_error(_("ZFS pool %s has bad mount status\n"), pool);
6035		filelist_free(&flist);
6036		return (NULL);
6037	}
6038
6039	is_mounted = strtok(flist.head->line, " \t\n");
6040	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
6041	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
6042	if (strcmp(is_mounted, "no") != 0) {
6043		filelist_free(&flist);
6044		*mnted = LEGACY_ALREADY;
6045		/* get_mountpoint returns a strdup'ed string */
6046		BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
6047		    fcn, pool));
6048		return (get_mountpoint(pool, "zfs"));
6049	}
6050
6051	filelist_free(&flist);
6052
6053	/*
6054	 * legacy top dataset is not mounted. Mount it now
6055	 * First create a mountpoint.
6056	 */
6057	(void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
6058	    ZFS_LEGACY_MNTPT, getpid());
6059
6060	ret = stat(tmpmnt, &sb);
6061	if (ret == -1) {
6062		BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
6063		    fcn, pool, tmpmnt));
6064		ret = mkdirp(tmpmnt, DIR_PERMS);
6065		INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
6066		if (ret == -1) {
6067			bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
6068			    strerror(errno));
6069			return (NULL);
6070		}
6071	} else {
6072		BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
6073		    "present\n", fcn, pool, tmpmnt));
6074	}
6075
6076	(void) snprintf(cmd, sizeof (cmd),
6077	    "/sbin/mount -F zfs %s %s",
6078	    pool, tmpmnt);
6079
6080	ret = exec_cmd(cmd, NULL);
6081	INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
6082	if (ret != 0) {
6083		bam_error(_("mount of ZFS pool %s failed\n"), pool);
6084		(void) rmdir(tmpmnt);
6085		return (NULL);
6086	}
6087
6088	*mnted = LEGACY_MOUNTED;
6089	BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
6090	    fcn, pool, tmpmnt));
6091	return (s_strdup(tmpmnt));
6092}
6093
6094/*
6095 * Mounts the top dataset (if needed)
6096 * Returns:	The mountpoint of the top dataset or NULL on error
6097 *		mnted returns one of the above values defined for zfs_mnted_t
6098 */
6099char *
6100mount_top_dataset(char *pool, zfs_mnted_t *mnted)
6101{
6102	char		cmd[PATH_MAX];
6103	filelist_t	flist = {0};
6104	char		*is_mounted;
6105	char		*mntpt;
6106	char		*zmntpt;
6107	int		ret;
6108	const char	*fcn = "mount_top_dataset()";
6109
6110	*mnted = ZFS_MNT_ERROR;
6111
6112	BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
6113
6114	/*
6115	 * First check if the top dataset is a "legacy" dataset
6116	 */
6117	(void) snprintf(cmd, sizeof (cmd),
6118	    "/sbin/zfs get -Ho value mountpoint %s",
6119	    pool);
6120	ret = exec_cmd(cmd, &flist);
6121	INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
6122	if (ret != 0) {
6123		bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6124		    pool);
6125		return (NULL);
6126	}
6127
6128	if (flist.head && (flist.head == flist.tail)) {
6129		char *legacy = strtok(flist.head->line, " \t\n");
6130		if (legacy && strcmp(legacy, "legacy") == 0) {
6131			filelist_free(&flist);
6132			BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
6133			return (mount_legacy_dataset(pool, mnted));
6134		}
6135	}
6136
6137	filelist_free(&flist);
6138
6139	BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
6140
6141	(void) snprintf(cmd, sizeof (cmd),
6142	    "/sbin/zfs get -Ho value mounted %s",
6143	    pool);
6144
6145	ret = exec_cmd(cmd, &flist);
6146	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
6147	if (ret != 0) {
6148		bam_error(_("failed to determine mount status of ZFS "
6149		    "pool %s\n"), pool);
6150		return (NULL);
6151	}
6152
6153	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
6154	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6155		bam_error(_("ZFS pool %s has bad mount status\n"), pool);
6156		filelist_free(&flist);
6157		return (NULL);
6158	}
6159
6160	is_mounted = strtok(flist.head->line, " \t\n");
6161	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
6162	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
6163	if (strcmp(is_mounted, "no") != 0) {
6164		filelist_free(&flist);
6165		*mnted = ZFS_ALREADY;
6166		BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
6167		    fcn, pool));
6168		goto mounted;
6169	}
6170
6171	filelist_free(&flist);
6172	BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
6173	    fcn, pool));
6174
6175	/* top dataset is not mounted. Mount it now */
6176	(void) snprintf(cmd, sizeof (cmd),
6177	    "/sbin/zfs mount %s", pool);
6178	ret = exec_cmd(cmd, NULL);
6179	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
6180	if (ret != 0) {
6181		bam_error(_("mount of ZFS pool %s failed\n"), pool);
6182		return (NULL);
6183	}
6184	*mnted = ZFS_MOUNTED;
6185	BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
6186	/*FALLTHRU*/
6187mounted:
6188	/*
6189	 * Now get the mountpoint
6190	 */
6191	(void) snprintf(cmd, sizeof (cmd),
6192	    "/sbin/zfs get -Ho value mountpoint %s",
6193	    pool);
6194
6195	ret = exec_cmd(cmd, &flist);
6196	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
6197	if (ret != 0) {
6198		bam_error(_("failed to determine mount point of ZFS pool %s\n"),
6199		    pool);
6200		goto error;
6201	}
6202
6203	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
6204	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6205		bam_error(_("ZFS pool %s has no mount-point\n"), pool);
6206		goto error;
6207	}
6208
6209	mntpt = strtok(flist.head->line, " \t\n");
6210	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
6211	if (*mntpt != '/') {
6212		bam_error(_("ZFS pool %s has bad mount-point %s\n"),
6213		    pool, mntpt);
6214		goto error;
6215	}
6216	zmntpt = s_strdup(mntpt);
6217
6218	filelist_free(&flist);
6219
6220	BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
6221	    fcn, pool, zmntpt));
6222
6223	return (zmntpt);
6224
6225error:
6226	filelist_free(&flist);
6227	(void) umount_top_dataset(pool, *mnted, NULL);
6228	BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
6229	return (NULL);
6230}
6231
6232int
6233umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
6234{
6235	char		cmd[PATH_MAX];
6236	int		ret;
6237	const char	*fcn = "umount_top_dataset()";
6238
6239	INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
6240	switch (mnted) {
6241	case LEGACY_ALREADY:
6242	case ZFS_ALREADY:
6243		/* nothing to do */
6244		BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
6245		    "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
6246		free(mntpt);
6247		return (BAM_SUCCESS);
6248	case LEGACY_MOUNTED:
6249		(void) snprintf(cmd, sizeof (cmd),
6250		    "/sbin/umount %s", pool);
6251		ret = exec_cmd(cmd, NULL);
6252		INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
6253		if (ret != 0) {
6254			bam_error(_("umount of %s failed\n"), pool);
6255			free(mntpt);
6256			return (BAM_ERROR);
6257		}
6258		if (mntpt)
6259			(void) rmdir(mntpt);
6260		free(mntpt);
6261		BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
6262		    "successfully unmounted\n", fcn, pool));
6263		return (BAM_SUCCESS);
6264	case ZFS_MOUNTED:
6265		free(mntpt);
6266		(void) snprintf(cmd, sizeof (cmd),
6267		    "/sbin/zfs unmount %s", pool);
6268		ret = exec_cmd(cmd, NULL);
6269		INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
6270		if (ret != 0) {
6271			bam_error(_("umount of %s failed\n"), pool);
6272			return (BAM_ERROR);
6273		}
6274		BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
6275		    "successfully unmounted\n", fcn, pool));
6276		return (BAM_SUCCESS);
6277	default:
6278		bam_error(_("Internal error: bad saved mount state for "
6279		    "pool %s\n"), pool);
6280		return (BAM_ERROR);
6281	}
6282	/*NOTREACHED*/
6283}
6284
6285/*
6286 * For ZFS, osdev can be one of two forms
6287 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
6288 * It can be a /dev/[r]dsk special file. We handle both instances
6289 */
6290static char *
6291get_pool(char *osdev)
6292{
6293	char		cmd[PATH_MAX];
6294	char		buf[PATH_MAX];
6295	filelist_t	flist = {0};
6296	char		*pool;
6297	char		*cp;
6298	char		*slash;
6299	int		ret;
6300	const char	*fcn = "get_pool()";
6301
6302	INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
6303	if (osdev == NULL) {
6304		bam_error(_("NULL device: cannot determine pool name\n"));
6305		return (NULL);
6306	}
6307
6308	BAM_DPRINTF(("%s: osdev arg = %s\n", fcn, osdev));
6309
6310	if (osdev[0] != '/') {
6311		(void) strlcpy(buf, osdev, sizeof (buf));
6312		slash = strchr(buf, '/');
6313		if (slash)
6314			*slash = '\0';
6315		pool = s_strdup(buf);
6316		BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6317		return (pool);
6318	} else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
6319	    strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
6320		bam_error(_("invalid device %s: cannot determine pool name\n"),
6321		    osdev);
6322		return (NULL);
6323	}
6324
6325	/*
6326	 * Call the zfs fstyp directly since this is a zpool. This avoids
6327	 * potential pcfs conflicts if the first block wasn't cleared.
6328	 */
6329	(void) snprintf(cmd, sizeof (cmd),
6330	    "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
6331	    osdev);
6332
6333	ret = exec_cmd(cmd, &flist);
6334	INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
6335	if (ret != 0) {
6336		bam_error(_("fstyp -a on device %s failed\n"), osdev);
6337		return (NULL);
6338	}
6339
6340	INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
6341	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6342		bam_error(_("NULL fstyp -a output for device %s\n"), osdev);
6343		filelist_free(&flist);
6344		return (NULL);
6345	}
6346
6347	(void) strtok(flist.head->line, "'");
6348	cp = strtok(NULL, "'");
6349	INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
6350	if (cp == NULL) {
6351		bam_error(_("bad fstyp -a output for device %s\n"), osdev);
6352		filelist_free(&flist);
6353		return (NULL);
6354	}
6355
6356	pool = s_strdup(cp);
6357
6358	filelist_free(&flist);
6359
6360	BAM_DPRINTF(("%s: got pool. pool = %s\n", fcn, pool));
6361
6362	return (pool);
6363}
6364
6365static char *
6366find_zfs_existing(char *osdev)
6367{
6368	char		*pool;
6369	zfs_mnted_t	mnted;
6370	char		*mntpt;
6371	char		*sign;
6372	const char	*fcn = "find_zfs_existing()";
6373
6374	pool = get_pool(osdev);
6375	INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
6376	if (pool == NULL) {
6377		bam_error(_("failed to get pool for device: %s\n"), osdev);
6378		return (NULL);
6379	}
6380
6381	mntpt = mount_top_dataset(pool, &mnted);
6382	INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
6383	if (mntpt == NULL) {
6384		bam_error(_("failed to mount top dataset for pool: %s\n"),
6385		    pool);
6386		free(pool);
6387		return (NULL);
6388	}
6389
6390	sign = find_primary_common(mntpt, "zfs");
6391	if (sign == NULL) {
6392		sign = find_backup_common(mntpt, "zfs");
6393		BAM_DPRINTF(("%s: existing backup sign: %s\n", fcn,
6394		    sign ? sign : "NULL"));
6395	} else {
6396		BAM_DPRINTF(("%s: existing primary sign: %s\n", fcn, sign));
6397	}
6398
6399	(void) umount_top_dataset(pool, mnted, mntpt);
6400
6401	free(pool);
6402
6403	return (sign);
6404}
6405
6406static char *
6407find_existing_sign(char *osroot, char *osdev, char *fstype)
6408{
6409	const char		*fcn = "find_existing_sign()";
6410
6411	INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6412	if (strcmp(fstype, "ufs") == 0) {
6413		BAM_DPRINTF(("%s: checking for existing UFS sign\n", fcn));
6414		return (find_ufs_existing(osroot));
6415	} else if (strcmp(fstype, "zfs") == 0) {
6416		BAM_DPRINTF(("%s: checking for existing ZFS sign\n", fcn));
6417		return (find_zfs_existing(osdev));
6418	} else {
6419		bam_error(_("boot signature not supported for fstype: %s\n"),
6420		    fstype);
6421		return (NULL);
6422	}
6423}
6424
6425#define	MH_HASH_SZ	16
6426
6427typedef enum {
6428	MH_ERROR = -1,
6429	MH_NOMATCH,
6430	MH_MATCH
6431} mh_search_t;
6432
6433typedef struct mcache {
6434	char	*mc_special;
6435	char	*mc_mntpt;
6436	char	*mc_fstype;
6437	struct mcache *mc_next;
6438} mcache_t;
6439
6440typedef struct mhash {
6441	mcache_t *mh_hash[MH_HASH_SZ];
6442} mhash_t;
6443
6444static int
6445mhash_fcn(char *key)
6446{
6447	int		i;
6448	uint64_t	sum = 0;
6449
6450	for (i = 0; key[i] != '\0'; i++) {
6451		sum += (uchar_t)key[i];
6452	}
6453
6454	sum %= MH_HASH_SZ;
6455
6456	assert(sum < MH_HASH_SZ);
6457
6458	return (sum);
6459}
6460
6461static mhash_t *
6462cache_mnttab(void)
6463{
6464	FILE		*mfp;
6465	struct extmnttab mnt;
6466	mcache_t	*mcp;
6467	mhash_t		*mhp;
6468	char		*ctds;
6469	int		idx;
6470	int		error;
6471	char		*special_dup;
6472	const char	*fcn = "cache_mnttab()";
6473
6474	mfp = fopen(MNTTAB, "r");
6475	error = errno;
6476	INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6477	if (mfp == NULL) {
6478		bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
6479		    strerror(error));
6480		return (NULL);
6481	}
6482
6483	mhp = s_calloc(1, sizeof (mhash_t));
6484
6485	resetmnttab(mfp);
6486
6487	while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6488		/* only cache ufs */
6489		if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6490			continue;
6491
6492		/* basename() modifies its arg, so dup it */
6493		special_dup = s_strdup(mnt.mnt_special);
6494		ctds = basename(special_dup);
6495
6496		mcp = s_calloc(1, sizeof (mcache_t));
6497		mcp->mc_special = s_strdup(ctds);
6498		mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6499		mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6500		BAM_DPRINTF(("%s: caching mount: special=%s, mntpt=%s, "
6501		    "fstype=%s\n", fcn, ctds, mnt.mnt_mountp, mnt.mnt_fstype));
6502		idx = mhash_fcn(ctds);
6503		mcp->mc_next = mhp->mh_hash[idx];
6504		mhp->mh_hash[idx] = mcp;
6505		free(special_dup);
6506	}
6507
6508	(void) fclose(mfp);
6509
6510	return (mhp);
6511}
6512
6513static void
6514free_mnttab(mhash_t *mhp)
6515{
6516	mcache_t	*mcp;
6517	int		i;
6518
6519	for (i = 0; i < MH_HASH_SZ; i++) {
6520		while ((mcp = mhp->mh_hash[i]) != NULL) {
6521			mhp->mh_hash[i] = mcp->mc_next;
6522			free(mcp->mc_special);
6523			free(mcp->mc_mntpt);
6524			free(mcp->mc_fstype);
6525			free(mcp);
6526		}
6527	}
6528
6529	for (i = 0; i < MH_HASH_SZ; i++) {
6530		assert(mhp->mh_hash[i] == NULL);
6531	}
6532	free(mhp);
6533}
6534
6535static mh_search_t
6536search_hash(mhash_t *mhp, char *special, char **mntpt)
6537{
6538	int		idx;
6539	mcache_t	*mcp;
6540	const char	*fcn = "search_hash()";
6541
6542	assert(mntpt);
6543
6544	*mntpt = NULL;
6545
6546	INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6547	if (strchr(special, '/')) {
6548		bam_error(_("invalid key for mnttab hash: %s\n"), special);
6549		return (MH_ERROR);
6550	}
6551
6552	idx = mhash_fcn(special);
6553
6554	for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6555		if (strcmp(mcp->mc_special, special) == 0)
6556			break;
6557	}
6558
6559	if (mcp == NULL) {
6560		BAM_DPRINTF(("%s: no match in cache for: %s\n", fcn, special));
6561		return (MH_NOMATCH);
6562	}
6563
6564	assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6565	*mntpt = mcp->mc_mntpt;
6566	BAM_DPRINTF(("%s: *MATCH* in cache for: %s\n", fcn, special));
6567	return (MH_MATCH);
6568}
6569
6570static int
6571check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6572{
6573	char		*sign;
6574	char		*signline;
6575	char		signbuf[MAXNAMELEN];
6576	int		len;
6577	int		error;
6578	const char	*fcn = "check_add_ufs_sign_to_list()";
6579
6580	/* safe to specify NULL as "osdev" arg for UFS */
6581	sign = find_existing_sign(mntpt, NULL, "ufs");
6582	if (sign == NULL) {
6583		/* No existing signature, nothing to add to list */
6584		BAM_DPRINTF(("%s: no sign on %s to add to signlist\n",
6585		    fcn, mntpt));
6586		return (0);
6587	}
6588
6589	(void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6590	signline = signbuf;
6591
6592	INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6593	if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6594	    strlen(GRUBSIGN_UFS_PREFIX))) {
6595		bam_error(_("invalid UFS boot signature %s\n"), sign);
6596		free(sign);
6597		/* ignore invalid signatures */
6598		return (0);
6599	}
6600
6601	len = fputs(signline, tfp);
6602	error = errno;
6603	INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6604	if (len != strlen(signline)) {
6605		bam_error(_("failed to write signature %s to signature "
6606		    "list: %s\n"), sign, strerror(error));
6607		free(sign);
6608		return (-1);
6609	}
6610
6611	free(sign);
6612
6613	BAM_DPRINTF(("%s: successfully added sign on %s to signlist\n",
6614	    fcn, mntpt));
6615	return (0);
6616}
6617
6618/*
6619 * slice is a basename not a full pathname
6620 */
6621static int
6622process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6623{
6624	int		ret;
6625	char		cmd[PATH_MAX];
6626	char		path[PATH_MAX];
6627	struct stat	sbuf;
6628	char		*mntpt;
6629	filelist_t	flist = {0};
6630	char		*fstype;
6631	char		blkslice[PATH_MAX];
6632	const char	*fcn = "process_slice_common()";
6633
6634
6635	ret = search_hash(mhp, slice, &mntpt);
6636	switch (ret) {
6637		case MH_MATCH:
6638			if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6639				return (-1);
6640			else
6641				return (0);
6642		case MH_NOMATCH:
6643			break;
6644		case MH_ERROR:
6645		default:
6646			return (-1);
6647	}
6648
6649	(void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6650	if (stat(path, &sbuf) == -1) {
6651		BAM_DPRINTF(("%s: slice does not exist: %s\n", fcn, path));
6652		return (0);
6653	}
6654
6655	/* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6656	(void) snprintf(cmd, sizeof (cmd),
6657	    "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6658	    slice);
6659
6660	if (exec_cmd(cmd, &flist) != 0) {
6661		if (bam_verbose)
6662			bam_print(_("fstyp failed for slice: %s\n"), slice);
6663		return (0);
6664	}
6665
6666	if ((flist.head == NULL) || (flist.head != flist.tail)) {
6667		if (bam_verbose)
6668			bam_print(_("bad output from fstyp for slice: %s\n"),
6669			    slice);
6670		filelist_free(&flist);
6671		return (0);
6672	}
6673
6674	fstype = strtok(flist.head->line, " \t\n");
6675	if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6676		if (bam_verbose)
6677			bam_print(_("%s is not a ufs slice: %s\n"),
6678			    slice, fstype);
6679		filelist_free(&flist);
6680		return (0);
6681	}
6682
6683	filelist_free(&flist);
6684
6685	/*
6686	 * Since we are mounting the filesystem read-only, the
6687	 * the last mount field of the superblock is unchanged
6688	 * and does not need to be fixed up post-mount;
6689	 */
6690
6691	(void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6692	    slice);
6693
6694	(void) snprintf(cmd, sizeof (cmd),
6695	    "/usr/sbin/mount -F ufs -o ro %s %s "
6696	    "> /dev/null 2>&1", blkslice, tmpmnt);
6697
6698	if (exec_cmd(cmd, NULL) != 0) {
6699		if (bam_verbose)
6700			bam_print(_("mount of %s (fstype %s) failed\n"),
6701			    blkslice, "ufs");
6702		return (0);
6703	}
6704
6705	ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6706
6707	(void) snprintf(cmd, sizeof (cmd),
6708	    "/usr/sbin/umount -f %s > /dev/null 2>&1",
6709	    tmpmnt);
6710
6711	if (exec_cmd(cmd, NULL) != 0) {
6712		bam_print(_("umount of %s failed\n"), slice);
6713		return (0);
6714	}
6715
6716	return (ret);
6717}
6718
6719static int
6720process_vtoc_slices(
6721	char *s0,
6722	struct vtoc *vtoc,
6723	FILE *tfp,
6724	mhash_t *mhp,
6725	char *tmpmnt)
6726{
6727	int		idx;
6728	char		slice[PATH_MAX];
6729	size_t		len;
6730	char		*cp;
6731	const char	*fcn = "process_vtoc_slices()";
6732
6733	len = strlen(s0);
6734
6735	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6736
6737	s0[len - 1] = '\0';
6738
6739	(void) strlcpy(slice, s0, sizeof (slice));
6740
6741	s0[len - 1] = '0';
6742
6743	cp = slice + len - 1;
6744
6745	for (idx = 0; idx < vtoc->v_nparts; idx++) {
6746
6747		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6748
6749		if (vtoc->v_part[idx].p_size == 0) {
6750			BAM_DPRINTF(("%s: VTOC: skipping 0-length slice: %s\n",
6751			    fcn, slice));
6752			continue;
6753		}
6754
6755		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6756		switch (vtoc->v_part[idx].p_tag) {
6757		case V_SWAP:
6758		case V_USR:
6759		case V_BACKUP:
6760		case V_VAR:
6761		case V_HOME:
6762		case V_ALTSCTR:
6763			BAM_DPRINTF(("%s: VTOC: unsupported tag, "
6764			    "skipping: %s\n", fcn, slice));
6765			continue;
6766		default:
6767			BAM_DPRINTF(("%s: VTOC: supported tag, checking: %s\n",
6768			    fcn, slice));
6769			break;
6770		}
6771
6772		/* skip unmountable and readonly slices */
6773		switch (vtoc->v_part[idx].p_flag) {
6774		case V_UNMNT:
6775		case V_RONLY:
6776			BAM_DPRINTF(("%s: VTOC: non-RDWR flag, skipping: %s\n",
6777			    fcn, slice));
6778			continue;
6779		default:
6780			BAM_DPRINTF(("%s: VTOC: RDWR flag, checking: %s\n",
6781			    fcn, slice));
6782			break;
6783		}
6784
6785		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6786			return (-1);
6787		}
6788	}
6789
6790	return (0);
6791}
6792
6793static int
6794process_efi_slices(
6795	char *s0,
6796	struct dk_gpt *efi,
6797	FILE *tfp,
6798	mhash_t *mhp,
6799	char *tmpmnt)
6800{
6801	int		idx;
6802	char