xref: /onnv-gate/usr/src/cmd/boot/bootadm/bootadm.c (revision 8751:0019d4be68ad)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51746Svikram  * Common Development and Distribution License (the "License").
61746Svikram  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
228642SVikram.Hegde@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * bootadm(1M) is a new utility for managing bootability of
280Sstevel@tonic-gate  * Solaris *Newboot* environments. It has two primary tasks:
290Sstevel@tonic-gate  * 	- Allow end users to manage bootability of Newboot Solaris instances
300Sstevel@tonic-gate  *	- Provide services to other subsystems in Solaris (primarily Install)
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate /* Headers */
340Sstevel@tonic-gate #include <stdio.h>
350Sstevel@tonic-gate #include <errno.h>
360Sstevel@tonic-gate #include <stdlib.h>
370Sstevel@tonic-gate #include <string.h>
380Sstevel@tonic-gate #include <unistd.h>
390Sstevel@tonic-gate #include <sys/types.h>
400Sstevel@tonic-gate #include <sys/stat.h>
410Sstevel@tonic-gate #include <stdarg.h>
420Sstevel@tonic-gate #include <limits.h>
430Sstevel@tonic-gate #include <signal.h>
440Sstevel@tonic-gate #include <sys/wait.h>
450Sstevel@tonic-gate #include <sys/mnttab.h>
467543SJerry.Gilliam@Sun.COM #include <sys/mntent.h>
470Sstevel@tonic-gate #include <sys/statvfs.h>
480Sstevel@tonic-gate #include <libnvpair.h>
490Sstevel@tonic-gate #include <ftw.h>
500Sstevel@tonic-gate #include <fcntl.h>
510Sstevel@tonic-gate #include <strings.h>
524581Ssherrym #include <utime.h>
530Sstevel@tonic-gate #include <sys/systeminfo.h>
540Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
552334Ssetje #include <sys/param.h>
566448Svikram #include <dirent.h>
576448Svikram #include <ctype.h>
586448Svikram #include <libgen.h>
596423Sgw25295 #include <sys/sysmacros.h>
608735SEnrico.Perla@Sun.COM #include <sys/elf.h>
616694Svikram #include <libscf.h>
628735SEnrico.Perla@Sun.COM #include <zlib.h>
638735SEnrico.Perla@Sun.COM #include <sys/lockfs.h>
648735SEnrico.Perla@Sun.COM #include <sys/filio.h>
655648Ssetje 
665648Ssetje #if !defined(_OPB)
674581Ssherrym #include <sys/ucode.h>
684581Ssherrym #endif
690Sstevel@tonic-gate 
700Sstevel@tonic-gate #include <pwd.h>
710Sstevel@tonic-gate #include <grp.h>
720Sstevel@tonic-gate #include <device_info.h>
736448Svikram #include <sys/vtoc.h>
746448Svikram #include <sys/efi_partition.h>
756448Svikram 
760Sstevel@tonic-gate #include <locale.h>
770Sstevel@tonic-gate 
780Sstevel@tonic-gate #include "message.h"
793446Smrj #include "bootadm.h"
800Sstevel@tonic-gate 
810Sstevel@tonic-gate #ifndef TEXT_DOMAIN
820Sstevel@tonic-gate #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
830Sstevel@tonic-gate #endif	/* TEXT_DOMAIN */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate /* Type definitions */
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /* Primary subcmds */
880Sstevel@tonic-gate typedef enum {
890Sstevel@tonic-gate 	BAM_MENU = 3,
900Sstevel@tonic-gate 	BAM_ARCHIVE
910Sstevel@tonic-gate } subcmd_t;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate typedef enum {
940Sstevel@tonic-gate     OPT_ABSENT = 0,	/* No option */
950Sstevel@tonic-gate     OPT_REQ,		/* option required */
960Sstevel@tonic-gate     OPT_OPTIONAL	/* option may or may not be present */
970Sstevel@tonic-gate } option_t;
980Sstevel@tonic-gate 
990Sstevel@tonic-gate typedef struct {
1000Sstevel@tonic-gate 	char	*subcmd;
1010Sstevel@tonic-gate 	option_t option;
1020Sstevel@tonic-gate 	error_t (*handler)();
1032115Svikram 	int	unpriv;			/* is this an unprivileged command */
1040Sstevel@tonic-gate } subcmd_defn_t;
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate #define	LINE_INIT	0	/* lineNum initial value */
1070Sstevel@tonic-gate #define	ENTRY_INIT	-1	/* entryNum initial value */
1080Sstevel@tonic-gate #define	ALL_ENTRIES	-2	/* selects all boot entries */
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate #define	GRUB_DIR		"/boot/grub"
1116694Svikram #define	GRUB_STAGE2		GRUB_DIR "/stage2"
1120Sstevel@tonic-gate #define	GRUB_MENU		"/boot/grub/menu.lst"
1130Sstevel@tonic-gate #define	MENU_TMP		"/boot/grub/menu.lst.tmp"
1146694Svikram #define	GRUB_BACKUP_MENU	"/etc/lu/GRUB_backup_menu"
1150Sstevel@tonic-gate #define	RAMDISK_SPECIAL		"/ramdisk"
116621Svikram #define	STUBBOOT		"/stubboot"
1176448Svikram #define	MULTIBOOT		"/platform/i86pc/multiboot"
1186448Svikram #define	GRUBSIGN_DIR		"/boot/grub/bootsign"
1196448Svikram #define	GRUBSIGN_BACKUP		"/etc/bootsign"
1206448Svikram #define	GRUBSIGN_UFS_PREFIX	"rootfs"
1216448Svikram #define	GRUBSIGN_ZFS_PREFIX	"pool_"
1226448Svikram #define	GRUBSIGN_LU_PREFIX	"BE_"
1236448Svikram #define	UFS_SIGNATURE_LIST	"/var/run/grub_ufs_signatures"
1246448Svikram #define	ZFS_LEGACY_MNTPT	"/tmp/bootadm_mnt_zfs_legacy"
1256448Svikram 
1266448Svikram #define	BOOTADM_RDONLY_TEST	"BOOTADM_RDONLY_TEST"
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate /* lock related */
1290Sstevel@tonic-gate #define	BAM_LOCK_FILE		"/var/run/bootadm.lock"
1300Sstevel@tonic-gate #define	LOCK_FILE_PERMS		(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
1310Sstevel@tonic-gate 
1325648Ssetje #define	CREATE_RAMDISK		"boot/solaris/bin/create_ramdisk"
1335648Ssetje #define	CREATE_DISKMAP		"boot/solaris/bin/create_diskmap"
1345648Ssetje #define	EXTRACT_BOOT_FILELIST	"boot/solaris/bin/extract_boot_filelist"
1350Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
1360Sstevel@tonic-gate 
137316Svikram #define	GRUB_slice		"/etc/lu/GRUB_slice"
138316Svikram #define	GRUB_root		"/etc/lu/GRUB_root"
1391746Svikram #define	GRUB_fdisk		"/etc/lu/GRUB_fdisk"
1401746Svikram #define	GRUB_fdisk_target	"/etc/lu/GRUB_fdisk_target"
1416694Svikram #define	FINDROOT_INSTALLGRUB	"/etc/lu/installgrub.findroot"
1426694Svikram #define	LULIB			"/usr/lib/lu/lulib"
1436694Svikram #define	LULIB_PROPAGATE_FILE	"lulib_propagate_file"
1446694Svikram #define	CKSUM			"/usr/bin/cksum"
1456694Svikram #define	LU_MENU_CKSUM		"/etc/lu/menu.cksum"
1466694Svikram #define	BOOTADM			"/sbin/bootadm"
147316Svikram 
148316Svikram #define	INSTALLGRUB		"/sbin/installgrub"
149316Svikram #define	STAGE1			"/boot/grub/stage1"
150316Svikram #define	STAGE2			"/boot/grub/stage2"
151316Svikram 
1526448Svikram typedef enum zfs_mnted {
1536448Svikram 	ZFS_MNT_ERROR = -1,
1546448Svikram 	LEGACY_MOUNTED = 1,
1556448Svikram 	LEGACY_ALREADY,
1566448Svikram 	ZFS_MOUNTED,
1576448Svikram 	ZFS_ALREADY
1586448Svikram } zfs_mnted_t;
1596448Svikram 
1600Sstevel@tonic-gate /*
1614493Snadkarni  * The following two defines are used to detect and create the correct
1624493Snadkarni  * boot archive  when safemode patching is underway.  LOFS_PATCH_FILE is a
1634493Snadkarni  * contracted private interface between bootadm and the install
1644493Snadkarni  * consolidation.  It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE
1654493Snadkarni  * is applied.
1664493Snadkarni  */
1674493Snadkarni #define	LOFS_PATCH_FILE		"/var/run/.patch_loopback_mode"
1684493Snadkarni #define	LOFS_PATCH_MNT		"/var/run/.patch_root_loopbackmnt"
1694493Snadkarni 
1704493Snadkarni /*
1710Sstevel@tonic-gate  * Default file attributes
1720Sstevel@tonic-gate  */
1730Sstevel@tonic-gate #define	DEFAULT_DEV_MODE	0644	/* default permissions */
1740Sstevel@tonic-gate #define	DEFAULT_DEV_UID		0	/* user root */
1750Sstevel@tonic-gate #define	DEFAULT_DEV_GID		3	/* group sys */
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate /*
1780Sstevel@tonic-gate  * Menu related
1790Sstevel@tonic-gate  * menu_cmd_t and menu_cmds must be kept in sync
1800Sstevel@tonic-gate  */
1813446Smrj char *menu_cmds[] = {
1820Sstevel@tonic-gate 	"default",	/* DEFAULT_CMD */
1830Sstevel@tonic-gate 	"timeout",	/* TIMEOUT_CMD */
1840Sstevel@tonic-gate 	"title",	/* TITLE_CMD */
1850Sstevel@tonic-gate 	"root",		/* ROOT_CMD */
1860Sstevel@tonic-gate 	"kernel",	/* KERNEL_CMD */
1873446Smrj 	"kernel$",	/* KERNEL_DOLLAR_CMD */
1880Sstevel@tonic-gate 	"module",	/* MODULE_CMD */
1893446Smrj 	"module$",	/* MODULE_DOLLAR_CMD */
1900Sstevel@tonic-gate 	" ",		/* SEP_CMD */
1910Sstevel@tonic-gate 	"#",		/* COMMENT_CMD */
1923446Smrj 	"chainloader",	/* CHAINLOADER_CMD */
1933446Smrj 	"args",		/* ARGS_CMD */
1946448Svikram 	"findroot",	/* FINDROOT_CMD */
1950Sstevel@tonic-gate 	NULL
1960Sstevel@tonic-gate };
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate #define	OPT_ENTRY_NUM	"entry"
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate /*
2016448Svikram  * exec_cmd related
2020Sstevel@tonic-gate  */
2030Sstevel@tonic-gate typedef struct {
2040Sstevel@tonic-gate 	line_t *head;
2050Sstevel@tonic-gate 	line_t *tail;
2060Sstevel@tonic-gate } filelist_t;
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate #define	BOOT_FILE_LIST	"boot/solaris/filelist.ramdisk"
2090Sstevel@tonic-gate #define	ETC_FILE_LIST	"etc/boot/solaris/filelist.ramdisk"
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate #define	FILE_STAT	"boot/solaris/filestat.ramdisk"
2120Sstevel@tonic-gate #define	FILE_STAT_TMP	"boot/solaris/filestat.ramdisk.tmp"
2130Sstevel@tonic-gate #define	DIR_PERMS	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
2140Sstevel@tonic-gate #define	FILE_STAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate /* Globals */
2173446Smrj int bam_verbose;
2183446Smrj int bam_force;
2196448Svikram int bam_debug;
2200Sstevel@tonic-gate static char *prog;
2210Sstevel@tonic-gate static subcmd_t bam_cmd;
2220Sstevel@tonic-gate static char *bam_root;
2230Sstevel@tonic-gate static int bam_rootlen;
2240Sstevel@tonic-gate static int bam_root_readonly;
225621Svikram static int bam_alt_root;
2268735SEnrico.Perla@Sun.COM static int bam_extend = 0;
2278735SEnrico.Perla@Sun.COM static int bam_purge = 0;
2280Sstevel@tonic-gate static char *bam_subcmd;
2290Sstevel@tonic-gate static char *bam_opt;
2300Sstevel@tonic-gate static char **bam_argv;
2310Sstevel@tonic-gate static int bam_argc;
2320Sstevel@tonic-gate static int bam_check;
2330Sstevel@tonic-gate static int bam_smf_check;
2340Sstevel@tonic-gate static int bam_lock_fd = -1;
2356423Sgw25295 static int bam_zfs;
2360Sstevel@tonic-gate static char rootbuf[PATH_MAX] = "/";
237316Svikram static int bam_update_all;
2386319Sjg static int bam_alt_platform;
2396319Sjg static char *bam_platform;
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate /* function prototypes */
2425648Ssetje static void parse_args_internal(int, char *[]);
2435648Ssetje static void parse_args(int, char *argv[]);
2445648Ssetje static error_t bam_menu(char *, char *, int, char *[]);
2455648Ssetje static error_t bam_archive(char *, char *);
2465648Ssetje 
2475648Ssetje static void bam_exit(int);
2480Sstevel@tonic-gate static void bam_lock(void);
2490Sstevel@tonic-gate static void bam_unlock(void);
2500Sstevel@tonic-gate 
2515648Ssetje static int exec_cmd(char *, filelist_t *);
2525648Ssetje static error_t read_globals(menu_t *, char *, char *, int);
2536448Svikram static int menu_on_bootdisk(char *os_root, char *menu_root);
2545648Ssetje static menu_t *menu_read(char *);
2555648Ssetje static error_t menu_write(char *, menu_t *);
2565648Ssetje static void linelist_free(line_t *);
2575648Ssetje static void menu_free(menu_t *);
2585648Ssetje static void filelist_free(filelist_t *);
2595648Ssetje static error_t list2file(char *, char *, char *, line_t *);
2605648Ssetje static error_t list_entry(menu_t *, char *, char *);
2615648Ssetje static error_t delete_all_entries(menu_t *, char *, char *);
2626448Svikram static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
2636448Svikram static error_t update_temp(menu_t *mp, char *dummy, char *opt);
2645648Ssetje 
2655648Ssetje static error_t update_archive(char *, char *);
2665648Ssetje static error_t list_archive(char *, char *);
2675648Ssetje static error_t update_all(char *, char *);
2685648Ssetje static error_t read_list(char *, filelist_t *);
2695648Ssetje static error_t set_global(menu_t *, char *, int);
2705648Ssetje static error_t set_option(menu_t *, char *, char *);
2715648Ssetje static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
2726448Svikram static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
2735648Ssetje static char *expand_path(const char *);
2745648Ssetje 
2755648Ssetje static long s_strtol(char *);
2765648Ssetje static int s_fputs(char *, FILE *);
2775648Ssetje 
2786448Svikram static int is_zfs(char *root);
2796448Svikram static int is_ufs(char *root);
2806448Svikram static int is_pcfs(char *root);
2810Sstevel@tonic-gate static int is_amd64(void);
2826582Ssetje static char *get_machine(void);
2830Sstevel@tonic-gate static void append_to_flist(filelist_t *, char *);
2846448Svikram static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted);
2856448Svikram static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt);
2866448Svikram static int ufs_add_to_sign_list(char *sign);
2876694Svikram static error_t synchronize_BE_menu(void);
2880Sstevel@tonic-gate 
2895648Ssetje #if !defined(_OPB)
2904581Ssherrym static void ucode_install();
2914581Ssherrym #endif
2924581Ssherrym 
2930Sstevel@tonic-gate /* Menu related sub commands */
2940Sstevel@tonic-gate static subcmd_defn_t menu_subcmds[] = {
2956448Svikram 	"set_option",		OPT_ABSENT,	set_option, 0,	/* PUB */
2962115Svikram 	"list_entry",		OPT_OPTIONAL,	list_entry, 1,	/* PUB */
2972115Svikram 	"delete_all_entries",	OPT_ABSENT,	delete_all_entries, 0, /* PVT */
2982115Svikram 	"update_entry",		OPT_REQ,	update_entry, 0, /* menu */
2992115Svikram 	"update_temp",		OPT_OPTIONAL,	update_temp, 0,	/* reboot */
3003446Smrj 	"upgrade",		OPT_ABSENT,	upgrade_menu, 0, /* menu */
3012115Svikram 	NULL,			0,		NULL, 0	/* must be last */
3020Sstevel@tonic-gate };
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate /* Archive related sub commands */
3050Sstevel@tonic-gate static subcmd_defn_t arch_subcmds[] = {
3062115Svikram 	"update",		OPT_ABSENT,	update_archive, 0, /* PUB */
3072115Svikram 	"update_all",		OPT_ABSENT,	update_all, 0,	/* PVT */
3082115Svikram 	"list",			OPT_OPTIONAL,	list_archive, 1, /* PUB */
3092115Svikram 	NULL,			0,		NULL, 0	/* must be last */
3100Sstevel@tonic-gate };
3110Sstevel@tonic-gate 
3128735SEnrico.Perla@Sun.COM enum dircache_copy_opt {
3138735SEnrico.Perla@Sun.COM 	FILE32 = 0,
3148735SEnrico.Perla@Sun.COM 	FILE64,
3158735SEnrico.Perla@Sun.COM 	CACHEDIR_NUM
3168735SEnrico.Perla@Sun.COM };
3178735SEnrico.Perla@Sun.COM 
3188735SEnrico.Perla@Sun.COM /*
3198735SEnrico.Perla@Sun.COM  * Directory specific flags:
3208735SEnrico.Perla@Sun.COM  * NEED_UPDATE : the specified archive needs to be updated
3218735SEnrico.Perla@Sun.COM  * NO_MULTI : don't extend the specified archive, but recreate it
3228735SEnrico.Perla@Sun.COM  */
3238735SEnrico.Perla@Sun.COM #define	NEED_UPDATE		0x00000001
3248735SEnrico.Perla@Sun.COM #define	NO_MULTI		0x00000002
3258735SEnrico.Perla@Sun.COM 
3268735SEnrico.Perla@Sun.COM #define	set_dir_flag(id, f)	(walk_arg.dirinfo[id].flags |= f)
3278735SEnrico.Perla@Sun.COM #define	unset_dir_flag(id, f)	(walk_arg.dirinfo[id].flags &= ~f)
3288735SEnrico.Perla@Sun.COM #define	is_dir_flag_on(id, f)	(walk_arg.dirinfo[id].flags & f ? 1 : 0)
3298735SEnrico.Perla@Sun.COM 
3308735SEnrico.Perla@Sun.COM #define	get_cachedir(id)	(walk_arg.dirinfo[id].cdir_path)
3318735SEnrico.Perla@Sun.COM #define	get_updatedir(id)	(walk_arg.dirinfo[id].update_path)
3328735SEnrico.Perla@Sun.COM #define	get_count(id)		(walk_arg.dirinfo[id].count)
3338735SEnrico.Perla@Sun.COM #define	has_cachedir(id)	(walk_arg.dirinfo[id].has_dir)
3348735SEnrico.Perla@Sun.COM #define	set_dir_present(id)	(walk_arg.dirinfo[id].has_dir = 1)
3358735SEnrico.Perla@Sun.COM 
3368735SEnrico.Perla@Sun.COM /*
3378735SEnrico.Perla@Sun.COM  * dirinfo_t (specific cache directory information):
3388735SEnrico.Perla@Sun.COM  * cdir_path:   path to the archive cache directory
3398735SEnrico.Perla@Sun.COM  * update_path: path to the update directory (contains the files that will be
3408735SEnrico.Perla@Sun.COM  *              used to extend the archive)
3418735SEnrico.Perla@Sun.COM  * has_dir:	the specified cache directory is active
3428735SEnrico.Perla@Sun.COM  * count:	the number of files to update
3438735SEnrico.Perla@Sun.COM  * flags:	directory specific flags
3448735SEnrico.Perla@Sun.COM  */
3458735SEnrico.Perla@Sun.COM typedef struct _dirinfo {
3468735SEnrico.Perla@Sun.COM 	char	cdir_path[PATH_MAX];
3478735SEnrico.Perla@Sun.COM 	char	update_path[PATH_MAX];
3488735SEnrico.Perla@Sun.COM 	int	has_dir;
3498735SEnrico.Perla@Sun.COM 	int	count;
3508735SEnrico.Perla@Sun.COM 	int	flags;
3518735SEnrico.Perla@Sun.COM } dirinfo_t;
3528735SEnrico.Perla@Sun.COM 
3538735SEnrico.Perla@Sun.COM /*
3548735SEnrico.Perla@Sun.COM  * Update flags:
3558735SEnrico.Perla@Sun.COM  * NEED_CACHE_DIR : cache directory is missing and needs to be created
3568735SEnrico.Perla@Sun.COM  * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
3578735SEnrico.Perla@Sun.COM  * UPDATE_ERROR : an error occourred while traversing the list of files
3588735SEnrico.Perla@Sun.COM  * RDONLY_FSCHK : the target filesystem is read-only
3598735SEnrico.Perla@Sun.COM  * RAMDSK_FSCHK : the target filesystem is on a ramdisk
3608735SEnrico.Perla@Sun.COM  */
3618735SEnrico.Perla@Sun.COM #define	NEED_CACHE_DIR		0x00000001
3628735SEnrico.Perla@Sun.COM #define	IS_SPARC_TARGET		0x00000002
3638735SEnrico.Perla@Sun.COM #define	UPDATE_ERROR		0x00000004
3648735SEnrico.Perla@Sun.COM #define	RDONLY_FSCHK		0x00000008
3658735SEnrico.Perla@Sun.COM #define	RAMDSK_FSCHK		0x00000010
3668735SEnrico.Perla@Sun.COM 
3678735SEnrico.Perla@Sun.COM #define	is_flag_on(flag)	(walk_arg.update_flags & flag ? 1 : 0)
3688735SEnrico.Perla@Sun.COM #define	set_flag(flag)		(walk_arg.update_flags |= flag)
3698735SEnrico.Perla@Sun.COM #define	unset_flag(flag)	(walk_arg.update_flags &= ~flag)
3708735SEnrico.Perla@Sun.COM 
3718735SEnrico.Perla@Sun.COM /*
3728735SEnrico.Perla@Sun.COM  * struct walk_arg :
3738735SEnrico.Perla@Sun.COM  * update_flags: flags related to the current updating process
3748735SEnrico.Perla@Sun.COM  * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
3758735SEnrico.Perla@Sun.COM  * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
3768735SEnrico.Perla@Sun.COM  */
3770Sstevel@tonic-gate static struct {
3788735SEnrico.Perla@Sun.COM 	int 		update_flags;
3798735SEnrico.Perla@Sun.COM 	nvlist_t 	*new_nvlp;
3808735SEnrico.Perla@Sun.COM 	nvlist_t 	*old_nvlp;
3818735SEnrico.Perla@Sun.COM 	FILE 		*sparcfile;
3828735SEnrico.Perla@Sun.COM 	dirinfo_t	dirinfo[CACHEDIR_NUM];
3830Sstevel@tonic-gate } walk_arg;
3840Sstevel@tonic-gate 
3852345Ssetje struct safefile {
3862334Ssetje 	char *name;
3872334Ssetje 	struct safefile *next;
3882334Ssetje };
3892334Ssetje 
3903446Smrj static struct safefile *safefiles = NULL;
3912334Ssetje #define	NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
3922334Ssetje 
3938735SEnrico.Perla@Sun.COM /* Thanks growisofs */
3948735SEnrico.Perla@Sun.COM #define	CD_BLOCK	((off64_t)2048)
3958735SEnrico.Perla@Sun.COM #define	VOLDESC_OFF	16
3968735SEnrico.Perla@Sun.COM #define	DVD_BLOCK	(32*1024)
3978735SEnrico.Perla@Sun.COM #define	MAX_IVDs	16
3988735SEnrico.Perla@Sun.COM 
3998735SEnrico.Perla@Sun.COM struct iso_pdesc {
4008735SEnrico.Perla@Sun.COM     unsigned char type	[1];
4018735SEnrico.Perla@Sun.COM     unsigned char id	[5];
4028735SEnrico.Perla@Sun.COM     unsigned char void1	[80-5-1];
4038735SEnrico.Perla@Sun.COM     unsigned char volume_space_size [8];
4048735SEnrico.Perla@Sun.COM     unsigned char void2	[2048-80-8];
4058735SEnrico.Perla@Sun.COM };
4068735SEnrico.Perla@Sun.COM 
4078735SEnrico.Perla@Sun.COM /*
4088735SEnrico.Perla@Sun.COM  * COUNT_MAX:	maximum number of changed files to justify a multisession update
4098735SEnrico.Perla@Sun.COM  * BA_SIZE_MAX:	maximum size of the boot_archive to justify a multisession
4108735SEnrico.Perla@Sun.COM  * 		update
4118735SEnrico.Perla@Sun.COM  */
4128735SEnrico.Perla@Sun.COM #define	COUNT_MAX		50
4138735SEnrico.Perla@Sun.COM #define	BA_SIZE_MAX		(50 * 1024 * 1024)
4148735SEnrico.Perla@Sun.COM 
4158735SEnrico.Perla@Sun.COM #define	bam_nowrite()		(bam_check || bam_smf_check)
4168735SEnrico.Perla@Sun.COM 
4177656SSherry.Moore@Sun.COM static int sync_menu = 1;	/* whether we need to sync the BE menus */
4187656SSherry.Moore@Sun.COM 
4190Sstevel@tonic-gate static void
4200Sstevel@tonic-gate usage(void)
4210Sstevel@tonic-gate {
4220Sstevel@tonic-gate 	(void) fprintf(stderr, "USAGE:\n");
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	/* archive usage */
4256319Sjg 	(void) fprintf(stderr,
4266319Sjg 	    "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog);
4276319Sjg 	(void) fprintf(stderr,
4286319Sjg 	    "\t%s list-archive [-R altroot [-p platform>]]\n", prog);
4295648Ssetje #if !defined(_OPB)
4300Sstevel@tonic-gate 	/* x86 only */
4310Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
4320Sstevel@tonic-gate 	(void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
4330Sstevel@tonic-gate #endif
4340Sstevel@tonic-gate }
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate int
4370Sstevel@tonic-gate main(int argc, char *argv[])
4380Sstevel@tonic-gate {
4390Sstevel@tonic-gate 	error_t ret;
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4420Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
4450Sstevel@tonic-gate 		prog = argv[0];
4460Sstevel@tonic-gate 	} else {
4470Sstevel@tonic-gate 		prog++;
4480Sstevel@tonic-gate 	}
4490Sstevel@tonic-gate 
4506448Svikram 	INJECT_ERROR1("ASSERT_ON", assert(0))
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	/*
4530Sstevel@tonic-gate 	 * Don't depend on caller's umask
4540Sstevel@tonic-gate 	 */
4550Sstevel@tonic-gate 	(void) umask(0022);
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	parse_args(argc, argv);
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	switch (bam_cmd) {
4600Sstevel@tonic-gate 		case BAM_MENU:
4610Sstevel@tonic-gate 			ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
4620Sstevel@tonic-gate 			break;
4630Sstevel@tonic-gate 		case BAM_ARCHIVE:
4640Sstevel@tonic-gate 			ret = bam_archive(bam_subcmd, bam_opt);
4650Sstevel@tonic-gate 			break;
4660Sstevel@tonic-gate 		default:
4670Sstevel@tonic-gate 			usage();
4680Sstevel@tonic-gate 			bam_exit(1);
4690Sstevel@tonic-gate 	}
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	if (ret != BAM_SUCCESS)
4720Sstevel@tonic-gate 		bam_exit(1);
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	bam_unlock();
4750Sstevel@tonic-gate 	return (0);
4760Sstevel@tonic-gate }
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate /*
4790Sstevel@tonic-gate  * Equivalence of public and internal commands:
4800Sstevel@tonic-gate  *	update-archive  -- -a update
4810Sstevel@tonic-gate  *	list-archive	-- -a list
4820Sstevel@tonic-gate  *	set-menu	-- -m set_option
4830Sstevel@tonic-gate  *	list-menu	-- -m list_entry
4840Sstevel@tonic-gate  *	update-menu	-- -m update_entry
4850Sstevel@tonic-gate  */
4860Sstevel@tonic-gate static struct cmd_map {
4870Sstevel@tonic-gate 	char *bam_cmdname;
4880Sstevel@tonic-gate 	int bam_cmd;
4890Sstevel@tonic-gate 	char *bam_subcmd;
4900Sstevel@tonic-gate } cmd_map[] = {
4910Sstevel@tonic-gate 	{ "update-archive",	BAM_ARCHIVE,	"update"},
4920Sstevel@tonic-gate 	{ "list-archive",	BAM_ARCHIVE,	"list"},
4930Sstevel@tonic-gate 	{ "set-menu",		BAM_MENU,	"set_option"},
4940Sstevel@tonic-gate 	{ "list-menu",		BAM_MENU,	"list_entry"},
4950Sstevel@tonic-gate 	{ "update-menu",	BAM_MENU,	"update_entry"},
4960Sstevel@tonic-gate 	{ NULL,			0,		NULL}
4970Sstevel@tonic-gate };
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate /*
5000Sstevel@tonic-gate  * Commands syntax published in bootadm(1M) are parsed here
5010Sstevel@tonic-gate  */
5020Sstevel@tonic-gate static void
5030Sstevel@tonic-gate parse_args(int argc, char *argv[])
5040Sstevel@tonic-gate {
5050Sstevel@tonic-gate 	struct cmd_map *cmp = cmd_map;
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	/* command conforming to the final spec */
5080Sstevel@tonic-gate 	if (argc > 1 && argv[1][0] != '-') {
5090Sstevel@tonic-gate 		/*
5100Sstevel@tonic-gate 		 * Map commands to internal table.
5110Sstevel@tonic-gate 		 */
5120Sstevel@tonic-gate 		while (cmp->bam_cmdname) {
5130Sstevel@tonic-gate 			if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
5140Sstevel@tonic-gate 				bam_cmd = cmp->bam_cmd;
5150Sstevel@tonic-gate 				bam_subcmd = cmp->bam_subcmd;
5160Sstevel@tonic-gate 				break;
5170Sstevel@tonic-gate 			}
5180Sstevel@tonic-gate 			cmp++;
5190Sstevel@tonic-gate 		}
5200Sstevel@tonic-gate 		if (cmp->bam_cmdname == NULL) {
5210Sstevel@tonic-gate 			usage();
5220Sstevel@tonic-gate 			bam_exit(1);
5230Sstevel@tonic-gate 		}
5240Sstevel@tonic-gate 		argc--;
5250Sstevel@tonic-gate 		argv++;
5260Sstevel@tonic-gate 	}
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	parse_args_internal(argc, argv);
5290Sstevel@tonic-gate }
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate /*
5320Sstevel@tonic-gate  * A combination of public and private commands are parsed here.
5330Sstevel@tonic-gate  * The internal syntax and the corresponding functionality are:
5340Sstevel@tonic-gate  *	-a update	-- update-archive
5350Sstevel@tonic-gate  *	-a list		-- list-archive
5360Sstevel@tonic-gate  *	-a update-all	-- (reboot to sync all mounted OS archive)
5370Sstevel@tonic-gate  *	-m update_entry	-- update-menu
5380Sstevel@tonic-gate  *	-m list_entry	-- list-menu
5390Sstevel@tonic-gate  *	-m update_temp	-- (reboot -- [boot-args])
5400Sstevel@tonic-gate  *	-m delete_all_entries -- (called from install)
5418735SEnrico.Perla@Sun.COM  * A set of private flags is there too:
5428735SEnrico.Perla@Sun.COM  *	-F		-- purge the cache directories and rebuild them
5438735SEnrico.Perla@Sun.COM  *	-e		-- use the (faster) archive update approach (used by
5448735SEnrico.Perla@Sun.COM  *			   reboot)
5450Sstevel@tonic-gate  */
5460Sstevel@tonic-gate static void
5470Sstevel@tonic-gate parse_args_internal(int argc, char *argv[])
5480Sstevel@tonic-gate {
5490Sstevel@tonic-gate 	int c, error;
5500Sstevel@tonic-gate 	extern char *optarg;
5510Sstevel@tonic-gate 	extern int optind, opterr;
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	/* Suppress error message from getopt */
5540Sstevel@tonic-gate 	opterr = 0;
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	error = 0;
5578735SEnrico.Perla@Sun.COM 	while ((c = getopt(argc, argv, "a:d:fm:no:veFCR:p:Z")) != -1) {
5580Sstevel@tonic-gate 		switch (c) {
5590Sstevel@tonic-gate 		case 'a':
5600Sstevel@tonic-gate 			if (bam_cmd) {
5610Sstevel@tonic-gate 				error = 1;
5620Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
5630Sstevel@tonic-gate 			}
5640Sstevel@tonic-gate 			bam_cmd = BAM_ARCHIVE;
5650Sstevel@tonic-gate 			bam_subcmd = optarg;
5660Sstevel@tonic-gate 			break;
5670Sstevel@tonic-gate 		case 'd':
5680Sstevel@tonic-gate 			if (bam_debug) {
5690Sstevel@tonic-gate 				error = 1;
5700Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
5710Sstevel@tonic-gate 			}
5720Sstevel@tonic-gate 			bam_debug = s_strtol(optarg);
5730Sstevel@tonic-gate 			break;
5740Sstevel@tonic-gate 		case 'f':
5750Sstevel@tonic-gate 			bam_force = 1;
5760Sstevel@tonic-gate 			break;
5778735SEnrico.Perla@Sun.COM 		case 'F':
5788735SEnrico.Perla@Sun.COM 			bam_purge = 1;
5798735SEnrico.Perla@Sun.COM 			break;
5800Sstevel@tonic-gate 		case 'm':
5810Sstevel@tonic-gate 			if (bam_cmd) {
5820Sstevel@tonic-gate 				error = 1;
5830Sstevel@tonic-gate 				bam_error(MULT_CMDS, c);
5840Sstevel@tonic-gate 			}
5850Sstevel@tonic-gate 			bam_cmd = BAM_MENU;
5860Sstevel@tonic-gate 			bam_subcmd = optarg;
5870Sstevel@tonic-gate 			break;
5880Sstevel@tonic-gate 		case 'n':
5890Sstevel@tonic-gate 			bam_check = 1;
5900Sstevel@tonic-gate 			break;
5910Sstevel@tonic-gate 		case 'o':
5920Sstevel@tonic-gate 			if (bam_opt) {
5930Sstevel@tonic-gate 				error = 1;
5940Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
5950Sstevel@tonic-gate 			}
5960Sstevel@tonic-gate 			bam_opt = optarg;
5970Sstevel@tonic-gate 			break;
5980Sstevel@tonic-gate 		case 'v':
5990Sstevel@tonic-gate 			bam_verbose = 1;
6000Sstevel@tonic-gate 			break;
601662Sszhou 		case 'C':
602662Sszhou 			bam_smf_check = 1;
603662Sszhou 			break;
6040Sstevel@tonic-gate 		case 'R':
6050Sstevel@tonic-gate 			if (bam_root) {
6060Sstevel@tonic-gate 				error = 1;
6070Sstevel@tonic-gate 				bam_error(DUP_OPT, c);
6080Sstevel@tonic-gate 				break;
6090Sstevel@tonic-gate 			} else if (realpath(optarg, rootbuf) == NULL) {
6100Sstevel@tonic-gate 				error = 1;
6110Sstevel@tonic-gate 				bam_error(CANT_RESOLVE, optarg,
6120Sstevel@tonic-gate 				    strerror(errno));
6130Sstevel@tonic-gate 				break;
6140Sstevel@tonic-gate 			}
615621Svikram 			bam_alt_root = 1;
6160Sstevel@tonic-gate 			bam_root = rootbuf;
6170Sstevel@tonic-gate 			bam_rootlen = strlen(rootbuf);
6180Sstevel@tonic-gate 			break;
6196319Sjg 		case 'p':
6206319Sjg 			bam_alt_platform = 1;
6216319Sjg 			bam_platform = optarg;
6226319Sjg 			if ((strcmp(bam_platform, "i86pc") != 0) &&
6236319Sjg 			    (strcmp(bam_platform, "sun4u") != 0) &&
6246319Sjg 			    (strcmp(bam_platform, "sun4v") != 0)) {
6256319Sjg 				error = 1;
6266319Sjg 				bam_error(INVALID_PLAT, bam_platform);
6276319Sjg 			}
6286319Sjg 			break;
6296423Sgw25295 		case 'Z':
6306423Sgw25295 			bam_zfs = 1;
6316423Sgw25295 			break;
6328735SEnrico.Perla@Sun.COM 		case 'e':
6338735SEnrico.Perla@Sun.COM 			bam_extend = 1;
6348735SEnrico.Perla@Sun.COM 			break;
6350Sstevel@tonic-gate 		case '?':
6360Sstevel@tonic-gate 			error = 1;
6370Sstevel@tonic-gate 			bam_error(BAD_OPT, optopt);
6380Sstevel@tonic-gate 			break;
6390Sstevel@tonic-gate 		default :
6400Sstevel@tonic-gate 			error = 1;
6410Sstevel@tonic-gate 			bam_error(BAD_OPT, c);
6420Sstevel@tonic-gate 			break;
6430Sstevel@tonic-gate 		}
6440Sstevel@tonic-gate 	}
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	/*
6476319Sjg 	 * An alternate platform requires an alternate root
6486319Sjg 	 */
6496319Sjg 	if (bam_alt_platform && bam_alt_root == 0) {
6506319Sjg 		usage();
6516319Sjg 		bam_exit(0);
6526319Sjg 	}
6536319Sjg 
6546319Sjg 	/*
6550Sstevel@tonic-gate 	 * A command option must be specfied
6560Sstevel@tonic-gate 	 */
6570Sstevel@tonic-gate 	if (!bam_cmd) {
6580Sstevel@tonic-gate 		if (bam_opt && strcmp(bam_opt, "all") == 0) {
6590Sstevel@tonic-gate 			usage();
6600Sstevel@tonic-gate 			bam_exit(0);
6610Sstevel@tonic-gate 		}
6620Sstevel@tonic-gate 		bam_error(NEED_CMD);
6630Sstevel@tonic-gate 		error = 1;
6640Sstevel@tonic-gate 	}
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	if (error) {
6670Sstevel@tonic-gate 		usage();
6680Sstevel@tonic-gate 		bam_exit(1);
6690Sstevel@tonic-gate 	}
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	if (optind > argc) {
6720Sstevel@tonic-gate 		bam_error(INT_ERROR, "parse_args");
6730Sstevel@tonic-gate 		bam_exit(1);
6740Sstevel@tonic-gate 	} else if (optind < argc) {
6750Sstevel@tonic-gate 		bam_argv = &argv[optind];
6760Sstevel@tonic-gate 		bam_argc = argc - optind;
6770Sstevel@tonic-gate 	}
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	/*
6800Sstevel@tonic-gate 	 * -n implies verbose mode
6810Sstevel@tonic-gate 	 */
6820Sstevel@tonic-gate 	if (bam_check)
6830Sstevel@tonic-gate 		bam_verbose = 1;
6840Sstevel@tonic-gate }
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate static error_t
6870Sstevel@tonic-gate check_subcmd_and_options(
6880Sstevel@tonic-gate 	char *subcmd,
6890Sstevel@tonic-gate 	char *opt,
6900Sstevel@tonic-gate 	subcmd_defn_t *table,
6910Sstevel@tonic-gate 	error_t (**fp)())
6920Sstevel@tonic-gate {
6930Sstevel@tonic-gate 	int i;
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 	if (subcmd == NULL) {
6960Sstevel@tonic-gate 		bam_error(NEED_SUBCMD);
6970Sstevel@tonic-gate 		return (BAM_ERROR);
6980Sstevel@tonic-gate 	}
6990Sstevel@tonic-gate 
7006448Svikram 	if (strcmp(subcmd, "set_option") == 0) {
7016448Svikram 		if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
7026448Svikram 			bam_error(MISSING_ARG);
7036448Svikram 			usage();
7046448Svikram 			return (BAM_ERROR);
7056448Svikram 		} else if (bam_argc > 1 || bam_argv[1] != NULL) {
7062115Svikram 			bam_error(TRAILING_ARGS);
7072115Svikram 			usage();
7082115Svikram 			return (BAM_ERROR);
7092115Svikram 		}
7107656SSherry.Moore@Sun.COM 	} else if (strcmp(subcmd, "update_all") == 0) {
7117656SSherry.Moore@Sun.COM 		/*
7127656SSherry.Moore@Sun.COM 		 * The only option we accept for the "update_all"
7137656SSherry.Moore@Sun.COM 		 * subcmd is "fastboot".
7147656SSherry.Moore@Sun.COM 		 */
7157656SSherry.Moore@Sun.COM 		if (bam_argc > 1 || (bam_argc == 1 &&
7167656SSherry.Moore@Sun.COM 		    strcmp(bam_argv[0], "fastboot") != 0)) {
7177656SSherry.Moore@Sun.COM 			bam_error(TRAILING_ARGS);
7187656SSherry.Moore@Sun.COM 			usage();
7197656SSherry.Moore@Sun.COM 			return (BAM_ERROR);
7207656SSherry.Moore@Sun.COM 		}
7217656SSherry.Moore@Sun.COM 		if (bam_argc == 1)
7227656SSherry.Moore@Sun.COM 			sync_menu = 0;
7236448Svikram 	} else if (bam_argc || bam_argv) {
7246448Svikram 		bam_error(TRAILING_ARGS);
7256448Svikram 		usage();
7266448Svikram 		return (BAM_ERROR);
7272115Svikram 	}
7282115Svikram 
7290Sstevel@tonic-gate 	if (bam_root == NULL) {
7300Sstevel@tonic-gate 		bam_root = rootbuf;
7310Sstevel@tonic-gate 		bam_rootlen = 1;
7320Sstevel@tonic-gate 	}
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	/* verify that subcmd is valid */
7350Sstevel@tonic-gate 	for (i = 0; table[i].subcmd != NULL; i++) {
7360Sstevel@tonic-gate 		if (strcmp(table[i].subcmd, subcmd) == 0)
7370Sstevel@tonic-gate 			break;
7380Sstevel@tonic-gate 	}
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	if (table[i].subcmd == NULL) {
7410Sstevel@tonic-gate 		bam_error(INVALID_SUBCMD, subcmd);
7420Sstevel@tonic-gate 		return (BAM_ERROR);
7430Sstevel@tonic-gate 	}
7440Sstevel@tonic-gate 
7452115Svikram 	if (table[i].unpriv == 0 && geteuid() != 0) {
7462115Svikram 		bam_error(MUST_BE_ROOT);
7472115Svikram 		return (BAM_ERROR);
7482115Svikram 	}
7492115Svikram 
7502115Svikram 	/*
7512115Svikram 	 * Currently only privileged commands need a lock
7522115Svikram 	 */
7532115Svikram 	if (table[i].unpriv == 0)
7542115Svikram 		bam_lock();
7552115Svikram 
7560Sstevel@tonic-gate 	/* subcmd verifies that opt is appropriate */
7570Sstevel@tonic-gate 	if (table[i].option != OPT_OPTIONAL) {
7580Sstevel@tonic-gate 		if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
7590Sstevel@tonic-gate 			if (opt)
7600Sstevel@tonic-gate 				bam_error(NO_OPT_REQ, subcmd);
7610Sstevel@tonic-gate 			else
7620Sstevel@tonic-gate 				bam_error(MISS_OPT, subcmd);
7630Sstevel@tonic-gate 			return (BAM_ERROR);
7640Sstevel@tonic-gate 		}
7650Sstevel@tonic-gate 	}
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	*fp = table[i].handler;
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	return (BAM_SUCCESS);
7700Sstevel@tonic-gate }
7710Sstevel@tonic-gate 
772621Svikram /*
773621Svikram  * NOTE: A single "/" is also considered a trailing slash and will
774621Svikram  * be deleted.
775621Svikram  */
776621Svikram static void
777621Svikram elide_trailing_slash(const char *src, char *dst, size_t dstsize)
778621Svikram {
779621Svikram 	size_t dstlen;
780621Svikram 
781621Svikram 	assert(src);
782621Svikram 	assert(dst);
783621Svikram 
784621Svikram 	(void) strlcpy(dst, src, dstsize);
785621Svikram 
786621Svikram 	dstlen = strlen(dst);
787621Svikram 	if (dst[dstlen - 1] == '/') {
788621Svikram 		dst[dstlen - 1] = '\0';
789621Svikram 	}
790621Svikram }
791621Svikram 
7920Sstevel@tonic-gate static error_t
7930Sstevel@tonic-gate bam_menu(char *subcmd, char *opt, int largc, char *largv[])
7940Sstevel@tonic-gate {
7956448Svikram 	error_t			ret;
7966448Svikram 	char			menu_path[PATH_MAX];
7976448Svikram 	char			clean_menu_root[PATH_MAX];
7986448Svikram 	char			path[PATH_MAX];
7996448Svikram 	menu_t			*menu;
8006448Svikram 	char			menu_root[PATH_MAX];
8016448Svikram 	struct stat		sb;
8020Sstevel@tonic-gate 	error_t (*f)(menu_t *mp, char *menu_path, char *opt);
8036448Svikram 	char			*special;
8046448Svikram 	char			*pool = NULL;
8056448Svikram 	zfs_mnted_t		zmnted;
8066448Svikram 	char			*zmntpt;
8076448Svikram 	char			*osdev;
8086448Svikram 	char			*osroot;
8096448Svikram 	const char		*fcn = "bam_menu()";
8105648Ssetje 
8115648Ssetje 	/*
8125648Ssetje 	 * Menu sub-command only applies to GRUB (i.e. x86)
8135648Ssetje 	 */
8146448Svikram 	if (!is_grub(bam_alt_root ? bam_root : "/")) {
8156448Svikram 		bam_error(NOT_GRUB_BOOT);
8165648Ssetje 		return (BAM_ERROR);
8175648Ssetje 	}
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	/*
8200Sstevel@tonic-gate 	 * Check arguments
8210Sstevel@tonic-gate 	 */
8220Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
8230Sstevel@tonic-gate 	if (ret == BAM_ERROR) {
8240Sstevel@tonic-gate 		return (BAM_ERROR);
8250Sstevel@tonic-gate 	}
8260Sstevel@tonic-gate 
8276448Svikram 	assert(bam_root);
8286448Svikram 
8296448Svikram 	(void) strlcpy(menu_root, bam_root, sizeof (menu_root));
8306448Svikram 	osdev = osroot = NULL;
8316448Svikram 
8326448Svikram 	if (strcmp(subcmd, "update_entry") == 0) {
8336448Svikram 		assert(opt);
8346448Svikram 
8356448Svikram 		osdev = strtok(opt, ",");
8366448Svikram 		assert(osdev);
8376448Svikram 		osroot = strtok(NULL, ",");
8386448Svikram 		if (osroot) {
8396448Svikram 			/* fixup bam_root so that it points at osroot */
8406448Svikram 			if (realpath(osroot, rootbuf) == NULL) {
8416448Svikram 				bam_error(CANT_RESOLVE, osroot,
8426448Svikram 				    strerror(errno));
8436448Svikram 				return (BAM_ERROR);
8446448Svikram 			}
8456448Svikram 			bam_alt_root = 1;
8466448Svikram 			bam_root  = rootbuf;
8476448Svikram 			bam_rootlen = strlen(rootbuf);
8486448Svikram 		}
849621Svikram 	}
850621Svikram 
8516423Sgw25295 	/*
8526448Svikram 	 * We support menu on PCFS (under certain conditions), but
8536448Svikram 	 * not the OS root
8546423Sgw25295 	 */
8556448Svikram 	if (is_pcfs(bam_root)) {
8566448Svikram 		bam_error(PCFS_ROOT_NOTSUP, bam_root);
8576448Svikram 		return (BAM_ERROR);
8586448Svikram 	}
8596448Svikram 
8606448Svikram 	if (stat(menu_root, &sb) == -1) {
8616448Svikram 		bam_error(CANNOT_LOCATE_GRUB_MENU);
8626448Svikram 		return (BAM_ERROR);
8636448Svikram 	}
8646448Svikram 
8656448Svikram 	BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root));
8666423Sgw25295 
8676423Sgw25295 	/*
8686448Svikram 	 * We no longer use the GRUB slice file. If it exists, then
8696448Svikram 	 * the user is doing something that is unsupported (such as
8706448Svikram 	 * standard upgrading an old Live Upgrade BE). If that
8716448Svikram 	 * happens, mimic existing behavior i.e. pretend that it is
8726448Svikram 	 * not a BE. Emit a warning though.
8736423Sgw25295 	 */
8746448Svikram 	if (bam_alt_root) {
8756448Svikram 		(void) snprintf(path, sizeof (path), "%s%s", bam_root,
8766448Svikram 		    GRUB_slice);
8776448Svikram 	} else {
8786448Svikram 		(void) snprintf(path, sizeof (path), "%s", GRUB_slice);
8796448Svikram 	}
8806448Svikram 
8818642SVikram.Hegde@Sun.COM 	if (bam_verbose && stat(path, &sb) == 0)
8826448Svikram 		bam_error(GRUB_SLICE_FILE_EXISTS, path);
8836448Svikram 
8846448Svikram 	if (is_zfs(menu_root)) {
8856448Svikram 		assert(strcmp(menu_root, bam_root) == 0);
8866448Svikram 		special = get_special(menu_root);
8876448Svikram 		INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
8886448Svikram 		if (special == NULL) {
8896448Svikram 			bam_error(CANT_FIND_SPECIAL, menu_root);
8906448Svikram 			return (BAM_ERROR);
8916448Svikram 		}
8926448Svikram 		pool = strtok(special, "/");
8936448Svikram 		INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
8946448Svikram 		if (pool == NULL) {
8956448Svikram 			free(special);
8966448Svikram 			bam_error(CANT_FIND_POOL, menu_root);
8976448Svikram 			return (BAM_ERROR);
8986448Svikram 		}
8996448Svikram 		BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool));
9006448Svikram 
9016448Svikram 		zmntpt = mount_top_dataset(pool, &zmnted);
9026448Svikram 		INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
9036448Svikram 		if (zmntpt == NULL) {
9046448Svikram 			bam_error(CANT_MOUNT_POOL_DATASET, pool);
9056448Svikram 			free(special);
9066448Svikram 			return (BAM_ERROR);
9076448Svikram 		}
9086448Svikram 		BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt));
9096448Svikram 
9106448Svikram 		(void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
9116448Svikram 		BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root));
9126448Svikram 	}
9136448Svikram 
9146448Svikram 	elide_trailing_slash(menu_root, clean_menu_root,
9156448Svikram 	    sizeof (clean_menu_root));
9166448Svikram 
9176448Svikram 	BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root));
9186448Svikram 
9196448Svikram 	(void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
920621Svikram 	(void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
921621Svikram 
9226448Svikram 	BAM_DPRINTF((D_MENU_PATH, fcn, menu_path));
9236448Svikram 
924621Svikram 	/*
9256448Svikram 	 * If listing the menu, display the menu location
926621Svikram 	 */
927621Svikram 	if (strcmp(subcmd, "list_entry") == 0) {
9286448Svikram 		bam_print(GRUB_MENU_PATH, menu_path);
9296448Svikram 	}
9306448Svikram 
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 	menu = menu_read(menu_path);
9330Sstevel@tonic-gate 	assert(menu);
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	/*
9366448Svikram 	 * We already checked the following case in
9376448Svikram 	 * check_subcmd_and_suboptions() above. Complete the
9386448Svikram 	 * final step now.
9390Sstevel@tonic-gate 	 */
9400Sstevel@tonic-gate 	if (strcmp(subcmd, "set_option") == 0) {
9416448Svikram 		assert(largc == 1 && largv[0] && largv[1] == NULL);
9420Sstevel@tonic-gate 		opt = largv[0];
9436448Svikram 	} else {
9446448Svikram 		assert(largc == 0 && largv == NULL);
9456448Svikram 	}
9466448Svikram 
9476448Svikram 	ret = get_boot_cap(bam_root);
9486448Svikram 	if (ret != BAM_SUCCESS) {
9496448Svikram 		BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
9506448Svikram 		goto out;
9516448Svikram 	}
9523446Smrj 
9530Sstevel@tonic-gate 	/*
9540Sstevel@tonic-gate 	 * Once the sub-cmd handler has run
9550Sstevel@tonic-gate 	 * only the line field is guaranteed to have valid values
9560Sstevel@tonic-gate 	 */
9576448Svikram 	if (strcmp(subcmd, "update_entry") == 0)
9586448Svikram 		ret = f(menu, menu_root, osdev);
9596448Svikram 	else if (strcmp(subcmd, "upgrade") == 0)
9606448Svikram 		ret = f(menu, bam_root, menu_root);
9616448Svikram 	else if (strcmp(subcmd, "list_entry") == 0)
9626448Svikram 		ret = f(menu, menu_path, opt);
9630Sstevel@tonic-gate 	else
9646448Svikram 		ret = f(menu, NULL, opt);
9656448Svikram 
9660Sstevel@tonic-gate 	if (ret == BAM_WRITE) {
9676448Svikram 		BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root));
9686448Svikram 		ret = menu_write(clean_menu_root, menu);
9696448Svikram 	}
9706448Svikram 
9716448Svikram out:
9726448Svikram 	INJECT_ERROR1("POOL_SET", pool = "/pooldata");
9736448Svikram 	assert((is_zfs(menu_root)) ^ (pool == NULL));
9746448Svikram 	if (pool) {
9756448Svikram 		(void) umount_top_dataset(pool, zmnted, zmntpt);
9766448Svikram 		free(special);
9776448Svikram 	}
9780Sstevel@tonic-gate 	menu_free(menu);
9790Sstevel@tonic-gate 	return (ret);
9800Sstevel@tonic-gate }
9810Sstevel@tonic-gate 
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate static error_t
9840Sstevel@tonic-gate bam_archive(
9850Sstevel@tonic-gate 	char *subcmd,
9860Sstevel@tonic-gate 	char *opt)
9870Sstevel@tonic-gate {
9886448Svikram 	error_t			ret;
9896448Svikram 	error_t			(*f)(char *root, char *opt);
9906448Svikram 	const char		*fcn = "bam_archive()";
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 	/*
993662Sszhou 	 * Add trailing / for archive subcommands
994662Sszhou 	 */
995662Sszhou 	if (rootbuf[strlen(rootbuf) - 1] != '/')
996662Sszhou 		(void) strcat(rootbuf, "/");
997662Sszhou 	bam_rootlen = strlen(rootbuf);
998662Sszhou 
999662Sszhou 	/*
10000Sstevel@tonic-gate 	 * Check arguments
10010Sstevel@tonic-gate 	 */
10020Sstevel@tonic-gate 	ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
10030Sstevel@tonic-gate 	if (ret != BAM_SUCCESS) {
10040Sstevel@tonic-gate 		return (BAM_ERROR);
10050Sstevel@tonic-gate 	}
10060Sstevel@tonic-gate 
10076448Svikram 	ret = get_boot_cap(rootbuf);
10086448Svikram 	if (ret != BAM_SUCCESS) {
10096448Svikram 		BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
10103446Smrj 		return (ret);
10116448Svikram 	}
10123446Smrj 
10130Sstevel@tonic-gate 	/*
10140Sstevel@tonic-gate 	 * Check archive not supported with update_all
10150Sstevel@tonic-gate 	 * since it is awkward to display out-of-sync
10160Sstevel@tonic-gate 	 * information for each BE.
10170Sstevel@tonic-gate 	 */
10180Sstevel@tonic-gate 	if (bam_check && strcmp(subcmd, "update_all") == 0) {
10190Sstevel@tonic-gate 		bam_error(CHECK_NOT_SUPPORTED, subcmd);
10200Sstevel@tonic-gate 		return (BAM_ERROR);
10210Sstevel@tonic-gate 	}
10220Sstevel@tonic-gate 
1023316Svikram 	if (strcmp(subcmd, "update_all") == 0)
1024316Svikram 		bam_update_all = 1;
1025316Svikram 
10265648Ssetje #if !defined(_OPB)
10274581Ssherrym 	ucode_install(bam_root);
10284581Ssherrym #endif
10294581Ssherrym 
1030316Svikram 	ret = f(bam_root, opt);
1031316Svikram 
1032316Svikram 	bam_update_all = 0;
1033316Svikram 
1034316Svikram 	return (ret);
10350Sstevel@tonic-gate }
10360Sstevel@tonic-gate 
10370Sstevel@tonic-gate /*PRINTFLIKE1*/
10383446Smrj void
10390Sstevel@tonic-gate bam_error(char *format, ...)
10400Sstevel@tonic-gate {
10410Sstevel@tonic-gate 	va_list ap;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	va_start(ap, format);
10440Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", prog);
10450Sstevel@tonic-gate 	(void) vfprintf(stderr, format, ap);
10460Sstevel@tonic-gate 	va_end(ap);
10470Sstevel@tonic-gate }
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate /*PRINTFLIKE1*/
10506448Svikram void
10516448Svikram bam_derror(char *format, ...)
10526448Svikram {
10536448Svikram 	va_list ap;
10546448Svikram 
10556448Svikram 	assert(bam_debug);
10566448Svikram 
10576448Svikram 	va_start(ap, format);
10586448Svikram 	(void) fprintf(stderr, "DEBUG: ");
10596448Svikram 	(void) vfprintf(stderr, format, ap);
10606448Svikram 	va_end(ap);
10616448Svikram }
10626448Svikram 
10636448Svikram /*PRINTFLIKE1*/
10646448Svikram void
10650Sstevel@tonic-gate bam_print(char *format, ...)
10660Sstevel@tonic-gate {
10670Sstevel@tonic-gate 	va_list ap;
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	va_start(ap, format);
10700Sstevel@tonic-gate 	(void) vfprintf(stdout, format, ap);
10710Sstevel@tonic-gate 	va_end(ap);
10720Sstevel@tonic-gate }
10730Sstevel@tonic-gate 
10743446Smrj /*PRINTFLIKE1*/
10753446Smrj void
10763446Smrj bam_print_stderr(char *format, ...)
10773446Smrj {
10783446Smrj 	va_list ap;
10793446Smrj 
10803446Smrj 	va_start(ap, format);
10813446Smrj 	(void) vfprintf(stderr, format, ap);
10823446Smrj 	va_end(ap);
10833446Smrj }
10843446Smrj 
10850Sstevel@tonic-gate static void
10860Sstevel@tonic-gate bam_exit(int excode)
10870Sstevel@tonic-gate {
10880Sstevel@tonic-gate 	bam_unlock();
10890Sstevel@tonic-gate 	exit(excode);
10900Sstevel@tonic-gate }
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate static void
10930Sstevel@tonic-gate bam_lock(void)
10940Sstevel@tonic-gate {
10950Sstevel@tonic-gate 	struct flock lock;
10960Sstevel@tonic-gate 	pid_t pid;
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate 	bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
10990Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11000Sstevel@tonic-gate 		/*
11010Sstevel@tonic-gate 		 * We may be invoked early in boot for archive verification.
11020Sstevel@tonic-gate 		 * In this case, root is readonly and /var/run may not exist.
11030Sstevel@tonic-gate 		 * Proceed without the lock
11040Sstevel@tonic-gate 		 */
11050Sstevel@tonic-gate 		if (errno == EROFS || errno == ENOENT) {
11060Sstevel@tonic-gate 			bam_root_readonly = 1;
11070Sstevel@tonic-gate 			return;
11080Sstevel@tonic-gate 		}
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 		bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
11110Sstevel@tonic-gate 		bam_exit(1);
11120Sstevel@tonic-gate 	}
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
11150Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
11160Sstevel@tonic-gate 	lock.l_start = 0;
11170Sstevel@tonic-gate 	lock.l_len = 0;
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
11200Sstevel@tonic-gate 		if (errno != EACCES && errno != EAGAIN) {
11210Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11220Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11230Sstevel@tonic-gate 			bam_lock_fd = -1;
11240Sstevel@tonic-gate 			bam_exit(1);
11250Sstevel@tonic-gate 		}
11260Sstevel@tonic-gate 		pid = 0;
11270Sstevel@tonic-gate 		(void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
11280Sstevel@tonic-gate 		bam_print(FILE_LOCKED, pid);
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 		lock.l_type = F_WRLCK;
11310Sstevel@tonic-gate 		lock.l_whence = SEEK_SET;
11320Sstevel@tonic-gate 		lock.l_start = 0;
11330Sstevel@tonic-gate 		lock.l_len = 0;
11340Sstevel@tonic-gate 		if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
11350Sstevel@tonic-gate 			bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11360Sstevel@tonic-gate 			(void) close(bam_lock_fd);
11370Sstevel@tonic-gate 			bam_lock_fd = -1;
11380Sstevel@tonic-gate 			bam_exit(1);
11390Sstevel@tonic-gate 		}
11400Sstevel@tonic-gate 	}
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	/* We own the lock now */
11430Sstevel@tonic-gate 	pid = getpid();
11440Sstevel@tonic-gate 	(void) write(bam_lock_fd, &pid, sizeof (pid));
11450Sstevel@tonic-gate }
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate static void
11480Sstevel@tonic-gate bam_unlock(void)
11490Sstevel@tonic-gate {
11500Sstevel@tonic-gate 	struct flock unlock;
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 	/*
11530Sstevel@tonic-gate 	 * NOP if we don't hold the lock
11540Sstevel@tonic-gate 	 */
11550Sstevel@tonic-gate 	if (bam_lock_fd < 0) {
11560Sstevel@tonic-gate 		return;
11570Sstevel@tonic-gate 	}
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
11600Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
11610Sstevel@tonic-gate 	unlock.l_start = 0;
11620Sstevel@tonic-gate 	unlock.l_len = 0;
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
11650Sstevel@tonic-gate 		bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
11660Sstevel@tonic-gate 	}
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	if (close(bam_lock_fd) == -1) {
11690Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
11700Sstevel@tonic-gate 	}
11710Sstevel@tonic-gate 	bam_lock_fd = -1;
11720Sstevel@tonic-gate }
11730Sstevel@tonic-gate 
11740Sstevel@tonic-gate static error_t
11750Sstevel@tonic-gate list_archive(char *root, char *opt)
11760Sstevel@tonic-gate {
11770Sstevel@tonic-gate 	filelist_t flist;
11780Sstevel@tonic-gate 	filelist_t *flistp = &flist;
11790Sstevel@tonic-gate 	line_t *lp;
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 	assert(root);
11820Sstevel@tonic-gate 	assert(opt == NULL);
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
11850Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
11860Sstevel@tonic-gate 		return (BAM_ERROR);
11870Sstevel@tonic-gate 	}
11880Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
11890Sstevel@tonic-gate 
11900Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
11910Sstevel@tonic-gate 		bam_print(PRINT, lp->line);
11920Sstevel@tonic-gate 	}
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 	filelist_free(flistp);
11950Sstevel@tonic-gate 
11960Sstevel@tonic-gate 	return (BAM_SUCCESS);
11970Sstevel@tonic-gate }
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate /*
12000Sstevel@tonic-gate  * This routine writes a list of lines to a file.
12010Sstevel@tonic-gate  * The list is *not* freed
12020Sstevel@tonic-gate  */
12030Sstevel@tonic-gate static error_t
12040Sstevel@tonic-gate list2file(char *root, char *tmp, char *final, line_t *start)
12050Sstevel@tonic-gate {
12066448Svikram 	char		tmpfile[PATH_MAX];
12076448Svikram 	char		path[PATH_MAX];
12086448Svikram 	FILE		*fp;
12096448Svikram 	int		ret;
12106448Svikram 	struct stat	sb;
12116448Svikram 	mode_t		mode;
12126448Svikram 	uid_t		root_uid;
12136448Svikram 	gid_t		sys_gid;
12146448Svikram 	struct passwd	*pw;
12156448Svikram 	struct group	*gp;
12166448Svikram 	const char	*fcn = "list2file()";
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, final);
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 	if (start == NULL) {
12216448Svikram 		/* Empty GRUB menu */
12220Sstevel@tonic-gate 		if (stat(path, &sb) != -1) {
12230Sstevel@tonic-gate 			bam_print(UNLINK_EMPTY, path);
12240Sstevel@tonic-gate 			if (unlink(path) != 0) {
12250Sstevel@tonic-gate 				bam_error(UNLINK_FAIL, path, strerror(errno));
12260Sstevel@tonic-gate 				return (BAM_ERROR);
12270Sstevel@tonic-gate 			} else {
12280Sstevel@tonic-gate 				return (BAM_SUCCESS);
12290Sstevel@tonic-gate 			}
12300Sstevel@tonic-gate 		}
12316448Svikram 		return (BAM_SUCCESS);
12320Sstevel@tonic-gate 	}
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate 	/*
12350Sstevel@tonic-gate 	 * Preserve attributes of existing file if possible,
12360Sstevel@tonic-gate 	 * otherwise ask the system for uid/gid of root/sys.
12370Sstevel@tonic-gate 	 * If all fails, fall back on hard-coded defaults.
12380Sstevel@tonic-gate 	 */
12390Sstevel@tonic-gate 	if (stat(path, &sb) != -1) {
12400Sstevel@tonic-gate 		mode = sb.st_mode;
12410Sstevel@tonic-gate 		root_uid = sb.st_uid;
12420Sstevel@tonic-gate 		sys_gid = sb.st_gid;
12430Sstevel@tonic-gate 	} else {
12440Sstevel@tonic-gate 		mode = DEFAULT_DEV_MODE;
12450Sstevel@tonic-gate 		if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
12460Sstevel@tonic-gate 			root_uid = pw->pw_uid;
12470Sstevel@tonic-gate 		} else {
12486448Svikram 			bam_error(CANT_FIND_USER,
12496448Svikram 			    DEFAULT_DEV_USER, DEFAULT_DEV_UID);
12500Sstevel@tonic-gate 			root_uid = (uid_t)DEFAULT_DEV_UID;
12510Sstevel@tonic-gate 		}
12520Sstevel@tonic-gate 		if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
12530Sstevel@tonic-gate 			sys_gid = gp->gr_gid;
12540Sstevel@tonic-gate 		} else {
12556448Svikram 			bam_error(CANT_FIND_GROUP,
12566448Svikram 			    DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
12570Sstevel@tonic-gate 			sys_gid = (gid_t)DEFAULT_DEV_GID;
12580Sstevel@tonic-gate 		}
12590Sstevel@tonic-gate 	}
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	(void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	/* Truncate tmpfile first */
12640Sstevel@tonic-gate 	fp = fopen(tmpfile, "w");
12650Sstevel@tonic-gate 	if (fp == NULL) {
12660Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12670Sstevel@tonic-gate 		return (BAM_ERROR);
12680Sstevel@tonic-gate 	}
12696694Svikram 	ret = fclose(fp);
12706694Svikram 	INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
12716694Svikram 	if (ret == EOF) {
12720Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12730Sstevel@tonic-gate 		return (BAM_ERROR);
12740Sstevel@tonic-gate 	}
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	/* Now open it in append mode */
12770Sstevel@tonic-gate 	fp = fopen(tmpfile, "a");
12780Sstevel@tonic-gate 	if (fp == NULL) {
12790Sstevel@tonic-gate 		bam_error(OPEN_FAIL, tmpfile, strerror(errno));
12800Sstevel@tonic-gate 		return (BAM_ERROR);
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate 	for (; start; start = start->next) {
12846694Svikram 		ret = s_fputs(start->line, fp);
12856694Svikram 		INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
12866694Svikram 		if (ret == EOF) {
12870Sstevel@tonic-gate 			bam_error(WRITE_FAIL, tmpfile, strerror(errno));
12880Sstevel@tonic-gate 			(void) fclose(fp);
12890Sstevel@tonic-gate 			return (BAM_ERROR);
12900Sstevel@tonic-gate 		}
12910Sstevel@tonic-gate 	}
12920Sstevel@tonic-gate 
12936694Svikram 	ret = fclose(fp);
12946694Svikram 	INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
12956694Svikram 	if (ret == EOF) {
12960Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
12970Sstevel@tonic-gate 		return (BAM_ERROR);
12980Sstevel@tonic-gate 	}
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate 	/*
1301271Sjg 	 * Set up desired attributes.  Ignore failures on filesystems
1302271Sjg 	 * not supporting these operations - pcfs reports unsupported
1303271Sjg 	 * operations as EINVAL.
13040Sstevel@tonic-gate 	 */
13050Sstevel@tonic-gate 	ret = chmod(tmpfile, mode);
1306271Sjg 	if (ret == -1 &&
1307271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13080Sstevel@tonic-gate 		bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
13090Sstevel@tonic-gate 		return (BAM_ERROR);
13100Sstevel@tonic-gate 	}
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	ret = chown(tmpfile, root_uid, sys_gid);
1313271Sjg 	if (ret == -1 &&
1314271Sjg 	    errno != EINVAL && errno != ENOTSUP) {
13150Sstevel@tonic-gate 		bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
13160Sstevel@tonic-gate 		return (BAM_ERROR);
13170Sstevel@tonic-gate 	}
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	/*
13210Sstevel@tonic-gate 	 * Do an atomic rename
13220Sstevel@tonic-gate 	 */
13230Sstevel@tonic-gate 	ret = rename(tmpfile, path);
13246694Svikram 	INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
13250Sstevel@tonic-gate 	if (ret != 0) {
13260Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path, strerror(errno));
13270Sstevel@tonic-gate 		return (BAM_ERROR);
13280Sstevel@tonic-gate 	}
13290Sstevel@tonic-gate 
13306448Svikram 	BAM_DPRINTF((D_WROTE_FILE, fcn, path));
13310Sstevel@tonic-gate 	return (BAM_SUCCESS);
13320Sstevel@tonic-gate }
13330Sstevel@tonic-gate 
13340Sstevel@tonic-gate /*
13358735SEnrico.Perla@Sun.COM  * Checks if the path specified (without the file name at the end) exists
13368735SEnrico.Perla@Sun.COM  * and creates it if not. If the path exists and is not a directory, an attempt
13378735SEnrico.Perla@Sun.COM  * to unlink is made.
13380Sstevel@tonic-gate  */
13398735SEnrico.Perla@Sun.COM static int
13408735SEnrico.Perla@Sun.COM setup_path(char *path)
13418735SEnrico.Perla@Sun.COM {
13428735SEnrico.Perla@Sun.COM 	char 		*p;
13438735SEnrico.Perla@Sun.COM 	int		ret;
13448735SEnrico.Perla@Sun.COM 	struct stat	sb;
13458735SEnrico.Perla@Sun.COM 
13468735SEnrico.Perla@Sun.COM 	p = strrchr(path, '/');
13478735SEnrico.Perla@Sun.COM 	if (p != NULL) {
13488735SEnrico.Perla@Sun.COM 		*p = '\0';
13498735SEnrico.Perla@Sun.COM 		if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
13508735SEnrico.Perla@Sun.COM 			/* best effort attempt, mkdirp will catch the error */
13518735SEnrico.Perla@Sun.COM 			(void) unlink(path);
13528735SEnrico.Perla@Sun.COM 			if (bam_verbose)
13538735SEnrico.Perla@Sun.COM 				bam_print(NEED_DIRPATH, path);
13548735SEnrico.Perla@Sun.COM 			ret = mkdirp(path, DIR_PERMS);
13558735SEnrico.Perla@Sun.COM 			if (ret == -1) {
13568735SEnrico.Perla@Sun.COM 				bam_error(MKDIR_FAILED, path, strerror(errno));
13578735SEnrico.Perla@Sun.COM 				*p = '/';
13588735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
13598735SEnrico.Perla@Sun.COM 			}
13608735SEnrico.Perla@Sun.COM 		}
13618735SEnrico.Perla@Sun.COM 		*p = '/';
13628735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
13638735SEnrico.Perla@Sun.COM 	}
13648735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
13658735SEnrico.Perla@Sun.COM }
13668735SEnrico.Perla@Sun.COM 
13678735SEnrico.Perla@Sun.COM typedef union {
13688735SEnrico.Perla@Sun.COM 	gzFile	gzfile;
13698735SEnrico.Perla@Sun.COM 	int	fdfile;
13708735SEnrico.Perla@Sun.COM } outfile;
13718735SEnrico.Perla@Sun.COM 
13728735SEnrico.Perla@Sun.COM typedef struct {
13738735SEnrico.Perla@Sun.COM 	char		path[PATH_MAX];
13748735SEnrico.Perla@Sun.COM 	outfile		out;
13758735SEnrico.Perla@Sun.COM } cachefile;
13768735SEnrico.Perla@Sun.COM 
13778735SEnrico.Perla@Sun.COM static int
13788735SEnrico.Perla@Sun.COM setup_file(char *base, const char *path, cachefile *cf)
13798735SEnrico.Perla@Sun.COM {
13808735SEnrico.Perla@Sun.COM 	int	ret;
13818735SEnrico.Perla@Sun.COM 	char	*strip;
13828735SEnrico.Perla@Sun.COM 
13838735SEnrico.Perla@Sun.COM 	/* init gzfile or fdfile in case we fail before opening */
13848735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT)
13858735SEnrico.Perla@Sun.COM 		cf->out.gzfile = NULL;
13868735SEnrico.Perla@Sun.COM 	else
13878735SEnrico.Perla@Sun.COM 		cf->out.fdfile = -1;
13888735SEnrico.Perla@Sun.COM 
13898735SEnrico.Perla@Sun.COM 	/* strip the trailing altroot path */
13908735SEnrico.Perla@Sun.COM 	strip = (char *)path + strlen(rootbuf);
13918735SEnrico.Perla@Sun.COM 
13928735SEnrico.Perla@Sun.COM 	ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
13938735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (cf->path)) {
13948735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
13958735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
13968735SEnrico.Perla@Sun.COM 	}
13978735SEnrico.Perla@Sun.COM 
13988735SEnrico.Perla@Sun.COM 	/* Check if path is present in the archive cache directory */
13998735SEnrico.Perla@Sun.COM 	if (setup_path(cf->path) == BAM_ERROR)
14008735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
14018735SEnrico.Perla@Sun.COM 
14028735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT) {
14038735SEnrico.Perla@Sun.COM 		if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
14048735SEnrico.Perla@Sun.COM 			bam_error(GZ_OPEN_FAIL, gzerror(cf->out.gzfile, NULL));
14058735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
14068735SEnrico.Perla@Sun.COM 		}
14078735SEnrico.Perla@Sun.COM 		(void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
14088735SEnrico.Perla@Sun.COM 		    Z_DEFAULT_STRATEGY);
14098735SEnrico.Perla@Sun.COM 	} else {
14108735SEnrico.Perla@Sun.COM 		if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
14118735SEnrico.Perla@Sun.COM 		    == -1) {
14128735SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, cf->path, strerror(errno));
14138735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
14148735SEnrico.Perla@Sun.COM 		}
14158735SEnrico.Perla@Sun.COM 	}
14168735SEnrico.Perla@Sun.COM 
14178735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
14188735SEnrico.Perla@Sun.COM }
14198735SEnrico.Perla@Sun.COM 
14208735SEnrico.Perla@Sun.COM static int
14218735SEnrico.Perla@Sun.COM cache_write(cachefile cf, char *buf, int size)
14228735SEnrico.Perla@Sun.COM {
14238735SEnrico.Perla@Sun.COM 	int	err;
14248735SEnrico.Perla@Sun.COM 
14258735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT) {
14268735SEnrico.Perla@Sun.COM 		if (gzwrite(cf.out.gzfile, buf, size) < 1) {
14278735SEnrico.Perla@Sun.COM 			bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err));
14288735SEnrico.Perla@Sun.COM 			if (err == Z_ERRNO)
14298735SEnrico.Perla@Sun.COM 				bam_error(WRITE_FAIL, cf.path, strerror(errno));
14308735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
14318735SEnrico.Perla@Sun.COM 		}
14328735SEnrico.Perla@Sun.COM 	} else {
14338735SEnrico.Perla@Sun.COM 		if (write(cf.out.fdfile, buf, size) < 1) {
14348735SEnrico.Perla@Sun.COM 			bam_error(WRITE_FAIL, cf.path, strerror(errno));
14358735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
14368735SEnrico.Perla@Sun.COM 		}
14378735SEnrico.Perla@Sun.COM 	}
14388735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
14398735SEnrico.Perla@Sun.COM }
14408735SEnrico.Perla@Sun.COM 
14418735SEnrico.Perla@Sun.COM static int
14428735SEnrico.Perla@Sun.COM cache_close(cachefile cf)
14438735SEnrico.Perla@Sun.COM {
14448735SEnrico.Perla@Sun.COM 	int	ret;
14458735SEnrico.Perla@Sun.COM 
14468735SEnrico.Perla@Sun.COM 	if (bam_direct == BAM_DIRECT_DBOOT) {
14478735SEnrico.Perla@Sun.COM 		if (cf.out.gzfile) {
14488735SEnrico.Perla@Sun.COM 			ret = gzclose(cf.out.gzfile);
14498735SEnrico.Perla@Sun.COM 			if (ret == Z_OK)
14508735SEnrico.Perla@Sun.COM 				return (BAM_SUCCESS);
14518735SEnrico.Perla@Sun.COM 			else if (ret == Z_ERRNO)
14528735SEnrico.Perla@Sun.COM 				bam_error(CLOSE_FAIL, cf.path, strerror(errno));
14538735SEnrico.Perla@Sun.COM 			else
14548735SEnrico.Perla@Sun.COM 				bam_error(GZCLOSE_FAIL, gzerror(cf.out.gzfile,
14558735SEnrico.Perla@Sun.COM 				    NULL));
14568735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
14578735SEnrico.Perla@Sun.COM 		}
14588735SEnrico.Perla@Sun.COM 	} else {
14598735SEnrico.Perla@Sun.COM 		if (cf.out.fdfile != -1) {
14608735SEnrico.Perla@Sun.COM 			ret = close(cf.out.fdfile);
14618735SEnrico.Perla@Sun.COM 			if (ret != 0) {
14628735SEnrico.Perla@Sun.COM 				bam_error(CLOSE_FAIL, cf.path, strerror(errno));
14638735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
14648735SEnrico.Perla@Sun.COM 			}
14658735SEnrico.Perla@Sun.COM 		}
14668735SEnrico.Perla@Sun.COM 	}
14678735SEnrico.Perla@Sun.COM 
14688735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
14698735SEnrico.Perla@Sun.COM }
14708735SEnrico.Perla@Sun.COM 
14718735SEnrico.Perla@Sun.COM static int
14728735SEnrico.Perla@Sun.COM dircache_updatefile(const char *path, int what)
14738735SEnrico.Perla@Sun.COM {
14748735SEnrico.Perla@Sun.COM 	int 		ret, exitcode;
14758735SEnrico.Perla@Sun.COM 	char 		buf[4096 * 4];
14768735SEnrico.Perla@Sun.COM 	FILE 		*infile;
14778735SEnrico.Perla@Sun.COM 	cachefile 	outfile, outupdt;
14788735SEnrico.Perla@Sun.COM 
14798735SEnrico.Perla@Sun.COM 	if (bam_nowrite())
14808735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
14818735SEnrico.Perla@Sun.COM 
14828735SEnrico.Perla@Sun.COM 	if ((infile = fopen(path, "rb")) == NULL) {
14838735SEnrico.Perla@Sun.COM 		bam_error(OPEN_FAIL, path, strerror(errno));
14848735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
14858735SEnrico.Perla@Sun.COM 	}
14868735SEnrico.Perla@Sun.COM 
14878735SEnrico.Perla@Sun.COM 	ret = setup_file(get_cachedir(what), path, &outfile);
14888735SEnrico.Perla@Sun.COM 	if (ret == BAM_ERROR) {
14898735SEnrico.Perla@Sun.COM 		exitcode = BAM_ERROR;
14908735SEnrico.Perla@Sun.COM 		goto out;
14918735SEnrico.Perla@Sun.COM 	}
14928735SEnrico.Perla@Sun.COM 	if (!is_dir_flag_on(what, NO_MULTI)) {
14938735SEnrico.Perla@Sun.COM 		ret = setup_file(get_updatedir(what), path, &outupdt);
14948735SEnrico.Perla@Sun.COM 		if (ret == BAM_ERROR)
14958735SEnrico.Perla@Sun.COM 			set_dir_flag(what, NO_MULTI);
14968735SEnrico.Perla@Sun.COM 	}
14978735SEnrico.Perla@Sun.COM 
14988735SEnrico.Perla@Sun.COM 	while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
14998735SEnrico.Perla@Sun.COM 		if (cache_write(outfile, buf, ret) == BAM_ERROR) {
15008735SEnrico.Perla@Sun.COM 			exitcode = BAM_ERROR;
15018735SEnrico.Perla@Sun.COM 			goto out;
15028735SEnrico.Perla@Sun.COM 		}
15038735SEnrico.Perla@Sun.COM 		if (!is_dir_flag_on(what, NO_MULTI))
15048735SEnrico.Perla@Sun.COM 			if (cache_write(outupdt, buf, ret) == BAM_ERROR)
15058735SEnrico.Perla@Sun.COM 				set_dir_flag(what, NO_MULTI);
15068735SEnrico.Perla@Sun.COM 	}
15078735SEnrico.Perla@Sun.COM 
15088735SEnrico.Perla@Sun.COM 	set_dir_flag(what, NEED_UPDATE);
15098735SEnrico.Perla@Sun.COM 	get_count(what)++;
15108735SEnrico.Perla@Sun.COM 	exitcode = BAM_SUCCESS;
15118735SEnrico.Perla@Sun.COM out:
15128735SEnrico.Perla@Sun.COM 	(void) fclose(infile);
15138735SEnrico.Perla@Sun.COM 	if (cache_close(outfile) == BAM_ERROR)
15148735SEnrico.Perla@Sun.COM 		exitcode = BAM_ERROR;
15158735SEnrico.Perla@Sun.COM 	if (!is_dir_flag_on(what, NO_MULTI) &&
15168735SEnrico.Perla@Sun.COM 	    cache_close(outupdt) == BAM_ERROR)
15178735SEnrico.Perla@Sun.COM 		exitcode = BAM_ERROR;
15188735SEnrico.Perla@Sun.COM 	if (exitcode == BAM_ERROR)
15198735SEnrico.Perla@Sun.COM 		set_flag(UPDATE_ERROR);
15208735SEnrico.Perla@Sun.COM 	return (exitcode);
15218735SEnrico.Perla@Sun.COM }
15228735SEnrico.Perla@Sun.COM 
15238735SEnrico.Perla@Sun.COM static int
15248735SEnrico.Perla@Sun.COM dircache_updatedir(const char *path, int what, int updt)
15258735SEnrico.Perla@Sun.COM {
15268735SEnrico.Perla@Sun.COM 	int		ret;
15278735SEnrico.Perla@Sun.COM 	char		dpath[PATH_MAX];
15288735SEnrico.Perla@Sun.COM 	char		*strip;
15298735SEnrico.Perla@Sun.COM 	struct stat	sb;
15308735SEnrico.Perla@Sun.COM 
15318735SEnrico.Perla@Sun.COM 	strip = (char *)path + strlen(rootbuf);
15328735SEnrico.Perla@Sun.COM 
15338735SEnrico.Perla@Sun.COM 	ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
15348735SEnrico.Perla@Sun.COM 	    get_updatedir(what) : get_cachedir(what), strip);
15358735SEnrico.Perla@Sun.COM 
15368735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (dpath)) {
15378735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
15388735SEnrico.Perla@Sun.COM 		set_flag(UPDATE_ERROR);
15398735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
15408735SEnrico.Perla@Sun.COM 	}
15418735SEnrico.Perla@Sun.COM 
15428735SEnrico.Perla@Sun.COM 	if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
15438735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
15448735SEnrico.Perla@Sun.COM 
15458735SEnrico.Perla@Sun.COM 	if (updt) {
15468735SEnrico.Perla@Sun.COM 		if (!is_dir_flag_on(what, NO_MULTI))
15478735SEnrico.Perla@Sun.COM 			if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
15488735SEnrico.Perla@Sun.COM 				set_dir_flag(what, NO_MULTI);
15498735SEnrico.Perla@Sun.COM 	} else {
15508735SEnrico.Perla@Sun.COM 		if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
15518735SEnrico.Perla@Sun.COM 			set_flag(UPDATE_ERROR);
15528735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
15538735SEnrico.Perla@Sun.COM 		}
15548735SEnrico.Perla@Sun.COM 	}
15558735SEnrico.Perla@Sun.COM 
15568735SEnrico.Perla@Sun.COM 	set_dir_flag(what, NEED_UPDATE);
15578735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
15588735SEnrico.Perla@Sun.COM }
15598735SEnrico.Perla@Sun.COM 
15608735SEnrico.Perla@Sun.COM #define	DO_CACHE_DIR	0
15618735SEnrico.Perla@Sun.COM #define	DO_UPDATE_DIR	1
15628735SEnrico.Perla@Sun.COM 
15638735SEnrico.Perla@Sun.COM #if defined(_LP64) || defined(_LONGLONG_TYPE)
15648735SEnrico.Perla@Sun.COM typedef		Elf64_Ehdr	_elfhdr;
15658735SEnrico.Perla@Sun.COM #else
15668735SEnrico.Perla@Sun.COM typedef		Elf32_Ehdr	_elfhdr;
15678735SEnrico.Perla@Sun.COM #endif
15688735SEnrico.Perla@Sun.COM 
15698735SEnrico.Perla@Sun.COM /*
15708735SEnrico.Perla@Sun.COM  * This routine updates the contents of the cache directory
15718735SEnrico.Perla@Sun.COM  */
15728735SEnrico.Perla@Sun.COM static int
15738735SEnrico.Perla@Sun.COM update_dircache(const char *path, int flags)
15748735SEnrico.Perla@Sun.COM {
15758735SEnrico.Perla@Sun.COM 	int rc = BAM_SUCCESS;
15768735SEnrico.Perla@Sun.COM 
15778735SEnrico.Perla@Sun.COM 	switch (flags) {
15788735SEnrico.Perla@Sun.COM 	case FTW_F:
15798735SEnrico.Perla@Sun.COM 		{
15808735SEnrico.Perla@Sun.COM 		int	fd;
15818735SEnrico.Perla@Sun.COM 		_elfhdr	elf;
15828735SEnrico.Perla@Sun.COM 
15838735SEnrico.Perla@Sun.COM 		if ((fd = open(path, O_RDONLY)) < 0) {
15848735SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, path, strerror(errno));
15858735SEnrico.Perla@Sun.COM 			set_flag(UPDATE_ERROR);
15868735SEnrico.Perla@Sun.COM 			rc = BAM_ERROR;
15878735SEnrico.Perla@Sun.COM 			break;
15888735SEnrico.Perla@Sun.COM 		}
15898735SEnrico.Perla@Sun.COM 
15908735SEnrico.Perla@Sun.COM 		/*
15918735SEnrico.Perla@Sun.COM 		 * libelf and gelf would be a cleaner and easier way to handle
15928735SEnrico.Perla@Sun.COM 		 * this, but libelf fails compilation if _ILP32 is defined &&
15938735SEnrico.Perla@Sun.COM 		 * _FILE_OFFSET_BITS is != 32 ...
15948735SEnrico.Perla@Sun.COM 		 */
15958735SEnrico.Perla@Sun.COM 		if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
15968735SEnrico.Perla@Sun.COM 			bam_error(READ_FAIL, path, strerror(errno));
15978735SEnrico.Perla@Sun.COM 			set_flag(UPDATE_ERROR);
15988735SEnrico.Perla@Sun.COM 			(void) close(fd);
15998735SEnrico.Perla@Sun.COM 			rc = BAM_ERROR;
16008735SEnrico.Perla@Sun.COM 			break;
16018735SEnrico.Perla@Sun.COM 		}
16028735SEnrico.Perla@Sun.COM 		(void) close(fd);
16038735SEnrico.Perla@Sun.COM 
16048735SEnrico.Perla@Sun.COM 		/*
16058735SEnrico.Perla@Sun.COM 		 * If the file is not an executable and is not inside an amd64
16068735SEnrico.Perla@Sun.COM 		 * directory, we copy it in both the cache directories,
16078735SEnrico.Perla@Sun.COM 		 * otherwise, we only copy it inside the 64-bit one.
16088735SEnrico.Perla@Sun.COM 		 */
16098735SEnrico.Perla@Sun.COM 		if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
16108735SEnrico.Perla@Sun.COM 			if (strstr(path, "/amd64")) {
16118735SEnrico.Perla@Sun.COM 				if (has_cachedir(FILE64))
16128735SEnrico.Perla@Sun.COM 					rc = dircache_updatefile(path, FILE64);
16138735SEnrico.Perla@Sun.COM 			} else if (has_cachedir(FILE64)) {
16148735SEnrico.Perla@Sun.COM 				rc = dircache_updatefile(path, FILE32);
16158735SEnrico.Perla@Sun.COM 				if (rc == BAM_SUCCESS)
16168735SEnrico.Perla@Sun.COM 					rc = dircache_updatefile(path, FILE64);
16178735SEnrico.Perla@Sun.COM 			} else {
16188735SEnrico.Perla@Sun.COM 				rc = dircache_updatefile(path, FILE32);
16198735SEnrico.Perla@Sun.COM 			}
16208735SEnrico.Perla@Sun.COM 		} else {
16218735SEnrico.Perla@Sun.COM 			/*
16228735SEnrico.Perla@Sun.COM 			 * Based on the ELF class we copy the file in the 32-bit
16238735SEnrico.Perla@Sun.COM 			 * or the 64-bit cache directory.
16248735SEnrico.Perla@Sun.COM 			 */
16258735SEnrico.Perla@Sun.COM 			if (elf.e_ident[EI_CLASS] == ELFCLASS32)
16268735SEnrico.Perla@Sun.COM 				rc = dircache_updatefile(path, FILE32);
16278735SEnrico.Perla@Sun.COM 			else if (elf.e_ident[EI_CLASS] == ELFCLASS64)
16288735SEnrico.Perla@Sun.COM 				if (has_cachedir(FILE64))
16298735SEnrico.Perla@Sun.COM 					rc = dircache_updatefile(path, FILE64);
16308735SEnrico.Perla@Sun.COM 			else {
16318735SEnrico.Perla@Sun.COM 				bam_print(NO3264ELF, path);
16328735SEnrico.Perla@Sun.COM 				/* paranoid */
16338735SEnrico.Perla@Sun.COM 				rc  = dircache_updatefile(path, FILE32);
16348735SEnrico.Perla@Sun.COM 				if (rc == BAM_SUCCESS)
16358735SEnrico.Perla@Sun.COM 					rc = dircache_updatefile(path, FILE64);
16368735SEnrico.Perla@Sun.COM 			}
16378735SEnrico.Perla@Sun.COM 		}
16388735SEnrico.Perla@Sun.COM 		break;
16398735SEnrico.Perla@Sun.COM 		}
16408735SEnrico.Perla@Sun.COM 	case FTW_D:
16418735SEnrico.Perla@Sun.COM 		if (strstr(path, "/amd64") == NULL) {
16428735SEnrico.Perla@Sun.COM 			rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
16438735SEnrico.Perla@Sun.COM 			if (rc == BAM_SUCCESS)
16448735SEnrico.Perla@Sun.COM 				rc = dircache_updatedir(path, FILE32,
16458735SEnrico.Perla@Sun.COM 				    DO_CACHE_DIR);
16468735SEnrico.Perla@Sun.COM 		} else {
16478735SEnrico.Perla@Sun.COM 			if (has_cachedir(FILE64)) {
16488735SEnrico.Perla@Sun.COM 				rc = dircache_updatedir(path, FILE64,
16498735SEnrico.Perla@Sun.COM 				    DO_UPDATE_DIR);
16508735SEnrico.Perla@Sun.COM 				if (rc == BAM_SUCCESS)
16518735SEnrico.Perla@Sun.COM 					rc = dircache_updatedir(path, FILE64,
16528735SEnrico.Perla@Sun.COM 					    DO_CACHE_DIR);
16538735SEnrico.Perla@Sun.COM 			}
16548735SEnrico.Perla@Sun.COM 		}
16558735SEnrico.Perla@Sun.COM 		break;
16568735SEnrico.Perla@Sun.COM 	default:
16578735SEnrico.Perla@Sun.COM 		rc = BAM_ERROR;
16588735SEnrico.Perla@Sun.COM 		break;
16598735SEnrico.Perla@Sun.COM 	}
16608735SEnrico.Perla@Sun.COM 
16618735SEnrico.Perla@Sun.COM 	return (rc);
16628735SEnrico.Perla@Sun.COM }
16638735SEnrico.Perla@Sun.COM 
16640Sstevel@tonic-gate /*ARGSUSED*/
16650Sstevel@tonic-gate static int
16660Sstevel@tonic-gate cmpstat(
16670Sstevel@tonic-gate 	const char *file,
16688735SEnrico.Perla@Sun.COM 	const struct stat *st,
16690Sstevel@tonic-gate 	int flags,
16700Sstevel@tonic-gate 	struct FTW *ftw)
16710Sstevel@tonic-gate {
16728735SEnrico.Perla@Sun.COM 	uint_t 		sz;
16738735SEnrico.Perla@Sun.COM 	uint64_t 	*value;
16748735SEnrico.Perla@Sun.COM 	uint64_t 	filestat[2];
16758735SEnrico.Perla@Sun.COM 	int 		error, ret;
16760Sstevel@tonic-gate 
16772334Ssetje 	struct safefile *safefilep;
16788735SEnrico.Perla@Sun.COM 	FILE 		*fp;
16798735SEnrico.Perla@Sun.COM 	struct stat	sb;
16802334Ssetje 
16810Sstevel@tonic-gate 	/*
16828735SEnrico.Perla@Sun.COM 	 * On SPARC we create/update links too.
16830Sstevel@tonic-gate 	 */
16848735SEnrico.Perla@Sun.COM 	if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
16858735SEnrico.Perla@Sun.COM 	    !is_flag_on(IS_SPARC_TARGET)))
16868735SEnrico.Perla@Sun.COM 		return (0);
16878735SEnrico.Perla@Sun.COM 
16888735SEnrico.Perla@Sun.COM 	/*
16898735SEnrico.Perla@Sun.COM 	 * Ignore broken links
16908735SEnrico.Perla@Sun.COM 	 */
16918735SEnrico.Perla@Sun.COM 	if (flags == FTW_SL && stat(file, &sb) < 0)
16920Sstevel@tonic-gate 		return (0);
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	/*
16950Sstevel@tonic-gate 	 * new_nvlp may be NULL if there were errors earlier
16960Sstevel@tonic-gate 	 * but this is not fatal to update determination.
16970Sstevel@tonic-gate 	 */
16980Sstevel@tonic-gate 	if (walk_arg.new_nvlp) {
16998735SEnrico.Perla@Sun.COM 		filestat[0] = st->st_size;
17008735SEnrico.Perla@Sun.COM 		filestat[1] = st->st_mtime;
17010Sstevel@tonic-gate 		error = nvlist_add_uint64_array(walk_arg.new_nvlp,
17020Sstevel@tonic-gate 		    file + bam_rootlen, filestat, 2);
17030Sstevel@tonic-gate 		if (error)
17040Sstevel@tonic-gate 			bam_error(NVADD_FAIL, file, strerror(error));
17050Sstevel@tonic-gate 	}
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	/*
17086319Sjg 	 * If we are invoked as part of system/filesystem/boot-archive, then
17092334Ssetje 	 * there are a number of things we should not worry about
17100Sstevel@tonic-gate 	 */
17112334Ssetje 	if (bam_smf_check) {
17122334Ssetje 		/* ignore amd64 modules unless we are booted amd64. */
17132334Ssetje 		if (!is_amd64() && strstr(file, "/amd64/") != 0)
17142334Ssetje 			return (0);
17152334Ssetje 
17162334Ssetje 		/* read in list of safe files */
17172334Ssetje 		if (safefiles == NULL)
17182334Ssetje 			if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
17192334Ssetje 				safefiles = s_calloc(1,
17202334Ssetje 				    sizeof (struct safefile));
17212334Ssetje 				safefilep = safefiles;
17222334Ssetje 				safefilep->name = s_calloc(1, MAXPATHLEN +
17232334Ssetje 				    MAXNAMELEN);
17242334Ssetje 				safefilep->next = NULL;
17252334Ssetje 				while (s_fgets(safefilep->name, MAXPATHLEN +
17262334Ssetje 				    MAXNAMELEN, fp) != NULL) {
17272334Ssetje 					safefilep->next = s_calloc(1,
17282334Ssetje 					    sizeof (struct safefile));
17292334Ssetje 					safefilep = safefilep->next;
17302334Ssetje 					safefilep->name = s_calloc(1,
17312334Ssetje 					    MAXPATHLEN + MAXNAMELEN);
17322334Ssetje 					safefilep->next = NULL;
17332334Ssetje 				}
17342334Ssetje 				(void) fclose(fp);
17352334Ssetje 			}
17362334Ssetje 	}
17370Sstevel@tonic-gate 
17380Sstevel@tonic-gate 	/*
17398735SEnrico.Perla@Sun.COM 	 * On SPARC we create a -path-list file for mkisofs
17408735SEnrico.Perla@Sun.COM 	 */
17418735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
17428735SEnrico.Perla@Sun.COM 		if (flags != FTW_D) {
17438735SEnrico.Perla@Sun.COM 			char	*strip;
17448735SEnrico.Perla@Sun.COM 
17458735SEnrico.Perla@Sun.COM 			strip = (char *)file + strlen(rootbuf);
17468735SEnrico.Perla@Sun.COM 			(void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
17478735SEnrico.Perla@Sun.COM 			    file);
17488735SEnrico.Perla@Sun.COM 		}
17498735SEnrico.Perla@Sun.COM 	}
17508735SEnrico.Perla@Sun.COM 
17518735SEnrico.Perla@Sun.COM 	/*
17528735SEnrico.Perla@Sun.COM 	 * We are transitioning from the old model to the dircache or the cache
17538735SEnrico.Perla@Sun.COM 	 * directory was removed: create the entry without further checkings.
17548735SEnrico.Perla@Sun.COM 	 */
17558735SEnrico.Perla@Sun.COM 	if (is_flag_on(NEED_CACHE_DIR)) {
17568735SEnrico.Perla@Sun.COM 		if (bam_verbose)
17578735SEnrico.Perla@Sun.COM 			bam_print(PARSEABLE_NEW_FILE, file);
17588735SEnrico.Perla@Sun.COM 
17598735SEnrico.Perla@Sun.COM 		if (is_flag_on(IS_SPARC_TARGET)) {
17608735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
17618735SEnrico.Perla@Sun.COM 			return (0);
17628735SEnrico.Perla@Sun.COM 		}
17638735SEnrico.Perla@Sun.COM 
17648735SEnrico.Perla@Sun.COM 		ret = update_dircache(file, flags);
17658735SEnrico.Perla@Sun.COM 		if (ret == BAM_ERROR) {
17668735SEnrico.Perla@Sun.COM 			bam_error(UPDT_CACHE_FAIL, file);
17678735SEnrico.Perla@Sun.COM 			return (-1);
17688735SEnrico.Perla@Sun.COM 		}
17698735SEnrico.Perla@Sun.COM 
17708735SEnrico.Perla@Sun.COM 		return (0);
17718735SEnrico.Perla@Sun.COM 	}
17728735SEnrico.Perla@Sun.COM 
17738735SEnrico.Perla@Sun.COM 	/*
17740Sstevel@tonic-gate 	 * We need an update if file doesn't exist in old archive
17750Sstevel@tonic-gate 	 */
17760Sstevel@tonic-gate 	if (walk_arg.old_nvlp == NULL ||
17770Sstevel@tonic-gate 	    nvlist_lookup_uint64_array(walk_arg.old_nvlp,
17780Sstevel@tonic-gate 	    file + bam_rootlen, &value, &sz) != 0) {
17790Sstevel@tonic-gate 		if (bam_smf_check)	/* ignore new during smf check */
17800Sstevel@tonic-gate 			return (0);
17818735SEnrico.Perla@Sun.COM 
17828735SEnrico.Perla@Sun.COM 		if (is_flag_on(IS_SPARC_TARGET)) {
17838735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
17848735SEnrico.Perla@Sun.COM 		} else {
17858735SEnrico.Perla@Sun.COM 			ret = update_dircache(file, flags);
17868735SEnrico.Perla@Sun.COM 			if (ret == BAM_ERROR) {
17878735SEnrico.Perla@Sun.COM 				bam_error(UPDT_CACHE_FAIL, file);
17888735SEnrico.Perla@Sun.COM 				return (-1);
17898735SEnrico.Perla@Sun.COM 			}
17908735SEnrico.Perla@Sun.COM 		}
17918735SEnrico.Perla@Sun.COM 
17920Sstevel@tonic-gate 		if (bam_verbose)
17930Sstevel@tonic-gate 			bam_print(PARSEABLE_NEW_FILE, file);
17940Sstevel@tonic-gate 		return (0);
17950Sstevel@tonic-gate 	}
17960Sstevel@tonic-gate 
17970Sstevel@tonic-gate 	/*
17988735SEnrico.Perla@Sun.COM 	 * If we got there, the file is already listed as to be included in the
17998735SEnrico.Perla@Sun.COM 	 * iso image. We just need to know if we are going to rebuild it or not
18008735SEnrico.Perla@Sun.COM 	 */
18018735SEnrico.Perla@Sun.COM 
18028735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET) &&
18038735SEnrico.Perla@Sun.COM 	    is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_smf_check)
18048735SEnrico.Perla@Sun.COM 		return (0);
18058735SEnrico.Perla@Sun.COM 
18068735SEnrico.Perla@Sun.COM 	/*
18070Sstevel@tonic-gate 	 * File exists in old archive. Check if file has changed
18080Sstevel@tonic-gate 	 */
18090Sstevel@tonic-gate 	assert(sz == 2);
18100Sstevel@tonic-gate 	bcopy(value, filestat, sizeof (filestat));
18110Sstevel@tonic-gate 
18128735SEnrico.Perla@Sun.COM 	if (flags != FTW_D && (filestat[0] != st->st_size ||
18138735SEnrico.Perla@Sun.COM 	    filestat[1] != st->st_mtime)) {
18143615Ssetje 		if (bam_smf_check) {
18153615Ssetje 			safefilep = safefiles;
18163615Ssetje 			while (safefilep != NULL) {
18173615Ssetje 				if (strcmp(file + bam_rootlen,
18183615Ssetje 				    safefilep->name) == 0) {
18193615Ssetje 					(void) creat(NEED_UPDATE_FILE, 0644);
18203615Ssetje 					return (0);
18213615Ssetje 				}
18223615Ssetje 				safefilep = safefilep->next;
18233615Ssetje 			}
18243615Ssetje 		}
18258735SEnrico.Perla@Sun.COM 
18268735SEnrico.Perla@Sun.COM 		if (is_flag_on(IS_SPARC_TARGET)) {
18278735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
18288735SEnrico.Perla@Sun.COM 		} else {
18298735SEnrico.Perla@Sun.COM 			ret = update_dircache(file, flags);
18308735SEnrico.Perla@Sun.COM 			if (ret == BAM_ERROR) {
18318735SEnrico.Perla@Sun.COM 				bam_error(UPDT_CACHE_FAIL, file);
18328735SEnrico.Perla@Sun.COM 				return (-1);
18338735SEnrico.Perla@Sun.COM 			}
18348735SEnrico.Perla@Sun.COM 		}
18358735SEnrico.Perla@Sun.COM 
18360Sstevel@tonic-gate 		if (bam_verbose)
18370Sstevel@tonic-gate 			if (bam_smf_check)
18380Sstevel@tonic-gate 				bam_print("    %s\n", file);
18390Sstevel@tonic-gate 			else
18400Sstevel@tonic-gate 				bam_print(PARSEABLE_OUT_DATE, file);
18410Sstevel@tonic-gate 	}
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 	return (0);
18440Sstevel@tonic-gate }
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate /*
18478735SEnrico.Perla@Sun.COM  * Remove a directory path recursively
18488735SEnrico.Perla@Sun.COM  */
18498735SEnrico.Perla@Sun.COM static int
18508735SEnrico.Perla@Sun.COM rmdir_r(char *path)
18518735SEnrico.Perla@Sun.COM {
18528735SEnrico.Perla@Sun.COM 	struct dirent 	*d = NULL;
18538735SEnrico.Perla@Sun.COM 	DIR 		*dir = NULL;
18548735SEnrico.Perla@Sun.COM 	char 		tpath[PATH_MAX];
18558735SEnrico.Perla@Sun.COM 	struct stat 	sb;
18568735SEnrico.Perla@Sun.COM 
18578735SEnrico.Perla@Sun.COM 	if ((dir = opendir(path)) == NULL)
18588735SEnrico.Perla@Sun.COM 		return (-1);
18598735SEnrico.Perla@Sun.COM 
18608735SEnrico.Perla@Sun.COM 	while (d = readdir(dir)) {
18618735SEnrico.Perla@Sun.COM 		if ((strcmp(d->d_name, ".") != 0) &&
18628735SEnrico.Perla@Sun.COM 		    (strcmp(d->d_name, "..") != 0)) {
18638735SEnrico.Perla@Sun.COM 			(void) snprintf(tpath, sizeof (tpath), "%s/%s",
18648735SEnrico.Perla@Sun.COM 			    path, d->d_name);
18658735SEnrico.Perla@Sun.COM 			if (stat(tpath, &sb) == 0) {
18668735SEnrico.Perla@Sun.COM 				if (sb.st_mode & S_IFDIR)
18678735SEnrico.Perla@Sun.COM 					(void) rmdir_r(tpath);
18688735SEnrico.Perla@Sun.COM 				else
18698735SEnrico.Perla@Sun.COM 					(void) remove(tpath);
18708735SEnrico.Perla@Sun.COM 			}
18718735SEnrico.Perla@Sun.COM 		}
18728735SEnrico.Perla@Sun.COM 	}
18738735SEnrico.Perla@Sun.COM 	return (remove(path));
18748735SEnrico.Perla@Sun.COM }
18758735SEnrico.Perla@Sun.COM 
18768735SEnrico.Perla@Sun.COM /*
18778735SEnrico.Perla@Sun.COM  * Check if cache directory exists and, if not, create it and update flags
18788735SEnrico.Perla@Sun.COM  * accordingly. If the path exists, but it's not a directory, a best effort
18798735SEnrico.Perla@Sun.COM  * attempt to remove and recreate it is made.
18808735SEnrico.Perla@Sun.COM  * If the user requested a 'purge', always recreate the directory from scratch.
18818735SEnrico.Perla@Sun.COM  */
18828735SEnrico.Perla@Sun.COM static int
18838735SEnrico.Perla@Sun.COM set_cache_dir(char *root, int what)
18848735SEnrico.Perla@Sun.COM {
18858735SEnrico.Perla@Sun.COM 	struct stat	sb;
18868735SEnrico.Perla@Sun.COM 	int		ret = 0;
18878735SEnrico.Perla@Sun.COM 
18888735SEnrico.Perla@Sun.COM 	ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
18898735SEnrico.Perla@Sun.COM 	    "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
18908735SEnrico.Perla@Sun.COM 	    "/amd64" : "", CACHEDIR_SUFFIX);
18918735SEnrico.Perla@Sun.COM 
18928735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (get_cachedir(what))) {
18938735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
18948735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
18958735SEnrico.Perla@Sun.COM 	}
18968735SEnrico.Perla@Sun.COM 
18978735SEnrico.Perla@Sun.COM 	if (bam_purge)
18988735SEnrico.Perla@Sun.COM 		(void) rmdir_r(get_cachedir(what));
18998735SEnrico.Perla@Sun.COM 
19008735SEnrico.Perla@Sun.COM 	if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
19018735SEnrico.Perla@Sun.COM 		/* best effort unlink attempt, mkdir will catch errors */
19028735SEnrico.Perla@Sun.COM 		(void) unlink(get_cachedir(what));
19038735SEnrico.Perla@Sun.COM 
19048735SEnrico.Perla@Sun.COM 		if (bam_verbose)
19058735SEnrico.Perla@Sun.COM 			bam_print(UPDATE_CDIR_MISS, get_cachedir(what));
19068735SEnrico.Perla@Sun.COM 		ret = mkdir(get_cachedir(what), DIR_PERMS);
19078735SEnrico.Perla@Sun.COM 		if (ret < 0) {
19088735SEnrico.Perla@Sun.COM 			bam_error(MKDIR_FAILED, get_cachedir(what),
19098735SEnrico.Perla@Sun.COM 			    strerror(errno));
19108735SEnrico.Perla@Sun.COM 			get_cachedir(what)[0] = '\0';
19118735SEnrico.Perla@Sun.COM 			return (ret);
19128735SEnrico.Perla@Sun.COM 		}
19138735SEnrico.Perla@Sun.COM 		set_flag(NEED_CACHE_DIR);
19148735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
19158735SEnrico.Perla@Sun.COM 	}
19168735SEnrico.Perla@Sun.COM 
19178735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
19188735SEnrico.Perla@Sun.COM }
19198735SEnrico.Perla@Sun.COM 
19208735SEnrico.Perla@Sun.COM static int
19218735SEnrico.Perla@Sun.COM set_update_dir(char *root, int what)
19228735SEnrico.Perla@Sun.COM {
19238735SEnrico.Perla@Sun.COM 	struct stat	sb;
19248735SEnrico.Perla@Sun.COM 	int		ret;
19258735SEnrico.Perla@Sun.COM 
19268735SEnrico.Perla@Sun.COM 	if (is_dir_flag_on(what, NO_MULTI))
19278735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
19288735SEnrico.Perla@Sun.COM 
19298735SEnrico.Perla@Sun.COM 	if (!bam_extend) {
19308735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
19318735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
19328735SEnrico.Perla@Sun.COM 	}
19338735SEnrico.Perla@Sun.COM 
19348735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
19358735SEnrico.Perla@Sun.COM 		ret = snprintf(get_updatedir(what),
19368735SEnrico.Perla@Sun.COM 		    sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
19378735SEnrico.Perla@Sun.COM 		    ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
19388735SEnrico.Perla@Sun.COM 	else
19398735SEnrico.Perla@Sun.COM 		ret = snprintf(get_updatedir(what),
19408735SEnrico.Perla@Sun.COM 		    sizeof (get_updatedir(what)), "%s%s%s%s", root,
19418735SEnrico.Perla@Sun.COM 		    ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
19428735SEnrico.Perla@Sun.COM 
19438735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (get_updatedir(what))) {
19448735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
19458735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
19468735SEnrico.Perla@Sun.COM 	}
19478735SEnrico.Perla@Sun.COM 
19488735SEnrico.Perla@Sun.COM 	if (stat(get_updatedir(what), &sb) == 0) {
19498735SEnrico.Perla@Sun.COM 		if (S_ISDIR(sb.st_mode))
19508735SEnrico.Perla@Sun.COM 			ret = rmdir_r(get_updatedir(what));
19518735SEnrico.Perla@Sun.COM 		else
19528735SEnrico.Perla@Sun.COM 			ret = unlink(get_updatedir(what));
19538735SEnrico.Perla@Sun.COM 
19548735SEnrico.Perla@Sun.COM 		if (ret != 0)
19558735SEnrico.Perla@Sun.COM 			set_dir_flag(what, NO_MULTI);
19568735SEnrico.Perla@Sun.COM 	}
19578735SEnrico.Perla@Sun.COM 
19588735SEnrico.Perla@Sun.COM 	if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
19598735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
19608735SEnrico.Perla@Sun.COM 
19618735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
19628735SEnrico.Perla@Sun.COM }
19638735SEnrico.Perla@Sun.COM 
19648735SEnrico.Perla@Sun.COM static int
19658735SEnrico.Perla@Sun.COM is_valid_archive(char *root, int what)
19668735SEnrico.Perla@Sun.COM {
19678735SEnrico.Perla@Sun.COM 	char 		archive_path[PATH_MAX];
19688735SEnrico.Perla@Sun.COM 	struct stat 	sb;
19698735SEnrico.Perla@Sun.COM 	int 		ret;
19708735SEnrico.Perla@Sun.COM 
19718735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
19728735SEnrico.Perla@Sun.COM 		ret = snprintf(archive_path, sizeof (archive_path),
19738735SEnrico.Perla@Sun.COM 		    "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
19748735SEnrico.Perla@Sun.COM 		    ARCHIVE_SUFFIX);
19758735SEnrico.Perla@Sun.COM 	else
19768735SEnrico.Perla@Sun.COM 		ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
19778735SEnrico.Perla@Sun.COM 		    root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
19788735SEnrico.Perla@Sun.COM 
19798735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (archive_path)) {
19808735SEnrico.Perla@Sun.COM 		bam_error(PATH_TOO_LONG, rootbuf);
19818735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
19828735SEnrico.Perla@Sun.COM 	}
19838735SEnrico.Perla@Sun.COM 
19848735SEnrico.Perla@Sun.COM 	if (stat(archive_path, &sb) != 0) {
19858735SEnrico.Perla@Sun.COM 		if (bam_verbose && !bam_check)
19868735SEnrico.Perla@Sun.COM 			bam_print(UPDATE_ARCH_MISS, archive_path);
19878735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NEED_UPDATE);
19888735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
19898735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
19908735SEnrico.Perla@Sun.COM 	}
19918735SEnrico.Perla@Sun.COM 
19928735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET))
19938735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
19948735SEnrico.Perla@Sun.COM 
19958735SEnrico.Perla@Sun.COM 	if (bam_extend && sb.st_size > BA_SIZE_MAX) {
19968735SEnrico.Perla@Sun.COM 		if (bam_verbose && !bam_check)
19978735SEnrico.Perla@Sun.COM 			bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX);
19988735SEnrico.Perla@Sun.COM 		set_dir_flag(what, NO_MULTI);
19998735SEnrico.Perla@Sun.COM 	}
20008735SEnrico.Perla@Sun.COM 
20018735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
20028735SEnrico.Perla@Sun.COM }
20038735SEnrico.Perla@Sun.COM 
20048735SEnrico.Perla@Sun.COM /*
20058735SEnrico.Perla@Sun.COM  * Check flags and presence of required files and directories.
20060Sstevel@tonic-gate  * The force flag and/or absence of files should
20070Sstevel@tonic-gate  * trigger an update.
20080Sstevel@tonic-gate  * Suppress stdout output if check (-n) option is set
20090Sstevel@tonic-gate  * (as -n should only produce parseable output.)
20100Sstevel@tonic-gate  */
20118735SEnrico.Perla@Sun.COM static int
20120Sstevel@tonic-gate check_flags_and_files(char *root)
20130Sstevel@tonic-gate {
20148735SEnrico.Perla@Sun.COM 
20158735SEnrico.Perla@Sun.COM 	struct stat 	sb;
20168735SEnrico.Perla@Sun.COM 	int 		ret;
20178735SEnrico.Perla@Sun.COM 
20188735SEnrico.Perla@Sun.COM 	/*
20198735SEnrico.Perla@Sun.COM 	 * If archive is missing, create archive
20208735SEnrico.Perla@Sun.COM 	 */
20218735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET)) {
20228735SEnrico.Perla@Sun.COM 		ret = is_valid_archive(root, FILE64);
20238735SEnrico.Perla@Sun.COM 	} else {
20248735SEnrico.Perla@Sun.COM 		int	what = FILE32;
20258735SEnrico.Perla@Sun.COM 		do {
20268735SEnrico.Perla@Sun.COM 			ret = is_valid_archive(root, what);
20278735SEnrico.Perla@Sun.COM 			if (ret == BAM_ERROR)
20288735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
20298735SEnrico.Perla@Sun.COM 			what++;
20308735SEnrico.Perla@Sun.COM 		} while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
20318735SEnrico.Perla@Sun.COM 	}
20328735SEnrico.Perla@Sun.COM 
20338735SEnrico.Perla@Sun.COM 	if (bam_nowrite())
20348735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
20358735SEnrico.Perla@Sun.COM 
20368735SEnrico.Perla@Sun.COM 
20378735SEnrico.Perla@Sun.COM 	/*
20388735SEnrico.Perla@Sun.COM 	 * check if cache directories exist on x86.
20398735SEnrico.Perla@Sun.COM 	 * check (and always open) the cache file on SPARC.
20408735SEnrico.Perla@Sun.COM 	 */
20418735SEnrico.Perla@Sun.COM 	if (is_sparc()) {
20428735SEnrico.Perla@Sun.COM 		ret = snprintf(get_cachedir(FILE64),
20438735SEnrico.Perla@Sun.COM 		    sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
20448735SEnrico.Perla@Sun.COM 		    ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
20458735SEnrico.Perla@Sun.COM 
20468735SEnrico.Perla@Sun.COM 		if (ret >= sizeof (get_cachedir(FILE64))) {
20478735SEnrico.Perla@Sun.COM 			bam_error(PATH_TOO_LONG, rootbuf);
20488735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
20498735SEnrico.Perla@Sun.COM 		}
20508735SEnrico.Perla@Sun.COM 
20518735SEnrico.Perla@Sun.COM 		if (stat(get_cachedir(FILE64), &sb) != 0) {
20528735SEnrico.Perla@Sun.COM 			set_flag(NEED_CACHE_DIR);
20538735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE64, NEED_UPDATE);
20548735SEnrico.Perla@Sun.COM 		}
20558735SEnrico.Perla@Sun.COM 
20568735SEnrico.Perla@Sun.COM 		walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
20578735SEnrico.Perla@Sun.COM 		if (walk_arg.sparcfile == NULL) {
20588735SEnrico.Perla@Sun.COM 			bam_error(OPEN_FAIL, get_cachedir(FILE64),
20598735SEnrico.Perla@Sun.COM 			    strerror(errno));
20608735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
20618735SEnrico.Perla@Sun.COM 		}
20628735SEnrico.Perla@Sun.COM 
20638735SEnrico.Perla@Sun.COM 		set_dir_present(FILE64);
20648735SEnrico.Perla@Sun.COM 	} else {
20658735SEnrico.Perla@Sun.COM 		int	what = FILE32;
20668735SEnrico.Perla@Sun.COM 
20678735SEnrico.Perla@Sun.COM 		do {
20688735SEnrico.Perla@Sun.COM 			if (set_cache_dir(root, what) != 0)
20698735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
20708735SEnrico.Perla@Sun.COM 
20718735SEnrico.Perla@Sun.COM 			set_dir_present(what);
20728735SEnrico.Perla@Sun.COM 
20738735SEnrico.Perla@Sun.COM 			if (set_update_dir(root, what) != 0)
20748735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
20758735SEnrico.Perla@Sun.COM 			what++;
20768735SEnrico.Perla@Sun.COM 		} while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
20778735SEnrico.Perla@Sun.COM 	}
20780Sstevel@tonic-gate 
20790Sstevel@tonic-gate 	/*
20800Sstevel@tonic-gate 	 * if force, create archive unconditionally
20810Sstevel@tonic-gate 	 */
20820Sstevel@tonic-gate 	if (bam_force) {
20838735SEnrico.Perla@Sun.COM 		if (!is_sparc())
20848735SEnrico.Perla@Sun.COM 			set_dir_flag(FILE32, NEED_UPDATE);
20858735SEnrico.Perla@Sun.COM 		set_dir_flag(FILE64, NEED_UPDATE);
20868735SEnrico.Perla@Sun.COM 		if (bam_verbose)
20870Sstevel@tonic-gate 			bam_print(UPDATE_FORCE);
20888735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
20898735SEnrico.Perla@Sun.COM 	}
20908735SEnrico.Perla@Sun.COM 
20918735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
20920Sstevel@tonic-gate }
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate static error_t
20950Sstevel@tonic-gate read_one_list(char *root, filelist_t  *flistp, char *filelist)
20960Sstevel@tonic-gate {
20978735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
20988735SEnrico.Perla@Sun.COM 	FILE 		*fp;
20998735SEnrico.Perla@Sun.COM 	char 		buf[BAM_MAXLINE];
21008735SEnrico.Perla@Sun.COM 	const char 	*fcn = "read_one_list()";
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, filelist);
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 	fp = fopen(path, "r");
21050Sstevel@tonic-gate 	if (fp == NULL) {
21066448Svikram 		BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
21070Sstevel@tonic-gate 		return (BAM_ERROR);
21080Sstevel@tonic-gate 	}
21090Sstevel@tonic-gate 	while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2110316Svikram 		/* skip blank lines */
2111316Svikram 		if (strspn(buf, " \t") == strlen(buf))
2112316Svikram 			continue;
21130Sstevel@tonic-gate 		append_to_flist(flistp, buf);
21140Sstevel@tonic-gate 	}
21150Sstevel@tonic-gate 	if (fclose(fp) != 0) {
21160Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, path, strerror(errno));
21170Sstevel@tonic-gate 		return (BAM_ERROR);
21180Sstevel@tonic-gate 	}
21190Sstevel@tonic-gate 	return (BAM_SUCCESS);
21200Sstevel@tonic-gate }
21210Sstevel@tonic-gate 
21220Sstevel@tonic-gate static error_t
21230Sstevel@tonic-gate read_list(char *root, filelist_t  *flistp)
21240Sstevel@tonic-gate {
21258735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
21268735SEnrico.Perla@Sun.COM 	char 		cmd[PATH_MAX];
21278735SEnrico.Perla@Sun.COM 	struct stat 	sb;
21288735SEnrico.Perla@Sun.COM 	int 		n, rval;
21298735SEnrico.Perla@Sun.COM 	const char 	*fcn = "read_list()";
21300Sstevel@tonic-gate 
21310Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 	/*
21345648Ssetje 	 * build and check path to extract_boot_filelist.ksh
21355648Ssetje 	 */
21365648Ssetje 	n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
21375648Ssetje 	if (n >= sizeof (path)) {
21385648Ssetje 		bam_error(NO_FLIST);
21395648Ssetje 		return (BAM_ERROR);
21405648Ssetje 	}
21415648Ssetje 
21425648Ssetje 	/*
21435648Ssetje 	 * If extract_boot_filelist is present, exec it, otherwise read
21445648Ssetje 	 * the filelists directly, for compatibility with older images.
21450Sstevel@tonic-gate 	 */
21465648Ssetje 	if (stat(path, &sb) == 0) {
21475648Ssetje 		/*
21485648Ssetje 		 * build arguments to exec extract_boot_filelist.ksh
21495648Ssetje 		 */
21506319Sjg 		char *rootarg, *platarg;
21516319Sjg 		int platarglen = 1, rootarglen = 1;
21526319Sjg 		if (strlen(root) > 1)
21536319Sjg 			rootarglen += strlen(root) + strlen("-R ");
21546319Sjg 		if (bam_alt_platform)
21556319Sjg 			platarglen += strlen(bam_platform) + strlen("-p ");
21566319Sjg 		platarg = s_calloc(1, platarglen);
21576319Sjg 		rootarg = s_calloc(1, rootarglen);
21586319Sjg 		*platarg = 0;
21596319Sjg 		*rootarg = 0;
21606319Sjg 
21615648Ssetje 		if (strlen(root) > 1) {
21626319Sjg 			(void) snprintf(rootarg, rootarglen,
21636319Sjg 			    "-R %s", root);
21645648Ssetje 		}
21656319Sjg 		if (bam_alt_platform) {
21666319Sjg 			(void) snprintf(platarg, platarglen,
21676319Sjg 			    "-p %s", bam_platform);
21686319Sjg 		}
21696319Sjg 		n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
21706319Sjg 		    path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
21716319Sjg 		free(platarg);
21726319Sjg 		free(rootarg);
21735648Ssetje 		if (n >= sizeof (cmd)) {
21745648Ssetje 			bam_error(NO_FLIST);
21755648Ssetje 			return (BAM_ERROR);
21765648Ssetje 		}
21775648Ssetje 		if (exec_cmd(cmd, flistp) != 0) {
21786448Svikram 			BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
21795648Ssetje 			return (BAM_ERROR);
21805648Ssetje 		}
21815648Ssetje 	} else {
21825648Ssetje 		/*
21835648Ssetje 		 * Read current lists of files - only the first is mandatory
21845648Ssetje 		 */
21855648Ssetje 		rval = read_one_list(root, flistp, BOOT_FILE_LIST);
21865648Ssetje 		if (rval != BAM_SUCCESS)
21875648Ssetje 			return (rval);
21885648Ssetje 		(void) read_one_list(root, flistp, ETC_FILE_LIST);
21895648Ssetje 	}
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate 	if (flistp->head == NULL) {
21920Sstevel@tonic-gate 		bam_error(NO_FLIST);
21930Sstevel@tonic-gate 		return (BAM_ERROR);
21940Sstevel@tonic-gate 	}
21950Sstevel@tonic-gate 
21960Sstevel@tonic-gate 	return (BAM_SUCCESS);
21970Sstevel@tonic-gate }
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate static void
22000Sstevel@tonic-gate getoldstat(char *root)
22010Sstevel@tonic-gate {
22028735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
22038735SEnrico.Perla@Sun.COM 	int 		fd, error;
22048735SEnrico.Perla@Sun.COM 	struct stat 	sb;
22058735SEnrico.Perla@Sun.COM 	char 		*ostat;
22060Sstevel@tonic-gate 
22070Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
22080Sstevel@tonic-gate 	fd = open(path, O_RDONLY);
22090Sstevel@tonic-gate 	if (fd == -1) {
22100Sstevel@tonic-gate 		if (bam_verbose)
22110Sstevel@tonic-gate 			bam_print(OPEN_FAIL, path, strerror(errno));
22128735SEnrico.Perla@Sun.COM 		goto out_err;
22130Sstevel@tonic-gate 	}
22140Sstevel@tonic-gate 
22150Sstevel@tonic-gate 	if (fstat(fd, &sb) != 0) {
22160Sstevel@tonic-gate 		bam_error(STAT_FAIL, path, strerror(errno));
22178735SEnrico.Perla@Sun.COM 		goto out_err;
22180Sstevel@tonic-gate 	}
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate 	ostat = s_calloc(1, sb.st_size);
22210Sstevel@tonic-gate 
22220Sstevel@tonic-gate 	if (read(fd, ostat, sb.st_size) != sb.st_size) {
22230Sstevel@tonic-gate 		bam_error(READ_FAIL, path, strerror(errno));
22240Sstevel@tonic-gate 		free(ostat);
22258735SEnrico.Perla@Sun.COM 		goto out_err;
22260Sstevel@tonic-gate 	}
22270Sstevel@tonic-gate 
22280Sstevel@tonic-gate 	(void) close(fd);
22298735SEnrico.Perla@Sun.COM 	fd = -1;
22300Sstevel@tonic-gate 
22310Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
22320Sstevel@tonic-gate 	error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
22330Sstevel@tonic-gate 
22340Sstevel@tonic-gate 	free(ostat);
22350Sstevel@tonic-gate 
22360Sstevel@tonic-gate 	if (error) {
22370Sstevel@tonic-gate 		bam_error(UNPACK_FAIL, path, strerror(error));
22380Sstevel@tonic-gate 		walk_arg.old_nvlp = NULL;
22398735SEnrico.Perla@Sun.COM 		goto out_err;
22408735SEnrico.Perla@Sun.COM 	} else {
22410Sstevel@tonic-gate 		return;
22420Sstevel@tonic-gate 	}
22438735SEnrico.Perla@Sun.COM 
22448735SEnrico.Perla@Sun.COM out_err:
22458735SEnrico.Perla@Sun.COM 	if (fd != -1)
22468735SEnrico.Perla@Sun.COM 		(void) close(fd);
22478735SEnrico.Perla@Sun.COM 	set_dir_flag(FILE32, NEED_UPDATE);
22488735SEnrico.Perla@Sun.COM 	set_dir_flag(FILE64, NEED_UPDATE);
22498735SEnrico.Perla@Sun.COM }
22508735SEnrico.Perla@Sun.COM 
22518735SEnrico.Perla@Sun.COM /* Best effort stale entry removal */
22528735SEnrico.Perla@Sun.COM static void
22538735SEnrico.Perla@Sun.COM delete_stale(char *file, int what)
22548735SEnrico.Perla@Sun.COM {
22558735SEnrico.Perla@Sun.COM 	char		path[PATH_MAX];
22568735SEnrico.Perla@Sun.COM 	struct stat	sb;
22578735SEnrico.Perla@Sun.COM 
22588735SEnrico.Perla@Sun.COM 	(void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
22598735SEnrico.Perla@Sun.COM 	if (!bam_check && stat(path, &sb) == 0) {
22608735SEnrico.Perla@Sun.COM 		if (sb.st_mode & S_IFDIR)
22618735SEnrico.Perla@Sun.COM 			(void) rmdir_r(path);
22628735SEnrico.Perla@Sun.COM 		else
22638735SEnrico.Perla@Sun.COM 			(void) unlink(path);
22648735SEnrico.Perla@Sun.COM 
22658735SEnrico.Perla@Sun.COM 		set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
22668735SEnrico.Perla@Sun.COM 	}
22678735SEnrico.Perla@Sun.COM 	if (bam_verbose)
22688735SEnrico.Perla@Sun.COM 		bam_print(PARSEABLE_STALE_FILE, path);
22690Sstevel@tonic-gate }
22700Sstevel@tonic-gate 
22712583Svikram /*
22722583Svikram  * Checks if a file in the current (old) archive has
22732583Svikram  * been deleted from the root filesystem. This is needed for
22742583Svikram  * software like Trusted Extensions (TX) that switch early
22752583Svikram  * in boot based on presence/absence of a kernel module.
22762583Svikram  */
22772583Svikram static void
22782583Svikram check4stale(char *root)
22792583Svikram {
22802583Svikram 	nvpair_t	*nvp;
22812583Svikram 	nvlist_t	*nvlp;
22822583Svikram 	char 		*file;
22832583Svikram 	char		path[PATH_MAX];
22842583Svikram 
22852583Svikram 	/*
22862583Svikram 	 * Skip stale file check during smf check
22872583Svikram 	 */
22882583Svikram 	if (bam_smf_check)
22892583Svikram 		return;
22902583Svikram 
22918735SEnrico.Perla@Sun.COM 	/*
22928735SEnrico.Perla@Sun.COM 	 * If we need to (re)create the cache, there's no need to check for
22938735SEnrico.Perla@Sun.COM 	 * stale files
22948735SEnrico.Perla@Sun.COM 	 */
22958735SEnrico.Perla@Sun.COM 	if (is_flag_on(NEED_CACHE_DIR))
22968735SEnrico.Perla@Sun.COM 		return;
22978735SEnrico.Perla@Sun.COM 
22982583Svikram 	/* Nothing to do if no old stats */
22992583Svikram 	if ((nvlp = walk_arg.old_nvlp) == NULL)
23002583Svikram 		return;
23012583Svikram 
23022583Svikram 	for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
23032583Svikram 	    nvp = nvlist_next_nvpair(nvlp, nvp)) {
23042583Svikram 		file = nvpair_name(nvp);
23052583Svikram 		if (file == NULL)
23062583Svikram 			continue;
23072583Svikram 		(void) snprintf(path, sizeof (path), "%s/%s",
23082583Svikram 		    root, file);
23098735SEnrico.Perla@Sun.COM 		if (access(path, F_OK) < 0) {
23108735SEnrico.Perla@Sun.COM 			int	what;
23118735SEnrico.Perla@Sun.COM 
23128735SEnrico.Perla@Sun.COM 			if (is_flag_on(IS_SPARC_TARGET))
23138735SEnrico.Perla@Sun.COM 				set_dir_flag(FILE64, NEED_UPDATE);
23148735SEnrico.Perla@Sun.COM 
23158735SEnrico.Perla@Sun.COM 			for (what = FILE32; what < CACHEDIR_NUM; what++)
23168735SEnrico.Perla@Sun.COM 				if (has_cachedir(what))
23178735SEnrico.Perla@Sun.COM 					delete_stale(file, what);
23182583Svikram 		}
23192583Svikram 	}
23202583Svikram }
23212583Svikram 
23220Sstevel@tonic-gate static void
23230Sstevel@tonic-gate create_newstat(void)
23240Sstevel@tonic-gate {
23250Sstevel@tonic-gate 	int error;
23260Sstevel@tonic-gate 
23270Sstevel@tonic-gate 	error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
23280Sstevel@tonic-gate 	if (error) {
23290Sstevel@tonic-gate 		/*
23300Sstevel@tonic-gate 		 * Not fatal - we can still create archive
23310Sstevel@tonic-gate 		 */
23320Sstevel@tonic-gate 		walk_arg.new_nvlp = NULL;
23330Sstevel@tonic-gate 		bam_error(NVALLOC_FAIL, strerror(error));
23340Sstevel@tonic-gate 	}
23350Sstevel@tonic-gate }
23360Sstevel@tonic-gate 
23378735SEnrico.Perla@Sun.COM static int
23380Sstevel@tonic-gate walk_list(char *root, filelist_t *flistp)
23390Sstevel@tonic-gate {
23400Sstevel@tonic-gate 	char path[PATH_MAX];
23410Sstevel@tonic-gate 	line_t *lp;
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 	for (lp = flistp->head; lp; lp = lp->next) {
23445648Ssetje 		/*
23455648Ssetje 		 * Don't follow symlinks.  A symlink must refer to
23465648Ssetje 		 * a file that would appear in the archive through
23475648Ssetje 		 * a direct reference.  This matches the archive
23485648Ssetje 		 * construction behavior.
23495648Ssetje 		 */
23500Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
23515648Ssetje 		if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
23528735SEnrico.Perla@Sun.COM 			if (is_flag_on(UPDATE_ERROR))
23538735SEnrico.Perla@Sun.COM 				return (BAM_ERROR);
23540Sstevel@tonic-gate 			/*
23550Sstevel@tonic-gate 			 * Some files may not exist.
23560Sstevel@tonic-gate 			 * For example: etc/rtc_config on a x86 diskless system
23570Sstevel@tonic-gate 			 * Emit verbose message only
23580Sstevel@tonic-gate 			 */
23590Sstevel@tonic-gate 			if (bam_verbose)
23600Sstevel@tonic-gate 				bam_print(NFTW_FAIL, path, strerror(errno));
23610Sstevel@tonic-gate 		}
23620Sstevel@tonic-gate 	}
23638735SEnrico.Perla@Sun.COM 
23648735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
23650Sstevel@tonic-gate }
23660Sstevel@tonic-gate 
23670Sstevel@tonic-gate static void
23680Sstevel@tonic-gate savenew(char *root)
23690Sstevel@tonic-gate {
23708735SEnrico.Perla@Sun.COM 	char 	path[PATH_MAX];
23718735SEnrico.Perla@Sun.COM 	char 	path2[PATH_MAX];
23728735SEnrico.Perla@Sun.COM 	size_t 	sz;
23738735SEnrico.Perla@Sun.COM 	char 	*nstat;
23748735SEnrico.Perla@Sun.COM 	int 	fd, wrote, error;
23750Sstevel@tonic-gate 
23760Sstevel@tonic-gate 	nstat = NULL;
23770Sstevel@tonic-gate 	sz = 0;
23780Sstevel@tonic-gate 	error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
23790Sstevel@tonic-gate 	    NV_ENCODE_XDR, 0);
23800Sstevel@tonic-gate 	if (error) {
23810Sstevel@tonic-gate 		bam_error(PACK_FAIL, strerror(error));
23820Sstevel@tonic-gate 		return;
23830Sstevel@tonic-gate 	}
23840Sstevel@tonic-gate 
23850Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
23860Sstevel@tonic-gate 	fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
23870Sstevel@tonic-gate 	if (fd == -1) {
23880Sstevel@tonic-gate 		bam_error(OPEN_FAIL, path, strerror(errno));
23890Sstevel@tonic-gate 		free(nstat);
23900Sstevel@tonic-gate 		return;
23910Sstevel@tonic-gate 	}
23920Sstevel@tonic-gate 	wrote = write(fd, nstat, sz);
23930Sstevel@tonic-gate 	if (wrote != sz) {
23940Sstevel@tonic-gate 		bam_error(WRITE_FAIL, path, strerror(errno));
23950Sstevel@tonic-gate 		(void) close(fd);
23960Sstevel@tonic-gate 		free(nstat);
23970Sstevel@tonic-gate 		return;
23980Sstevel@tonic-gate 	}
23990Sstevel@tonic-gate 	(void) close(fd);
24000Sstevel@tonic-gate 	free(nstat);
24010Sstevel@tonic-gate 
24020Sstevel@tonic-gate 	(void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
24030Sstevel@tonic-gate 	if (rename(path, path2) != 0) {
24040Sstevel@tonic-gate 		bam_error(RENAME_FAIL, path2, strerror(errno));
24050Sstevel@tonic-gate 	}
24060Sstevel@tonic-gate }
24070Sstevel@tonic-gate 
24088735SEnrico.Perla@Sun.COM #define	init_walk_args()	bzero(&walk_arg, sizeof (walk_arg))
24098735SEnrico.Perla@Sun.COM 
24100Sstevel@tonic-gate static void
24110Sstevel@tonic-gate clear_walk_args(void)
24120Sstevel@tonic-gate {
24130Sstevel@tonic-gate 	if (walk_arg.old_nvlp)
24140Sstevel@tonic-gate 		nvlist_free(walk_arg.old_nvlp);
24150Sstevel@tonic-gate 	if (walk_arg.new_nvlp)
24160Sstevel@tonic-gate 		nvlist_free(walk_arg.new_nvlp);
24178735SEnrico.Perla@Sun.COM 	if (walk_arg.sparcfile)
24188735SEnrico.Perla@Sun.COM 		(void) fclose(walk_arg.sparcfile);
24190Sstevel@tonic-gate 	walk_arg.old_nvlp = NULL;
24200Sstevel@tonic-gate 	walk_arg.new_nvlp = NULL;
24218735SEnrico.Perla@Sun.COM 	walk_arg.sparcfile = NULL;
24220Sstevel@tonic-gate }
24230Sstevel@tonic-gate 
24240Sstevel@tonic-gate /*
24250Sstevel@tonic-gate  * Returns:
24260Sstevel@tonic-gate  *	0 - no update necessary
24270Sstevel@tonic-gate  *	1 - update required.
24280Sstevel@tonic-gate  *	BAM_ERROR (-1) - An error occurred
24290Sstevel@tonic-gate  *
24300Sstevel@tonic-gate  * Special handling for check (-n):
24310Sstevel@tonic-gate  * ================================
24320Sstevel@tonic-gate  * The check (-n) option produces parseable output.
24330Sstevel@tonic-gate  * To do this, we suppress all stdout messages unrelated
24340Sstevel@tonic-gate  * to out of sync files.
24350Sstevel@tonic-gate  * All stderr messages are still printed though.
24360Sstevel@tonic-gate  *
24370Sstevel@tonic-gate  */
24380Sstevel@tonic-gate static int
24390Sstevel@tonic-gate update_required(char *root)
24400Sstevel@tonic-gate {
24418735SEnrico.Perla@Sun.COM 	struct stat 	sb;
24428735SEnrico.Perla@Sun.COM 	char 		path[PATH_MAX];
24438735SEnrico.Perla@Sun.COM 	filelist_t 	flist;
24448735SEnrico.Perla@Sun.COM 	filelist_t 	*flistp = &flist;
24458735SEnrico.Perla@Sun.COM 	int 		ret;
24460Sstevel@tonic-gate 
24470Sstevel@tonic-gate 	flistp->head = flistp->tail = NULL;
24480Sstevel@tonic-gate 
24498735SEnrico.Perla@Sun.COM 	if (is_sparc())
24508735SEnrico.Perla@Sun.COM 		set_flag(IS_SPARC_TARGET);
24510Sstevel@tonic-gate 
24520Sstevel@tonic-gate 	/*
24538735SEnrico.Perla@Sun.COM 	 * Check if cache directories and archives are present
24540Sstevel@tonic-gate 	 */
24558735SEnrico.Perla@Sun.COM 
24568735SEnrico.Perla@Sun.COM 	ret = check_flags_and_files(root);
24578735SEnrico.Perla@Sun.COM 	if (ret < 0)
24588735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
24590Sstevel@tonic-gate 
24600Sstevel@tonic-gate 	/*
24610Sstevel@tonic-gate 	 * In certain deployment scenarios, filestat may not
24620Sstevel@tonic-gate 	 * exist. Ignore it during boot-archive SMF check.
24630Sstevel@tonic-gate 	 */
24640Sstevel@tonic-gate 	if (bam_smf_check) {
24650Sstevel@tonic-gate 		(void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
24660Sstevel@tonic-gate 		if (stat(path, &sb) != 0)
24670Sstevel@tonic-gate 			return (0);
24680Sstevel@tonic-gate 	}
24690Sstevel@tonic-gate 
24708735SEnrico.Perla@Sun.COM 	getoldstat(root);
24710Sstevel@tonic-gate 
24720Sstevel@tonic-gate 	/*
24732583Svikram 	 * Check if the archive contains files that are no longer
24742583Svikram 	 * present on the root filesystem.
24752583Svikram 	 */
24768735SEnrico.Perla@Sun.COM 	check4stale(root);
24772583Svikram 
24782583Svikram 	/*
24790Sstevel@tonic-gate 	 * read list of files
24800Sstevel@tonic-gate 	 */
24810Sstevel@tonic-gate 	if (read_list(root, flistp) != BAM_SUCCESS) {
24820Sstevel@tonic-gate 		clear_walk_args();
24830Sstevel@tonic-gate 		return (BAM_ERROR);
24840Sstevel@tonic-gate 	}
24850Sstevel@tonic-gate 
24860Sstevel@tonic-gate 	assert(flistp->head && flistp->tail);
24870Sstevel@tonic-gate 
24880Sstevel@tonic-gate 	/*
24890Sstevel@tonic-gate 	 * At this point either the update is required
24900Sstevel@tonic-gate 	 * or the decision is pending. In either case
24910Sstevel@tonic-gate 	 * we need to create new stat nvlist
24920Sstevel@tonic-gate 	 */
24930Sstevel@tonic-gate 	create_newstat();
24940Sstevel@tonic-gate 	/*
24950Sstevel@tonic-gate 	 * This walk does 2 things:
24960Sstevel@tonic-gate 	 *  	- gets new stat data for every file
24970Sstevel@tonic-gate 	 *	- (optional) compare old and new stat data
24980Sstevel@tonic-gate 	 */
24998735SEnrico.Perla@Sun.COM 	ret = walk_list(root, &flist);
25000Sstevel@tonic-gate 
25010Sstevel@tonic-gate 	/* done with the file list */
25020Sstevel@tonic-gate 	filelist_free(flistp);
25030Sstevel@tonic-gate 
25048735SEnrico.Perla@Sun.COM 	/* something went wrong */
25058735SEnrico.Perla@Sun.COM 
25068735SEnrico.Perla@Sun.COM 	if (ret == BAM_ERROR) {
25078735SEnrico.Perla@Sun.COM 		bam_error(CACHE_FAIL);
25088735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
25098735SEnrico.Perla@Sun.COM 	}
25108735SEnrico.Perla@Sun.COM 
25110Sstevel@tonic-gate 	if (walk_arg.new_nvlp == NULL) {
25128735SEnrico.Perla@Sun.COM 		(void) fclose(walk_arg.sparcfile);
25130Sstevel@tonic-gate 		bam_error(NO_NEW_STAT);
25148735SEnrico.Perla@Sun.COM 	}
25158735SEnrico.Perla@Sun.COM 
25168735SEnrico.Perla@Sun.COM 	/* If nothing was updated, discard newstat. */
25178735SEnrico.Perla@Sun.COM 
25188735SEnrico.Perla@Sun.COM 	if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
25198735SEnrico.Perla@Sun.COM 	    !is_dir_flag_on(FILE64, NEED_UPDATE)) {
25200Sstevel@tonic-gate 		clear_walk_args();
25218735SEnrico.Perla@Sun.COM 		return (0);
25228735SEnrico.Perla@Sun.COM 	}
25238735SEnrico.Perla@Sun.COM 
25248735SEnrico.Perla@Sun.COM 	(void) fclose(walk_arg.sparcfile);
25258735SEnrico.Perla@Sun.COM 
25268735SEnrico.Perla@Sun.COM 	return (1);
25278735SEnrico.Perla@Sun.COM }
25288735SEnrico.Perla@Sun.COM 
25298735SEnrico.Perla@Sun.COM /*
25308735SEnrico.Perla@Sun.COM  * Returns 1 if we're dealing with a bfu archive update, 0 otherwise
25318735SEnrico.Perla@Sun.COM  */
25328735SEnrico.Perla@Sun.COM static int
25338735SEnrico.Perla@Sun.COM is_bfu()
25348735SEnrico.Perla@Sun.COM {
25358735SEnrico.Perla@Sun.COM 	char *path;
25368735SEnrico.Perla@Sun.COM 
25378735SEnrico.Perla@Sun.COM 	path = getenv("PATH");
25388735SEnrico.Perla@Sun.COM 	if (path != NULL &&
25398735SEnrico.Perla@Sun.COM 	    strncmp(path, "/tmp/bfubin", strlen("/tmp/bfubin")) == 0) {
25408735SEnrico.Perla@Sun.COM 		struct stat	sb;
25418735SEnrico.Perla@Sun.COM 		if (stat("/tmp/bfubin", &sb) == 0) {
25428735SEnrico.Perla@Sun.COM 			if (!(sb.st_mode & S_IFDIR))
25438735SEnrico.Perla@Sun.COM 				return (0);
25448735SEnrico.Perla@Sun.COM 			if (sb.st_uid != getuid())
25458735SEnrico.Perla@Sun.COM 				return (0);
25468735SEnrico.Perla@Sun.COM 			return (1);
25478735SEnrico.Perla@Sun.COM 		}
25488735SEnrico.Perla@Sun.COM 	}
25498735SEnrico.Perla@Sun.COM 	return (0);
25508735SEnrico.Perla@Sun.COM }
25518735SEnrico.Perla@Sun.COM 
25528735SEnrico.Perla@Sun.COM #define	LOCKFS_PATH	(is_bfu() ? LOCKFS_BFU : LOCKFS_BIN)
25538735SEnrico.Perla@Sun.COM 
25548735SEnrico.Perla@Sun.COM static int
25558735SEnrico.Perla@Sun.COM flushfs(char *root)
25568735SEnrico.Perla@Sun.COM {
25578735SEnrico.Perla@Sun.COM 	char	cmd[PATH_MAX + 30];
25588735SEnrico.Perla@Sun.COM 
25598735SEnrico.Perla@Sun.COM 	(void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
25608735SEnrico.Perla@Sun.COM 	    LOCKFS_PATH, root);
25618735SEnrico.Perla@Sun.COM 
25628735SEnrico.Perla@Sun.COM 	return (exec_cmd(cmd, NULL));
25638735SEnrico.Perla@Sun.COM }
25648735SEnrico.Perla@Sun.COM 
25658735SEnrico.Perla@Sun.COM static int
25668735SEnrico.Perla@Sun.COM do_archive_copy(char *source, char *dest)
25678735SEnrico.Perla@Sun.COM {
25688735SEnrico.Perla@Sun.COM 
25698735SEnrico.Perla@Sun.COM 	(void) sync();
25708735SEnrico.Perla@Sun.COM 
25718735SEnrico.Perla@Sun.COM 	/* the equivalent of mv archive-new-$pid boot_archive */
25728735SEnrico.Perla@Sun.COM 	if (rename(source, dest) != 0)
25738735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
25748735SEnrico.Perla@Sun.COM 
25758735SEnrico.Perla@Sun.COM 	if (flushfs(bam_root) != 0)
25768735SEnrico.Perla@Sun.COM 		(void) sync();
25778735SEnrico.Perla@Sun.COM 
25788735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
25798735SEnrico.Perla@Sun.COM }
25808735SEnrico.Perla@Sun.COM 
25818735SEnrico.Perla@Sun.COM static int
25828735SEnrico.Perla@Sun.COM check_cmdline(filelist_t flist)
25838735SEnrico.Perla@Sun.COM {
25848735SEnrico.Perla@Sun.COM 	line_t	*lp;
25858735SEnrico.Perla@Sun.COM 
25868735SEnrico.Perla@Sun.COM 	for (lp = flist.head; lp; lp = lp->next) {
25878735SEnrico.Perla@Sun.COM 		if (strstr(lp->line, "Error:") != NULL ||
25888735SEnrico.Perla@Sun.COM 		    strstr(lp->line, "Inode number overflow") != NULL) {
25898735SEnrico.Perla@Sun.COM 			(void) fprintf(stderr, "%s", lp->line);
25908735SEnrico.Perla@Sun.COM 			return (BAM_ERROR);
25918735SEnrico.Perla@Sun.COM 		}
25928735SEnrico.Perla@Sun.COM 	}
25938735SEnrico.Perla@Sun.COM 
25948735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
25958735SEnrico.Perla@Sun.COM }
25968735SEnrico.Perla@Sun.COM 
25978735SEnrico.Perla@Sun.COM static int
25988735SEnrico.Perla@Sun.COM check_archive(char *dest)
25998735SEnrico.Perla@Sun.COM {
26008735SEnrico.Perla@Sun.COM 	struct stat	sb;
26018735SEnrico.Perla@Sun.COM 
26028735SEnrico.Perla@Sun.COM 	if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
26038735SEnrico.Perla@Sun.COM 	    sb.st_size < 10000) {
26048735SEnrico.Perla@Sun.COM 		bam_error(ARCHIVE_BAD, dest);
26058735SEnrico.Perla@Sun.COM 		(void) unlink(dest);
26068735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
26078735SEnrico.Perla@Sun.COM 	}
26088735SEnrico.Perla@Sun.COM 
26098735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
26108735SEnrico.Perla@Sun.COM }
26118735SEnrico.Perla@Sun.COM 
26128735SEnrico.Perla@Sun.COM /*
26138735SEnrico.Perla@Sun.COM  * Returns 1 if mkiso is in the expected PATH, 0 otherwise
26148735SEnrico.Perla@Sun.COM  */
26158735SEnrico.Perla@Sun.COM static int
26168735SEnrico.Perla@Sun.COM is_mkisofs()
26178735SEnrico.Perla@Sun.COM {
26188735SEnrico.Perla@Sun.COM 	if (is_bfu()) {
26198735SEnrico.Perla@Sun.COM 		if (access(MKISOFS_BFUBIN, X_OK) == 0)
26208735SEnrico.Perla@Sun.COM 			return (1);
26218735SEnrico.Perla@Sun.COM 	} else {
26228735SEnrico.Perla@Sun.COM 		if (access(MKISOFS_BIN, X_OK) == 0)
26238735SEnrico.Perla@Sun.COM 			return (1);
26248735SEnrico.Perla@Sun.COM 	}
26258735SEnrico.Perla@Sun.COM 	return (0);
26268735SEnrico.Perla@Sun.COM }
26278735SEnrico.Perla@Sun.COM 
26288735SEnrico.Perla@Sun.COM #define	MKISO_PARAMS	" -quiet -graft-points -dlrDJN -relaxed-filenames "
26298735SEnrico.Perla@Sun.COM #define	MKISO_PATH	(is_bfu() ? MKISOFS_BFUBIN : MKISOFS_BIN)
26308735SEnrico.Perla@Sun.COM #define	DD_PATH		(is_bfu() ? DD_PATH_BFU : DD_PATH_USR)
26318735SEnrico.Perla@Sun.COM 
26328735SEnrico.Perla@Sun.COM static int
26338735SEnrico.Perla@Sun.COM create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
26348735SEnrico.Perla@Sun.COM {
26358735SEnrico.Perla@Sun.COM 	int		ret;
26368735SEnrico.Perla@Sun.COM 	char		cmdline[3 * PATH_MAX + 64];
26378735SEnrico.Perla@Sun.COM 	filelist_t	flist = {0};
26388735SEnrico.Perla@Sun.COM 	const char	*func = "create_sparc_archive()";
26398735SEnrico.Perla@Sun.COM 
26408735SEnrico.Perla@Sun.COM 	if (access(bootblk, R_OK) == 1) {
26418735SEnrico.Perla@Sun.COM 		bam_error(BOOTBLK_FAIL, bootblk);
26428735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
26438735SEnrico.Perla@Sun.COM 	}
26448735SEnrico.Perla@Sun.COM 
26458735SEnrico.Perla@Sun.COM 	/*
26468735SEnrico.Perla@Sun.COM 	 * Prepare mkisofs command line and execute it
26478735SEnrico.Perla@Sun.COM 	 */
26488735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
26498735SEnrico.Perla@Sun.COM 	    "-path-list \"%s\" 2>&1", MKISO_PATH, MKISO_PARAMS, bootblk,
26508735SEnrico.Perla@Sun.COM 	    tempname, list);
26518735SEnrico.Perla@Sun.COM 
26528735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
26538735SEnrico.Perla@Sun.COM 
26548735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
26558735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
26568735SEnrico.Perla@Sun.COM 		goto out_err;
26578735SEnrico.Perla@Sun.COM 
26588735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
26598735SEnrico.Perla@Sun.COM 
26608735SEnrico.Perla@Sun.COM 	/*
26618735SEnrico.Perla@Sun.COM 	 * Prepare dd command line to copy the bootblk on the new archive and
26628735SEnrico.Perla@Sun.COM 	 * execute it
26638735SEnrico.Perla@Sun.COM 	 */
26648735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
26658735SEnrico.Perla@Sun.COM 	    " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH,
26668735SEnrico.Perla@Sun.COM 	    bootblk, tempname);
26678735SEnrico.Perla@Sun.COM 
26688735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
26698735SEnrico.Perla@Sun.COM 
26708735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
26718735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
26728735SEnrico.Perla@Sun.COM 		goto out_err;
26738735SEnrico.Perla@Sun.COM 
26748735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
26758735SEnrico.Perla@Sun.COM 
26768735SEnrico.Perla@Sun.COM 	/* Did we get a valid archive ? */
26778735SEnrico.Perla@Sun.COM 	if (check_archive(tempname) == BAM_ERROR)
26788735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
26798735SEnrico.Perla@Sun.COM 
26808735SEnrico.Perla@Sun.COM 	return (do_archive_copy(tempname, archive));
26818735SEnrico.Perla@Sun.COM 
26828735SEnrico.Perla@Sun.COM out_err:
26838735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
26848735SEnrico.Perla@Sun.COM 	bam_error(ARCHIVE_FAIL, cmdline);
26858735SEnrico.Perla@Sun.COM 	(void) unlink(tempname);
26868735SEnrico.Perla@Sun.COM 	return (BAM_ERROR);
26878735SEnrico.Perla@Sun.COM }
26888735SEnrico.Perla@Sun.COM 
26898735SEnrico.Perla@Sun.COM static unsigned int
26908735SEnrico.Perla@Sun.COM from_733(unsigned char *s)
26918735SEnrico.Perla@Sun.COM {
26928735SEnrico.Perla@Sun.COM 	int		i;
26938735SEnrico.Perla@Sun.COM 	unsigned int	ret = 0;
26948735SEnrico.Perla@Sun.COM 
26958735SEnrico.Perla@Sun.COM 	for (i = 0; i < 4; i++)
26968735SEnrico.Perla@Sun.COM 		ret |= s[i] << (8 * i);
26978735SEnrico.Perla@Sun.COM 
26988735SEnrico.Perla@Sun.COM 	return (ret);
26998735SEnrico.Perla@Sun.COM }
27008735SEnrico.Perla@Sun.COM 
27018735SEnrico.Perla@Sun.COM static void
27028735SEnrico.Perla@Sun.COM to_733(unsigned char *s, unsigned int val)
27038735SEnrico.Perla@Sun.COM {
27048735SEnrico.Perla@Sun.COM 	int	i;
27058735SEnrico.Perla@Sun.COM 
27068735SEnrico.Perla@Sun.COM 	for (i = 0; i < 4; i++)
27078735SEnrico.Perla@Sun.COM 		s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
27088735SEnrico.Perla@Sun.COM }
27098735SEnrico.Perla@Sun.COM 
27108735SEnrico.Perla@Sun.COM /*
27118735SEnrico.Perla@Sun.COM  * Extends the current boot archive without recreating it from scratch
27128735SEnrico.Perla@Sun.COM  */
27138735SEnrico.Perla@Sun.COM static int
27148735SEnrico.Perla@Sun.COM extend_iso_archive(char *archive, char *tempname, char *update_dir)
27158735SEnrico.Perla@Sun.COM {
27168735SEnrico.Perla@Sun.COM 	int			fd = -1, newfd = -1, ret, i;
27178735SEnrico.Perla@Sun.COM 	int			next_session = 0, new_size = 0;
27188735SEnrico.Perla@Sun.COM 	char			cmdline[3 * PATH_MAX + 64];
27198735SEnrico.Perla@Sun.COM 	const char		*func = "extend_iso_archive()";
27208735SEnrico.Perla@Sun.COM 	filelist_t		flist = {0};
27218735SEnrico.Perla@Sun.COM 	struct iso_pdesc	saved_desc[MAX_IVDs];
27228735SEnrico.Perla@Sun.COM 
27238735SEnrico.Perla@Sun.COM 	fd = open(archive, O_RDWR);
27248735SEnrico.Perla@Sun.COM 	if (fd == -1) {
27258735SEnrico.Perla@Sun.COM 		bam_error(OPEN_FAIL, archive, strerror(errno));
27268735SEnrico.Perla@Sun.COM 		goto out_err;
27278735SEnrico.Perla@Sun.COM 	}
27280Sstevel@tonic-gate 
27290Sstevel@tonic-gate 	/*
27308735SEnrico.Perla@Sun.COM 	 * A partial read is likely due to a corrupted file
27318735SEnrico.Perla@Sun.COM 	 */
27328735SEnrico.Perla@Sun.COM 	ret = pread64(fd, saved_desc, sizeof (saved_desc),
27338735SEnrico.Perla@Sun.COM 	    VOLDESC_OFF * CD_BLOCK);
27348735SEnrico.Perla@Sun.COM 	if (ret != sizeof (saved_desc)) {
27358735SEnrico.Perla@Sun.COM 		bam_error(READ_FAIL, archive, strerror(errno));
27368735SEnrico.Perla@Sun.COM 		goto out_err;
27378735SEnrico.Perla@Sun.COM 	}
27388735SEnrico.Perla@Sun.COM 
27398735SEnrico.Perla@Sun.COM 	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
27408735SEnrico.Perla@Sun.COM 		bam_error(SIGN_FAIL, archive);
27418735SEnrico.Perla@Sun.COM 		goto out_err;
27428735SEnrico.Perla@Sun.COM 	}
27438735SEnrico.Perla@Sun.COM 
27448735SEnrico.Perla@Sun.COM 	/*
27458735SEnrico.Perla@Sun.COM 	 * Read primary descriptor and locate next_session offset (it should
27468735SEnrico.Perla@Sun.COM 	 * point to the end of the archive)
27470Sstevel@tonic-gate 	 */
27488735SEnrico.Perla@Sun.COM 	next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
27498735SEnrico.Perla@Sun.COM 
27508735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
27518735SEnrico.Perla@Sun.COM 	    "%s\" \"%s\" 2>&1", MKISO_PATH, next_session, archive, MKISO_PARAMS,
27528735SEnrico.Perla@Sun.COM 	    tempname, update_dir);
27538735SEnrico.Perla@Sun.COM 
27548735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
27558735SEnrico.Perla@Sun.COM 
27568735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
27578735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
27588735SEnrico.Perla@Sun.COM 		bam_error(MULTI_FAIL, cmdline);
27598735SEnrico.Perla@Sun.COM 		goto out_flist_err;
27608735SEnrico.Perla@Sun.COM 	}
27618735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
27628735SEnrico.Perla@Sun.COM 
27638735SEnrico.Perla@Sun.COM 	newfd = open(tempname, O_RDONLY);
27648735SEnrico.Perla@Sun.COM 	if (newfd == -1) {
27658735SEnrico.Perla@Sun.COM 		bam_error(OPEN_FAIL, archive, strerror(errno));
27668735SEnrico.Perla@Sun.COM 		goto out_err;
27678735SEnrico.Perla@Sun.COM 	}
27688735SEnrico.Perla@Sun.COM 
27698735SEnrico.Perla@Sun.COM 	ret = pread64(newfd, saved_desc, sizeof (saved_desc),
27708735SEnrico.Perla@Sun.COM 	    VOLDESC_OFF * CD_BLOCK);
27718735SEnrico.Perla@Sun.COM 	if (ret != sizeof (saved_desc)) {
27728735SEnrico.Perla@Sun.COM 		bam_error(READ_FAIL, archive, strerror(errno));
27738735SEnrico.Perla@Sun.COM 		goto out_err;
27748735SEnrico.Perla@Sun.COM 	}
27758735SEnrico.Perla@Sun.COM 
27768735SEnrico.Perla@Sun.COM 	if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
27778735SEnrico.Perla@Sun.COM 		bam_error(SIGN_FAIL, archive);
27788735SEnrico.Perla@Sun.COM 		goto out_err;
27798735SEnrico.Perla@Sun.COM 	}
27808735SEnrico.Perla@Sun.COM 
27818735SEnrico.Perla@Sun.COM 	new_size = from_733(saved_desc[0].volume_space_size) + next_session;
27828735SEnrico.Perla@Sun.COM 	to_733(saved_desc[0].volume_space_size, new_size);
27838735SEnrico.Perla@Sun.COM 
27848735SEnrico.Perla@Sun.COM 	for (i = 1; i < MAX_IVDs; i++) {
27858735SEnrico.Perla@Sun.COM 		if (saved_desc[i].type[0] == (unsigned char)255)
27868735SEnrico.Perla@Sun.COM 			break;
27878735SEnrico.Perla@Sun.COM 		if (memcmp(saved_desc[i].id, "CD001", 5))
27888735SEnrico.Perla@Sun.COM 			break;
27898735SEnrico.Perla@Sun.COM 
27908735SEnrico.Perla@Sun.COM 		if (bam_verbose)
27918735SEnrico.Perla@Sun.COM 			bam_print("%s: Updating descriptor entry [%d]\n", func,
27928735SEnrico.Perla@Sun.COM 			    i);
27938735SEnrico.Perla@Sun.COM 
27948735SEnrico.Perla@Sun.COM 		to_733(saved_desc[i].volume_space_size, new_size);
27958735SEnrico.Perla@Sun.COM 	}
27968735SEnrico.Perla@Sun.COM 
27978735SEnrico.Perla@Sun.COM 	ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
27988735SEnrico.Perla@Sun.COM 	if (ret != DVD_BLOCK) {
27998735SEnrico.Perla@Sun.COM 		bam_error(WRITE_FAIL, archive, strerror(errno));
28008735SEnrico.Perla@Sun.COM 		goto out_err;
28018735SEnrico.Perla@Sun.COM 	}
28028735SEnrico.Perla@Sun.COM 	(void) close(newfd);
28038735SEnrico.Perla@Sun.COM 	newfd = -1;
28048735SEnrico.Perla@Sun.COM 
28058735SEnrico.Perla@Sun.COM 	ret = close(fd);
28068735SEnrico.Perla@Sun.COM 	if (ret != 0) {
28078735SEnrico.Perla@Sun.COM 		bam_error(CLOSE_FAIL, archive, strerror(errno));
28088735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
28098735SEnrico.Perla@Sun.COM 	}
28108735SEnrico.Perla@Sun.COM 	fd = -1;
28118735SEnrico.Perla@Sun.COM 
28128735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
28138735SEnrico.Perla@Sun.COM 	    "seek=%d conv=sync 2>&1", DD_PATH, tempname, archive,
28148735SEnrico.Perla@Sun.COM 	    (next_session/16));
28158735SEnrico.Perla@Sun.COM 
28168735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
28178735SEnrico.Perla@Sun.COM 
28188735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
28198735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
28208735SEnrico.Perla@Sun.COM 		bam_error(MULTI_FAIL, cmdline);
28218735SEnrico.Perla@Sun.COM 		goto out_flist_err;
28228735SEnrico.Perla@Sun.COM 	}
28238735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
28248735SEnrico.Perla@Sun.COM 
28258735SEnrico.Perla@Sun.COM 	(void) unlink(tempname);
28268735SEnrico.Perla@Sun.COM 
28278735SEnrico.Perla@Sun.COM 	if (bam_verbose)
28288735SEnrico.Perla@Sun.COM 		bam_print("boot archive updated successfully\n");
28298735SEnrico.Perla@Sun.COM 
28308735SEnrico.Perla@Sun.COM 	return (BAM_SUCCESS);
28318735SEnrico.Perla@Sun.COM 
28328735SEnrico.Perla@Sun.COM out_flist_err:
28338735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
28348735SEnrico.Perla@Sun.COM out_err:
28358735SEnrico.Perla@Sun.COM 	if (fd != -1)
28368735SEnrico.Perla@Sun.COM 		(void) close(fd);
28378735SEnrico.Perla@Sun.COM 	if (newfd != -1)
28388735SEnrico.Perla@Sun.COM 		(void) close(newfd);
28398735SEnrico.Perla@Sun.COM 	return (BAM_ERROR);
28408735SEnrico.Perla@Sun.COM }
28418735SEnrico.Perla@Sun.COM 
28428735SEnrico.Perla@Sun.COM static int
28438735SEnrico.Perla@Sun.COM create_x86_archive(char *archive, char *tempname, char *update_dir)
28448735SEnrico.Perla@Sun.COM {
28458735SEnrico.Perla@Sun.COM 	int		ret;
28468735SEnrico.Perla@Sun.COM 	char		cmdline[3 * PATH_MAX + 64];
28478735SEnrico.Perla@Sun.COM 	filelist_t	flist = {0};
28488735SEnrico.Perla@Sun.COM 	const char	*func = "create_x86_archive()";
28498735SEnrico.Perla@Sun.COM 
28508735SEnrico.Perla@Sun.COM 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
28518735SEnrico.Perla@Sun.COM 	    "2>&1", MKISO_PATH, MKISO_PARAMS, tempname, update_dir);
28528735SEnrico.Perla@Sun.COM 
28538735SEnrico.Perla@Sun.COM 	BAM_DPRINTF((D_CMDLINE, func, cmdline));
28548735SEnrico.Perla@Sun.COM 
28558735SEnrico.Perla@Sun.COM 	ret = exec_cmd(cmdline, &flist);
28568735SEnrico.Perla@Sun.COM 	if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
28578735SEnrico.Perla@Sun.COM 		bam_error(ARCHIVE_FAIL, cmdline);
28588735SEnrico.Perla@Sun.COM 		filelist_free(&flist);
28598735SEnrico.Perla@Sun.COM 		(void) unlink(tempname);
28608735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
28618735SEnrico.Perla@Sun.COM 	}
28628735SEnrico.Perla@Sun.COM 
28638735SEnrico.Perla@Sun.COM 	filelist_free(&flist);
28648735SEnrico.Perla@Sun.COM 
28658735SEnrico.Perla@Sun.COM 	if (check_archive(tempname) == BAM_ERROR)
28668735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
28678735SEnrico.Perla@Sun.COM 
28688735SEnrico.Perla@Sun.COM 	return (do_archive_copy(tempname, archive));
28698735SEnrico.Perla@Sun.COM }
28708735SEnrico.Perla@Sun.COM 
28718735SEnrico.Perla@Sun.COM static int
28728735SEnrico.Perla@Sun.COM mkisofs_archive(char *root, int what)
28738735SEnrico.Perla@Sun.COM {
28748735SEnrico.Perla@Sun.COM 	int		ret;
28758735SEnrico.Perla@Sun.COM 	char		temp[PATH_MAX];
28768735SEnrico.Perla@Sun.COM 	char 		bootblk[PATH_MAX];
28778735SEnrico.Perla@Sun.COM 	char		boot_archive[PATH_MAX];
28788735SEnrico.Perla@Sun.COM 
28798735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
28808735SEnrico.Perla@Sun.COM 		ret = snprintf(temp, sizeof (temp),
28818735SEnrico.Perla@Sun.COM 		    "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
28828735SEnrico.Perla@Sun.COM 		    get_machine(), getpid());
28838735SEnrico.Perla@Sun.COM 	else
28848735SEnrico.Perla@Sun.COM 		ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
28858735SEnrico.Perla@Sun.COM 		    root, ARCHIVE_PREFIX, get_machine(), getpid());
28868735SEnrico.Perla@Sun.COM 
28878735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (temp))
28888735SEnrico.Perla@Sun.COM 		goto out_path_err;
28898735SEnrico.Perla@Sun.COM 
28908735SEnrico.Perla@Sun.COM 	if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
28918735SEnrico.Perla@Sun.COM 		ret = snprintf(boot_archive, sizeof (boot_archive),
28928735SEnrico.Perla@Sun.COM 		    "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
28938735SEnrico.Perla@Sun.COM 		    ARCHIVE_SUFFIX);
28948735SEnrico.Perla@Sun.COM 	else
28958735SEnrico.Perla@Sun.COM 		ret = snprintf(boot_archive, sizeof (boot_archive),
28968735SEnrico.Perla@Sun.COM 		    "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
28978735SEnrico.Perla@Sun.COM 		    ARCHIVE_SUFFIX);
28988735SEnrico.Perla@Sun.COM 
28998735SEnrico.Perla@Sun.COM 	if (ret >= sizeof (boot_archive))
29008735SEnrico.Perla@Sun.COM 		goto out_path_err;
29018735SEnrico.Perla@Sun.COM 
29028735SEnrico.Perla@Sun.COM 	bam_print("updating %s\n", boot_archive);
29038735SEnrico.Perla@Sun.COM 
29048735SEnrico.Perla@Sun.COM 	if (is_flag_on(IS_SPARC_TARGET)) {
29058735SEnrico.Perla@Sun.COM 		ret = snprintf(bootblk, sizeof (bootblk),
29068735SEnrico.Perla@Sun.COM 		    "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
29078735SEnrico.Perla@Sun.COM 		if (ret >= sizeof (bootblk))
29088735SEnrico.Perla@Sun.COM 			goto out_path_err;
29098735SEnrico.Perla@Sun.COM 
29108735SEnrico.Perla@Sun.COM 		ret = create_sparc_archive(boot_archive, temp, bootblk,
29118735SEnrico.Perla@Sun.COM 		    get_cachedir(what));
29128735SEnrico.Perla@Sun.COM 	} else {
29138735SEnrico.Perla@Sun.COM 		if (!is_dir_flag_on(what, NO_MULTI)) {
29148735SEnrico.Perla@Sun.COM 			if (bam_verbose)
29158735SEnrico.Perla@Sun.COM 				bam_print("Attempting to extend x86 archive: "
29168735SEnrico.Perla@Sun.COM 				    "%s\n", boot_archive);
29178735SEnrico.Perla@Sun.COM 
29188735SEnrico.Perla@Sun.COM 			ret = extend_iso_archive(boot_archive, temp,
29198735SEnrico.Perla@Sun.COM 			    get_updatedir(what));
29208735SEnrico.Perla@Sun.COM 			if (ret == BAM_SUCCESS) {
29218735SEnrico.Perla@Sun.COM 				if (bam_verbose)
29228735SEnrico.Perla@Sun.COM 					bam_print("Successfully extended %s\n",
29238735SEnrico.Perla@Sun.COM 					    boot_archive);
29248735SEnrico.Perla@Sun.COM 
29258735SEnrico.Perla@Sun.COM 				(void) rmdir_r(get_updatedir(what));
29268735SEnrico.Perla@Sun.COM 				return (BAM_SUCCESS);
29278735SEnrico.Perla@Sun.COM 			}
29288735SEnrico.Perla@Sun.COM 		}
29298735SEnrico.Perla@Sun.COM 		/*
29308735SEnrico.Perla@Sun.COM 		 * The boot archive will be recreated from scratch. We get here
29318735SEnrico.Perla@Sun.COM 		 * if at least one of these conditions is true:
29328735SEnrico.Perla@Sun.COM 		 * - bootadm was called without the -e switch
29338735SEnrico.Perla@Sun.COM 		 * - the archive (or the archive cache) doesn't exist
29348735SEnrico.Perla@Sun.COM 		 * - archive size is bigger than BA_SIZE_MAX
29358735SEnrico.Perla@Sun.COM 		 * - more than COUNT_MAX files need to be updated
29368735SEnrico.Perla@Sun.COM 		 * - an error occourred either populating the /updates directory
29378735SEnrico.Perla@Sun.COM 		 *   or extend_iso_archive() failed
29388735SEnrico.Perla@Sun.COM 		 */
29398735SEnrico.Perla@Sun.COM 		if (bam_verbose)
29408735SEnrico.Perla@Sun.COM 			bam_print("Unable to extend %s... rebuilding archive\n",
29418735SEnrico.Perla@Sun.COM 			    boot_archive);
29428735SEnrico.Perla@Sun.COM 
29438735SEnrico.Perla@Sun.COM 		if (get_updatedir(what)[0] != '\0')
29448735SEnrico.Perla@Sun.COM 			(void) rmdir_r(get_updatedir(what));
29458735SEnrico.Perla@Sun.COM 
29468735SEnrico.Perla@Sun.COM 
29478735SEnrico.Perla@Sun.COM 		ret = create_x86_archive(boot_archive, temp,
29488735SEnrico.Perla@Sun.COM 		    get_cachedir(what));
29498735SEnrico.Perla@Sun.COM 	}
29508735SEnrico.Perla@Sun.COM 
29518735SEnrico.Perla@Sun.COM 	if (ret == BAM_SUCCESS && bam_verbose)
29528735SEnrico.Perla@Sun.COM 		bam_print("Successfully created %s\n", boot_archive);
29538735SEnrico.Perla@Sun.COM 
29548735SEnrico.Perla@Sun.COM 	return (ret);
29558735SEnrico.Perla@Sun.COM 
29568735SEnrico.Perla@Sun.COM out_path_err:
29578735SEnrico.Perla@Sun.COM 	bam_error(PATH_TOO_LONG, root);
29588735SEnrico.Perla@Sun.COM 	return (BAM_ERROR);
29590Sstevel@tonic-gate }
29600Sstevel@tonic-gate 
29610Sstevel@tonic-gate static error_t
29620Sstevel@tonic-gate create_ramdisk(char *root)
29630Sstevel@tonic-gate {
29640Sstevel@tonic-gate 	char *cmdline, path[PATH_MAX];
29650Sstevel@tonic-gate 	size_t len;
29660Sstevel@tonic-gate 	struct stat sb;
29678735SEnrico.Perla@Sun.COM 	int ret, what;
29688735SEnrico.Perla@Sun.COM 
29698735SEnrico.Perla@Sun.COM 	/* If there is mkisofs, use it to create the required archives */
29708735SEnrico.Perla@Sun.COM 	if (is_mkisofs()) {
29718735SEnrico.Perla@Sun.COM 		for (what = FILE32; what < CACHEDIR_NUM; what++) {
29728735SEnrico.Perla@Sun.COM 			if (is_dir_flag_on(what, NEED_UPDATE)) {
29738735SEnrico.Perla@Sun.COM 				ret = mkisofs_archive(root, what);
29748735SEnrico.Perla@Sun.COM 				if (ret != 0)
29758735SEnrico.Perla@Sun.COM 					return (BAM_ERROR);
29768735SEnrico.Perla@Sun.COM 			}
29778735SEnrico.Perla@Sun.COM 		}
29788735SEnrico.Perla@Sun.COM 		return (BAM_SUCCESS);
29798735SEnrico.Perla@Sun.COM 	}
29800Sstevel@tonic-gate 
29810Sstevel@tonic-gate 	/*
29828735SEnrico.Perla@Sun.COM 	 * Else setup command args for create_ramdisk.ksh for the UFS archives
29830Sstevel@tonic-gate 	 */
29848735SEnrico.Perla@Sun.COM 	if (bam_verbose)
29858735SEnrico.Perla@Sun.COM 		bam_print("mkisofs not found, creating UFS archive\n");
29868735SEnrico.Perla@Sun.COM 
29875648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
29880Sstevel@tonic-gate 	if (stat(path, &sb) != 0) {
29890Sstevel@tonic-gate 		bam_error(ARCH_EXEC_MISS, path, strerror(errno));
29900Sstevel@tonic-gate 		return (BAM_ERROR);
29910Sstevel@tonic-gate 	}
29920Sstevel@tonic-gate 
29930Sstevel@tonic-gate 	len = strlen(path) + strlen(root) + 10;	/* room for space + -R */
29946319Sjg 	if (bam_alt_platform)
29956319Sjg 		len += strlen(bam_platform) + strlen("-p ");
29960Sstevel@tonic-gate 	cmdline = s_calloc(1, len);
29970Sstevel@tonic-gate 
29986319Sjg 	if (bam_alt_platform) {
29996319Sjg 		assert(strlen(root) > 1);
30006319Sjg 		(void) snprintf(cmdline, len, "%s -p %s -R %s",
30016319Sjg 		    path, bam_platform, root);
30026319Sjg 		/* chop off / at the end */
30036319Sjg 		cmdline[strlen(cmdline) - 1] = '\0';
30046319Sjg 	} else if (strlen(root) > 1) {
30050Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s -R %s", path, root);
30060Sstevel@tonic-gate 		/* chop off / at the end */
30070Sstevel@tonic-gate 		cmdline[strlen(cmdline) - 1] = '\0';
30080Sstevel@tonic-gate 	} else
30090Sstevel@tonic-gate 		(void) snprintf(cmdline, len, "%s", path);
30100Sstevel@tonic-gate 
30115648Ssetje 	if (exec_cmd(cmdline, NULL) != 0) {
30120Sstevel@tonic-gate 		bam_error(ARCHIVE_FAIL, cmdline);
30130Sstevel@tonic-gate 		free(cmdline);
30140Sstevel@tonic-gate 		return (BAM_ERROR);
30150Sstevel@tonic-gate 	}
30160Sstevel@tonic-gate 	free(cmdline);
30170Sstevel@tonic-gate 	/*
30185648Ssetje 	 * The existence of the expected archives used to be
30195648Ssetje 	 * verified here. This check is done in create_ramdisk as
30205648Ssetje 	 * it needs to be in sync with the altroot operated upon.
30210Sstevel@tonic-gate 	 */
30220Sstevel@tonic-gate 	return (BAM_SUCCESS);
30230Sstevel@tonic-gate }
30240Sstevel@tonic-gate 
30250Sstevel@tonic-gate /*
30260Sstevel@tonic-gate  * Checks if target filesystem is on a ramdisk
30270Sstevel@tonic-gate  * 1 - is miniroot
30280Sstevel@tonic-gate  * 0 - is not
30290Sstevel@tonic-gate  * When in doubt assume it is not a ramdisk.
30300Sstevel@tonic-gate  */
30310Sstevel@tonic-gate static int
30320Sstevel@tonic-gate is_ramdisk(char *root)
30330Sstevel@tonic-gate {
30340Sstevel@tonic-gate 	struct extmnttab mnt;
30350Sstevel@tonic-gate 	FILE *fp;
30360Sstevel@tonic-gate 	int found;
3037316Svikram 	char mntpt[PATH_MAX];
3038316Svikram 	char *cp;
30390Sstevel@tonic-gate 
30400Sstevel@tonic-gate 	/*
30410Sstevel@tonic-gate 	 * There are 3 situations where creating archive is
30420Sstevel@tonic-gate 	 * of dubious value:
3043316Svikram 	 *	- create boot_archive on a lofi-mounted boot_archive
30440Sstevel@tonic-gate 	 *	- create it on a ramdisk which is the root filesystem
30450Sstevel@tonic-gate 	 *	- create it on a ramdisk mounted somewhere else
30460Sstevel@tonic-gate 	 * The first is not easy to detect and checking for it is not
30470Sstevel@tonic-gate 	 * worth it.
30480Sstevel@tonic-gate 	 * The other two conditions are handled here
30490Sstevel@tonic-gate 	 */
30500Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
30510Sstevel@tonic-gate 	if (fp == NULL) {
30520Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
30530Sstevel@tonic-gate 		return (0);
30540Sstevel@tonic-gate 	}
30550Sstevel@tonic-gate 
30560Sstevel@tonic-gate 	resetmnttab(fp);
30570Sstevel@tonic-gate 
3058316Svikram 	/*
3059316Svikram 	 * Remove any trailing / from the mount point
3060316Svikram 	 */
3061316Svikram 	(void) strlcpy(mntpt, root, sizeof (mntpt));
3062316Svikram 	if (strcmp(root, "/") != 0) {
3063316Svikram 		cp = mntpt + strlen(mntpt) - 1;
3064316Svikram 		if (*cp == '/')
3065316Svikram 			*cp = '\0';
3066316Svikram 	}
30670Sstevel@tonic-gate 	found = 0;
30680Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3069316Svikram 		if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
30700Sstevel@tonic-gate 			found = 1;
30710Sstevel@tonic-gate 			break;
30720Sstevel@tonic-gate 		}
30730Sstevel@tonic-gate 	}
30740Sstevel@tonic-gate 
30750Sstevel@tonic-gate 	if (!found) {
30760Sstevel@tonic-gate 		if (bam_verbose)
3077316Svikram 			bam_error(NOT_IN_MNTTAB, mntpt);
30780Sstevel@tonic-gate 		(void) fclose(fp);
30790Sstevel@tonic-gate 		return (0);
30800Sstevel@tonic-gate 	}
30810Sstevel@tonic-gate 
30820Sstevel@tonic-gate 	if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) {
30830Sstevel@tonic-gate 		if (bam_verbose)
30840Sstevel@tonic-gate 			bam_error(IS_RAMDISK, bam_root);
30850Sstevel@tonic-gate 		(void) fclose(fp);
30860Sstevel@tonic-gate 		return (1);
30870Sstevel@tonic-gate 	}
30880Sstevel@tonic-gate 
30890Sstevel@tonic-gate 	(void) fclose(fp);
30900Sstevel@tonic-gate 
30910Sstevel@tonic-gate 	return (0);
30920Sstevel@tonic-gate }
30930Sstevel@tonic-gate 
30940Sstevel@tonic-gate static int
30955648Ssetje is_boot_archive(char *root)
30960Sstevel@tonic-gate {
30976448Svikram 	char		path[PATH_MAX];
30986448Svikram 	struct stat	sb;
30996448Svikram 	int		error;
31006448Svikram 	const char	*fcn = "is_boot_archive()";
31010Sstevel@tonic-gate 
31020Sstevel@tonic-gate 	/*
31035648Ssetje 	 * We can't create an archive without the create_ramdisk script
31040Sstevel@tonic-gate 	 */
31055648Ssetje 	(void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
31066448Svikram 	error = stat(path, &sb);
31076448Svikram 	INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
31086448Svikram 	if (error == -1) {
31090Sstevel@tonic-gate 		if (bam_verbose)
31100Sstevel@tonic-gate 			bam_print(FILE_MISS, path);
31116448Svikram 		BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
31120Sstevel@tonic-gate 		return (0);
31130Sstevel@tonic-gate 	}
31140Sstevel@tonic-gate 
31156448Svikram 	BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
31165648Ssetje 	return (1);
31175648Ssetje }
31185648Ssetje 
31195648Ssetje /*
31205648Ssetje  * Need to call this for anything that operates on the GRUB menu
31216694Svikram  * In the x86 live upgrade case the directory /boot/grub may be present
31226694Svikram  * even on pre-newboot BEs. The authoritative way to check for a GRUB target
31236694Svikram  * is to check for the presence of the stage2 binary which is present
31246694Svikram  * only on GRUB targets (even on x86 boot partitions). Checking for the
31256694Svikram  * presence of the multiboot binary is not correct as it is not present
31266694Svikram  * on x86 boot partitions.
31275648Ssetje  */
31285648Ssetje int
31295648Ssetje is_grub(const char *root)
31305648Ssetje {
31315648Ssetje 	char path[PATH_MAX];
31325648Ssetje 	struct stat sb;
31336448Svikram 	const char *fcn = "is_grub()";
31346448Svikram 
31356694Svikram 	(void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
31360Sstevel@tonic-gate 	if (stat(path, &sb) == -1) {
31376448Svikram 		BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
31380Sstevel@tonic-gate 		return (0);
31390Sstevel@tonic-gate 	}
31400Sstevel@tonic-gate 
31410Sstevel@tonic-gate 	return (1);
31420Sstevel@tonic-gate }
31430Sstevel@tonic-gate 
31440Sstevel@tonic-gate static int
31456448Svikram is_zfs(char *root)
31466448Svikram {
31476448Svikram 	struct statvfs		vfs;
31486448Svikram 	int			ret;
31496448Svikram 	const char		*fcn = "is_zfs()";
31506448Svikram 
31516448Svikram 	ret = statvfs(root, &vfs);
31526448Svikram 	INJECT_ERROR1("STATVFS_ZFS", ret = 1);
31536448Svikram 	if (ret != 0) {
31546448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
31556448Svikram 		return (0);
31566448Svikram 	}
31576448Svikram 
31586448Svikram 	if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
31596448Svikram 		BAM_DPRINTF((D_IS_ZFS, fcn, root));
31606448Svikram 		return (1);
31616448Svikram 	} else {
31626448Svikram 		BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
31630Sstevel@tonic-gate 		return (0);
31640Sstevel@tonic-gate 	}
31656448Svikram }
31666448Svikram 
31676448Svikram static int
31686448Svikram is_ufs(char *root)
31696448Svikram {
31706448Svikram 	struct statvfs		vfs;
31716448Svikram 	int			ret;
31726448Svikram 	const char		*fcn = "is_ufs()";
31736448Svikram 
31746448Svikram 	ret = statvfs(root, &vfs);
31756448Svikram 	INJECT_ERROR1("STATVFS_UFS", ret = 1);
31766448Svikram 	if (ret != 0) {
31776448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
31786448Svikram 		return (0);
31796448Svikram 	}
31806448Svikram 
31816448Svikram 	if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
31826448Svikram 		BAM_DPRINTF((D_IS_UFS, fcn, root));
31830Sstevel@tonic-gate 		return (1);
31846448Svikram 	} else {
31856448Svikram 		BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
31866448Svikram 		return (0);
31876448Svikram 	}
31880Sstevel@tonic-gate }
31890Sstevel@tonic-gate 
31906423Sgw25295 static int
31916448Svikram is_pcfs(char *root)
31926448Svikram {
31936448Svikram 	struct statvfs		vfs;
31946448Svikram 	int			ret;
31956448Svikram 	const char		*fcn = "is_pcfs()";
31966448Svikram 
31976448Svikram 	ret = statvfs(root, &vfs);
31986448Svikram 	INJECT_ERROR1("STATVFS_PCFS", ret = 1);
31996448Svikram 	if (ret != 0) {
32006448Svikram 		bam_error(STATVFS_FAIL, root, strerror(errno));
32016423Sgw25295 		return (0);
32026423Sgw25295 	}
32036423Sgw25295 
32046448Svikram 	if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
32056448Svikram 		BAM_DPRINTF((D_IS_PCFS, fcn, root));
32066448Svikram 		return (1);
32076448Svikram 	} else {
32086448Svikram 		BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
32096423Sgw25295 		return (0);
32106448Svikram 	}
32116448Svikram }
32126448Svikram 
32136448Svikram static int
32146448Svikram is_readonly(char *root)
32156448Svikram {
32166448Svikram 	int		fd;
32176448Svikram 	int		error;
32186448Svikram 	char		testfile[PATH_MAX];
32196448Svikram 	const char	*fcn = "is_readonly()";
32206423Sgw25295 
32216423Sgw25295 	/*
32226448Svikram 	 * Using statvfs() to check for a read-only filesystem is not
32236448Svikram 	 * reliable. The only way to reliably test is to attempt to
32246448Svikram 	 * create a file
32256423Sgw25295 	 */
32266448Svikram 	(void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
32276448Svikram 	    root, BOOTADM_RDONLY_TEST, getpid());
32286448Svikram 
32296448Svikram 	(void) unlink(testfile);
32306448Svikram 
32316448Svikram 	errno = 0;
32326448Svikram 	fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
32336448Svikram 	error = errno;
32346448Svikram 	INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
32356448Svikram 	if (fd == -1 && error == EROFS) {
32366448Svikram 		BAM_DPRINTF((D_RDONLY_FS, fcn, root));
32376423Sgw25295 		return (1);
32386448Svikram 	} else if (fd == -1) {
32396448Svikram 		bam_error(RDONLY_TEST_ERROR, root, strerror(error));
32406448Svikram 	}
32416448Svikram 
32426448Svikram 	(void) close(fd);
32436448Svikram 	(void) unlink(testfile);
32446448Svikram 
32456448Svikram 	BAM_DPRINTF((D_RDWR_FS, fcn, root));
32466423Sgw25295 	return (0);
32476423Sgw25295 }
32486423Sgw25295 
32490Sstevel@tonic-gate static error_t
32500Sstevel@tonic-gate update_archive(char *root, char *opt)
32510Sstevel@tonic-gate {
32520Sstevel@tonic-gate 	error_t ret;
32530Sstevel@tonic-gate 
32540Sstevel@tonic-gate 	assert(root);
32550Sstevel@tonic-gate 	assert(opt == NULL);
32560Sstevel@tonic-gate 
32578735SEnrico.Perla@Sun.COM 	init_walk_args();
32588735SEnrico.Perla@Sun.COM 	(void) umask(022);
32598735SEnrico.Perla@Sun.COM 
32600Sstevel@tonic-gate 	/*
32616448Svikram 	 * root must belong to a boot archive based OS,
32620Sstevel@tonic-gate 	 */
32635648Ssetje 	if (!is_boot_archive(root)) {
3264316Svikram 		/*
3265316Svikram 		 * Emit message only if not in context of update_all.
3266316Svikram 		 * If in update_all, emit only if verbose flag is set.
3267316Svikram 		 */
3268316Svikram 		if (!bam_update_all || bam_verbose)
32696448Svikram 			bam_print(NOT_ARCHIVE_BOOT, root);
32708735SEnrico.Perla@Sun.COM 		return (BAM_ERROR);
32710Sstevel@tonic-gate 	}
32720Sstevel@tonic-gate 
32730Sstevel@tonic-gate 	/*
3274662Sszhou 	 * If smf check is requested when / is writable (can happen
3275662Sszhou 	 * on first reboot following an upgrade because service
3276662Sszhou 	 * dependency is messed up), skip the check.
3277662Sszhou 	 */
32788735SEnrico.Perla@Sun.COM 	if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
3279662Sszhou 		return (BAM_SUCCESS);
3280662Sszhou 
3281662Sszhou 	/*
3282662Sszhou 	 * root must be writable. This check applies to alternate
3283662Sszhou 	 * root (-R option); bam_root_readonly applies to '/' only.
32848735SEnrico.Perla@Sun.COM 	 * The behaviour translates into being the one of a 'check'.
32850Sstevel@tonic-gate 	 */
3286756Ssetje 	if (!bam_smf_check && !bam_check && is_readonly(root)) {
32878735SEnrico.Perla@Sun.COM 		set_flag(RDONLY_FSCHK);
32888735SEnrico.Perla@Sun.COM 		bam_check = 1;
32890Sstevel@tonic-gate 	}
32900Sstevel@tonic-gate 
32910Sstevel@tonic-gate 	/*
32928735SEnrico.Perla@Sun.COM 	 * Don't generate archive on ramdisk, but still check if the
32938735SEnrico.Perla@Sun.COM 	 * archive is up-to-date
32940Sstevel@tonic-gate 	 */
32950Sstevel@tonic-gate 	if (is_ramdisk(root)) {
32968735SEnrico.Perla@Sun.COM 		set_flag(RAMDSK_FSCHK);
32978735SEnrico.Perla@Sun.COM 		bam_check = 1;
32980Sstevel@tonic-gate 	}
32990Sstevel@tonic-gate 
33000Sstevel@tonic-gate 	/*
33010Sstevel@tonic-gate 	 * Now check if updated is really needed
33020Sstevel@tonic-gate 	 */
33030Sstevel@tonic-gate 	ret = update_required(root);
33040Sstevel@tonic-gate 
33050Sstevel@tonic-gate 	/*
33060Sstevel@tonic-gate 	 * The check command (-n) is *not* a dry run
33070Sstevel@tonic-gate 	 * It only checks if the archive is in sync.
33088735SEnrico.Perla@Sun.COM 	 * A readonly filesystem or being on a ramdisk have to be considered
33098735SEnrico.Perla@Sun.COM 	 * errors only if an update is required.
33100Sstevel@tonic-gate 	 */
33110Sstevel@tonic-gate 	if (bam_check) {
33128735SEnrico.Perla@Sun.COM 		if (is_flag_on(RDONLY_FSCHK)) {
33138735SEnrico.Perla@Sun.COM 			if (ret > 0)
33148735SEnrico.Perla@Sun.COM 				bam_error(RDONLY_FS, root);
33158735SEnrico.Perla@Sun.COM 			if (bam_update_all)
33168735SEnrico.Perla@Sun.COM 				return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
33178735SEnrico.Perla@Sun.COM 		}
33188735SEnrico.Perla@Sun.COM 
33198735SEnrico.Perla@Sun.COM 		if (is_flag_on(RAMDSK_FSCHK)) {
33208735SEnrico.Perla@Sun.COM 			if (ret > 0)
33218735SEnrico.Perla@Sun.COM 				bam_error(SKIP_RAMDISK, root);
33228735SEnrico.Perla@Sun.COM 			if (bam_update_all)
33238735SEnrico.Perla@Sun.COM 				return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
33248735SEnrico.Perla@Sun.COM 		}
33258735SEnrico.Perla@Sun.COM 
33260Sstevel@tonic-gate 		bam_exit((ret != 0) ? 1 : 0);
33270Sstevel@tonic-gate 	}
33280Sstevel@tonic-gate 
33290Sstevel@tonic-gate 	if (ret == 1) {
33300Sstevel@tonic-gate 		/* create the ramdisk */
33310Sstevel@tonic-gate 		ret = create_ramdisk(root);
33320Sstevel@tonic-gate 	}
33335648Ssetje 
33345648Ssetje 	/* if the archive is updated, save the new stat data */
33355648Ssetje 	if (ret == 0 && walk_arg.new_nvlp != NULL) {
33365648Ssetje 		savenew(root);
33375648Ssetje 	}
33385648Ssetje 
33395648Ssetje 	clear_walk_args();
33405648Ssetje 
33410Sstevel@tonic-gate 	return (ret);
33420Sstevel@tonic-gate }
33430Sstevel@tonic-gate 
33446694Svikram static error_t
33456694Svikram synchronize_BE_menu(void)
33466694Svikram {
33476694Svikram 	struct stat	sb;
33486694Svikram 	char		cmdline[PATH_MAX];
33496694Svikram 	char		cksum_line[PATH_MAX];
33506694Svikram 	filelist_t	flist = {0};
33516694Svikram 	char		*old_cksum_str;
33526694Svikram 	char		*old_size_str;
33536694Svikram 	char		*old_file;
33546694Svikram 	char		*curr_cksum_str;
33556694Svikram 	char		*curr_size_str;
33566694Svikram 	char		*curr_file;
33576694Svikram 	FILE		*cfp;
33586694Svikram 	int		found;
33596694Svikram 	int		ret;
33606694Svikram 	const char	*fcn = "synchronize_BE_menu()";
33616694Svikram 
33626694Svikram 	BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
33636694Svikram 
33646694Svikram 	/* Check if findroot enabled LU BE */
33656694Svikram 	if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
33666694Svikram 		BAM_DPRINTF((D_NOT_LU_BE, fcn));
33676694Svikram 		return (BAM_SUCCESS);
33686694Svikram 	}
33696694Svikram 
33706694Svikram 	if (stat(LU_MENU_CKSUM, &sb) != 0) {
33716694Svikram 		BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
33726694Svikram 		goto menu_sync;
33736694Svikram 	}
33746694Svikram 
33756694Svikram 	cfp = fopen(LU_MENU_CKSUM, "r");
33766694Svikram 	INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
33776694Svikram 	if (cfp == NULL) {
33786694Svikram 		bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
33796694Svikram 		goto menu_sync;
33806694Svikram 	}
33816694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
33826694Svikram 
33836694Svikram 	found = 0;
33846694Svikram 	while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
33856694Svikram 		INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
33866694Svikram 		if (found) {
33876694Svikram 			bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
33886694Svikram 			(void) fclose(cfp);
33896694Svikram 			goto menu_sync;
33906694Svikram 		}
33916694Svikram 		found = 1;
33926694Svikram 	}
33936694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
33946694Svikram 
33956694Svikram 
33966694Svikram 	old_cksum_str = strtok(cksum_line, " \t");
33976694Svikram 	old_size_str = strtok(NULL, " \t");
33986694Svikram 	old_file = strtok(NULL, " \t");
33996694Svikram 
34006694Svikram 	INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
34016694Svikram 	INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
34026694Svikram 	INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
34036694Svikram 	if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
34046694Svikram 		bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
34056694Svikram 		goto menu_sync;
34066694Svikram 	}
34076694Svikram 	BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
34086694Svikram 
34096694Svikram 	/* Get checksum of current menu */
34106694Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s",
34116694Svikram 	    CKSUM, GRUB_MENU);
34126694Svikram 	ret = exec_cmd(cmdline, &flist);
34136694Svikram 	INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
34146694Svikram 	if (ret != 0) {
34156694Svikram 		bam_error(MENU_CKSUM_FAIL);
34166694Svikram 		return (BAM_ERROR);
34176694Svikram 	}
34186694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
34196694Svikram 
34206694Svikram 	INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
34216694Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
34226694Svikram 		bam_error(BAD_CKSUM);
34236694Svikram 		filelist_free(&flist);
34246694Svikram 		return (BAM_ERROR);
34256694Svikram 	}
34266694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
34276694Svikram 
34286694Svikram 	curr_cksum_str = strtok(flist.head->line, " \t");
34296694Svikram 	curr_size_str = strtok(NULL, " \t");
34306694Svikram 	curr_file = strtok(NULL, " \t");
34316694Svikram 
34326694Svikram 	INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
34336694Svikram 	INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
34346694Svikram 	INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
34356694Svikram 	if (curr_cksum_str == NULL || curr_size_str == NULL ||
34366694Svikram 	    curr_file == NULL) {
34376694Svikram 		bam_error(BAD_CKSUM_PARSE);
34386694Svikram 		filelist_free(&flist);
34396694Svikram 		return (BAM_ERROR);
34406694Svikram 	}
34416694Svikram 	BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
34426694Svikram 
34436694Svikram 	if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
34446694Svikram 	    strcmp(old_size_str, curr_size_str) == 0 &&
34456694Svikram 	    strcmp(old_file, curr_file) == 0) {
34466694Svikram 		filelist_free(&flist);
34476694Svikram 		BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
34486694Svikram 		return (BAM_SUCCESS);
34496694Svikram 	}
34506694Svikram 
34516694Svikram 	filelist_free(&flist);
34526694Svikram 
34536694Svikram 	/* cksum doesn't match - the menu has changed */
34546694Svikram 	BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
34556694Svikram 
34566694Svikram menu_sync:
34576694Svikram 	bam_print(PROP_GRUB_MENU);
34586694Svikram 
34596694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
34607048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
34616694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
34626694Svikram 	ret = exec_cmd(cmdline, NULL);
34636694Svikram 	INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
34646694Svikram 	if (ret != 0) {
34656694Svikram 		bam_error(MENU_PROP_FAIL);
34666694Svikram 		return (BAM_ERROR);
34676694Svikram 	}
34686694Svikram 	BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
34696694Svikram 
34707048Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
34716694Svikram 	    GRUB_MENU, GRUB_BACKUP_MENU);
34726694Svikram 	ret = exec_cmd(cmdline, NULL);
34736694Svikram 	INJECT_ERROR1("CREATE_BACKUP", ret = 1);
34746694Svikram 	if (ret != 0) {
34756694Svikram 		bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
34766694Svikram 		return (BAM_ERROR);
34776694Svikram 	}
34786694Svikram 	BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
34796694Svikram 
34806694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
34817048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
34826694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
34836694Svikram 	ret = exec_cmd(cmdline, NULL);
34846694Svikram 	INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
34856694Svikram 	if (ret != 0) {
34866694Svikram 		bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
34876694Svikram 		return (BAM_ERROR);
34886694Svikram 	}
34896694Svikram 	BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
34906694Svikram 
34916694Svikram 	(void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
34926694Svikram 	    CKSUM, GRUB_MENU, LU_MENU_CKSUM);
34936694Svikram 	ret = exec_cmd(cmdline, NULL);
34946694Svikram 	INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
34956694Svikram 	if (ret != 0) {
34966694Svikram 		bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
34976694Svikram 		return (BAM_ERROR);
34986694Svikram 	}
34996694Svikram 	BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
35006694Svikram 
35016694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
35027048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
35036694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
35046694Svikram 	ret = exec_cmd(cmdline, NULL);
35056694Svikram 	INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
35066694Svikram 	if (ret != 0) {
35076694Svikram 		bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
35086694Svikram 		return (BAM_ERROR);
35096694Svikram 	}
35106694Svikram 	BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
35116694Svikram 
35126694Svikram 	(void) snprintf(cmdline, sizeof (cmdline),
35137048Svikram 	    "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
35146694Svikram 	    LULIB, LULIB_PROPAGATE_FILE, BOOTADM);
35156694Svikram 	ret = exec_cmd(cmdline, NULL);
35166694Svikram 	INJECT_ERROR1("PROPAGATE_BOOTADM_FILE", ret = 1);
35176694Svikram 	if (ret != 0) {
35186694Svikram 		bam_error(BOOTADM_PROP_FAIL, BOOTADM);
35196694Svikram 		return (BAM_ERROR);
35206694Svikram 	}
35216694Svikram 	BAM_DPRINTF((D_PROPAGATED_BOOTADM, fcn, BOOTADM));
35226694Svikram 
35236694Svikram 	return (BAM_SUCCESS);
35241746Svikram }
35251746Svikram 
35260Sstevel@tonic-gate static error_t
35270Sstevel@tonic-gate update_all(char *root, char *opt)
35280Sstevel@tonic-gate {
35290Sstevel@tonic-gate 	struct extmnttab mnt;
35300Sstevel@tonic-gate 	struct stat sb;
35310Sstevel@tonic-gate 	FILE *fp;
35320Sstevel@tonic-gate 	char multibt[PATH_MAX];
35335648Ssetje 	char creatram[PATH_MAX];
35340Sstevel@tonic-gate 	error_t ret = BAM_SUCCESS;
35350Sstevel@tonic-gate 
3536621Svikram 	assert(root);
35370Sstevel@tonic-gate 	assert(opt == NULL);
35380Sstevel@tonic-gate 
3539621Svikram 	if (bam_rootlen != 1 || *root != '/') {
3540621Svikram 		elide_trailing_slash(root, multibt, sizeof (multibt));
3541621Svikram 		bam_error(ALT_ROOT_INVALID, multibt);
3542621Svikram 		return (BAM_ERROR);
3543621Svikram 	}
3544621Svikram 
35450Sstevel@tonic-gate 	/*
35464493Snadkarni 	 * Check to see if we are in the midst of safemode patching
35474493Snadkarni 	 * If so skip building the archive for /. Instead build it
35484493Snadkarni 	 * against the latest bits obtained by creating a fresh lofs
35494493Snadkarni 	 * mount of root.
35500Sstevel@tonic-gate 	 */
35514493Snadkarni 	if (stat(LOFS_PATCH_FILE, &sb) == 0)  {
35528735SEnrico.Perla@Sun.COM 		if (mkdir(LOFS_PATCH_MNT, DIR_PERMS) == -1 &&
35534493Snadkarni 		    errno != EEXIST) {
35544493Snadkarni 			bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT,
35554493Snadkarni 			    strerror(errno));
35564493Snadkarni 			ret = BAM_ERROR;
35574493Snadkarni 			goto out;
35584493Snadkarni 		}
35594493Snadkarni 		(void) snprintf(multibt, sizeof (multibt),
35604493Snadkarni 		    "/sbin/mount -F lofs -o nosub /  %s", LOFS_PATCH_MNT);
35615648Ssetje 		if (exec_cmd(multibt, NULL) != 0) {
35624493Snadkarni 			bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs");
35634493Snadkarni 			ret = BAM_ERROR;
35644493Snadkarni 		}
35654493Snadkarni 		if (ret != BAM_ERROR) {
35664493Snadkarni 			(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
35674493Snadkarni 			    LOFS_PATCH_MNT);
35684493Snadkarni 			bam_rootlen = strlen(rootbuf);
35694493Snadkarni 			if (update_archive(rootbuf, opt) != BAM_SUCCESS)
35704493Snadkarni 				ret = BAM_ERROR;
35714550Snadkarni 			/*
35724550Snadkarni 			 * unmount the lofs mount since there could be
35734550Snadkarni 			 * multiple invocations of bootadm -a update_all
35744550Snadkarni 			 */
35754550Snadkarni 			(void) snprintf(multibt, sizeof (multibt),
35764550Snadkarni 			    "/sbin/umount %s", LOFS_PATCH_MNT);
35775648Ssetje 			if (exec_cmd(multibt, NULL) != 0) {
35784550Snadkarni 				bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT);
35794550Snadkarni 				ret = BAM_ERROR;
35804550Snadkarni 			}
35814493Snadkarni 		}
35824493Snadkarni 	} else {
35834493Snadkarni 		/*
35844493Snadkarni 		 * First update archive for current root
35854493Snadkarni 		 */
35864493Snadkarni 		if (update_archive(root, opt) != BAM_SUCCESS)
35874493Snadkarni 			ret = BAM_ERROR;
35884493Snadkarni 	}
35894493Snadkarni 
35904493Snadkarni 	if (ret == BAM_ERROR)
35914493Snadkarni 		goto out;
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate 	/*
35940Sstevel@tonic-gate 	 * Now walk the mount table, performing archive update
35950Sstevel@tonic-gate 	 * for all mounted Newboot root filesystems
35960Sstevel@tonic-gate 	 */
35970Sstevel@tonic-gate 	fp = fopen(MNTTAB, "r");
35980Sstevel@tonic-gate 	if (fp == NULL) {
35990Sstevel@tonic-gate 		bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3600316Svikram 		ret = BAM_ERROR;
3601316Svikram 		goto out;
36020Sstevel@tonic-gate 	}
36030Sstevel@tonic-gate 
36040Sstevel@tonic-gate 	resetmnttab(fp);
36050Sstevel@tonic-gate 
36060Sstevel@tonic-gate 	while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
36070Sstevel@tonic-gate 		if (mnt.mnt_special == NULL)
36080Sstevel@tonic-gate 			continue;
36097543SJerry.Gilliam@Sun.COM 		if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
36107543SJerry.Gilliam@Sun.COM 		    (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
36110Sstevel@tonic-gate 			continue;
36120Sstevel@tonic-gate 		if (strcmp(mnt.mnt_mountp, "/") == 0)
36130Sstevel@tonic-gate 			continue;
36140Sstevel@tonic-gate 
36155648Ssetje 		(void) snprintf(creatram, sizeof (creatram), "%s/%s",
36165648Ssetje 		    mnt.mnt_mountp, CREATE_RAMDISK);
36175648Ssetje 
36185648Ssetje 		if (stat(creatram, &sb) == -1)
36190Sstevel@tonic-gate 			continue;
36200Sstevel@tonic-gate 
36210Sstevel@tonic-gate 		/*
36220Sstevel@tonic-gate 		 * We put a trailing slash to be consistent with root = "/"
36230Sstevel@tonic-gate 		 * case, such that we don't have to print // in some cases.
36240Sstevel@tonic-gate 		 */
36250Sstevel@tonic-gate 		(void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
36260Sstevel@tonic-gate 		    mnt.mnt_mountp);
36270Sstevel@tonic-gate 		bam_rootlen = strlen(rootbuf);
36283446Smrj 
36293446Smrj 		/*
36303446Smrj 		 * It's possible that other mounts may be an alternate boot
36313446Smrj 		 * architecture, so check it again.
36323446Smrj 		 */
36336448Svikram 		if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
36343446Smrj 		    (update_archive(rootbuf, opt) != BAM_SUCCESS))
36350Sstevel@tonic-gate 			ret = BAM_ERROR;
36360Sstevel@tonic-gate 	}
36370Sstevel@tonic-gate 
36380Sstevel@tonic-gate 	(void) fclose(fp);
36390Sstevel@tonic-gate 
3640316Svikram out:
36411746Svikram 	/*
36426694Svikram 	 * We no longer use biosdev for Live Upgrade. Hence
36436694Svikram 	 * there is no need to defer (to shutdown time) any fdisk
36446694Svikram 	 * updates
36451746Svikram 	 */
36466694Svikram 	if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
36476694Svikram 		bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
36486694Svikram 	}
36496694Svikram 
36506694Svikram 	/*
36516694Svikram 	 * If user has updated menu in current BE, propagate the
36526694Svikram 	 * updates to all BEs.
36536694Svikram 	 */
36547656SSherry.Moore@Sun.COM 	if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
36551746Svikram 		ret = BAM_ERROR;
36561746Svikram 
36570Sstevel@tonic-gate 	return (ret);
36580Sstevel@tonic-gate }
36590Sstevel@tonic-gate 
36600Sstevel@tonic-gate static void
36610Sstevel@tonic-gate append_line(menu_t *mp, line_t *lp)
36620Sstevel@tonic-gate {
36630Sstevel@tonic-gate 	if (mp->start == NULL) {
36640Sstevel@tonic-gate 		mp->start = lp;
36650Sstevel@tonic-gate 	} else {
36660Sstevel@tonic-gate 		mp->end->next = lp;
3667662Sszhou 		lp->prev = mp->end;
36680Sstevel@tonic-gate 	}
36690Sstevel@tonic-gate 	mp->end = lp;
36700Sstevel@tonic-gate }
36710Sstevel@tonic-gate 
36726448Svikram void
3673662Sszhou unlink_line(menu_t *mp, line_t *lp)
3674662Sszhou {
3675662Sszhou 	/* unlink from list */
3676662Sszhou 	if (lp->prev)
3677662Sszhou 		lp->prev->next = lp->next;
3678662Sszhou 	else
3679662Sszhou 		mp->start = lp->next;
3680662Sszhou 	if (lp->next)
3681662Sszhou 		lp->next->prev = lp->prev;
3682662Sszhou 	else
3683662Sszhou 		mp->end = lp->prev;
3684662Sszhou }
3685662Sszhou 
3686662Sszhou static entry_t *
3687662Sszhou boot_entry_new(menu_t *mp, line_t *start, line_t *end)
3688662Sszhou {
3689662Sszhou 	entry_t *ent, *prev;
36906448Svikram 	const char *fcn = "boot_entry_new()";
36916448Svikram 
36926448Svikram 	assert(mp);
36936448Svikram 	assert(start);
36946448Svikram 	assert(end);
3695662Sszhou 
3696662Sszhou 	ent = s_calloc(1, sizeof (entry_t));
36976448Svikram 	BAM_DPRINTF((D_ENTRY_NEW, fcn));
3698662Sszhou 	ent->start = start;
3699662Sszhou 	ent->end = end;
3700662Sszhou 
3701662Sszhou 	if (mp->entries == NULL) {
3702662Sszhou 		mp->entries = ent;
37036448Svikram 		BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
3704662Sszhou 		return (ent);
3705662Sszhou 	}
3706662Sszhou 
3707662Sszhou 	prev = mp->entries;
3708662Sszhou 	while (prev->next)
37096448Svikram 		prev = prev->next;
3710662Sszhou 	prev->next = ent;
3711662Sszhou 	ent->prev = prev;
37126448Svikram 	BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
3713662Sszhou 	return (ent);
3714662Sszhou }
3715662Sszhou 
3716662Sszhou static void
3717662Sszhou boot_entry_addline(entry_t *ent, line_t *lp)
3718662Sszhou {
3719662Sszhou 	if (ent)
3720662Sszhou 		ent->end = lp;
3721662Sszhou }
3722662Sszhou 
37230Sstevel@tonic-gate /*
37245084Sjohnlev  * Check whether cmd matches the one indexed by which, and whether arg matches
37255084Sjohnlev  * str.  which must be either KERNEL_CMD or MODULE_CMD, and a match to the
37265084Sjohnlev  * respective *_DOLLAR_CMD is also acceptable.  The arg is searched using
37275084Sjohnlev  * strstr(), so it can be a partial match.
37285084Sjohnlev  */
37295084Sjohnlev static int
37305084Sjohnlev check_cmd(const char *cmd, const int which, const char *arg, const char *str)
37315084Sjohnlev {
37326448Svikram 	int			ret;
37336448Svikram 	const char		*fcn = "check_cmd()";
37346448Svikram 
37356448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
37366448Svikram 
37375084Sjohnlev 	if ((strcmp(cmd, menu_cmds[which]) != 0) &&
37385084Sjohnlev 	    (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
37396448Svikram 		BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
37406448Svikram 		    fcn, cmd, menu_cmds[which]));
37415084Sjohnlev 		return (0);
37425084Sjohnlev 	}
37436448Svikram 	ret = (strstr(arg, str) != NULL);
37446448Svikram 
37456448Svikram 	if (ret) {
37466448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
37476448Svikram 	} else {
37486448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
37496448Svikram 	}
37506448Svikram 
37516448Svikram 	return (ret);
37526448Svikram }
37536448Svikram 
37546448Svikram static error_t
37556448Svikram kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
37566448Svikram {
37576448Svikram 	const char		*fcn  = "kernel_parser()";
37586448Svikram 
37596448Svikram 	assert(entry);
37606448Svikram 	assert(cmd);
37616448Svikram 	assert(arg);
37626448Svikram 
37636448Svikram 	if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
37646448Svikram 	    strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
37656448Svikram 		BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
37666448Svikram 		return (BAM_ERROR);
37676448Svikram 	}
37686448Svikram 
37696448Svikram 	if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
37706448Svikram 		BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
37716448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
37726448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_KERNEL,
37736448Svikram 	    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
37746448Svikram 		BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
37756448Svikram 		entry->flags |= BAM_ENTRY_DBOOT;
37766448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_64,
37776448Svikram 	    sizeof (DIRECT_BOOT_64) - 1) == 0) {
37786448Svikram 		BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
37796448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
37806448Svikram 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
37816448Svikram 	    sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
37826448Svikram 		BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
37836448Svikram 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
37848104SEnrico.Perla@Sun.COM 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
37858104SEnrico.Perla@Sun.COM 	    sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
37868104SEnrico.Perla@Sun.COM 		BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg));
37878104SEnrico.Perla@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
37888104SEnrico.Perla@Sun.COM 		    | BAM_ENTRY_32BIT;
37898104SEnrico.Perla@Sun.COM 	} else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
37908104SEnrico.Perla@Sun.COM 	    sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
37918104SEnrico.Perla@Sun.COM 		BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg));
37928104SEnrico.Perla@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
37938104SEnrico.Perla@Sun.COM 		    | BAM_ENTRY_64BIT;
37946448Svikram 	} else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
37956448Svikram 		BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
37966448Svikram 		entry->flags |= BAM_ENTRY_MULTIBOOT;
37976448Svikram 	} else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
37986448Svikram 	    sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
37996448Svikram 		BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
38006448Svikram 		entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
38016448Svikram 	} else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
38026448Svikram 		BAM_DPRINTF((D_SET_HV, fcn, arg));
38036448Svikram 		entry->flags |= BAM_ENTRY_HV;
38046448Svikram 	} else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
38056448Svikram 		BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
38066448Svikram 		return (BAM_ERROR);
38078642SVikram.Hegde@Sun.COM 	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
38088642SVikram.Hegde@Sun.COM 	    strstr(arg, UNIX_SPACE)) {
38098642SVikram.Hegde@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
38108642SVikram.Hegde@Sun.COM 	} else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
38118642SVikram.Hegde@Sun.COM 	    strstr(arg, AMD_UNIX_SPACE)) {
38128642SVikram.Hegde@Sun.COM 		entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
38136448Svikram 	} else {
38146448Svikram 		BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
38156448Svikram 		bam_error(UNKNOWN_KERNEL_LINE, linenum);
38166448Svikram 		return (BAM_ERROR);
38176448Svikram 	}
38186448Svikram 
38196448Svikram 	return (BAM_SUCCESS);
38206448Svikram }
38216448Svikram 
38226448Svikram static error_t
38236448Svikram module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
38246448Svikram {
38256448Svikram 	const char		*fcn = "module_parser()";
38266448Svikram 
38276448Svikram 	assert(entry);
38286448Svikram 	assert(cmd);
38296448Svikram 	assert(arg);
38306448Svikram 
38316448Svikram 	if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
38326448Svikram 	    strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
38336448Svikram 		BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
38346448Svikram 		return (BAM_ERROR);
38356448Svikram 	}
38366448Svikram 
38376448Svikram 	if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
38386448Svikram 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
38396448Svikram 	    strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
38406448Svikram 	    strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
38416448Svikram 	    strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
38428104SEnrico.Perla@Sun.COM 	    strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
38438104SEnrico.Perla@Sun.COM 	    strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
38446448Svikram 	    strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
38456448Svikram 	    strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
38466448Svikram 		BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
38476448Svikram 		return (BAM_SUCCESS);
38486448Svikram 	} else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
38496448Svikram 	    !(entry->flags & BAM_ENTRY_LU)) {
38506448Svikram 		/* don't emit warning for hand entries */
38516448Svikram 		BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
38526448Svikram 		return (BAM_ERROR);
38536448Svikram 	} else {
38546448Svikram 		BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
38556448Svikram 		bam_error(UNKNOWN_MODULE_LINE, linenum);
38566448Svikram 		return (BAM_ERROR);
38576448Svikram 	}
38585084Sjohnlev }
38595084Sjohnlev 
38605084Sjohnlev /*
38610Sstevel@tonic-gate  * A line in menu.lst looks like
38620Sstevel@tonic-gate  * [ ]*<cmd>[ \t=]*<arg>*
38630Sstevel@tonic-gate  */
38640Sstevel@tonic-gate static void
38650Sstevel@tonic-gate line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
38660Sstevel@tonic-gate {
38670Sstevel@tonic-gate 	/*
38680Sstevel@tonic-gate 	 * save state across calls. This is so that
38690Sstevel@tonic-gate 	 * header gets the right entry# after title has
38700Sstevel@tonic-gate 	 * been processed
38710Sstevel@tonic-gate 	 */
3872662Sszhou 	static line_t *prev = NULL;
3873662Sszhou 	static entry_t *curr_ent = NULL;
38743446Smrj 	static int in_liveupgrade = 0;
38750Sstevel@tonic-gate 
38760Sstevel@tonic-gate 	line_t	*lp;
38770Sstevel@tonic-gate 	char *cmd, *sep, *arg;
38780Sstevel@tonic-gate 	char save, *cp, *line;
38790Sstevel@tonic-gate 	menu_flag_t flag = BAM_INVALID;
38806448Svikram 	const char *fcn = "line_parser()";
38810Sstevel@tonic-gate 
38820Sstevel@tonic-gate 	if (str == NULL) {
38830Sstevel@tonic-gate 		return;
38840Sstevel@tonic-gate 	}
38850Sstevel@tonic-gate 
38860Sstevel@tonic-gate 	/*
38870Sstevel@tonic-gate 	 * First save a copy of the entire line.
38880Sstevel@tonic-gate 	 * We use this later to set the line field.
38890Sstevel@tonic-gate 	 */
38900Sstevel@tonic-gate 	line = s_strdup(str);
38910Sstevel@tonic-gate 
38920Sstevel@tonic-gate 	/* Eat up leading whitespace */
38930Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
38940Sstevel@tonic-gate 		str++;
38950Sstevel@tonic-gate 
38960Sstevel@tonic-gate 	if (*str == '#') {		/* comment */
38970Sstevel@tonic-gate 		cmd = s_strdup("#");
38980Sstevel@tonic-gate 		sep = NULL;
38990Sstevel@tonic-gate 		arg = s_strdup(str + 1);
39000Sstevel@tonic-gate 		flag = BAM_COMMENT;
39013446Smrj 		if (strstr(arg, BAM_LU_HDR) != NULL) {
39023446Smrj 			in_liveupgrade = 1;
39033446Smrj 		} else if (strstr(arg, BAM_LU_FTR) != NULL) {
39043446Smrj 			in_liveupgrade = 0;
39053446Smrj 		}
39060Sstevel@tonic-gate 	} else if (*str == '\0') {	/* blank line */
39070Sstevel@tonic-gate 		cmd = sep = arg = NULL;
39080Sstevel@tonic-gate 		flag = BAM_EMPTY;
39090Sstevel@tonic-gate 	} else {
39100Sstevel@tonic-gate 		/*
39110Sstevel@tonic-gate 		 * '=' is not a documented separator in grub syntax.
39120Sstevel@tonic-gate 		 * However various development bits use '=' as a
39130Sstevel@tonic-gate 		 * separator. In addition, external users also
39140Sstevel@tonic-gate 		 * use = as a separator. So we will allow that usage.
39150Sstevel@tonic-gate 		 */
39160Sstevel@tonic-gate 		cp = str;
39170Sstevel@tonic-gate 		while (*str != ' ' && *str != '\t' && *str != '=') {
39180Sstevel@tonic-gate 			if (*str == '\0') {
39190Sstevel@tonic-gate 				cmd = s_strdup(cp);
39200Sstevel@tonic-gate 				sep = arg = NULL;
39210Sstevel@tonic-gate 				break;
39220Sstevel@tonic-gate 			}
39230Sstevel@tonic-gate 			str++;
39240Sstevel@tonic-gate 		}
39250Sstevel@tonic-gate 
39260Sstevel@tonic-gate 		if (*str != '\0') {
39270Sstevel@tonic-gate 			save = *str;
39280Sstevel@tonic-gate 			*str = '\0';
39290Sstevel@tonic-gate 			cmd = s_strdup(cp);
39300Sstevel@tonic-gate 			*str = save;
39310Sstevel@tonic-gate 
39320Sstevel@tonic-gate 			str++;
39330Sstevel@tonic-gate 			save = *str;
39340Sstevel@tonic-gate 			*str = '\0';
39350Sstevel@tonic-gate 			sep = s_strdup(str - 1);
39360Sstevel@tonic-gate 			*str = save;
39370Sstevel@tonic-gate 
39380Sstevel@tonic-gate 			while (*str == ' ' || *str == '\t')
39390Sstevel@tonic-gate 				str++;
39400Sstevel@tonic-gate 			if (*str == '\0')
39410Sstevel@tonic-gate 				arg = NULL;
39420Sstevel@tonic-gate 			else
39430Sstevel@tonic-gate 				arg = s_strdup(str);
39440Sstevel@tonic-gate 		}
39450Sstevel@tonic-gate 	}
39460Sstevel@tonic-gate 
39470Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
39480Sstevel@tonic-gate 
39490Sstevel@tonic-gate 	lp->cmd = cmd;
39500Sstevel@tonic-gate 	lp->sep = sep;
39510Sstevel@tonic-gate 	lp->arg = arg;
39520Sstevel@tonic-gate 	lp->line = line;
39530Sstevel@tonic-gate 	lp->lineNum = ++(*lineNum);
39540Sstevel@tonic-gate 	if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
39550Sstevel@tonic-gate 		lp->entryNum = ++(*entryNum);
39560Sstevel@tonic-gate 		lp->flags = BAM_TITLE;
39570Sstevel@tonic-gate 		if (prev && prev->flags == BAM_COMMENT &&
39583446Smrj 		    prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
39590Sstevel@tonic-gate 			prev->entryNum = lp->entryNum;
3960662Sszhou 			curr_ent = boot_entry_new(mp, prev, lp);
39616448Svikram 			curr_ent->flags |= BAM_ENTRY_BOOTADM;
39626448Svikram 			BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
3963662Sszhou 		} else {
3964662Sszhou 			curr_ent = boot_entry_new(mp, lp, lp);
39653446Smrj 			if (in_liveupgrade) {
39666448Svikram 				curr_ent->flags |= BAM_ENTRY_LU;
39676448Svikram 				BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
39683446Smrj 			}
3969662Sszhou 		}
39703446Smrj 		curr_ent->entryNum = *entryNum;
39710Sstevel@tonic-gate 	} else if (flag != BAM_INVALID) {
39720Sstevel@tonic-gate 		/*
39730Sstevel@tonic-gate 		 * For header comments, the entry# is "fixed up"
39740Sstevel@tonic-gate 		 * by the subsequent title
39750Sstevel@tonic-gate 		 */
39760Sstevel@tonic-gate 		lp->entryNum = *entryNum;
39770Sstevel@tonic-gate 		lp->flags = flag;
39780Sstevel@tonic-gate 	} else {
39790Sstevel@tonic-gate 		lp->entryNum = *entryNum;
39803446Smrj 
39813446Smrj 		if (*entryNum == ENTRY_INIT) {
39823446Smrj 			lp->flags = BAM_GLOBAL;
39833446Smrj 		} else {
39843446Smrj 			lp->flags = BAM_ENTRY;
39853446Smrj 
39863446Smrj 			if (cmd && arg) {
39876448Svikram 				if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
39886448Svikram 					BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
39893446Smrj 					curr_ent->flags |= BAM_ENTRY_ROOT;
39906448Svikram 				} else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
39916448Svikram 				    == 0) {
39926448Svikram 					BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
39936448Svikram 					    arg));
39946448Svikram 					curr_ent->flags |= BAM_ENTRY_FINDROOT;
39956448Svikram 				} else if (strcmp(cmd,
39966448Svikram 				    menu_cmds[CHAINLOADER_CMD]) == 0) {
39976448Svikram 					BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
39986448Svikram 					    arg));
39993446Smrj 					curr_ent->flags |=
40003446Smrj 					    BAM_ENTRY_CHAINLOADER;
40016448Svikram 				} else if (kernel_parser(curr_ent, cmd, arg,
40026448Svikram 				    lp->lineNum) != BAM_SUCCESS) {
40036448Svikram 					(void) module_parser(curr_ent, cmd,
40046448Svikram 					    arg, lp->lineNum);
40056448Svikram 				}
40063446Smrj 			}
40073446Smrj 		}
40080Sstevel@tonic-gate 	}
40090Sstevel@tonic-gate 
4010662Sszhou 	/* record default, old default, and entry line ranges */
4011662Sszhou 	if (lp->flags == BAM_GLOBAL &&
4012662Sszhou 	    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4013662Sszhou 		mp->curdefault = lp;
4014662Sszhou 	} else if (lp->flags == BAM_COMMENT &&
4015662Sszhou 	    strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4016662Sszhou 		mp->olddefault = lp;
40173446Smrj 	} else if (lp->flags == BAM_COMMENT &&
40183446Smrj 	    strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
40193446Smrj 		mp->old_rc_default = lp;
4020662Sszhou 	} else if (lp->flags == BAM_ENTRY ||
40213446Smrj 	    (lp->flags == BAM_COMMENT &&
40223446Smrj 	    strcmp(lp->arg, BAM_BOOTADM_FTR) == 0)) {
4023662Sszhou 		boot_entry_addline(curr_ent, lp);
4024662Sszhou 	}
40250Sstevel@tonic-gate 	append_line(mp, lp);
40260Sstevel@tonic-gate 
40270Sstevel@tonic-gate 	prev = lp;
40280Sstevel@tonic-gate }
40290Sstevel@tonic-gate 
40306448Svikram void
4031621Svikram update_numbering(menu_t *mp)
4032621Svikram {
4033621Svikram 	int lineNum;
4034621Svikram 	int entryNum;
4035621Svikram 	int old_default_value;
4036621Svikram 	line_t *lp, *prev, *default_lp, *default_entry;
4037621Svikram 	char buf[PATH_MAX];
4038621Svikram 
4039621Svikram 	if (mp->start == NULL) {
4040621Svikram 		return;
4041621Svikram 	}
4042621Svikram 
4043621Svikram 	lineNum = LINE_INIT;
4044621Svikram 	entryNum = ENTRY_INIT;
4045621Svikram 	old_default_value = ENTRY_INIT;
4046621Svikram 	lp = default_lp = default_entry = NULL;
4047621Svikram 
4048621Svikram 	prev = NULL;
4049621Svikram 	for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4050621Svikram 		lp->lineNum = ++lineNum;
4051621Svikram 
4052621Svikram 		/*
4053621Svikram 		 * Get the value of the default command
4054621Svikram 		 */
4055621Svikram 		if (lp->entryNum == ENTRY_INIT && lp->cmd &&
4056621Svikram 		    strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4057621Svikram 		    lp->arg) {
4058621Svikram 			old_default_value = atoi(lp->arg);
4059621Svikram 			default_lp = lp;
4060621Svikram 		}
4061621Svikram 
4062621Svikram 		/*
40636448Svikram 		 * If not a booting entry, nothing else to fix for this
4064621Svikram 		 * entry
4065621Svikram 		 */
4066621Svikram 		if (lp->entryNum == ENTRY_INIT)
4067621Svikram 			continue;
4068621Svikram 
4069621Svikram 		/*
4070621Svikram 		 * Record the position of the default entry.
4071621Svikram 		 * The following works because global
4072621Svikram 		 * commands like default and timeout should precede
4073621Svikram 		 * actual boot entries, so old_default_value
4074621Svikram 		 * is already known (or default cmd is missing).
4075621Svikram 		 */
4076621Svikram 		if (default_entry == NULL &&
4077621Svikram 		    old_default_value != ENTRY_INIT &&
4078621Svikram 		    lp->entryNum == old_default_value) {
4079621Svikram 			default_entry = lp;
4080621Svikram 		}
4081621Svikram 
4082621Svikram 		/*
4083621Svikram 		 * Now fixup the entry number
4084621Svikram 		 */
4085621Svikram 		if (lp->cmd && strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4086621Svikram 			lp->entryNum = ++entryNum;
4087621Svikram 			/* fixup the bootadm header */
4088621Svikram 			if (prev && prev->flags == BAM_COMMENT &&
40893446Smrj 			    prev->arg &&
40903446Smrj 			    strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4091621Svikram 				prev->entryNum = lp->entryNum;
4092621Svikram 			}
4093621Svikram 		} else {
4094621Svikram 			lp->entryNum = entryNum;
4095621Svikram 		}
4096621Svikram 	}
4097621Svikram 
4098621Svikram 	/*
4099621Svikram 	 * No default command in menu, simply return
4100621Svikram 	 */
4101621Svikram 	if (default_lp == NULL) {
4102621Svikram 		return;
4103621Svikram 	}
4104621Svikram 
4105621Svikram 	free(default_lp->arg);
4106621Svikram 	free(default_lp->line);
4107621Svikram 
4108621Svikram 	if (default_entry == NULL) {
4109621Svikram 		default_lp->arg = s_strdup("0");
4110621Svikram 	} else {
4111621Svikram 		(void) snprintf(buf, sizeof (buf), "%d",
4112621Svikram 		    default_entry->entryNum);
4113621Svikram 		default_lp->arg = s_strdup(buf);
4114621Svikram 	}
4115621Svikram 
4116621Svikram 	/*
4117621Svikram 	 * The following is required since only the line field gets
4118621Svikram 	 * written back to menu.lst
4119621Svikram 	 */
4120621Svikram 	(void) snprintf(buf, sizeof (buf), "%s%s%s",
4121621Svikram 	    menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4122621Svikram 	default_lp->line = s_strdup(buf);
4123621Svikram }
4124621Svikram 
4125621Svikram 
41260Sstevel@tonic-gate static menu_t *
41270Sstevel@tonic-gate menu_read(char *menu_path)
41280Sstevel@tonic-gate {
41290Sstevel@tonic-gate 	FILE *fp;
41300Sstevel@tonic-gate 	char buf[BAM_MAXLINE], *cp;
41310Sstevel@tonic-gate 	menu_t *mp;
41320Sstevel@tonic-gate 	int line, entry, len, n;
41330Sstevel@tonic-gate 
41340Sstevel@tonic-gate 	mp = s_calloc(1, sizeof (menu_t));
41350Sstevel@tonic-gate 
41360Sstevel@tonic-gate 	fp = fopen(menu_path, "r");
41370Sstevel@tonic-gate 	if (fp == NULL) { /* Let the caller handle this error */
41380Sstevel@tonic-gate 		return (mp);
41390Sstevel@tonic-gate 	}
41400Sstevel@tonic-gate 
41410Sstevel@tonic-gate 
41420Sstevel@tonic-gate 	/* Note: GRUB boot entry number starts with 0 */
41430Sstevel@tonic-gate 	line = LINE_INIT;
41440Sstevel@tonic-gate 	entry = ENTRY_INIT;
41450Sstevel@tonic-gate 	cp = buf;
41460Sstevel@tonic-gate 	len = sizeof (buf);
41470Sstevel@tonic-gate 	while (s_fgets(cp, len, fp) != NULL) {
41480Sstevel@tonic-gate 		n = strlen(cp);
41490Sstevel@tonic-gate 		if (cp[n - 1] == '\\') {
41500Sstevel@tonic-gate 			len -= n - 1;
41510Sstevel@tonic-gate 			assert(len >= 2);
41520Sstevel@tonic-gate 			cp += n - 1;
41530Sstevel@tonic-gate 			continue;
41540Sstevel@tonic-gate 		}
41550Sstevel@tonic-gate 		line_parser(mp, buf, &line, &entry);
41560Sstevel@tonic-gate 		cp = buf;
41570Sstevel@tonic-gate 		len = sizeof (buf);
41580Sstevel@tonic-gate 	}
41590Sstevel@tonic-gate 
41600Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
41610Sstevel@tonic-gate 		bam_error(CLOSE_FAIL, menu_path, strerror(errno));
41620Sstevel@tonic-gate 	}
41630Sstevel@tonic-gate 
41640Sstevel@tonic-gate 	return (mp);
41650Sstevel@tonic-gate }
41660Sstevel@tonic-gate 
41670Sstevel@tonic-gate static error_t
41680Sstevel@tonic-gate selector(menu_t *mp, char *opt, int *entry, char **title)
41690Sstevel@tonic-gate {
41700Sstevel@tonic-gate 	char *eq;
41710Sstevel@tonic-gate 	char *opt_dup;
41720Sstevel@tonic-gate 	int entryNum;
41730Sstevel@tonic-gate 
41740Sstevel@tonic-gate 	assert(mp);
41750Sstevel@tonic-gate 	assert(mp->start);
41760Sstevel@tonic-gate 	assert(opt);
41770Sstevel@tonic-gate 
41780Sstevel@tonic-gate 	opt_dup = s_strdup(opt);
41790Sstevel@tonic-gate 
41800Sstevel@tonic-gate 	if (entry)
41810Sstevel@tonic-gate 		*entry = ENTRY_INIT;
41820Sstevel@tonic-gate 	if (title)
41830Sstevel@tonic-gate 		*title = NULL;
41840Sstevel@tonic-gate 
41850Sstevel@tonic-gate 	eq = strchr(opt_dup, '=');
41860Sstevel@tonic-gate 	if (eq == NULL) {
41870Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
41880Sstevel@tonic-gate 		free(opt_dup);
41890Sstevel@tonic-gate 		return (BAM_ERROR);
41900Sstevel@tonic-gate 	}
41910Sstevel@tonic-gate 
41920Sstevel@tonic-gate 	*eq = '\0';
41930Sstevel@tonic-gate 	if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
41940Sstevel@tonic-gate 		assert(mp->end);
41950Sstevel@tonic-gate 		entryNum = s_strtol(eq + 1);
41960Sstevel@tonic-gate 		if (entryNum < 0 || entryNum > mp->end->entryNum) {
41970Sstevel@tonic-gate 			bam_error(INVALID_ENTRY, eq + 1);
41980Sstevel@tonic-gate 			free(opt_dup);
41990Sstevel@tonic-gate 			return (BAM_ERROR);
42000Sstevel@tonic-gate 		}
42010Sstevel@tonic-gate 		*entry = entryNum;
42020Sstevel@tonic-gate 	} else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
42030Sstevel@tonic-gate 		*title = opt + (eq - opt_dup) + 1;
42040Sstevel@tonic-gate 	} else {
42050Sstevel@tonic-gate 		bam_error(INVALID_OPT, opt);
42060Sstevel@tonic-gate 		free(opt_dup);
42070Sstevel@tonic-gate 		return (BAM_ERROR);
42080Sstevel@tonic-gate 	}
42090Sstevel@tonic-gate 
42100Sstevel@tonic-gate 	free(opt_dup);
42110Sstevel@tonic-gate 	return (BAM_SUCCESS);
42120Sstevel@tonic-gate }
42130Sstevel@tonic-gate 
42140Sstevel@tonic-gate /*
42150Sstevel@tonic-gate  * If invoked with no titles/entries (opt == NULL)
42160Sstevel@tonic-gate  * only title lines in file are printed.
42170Sstevel@tonic-gate  *
42180Sstevel@tonic-gate  * If invoked with a title or entry #, all
42190Sstevel@tonic-gate  * lines in *every* matching entry are listed
42200Sstevel@tonic-gate  */
42210Sstevel@tonic-gate static error_t
42220Sstevel@tonic-gate list_entry(menu_t *mp, char *menu_path, char *opt)
42230Sstevel@tonic-gate {
42240Sstevel@tonic-gate 	line_t *lp;
42250Sstevel@tonic-gate 	int entry = ENTRY_INIT;
42260Sstevel@tonic-gate 	int found;
42270Sstevel@tonic-gate 	char *title = NULL;
42280Sstevel@tonic-gate 
42290Sstevel@tonic-gate 	assert(mp);
42300Sstevel@tonic-gate 	assert(menu_path);
42310Sstevel@tonic-gate 
42326448Svikram 	/* opt is optional */
42336448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
42346448Svikram 	    opt ? opt : "<NULL>"));
42356448Svikram 
42360Sstevel@tonic-gate 	if (mp->start == NULL) {
42370Sstevel@tonic-gate 		bam_error(NO_MENU, menu_path);
42380Sstevel@tonic-gate 		return (BAM_ERROR);
42390Sstevel@tonic-gate 	}
42400Sstevel@tonic-gate 
42410Sstevel@tonic-gate 	if (opt != NULL) {
42420Sstevel@tonic-gate 		if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
42430Sstevel@tonic-gate 			return (BAM_ERROR);
42440Sstevel@tonic-gate 		}
42450Sstevel@tonic-gate 		assert((entry != ENTRY_INIT) ^ (title != NULL));
42460Sstevel@tonic-gate 	} else {
42470Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
42480Sstevel@tonic-gate 		(void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
42490Sstevel@tonic-gate 	}
42500Sstevel@tonic-gate 
42510Sstevel@tonic-gate 	found = 0;
42520Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
42530Sstevel@tonic-gate 		if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
42540Sstevel@tonic-gate 			continue;
42550Sstevel@tonic-gate 		if (opt == NULL && lp->flags == BAM_TITLE) {
42560Sstevel@tonic-gate 			bam_print(PRINT_TITLE, lp->entryNum,
42570Sstevel@tonic-gate 			    lp->arg);
42580Sstevel@tonic-gate 			found = 1;
42590Sstevel@tonic-gate 			continue;
42600Sstevel@tonic-gate 		}
42610Sstevel@tonic-gate 		if (entry != ENTRY_INIT && lp->entryNum == entry) {
42620Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
42630Sstevel@tonic-gate 			found = 1;
42640Sstevel@tonic-gate 			continue;
42650Sstevel@tonic-gate 		}
42660Sstevel@tonic-gate 
42670Sstevel@tonic-gate 		/*
42680Sstevel@tonic-gate 		 * We set the entry value here so that all lines
42690Sstevel@tonic-gate 		 * in entry get printed. If we subsequently match
42700Sstevel@tonic-gate 		 * title in other entries, all lines in those
42710Sstevel@tonic-gate 		 * entries get printed as well.
42720Sstevel@tonic-gate 		 */
42730Sstevel@tonic-gate 		if (title && lp->flags == BAM_TITLE && lp->arg &&
42740Sstevel@tonic-gate 		    strncmp(title, lp->arg, strlen(title)) == 0) {
42750Sstevel@tonic-gate 			bam_print(PRINT, lp->line);
42760Sstevel@tonic-gate 			entry = lp->entryNum;
42770Sstevel@tonic-gate 			found = 1;
42780Sstevel@tonic-gate 			continue;
42790Sstevel@tonic-gate 		}
42800Sstevel@tonic-gate 	}
42810Sstevel@tonic-gate 
42820Sstevel@tonic-gate 	if (!found) {
42830Sstevel@tonic-gate 		bam_error(NO_MATCH_ENTRY);
42840Sstevel@tonic-gate 		return (BAM_ERROR);
42850Sstevel@tonic-gate 	}
42860Sstevel@tonic-gate 
42870Sstevel@tonic-gate 	return (BAM_SUCCESS);
42880Sstevel@tonic-gate }
42890Sstevel@tonic-gate 
42905084Sjohnlev int
42910Sstevel@tonic-gate add_boot_entry(menu_t *mp,
42920Sstevel@tonic-gate 	char *title,
42936448Svikram 	char *findroot,
42940Sstevel@tonic-gate 	char *kernel,
42955084Sjohnlev 	char *mod_kernel,
42960Sstevel@tonic-gate 	char *module)
42970Sstevel@tonic-gate {
42986448Svikram 	int		lineNum;
42996448Svikram 	int		entryNum;
43006448Svikram 	char		linebuf[BAM_MAXLINE];
43016448Svikram 	menu_cmd_t	k_cmd;
43026448Svikram 	menu_cmd_t	m_cmd;
43036448Svikram 	const char	*fcn = "add_boot_entry()";
43040Sstevel@tonic-gate 
43050Sstevel@tonic-gate 	assert(mp);
43060Sstevel@tonic-gate 
43076448Svikram 	INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
43086448Svikram 	if (findroot == NULL) {
43096448Svikram 		bam_error(NULL_FINDROOT);
43106448Svikram 		return (BAM_ERROR);
43116448Svikram 	}
43126448Svikram 
43130Sstevel@tonic-gate 	if (title == NULL) {
4314656Sszhou 		title = "Solaris";	/* default to Solaris */
43150Sstevel@tonic-gate 	}
43160Sstevel@tonic-gate 	if (kernel == NULL) {
43170Sstevel@tonic-gate 		bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
43180Sstevel@tonic-gate 		return (BAM_ERROR);
43190Sstevel@tonic-gate 	}
43200Sstevel@tonic-gate 	if (module == NULL) {
43213446Smrj 		if (bam_direct != BAM_DIRECT_DBOOT) {
43223446Smrj 			bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
43233446Smrj 			return (BAM_ERROR);
43243446Smrj 		}
43253446Smrj 
43263446Smrj 		/* Figure the commands out from the kernel line */
43273446Smrj 		if (strstr(kernel, "$ISADIR") != NULL) {
43283446Smrj 			module = DIRECT_BOOT_ARCHIVE;
43293446Smrj 			k_cmd = KERNEL_DOLLAR_CMD;
43303446Smrj 			m_cmd = MODULE_DOLLAR_CMD;
43313446Smrj 		} else if (strstr(kernel, "amd64") != NULL) {
43323446Smrj 			module = DIRECT_BOOT_ARCHIVE_64;
43333446Smrj 			k_cmd = KERNEL_CMD;
43343446Smrj 			m_cmd = MODULE_CMD;
43353446Smrj 		} else {
43363446Smrj 			module = DIRECT_BOOT_ARCHIVE_32;
43373446Smrj 			k_cmd = KERNEL_CMD;
43383446Smrj 			m_cmd = MODULE_CMD;
43393446Smrj 		}
43403446Smrj 	} else if ((bam_direct == BAM_DIRECT_DBOOT) &&
43413446Smrj 	    (strstr(kernel, "$ISADIR") != NULL)) {
43423446Smrj 		/*
43433446Smrj 		 * If it's a non-failsafe dboot kernel, use the "kernel$"
43443446Smrj 		 * command.  Otherwise, use "kernel".
43453446Smrj 		 */
43463446Smrj 		k_cmd = KERNEL_DOLLAR_CMD;
43473446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
43483446Smrj 	} else {
43493446Smrj 		k_cmd = KERNEL_CMD;
43503446Smrj 		m_cmd = MODULE_CMD;
43510Sstevel@tonic-gate 	}
43520Sstevel@tonic-gate 
43530Sstevel@tonic-gate 	if (mp->start) {
43540Sstevel@tonic-gate 		lineNum = mp->end->lineNum;
43550Sstevel@tonic-gate 		entryNum = mp->end->entryNum;
43560Sstevel@tonic-gate 	} else {
43570Sstevel@tonic-gate 		lineNum = LINE_INIT;
43580Sstevel@tonic-gate 		entryNum = ENTRY_INIT;
43590Sstevel@tonic-gate 	}
43600Sstevel@tonic-gate 
43610Sstevel@tonic-gate 	/*
43620Sstevel@tonic-gate 	 * No separator for comment (HDR/FTR) commands
43630Sstevel@tonic-gate 	 * The syntax for comments is #<comment>
43640Sstevel@tonic-gate 	 */
43650Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
43663446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
4367662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
43680Sstevel@tonic-gate 
43690Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
43700Sstevel@tonic-gate 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
4371662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
4372662Sszhou 
43736448Svikram 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
43746448Svikram 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
43756448Svikram 	line_parser(mp, linebuf, &lineNum, &entryNum);
43766448Svikram 	BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
43770Sstevel@tonic-gate 
43780Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
43793446Smrj 	    menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
4380662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
43810Sstevel@tonic-gate 
43825084Sjohnlev 	if (mod_kernel != NULL) {
43835084Sjohnlev 		(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
43845084Sjohnlev 		    menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
43855084Sjohnlev 		line_parser(mp, linebuf, &lineNum, &entryNum);
43865084Sjohnlev 	}
43875084Sjohnlev 
43880Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
43893446Smrj 	    menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
4390662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
43910Sstevel@tonic-gate 
43920Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s",
43933446Smrj 	    menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
4394662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
43950Sstevel@tonic-gate 
43960Sstevel@tonic-gate 	return (entryNum);
43970Sstevel@tonic-gate }
43980Sstevel@tonic-gate 
43990Sstevel@tonic-gate static error_t
44000Sstevel@tonic-gate do_delete(menu_t *mp, int entryNum)
44010Sstevel@tonic-gate {
44026448Svikram 	line_t		*lp;
44036448Svikram 	line_t		*freed;
44046448Svikram 	entry_t		*ent;
44056448Svikram 	entry_t		*tmp;
44066448Svikram 	int		deleted;
44076448Svikram 	const char	*fcn = "do_delete()";
44080Sstevel@tonic-gate 
44090Sstevel@tonic-gate 	assert(entryNum != ENTRY_INIT);
44100Sstevel@tonic-gate 
44116448Svikram 	tmp = NULL;
44126448Svikram 
4413662Sszhou 	ent = mp->entries;
4414662Sszhou 	while (ent) {
4415662Sszhou 		lp = ent->start;
4416662Sszhou 		/* check entry number and make sure it's a bootadm entry */
4417662Sszhou 		if (lp->flags != BAM_COMMENT ||
44183446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0 ||
4419662Sszhou 		    (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
4420662Sszhou 			ent = ent->next;
44210Sstevel@tonic-gate 			continue;
44220Sstevel@tonic-gate 		}
44230Sstevel@tonic-gate 
4424662Sszhou 		/* free the entry content */
4425662Sszhou 		do {
4426662Sszhou 			freed = lp;
4427662Sszhou 			lp = lp->next;	/* prev stays the same */
44286448Svikram 			BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
4429662Sszhou 			unlink_line(mp, freed);
4430662Sszhou 			line_free(freed);
4431662Sszhou 		} while (freed != ent->end);
4432662Sszhou 
4433662Sszhou 		/* free the entry_t structure */
44346448Svikram 		assert(tmp == NULL);
4435662Sszhou 		tmp = ent;
4436662Sszhou 		ent = ent->next;
4437662Sszhou 		if (tmp->prev)
4438662Sszhou 			tmp->prev->next = ent;
44390Sstevel@tonic-gate 		else
4440662Sszhou 			mp->entries = ent;
4441662Sszhou 		if (ent)
4442662Sszhou 			ent->prev = tmp->prev;
44436448Svikram 		BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
44446448Svikram 		free(tmp);
44456448Svikram 		tmp = NULL;
44460Sstevel@tonic-gate 		deleted = 1;
44470Sstevel@tonic-gate 	}
44480Sstevel@tonic-gate 
44496448Svikram 	assert(tmp == NULL);
44506448Svikram 
44510Sstevel@tonic-gate 	if (!deleted && entryNum != ALL_ENTRIES) {
44520Sstevel@tonic-gate 		bam_error(NO_BOOTADM_MATCH);
44530Sstevel@tonic-gate 		return (BAM_ERROR);
44540Sstevel@tonic-gate 	}
44550Sstevel@tonic-gate 
4456621Svikram 	/*
4457621Svikram 	 * Now that we have deleted an entry, update
4458621Svikram 	 * the entry numbering and the default cmd.
4459621Svikram 	 */
4460621Svikram 	update_numbering(mp);
4461621Svikram 
44620Sstevel@tonic-gate 	return (BAM_SUCCESS);
44630Sstevel@tonic-gate }
44640Sstevel@tonic-gate 
44650Sstevel@tonic-gate static error_t
44666448Svikram delete_all_entries(menu_t *mp, char *dummy, char *opt)
44670Sstevel@tonic-gate {
44680Sstevel@tonic-gate 	assert(mp);
44696448Svikram 	assert(dummy == NULL);
44700Sstevel@tonic-gate 	assert(opt == NULL);
44710Sstevel@tonic-gate 
44726448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
44736448Svikram 
44740Sstevel@tonic-gate 	if (mp->start == NULL) {
44756448Svikram 		bam_print(EMPTY_MENU);
44760Sstevel@tonic-gate 		return (BAM_SUCCESS);
44770Sstevel@tonic-gate 	}
44780Sstevel@tonic-gate 
44790Sstevel@tonic-gate 	if (do_delete(mp, ALL_ENTRIES) != BAM_SUCCESS) {
44800Sstevel@tonic-gate 		return (BAM_ERROR);
44810Sstevel@tonic-gate 	}
44820Sstevel@tonic-gate 
44830Sstevel@tonic-gate 	return (BAM_WRITE);
44840Sstevel@tonic-gate }
44850Sstevel@tonic-gate 
44860Sstevel@tonic-gate static FILE *
44876448Svikram create_diskmap(char *osroot)
44880Sstevel@tonic-gate {
44890Sstevel@tonic-gate 	FILE *fp;
44900Sstevel@tonic-gate 	char cmd[PATH_MAX];
44916448Svikram 	const char *fcn = "create_diskmap()";
44920Sstevel@tonic-gate 
44930Sstevel@tonic-gate 	/* make sure we have a map file */
44940Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
44950Sstevel@tonic-gate 	if (fp == NULL) {
44960Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
44976448Svikram 		    "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
44986448Svikram 		if (exec_cmd(cmd, NULL) != 0)
44996448Svikram 			return (NULL);
45000Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
45016448Svikram 		INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
45026448Svikram 		if (fp) {
45036448Svikram 			BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
45046448Svikram 		} else {
45056448Svikram 			BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
45066448Svikram 		}
45070Sstevel@tonic-gate 	}
45080Sstevel@tonic-gate 	return (fp);
45090Sstevel@tonic-gate }
45100Sstevel@tonic-gate 
45110Sstevel@tonic-gate #define	SECTOR_SIZE	512
45120Sstevel@tonic-gate 
45130Sstevel@tonic-gate static int
45140Sstevel@tonic-gate get_partition(char *device)
45150Sstevel@tonic-gate {
45160Sstevel@tonic-gate 	int i, fd, is_pcfs, partno = -1;
45170Sstevel@tonic-gate 	struct mboot *mboot;
45180Sstevel@tonic-gate 	char boot_sect[SECTOR_SIZE];
45190Sstevel@tonic-gate 	char *wholedisk, *slice;
45200Sstevel@tonic-gate 
45210Sstevel@tonic-gate 	/* form whole disk (p0) */
45220Sstevel@tonic-gate 	slice = device + strlen(device) - 2;
45230Sstevel@tonic-gate 	is_pcfs = (*slice != 's');
45240Sstevel@tonic-gate 	if (!is_pcfs)
45250Sstevel@tonic-gate 		*slice = '\0';
45260Sstevel@tonic-gate 	wholedisk = s_calloc(1, strlen(device) + 3);
45270Sstevel@tonic-gate 	(void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
45280Sstevel@tonic-gate 	if (!is_pcfs)
45290Sstevel@tonic-gate 		*slice = 's';
45300Sstevel@tonic-gate 
45310Sstevel@tonic-gate 	/* read boot sector */
45320Sstevel@tonic-gate 	fd = open(wholedisk, O_RDONLY);
45330Sstevel@tonic-gate 	free(wholedisk);
45340Sstevel@tonic-gate 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
45350Sstevel@tonic-gate 		return (partno);
45360Sstevel@tonic-gate 	}
45370Sstevel@tonic-gate 	(void) close(fd);
45380Sstevel@tonic-gate 
45390Sstevel@tonic-gate 	/* parse fdisk table */
45400Sstevel@tonic-gate 	mboot = (struct mboot *)((void *)boot_sect);
45410Sstevel@tonic-gate 	for (i = 0; i < FD_NUMPART; i++) {
45420Sstevel@tonic-gate 		struct ipart *part =
45430Sstevel@tonic-gate 		    (struct ipart *)(uintptr_t)mboot->parts + i;
45440Sstevel@tonic-gate 		if (is_pcfs) {	/* looking for solaris boot part */
45450Sstevel@tonic-gate 			if (part->systid == 0xbe) {
45460Sstevel@tonic-gate 				partno = i;
45470Sstevel@tonic-gate 				break;
45480Sstevel@tonic-gate 			}
45490Sstevel@tonic-gate 		} else {	/* look for solaris partition, old and new */
45500Sstevel@tonic-gate 			if (part->systid == SUNIXOS ||
45510Sstevel@tonic-gate 			    part->systid == SUNIXOS2) {
45520Sstevel@tonic-gate 				partno = i;
45530Sstevel@tonic-gate 				break;
45540Sstevel@tonic-gate 			}
45550Sstevel@tonic-gate 		}
45560Sstevel@tonic-gate 	}
45570Sstevel@tonic-gate 	return (partno);
45580Sstevel@tonic-gate }
45590Sstevel@tonic-gate 
45606448Svikram char *
45616448Svikram get_grubroot(char *osroot, char *osdev, char *menu_root)
45626448Svikram {
45636448Svikram 	char		*grubroot;	/* (hd#,#,#) */
45646448Svikram 	char		*slice;
45656448Svikram 	char		*grubhd;
45666448Svikram 	int		fdiskpart;
45676448Svikram 	int		found = 0;
45686448Svikram 	char		*devname;
45696448Svikram 	char		*ctdname = strstr(osdev, "dsk/");
45706448Svikram 	char		linebuf[PATH_MAX];
45716448Svikram 	FILE		*fp;
45726448Svikram 
45736448Svikram 	INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
45746448Svikram 	if (ctdname == NULL) {
45756448Svikram 		bam_error(INVALID_DEV_DSK, osdev);
45760Sstevel@tonic-gate 		return (NULL);
45776448Svikram 	}
45786448Svikram 
45796448Svikram 	if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
45806448Svikram 		/* menu bears no resemblance to our reality */
45818642SVikram.Hegde@Sun.COM 		bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev);
45826448Svikram 		return (NULL);
45836448Svikram 	}
45840Sstevel@tonic-gate 
45850Sstevel@tonic-gate 	ctdname += strlen("dsk/");
45860Sstevel@tonic-gate 	slice = strrchr(ctdname, 's');
45870Sstevel@tonic-gate 	if (slice)
45880Sstevel@tonic-gate 		*slice = '\0';
45890Sstevel@tonic-gate 
45906448Svikram 	fp = create_diskmap(osroot);
45916448Svikram 	if (fp == NULL) {
45926448Svikram 		bam_error(DISKMAP_FAIL, osroot);
45936448Svikram 		return (NULL);
45946448Svikram 	}
45956448Svikram 
45960Sstevel@tonic-gate 	rewind(fp);
45970Sstevel@tonic-gate 	while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
45980Sstevel@tonic-gate 		grubhd = strtok(linebuf, " \t\n");
45990Sstevel@tonic-gate 		if (grubhd)
46000Sstevel@tonic-gate 			devname = strtok(NULL, " \t\n");
46010Sstevel@tonic-gate 		else
46020Sstevel@tonic-gate 			devname = NULL;
46030Sstevel@tonic-gate 		if (devname && strcmp(devname, ctdname) == 0) {
46040Sstevel@tonic-gate 			found = 1;
46050Sstevel@tonic-gate 			break;
46060Sstevel@tonic-gate 		}
46070Sstevel@tonic-gate 	}
46080Sstevel@tonic-gate 
46090Sstevel@tonic-gate 	if (slice)
46100Sstevel@tonic-gate 		*slice = 's';
46110Sstevel@tonic-gate 
46126448Svikram 	(void) fclose(fp);
46136448Svikram 	fp = NULL;
46146448Svikram 
46156448Svikram 	INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
46160Sstevel@tonic-gate 	if (found == 0) {
46178642SVikram.Hegde@Sun.COM 		bam_error(BIOSDEV_SKIP, osdev);
46180Sstevel@tonic-gate 		return (NULL);
46196448Svikram 	}
46206448Svikram 
46216448Svikram 	fdiskpart = get_partition(osdev);
46226448Svikram 	INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1);
46236448Svikram 	if (fdiskpart == -1) {
46246448Svikram 		bam_error(FDISKPART_FAIL, osdev);
46256448Svikram 		return (NULL);
46266448Svikram 	}
46276448Svikram 
46286448Svikram 	grubroot = s_calloc(1, 10);
46290Sstevel@tonic-gate 	if (slice) {
46306448Svikram 		(void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
46310Sstevel@tonic-gate 		    grubhd, fdiskpart, slice[1] + 'a' - '0');
46320Sstevel@tonic-gate 	} else
46336448Svikram 		(void) snprintf(grubroot, 10, "(hd%s,%d)",
46340Sstevel@tonic-gate 		    grubhd, fdiskpart);
46350Sstevel@tonic-gate 
46366448Svikram 	assert(fp == NULL);
46376448Svikram 	assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
46386448Svikram 	return (grubroot);
46396448Svikram }
46406448Svikram 
46416448Svikram static char *
46426448Svikram find_primary_common(char *mntpt, char *fstype)
46436448Svikram {
46446448Svikram 	char		signdir[PATH_MAX];
46456448Svikram 	char		tmpsign[MAXNAMELEN + 1];
46466448Svikram 	char		*lu;
46476448Svikram 	char		*ufs;
46486448Svikram 	char		*zfs;
46496448Svikram 	DIR		*dirp = NULL;
46506448Svikram 	struct dirent	*entp;
46516448Svikram 	struct stat	sb;
46526448Svikram 	const char	*fcn = "find_primary_common()";
46536448Svikram 
46546448Svikram 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
46556448Svikram 	    mntpt, GRUBSIGN_DIR);
46566448Svikram 
46576448Svikram 	if (stat(signdir, &sb) == -1) {
46586448Svikram 		BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
46596448Svikram 		return (NULL);
46606448Svikram 	}
46616448Svikram 
46626448Svikram 	dirp = opendir(signdir);
46636448Svikram 	INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
46646448Svikram 	if (dirp == NULL) {
46656448Svikram 		bam_error(OPENDIR_FAILED, signdir, strerror(errno));
46666448Svikram 		return (NULL);
46676448Svikram 	}
46686448Svikram 
46696448Svikram 	ufs = zfs = lu = NULL;
46706448Svikram 
46716448Svikram 	while (entp = readdir(dirp)) {
46726448Svikram 		if (strcmp(entp->d_name, ".") == 0 ||
46736448Svikram 		    strcmp(entp->d_name, "..") == 0)
46746448Svikram 			continue;
46756448Svikram 
46766448Svikram 		(void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
46776448Svikram 
46786448Svikram 		if (lu == NULL &&
46796448Svikram 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
46806448Svikram 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
46816448Svikram 			lu = s_strdup(tmpsign);
46826448Svikram 		}
46836448Svikram 
46846448Svikram 		if (ufs == NULL &&
46856448Svikram 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
46866448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
46876448Svikram 			ufs = s_strdup(tmpsign);
46886448Svikram 		}
46896448Svikram 
46906448Svikram 		if (zfs == NULL &&
46916448Svikram 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
46926448Svikram 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
46936448Svikram 			zfs = s_strdup(tmpsign);
46946448Svikram 		}
46956448Svikram 	}
46966448Svikram 
46976448Svikram 	BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
46986448Svikram 	    zfs ? zfs : "NULL",
46996448Svikram 	    ufs ? ufs : "NULL",
47006448Svikram 	    lu ? lu : "NULL"));
47016448Svikram 
47026448Svikram 	if (dirp) {
47036448Svikram 		(void) closedir(dirp);
47046448Svikram 		dirp = NULL;
47056448Svikram 	}
47066448Svikram 
47076448Svikram 	if (strcmp(fstype, "ufs") == 0 && zfs) {
47086448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
47096448Svikram 		free(zfs);
47106448Svikram 		zfs = NULL;
47116448Svikram 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
47126448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
47136448Svikram 		free(ufs);
47146448Svikram 		ufs = NULL;
47156448Svikram 	}
47166448Svikram 
47176448Svikram 	assert(dirp == NULL);
47186448Svikram 
47196448Svikram 	/* For now, we let Live Upgrade take care of its signature itself */
47206448Svikram 	if (lu) {
47216448Svikram 		BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
47226448Svikram 		free(lu);
47236448Svikram 		lu = NULL;
47246448Svikram 	}
47256448Svikram 
47266448Svikram 	return (zfs ? zfs : ufs);
47276448Svikram }
47286448Svikram 
47296448Svikram static char *
47306448Svikram find_backup_common(char *mntpt, char *fstype)
47316448Svikram {
47326448Svikram 	FILE		*bfp = NULL;
47336448Svikram 	char		tmpsign[MAXNAMELEN + 1];
47346448Svikram 	char		backup[PATH_MAX];
47356448Svikram 	char		*ufs;
47366448Svikram 	char		*zfs;
47376448Svikram 	char		*lu;
47386448Svikram 	int		error;
47396448Svikram 	const char	*fcn = "find_backup_common()";
47406448Svikram 
47416448Svikram 	/*
47426448Svikram 	 * We didn't find it in the primary directory.
47436448Svikram 	 * Look at the backup
47446448Svikram 	 */
47456448Svikram 	(void) snprintf(backup, sizeof (backup), "%s%s",
47466448Svikram 	    mntpt, GRUBSIGN_BACKUP);
47476448Svikram 
47486448Svikram 	bfp = fopen(backup, "r");
47496448Svikram 	if (bfp == NULL) {
47506448Svikram 		error = errno;
47516448Svikram 		if (bam_verbose) {
47526448Svikram 			bam_error(OPEN_FAIL, backup, strerror(error));
47536448Svikram 		}
47546448Svikram 		BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
47556448Svikram 		return (NULL);
47566448Svikram 	}
47576448Svikram 
47586448Svikram 	ufs = zfs = lu = NULL;
47596448Svikram 
47606448Svikram 	while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
47616448Svikram 
47626448Svikram 		if (lu == NULL &&
47636448Svikram 		    strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
47646448Svikram 		    strlen(GRUBSIGN_LU_PREFIX)) == 0) {
47656448Svikram 			lu = s_strdup(tmpsign);
47666448Svikram 		}
47676448Svikram 
47686448Svikram 		if (ufs == NULL &&
47696448Svikram 		    strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
47706448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
47716448Svikram 			ufs = s_strdup(tmpsign);
47726448Svikram 		}
47736448Svikram 
47746448Svikram 		if (zfs == NULL &&
47756448Svikram 		    strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
47766448Svikram 		    strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
47776448Svikram 			zfs = s_strdup(tmpsign);
47786448Svikram 		}
47796448Svikram 	}
47806448Svikram 
47816448Svikram 	BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
47826448Svikram 	    zfs ? zfs : "NULL",
47836448Svikram 	    ufs ? ufs : "NULL",
47846448Svikram 	    lu ? lu : "NULL"));
47856448Svikram 
47866448Svikram 	if (bfp) {
47876448Svikram 		(void) fclose(bfp);
47886448Svikram 		bfp = NULL;
47896448Svikram 	}
47906448Svikram 
47916448Svikram 	if (strcmp(fstype, "ufs") == 0 && zfs) {
47926448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
47936448Svikram 		free(zfs);
47946448Svikram 		zfs = NULL;
47956448Svikram 	} else if (strcmp(fstype, "zfs") == 0 && ufs) {
47966448Svikram 		bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
47976448Svikram 		free(ufs);
47986448Svikram 		ufs = NULL;
47996448Svikram 	}
48006448Svikram 
48016448Svikram 	assert(bfp == NULL);
48026448Svikram 
48036448Svikram 	/* For now, we let Live Upgrade take care of its signature itself */
48046448Svikram 	if (lu) {
48056448Svikram 		BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
48066448Svikram 		free(lu);
48076448Svikram 		lu = NULL;
48086448Svikram 	}
48096448Svikram 
48106448Svikram 	return (zfs ? zfs : ufs);
48116448Svikram }
48126448Svikram 
48136448Svikram static char *
48146448Svikram find_ufs_existing(char *osroot)
48156448Svikram {
48166448Svikram 	char		*sign;
48176448Svikram 	const char	*fcn = "find_ufs_existing()";
48186448Svikram 
48196448Svikram 	sign = find_primary_common(osroot, "ufs");
48206448Svikram 	if (sign == NULL) {
48216448Svikram 		sign = find_backup_common(osroot, "ufs");
48226448Svikram 		BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
48236448Svikram 	} else {
48246448Svikram 		BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
48256448Svikram 	}
48266448Svikram 
48276448Svikram 	return (sign);
48286448Svikram }
48296448Svikram 
48306448Svikram char *
48316448Svikram get_mountpoint(char *special, char *fstype)
48326448Svikram {
48336448Svikram 	FILE		*mntfp;
48346448Svikram 	struct mnttab	mp = {0};
48356448Svikram 	struct mnttab	mpref = {0};
48366448Svikram 	int		error;
48376448Svikram 	int		ret;
48386448Svikram 	const char	*fcn = "get_mountpoint()";
48396448Svikram 
48406448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
48416448Svikram 
48426448Svikram 	mntfp = fopen(MNTTAB, "r");
48436448Svikram 	error = errno;
48446448Svikram 	INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
48456448Svikram 	if (mntfp == NULL) {
48466448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
48476448Svikram 		return (NULL);
48486448Svikram 	}
48496448Svikram 
48506448Svikram 	mpref.mnt_special = special;
48516448Svikram 	mpref.mnt_fstype = fstype;
48526448Svikram 
48536448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
48546448Svikram 	INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
48556448Svikram 	if (ret != 0) {
48566448Svikram 		(void) fclose(mntfp);
48576448Svikram 		BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
48586448Svikram 		return (NULL);
48596448Svikram 	}
48606448Svikram 	(void) fclose(mntfp);
48616448Svikram 
48626448Svikram 	assert(mp.mnt_mountp);
48636448Svikram 
48646448Svikram 	BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
48656448Svikram 
48666448Svikram 	return (s_strdup(mp.mnt_mountp));
48676448Svikram }
48686448Svikram 
48696448Svikram /*
48706448Svikram  * Mounts a "legacy" top dataset (if needed)
48716448Svikram  * Returns:	The mountpoint of the legacy top dataset or NULL on error
48726448Svikram  * 		mnted returns one of the above values defined for zfs_mnted_t
48736448Svikram  */
48746448Svikram static char *
48756448Svikram mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
48766448Svikram {
48776448Svikram 	char		cmd[PATH_MAX];
48786448Svikram 	char		tmpmnt[PATH_MAX];
48796448Svikram 	filelist_t	flist = {0};
48806448Svikram 	char		*is_mounted;
48816448Svikram 	struct stat	sb;
48826448Svikram 	int		ret;
48836448Svikram 	const char	*fcn = "mount_legacy_dataset()";
48846448Svikram 
48856448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
48866448Svikram 
48876448Svikram 	*mnted = ZFS_MNT_ERROR;
48886448Svikram 
48896448Svikram 	(void) snprintf(cmd, sizeof (cmd),
48906448Svikram 	    "/sbin/zfs get -Ho value mounted %s",
48916448Svikram 	    pool);
48926448Svikram 
48936448Svikram 	ret = exec_cmd(cmd, &flist);
48946448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
48956448Svikram 	if (ret != 0) {
48966448Svikram 		bam_error(ZFS_MNTED_FAILED, pool);
48976448Svikram 		return (NULL);
48986448Svikram 	}
48996448Svikram 
49006448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
49016448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
49026448Svikram 		bam_error(BAD_ZFS_MNTED, pool);
49036448Svikram 		filelist_free(&flist);
49046448Svikram 		return (NULL);
49056448Svikram 	}
49066448Svikram 
49076448Svikram 	is_mounted = strtok(flist.head->line, " \t\n");
49086448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
49096448Svikram 	INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
49106448Svikram 	if (strcmp(is_mounted, "no") != 0) {
49116448Svikram 		filelist_free(&flist);
49126448Svikram 		*mnted = LEGACY_ALREADY;
49136448Svikram 		/* get_mountpoint returns a strdup'ed string */
49146448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
49156448Svikram 		return (get_mountpoint(pool, "zfs"));
49166448Svikram 	}
49176448Svikram 
49186448Svikram 	filelist_free(&flist);
49196448Svikram 
49206448Svikram 	/*
49216448Svikram 	 * legacy top dataset is not mounted. Mount it now
49226448Svikram 	 * First create a mountpoint.
49236448Svikram 	 */
49246448Svikram 	(void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
49256448Svikram 	    ZFS_LEGACY_MNTPT, getpid());
49266448Svikram 
49276448Svikram 	ret = stat(tmpmnt, &sb);
49286448Svikram 	if (ret == -1) {
49296448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
49308735SEnrico.Perla@Sun.COM 		ret = mkdirp(tmpmnt, DIR_PERMS);
49316448Svikram 		INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
49326448Svikram 		if (ret == -1) {
49336448Svikram 			bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
49346448Svikram 			return (NULL);
49356448Svikram 		}
49366448Svikram 	} else {
49376448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
49386448Svikram 	}
49396448Svikram 
49406448Svikram 	(void) snprintf(cmd, sizeof (cmd),
49416448Svikram 	    "/sbin/mount -F zfs %s %s",
49426448Svikram 	    pool, tmpmnt);
49436448Svikram 
49446448Svikram 	ret = exec_cmd(cmd, NULL);
49456448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
49466448Svikram 	if (ret != 0) {
49476448Svikram 		bam_error(ZFS_MOUNT_FAILED, pool);
49486448Svikram 		(void) rmdir(tmpmnt);
49496448Svikram 		return (NULL);
49506448Svikram 	}
49516448Svikram 
49526448Svikram 	*mnted = LEGACY_MOUNTED;
49536448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
49546448Svikram 	return (s_strdup(tmpmnt));
49556448Svikram }
49566448Svikram 
49576448Svikram /*
49586448Svikram  * Mounts the top dataset (if needed)
49596448Svikram  * Returns:	The mountpoint of the top dataset or NULL on error
49606448Svikram  * 		mnted returns one of the above values defined for zfs_mnted_t
49616448Svikram  */
49626448Svikram static char *
49636448Svikram mount_top_dataset(char *pool, zfs_mnted_t *mnted)
49646448Svikram {
49656448Svikram 	char		cmd[PATH_MAX];
49666448Svikram 	filelist_t	flist = {0};
49676448Svikram 	char		*is_mounted;
49686448Svikram 	char		*mntpt;
49696448Svikram 	char		*zmntpt;
49706448Svikram 	int		ret;
49716448Svikram 	const char	*fcn = "mount_top_dataset()";
49726448Svikram 
49736448Svikram 	*mnted = ZFS_MNT_ERROR;
49746448Svikram 
49756448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
49766448Svikram 
49776448Svikram 	/*
49786448Svikram 	 * First check if the top dataset is a "legacy" dataset
49796448Svikram 	 */
49806448Svikram 	(void) snprintf(cmd, sizeof (cmd),
49816448Svikram 	    "/sbin/zfs get -Ho value mountpoint %s",
49826448Svikram 	    pool);
49836448Svikram 	ret = exec_cmd(cmd, &flist);
49846448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
49856448Svikram 	if (ret != 0) {
49866448Svikram 		bam_error(ZFS_MNTPT_FAILED, pool);
49876448Svikram 		return (NULL);
49886448Svikram 	}
49896448Svikram 
49906448Svikram 	if (flist.head && (flist.head == flist.tail)) {
49916448Svikram 		char *legacy = strtok(flist.head->line, " \t\n");
49926448Svikram 		if (legacy && strcmp(legacy, "legacy") == 0) {
49936448Svikram 			filelist_free(&flist);
49946448Svikram 			BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
49956448Svikram 			return (mount_legacy_dataset(pool, mnted));
49966448Svikram 		}
49976448Svikram 	}
49986448Svikram 
49996448Svikram 	filelist_free(&flist);
50006448Svikram 
50016448Svikram 	BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
50026448Svikram 
50036448Svikram 	(void) snprintf(cmd, sizeof (cmd),
50046448Svikram 	    "/sbin/zfs get -Ho value mounted %s",
50056448Svikram 	    pool);
50066448Svikram 
50076448Svikram 	ret = exec_cmd(cmd, &flist);
50086448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
50096448Svikram 	if (ret != 0) {
50106448Svikram 		bam_error(ZFS_MNTED_FAILED, pool);
50116448Svikram 		return (NULL);
50126448Svikram 	}
50136448Svikram 
50146448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
50156448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
50166448Svikram 		bam_error(BAD_ZFS_MNTED, pool);
50176448Svikram 		filelist_free(&flist);
50186448Svikram 		return (NULL);
50196448Svikram 	}
50206448Svikram 
50216448Svikram 	is_mounted = strtok(flist.head->line, " \t\n");
50226448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
50236448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
50246448Svikram 	if (strcmp(is_mounted, "no") != 0) {
50256448Svikram 		filelist_free(&flist);
50266448Svikram 		*mnted = ZFS_ALREADY;
50276448Svikram 		BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
50286448Svikram 		goto mounted;
50296448Svikram 	}
50306448Svikram 
50316448Svikram 	filelist_free(&flist);
50326448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
50336448Svikram 
50346448Svikram 	/* top dataset is not mounted. Mount it now */
50356448Svikram 	(void) snprintf(cmd, sizeof (cmd),
50366448Svikram 	    "/sbin/zfs mount %s", pool);
50376448Svikram 	ret = exec_cmd(cmd, NULL);
50386448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
50396448Svikram 	if (ret != 0) {
50406448Svikram 		bam_error(ZFS_MOUNT_FAILED, pool);
50416448Svikram 		return (NULL);
50426448Svikram 	}
50436448Svikram 	*mnted = ZFS_MOUNTED;
50446448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
50456448Svikram 	/*FALLTHRU*/
50466448Svikram mounted:
50476448Svikram 	/*
50486448Svikram 	 * Now get the mountpoint
50496448Svikram 	 */
50506448Svikram 	(void) snprintf(cmd, sizeof (cmd),
50516448Svikram 	    "/sbin/zfs get -Ho value mountpoint %s",
50526448Svikram 	    pool);
50536448Svikram 
50546448Svikram 	ret = exec_cmd(cmd, &flist);
50556448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
50566448Svikram 	if (ret != 0) {
50576448Svikram 		bam_error(ZFS_MNTPT_FAILED, pool);
50586448Svikram 		goto error;
50596448Svikram 	}
50606448Svikram 
50616448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
50626448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
50636448Svikram 		bam_error(NULL_ZFS_MNTPT, pool);
50646448Svikram 		goto error;
50656448Svikram 	}
50666448Svikram 
50676448Svikram 	mntpt = strtok(flist.head->line, " \t\n");
50686448Svikram 	INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
50696448Svikram 	if (*mntpt != '/') {
50706448Svikram 		bam_error(BAD_ZFS_MNTPT, pool, mntpt);
50716448Svikram 		goto error;
50726448Svikram 	}
50736448Svikram 	zmntpt = s_strdup(mntpt);
50746448Svikram 
50756448Svikram 	filelist_free(&flist);
50766448Svikram 
50776448Svikram 	BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
50786448Svikram 
50796448Svikram 	return (zmntpt);
50806448Svikram 
50816448Svikram error:
50826448Svikram 	filelist_free(&flist);
50836448Svikram 	(void) umount_top_dataset(pool, *mnted, NULL);
50846448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
50856448Svikram 	return (NULL);
50866448Svikram }
50876448Svikram 
50886448Svikram static int
50896448Svikram umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
50906448Svikram {
50916448Svikram 	char		cmd[PATH_MAX];
50926448Svikram 	int		ret;
50936448Svikram 	const char	*fcn = "umount_top_dataset()";
50946448Svikram 
50956448Svikram 	INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
50966448Svikram 	switch (mnted) {
50976448Svikram 	case LEGACY_ALREADY:
50986448Svikram 	case ZFS_ALREADY:
50996448Svikram 		/* nothing to do */
51006448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
51016448Svikram 		    mntpt ? mntpt : "NULL"));
51026448Svikram 		free(mntpt);
51036448Svikram 		return (BAM_SUCCESS);
51046448Svikram 	case LEGACY_MOUNTED:
51056448Svikram 		(void) snprintf(cmd, sizeof (cmd),
51066448Svikram 		    "/sbin/umount %s", pool);
51076448Svikram 		ret = exec_cmd(cmd, NULL);
51086448Svikram 		INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
51096448Svikram 		if (ret != 0) {
51106448Svikram 			bam_error(UMOUNT_FAILED, pool);
51116448Svikram 			free(mntpt);
51126448Svikram 			return (BAM_ERROR);
51136448Svikram 		}
51146448Svikram 		if (mntpt)
51156448Svikram 			(void) rmdir(mntpt);
51166448Svikram 		free(mntpt);
51176448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
51186448Svikram 		return (BAM_SUCCESS);
51196448Svikram 	case ZFS_MOUNTED:
51206448Svikram 		free(mntpt);
51216448Svikram 		(void) snprintf(cmd, sizeof (cmd),
51226448Svikram 		    "/sbin/zfs unmount %s", pool);
51236448Svikram 		ret = exec_cmd(cmd, NULL);
51246448Svikram 		INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
51256448Svikram 		if (ret != 0) {
51266448Svikram 			bam_error(UMOUNT_FAILED, pool);
51276448Svikram 			return (BAM_ERROR);
51286448Svikram 		}
51296448Svikram 		BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
51306448Svikram 		return (BAM_SUCCESS);
51316448Svikram 	default:
51326448Svikram 		bam_error(INT_BAD_MNTSTATE, pool);
51336448Svikram 		return (BAM_ERROR);
51346448Svikram 	}
51356448Svikram 	/*NOTREACHED*/
51366448Svikram }
51376448Svikram 
51386448Svikram /*
51396448Svikram  * For ZFS, osdev can be one of two forms
51406448Svikram  * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
51416448Svikram  * It can be a /dev/[r]dsk special file. We handle both instances
51426448Svikram  */
51436448Svikram static char *
51446448Svikram get_pool(char *osdev)
51456448Svikram {
51466448Svikram 	char		cmd[PATH_MAX];
51476448Svikram 	char		buf[PATH_MAX];
51486448Svikram 	filelist_t	flist = {0};
51496448Svikram 	char		*pool;
51506448Svikram 	char		*cp;
51516448Svikram 	char		*slash;
51526448Svikram 	int		ret;
51536448Svikram 	const char	*fcn = "get_pool()";
51546448Svikram 
51556448Svikram 	INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
51566448Svikram 	if (osdev == NULL) {
51576448Svikram 		bam_error(GET_POOL_OSDEV_NULL);
51586448Svikram 		return (NULL);
51596448Svikram 	}
51606448Svikram 
51616448Svikram 	BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
51626448Svikram 
51636448Svikram 	if (osdev[0] != '/') {
51646448Svikram 		(void) strlcpy(buf, osdev, sizeof (buf));
51656448Svikram 		slash = strchr(buf, '/');
51666448Svikram 		if (slash)
51676448Svikram 			*slash = '\0';
51686448Svikram 		pool = s_strdup(buf);
51696448Svikram 		BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
51706448Svikram 		return (pool);
51716448Svikram 	} else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
51726448Svikram 	    strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
51736448Svikram 		bam_error(GET_POOL_BAD_OSDEV, osdev);
51746448Svikram 		return (NULL);
51756448Svikram 	}
51766448Svikram 
51776448Svikram 	(void) snprintf(cmd, sizeof (cmd),
51786448Svikram 	    "/usr/sbin/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
51796448Svikram 	    osdev);
51806448Svikram 
51816448Svikram 	ret = exec_cmd(cmd, &flist);
51826448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
51836448Svikram 	if (ret != 0) {
51846448Svikram 		bam_error(FSTYP_A_FAILED, osdev);
51856448Svikram 		return (NULL);
51866448Svikram 	}
51876448Svikram 
51886448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
51896448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
51906448Svikram 		bam_error(NULL_FSTYP_A, osdev);
51916448Svikram 		filelist_free(&flist);
51926448Svikram 		return (NULL);
51936448Svikram 	}
51946448Svikram 
51956448Svikram 	(void) strtok(flist.head->line, "'");
51966448Svikram 	cp = strtok(NULL, "'");
51976448Svikram 	INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
51986448Svikram 	if (cp == NULL) {
51996448Svikram 		bam_error(BAD_FSTYP_A, osdev);
52006448Svikram 		filelist_free(&flist);
52016448Svikram 		return (NULL);
52026448Svikram 	}
52036448Svikram 
52046448Svikram 	pool = s_strdup(cp);
52056448Svikram 
52066448Svikram 	filelist_free(&flist);
52076448Svikram 
52086448Svikram 	BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
52096448Svikram 
52106448Svikram 	return (pool);
52116448Svikram }
52126448Svikram 
52136448Svikram static char *
52146448Svikram find_zfs_existing(char *osdev)
52156448Svikram {
52166448Svikram 	char		*pool;
52176448Svikram 	zfs_mnted_t	mnted;
52186448Svikram 	char		*mntpt;
52196448Svikram 	char		*sign;
52206448Svikram 	const char	*fcn = "find_zfs_existing()";
52216448Svikram 
52226448Svikram 	pool = get_pool(osdev);
52236448Svikram 	INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
52246448Svikram 	if (pool == NULL) {
52256448Svikram 		bam_error(ZFS_GET_POOL_FAILED, osdev);
52266448Svikram 		return (NULL);
52276448Svikram 	}
52286448Svikram 
52296448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
52306448Svikram 	INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
52316448Svikram 	if (mntpt == NULL) {
52326448Svikram 		bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
52336448Svikram 		free(pool);
52346448Svikram 		return (NULL);
52356448Svikram 	}
52366448Svikram 
52376448Svikram 	sign = find_primary_common(mntpt, "zfs");
52386448Svikram 	if (sign == NULL) {
52396448Svikram 		sign = find_backup_common(mntpt, "zfs");
52406448Svikram 		BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
52416448Svikram 	} else {
52426448Svikram 		BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
52436448Svikram 	}
52446448Svikram 
52456448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
52466448Svikram 
52476448Svikram 	free(pool);
52486448Svikram 
52496448Svikram 	return (sign);
52506448Svikram }
52516448Svikram 
52526448Svikram static char *
52536448Svikram find_existing_sign(char *osroot, char *osdev, char *fstype)
52546448Svikram {
52556448Svikram 	const char		*fcn = "find_existing_sign()";
52566448Svikram 
52576448Svikram 	INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
52586448Svikram 	if (strcmp(fstype, "ufs") == 0) {
52596448Svikram 		BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
52606448Svikram 		return (find_ufs_existing(osroot));
52616448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
52626448Svikram 		BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
52636448Svikram 		return (find_zfs_existing(osdev));
52646448Svikram 	} else {
52656448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
52666448Svikram 		return (NULL);
52676448Svikram 	}
52686448Svikram }
52696448Svikram 
52706448Svikram #define	MH_HASH_SZ	16
52716448Svikram 
52726448Svikram typedef enum {
52736448Svikram 	MH_ERROR = -1,
52746448Svikram 	MH_NOMATCH,
52756448Svikram 	MH_MATCH
52766448Svikram } mh_search_t;
52776448Svikram 
52786448Svikram typedef struct mcache {
52796448Svikram 	char	*mc_special;
52806448Svikram 	char	*mc_mntpt;
52816448Svikram 	char	*mc_fstype;
52826448Svikram 	struct mcache *mc_next;
52836448Svikram } mcache_t;
52846448Svikram 
52856448Svikram typedef struct mhash {
52866448Svikram 	mcache_t *mh_hash[MH_HASH_SZ];
52876448Svikram } mhash_t;
52886448Svikram 
52896448Svikram static int
52906448Svikram mhash_fcn(char *key)
52916448Svikram {
52926448Svikram 	int		i;
52936448Svikram 	uint64_t	sum = 0;
52946448Svikram 
52956448Svikram 	for (i = 0; key[i] != '\0'; i++) {
52966448Svikram 		sum += (uchar_t)key[i];
52976448Svikram 	}
52986448Svikram 
52996448Svikram 	sum %= MH_HASH_SZ;
53006448Svikram 
53016448Svikram 	assert(sum < MH_HASH_SZ);
53026448Svikram 
53036448Svikram 	return (sum);
53046448Svikram }
53056448Svikram 
53066448Svikram static mhash_t *
53076448Svikram cache_mnttab(void)
53086448Svikram {
53096448Svikram 	FILE		*mfp;
53106448Svikram 	struct extmnttab mnt;
53116448Svikram 	mcache_t	*mcp;
53126448Svikram 	mhash_t		*mhp;
53136448Svikram 	char		*ctds;
53146448Svikram 	int		idx;
53156448Svikram 	int		error;
53166448Svikram 	char		*special_dup;
53176448Svikram 	const char	*fcn = "cache_mnttab()";
53186448Svikram 
53196448Svikram 	mfp = fopen(MNTTAB, "r");
53206448Svikram 	error = errno;
53216448Svikram 	INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
53226448Svikram 	if (mfp == NULL) {
53236448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
53246448Svikram 		return (NULL);
53256448Svikram 	}
53266448Svikram 
53276448Svikram 	mhp = s_calloc(1, sizeof (mhash_t));
53286448Svikram 
53296448Svikram 	resetmnttab(mfp);
53306448Svikram 
53316448Svikram 	while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
53326448Svikram 		/* only cache ufs */
53336448Svikram 		if (strcmp(mnt.mnt_fstype, "ufs") != 0)
53346448Svikram 			continue;
53356448Svikram 
53366448Svikram 		/* basename() modifies its arg, so dup it */
53376448Svikram 		special_dup = s_strdup(mnt.mnt_special);
53386448Svikram 		ctds = basename(special_dup);
53396448Svikram 
53406448Svikram 		mcp = s_calloc(1, sizeof (mcache_t));
53416448Svikram 		mcp->mc_special = s_strdup(ctds);
53426448Svikram 		mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
53436448Svikram 		mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
53446448Svikram 		BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
53456448Svikram 		    mnt.mnt_mountp, mnt.mnt_fstype));
53466448Svikram 		idx = mhash_fcn(ctds);
53476448Svikram 		mcp->mc_next = mhp->mh_hash[idx];
53486448Svikram 		mhp->mh_hash[idx] = mcp;
53496448Svikram 		free(special_dup);
53506448Svikram 	}
53516448Svikram 
53526448Svikram 	(void) fclose(mfp);
53536448Svikram 
53546448Svikram 	return (mhp);
53556448Svikram }
53566448Svikram 
53576448Svikram static void
53586448Svikram free_mnttab(mhash_t *mhp)
53596448Svikram {
53606448Svikram 	mcache_t	*mcp;
53616448Svikram 	int		i;
53626448Svikram 
53636448Svikram 	for (i = 0; i < MH_HASH_SZ; i++) {
53646448Svikram 		/*LINTED*/
53656448Svikram 		while (mcp = mhp->mh_hash[i]) {
53666448Svikram 			mhp->mh_hash[i] = mcp->mc_next;
53676448Svikram 			free(mcp->mc_special);
53686448Svikram 			free(mcp->mc_mntpt);
53696448Svikram 			free(mcp->mc_fstype);
53706448Svikram 			free(mcp);
53716448Svikram 		}
53726448Svikram 	}
53736448Svikram 
53746448Svikram 	for (i = 0; i < MH_HASH_SZ; i++) {
53756448Svikram 		assert(mhp->mh_hash[i] == NULL);
53766448Svikram 	}
53776448Svikram 	free(mhp);
53786448Svikram }
53796448Svikram 
53806448Svikram static mh_search_t
53816448Svikram search_hash(mhash_t *mhp, char *special, char **mntpt)
53826448Svikram {
53836448Svikram 	int		idx;
53846448Svikram 	mcache_t	*mcp;
53856448Svikram 	const char 	*fcn = "search_hash()";
53866448Svikram 
53876448Svikram 	assert(mntpt);
53886448Svikram 
53896448Svikram 	*mntpt = NULL;
53906448Svikram 
53916448Svikram 	INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
53926448Svikram 	if (strchr(special, '/')) {
53936448Svikram 		bam_error(INVALID_MHASH_KEY, special);
53946448Svikram 		return (MH_ERROR);
53956448Svikram 	}
53966448Svikram 
53976448Svikram 	idx = mhash_fcn(special);
53986448Svikram 
53996448Svikram 	for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
54006448Svikram 		if (strcmp(mcp->mc_special, special) == 0)
54016448Svikram 			break;
54026448Svikram 	}
54036448Svikram 
54046448Svikram 	if (mcp == NULL) {
54056448Svikram 		BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
54066448Svikram 		return (MH_NOMATCH);
54076448Svikram 	}
54086448Svikram 
54096448Svikram 	assert(strcmp(mcp->mc_fstype, "ufs") == 0);
54106448Svikram 	*mntpt = mcp->mc_mntpt;
54116448Svikram 	BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
54126448Svikram 	return (MH_MATCH);
54136448Svikram }
54146448Svikram 
54156448Svikram static int
54166448Svikram check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
54176448Svikram {
54186448Svikram 	char		*sign;
54196448Svikram 	char		*signline;
54206448Svikram 	char		signbuf[MAXNAMELEN];
54216448Svikram 	int		len;
54226448Svikram 	int		error;
54236448Svikram 	const char	*fcn = "check_add_ufs_sign_to_list()";
54246448Svikram 
54256448Svikram 	/* safe to specify NULL as "osdev" arg for UFS */
54266448Svikram 	sign = find_existing_sign(mntpt, NULL, "ufs");
54276448Svikram 	if (sign == NULL) {
54286448Svikram 		/* No existing signature, nothing to add to list */
54296448Svikram 		BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
54306448Svikram 		return (0);
54316448Svikram 	}
54326448Svikram 
54336448Svikram 	(void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
54346448Svikram 	signline = signbuf;
54356448Svikram 
54366448Svikram 	INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
54376448Svikram 	if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
54386448Svikram 	    strlen(GRUBSIGN_UFS_PREFIX))) {
54396448Svikram 		bam_error(INVALID_UFS_SIGNATURE, sign);
54406448Svikram 		free(sign);
54416448Svikram 		/* ignore invalid signatures */
54426448Svikram 		return (0);
54436448Svikram 	}
54446448Svikram 
54456448Svikram 	len = fputs(signline, tfp);
54466448Svikram 	error = errno;
54476448Svikram 	INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
54486448Svikram 	if (len != strlen(signline)) {
54496448Svikram 		bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
54506448Svikram 		free(sign);
54516448Svikram 		return (-1);
54526448Svikram 	}
54536448Svikram 
54546448Svikram 	free(sign);
54556448Svikram 
54566448Svikram 	BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
54576448Svikram 	return (0);
54586448Svikram }
54596448Svikram 
54606448Svikram /*
54616448Svikram  * slice is a basename not a full pathname
54626448Svikram  */
54636448Svikram static int
54646448Svikram process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
54656448Svikram {
54666448Svikram 	int		ret;
54676448Svikram 	char		cmd[PATH_MAX];
54686448Svikram 	char		path[PATH_MAX];
54696448Svikram 	struct stat	sbuf;
54706448Svikram 	char		*mntpt;
54716448Svikram 	filelist_t	flist = {0};
54726448Svikram 	char		*fstype;
54736448Svikram 	char		blkslice[PATH_MAX];
54746448Svikram 	const char	*fcn = "process_slice_common()";
54756448Svikram 
54766448Svikram 
54776448Svikram 	ret = search_hash(mhp, slice, &mntpt);
54786448Svikram 	switch (ret) {
54796448Svikram 		case MH_MATCH:
54806448Svikram 			if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
54816448Svikram 				return (-1);
54826448Svikram 			else
54836448Svikram 				return (0);
54846448Svikram 		case MH_NOMATCH:
54856448Svikram 			break;
54866448Svikram 		case MH_ERROR:
54876448Svikram 		default:
54886448Svikram 			return (-1);
54896448Svikram 	}
54906448Svikram 
54916448Svikram 	(void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
54926448Svikram 	if (stat(path, &sbuf) == -1) {
54936448Svikram 		BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
54946448Svikram 		return (0);
54956448Svikram 	}
54966448Svikram 
54976448Svikram 	/* Check if ufs */
54986448Svikram 	(void) snprintf(cmd, sizeof (cmd),
54996448Svikram 	    "/usr/sbin/fstyp /dev/rdsk/%s 2>/dev/null",
55006448Svikram 	    slice);
55016448Svikram 
55026448Svikram 	if (exec_cmd(cmd, &flist) != 0) {
55036448Svikram 		if (bam_verbose)
55046448Svikram 			bam_print(FSTYP_FAILED, slice);
55056448Svikram 		return (0);
55066448Svikram 	}
55076448Svikram 
55086448Svikram 	if ((flist.head == NULL) || (flist.head != flist.tail)) {
55096448Svikram 		if (bam_verbose)
55106448Svikram 			bam_print(FSTYP_BAD, slice);
55116448Svikram 		filelist_free(&flist);
55126448Svikram 		return (0);
55136448Svikram 	}
55146448Svikram 
55156448Svikram 	fstype = strtok(flist.head->line, " \t\n");
55166448Svikram 	if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
55176448Svikram 		if (bam_verbose)
55186448Svikram 			bam_print(NOT_UFS_SLICE, slice, fstype);
55196448Svikram 		filelist_free(&flist);
55206448Svikram 		return (0);
55216448Svikram 	}
55226448Svikram 
55236448Svikram 	filelist_free(&flist);
55246448Svikram 
55256448Svikram 	/*
55266448Svikram 	 * Since we are mounting the filesystem read-only, the
55276448Svikram 	 * the last mount field of the superblock is unchanged
55286448Svikram 	 * and does not need to be fixed up post-mount;
55296448Svikram 	 */
55306448Svikram 
55316448Svikram 	(void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
55326448Svikram 	    slice);
55336448Svikram 
55346448Svikram 	(void) snprintf(cmd, sizeof (cmd),
55356448Svikram 	    "/usr/sbin/mount -F ufs -o ro %s %s "
55366448Svikram 	    "> /dev/null 2>&1", blkslice, tmpmnt);
55376448Svikram 
55386448Svikram 	if (exec_cmd(cmd, NULL) != 0) {
55396448Svikram 		if (bam_verbose)
55406448Svikram 			bam_print(MOUNT_FAILED, blkslice, "ufs");
55416448Svikram 		return (0);
55426448Svikram 	}
55436448Svikram 
55446448Svikram 	ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
55456448Svikram 
55466448Svikram 	(void) snprintf(cmd, sizeof (cmd),
55476448Svikram 	    "/usr/sbin/umount -f %s > /dev/null 2>&1",
55486448Svikram 	    tmpmnt);
55496448Svikram 
55506448Svikram 	if (exec_cmd(cmd, NULL) != 0) {
55516448Svikram 		bam_print(UMOUNT_FAILED, slice);
55526448Svikram 		return (0);
55536448Svikram 	}
55546448Svikram 
55556448Svikram 	return (ret);
55566448Svikram }
55576448Svikram 
55586448Svikram static int
55596448Svikram process_vtoc_slices(
55606448Svikram 	char *s0,
55616448Svikram 	struct vtoc *vtoc,
55626448Svikram 	FILE *tfp,
55636448Svikram 	mhash_t *mhp,
55646448Svikram 	char *tmpmnt)
55656448Svikram {
55666448Svikram 	int		idx;
55676448Svikram 	char		slice[PATH_MAX];
55686448Svikram 	size_t		len;
55696448Svikram 	char		*cp;
55706448Svikram 	const char	*fcn = "process_vtoc_slices()";
55716448Svikram 
55726448Svikram 	len = strlen(s0);
55736448Svikram 
55746448Svikram 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
55756448Svikram 
55766448Svikram 	s0[len - 1] = '\0';
55776448Svikram 
55786448Svikram 	(void) strlcpy(slice, s0, sizeof (slice));
55796448Svikram 
55806448Svikram 	s0[len - 1] = '0';
55816448Svikram 
55826448Svikram 	cp = slice + len - 1;
55836448Svikram 
55846448Svikram 	for (idx = 0; idx < vtoc->v_nparts; idx++) {
55856448Svikram 
55866448Svikram 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
55876448Svikram 
55886448Svikram 		if (vtoc->v_part[idx].p_size == 0) {
55896448Svikram 			BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
55906448Svikram 			continue;
55916448Svikram 		}
55926448Svikram 
55936448Svikram 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
55946448Svikram 		switch (vtoc->v_part[idx].p_tag) {
55956448Svikram 		case V_SWAP:
55966448Svikram 		case V_USR:
55976448Svikram 		case V_BACKUP:
55986448Svikram 		case V_VAR:
55996448Svikram 		case V_HOME:
56006448Svikram 		case V_ALTSCTR:
56016448Svikram 			BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
56026448Svikram 			continue;
56036448Svikram 		default:
56046448Svikram 			BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
56056448Svikram 			break;
56066448Svikram 		}
56076448Svikram 
56086448Svikram 		/* skip unmountable and readonly slices */
56096448Svikram 		switch (vtoc->v_part[idx].p_flag) {
56106448Svikram 		case V_UNMNT:
56116448Svikram 		case V_RONLY:
56126448Svikram 			BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
56136448Svikram 			continue;
56146448Svikram 		default:
56156448Svikram 			BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
56166448Svikram 			break;
56176448Svikram 		}
56186448Svikram 
56196448Svikram 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
56206448Svikram 			return (-1);
56216448Svikram 		}
56226448Svikram 	}
56236448Svikram 
56246448Svikram 	return (0);
56256448Svikram }
56266448Svikram 
56276448Svikram static int
56286448Svikram process_efi_slices(
56296448Svikram 	char *s0,
56306448Svikram 	struct dk_gpt *efi,
56316448Svikram 	FILE *tfp,
56326448Svikram 	mhash_t *mhp,
56336448Svikram 	char *tmpmnt)
56346448Svikram {
56356448Svikram 	int		idx;
56366448Svikram 	char		slice[PATH_MAX];
56376448Svikram 	size_t		len;
56386448Svikram 	char		*cp;
56396448Svikram 	const char	*fcn = "process_efi_slices()";
56406448Svikram 
56416448Svikram 	len = strlen(s0);
56426448Svikram 
56436448Svikram 	assert(s0[len - 2] == 's' && s0[len - 1] == '0');
56446448Svikram 
56456448Svikram 	s0[len - 1] = '\0';
56466448Svikram 
56476448Svikram 	(void) strlcpy(slice, s0, sizeof (slice));
56486448Svikram 
56496448Svikram 	s0[len - 1] = '0';
56506448Svikram 
56516448Svikram 	cp = slice + len - 1;
56526448Svikram 
56536448Svikram 	for (idx = 0; idx < efi->efi_nparts; idx++) {
56546448Svikram 
56556448Svikram 		(void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
56566448Svikram 
56576448Svikram 		if (efi->efi_parts[idx].p_size == 0) {
56586448Svikram 			BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
56596448Svikram 			continue;
56606448Svikram 		}
56616448Svikram 
56626448Svikram 		/* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
56636448Svikram 		switch (efi->efi_parts[idx].p_tag) {
56646448Svikram 		case V_SWAP:
56656448Svikram 		case V_USR:
56666448Svikram 		case V_BACKUP:
56676448Svikram 		case V_VAR:
56686448Svikram 		case V_HOME:
56696448Svikram 		case V_ALTSCTR:
56706448Svikram 			BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
56716448Svikram 			continue;
56726448Svikram 		default:
56736448Svikram 			BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
56746448Svikram 			break;
56756448Svikram 		}
56766448Svikram 
56776448Svikram 		/* skip unmountable and readonly slices */
56786448Svikram 		switch (efi->efi_parts[idx].p_flag) {
56796448Svikram 		case V_UNMNT:
56806448Svikram 		case V_RONLY:
56816448Svikram 			BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
56826448Svikram 			continue;
56836448Svikram 		default:
56846448Svikram 			BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
56856448Svikram 			break;
56866448Svikram 		}
56876448Svikram 
56886448Svikram 		if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
56896448Svikram 			return (-1);
56906448Svikram 		}
56916448Svikram 	}
56926448Svikram 
56936448Svikram 	return (0);
56946448Svikram }
56956448Svikram 
56966448Svikram /*
56976448Svikram  * s0 is a basename not a full path
56986448Svikram  */
56996448Svikram static int
57006448Svikram process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
57016448Svikram {
57026448Svikram 	struct vtoc		vtoc;
57036448Svikram 	struct dk_gpt		*efi;
57046448Svikram 	char			s0path[PATH_MAX];
57056448Svikram 	struct stat		sbuf;
57066448Svikram 	int			e_flag;
57076448Svikram 	int			v_flag;
57086448Svikram 	int			retval;
57096448Svikram 	int			err;
57106448Svikram 	int			fd;
57116448Svikram 	const char		*fcn = "process_slice0()";
57126448Svikram 
57136448Svikram 	(void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
57146448Svikram 
57156448Svikram 	if (stat(s0path, &sbuf) == -1) {
57166448Svikram 		BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
57176448Svikram 		return (0);
57186448Svikram 	}
57196448Svikram 
57206448Svikram 	fd = open(s0path, O_NONBLOCK|O_RDONLY);
57216448Svikram 	if (fd == -1) {
57226448Svikram 		bam_error(OPEN_FAIL, s0path, strerror(errno));
57236448Svikram 		return (0);
57246448Svikram 	}
57256448Svikram 
57266448Svikram 	e_flag = v_flag = 0;
57276448Svikram 	retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
57286448Svikram 	switch (retval) {
57296448Svikram 		case VT_EIO:
57306448Svikram 			BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
57316448Svikram 			break;
57326448Svikram 		case VT_EINVAL:
57336448Svikram 			BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
57346448Svikram 			break;
57356448Svikram 		case VT_ERROR:
57366448Svikram 			BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
57376448Svikram 			break;
57386448Svikram 		case VT_ENOTSUP:
57396448Svikram 			e_flag = 1;
57406448Svikram 			BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
57416448Svikram 			break;
57426448Svikram 		case 0:
57436448Svikram 			v_flag = 1;
57446448Svikram 			BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
57456448Svikram 			break;
57466448Svikram 		default:
57476448Svikram 			BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
57486448Svikram 			break;
57496448Svikram 	}
57506448Svikram 
57516448Svikram 
57526448Svikram 	if (e_flag) {
57536448Svikram 		e_flag = 0;
57546448Svikram 		retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
57556448Svikram 		switch (retval) {
57566448Svikram 		case VT_EIO:
57576448Svikram 			BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
57586448Svikram 			break;
57596448Svikram 		case VT_EINVAL:
57606448Svikram 			BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
57616448Svikram 			break;
57626448Svikram 		case VT_ERROR:
57636448Svikram 			BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
57646448Svikram 			break;
57656448Svikram 		case VT_ENOTSUP:
57666448Svikram 			BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
57676448Svikram 			break;
57686448Svikram 		case 0:
57696448Svikram 			e_flag = 1;
57706448Svikram 			BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
57716448Svikram 			break;
57726448Svikram 		default:
57736448Svikram 			BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
57746448Svikram 			break;
57756448Svikram 		}
57766448Svikram 	}
57776448Svikram 
57786448Svikram 	(void) close(fd);
57796448Svikram 
57806448Svikram 	if (v_flag) {
57816448Svikram 		retval = process_vtoc_slices(s0,
57826448Svikram 		    &vtoc, tfp, mhp, tmpmnt);
57836448Svikram 	} else if (e_flag) {
57846448Svikram 		retval = process_efi_slices(s0,
57856448Svikram 		    efi, tfp, mhp, tmpmnt);
57866448Svikram 	} else {
57876448Svikram 		BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
57886448Svikram 		return (0);
57896448Svikram 	}
57906448Svikram 
57916448Svikram 	return (retval);
57926448Svikram }
57936448Svikram 
57946448Svikram /*
57956448Svikram  * Find and create a list of all existing UFS boot signatures
57966448Svikram  */
57976448Svikram static int
57986448Svikram FindAllUfsSignatures(void)
57996448Svikram {
58006448Svikram 	mhash_t		*mnttab_hash;
58016448Svikram 	DIR		*dirp = NULL;
58026448Svikram 	struct dirent	*dp;
58036448Svikram 	char		tmpmnt[PATH_MAX];
58046448Svikram 	char		cmd[PATH_MAX];
58056448Svikram 	struct stat	sb;
58066448Svikram 	int		fd;
58076448Svikram 	FILE		*tfp;
58086448Svikram 	size_t		len;
58096448Svikram 	int		ret;
58106448Svikram 	int		error;
58116448Svikram 	const char	*fcn = "FindAllUfsSignatures()";
58126448Svikram 
58136448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) != -1)  {
58146448Svikram 		bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
58156448Svikram 		return (0);
58166448Svikram 	}
58176448Svikram 
58186448Svikram 	fd = open(UFS_SIGNATURE_LIST".tmp",
58196448Svikram 	    O_RDWR|O_CREAT|O_TRUNC, 0644);
58206448Svikram 	error = errno;
58216448Svikram 	INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
58226448Svikram 	if (fd == -1) {
58236448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
58246448Svikram 		return (-1);
58256448Svikram 	}
58266448Svikram 
58276448Svikram 	ret = close(fd);
58286448Svikram 	error = errno;
58296448Svikram 	INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
58306448Svikram 	if (ret == -1) {
58316448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
58326448Svikram 		    strerror(error));
58336448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
58346448Svikram 		return (-1);
58356448Svikram 	}
58366448Svikram 
58376448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
58386448Svikram 	error = errno;
58396448Svikram 	INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
58406448Svikram 	if (tfp == NULL) {
58416448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
58426448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
58436448Svikram 		return (-1);
58446448Svikram 	}
58456448Svikram 
58466448Svikram 	mnttab_hash = cache_mnttab();
58476448Svikram 	INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
58486448Svikram 	if (mnttab_hash == NULL) {
58496448Svikram 		(void) fclose(tfp);
58506448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
58516448Svikram 		bam_error(CACHE_MNTTAB_FAIL, fcn);
58526448Svikram 		return (-1);
58536448Svikram 	}
58546448Svikram 
58556448Svikram 	(void) snprintf(tmpmnt, sizeof (tmpmnt),
58566448Svikram 	    "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
58576448Svikram 	(void) unlink(tmpmnt);
58586448Svikram 
58598735SEnrico.Perla@Sun.COM 	ret = mkdirp(tmpmnt, DIR_PERMS);
58606448Svikram 	error = errno;
58616448Svikram 	INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
58626448Svikram 	if (ret == -1) {
58636448Svikram 		bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
58646448Svikram 		free_mnttab(mnttab_hash);
58656448Svikram 		(void) fclose(tfp);
58666448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
58676448Svikram 		return (-1);
58686448Svikram 	}
58696448Svikram 
58706448Svikram 	dirp = opendir("/dev/rdsk");
58716448Svikram 	error = errno;
58726448Svikram 	INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
58736448Svikram 	if (dirp == NULL) {
58746448Svikram 		bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
58756448Svikram 		goto fail;
58766448Svikram 	}
58776448Svikram 
58786448Svikram 	while (dp = readdir(dirp)) {
58796448Svikram 		if (strcmp(dp->d_name, ".") == 0 ||
58806448Svikram 		    strcmp(dp->d_name, "..") == 0)
58816448Svikram 			continue;
58826448Svikram 
58836448Svikram 		/*
58846448Svikram 		 * we only look for the s0 slice. This is guranteed to
58856448Svikram 		 * have 's' at len - 2.
58866448Svikram 		 */
58876448Svikram 		len = strlen(dp->d_name);
58886448Svikram 		if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
58896448Svikram 			BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
58906448Svikram 			continue;
58916448Svikram 		}
58926448Svikram 
58936448Svikram 		ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
58946448Svikram 		INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
58956448Svikram 		if (ret == -1)
58966448Svikram 			goto fail;
58976448Svikram 	}
58986448Svikram 
58996448Svikram 	(void) closedir(dirp);
59006448Svikram 	free_mnttab(mnttab_hash);
59016448Svikram 	(void) rmdir(tmpmnt);
59026448Svikram 
59036448Svikram 	ret = fclose(tfp);
59046448Svikram 	error = errno;
59056448Svikram 	INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
59066448Svikram 	if (ret == EOF) {
59076448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
59086448Svikram 		    strerror(error));
59096448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
59106448Svikram 		return (-1);
59116448Svikram 	}
59126448Svikram 
59136448Svikram 	/* We have a list of existing GRUB signatures. Sort it first */
59146448Svikram 	(void) snprintf(cmd, sizeof (cmd),
59156448Svikram 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
59166448Svikram 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
59176448Svikram 
59186448Svikram 	ret = exec_cmd(cmd, NULL);
59196448Svikram 	INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
59206448Svikram 	if (ret != 0) {
59216448Svikram 		bam_error(GRUBSIGN_SORT_FAILED);
59226448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
59236448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
59246448Svikram 		return (-1);
59256448Svikram 	}
59266448Svikram 
59276448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
59286448Svikram 
59296448Svikram 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
59306448Svikram 	error = errno;
59316448Svikram 	INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
59326448Svikram 	if (ret == -1) {
59336448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
59346448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
59356448Svikram 		return (-1);
59366448Svikram 	}
59376448Svikram 
59386448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
59396448Svikram 		BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
59406448Svikram 	}
59416448Svikram 
59426448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
59436448Svikram 	return (0);
59446448Svikram 
59456448Svikram fail:
59466448Svikram 	if (dirp)
59476448Svikram 		(void) closedir(dirp);
59486448Svikram 	free_mnttab(mnttab_hash);
59496448Svikram 	(void) rmdir(tmpmnt);
59506448Svikram 	(void) fclose(tfp);
59516448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
59526448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
59536448Svikram 	return (-1);
59546448Svikram }
59556448Svikram 
59566448Svikram static char *
59576448Svikram create_ufs_sign(void)
59586448Svikram {
59596448Svikram 	struct stat	sb;
59606448Svikram 	int		signnum = -1;
59616448Svikram 	char		tmpsign[MAXNAMELEN + 1];
59626448Svikram 	char		*numstr;
59636448Svikram 	int		i;
59646448Svikram 	FILE		*tfp;
59656448Svikram 	int		ret;
59666448Svikram 	int		error;
59676448Svikram 	const char	*fcn = "create_ufs_sign()";
59686448Svikram 
59696448Svikram 	bam_print(SEARCHING_UFS_SIGN);
59706448Svikram 
59716448Svikram 	ret = FindAllUfsSignatures();
59726448Svikram 	INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
59736448Svikram 	if (ret == -1) {
59746448Svikram 		bam_error(ERR_FIND_UFS_SIGN);
59756448Svikram 		return (NULL);
59766448Svikram 	}
59776448Svikram 
59786448Svikram 	/* Make sure the list exists and is owned by root */
59796448Svikram 	INJECT_ERROR1("SIGNLIST_NOT_CREATED",
59806448Svikram 	    (void) unlink(UFS_SIGNATURE_LIST));
59816448Svikram 	if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
59826448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
59836448Svikram 		bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
59846448Svikram 		return (NULL);
59856448Svikram 	}
59866448Svikram 
59876448Svikram 	if (sb.st_size == 0) {
59886448Svikram 		bam_print(GRUBSIGN_UFS_NONE);
59896448Svikram 		i = 0;
59906448Svikram 		goto found;
59916448Svikram 	}
59926448Svikram 
59936448Svikram 	/* The signature list was sorted when it was created */
59946448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST, "r");
59956448Svikram 	error = errno;
59966448Svikram 	INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
59976448Svikram 	if (tfp == NULL) {
59986448Svikram 		bam_error(UFS_SIGNATURE_LIST_OPENERR,
59996448Svikram 		    UFS_SIGNATURE_LIST, strerror(error));
60006448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
60016448Svikram 		return (NULL);
60026448Svikram 	}
60036448Svikram 
60046448Svikram 	for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
60056448Svikram 
60066448Svikram 		if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
60076448Svikram 		    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
60086448Svikram 			(void) fclose(tfp);
60096448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
60106448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
60116448Svikram 			return (NULL);
60126448Svikram 		}
60136448Svikram 		numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
60146448Svikram 
60156448Svikram 		if (numstr[0] == '\0' || !isdigit(numstr[0])) {
60166448Svikram 			(void) fclose(tfp);
60176448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
60186448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
60196448Svikram 			return (NULL);
60206448Svikram 		}
60216448Svikram 
60226448Svikram 		signnum = atoi(numstr);
60236448Svikram 		INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
60246448Svikram 		if (signnum < 0) {
60256448Svikram 			(void) fclose(tfp);
60266448Svikram 			(void) unlink(UFS_SIGNATURE_LIST);
60276448Svikram 			bam_error(UFS_BADSIGN, tmpsign);
60286448Svikram 			return (NULL);
60296448Svikram 		}
60306448Svikram 
60316448Svikram 		if (i != signnum) {
60326448Svikram 			BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
60336448Svikram 			break;
60346448Svikram 		}
60356448Svikram 	}
60366448Svikram 
60376448Svikram 	(void) fclose(tfp);
60386448Svikram 
60396448Svikram found:
60406448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
60416448Svikram 
60426448Svikram 	/* add the ufs signature to the /var/run list of signatures */
60436448Svikram 	ret = ufs_add_to_sign_list(tmpsign);
60446448Svikram 	INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
60456448Svikram 	if (ret == -1) {
60466448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
60476448Svikram 		bam_error(FAILED_ADD_SIGNLIST, tmpsign);
60486448Svikram 		return (NULL);
60496448Svikram 	}
60506448Svikram 
60516448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
60526448Svikram 
60536448Svikram 	return (s_strdup(tmpsign));
60546448Svikram }
60556448Svikram 
60566448Svikram static char *
60576448Svikram get_fstype(char *osroot)
60586448Svikram {
60596448Svikram 	FILE		*mntfp;
60606448Svikram 	struct mnttab	mp = {0};
60616448Svikram 	struct mnttab	mpref = {0};
60626448Svikram 	int		error;
60636448Svikram 	int		ret;
60646448Svikram 	const char	*fcn = "get_fstype()";
60656448Svikram 
60666448Svikram 	INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
60676448Svikram 	if (osroot == NULL) {
60686448Svikram 		bam_error(GET_FSTYPE_ARGS);
60696448Svikram 		return (NULL);
60706448Svikram 	}
60716448Svikram 
60726448Svikram 	mntfp = fopen(MNTTAB, "r");
60736448Svikram 	error = errno;
60746448Svikram 	INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
60756448Svikram 	if (mntfp == NULL) {
60766448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
60776448Svikram 		return (NULL);
60786448Svikram 	}
60796448Svikram 
60806448Svikram 	if (*osroot == '\0')
60816448Svikram 		mpref.mnt_mountp = "/";
60826448Svikram 	else
60836448Svikram 		mpref.mnt_mountp = osroot;
60846448Svikram 
60856448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
60866448Svikram 	INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
60876448Svikram 	if (ret != 0) {
60886448Svikram 		bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
60896448Svikram 		(void) fclose(mntfp);
60906448Svikram 		return (NULL);
60916448Svikram 	}
60926448Svikram 	(void) fclose(mntfp);
60936448Svikram 
60946448Svikram 	INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
60956448Svikram 	if (mp.mnt_fstype == NULL) {
60966448Svikram 		bam_error(MNTTAB_FSTYPE_NULL, osroot);
60976448Svikram 		return (NULL);
60986448Svikram 	}
60996448Svikram 
61006448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
61016448Svikram 
61026448Svikram 	return (s_strdup(mp.mnt_fstype));
61036448Svikram }
61046448Svikram 
61056448Svikram static char *
61066448Svikram create_zfs_sign(char *osdev)
61076448Svikram {
61086448Svikram 	char		tmpsign[PATH_MAX];
61096448Svikram 	char		*pool;
61106448Svikram 	const char	*fcn = "create_zfs_sign()";
61116448Svikram 
61126448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
61136448Svikram 
61146448Svikram 	/*
61156448Svikram 	 * First find the pool name
61166448Svikram 	 */
61176448Svikram 	pool = get_pool(osdev);
61186448Svikram 	INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
61196448Svikram 	if (pool == NULL) {
61206448Svikram 		bam_error(GET_POOL_FAILED, osdev);
61216448Svikram 		return (NULL);
61226448Svikram 	}
61236448Svikram 
61246448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
61256448Svikram 
61266448Svikram 	BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
61276448Svikram 
61286448Svikram 	free(pool);
61296448Svikram 
61306448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
61316448Svikram 
61326448Svikram 	return (s_strdup(tmpsign));
61336448Svikram }
61346448Svikram 
61356448Svikram static char *
61366448Svikram create_new_sign(char *osdev, char *fstype)
61376448Svikram {
61386448Svikram 	char		*sign;
61396448Svikram 	const char	*fcn = "create_new_sign()";
61406448Svikram 
61416448Svikram 	INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
61426448Svikram 
61436448Svikram 	if (strcmp(fstype, "zfs") == 0) {
61446448Svikram 		BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
61456448Svikram 		sign = create_zfs_sign(osdev);
61466448Svikram 	} else if (strcmp(fstype, "ufs") == 0) {
61476448Svikram 		BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
61486448Svikram 		sign = create_ufs_sign();
61496448Svikram 	} else {
61506448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
61516448Svikram 		sign = NULL;
61526448Svikram 	}
61536448Svikram 
61546448Svikram 	BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
61556448Svikram 	return (sign);
61566448Svikram }
61576448Svikram 
61586448Svikram static int
61596448Svikram set_backup_common(char *mntpt, char *sign)
61606448Svikram {
61616448Svikram 	FILE		*bfp;
61626448Svikram 	char		backup[PATH_MAX];
61636448Svikram 	char		tmpsign[PATH_MAX];
61646448Svikram 	int		error;
61656448Svikram 	char		*bdir;
61666448Svikram 	char		*backup_dup;
61676448Svikram 	struct stat	sb;
61686448Svikram 	int		ret;
61696448Svikram 	const char	*fcn = "set_backup_common()";
61706448Svikram 
61716448Svikram 	(void) snprintf(backup, sizeof (backup), "%s%s",
61726448Svikram 	    mntpt, GRUBSIGN_BACKUP);
61736448Svikram 
61746448Svikram 	/* First read the backup */
61756448Svikram 	bfp = fopen(backup, "r");
61766448Svikram 	if (bfp != NULL) {
61776448Svikram 		while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
61786448Svikram 			if (strcmp(tmpsign, sign) == 0) {
61796448Svikram 				BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
61806448Svikram 				(void) fclose(bfp);
61816448Svikram 				return (0);
61826448Svikram 			}
61836448Svikram 		}
61846448Svikram 		(void) fclose(bfp);
61856448Svikram 		BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
61866448Svikram 	} else {
61876448Svikram 		BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
61886448Svikram 	}
61896448Svikram 
61906448Svikram 	/*
61916448Svikram 	 * Didn't find the correct signature. First create
61926448Svikram 	 * the directory if necessary.
61936448Svikram 	 */
61946448Svikram 
61956448Svikram 	/* dirname() modifies its argument so dup it */
61966448Svikram 	backup_dup = s_strdup(backup);
61976448Svikram 	bdir = dirname(backup_dup);
61986448Svikram 	assert(bdir);
61996448Svikram 
62006448Svikram 	ret = stat(bdir, &sb);
62016448Svikram 	INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
62026448Svikram 	if (ret == -1) {
62036448Svikram 		BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
62048735SEnrico.Perla@Sun.COM 		ret = mkdirp(bdir, DIR_PERMS);
62056448Svikram 		error = errno;
62066448Svikram 		INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
62076448Svikram 		if (ret == -1) {
62086448Svikram 			bam_error(GRUBSIGN_BACKUP_MKDIRERR,
62096448Svikram 			    GRUBSIGN_BACKUP, strerror(error));
62106448Svikram 			free(backup_dup);
62116448Svikram 			return (-1);
62126448Svikram 		}
62136448Svikram 	}
62146448Svikram 	free(backup_dup);
62156448Svikram 
62166448Svikram 	/*
62176448Svikram 	 * Open the backup in append mode to add the correct
62186448Svikram 	 * signature;
62196448Svikram 	 */
62206448Svikram 	bfp = fopen(backup, "a");
62216448Svikram 	error = errno;
62226448Svikram 	INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
62236448Svikram 	if (bfp == NULL) {
62246448Svikram 		bam_error(GRUBSIGN_BACKUP_OPENERR,
62256448Svikram 		    GRUBSIGN_BACKUP, strerror(error));
62266448Svikram 		return (-1);
62276448Svikram 	}
62286448Svikram 
62296448Svikram 	(void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
62306448Svikram 
62316448Svikram 	ret = fputs(tmpsign, bfp);
62326448Svikram 	error = errno;
62336448Svikram 	INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
62346448Svikram 	if (ret != strlen(tmpsign)) {
62356448Svikram 		bam_error(GRUBSIGN_BACKUP_WRITEERR,
62366448Svikram 		    GRUBSIGN_BACKUP, strerror(error));
62376448Svikram 		(void) fclose(bfp);
62386448Svikram 		return (-1);
62396448Svikram 	}
62406448Svikram 
62416448Svikram 	(void) fclose(bfp);
62426448Svikram 
62436448Svikram 	if (bam_verbose)
62446448Svikram 		bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
62456448Svikram 
62466448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
62476448Svikram 
62486448Svikram 	return (0);
62496448Svikram }
62506448Svikram 
62516448Svikram static int
62526448Svikram set_backup_ufs(char *osroot, char *sign)
62536448Svikram {
62546448Svikram 	const char	*fcn = "set_backup_ufs()";
62556448Svikram 
62566448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
62576448Svikram 	return (set_backup_common(osroot, sign));
62586448Svikram }
62596448Svikram 
62606448Svikram static int
62616448Svikram set_backup_zfs(char *osdev, char *sign)
62626448Svikram {
62636448Svikram 	char		*pool;
62646448Svikram 	char		*mntpt;
62656448Svikram 	zfs_mnted_t	mnted;
62666448Svikram 	int		ret;
62676448Svikram 	const char	*fcn = "set_backup_zfs()";
62686448Svikram 
62696448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
62706448Svikram 
62716448Svikram 	pool = get_pool(osdev);
62726448Svikram 	INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
62736448Svikram 	if (pool == NULL) {
62746448Svikram 		bam_error(GET_POOL_FAILED, osdev);
62756448Svikram 		return (-1);
62766448Svikram 	}
62776448Svikram 
62786448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
62796448Svikram 	INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
62806448Svikram 	if (mntpt == NULL) {
62816448Svikram 		bam_error(FAIL_MNT_TOP_DATASET, pool);
62826448Svikram 		free(pool);
62836448Svikram 		return (-1);
62846448Svikram 	}
62856448Svikram 
62866448Svikram 	ret = set_backup_common(mntpt, sign);
62876448Svikram 
62886448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
62896448Svikram 
62906448Svikram 	free(pool);
62916448Svikram 
62926448Svikram 	INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
62936448Svikram 	if (ret == 0) {
62946448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
62956448Svikram 	} else {
62966448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
62976448Svikram 	}
62986448Svikram 
62996448Svikram 	return (ret);
63006448Svikram }
63016448Svikram 
63026448Svikram static int
63036448Svikram set_backup(char *osroot, char *osdev, char *sign, char *fstype)
63046448Svikram {
63056448Svikram 	const char	*fcn = "set_backup()";
63066448Svikram 	int		ret;
63076448Svikram 
63086448Svikram 	INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
63096448Svikram 
63106448Svikram 	if (strcmp(fstype, "ufs") == 0) {
63116448Svikram 		BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
63126448Svikram 		ret = set_backup_ufs(osroot, sign);
63136448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
63146448Svikram 		BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
63156448Svikram 		ret = set_backup_zfs(osdev, sign);
63166448Svikram 	} else {
63176448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
63186448Svikram 		ret = -1;
63196448Svikram 	}
63206448Svikram 
63216448Svikram 	if (ret == 0) {
63226448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
63236448Svikram 	} else {
63246448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
63256448Svikram 	}
63266448Svikram 
63276448Svikram 	return (ret);
63286448Svikram }
63296448Svikram 
63306448Svikram static int
63316448Svikram set_primary_common(char *mntpt, char *sign)
63326448Svikram {
63336448Svikram 	char		signfile[PATH_MAX];
63346448Svikram 	char		signdir[PATH_MAX];
63356448Svikram 	struct stat	sb;
63366448Svikram 	int		fd;
63376448Svikram 	int		error;
63386448Svikram 	int		ret;
63396448Svikram 	const char	*fcn = "set_primary_common()";
63406448Svikram 
63416448Svikram 	(void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
63426448Svikram 	    mntpt, GRUBSIGN_DIR, sign);
63436448Svikram 
63446448Svikram 	if (stat(signfile, &sb) != -1) {
63456448Svikram 		if (bam_verbose)
63466448Svikram 			bam_print(PRIMARY_SIGN_EXISTS, sign);
63476448Svikram 		return (0);
63486448Svikram 	} else {
63496448Svikram 		BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
63506448Svikram 	}
63516448Svikram 
63526448Svikram 	(void) snprintf(signdir, sizeof (signdir), "%s/%s",
63536448Svikram 	    mntpt, GRUBSIGN_DIR);
63546448Svikram 
63556448Svikram 	if (stat(signdir, &sb) == -1) {
63566448Svikram 		BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
63578735SEnrico.Perla@Sun.COM 		ret = mkdirp(signdir, DIR_PERMS);
63586448Svikram 		error = errno;
63596448Svikram 		INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
63606448Svikram 		if (ret == -1) {
63616448Svikram 			bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
63626448Svikram 			return (-1);
63636448Svikram 		}
63646448Svikram 	}
63656448Svikram 
63666448Svikram 	fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
63676448Svikram 	error = errno;
63686448Svikram 	INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
63696448Svikram 	if (fd == -1) {
63706448Svikram 		bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
63716448Svikram 		return (-1);
63726448Svikram 	}
63736448Svikram 
63746448Svikram 	ret = fsync(fd);
63756448Svikram 	error = errno;
63766448Svikram 	INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
63776448Svikram 	if (ret != 0) {
63786448Svikram 		bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
63796448Svikram 	}
63806448Svikram 
63816448Svikram 	(void) close(fd);
63826448Svikram 
63836448Svikram 	if (bam_verbose)
63846448Svikram 		bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
63856448Svikram 
63866448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
63876448Svikram 
63886448Svikram 	return (0);
63896448Svikram }
63906448Svikram 
63916448Svikram static int
63926448Svikram set_primary_ufs(char *osroot, char *sign)
63936448Svikram {
63946448Svikram 	const char	*fcn = "set_primary_ufs()";
63956448Svikram 
63966448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
63976448Svikram 	return (set_primary_common(osroot, sign));
63986448Svikram }
63996448Svikram 
64006448Svikram static int
64016448Svikram set_primary_zfs(char *osdev, char *sign)
64026448Svikram {
64036448Svikram 	char		*pool;
64046448Svikram 	char		*mntpt;
64056448Svikram 	zfs_mnted_t	mnted;
64066448Svikram 	int		ret;
64076448Svikram 	const char	*fcn = "set_primary_zfs()";
64086448Svikram 
64096448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
64106448Svikram 
64116448Svikram 	pool = get_pool(osdev);
64126448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
64136448Svikram 	if (pool == NULL) {
64146448Svikram 		bam_error(GET_POOL_FAILED, osdev);
64156448Svikram 		return (-1);
64166448Svikram 	}
64176448Svikram 
64186448Svikram 	/* Pool name must exist in the sign */
64196448Svikram 	ret = (strstr(sign, pool) != NULL);
64206448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
64216448Svikram 	if (ret == 0) {
64226448Svikram 		bam_error(POOL_SIGN_INCOMPAT, pool, sign);
64236448Svikram 		free(pool);
64246448Svikram 		return (-1);
64256448Svikram 	}
64266448Svikram 
64276448Svikram 	mntpt = mount_top_dataset(pool, &mnted);
64286448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
64296448Svikram 	if (mntpt == NULL) {
64306448Svikram 		bam_error(FAIL_MNT_TOP_DATASET, pool);
64316448Svikram 		free(pool);
64326448Svikram 		return (-1);
64336448Svikram 	}
64346448Svikram 
64356448Svikram 	ret = set_primary_common(mntpt, sign);
64366448Svikram 
64376448Svikram 	(void) umount_top_dataset(pool, mnted, mntpt);
64386448Svikram 
64396448Svikram 	free(pool);
64406448Svikram 
64416448Svikram 	INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
64426448Svikram 	if (ret == 0) {
64436448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
64446448Svikram 	} else {
64456448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
64466448Svikram 	}
64476448Svikram 
64486448Svikram 	return (ret);
64496448Svikram }
64506448Svikram 
64516448Svikram static int
64526448Svikram set_primary(char *osroot, char *osdev, char *sign, char *fstype)
64536448Svikram {
64546448Svikram 	const char	*fcn = "set_primary()";
64556448Svikram 	int		ret;
64566448Svikram 
64576448Svikram 	INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
64586448Svikram 	if (strcmp(fstype, "ufs") == 0) {
64596448Svikram 		BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
64606448Svikram 		ret = set_primary_ufs(osroot, sign);
64616448Svikram 	} else if (strcmp(fstype, "zfs") == 0) {
64626448Svikram 		BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
64636448Svikram 		ret = set_primary_zfs(osdev, sign);
64646448Svikram 	} else {
64656448Svikram 		bam_error(GRUBSIGN_NOTSUP, fstype);
64666448Svikram 		ret = -1;
64676448Svikram 	}
64686448Svikram 
64696448Svikram 	if (ret == 0) {
64706448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
64716448Svikram 	} else {
64726448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
64736448Svikram 	}
64746448Svikram 
64756448Svikram 	return (ret);
64766448Svikram }
64776448Svikram 
64786448Svikram static int
64796448Svikram ufs_add_to_sign_list(char *sign)
64806448Svikram {
64816448Svikram 	FILE		*tfp;
64826448Svikram 	char		signline[MAXNAMELEN];
64836448Svikram 	char		cmd[PATH_MAX];
64846448Svikram 	int		ret;
64856448Svikram 	int		error;
64866448Svikram 	const char	*fcn = "ufs_add_to_sign_list()";
64876448Svikram 
64886448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
64896448Svikram 	if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
64906448Svikram 	    strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
64916448Svikram 		bam_error(INVALID_UFS_SIGN, sign);
64926448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
64936448Svikram 		return (-1);
64946448Svikram 	}
64956448Svikram 
64966448Svikram 	/*
64976448Svikram 	 * most failures in this routine are not a fatal error
64986448Svikram 	 * We simply unlink the /var/run file and continue
64996448Svikram 	 */
65006448Svikram 
65016448Svikram 	ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
65026448Svikram 	error = errno;
65036448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
65046448Svikram 	if (ret == -1) {
65056448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
65066448Svikram 		    strerror(error));
65076448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
65086448Svikram 		return (0);
65096448Svikram 	}
65106448Svikram 
65116448Svikram 	tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
65126448Svikram 	error = errno;
65136448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
65146448Svikram 	if (tfp == NULL) {
65156448Svikram 		bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
65166448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
65176448Svikram 		return (0);
65186448Svikram 	}
65196448Svikram 
65206448Svikram 	(void) snprintf(signline, sizeof (signline), "%s\n", sign);
65216448Svikram 
65226448Svikram 	ret = fputs(signline, tfp);
65236448Svikram 	error = errno;
65246448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
65256448Svikram 	if (ret != strlen(signline)) {
65266448Svikram 		bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
65276448Svikram 		(void) fclose(tfp);
65286448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
65296448Svikram 		return (0);
65306448Svikram 	}
65316448Svikram 
65326448Svikram 	ret = fclose(tfp);
65336448Svikram 	error = errno;
65346448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
65356448Svikram 	if (ret == EOF) {
65366448Svikram 		bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
65376448Svikram 		    strerror(error));
65386448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
65396448Svikram 		return (0);
65406448Svikram 	}
65416448Svikram 
65426448Svikram 	/* Sort the list again */
65436448Svikram 	(void) snprintf(cmd, sizeof (cmd),
65446448Svikram 	    "/usr/bin/sort -u %s.tmp > %s.sorted",
65456448Svikram 	    UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
65466448Svikram 
65476448Svikram 	ret = exec_cmd(cmd, NULL);
65486448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
65496448Svikram 	if (ret != 0) {
65506448Svikram 		bam_error(GRUBSIGN_SORT_FAILED);
65516448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
65526448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".tmp");
65536448Svikram 		return (0);
65546448Svikram 	}
65556448Svikram 
65566448Svikram 	(void) unlink(UFS_SIGNATURE_LIST".tmp");
65576448Svikram 
65586448Svikram 	ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
65596448Svikram 	error = errno;
65606448Svikram 	INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
65616448Svikram 	if (ret == -1) {
65626448Svikram 		bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
65636448Svikram 		(void) unlink(UFS_SIGNATURE_LIST".sorted");
65646448Svikram 		return (0);
65656448Svikram 	}
65666448Svikram 
65676448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
65686448Svikram 
65696448Svikram 	return (0);
65706448Svikram }
65716448Svikram 
65726448Svikram static int
65736448Svikram set_signature(char *osroot, char *osdev, char *sign, char *fstype)
65746448Svikram {
65756448Svikram 	int		ret;
65766448Svikram 	const char	*fcn = "set_signature()";
65776448Svikram 
65786448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
65796448Svikram 
65806448Svikram 	ret = set_backup(osroot, osdev, sign, fstype);
65816448Svikram 	INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
65826448Svikram 	if (ret == -1) {
65836448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
65846448Svikram 		bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
65856448Svikram 		return (-1);
65866448Svikram 	}
65876448Svikram 
65886448Svikram 	ret = set_primary(osroot, osdev, sign, fstype);
65896448Svikram 	INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
65906448Svikram 
65916448Svikram 	if (ret == 0) {
65926448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
65936448Svikram 	} else {
65946448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
65956448Svikram 		bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
65966448Svikram 
65976448Svikram 	}
65986448Svikram 	return (ret);
65996448Svikram }
66006448Svikram 
66016448Svikram char *
66026448Svikram get_grubsign(char *osroot, char *osdev)
66036448Svikram {
66046448Svikram 	char		*grubsign;	/* (<sign>,#,#) */
66056448Svikram 	char		*slice;
66066448Svikram 	int		fdiskpart;
66076448Svikram 	char		*sign;
66086448Svikram 	char		*fstype;
66096448Svikram 	int		ret;
66106448Svikram 	const char	*fcn = "get_grubsign()";
66116448Svikram 
66126448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
66136448Svikram 
66146448Svikram 	fstype = get_fstype(osroot);
66156448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
66166448Svikram 	if (fstype == NULL) {
66176448Svikram 		bam_error(GET_FSTYPE_FAILED, osroot);
66186448Svikram 		return (NULL);
66196448Svikram 	}
66206448Svikram 
66216448Svikram 	sign = find_existing_sign(osroot, osdev, fstype);
66226448Svikram 	INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
66236448Svikram 	if (sign == NULL) {
66246448Svikram 		BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
66256448Svikram 		sign = create_new_sign(osdev, fstype);
66266448Svikram 		INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
66276448Svikram 		if (sign == NULL) {
66286448Svikram 			bam_error(GRUBSIGN_CREATE_FAIL, osdev);
66296448Svikram 			free(fstype);
66306448Svikram 			return (NULL);
66316448Svikram 		}
66326448Svikram 	}
66336448Svikram 
66346448Svikram 	ret = set_signature(osroot, osdev, sign, fstype);
66356448Svikram 	INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
66366448Svikram 	if (ret == -1) {
66376448Svikram 		bam_error(GRUBSIGN_WRITE_FAIL, osdev);
66386448Svikram 		free(sign);
66396448Svikram 		free(fstype);
66406448Svikram 		(void) unlink(UFS_SIGNATURE_LIST);
66416448Svikram 		return (NULL);
66426448Svikram 	}
66436448Svikram 
66446448Svikram 	free(fstype);
66456448Svikram 
66466448Svikram 	if (bam_verbose)
66476448Svikram 		bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
66486448Svikram 
66496448Svikram 	fdiskpart = get_partition(osdev);
66506448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1);
66516448Svikram 	if (fdiskpart == -1) {
66526448Svikram 		bam_error(FDISKPART_FAIL, osdev);
66536448Svikram 		free(sign);
66546448Svikram 		return (NULL);
66556448Svikram 	}
66566448Svikram 
66576448Svikram 	slice = strrchr(osdev, 's');
66586448Svikram 
66596448Svikram 	grubsign = s_calloc(1, MAXNAMELEN + 10);
66606448Svikram 	if (slice) {
66616448Svikram 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
66626448Svikram 		    sign, fdiskpart, slice[1] + 'a' - '0');
66636448Svikram 	} else
66646448Svikram 		(void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
66656448Svikram 		    sign, fdiskpart);
66666448Svikram 
66676448Svikram 	free(sign);
66686448Svikram 
66696448Svikram 	BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
66706448Svikram 
66716448Svikram 	return (grubsign);
66720Sstevel@tonic-gate }
66730Sstevel@tonic-gate 
6674656Sszhou static char *
6675656Sszhou get_title(char *rootdir)
66760Sstevel@tonic-gate {
66776448Svikram 	static char	title[80];
66786448Svikram 	char		*cp = NULL;
66796448Svikram 	char		release[PATH_MAX];
66806448Svikram 	FILE		*fp;
66816448Svikram 	const char	*fcn = "get_title()";
66820Sstevel@tonic-gate 
66830Sstevel@tonic-gate 	/* open the /etc/release file */
66840Sstevel@tonic-gate 	(void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
66850Sstevel@tonic-gate 
66860Sstevel@tonic-gate 	fp = fopen(release, "r");
66876448Svikram 	if (fp == NULL) {
66886448Svikram 		bam_error(OPEN_FAIL, release, strerror(errno));
66896448Svikram 		cp = NULL;
66906448Svikram 		goto out;
66916448Svikram 	}
66920Sstevel@tonic-gate 
66930Sstevel@tonic-gate 	while (s_fgets(title, sizeof (title), fp) != NULL) {
66940Sstevel@tonic-gate 		cp = strstr(title, "Solaris");
66950Sstevel@tonic-gate 		if (cp)
66960Sstevel@tonic-gate 			break;
66970Sstevel@tonic-gate 	}
66980Sstevel@tonic-gate 	(void) fclose(fp);
66996448Svikram 
67006448Svikram out:
67016448Svikram 	cp = cp ? cp : "Solaris";
67026448Svikram 
67036448Svikram 	BAM_DPRINTF((D_GET_TITLE, fcn, cp));
67046448Svikram 
67056448Svikram 	return (cp);
67060Sstevel@tonic-gate }
67070Sstevel@tonic-gate 
67083446Smrj char *
67090Sstevel@tonic-gate get_special(char *mountp)
67100Sstevel@tonic-gate {
67116448Svikram 	FILE		*mntfp;
67126448Svikram 	struct mnttab	mp = {0};
67136448Svikram 	struct mnttab	mpref = {0};
67146448Svikram 	int		error;
67156448Svikram 	int		ret;
67166448Svikram 	const char 	*fcn = "get_special()";
67176448Svikram 
67186448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
67196448Svikram 	if (mountp == NULL) {
67206448Svikram 		bam_error(GET_SPECIAL_NULL_MNTPT);
67216448Svikram 		return (NULL);
67226448Svikram 	}
67230Sstevel@tonic-gate 
67240Sstevel@tonic-gate 	mntfp = fopen(MNTTAB, "r");
67256448Svikram 	error = errno;
67266448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
67270Sstevel@tonic-gate 	if (mntfp == NULL) {
67286448Svikram 		bam_error(OPEN_FAIL, MNTTAB, strerror(error));
67296448Svikram 		return (NULL);
67300Sstevel@tonic-gate 	}
67310Sstevel@tonic-gate 
67320Sstevel@tonic-gate 	if (*mountp == '\0')
67330Sstevel@tonic-gate 		mpref.mnt_mountp = "/";
67340Sstevel@tonic-gate 	else
67350Sstevel@tonic-gate 		mpref.mnt_mountp = mountp;
67366448Svikram 
67376448Svikram 	ret = getmntany(mntfp, &mp, &mpref);
67386448Svikram 	INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
67396448Svikram 	if (ret != 0) {
67400Sstevel@tonic-gate 		(void) fclose(mntfp);
67416448Svikram 		BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
67420Sstevel@tonic-gate 		return (NULL);
67430Sstevel@tonic-gate 	}
67440Sstevel@tonic-gate 	(void) fclose(mntfp);
67450Sstevel@tonic-gate 
67466448Svikram 	BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
67476448Svikram 
67480Sstevel@tonic-gate 	return (s_strdup(mp.mnt_special));
67490Sstevel@tonic-gate }
67500Sstevel@tonic-gate 
67516448Svikram static void
67526448Svikram free_physarray(char **physarray, int n)
67536448Svikram {
67546448Svikram 	int			i;
67556448Svikram 	const char		*fcn = "free_physarray()";
67566448Svikram 
67576448Svikram 	assert(physarray);
67586448Svikram 	assert(n);
67596448Svikram 
67606448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
67616448Svikram 
67626448Svikram 	for (i = 0; i < n; i++) {
67636448Svikram 		free(physarray[i]);
67646448Svikram 	}
67656448Svikram 	free(physarray);
67666448Svikram 
67676448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
67686448Svikram }
67696448Svikram 
67706448Svikram static int
67716448Svikram zfs_get_physical(char *special, char ***physarray, int *n)
67726448Svikram {
67736448Svikram 	char			sdup[PATH_MAX];
67746448Svikram 	char			cmd[PATH_MAX];
67756448Svikram 	char			dsk[PATH_MAX];
67766448Svikram 	char			*pool;
67776448Svikram 	filelist_t		flist = {0};
67786448Svikram 	line_t			*lp;
67796448Svikram 	line_t			*startlp;
67806448Svikram 	char			*comp1;
67816448Svikram 	int			i;
67826448Svikram 	int			ret;
67836448Svikram 	const char		*fcn = "zfs_get_physical()";
67846448Svikram 
67856448Svikram 	assert(special);
67866448Svikram 
67876448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
67886448Svikram 
67896448Svikram 	INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
67906448Svikram 	if (special[0] == '/') {
67916448Svikram 		bam_error(INVALID_ZFS_SPECIAL, special);
67926448Svikram 		return (-1);
67936448Svikram 	}
67946448Svikram 
67956448Svikram 	(void) strlcpy(sdup, special, sizeof (sdup));
67966448Svikram 
67976448Svikram 	pool = strtok(sdup, "/");
67986448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
67996448Svikram 	if (pool == NULL) {
68006448Svikram 		bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
68016448Svikram 		return (-1);
68026448Svikram 	}
68036448Svikram 
68046448Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
68056448Svikram 
68066448Svikram 	ret = exec_cmd(cmd, &flist);
68076448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
68086448Svikram 	if (ret != 0) {
68096448Svikram 		bam_error(ZFS_GET_POOL_STATUS, pool);
68106448Svikram 		return (-1);
68116448Svikram 	}
68126448Svikram 
68136448Svikram 	INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
68146448Svikram 	if (flist.head == NULL) {
68156448Svikram 		bam_error(BAD_ZPOOL_STATUS, pool);
68166448Svikram 		filelist_free(&flist);
68176448Svikram 		return (-1);
68186448Svikram 	}
68196448Svikram 
68206448Svikram 	for (lp = flist.head; lp; lp = lp->next) {
68216448Svikram 		BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
68226448Svikram 		comp1 = strtok(lp->line, " \t");
68236448Svikram 		if (comp1 == NULL) {
68246448Svikram 			free(lp->line);
68256448Svikram 			lp->line = NULL;
68266448Svikram 		} else {
68276448Svikram 			comp1 = s_strdup(comp1);
68286448Svikram 			free(lp->line);
68296448Svikram 			lp->line = comp1;
68306448Svikram 		}
68316448Svikram 	}
68326448Svikram 
68336448Svikram 	for (lp = flist.head; lp; lp = lp->next) {
68346448Svikram 		if (lp->line == NULL)
68356448Svikram 			continue;
68366448Svikram 		if (strcmp(lp->line, pool) == 0) {
68376448Svikram 			BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
68386448Svikram 			break;
68396448Svikram 		}
68406448Svikram 	}
68416448Svikram 
68426448Svikram 	if (lp == NULL) {
68436448Svikram 		bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
68446448Svikram 		filelist_free(&flist);
68456448Svikram 		return (-1);
68466448Svikram 	}
68476448Svikram 
68486448Svikram 	startlp = lp->next;
68496448Svikram 	for (i = 0, lp = startlp; lp; lp = lp->next) {
68506448Svikram 		if (lp->line == NULL)
68516448Svikram 			continue;
68526448Svikram 		if (strcmp(lp->line, "mirror") == 0)
68536448Svikram 			continue;
68546448Svikram 		if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
68556448Svikram 			break;
68566448Svikram 		i++;
68576448Svikram 		BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
68586448Svikram 	}
68596448Svikram 
68606448Svikram 	if (i == 0) {
68616448Svikram 		bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
68626448Svikram 		filelist_free(&flist);
68636448Svikram 		return (-1);
68646448Svikram 	}
68656448Svikram 
68666448Svikram 	*n = i;
68676448Svikram 	*physarray = s_calloc(*n, sizeof (char *));
68686448Svikram 	for (i = 0, lp = startlp; lp; lp = lp->next) {
68696448Svikram 		if (lp->line == NULL)
68706448Svikram 			continue;
68716448Svikram 		if (strcmp(lp->line, "mirror") == 0)
68726448Svikram 			continue;
68736448Svikram 		if (strcmp(lp->line, "errors:") == 0)
68746448Svikram 			break;
68756448Svikram 		if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
68766448Svikram 		    strncmp(lp->line, "/dev/rdsk/",
68776448Svikram 		    strlen("/dev/rdsk/")) != 0)  {
68786448Svikram 			(void) snprintf(dsk, sizeof (dsk), "/dev/dsk/%s",
68796448Svikram 			    lp->line);
68806448Svikram 		} else {
68816448Svikram 			(void) strlcpy(dsk, lp->line, sizeof (dsk));
68826448Svikram 		}
68836448Svikram 		BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
68846448Svikram 		(*physarray)[i++] = s_strdup(dsk);
68856448Svikram 	}
68866448Svikram 
68876448Svikram 	assert(i == *n);
68886448Svikram 
68896448Svikram 	filelist_free(&flist);
68906448Svikram 
68916448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
68926448Svikram 	return (0);
68936448Svikram }
68946448Svikram 
68956694Svikram /*
68966694Svikram  * Certain services needed to run metastat successfully may not
68976694Svikram  * be enabled. Enable them now.
68986694Svikram  */
68996694Svikram /*
69006694Svikram  * Checks if the specified service is online
69016694Svikram  * Returns: 	1 if the service is online
69026694Svikram  *		0 if the service is not online
69036694Svikram  *		-1 on error
69046694Svikram  */
69056694Svikram static int
69066694Svikram is_svc_online(char *svc)
69076694Svikram {
69086694Svikram 	char			*state;
69096694Svikram 	const char		*fcn = "is_svc_online()";
69106694Svikram 
69116694Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
69126694Svikram 
69136694Svikram 	state = smf_get_state(svc);
69146694Svikram 	INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
69156694Svikram 	if (state == NULL) {
69166694Svikram 		bam_error(GET_SVC_STATE_ERR, svc);
69176694Svikram 		return (-1);
69186694Svikram 	}
69196694Svikram 	BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
69206694Svikram 
69216694Svikram 	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
69226694Svikram 		BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
69236694Svikram 		free(state);
69246694Svikram 		return (1);
69256694Svikram 	}
69266694Svikram 
69276694Svikram 	BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
69286694Svikram 
69296694Svikram 	free(state);
69306694Svikram 
69316694Svikram 	return (0);
69326694Svikram }
69336694Svikram 
69346694Svikram static int
69356694Svikram enable_svc(char *svc)
69366694Svikram {
69376694Svikram 	int			ret;
69386694Svikram 	int			sleeptime;
69396694Svikram 	const char		*fcn = "enable_svc()";
69406694Svikram 
69416694Svikram 	ret = is_svc_online(svc);
69426694Svikram 	if (ret == -1) {
69436694Svikram 		bam_error(SVC_IS_ONLINE_FAILED, svc);
69446694Svikram 		return (-1);
69456694Svikram 	} else if (ret == 1) {
69466694Svikram 		BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
69476694Svikram 		return (0);
69486694Svikram 	}
69496694Svikram 
69506694Svikram 	/* Service is not enabled. Enable it now. */
69516694Svikram 	ret = smf_enable_instance(svc, 0);
69526694Svikram 	INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
69536694Svikram 	if (ret != 0) {
69546694Svikram 		bam_error(ENABLE_SVC_FAILED, svc);
69556694Svikram 		return (-1);
69566694Svikram 	}
69576694Svikram 
69586694Svikram 	BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
69596694Svikram 
69606694Svikram 	sleeptime = 0;
69616694Svikram 	do {
69626694Svikram 		ret = is_svc_online(svc);
69636694Svikram 		INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
69646694Svikram 		INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
69656694Svikram 		INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
69666694Svikram 		if (ret == -1) {
69676694Svikram 			bam_error(ERR_SVC_GET_ONLINE, svc);
69686694Svikram 			return (-1);
69696694Svikram 		} else if (ret == 1) {
69706694Svikram 			BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
69716694Svikram 			return (1);
69726694Svikram 		}
69736694Svikram 		(void) sleep(1);
69746694Svikram 	} while (sleeptime < 60);
69756694Svikram 
69766694Svikram 	bam_error(TIMEOUT_ENABLE_SVC, svc);
69776694Svikram 
69786694Svikram 	return (-1);
69796694Svikram }
69806694Svikram 
69816448Svikram static int
69826448Svikram ufs_get_physical(char *special, char ***physarray, int *n)
69836448Svikram {
69846448Svikram 	char			cmd[PATH_MAX];
69856448Svikram 	char			*shortname;
69866448Svikram 	filelist_t		flist = {0};
69876448Svikram 	char			*meta;
69886448Svikram 	char			*type;
69896448Svikram 	char			*comp1;
69906448Svikram 	char			*comp2;
69916448Svikram 	char			*comp3;
69926448Svikram 	char			*comp4;
69936448Svikram 	int			i;
69946448Svikram 	line_t			*lp;
69956448Svikram 	int			ret;
69966694Svikram 	char			*svc;
69976448Svikram 	const char		*fcn = "ufs_get_physical()";
69986448Svikram 
69996448Svikram 	assert(special);
70006448Svikram 
70016448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
70026448Svikram 
70036448Svikram 	if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
70046448Svikram 		bam_error(UFS_GET_PHYS_NOT_SVM, special);
70056448Svikram 		return (-1);
70066448Svikram 	}
70076448Svikram 
70086448Svikram 	if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
70096448Svikram 		shortname = special + strlen("/dev/md/dsk/");
70106448Svikram 	} else if (strncmp(special, "/dev/md/rdsk/",
70116448Svikram 	    strlen("/dev/md/rdsk/")) == 0) {
70126448Svikram 		shortname = special + strlen("/dev/md/rdsk");
70136448Svikram 	} else {
70146448Svikram 		bam_error(UFS_GET_PHYS_INVALID_SVM, special);
70156448Svikram 		return (-1);
70166448Svikram 	}
70176448Svikram 
70186448Svikram 	BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
70196448Svikram 
70206694Svikram 	svc = "network/rpc/meta:default";
70216694Svikram 	if (enable_svc(svc) == -1) {
70226694Svikram 		bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
70236694Svikram 	}
70246694Svikram 
70256448Svikram 	(void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
70266448Svikram 
70276448Svikram 	ret = exec_cmd(cmd, &flist);
70286448Svikram 	INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
70296448Svikram 	if (ret != 0) {
70306448Svikram 		bam_error(UFS_SVM_METASTAT_ERR, shortname);
70316448Svikram 		return (-1);
70326448Svikram 	}
70336448Svikram 
70346448Svikram 	INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
70356448Svikram 	if (flist.head == NULL) {
70366448Svikram 		bam_error(BAD_UFS_SVM_METASTAT, shortname);
70376448Svikram 		filelist_free(&flist);
70386448Svikram 		return (-1);
70396448Svikram 	}
70406448Svikram 
70416448Svikram 	/*
70426448Svikram 	 * Check if not a mirror. We only parse a single metadevice
70436448Svikram 	 * if not a mirror
70446448Svikram 	 */
70456448Svikram 	meta = strtok(flist.head->line, " \t");
70466448Svikram 	type = strtok(NULL, " \t");
70476448Svikram 	if (meta == NULL || type == NULL) {
70486448Svikram 		bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
70496448Svikram 		filelist_free(&flist);
70506448Svikram 		return (-1);
70516448Svikram 	}
70526448Svikram 	if (strcmp(type, "-m") != 0) {
70536448Svikram 		comp1 = strtok(NULL, " \t");
70546448Svikram 		comp2 = strtok(NULL, " \t");
70556448Svikram 		if (comp1 == NULL || comp2 != NULL) {
70566448Svikram 			bam_error(INVALID_UFS_SVM_METASTAT, shortname);
70576448Svikram 			filelist_free(&flist);
70586448Svikram 			return (-1);
70596448Svikram 		}
70606448Svikram 		BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
70616448Svikram 		*physarray = s_calloc(1, sizeof (char *));
70626448Svikram 		(*physarray)[0] = s_strdup(comp1);
70636448Svikram 		*n = 1;
70646448Svikram 		filelist_free(&flist);
70656448Svikram 		return (0);
70666448Svikram 	}
70676448Svikram 
70686448Svikram 	/*
70696448Svikram 	 * Okay we have a mirror. Everything after the first line
70706448Svikram 	 * is a submirror
70716448Svikram 	 */
70726448Svikram 	for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
70736448Svikram 		if (strstr(lp->line, "/dev/dsk/") == NULL &&
70746448Svikram 		    strstr(lp->line, "/dev/rdsk/") == NULL) {
70756448Svikram 			bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
70766448Svikram 			filelist_free(&flist);
70776448Svikram 			return (-1);
70786448Svikram 		}
70796448Svikram 		i++;
70806448Svikram 	}
70816448Svikram 
70826448Svikram 	*physarray = s_calloc(i, sizeof (char *));
70836448Svikram 	*n = i;
70846448Svikram 
70856448Svikram 	for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
70866448Svikram 		comp1 = strtok(lp->line, " \t");
70876448Svikram 		comp2 = strtok(NULL, " \t");
70886448Svikram 		comp3 = strtok(NULL, " \t");
70896448Svikram 		comp4 = strtok(NULL, " \t");
70906448Svikram 
70916448Svikram 		if (comp3 == NULL || comp4 == NULL ||
70926448Svikram 		    (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
70936448Svikram 		    strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
70946448Svikram 			bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
70956448Svikram 			filelist_free(&flist);
70966448Svikram 			free_physarray(*physarray, *n);
70976448Svikram 			return (-1);
70986448Svikram 		}
70996448Svikram 
71006448Svikram 		(*physarray)[i++] = s_strdup(comp4);
71016448Svikram 	}
71026448Svikram 
71036448Svikram 	assert(i == *n);
71046448Svikram 
71056448Svikram 	filelist_free(&flist);
71066448Svikram 
71076448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
71086448Svikram 	return (0);
71096448Svikram }
71106448Svikram 
71116448Svikram static int
71126448Svikram get_physical(char *menu_root, char ***physarray, int *n)
71136448Svikram {
71146448Svikram 	char			*special;
71156448Svikram 	int			ret;
71166448Svikram 	const char		*fcn = "get_physical()";
71176448Svikram 
71186448Svikram 	assert(menu_root);
71196448Svikram 	assert(physarray);
71206448Svikram 	assert(n);
71216448Svikram 
71226448Svikram 	*physarray = NULL;
71236448Svikram 	*n = 0;
71246448Svikram 
71256448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
71266448Svikram 
71276448Svikram 	/* First get the device special file from /etc/mnttab */
71286448Svikram 	special = get_special(menu_root);
71296448Svikram 	INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
71306448Svikram 	if (special == NULL) {
71316448Svikram 		bam_error(GET_SPECIAL_NULL, menu_root);
71326448Svikram 		return (-1);
71336448Svikram 	}
71346448Svikram 
71356448Svikram 	/* If already a physical device nothing to do */
71366448Svikram 	if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
71376448Svikram 	    strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
71386448Svikram 		BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
71396448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
71406448Svikram 		*physarray = s_calloc(1, sizeof (char *));
71416448Svikram 		(*physarray)[0] = special;
71426448Svikram 		*n = 1;
71436448Svikram 		return (0);
71446448Svikram 	}
71456448Svikram 
71466448Svikram 	if (is_zfs(menu_root)) {
71476448Svikram 		ret = zfs_get_physical(special, physarray, n);
71486448Svikram 	} else if (is_ufs(menu_root)) {
71496448Svikram 		ret = ufs_get_physical(special, physarray, n);
71506448Svikram 	} else {
71516448Svikram 		bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
71526448Svikram 		ret = -1;
71536448Svikram 	}
71546448Svikram 
71556448Svikram 	free(special);
71566448Svikram 
71576448Svikram 	INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
71586448Svikram 	if (ret == -1) {
71596448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
71606448Svikram 	} else {
71616448Svikram 		int	i;
71626448Svikram 		assert (*n > 0);
71636448Svikram 		for (i = 0; i < *n; i++) {
71646448Svikram 			BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
71656448Svikram 		}
71666448Svikram 	}
71676448Svikram 
71686448Svikram 	return (ret);
71696448Svikram }
71706448Svikram 
71716448Svikram static int
71726448Svikram is_bootdisk(char *osroot, char *physical)
71736448Svikram {
71746448Svikram 	int			ret;
71756448Svikram 	char			*grubroot;
71766448Svikram 	char			*bootp;
71776448Svikram 	const char		*fcn = "is_bootdisk()";
71786448Svikram 
71796448Svikram 	assert(osroot);
71806448Svikram 	assert(physical);
71816448Svikram 
71826448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
71836448Svikram 
71846448Svikram 	bootp = strstr(physical, "p0:boot");
71856448Svikram 	if (bootp)
71866448Svikram 		*bootp = '\0';
71876448Svikram 	/*
71886448Svikram 	 * We just want the BIOS mapping for menu disk.
71896448Svikram 	 * Don't pass menu_root to get_grubroot() as the
71906448Svikram 	 * check that it is used for is not relevant here.
71916448Svikram 	 * The osroot is immaterial as well - it is only used to
71926448Svikram 	 * to find create_diskmap script. Everything hinges on
71936448Svikram 	 * "physical"
71946448Svikram 	 */
71956448Svikram 	grubroot = get_grubroot(osroot, physical, NULL);
71966448Svikram 
71976448Svikram 	INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
71986448Svikram 	if (grubroot == NULL) {
71998642SVikram.Hegde@Sun.COM 		if (bam_verbose)
72008642SVikram.Hegde@Sun.COM 			bam_error(NO_GRUBROOT_FOR_DISK, physical);
72016448Svikram 		return (0);
72026448Svikram 	}
72036448Svikram 	ret = grubroot[3] == '0';
72046448Svikram 	free(grubroot);
72056448Svikram 
72066448Svikram 	BAM_DPRINTF((D_RETURN_RET, fcn, ret));
72076448Svikram 
72086448Svikram 	return (ret);
72090Sstevel@tonic-gate }
72100Sstevel@tonic-gate 
72110Sstevel@tonic-gate /*
72126448Svikram  * Check if menu is on the boot device
72130Sstevel@tonic-gate  * Return 0 (false) on error
72140Sstevel@tonic-gate  */
72150Sstevel@tonic-gate static int
72166448Svikram menu_on_bootdisk(char *osroot, char *menu_root)
72176448Svikram {
72186448Svikram 	char		**physarray;
72196448Svikram 	int		ret;
72206448Svikram 	int		n;
72216448Svikram 	int		i;
72226448Svikram 	int		on_bootdisk;
72236448Svikram 	const char	*fcn = "menu_on_bootdisk()";
72246448Svikram 
72256448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
72266448Svikram 
72276448Svikram 	ret = get_physical(menu_root, &physarray, &n);
72286448Svikram 	INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
72296448Svikram 	if (ret != 0) {
72306448Svikram 		bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
72310Sstevel@tonic-gate 		return (0);
72326448Svikram 	}
72336448Svikram 
72346448Svikram 	assert(physarray);
72356448Svikram 	assert(n > 0);
72366448Svikram 
72376448Svikram 	on_bootdisk = 0;
72386448Svikram 	for (i = 0; i < n; i++) {
72396448Svikram 		assert(strncmp(physarray[i], "/dev/dsk/",
72406448Svikram 		    strlen("/dev/dsk/")) == 0 ||
72416448Svikram 		    strncmp(physarray[i], "/dev/rdsk/",
72426448Svikram 		    strlen("/dev/rdsk/")) == 0);
72436448Svikram 
72446448Svikram 		BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
72456448Svikram 		if (is_bootdisk(osroot, physarray[i])) {
72466448Svikram 			on_bootdisk = 1;
72476448Svikram 			BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
72486448Svikram 		}
72496448Svikram 	}
72506448Svikram 
72516448Svikram 	free_physarray(physarray, n);
72526448Svikram 
72536448Svikram 	INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
72546448Svikram 	INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
72556448Svikram 	if (on_bootdisk) {
72566448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
72576448Svikram 	} else {
72586448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
72596448Svikram 	}
72606448Svikram 
72616448Svikram 	return (on_bootdisk);
72626448Svikram }
72636448Svikram 
72646448Svikram void
72656448Svikram bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
72666448Svikram {
72676448Svikram 	const char	*fcn = "bam_add_line()";
72686448Svikram 
72696448Svikram 	assert(mp);
72706448Svikram 	assert(entry);
72716448Svikram 	assert(prev);
72726448Svikram 	assert(lp);
72736448Svikram 
72746448Svikram 	lp->next = prev->next;
72756448Svikram 	if (prev->next) {
72766448Svikram 		BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
72776448Svikram 		prev->next->prev = lp;
72786448Svikram 	} else {
72796448Svikram 		BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
72806448Svikram 	}
72816448Svikram 	prev->next = lp;
72826448Svikram 	lp->prev = prev;
72836448Svikram 
72846448Svikram 	if (entry->end == prev) {
72856448Svikram 		BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
72866448Svikram 		entry->end = lp;
72876448Svikram 	}
72886448Svikram 	if (mp->end == prev) {
72896448Svikram 		assert(lp->next == NULL);
72906448Svikram 		mp->end = lp;
72916448Svikram 		BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
72926448Svikram 	}
72930Sstevel@tonic-gate }
72940Sstevel@tonic-gate 
7295662Sszhou /*
7296662Sszhou  * look for matching bootadm entry with specified parameters
7297662Sszhou  * Here are the rules (based on existing usage):
7298662Sszhou  * - If title is specified, match on title only
72996448Svikram  * - Else, match on root/findroot, kernel, and module.
73006448Svikram  *   Note that, if root_opt is non-zero, the absence of
73016448Svikram  *   root line is considered a match.
7302662Sszhou  */
7303662Sszhou static entry_t *
73046448Svikram find_boot_entry(
73056448Svikram 	menu_t *mp,
73066448Svikram 	char *title,
73076448Svikram 	char *kernel,
73086448Svikram 	char *findroot,
73096448Svikram 	char *root,
73106448Svikram 	char *module,
73116448Svikram 	int root_opt,
73126448Svikram 	int *entry_num)
73136448Svikram {
73146448Svikram 	int		i;
73156448Svikram 	line_t		*lp;
73166448Svikram 	entry_t		*ent;
73176448Svikram 	const char	*fcn = "find_boot_entry()";
73186448Svikram 
73196448Svikram 	if (entry_num)
73206448Svikram 		*entry_num = BAM_ERROR;
7321662Sszhou 
7322662Sszhou 	/* find matching entry */
7323662Sszhou 	for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
7324662Sszhou 		lp = ent->start;
7325662Sszhou 
7326662Sszhou 		/* first line of entry must be bootadm comment */
7327662Sszhou 		lp = ent->start;
73283446Smrj 		if (lp->flags != BAM_COMMENT ||
73293446Smrj 		    strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
7330662Sszhou 			continue;
7331662Sszhou 		}
7332662Sszhou 
7333662Sszhou 		/* advance to title line */
7334662Sszhou 		lp = lp->next;
7335662Sszhou 		if (title) {
7336662Sszhou 			if (lp->flags == BAM_TITLE && lp->arg &&
73376448Svikram 			    strcmp(lp->arg, title) == 0) {
73386448Svikram 				BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
7339662Sszhou 				break;
73406448Svikram 			}
73416448Svikram 			BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
7342662Sszhou 			continue;	/* check title only */
7343662Sszhou 		}
7344662Sszhou 
7345662Sszhou 		lp = lp->next;	/* advance to root line */
73465084Sjohnlev 		if (lp == NULL) {
73475084Sjohnlev 			continue;
73486448Svikram 		} else if (strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
73496448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
73506448Svikram 			    findroot = NULL);
73516448Svikram 			if (findroot == NULL) {
73526448Svikram 				BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
73536448Svikram 				    fcn, lp->arg));
73546448Svikram 				continue;
73556448Svikram 			}
73566448Svikram 			/* findroot command found, try match  */
73576448Svikram 			if (strcmp(lp->arg, findroot) != 0) {
73586448Svikram 				BAM_DPRINTF((D_NOMATCH_FINDROOT,
73596448Svikram 				    fcn, findroot, lp->arg));
7360662Sszhou 				continue;
7361662Sszhou 			}
73626448Svikram 			BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
73636448Svikram 			lp = lp->next;	/* advance to kernel line */
73646448Svikram 		} else if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
73656448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
73666448Svikram 			if (root == NULL) {
73676448Svikram 				BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
73686448Svikram 				    fcn, lp->arg));
73696448Svikram 				continue;
73706448Svikram 			}
73716448Svikram 			/* root cmd found, try match */
73726448Svikram 			if (strcmp(lp->arg, root) != 0) {
73736448Svikram 				BAM_DPRINTF((D_NOMATCH_ROOT,
73746448Svikram 				    fcn, root, lp->arg));
73756448Svikram 				continue;
73766448Svikram 			}
73776448Svikram 			BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
7378662Sszhou 			lp = lp->next;	/* advance to kernel line */
7379662Sszhou 		} else {
73806448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
73816448Svikram 			    root_opt = 0);
73826448Svikram 			INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
73836448Svikram 			    root_opt = 1);
7384662Sszhou 			/* no root command, see if root is optional */
7385662Sszhou 			if (root_opt == 0) {
73866448Svikram 				BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
7387662Sszhou 				continue;
7388662Sszhou 			}
73896448Svikram 			BAM_DPRINTF((D_ROOT_OPT, fcn));
7390662Sszhou 		}
7391662Sszhou 
7392662Sszhou 		if (lp == NULL || lp->next == NULL) {
7393662Sszhou 			continue;
7394662Sszhou 		}
7395662Sszhou 
73965084Sjohnlev 		if (kernel &&
73975084Sjohnlev 		    (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
73988104SEnrico.Perla@Sun.COM 			if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
73998104SEnrico.Perla@Sun.COM 			    !(ent->flags & BAM_ENTRY_DBOOT) ||
74008104SEnrico.Perla@Sun.COM 			    strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
74018104SEnrico.Perla@Sun.COM 				continue;
74028104SEnrico.Perla@Sun.COM 
74038104SEnrico.Perla@Sun.COM 			ent->flags |= BAM_ENTRY_UPGFSKERNEL;
74048104SEnrico.Perla@Sun.COM 
74055084Sjohnlev 		}
74066448Svikram 		BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
74075084Sjohnlev 
74083467Srscott 		/*
74095084Sjohnlev 		 * Check for matching module entry (failsafe or normal).
74105084Sjohnlev 		 * If it fails to match, we go around the loop again.
74115084Sjohnlev 		 * For xpv entries, there are two module lines, so we
74125084Sjohnlev 		 * do the check twice.
74133467Srscott 		 */
7414662Sszhou 		lp = lp->next;	/* advance to module line */
74155084Sjohnlev 		if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
74165084Sjohnlev 		    (((lp = lp->next) != NULL) &&
74175084Sjohnlev 		    check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
74185084Sjohnlev 			/* match found */
74196448Svikram 			BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
74205084Sjohnlev 			break;
7421662Sszhou 		}
74228104SEnrico.Perla@Sun.COM 
74238104SEnrico.Perla@Sun.COM 		if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
74248104SEnrico.Perla@Sun.COM 		    (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
74258104SEnrico.Perla@Sun.COM 		    strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
74268104SEnrico.Perla@Sun.COM 			ent->flags |= BAM_ENTRY_UPGFSMODULE;
74278104SEnrico.Perla@Sun.COM 			break;
74288104SEnrico.Perla@Sun.COM 		}
74298104SEnrico.Perla@Sun.COM 
74305084Sjohnlev 	}
74315084Sjohnlev 
74326448Svikram 	if (ent && entry_num) {
74335084Sjohnlev 		*entry_num = i;
74345084Sjohnlev 	}
74356448Svikram 
74366448Svikram 	if (ent) {
74376448Svikram 		BAM_DPRINTF((D_RETURN_RET, fcn, i));
74386448Svikram 	} else {
74396448Svikram 		BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
74406448Svikram 	}
7441662Sszhou 	return (ent);
7442662Sszhou }
7443662Sszhou 
7444662Sszhou static int
74456448Svikram update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
74466448Svikram     char *kernel, char *mod_kernel, char *module, int root_opt)
74476448Svikram {
74486448Svikram 	int		i;
74496448Svikram 	int		change_kernel = 0;
74506448Svikram 	entry_t		*ent;
74516448Svikram 	line_t		*lp;
74526448Svikram 	line_t		*tlp;
74536448Svikram 	char		linebuf[BAM_MAXLINE];
74546448Svikram 	const char	*fcn = "update_boot_entry()";
7455662Sszhou 
7456662Sszhou 	/* note: don't match on title, it's updated on upgrade */
74576448Svikram 	ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
74586448Svikram 	    root_opt, &i);
74593446Smrj 	if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
74603446Smrj 		/*
74613446Smrj 		 * We may be upgrading a kernel from multiboot to
74626448Svikram 		 * directboot.  Look for a multiboot entry. A multiboot
74636448Svikram 		 * entry will not have a findroot line.
74643446Smrj 		 */
74656448Svikram 		ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
74666448Svikram 		    MULTIBOOT_ARCHIVE, root_opt, &i);
74673446Smrj 		if (ent != NULL) {
74686448Svikram 			BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
74693446Smrj 			change_kernel = 1;
74703446Smrj 		}
74716448Svikram 	} else if (ent) {
74726448Svikram 		BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
74736448Svikram 	}
74746448Svikram 
74756448Svikram 	if (ent == NULL) {
74766448Svikram 		BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
74776448Svikram 		return (add_boot_entry(mp, title, findroot,
74785084Sjohnlev 		    kernel, mod_kernel, module));
74796448Svikram 	}
74806448Svikram 
74816448Svikram 	/* replace title of existing entry and update findroot line */
7482662Sszhou 	lp = ent->start;
7483662Sszhou 	lp = lp->next;	/* title line */
7484662Sszhou 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
7485662Sszhou 	    menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
7486662Sszhou 	free(lp->arg);
7487662Sszhou 	free(lp->line);
7488662Sszhou 	lp->arg = s_strdup(title);
7489662Sszhou 	lp->line = s_strdup(linebuf);
74906448Svikram 	BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
74916448Svikram 
74926448Svikram 	tlp = lp;	/* title line */
7493662Sszhou 	lp = lp->next;	/* root line */
74946448Svikram 
74956448Svikram 	/* if no root or findroot command, create a new line_t */
74966448Svikram 	if (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
74976448Svikram 	    strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0) {
74986448Svikram 		lp = s_calloc(1, sizeof (line_t));
74996448Svikram 		bam_add_line(mp, ent, tlp, lp);
75006448Svikram 	} else {
75016448Svikram 		free(lp->cmd);
75026448Svikram 		free(lp->sep);
75036448Svikram 		free(lp->arg);
75046448Svikram 		free(lp->line);
75056448Svikram 	}
75066448Svikram 
75076448Svikram 	lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
75086448Svikram 	lp->sep = s_strdup(menu_cmds[SEP_CMD]);
75096448Svikram 	lp->arg = s_strdup(findroot);
75106448Svikram 	(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
75116448Svikram 	    menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
75126448Svikram 	lp->line = s_strdup(linebuf);
75136448Svikram 	BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
75146448Svikram 
75156448Svikram 	/* kernel line */
75166448Svikram 	lp = lp->next;
75173446Smrj 
75188104SEnrico.Perla@Sun.COM 	if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
75198104SEnrico.Perla@Sun.COM 		char		*params = NULL;
75208104SEnrico.Perla@Sun.COM 
75218104SEnrico.Perla@Sun.COM 		params = strstr(lp->line, "-s");
75228104SEnrico.Perla@Sun.COM 		if (params != NULL)
75238104SEnrico.Perla@Sun.COM 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
75248104SEnrico.Perla@Sun.COM 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
75258104SEnrico.Perla@Sun.COM 			    kernel, params+2);
75268104SEnrico.Perla@Sun.COM 		else
75278104SEnrico.Perla@Sun.COM 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
75288104SEnrico.Perla@Sun.COM 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
75298104SEnrico.Perla@Sun.COM 			    kernel);
75308104SEnrico.Perla@Sun.COM 
75318104SEnrico.Perla@Sun.COM 		free(lp->cmd);
75328104SEnrico.Perla@Sun.COM 		free(lp->arg);
75338104SEnrico.Perla@Sun.COM 		free(lp->line);
75348104SEnrico.Perla@Sun.COM 		lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
75358104SEnrico.Perla@Sun.COM 		lp->arg = s_strdup(strstr(linebuf, "/"));
75368104SEnrico.Perla@Sun.COM 		lp->line = s_strdup(linebuf);
75378104SEnrico.Perla@Sun.COM 		ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
75388104SEnrico.Perla@Sun.COM 		BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd));
75398104SEnrico.Perla@Sun.COM 	}
75408104SEnrico.Perla@Sun.COM 
75413446Smrj 	if (change_kernel) {
75423446Smrj 		/*
75433446Smrj 		 * We're upgrading from multiboot to directboot.
75443446Smrj 		 */
75453446Smrj 		if (strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
75463446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
75473446Smrj 			    menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
75483446Smrj 			    kernel);
75496448Svikram 			free(lp->cmd);
75503446Smrj 			free(lp->arg);
75513446Smrj 			free(lp->line);
75526448Svikram 			lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
75533446Smrj 			lp->arg = s_strdup(kernel);
75543446Smrj 			lp->line = s_strdup(linebuf);
75553446Smrj 			lp = lp->next;
75566448Svikram 			BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
75573446Smrj 		}
75583446Smrj 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
75593446Smrj 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
75603446Smrj 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
75613446Smrj 			    module);
75626448Svikram 			free(lp->cmd);
75633446Smrj 			free(lp->arg);
75643446Smrj 			free(lp->line);
75656448Svikram 			lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
75663446Smrj 			lp->arg = s_strdup(module);
75673446Smrj 			lp->line = s_strdup(linebuf);
75683446Smrj 			lp = lp->next;
75696448Svikram 			BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
75706448Svikram 		}
75716448Svikram 	}
75728104SEnrico.Perla@Sun.COM 
75738104SEnrico.Perla@Sun.COM 	/* module line */
75748104SEnrico.Perla@Sun.COM 	lp = lp->next;
75758104SEnrico.Perla@Sun.COM 
75768104SEnrico.Perla@Sun.COM 	if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
75778104SEnrico.Perla@Sun.COM 		if (strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
75788104SEnrico.Perla@Sun.COM 			(void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
75798104SEnrico.Perla@Sun.COM 			    menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
75808104SEnrico.Perla@Sun.COM 			    module);
75818104SEnrico.Perla@Sun.COM 			free(lp->cmd);
75828104SEnrico.Perla@Sun.COM 			free(lp->arg);
75838104SEnrico.Perla@Sun.COM 			free(lp->line);
75848104SEnrico.Perla@Sun.COM 			lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
75858104SEnrico.Perla@Sun.COM 			lp->arg = s_strdup(module);
75868104SEnrico.Perla@Sun.COM 			lp->line = s_strdup(linebuf);
75878104SEnrico.Perla@Sun.COM 			lp = lp->next;
75888104SEnrico.Perla@Sun.COM 			ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
75898104SEnrico.Perla@Sun.COM 			BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
75908104SEnrico.Perla@Sun.COM 		}
75918104SEnrico.Perla@Sun.COM 	}
75928104SEnrico.Perla@Sun.COM 
75936448Svikram 	BAM_DPRINTF((D_RETURN_RET, fcn, i));
7594662Sszhou 	return (i);
7595662Sszhou }
7596662Sszhou 
75976448Svikram int
75986448Svikram root_optional(char *osroot, char *menu_root)
75996448Svikram {
76006448Svikram 	char			*ospecial;
76016448Svikram 	char			*mspecial;
76026448Svikram 	char			*slash;
76036448Svikram 	int			root_opt;
76046448Svikram 	int			ret1;
76056448Svikram 	int			ret2;
76066448Svikram 	const char		*fcn = "root_optional()";
76076448Svikram 
76086448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
76096448Svikram 
76106448Svikram 	/*
76116448Svikram 	 * For all filesystems except ZFS, a straight compare of osroot
76126448Svikram 	 * and menu_root will tell us if root is optional.
76136448Svikram 	 * For ZFS, the situation is complicated by the fact that
76146448Svikram 	 * menu_root and osroot are always different
76156448Svikram 	 */
76166448Svikram 	ret1 = is_zfs(osroot);
76176448Svikram 	ret2 = is_zfs(menu_root);
76186448Svikram 	INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
76196448Svikram 	if (!ret1 || !ret2) {
76206448Svikram 		BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
76216448Svikram 		root_opt = (strcmp(osroot, menu_root) == 0);
76226448Svikram 		goto out;
76236448Svikram 	}
76246448Svikram 
76256448Svikram 	ospecial = get_special(osroot);
76266448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
76276448Svikram 	if (ospecial == NULL) {
76286448Svikram 		bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
76296448Svikram 		return (0);
76306448Svikram 	}
76316448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
76326448Svikram 
76336448Svikram 	mspecial = get_special(menu_root);
76346448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
76356448Svikram 	if (mspecial == NULL) {
76366448Svikram 		bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
76376448Svikram 		free(ospecial);
76386448Svikram 		return (0);
76396448Svikram 	}
76406448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
76416448Svikram 
76426448Svikram 	slash = strchr(ospecial, '/');
76436448Svikram 	if (slash)
76446448Svikram 		*slash = '\0';
76456448Svikram 	BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
76466448Svikram 
76476448Svikram 	root_opt = (strcmp(ospecial, mspecial) == 0);
76486448Svikram 
76496448Svikram 	free(ospecial);
76506448Svikram 	free(mspecial);
76516448Svikram 
76526448Svikram out:
76536448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
76546448Svikram 	INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
76556448Svikram 	if (root_opt) {
76566448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
76576448Svikram 	} else {
76586448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
76596448Svikram 	}
76606448Svikram 
76616448Svikram 	return (root_opt);
76626448Svikram }
76636448Svikram 
76640Sstevel@tonic-gate /*ARGSUSED*/
76650Sstevel@tonic-gate static error_t
76666448Svikram update_entry(menu_t *mp, char *menu_root, char *osdev)
76676448Svikram {
76686448Svikram 	int		entry;
76696448Svikram 	char		*grubsign;
76706448Svikram 	char		*grubroot;
76716448Svikram 	char		*title;
76726448Svikram 	char		osroot[PATH_MAX];
76736448Svikram 	char		*failsafe_kernel = NULL;
76746448Svikram 	struct stat	sbuf;
76756448Svikram 	char		failsafe[256];
76768104SEnrico.Perla@Sun.COM 	char		failsafe_64[256];
76776448Svikram 	int		ret;
76786448Svikram 	const char	*fcn = "update_entry()";
76790Sstevel@tonic-gate 
76800Sstevel@tonic-gate 	assert(mp);
76816448Svikram 	assert(menu_root);
76826448Svikram 	assert(osdev);
76836448Svikram 	assert(bam_root);
76846448Svikram 
76856448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
76866448Svikram 
76876448Svikram 	(void) strlcpy(osroot, bam_root, sizeof (osroot));
76886448Svikram 
76890Sstevel@tonic-gate 	title = get_title(osroot);
76906448Svikram 	assert(title);
76916448Svikram 
76926448Svikram 	grubsign = get_grubsign(osroot, osdev);
76936448Svikram 	INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
76946448Svikram 	if (grubsign == NULL) {
76956448Svikram 		bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
76960Sstevel@tonic-gate 		return (BAM_ERROR);
76970Sstevel@tonic-gate 	}
76986448Svikram 
76996448Svikram 	/*
77006448Svikram 	 * It is not a fatal error if get_grubroot() fails
77016448Svikram 	 * We no longer rely on biosdev to populate the
77026448Svikram 	 * menu
77036448Svikram 	 */
77046448Svikram 	grubroot = get_grubroot(osroot, osdev, menu_root);
77056448Svikram 	INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
77066448Svikram 	if (grubroot) {
77076448Svikram 		BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
77086448Svikram 		    fcn, osroot, osdev, menu_root));
77096448Svikram 	} else {
77106448Svikram 		BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
77116448Svikram 		    fcn, osroot, osdev, menu_root));
77120Sstevel@tonic-gate 	}
77130Sstevel@tonic-gate 
77140Sstevel@tonic-gate 	/* add the entry for normal Solaris */
77156448Svikram 	INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
77166448Svikram 	    bam_direct = BAM_DIRECT_MULTIBOOT);
77173446Smrj 	if (bam_direct == BAM_DIRECT_DBOOT) {
77186448Svikram 		entry = update_boot_entry(mp, title, grubsign, grubroot,
77196423Sgw25295 		    (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
77206448Svikram 		    NULL, DIRECT_BOOT_ARCHIVE,
77216448Svikram 		    root_optional(osroot, menu_root));
77226448Svikram 		BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
77235084Sjohnlev 		if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
77246448Svikram 			(void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
77256448Svikram 			    grubroot, XEN_MENU, bam_zfs ?
77266448Svikram 			    XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
77276448Svikram 			    DIRECT_BOOT_ARCHIVE,
77286448Svikram 			    root_optional(osroot, menu_root));
77296448Svikram 			BAM_DPRINTF((D_UPDATED_HV_ENTRY,
77306448Svikram 			    fcn, bam_zfs, grubsign));
77315084Sjohnlev 		}
77323446Smrj 	} else {
77336448Svikram 		entry = update_boot_entry(mp, title, grubsign, grubroot,
77346448Svikram 		    MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
77356448Svikram 		    root_optional(osroot, menu_root));
77366448Svikram 
77376448Svikram 		BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
77385084Sjohnlev 	}
77395084Sjohnlev 
77405084Sjohnlev 	/*
77415084Sjohnlev 	 * Add the entry for failsafe archive.  On a bfu'd system, the
77425084Sjohnlev 	 * failsafe may be different than the installed kernel.
77435084Sjohnlev 	 */
77446448Svikram 	(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
77458104SEnrico.Perla@Sun.COM 	    osroot, FAILSAFE_ARCHIVE_32);
77468104SEnrico.Perla@Sun.COM 	(void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
77478104SEnrico.Perla@Sun.COM 	    osroot, FAILSAFE_ARCHIVE_64);
77488104SEnrico.Perla@Sun.COM 
77498104SEnrico.Perla@Sun.COM 	/*
77508104SEnrico.Perla@Sun.COM 	 * Check if at least one of the two archives exists
77518104SEnrico.Perla@Sun.COM 	 * Using $ISADIR as the default line, we have an entry which works
77528104SEnrico.Perla@Sun.COM 	 * for both the cases.
77538104SEnrico.Perla@Sun.COM 	 */
77548104SEnrico.Perla@Sun.COM 
77558104SEnrico.Perla@Sun.COM 	if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
77563449Srscott 
77573449Srscott 		/* Figure out where the kernel line should point */
77583449Srscott 		(void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
77598104SEnrico.Perla@Sun.COM 		    DIRECT_BOOT_FAILSAFE_32);
77608104SEnrico.Perla@Sun.COM 		(void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
77618104SEnrico.Perla@Sun.COM 		    osroot, DIRECT_BOOT_FAILSAFE_64);
77628104SEnrico.Perla@Sun.COM 		if (stat(failsafe, &sbuf) == 0 ||
77638104SEnrico.Perla@Sun.COM 		    stat(failsafe_64, &sbuf) == 0) {
77646694Svikram 			failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
77653449Srscott 		} else {
77663449Srscott 			(void) snprintf(failsafe, sizeof (failsafe), "%s%s",
77673449Srscott 			    osroot, MULTI_BOOT_FAILSAFE);
77683449Srscott 			if (stat(failsafe, &sbuf) == 0) {
77693449Srscott 				failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
77703449Srscott 			}
77713449Srscott 		}
77723449Srscott 		if (failsafe_kernel != NULL) {
77736448Svikram 			(void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
77746448Svikram 			    grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
77756448Svikram 			    root_optional(osroot, menu_root));
77766448Svikram 			BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
77776448Svikram 			    failsafe_kernel));
77786448Svikram 		}
77796448Svikram 	}
77806448Svikram 	free(grubroot);
77816448Svikram 
77826448Svikram 	INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
77830Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
77846448Svikram 		bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
77856448Svikram 		free(grubsign);
77860Sstevel@tonic-gate 		return (BAM_ERROR);
77870Sstevel@tonic-gate 	}
77886448Svikram 	free(grubsign);
77896448Svikram 
77906448Svikram 	update_numbering(mp);
77916448Svikram 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
77926448Svikram 	INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
77936448Svikram 	if (ret == BAM_ERROR) {
77946448Svikram 		bam_error(SET_DEFAULT_FAILED, entry);
77956448Svikram 	}
77966448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
77970Sstevel@tonic-gate 	return (BAM_WRITE);
77980Sstevel@tonic-gate }
77990Sstevel@tonic-gate 
7800662Sszhou static void
78013446Smrj save_default_entry(menu_t *mp, const char *which)
7802662Sszhou {
78036448Svikram 	int		lineNum;
78046448Svikram 	int		entryNum;
78056448Svikram 	int		entry = 0;	/* default is 0 */
78066448Svikram 	char		linebuf[BAM_MAXLINE];
78076448Svikram 	line_t		*lp = mp->curdefault;
78086448Svikram 	const char	*fcn = "save_default_entry()";
7809662Sszhou 
78103381Svikram 	if (mp->start) {
78113381Svikram 		lineNum = mp->end->lineNum;
78123381Svikram 		entryNum = mp->end->entryNum;
78133381Svikram 	} else {
78143381Svikram 		lineNum = LINE_INIT;
78153381Svikram 		entryNum = ENTRY_INIT;
78163381Svikram 	}
78173381Svikram 
7818662Sszhou 	if (lp)
7819662Sszhou 		entry = s_strtol(lp->arg);
7820662Sszhou 
78213446Smrj 	(void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
78226448Svikram 	BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
7823662Sszhou 	line_parser(mp, linebuf, &lineNum, &entryNum);
78246448Svikram 	BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
7825662Sszhou }
7826662Sszhou 
7827662Sszhou static void
78283446Smrj restore_default_entry(menu_t *mp, const char *which, line_t *lp)
7829662Sszhou {
78306448Svikram 	int		entry;
78316448Svikram 	char		*str;
78326448Svikram 	const char	*fcn = "restore_default_entry()";
78336448Svikram 
78346448Svikram 	if (lp == NULL) {
78356448Svikram 		BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
7836662Sszhou 		return;		/* nothing to restore */
78376448Svikram 	}
78386448Svikram 
78396448Svikram 	BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
7840662Sszhou 
78413446Smrj 	str = lp->arg + strlen(which);
7842662Sszhou 	entry = s_strtol(str);
7843662Sszhou 	(void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
7844662Sszhou 
78456448Svikram 	BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
78466448Svikram 
7847662Sszhou 	/* delete saved old default line */
7848662Sszhou 	unlink_line(mp, lp);
7849662Sszhou 	line_free(lp);
7850662Sszhou }
7851662Sszhou 
78520Sstevel@tonic-gate /*
78530Sstevel@tonic-gate  * This function is for supporting reboot with args.
78540Sstevel@tonic-gate  * The opt value can be:
78550Sstevel@tonic-gate  * NULL		delete temp entry, if present
78566448Svikram  * entry=<n>	switches default entry to <n>
78570Sstevel@tonic-gate  * else		treated as boot-args and setup a temperary menu entry
78580Sstevel@tonic-gate  *		and make it the default
78596448Svikram  * Note that we are always rebooting the current OS instance
78606448Svikram  * so osroot == / always.
78610Sstevel@tonic-gate  */
78620Sstevel@tonic-gate #define	REBOOT_TITLE	"Solaris_reboot_transient"
78630Sstevel@tonic-gate 
7864662Sszhou /*ARGSUSED*/
78650Sstevel@tonic-gate static error_t
78666448Svikram update_temp(menu_t *mp, char *dummy, char *opt)
78676448Svikram {
78686448Svikram 	int		entry;
78696448Svikram 	char		*osdev;
78706448Svikram 	char		*fstype;
78716448Svikram 	char		*sign;
78726448Svikram 	char		*opt_ptr;
78736448Svikram 	char		*path;
78746448Svikram 	char		kernbuf[BUFSIZ];
78756448Svikram 	char		args_buf[BUFSIZ];
78766448Svikram 	char		signbuf[PATH_MAX];
78776448Svikram 	int		ret;
78786448Svikram 	const char	*fcn = "update_temp()";
78790Sstevel@tonic-gate 
78800Sstevel@tonic-gate 	assert(mp);
78816448Svikram 	assert(dummy == NULL);
78826448Svikram 
78836448Svikram 	/* opt can be NULL */
78846448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
78856448Svikram 	BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
78866448Svikram 
78876448Svikram 	if (bam_alt_root || bam_rootlen != 1 ||
78886448Svikram 	    strcmp(bam_root, "/") != 0 ||
78896448Svikram 	    strcmp(rootbuf, "/") != 0) {
78906448Svikram 		bam_error(ALT_ROOT_INVALID, bam_root);
78916448Svikram 		return (BAM_ERROR);
78926448Svikram 	}
78930Sstevel@tonic-gate 
7894662Sszhou 	/* If no option, delete exiting reboot menu entry */
7895662Sszhou 	if (opt == NULL) {
78966448Svikram 		entry_t		*ent;
78976448Svikram 		BAM_DPRINTF((D_OPT_NULL, fcn));
78986448Svikram 		ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
78996448Svikram 		    NULL, NULL, 0, &entry);
79006448Svikram 		if (ent == NULL) {	/* not found is ok */
79016448Svikram 			BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
7902662Sszhou 			return (BAM_SUCCESS);
79036448Svikram 		}
7904662Sszhou 		(void) do_delete(mp, entry);
79053446Smrj 		restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
79063446Smrj 		mp->olddefault = NULL;
79076448Svikram 		BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
79086448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7909662Sszhou 		return (BAM_WRITE);
7910662Sszhou 	}
7911662Sszhou 
7912662Sszhou 	/* if entry= is specified, set the default entry */
79136448Svikram 	if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
79146448Svikram 		int entryNum = s_strtol(opt + strlen("entry="));
79156448Svikram 		BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
79166448Svikram 		if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
79176448Svikram 			/* this is entry=# option */
79186448Svikram 			ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
79196448Svikram 			BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
79206448Svikram 			return (ret);
79216448Svikram 		} else {
79226448Svikram 			bam_error(SET_DEFAULT_FAILED, entryNum);
79236448Svikram 			return (BAM_ERROR);
79246448Svikram 		}
79250Sstevel@tonic-gate 	}
79260Sstevel@tonic-gate 
79270Sstevel@tonic-gate 	/*
79286448Svikram 	 * add a new menu entry based on opt and make it the default
79290Sstevel@tonic-gate 	 */
79306448Svikram 
79316448Svikram 	fstype = get_fstype("/");
79326448Svikram 	INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
79336448Svikram 	if (fstype == NULL) {
79346448Svikram 		bam_error(REBOOT_FSTYPE_FAILED);
79356448Svikram 		return (BAM_ERROR);
79366448Svikram 	}
79376448Svikram 
79386448Svikram 	osdev = get_special("/");
79396448Svikram 	INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
79406448Svikram 	if (osdev == NULL) {
79416448Svikram 		free(fstype);
79426448Svikram 		bam_error(REBOOT_SPECIAL_FAILED);
79436448Svikram 		return (BAM_ERROR);
79446448Svikram 	}
79456448Svikram 
79466448Svikram 	sign = find_existing_sign("/", osdev, fstype);
79476448Svikram 	INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
79486448Svikram 	if (sign == NULL) {
79496448Svikram 		free(fstype);
79506448Svikram 		free(osdev);
79516448Svikram 		bam_error(REBOOT_SIGN_FAILED);
79526448Svikram 		return (BAM_ERROR);
79536448Svikram 	}
79546448Svikram 
79556448Svikram 	free(osdev);
79566448Svikram 	(void) strlcpy(signbuf, sign, sizeof (signbuf));
79576448Svikram 	free(sign);
79586448Svikram 
79596448Svikram 	assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
79606448Svikram 	    strchr(signbuf, ')') == NULL);
79616448Svikram 
79626448Svikram 	/*
79636448Svikram 	 * There is no alternate root while doing reboot with args
79646448Svikram 	 * This version of bootadm is only delivered with a DBOOT
79656448Svikram 	 * version of Solaris.
79666448Svikram 	 */
79676448Svikram 	INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
79686448Svikram 	if (bam_direct != BAM_DIRECT_DBOOT) {
7969*8751SLin.Ling@Sun.COM 		free(fstype);
79706448Svikram 		bam_error(REBOOT_DIRECT_FAILED);
79710Sstevel@tonic-gate 		return (BAM_ERROR);
79720Sstevel@tonic-gate 	}
79730Sstevel@tonic-gate 
79740Sstevel@tonic-gate 	/* add an entry for Solaris reboot */
79756448Svikram 	if (opt[0] == '-') {
79766448Svikram 		/* It's an option - first see if boot-file is set */
79776448Svikram 		ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
79786448Svikram 		INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
79796448Svikram 		if (ret != BAM_SUCCESS) {
7980*8751SLin.Ling@Sun.COM 			free(fstype);
79816448Svikram 			bam_error(REBOOT_GET_KERNEL_FAILED);
79826448Svikram 			return (BAM_ERROR);
79836448Svikram 		}
79846448Svikram 		if (kernbuf[0] == '\0')
79856448Svikram 			(void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
79866448Svikram 			    sizeof (kernbuf));
7987*8751SLin.Ling@Sun.COM 		/*
7988*8751SLin.Ling@Sun.COM 		 * If this is a zfs file system and kernbuf does not
7989*8751SLin.Ling@Sun.COM 		 * have "-B $ZFS-BOOTFS" string yet, add it.
7990*8751SLin.Ling@Sun.COM 		 */
7991*8751SLin.Ling@Sun.COM 		if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
7992*8751SLin.Ling@Sun.COM 			(void) strlcat(kernbuf, " ", sizeof (kernbuf));
7993*8751SLin.Ling@Sun.COM 			(void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
7994*8751SLin.Ling@Sun.COM 		}
79956448Svikram 		(void) strlcat(kernbuf, " ", sizeof (kernbuf));
79966448Svikram 		(void) strlcat(kernbuf, opt, sizeof (kernbuf));
79976448Svikram 		BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
79986448Svikram 	} else if (opt[0] == '/') {
79996448Svikram 		/* It's a full path, so write it out. */
80006448Svikram 		(void) strlcpy(kernbuf, opt, sizeof (kernbuf));
80016448Svikram 
80026448Svikram 		/*
80036448Svikram 		 * If someone runs:
80046448Svikram 		 *
80056448Svikram 		 *	# eeprom boot-args='-kd'
80066448Svikram 		 *	# reboot /platform/i86pc/kernel/unix
80076448Svikram 		 *
80086448Svikram 		 * we want to use the boot-args as part of the boot
80096448Svikram 		 * line.  On the other hand, if someone runs:
80106448Svikram 		 *
80116448Svikram 		 *	# reboot "/platform/i86pc/kernel/unix -kd"
80126448Svikram 		 *
80136448Svikram 		 * we don't need to mess with boot-args.  If there's
80146448Svikram 		 * no space in the options string, assume we're in the
80156448Svikram 		 * first case.
80166448Svikram 		 */
80176448Svikram 		if (strchr(opt, ' ') == NULL) {
80186448Svikram 			ret = get_kernel(mp, ARGS_CMD, args_buf,
80196448Svikram 			    sizeof (args_buf));
80206448Svikram 			INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
80216448Svikram 			if (ret != BAM_SUCCESS) {
8022*8751SLin.Ling@Sun.COM 				free(fstype);
80236448Svikram 				bam_error(REBOOT_GET_ARGS_FAILED);
80243446Smrj 				return (BAM_ERROR);
80256448Svikram 			}
80266448Svikram 
80276448Svikram 			if (args_buf[0] != '\0') {
80286448Svikram 				(void) strlcat(kernbuf, " ", sizeof (kernbuf));
80296448Svikram 				(void) strlcat(kernbuf, args_buf,
80306448Svikram 				    sizeof (kernbuf));
80316448Svikram 			}
80326448Svikram 		}
80336448Svikram 		BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
80346448Svikram 	} else {
80356448Svikram 		/*
80366448Svikram 		 * It may be a partial path, or it may be a partial
80376448Svikram 		 * path followed by options.  Assume that only options
80386448Svikram 		 * follow a space.  If someone sends us a kernel path
80396448Svikram 		 * that includes a space, they deserve to be broken.
80406448Svikram 		 */
80416448Svikram 		opt_ptr = strchr(opt, ' ');
80426448Svikram 		if (opt_ptr != NULL) {
80436448Svikram 			*opt_ptr = '\0';
80446448Svikram 		}
80456448Svikram 
80466448Svikram 		path = expand_path(opt);
80476448Svikram 		if (path != NULL) {
80486448Svikram 			(void) strlcpy(kernbuf, path, sizeof (kernbuf));
80496448Svikram 			free(path);
80504346Srscott 
80514346Srscott 			/*
80526448Svikram 			 * If there were options given, use those.
80536448Svikram 			 * Otherwise, copy over the default options.
80544346Srscott 			 */
80556448Svikram 			if (opt_ptr != NULL) {
80566448Svikram 				/* Restore the space in opt string */
80576448Svikram 				*opt_ptr = ' ';
80586448Svikram 				(void) strlcat(kernbuf, opt_ptr,
80596448Svikram 				    sizeof (kernbuf));
80606448Svikram 			} else {
80616448Svikram 				ret = get_kernel(mp, ARGS_CMD, args_buf,
80626448Svikram 				    sizeof (args_buf));
80636448Svikram 				INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
80646448Svikram 				    ret = BAM_ERROR);
80656448Svikram 				if (ret != BAM_SUCCESS) {
8066*8751SLin.Ling@Sun.COM 					free(fstype);
80676448Svikram 					bam_error(REBOOT_GET_ARGS_FAILED);
80684346Srscott 					return (BAM_ERROR);
80696448Svikram 				}
80704346Srscott 
80714346Srscott 				if (args_buf[0] != '\0') {
80726448Svikram 					(void) strlcat(kernbuf, " ",
80736448Svikram 					    sizeof (kernbuf));
80746448Svikram 					(void) strlcat(kernbuf,
80756448Svikram 					    args_buf, sizeof (kernbuf));
80764346Srscott 				}
80774346Srscott 			}
80786448Svikram 			BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
80793446Smrj 		} else {
8080*8751SLin.Ling@Sun.COM 			free(fstype);
80816448Svikram 			bam_error(UNKNOWN_KERNEL, opt);
80826448Svikram 			bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
80836448Svikram 			return (BAM_ERROR);
80846448Svikram 		}
80856448Svikram 	}
8086*8751SLin.Ling@Sun.COM 	free(fstype);
80876448Svikram 	entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
80886448Svikram 	    NULL, NULL);
80896448Svikram 	INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
80900Sstevel@tonic-gate 	if (entry == BAM_ERROR) {
80916448Svikram 		bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
80920Sstevel@tonic-gate 		return (BAM_ERROR);
80930Sstevel@tonic-gate 	}
8094662Sszhou 
80953446Smrj 	save_default_entry(mp, BAM_OLDDEF);
80966448Svikram 	ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
80976448Svikram 	INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
80986448Svikram 	if (ret == BAM_ERROR) {
80996448Svikram 		bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
81006448Svikram 	}
81016448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
81020Sstevel@tonic-gate 	return (BAM_WRITE);
81030Sstevel@tonic-gate }
81040Sstevel@tonic-gate 
81050Sstevel@tonic-gate static error_t
81060Sstevel@tonic-gate set_global(menu_t *mp, char *globalcmd, int val)
81070Sstevel@tonic-gate {
81086448Svikram 	line_t		*lp;
81096448Svikram 	line_t		*found;
81106448Svikram 	line_t		*last;
81116448Svikram 	char		*cp;
81126448Svikram 	char		*str;
81136448Svikram 	char		prefix[BAM_MAXLINE];
81146448Svikram 	size_t		len;
81156448Svikram 	const char	*fcn = "set_global()";
81160Sstevel@tonic-gate 
81170Sstevel@tonic-gate 	assert(mp);
81180Sstevel@tonic-gate 	assert(globalcmd);
81190Sstevel@tonic-gate 
8120316Svikram 	if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
81216448Svikram 		INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
81226448Svikram 		INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
81236448Svikram 		INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
8124316Svikram 		if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
8125316Svikram 			(void) snprintf(prefix, sizeof (prefix), "%d", val);
8126316Svikram 			bam_error(INVALID_ENTRY, prefix);
8127316Svikram 			return (BAM_ERROR);
8128316Svikram 		}
8129316Svikram 	}
8130316Svikram 
81310Sstevel@tonic-gate 	found = last = NULL;
81320Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
81330Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
81340Sstevel@tonic-gate 			continue;
81350Sstevel@tonic-gate 
81360Sstevel@tonic-gate 		last = lp; /* track the last global found */
81370Sstevel@tonic-gate 
81386448Svikram 		INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
81390Sstevel@tonic-gate 		if (lp->cmd == NULL) {
81400Sstevel@tonic-gate 			bam_error(NO_CMD, lp->lineNum);
81410Sstevel@tonic-gate 			continue;
81420Sstevel@tonic-gate 		}
81430Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
81440Sstevel@tonic-gate 			continue;
81450Sstevel@tonic-gate 
81466448Svikram 		BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
81476448Svikram 
81480Sstevel@tonic-gate 		if (found) {
81490Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
81500Sstevel@tonic-gate 		}
81510Sstevel@tonic-gate 		found = lp;
81520Sstevel@tonic-gate 	}
81530Sstevel@tonic-gate 
81540Sstevel@tonic-gate 	if (found == NULL) {
81550Sstevel@tonic-gate 		lp = s_calloc(1, sizeof (line_t));
81560Sstevel@tonic-gate 		if (last == NULL) {
81570Sstevel@tonic-gate 			lp->next = mp->start;
81580Sstevel@tonic-gate 			mp->start = lp;
81590Sstevel@tonic-gate 			mp->end = (mp->end) ? mp->end : lp;
81600Sstevel@tonic-gate 		} else {
81610Sstevel@tonic-gate 			lp->next = last->next;
81620Sstevel@tonic-gate 			last->next = lp;
81630Sstevel@tonic-gate 			if (lp->next == NULL)
81640Sstevel@tonic-gate 				mp->end = lp;
81650Sstevel@tonic-gate 		}
81660Sstevel@tonic-gate 		lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
81670Sstevel@tonic-gate 		len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
81680Sstevel@tonic-gate 		len += 10;	/* val < 10 digits */
81690Sstevel@tonic-gate 		lp->line = s_calloc(1, len);
81700Sstevel@tonic-gate 		(void) snprintf(lp->line, len, "%s%s%d",
81710Sstevel@tonic-gate 		    globalcmd, menu_cmds[SEP_CMD], val);
81726448Svikram 		BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
81736448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
81740Sstevel@tonic-gate 		return (BAM_WRITE);
81750Sstevel@tonic-gate 	}
81760Sstevel@tonic-gate 
81770Sstevel@tonic-gate 	/*
81780Sstevel@tonic-gate 	 * We are changing an existing entry. Retain any prefix whitespace,
81790Sstevel@tonic-gate 	 * but overwrite everything else. This preserves tabs added for
81800Sstevel@tonic-gate 	 * readability.
81810Sstevel@tonic-gate 	 */
81820Sstevel@tonic-gate 	str = found->line;
81830Sstevel@tonic-gate 	cp = prefix;
81840Sstevel@tonic-gate 	while (*str == ' ' || *str == '\t')
81850Sstevel@tonic-gate 		*(cp++) = *(str++);
81860Sstevel@tonic-gate 	*cp = '\0'; /* Terminate prefix */
81870Sstevel@tonic-gate 	len = strlen(prefix) + strlen(globalcmd);
81880Sstevel@tonic-gate 	len += strlen(menu_cmds[SEP_CMD]) + 10;
81890Sstevel@tonic-gate 
81900Sstevel@tonic-gate 	free(found->line);
81910Sstevel@tonic-gate 	found->line = s_calloc(1, len);
81920Sstevel@tonic-gate 	(void) snprintf(found->line, len,
81934346Srscott 	    "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
81940Sstevel@tonic-gate 
81956448Svikram 	BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
81966448Svikram 	BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
81970Sstevel@tonic-gate 	return (BAM_WRITE); /* need a write to menu */
81980Sstevel@tonic-gate }
81990Sstevel@tonic-gate 
82003446Smrj /*
82013446Smrj  * partial_path may be anything like "kernel/unix" or "kmdb".  Try to
82024346Srscott  * expand it to a full unix path.  The calling function is expected to
82034346Srscott  * output a message if an error occurs and NULL is returned.
82043446Smrj  */
82053446Smrj static char *
82063446Smrj expand_path(const char *partial_path)
82073446Smrj {
82086448Svikram 	int		new_path_len;
82096448Svikram 	char		*new_path;
82106448Svikram 	char		new_path2[PATH_MAX];
82116448Svikram 	struct stat	sb;
82126448Svikram 	const char	*fcn = "expand_path()";
82133446Smrj 
82143446Smrj 	new_path_len = strlen(partial_path) + 64;
82153446Smrj 	new_path = s_calloc(1, new_path_len);
82163446Smrj 
82173446Smrj 	/* First, try the simplest case - something like "kernel/unix" */
82183446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
82193446Smrj 	    partial_path);
82203446Smrj 	if (stat(new_path, &sb) == 0) {
82216448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
82223446Smrj 		return (new_path);
82233446Smrj 	}
82243446Smrj 
82253446Smrj 	if (strcmp(partial_path, "kmdb") == 0) {
82263446Smrj 		(void) snprintf(new_path, new_path_len, "%s -k",
82273446Smrj 		    DIRECT_BOOT_KERNEL);
82286448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
82293446Smrj 		return (new_path);
82303446Smrj 	}
82313446Smrj 
82323446Smrj 	/*
82333446Smrj 	 * We've quickly reached unsupported usage.  Try once more to
82343446Smrj 	 * see if we were just given a glom name.
82353446Smrj 	 */
82363446Smrj 	(void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
82373446Smrj 	    partial_path);
82383446Smrj 	(void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
82393446Smrj 	    partial_path);
82403446Smrj 	if (stat(new_path, &sb) == 0) {
82413446Smrj 		if (stat(new_path2, &sb) == 0) {
82423446Smrj 			/*
82433446Smrj 			 * We matched both, so we actually
82443446Smrj 			 * want to write the $ISADIR version.
82453446Smrj 			 */
82463446Smrj 			(void) snprintf(new_path, new_path_len,
82473446Smrj 			    "/platform/i86pc/kernel/%s/$ISADIR/unix",
82483446Smrj 			    partial_path);
82493446Smrj 		}
82506448Svikram 		BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
82513446Smrj 		return (new_path);
82523446Smrj 	}
82533446Smrj 
82543446Smrj 	free(new_path);
82556448Svikram 	BAM_DPRINTF((D_RETURN_FAILURE, fcn));
82563446Smrj 	return (NULL);
82573446Smrj }
82583446Smrj 
82593446Smrj /*
82603446Smrj  * The kernel cmd and arg have been changed, so
82613446Smrj  * check whether the archive line needs to change.
82623446Smrj  */
82633446Smrj static void
82643446Smrj set_archive_line(entry_t *entryp, line_t *kernelp)
82653446Smrj {
82666448Svikram 	line_t		*lp = entryp->start;
82676448Svikram 	char		*new_archive;
82686448Svikram 	menu_cmd_t	m_cmd;
82696448Svikram 	const char	*fcn = "set_archive_line()";
82703446Smrj 
82713446Smrj 	for (; lp != NULL; lp = lp->next) {
82723446Smrj 		if (strncmp(lp->cmd, menu_cmds[MODULE_CMD],
82733446Smrj 		    sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
82743446Smrj 			break;
82753446Smrj 		}
82766448Svikram 
82776448Svikram 		INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
82786448Svikram 		if (lp == entryp->end) {
82796448Svikram 			BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
82806448Svikram 			    entryp->entryNum));
82813446Smrj 			return;
82826448Svikram 		}
82836448Svikram 	}
82846448Svikram 	INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
82856448Svikram 	if (lp == NULL) {
82866448Svikram 		BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
82873446Smrj 		return;
82886448Svikram 	}
82893446Smrj 
82903446Smrj 	if (strstr(kernelp->arg, "$ISADIR") != NULL) {
82913446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE;
82923446Smrj 		m_cmd = MODULE_DOLLAR_CMD;
82933446Smrj 	} else if (strstr(kernelp->arg, "amd64") != NULL) {
82943446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_64;
82953446Smrj 		m_cmd = MODULE_CMD;
82963446Smrj 	} else {
82973446Smrj 		new_archive = DIRECT_BOOT_ARCHIVE_32;
82983446Smrj 		m_cmd = MODULE_CMD;
82993446Smrj 	}
83003446Smrj 
83016448Svikram 	if (strcmp(lp->arg, new_archive) == 0) {
83026448Svikram 		BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
83033446Smrj 		return;
83046448Svikram 	}
83053446Smrj 
83063446Smrj 	if (strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
83073446Smrj 		free(lp->cmd);
83083446Smrj 		lp->cmd = s_strdup(menu_cmds[m_cmd]);
83093446Smrj 	}
83103446Smrj 
83113446Smrj 	free(lp->arg);
83123446Smrj 	lp->arg = s_strdup(new_archive);
83133446Smrj 	update_line(lp);
83146448Svikram 	BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
83153446Smrj }
83163446Smrj 
83173446Smrj /*
83183446Smrj  * Title for an entry to set properties that once went in bootenv.rc.
83193446Smrj  */
83203446Smrj #define	BOOTENV_RC_TITLE	"Solaris bootenv rc"
83213446Smrj 
83223446Smrj /*
83233446Smrj  * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
83243446Smrj  * (optnum == ARGS_CMD) in the argument buf.  If path is a zero-length
83253446Smrj  * string, reset the value to the default.  If path is a non-zero-length
83263446Smrj  * string, set the kernel or arguments.
83273446Smrj  */
83283446Smrj static error_t
83296448Svikram get_set_kernel(
83306448Svikram 	menu_t *mp,
83316448Svikram 	menu_cmd_t optnum,
83326448Svikram 	char *path,
83336448Svikram 	char *buf,
83346448Svikram 	size_t bufsize)
83356448Svikram {
83366448Svikram 	int		entryNum;
83376448Svikram 	int		rv = BAM_SUCCESS;
83386448Svikram 	int		free_new_path = 0;
83396448Svikram 	entry_t		*entryp;
83406448Svikram 	line_t		*ptr;
83416448Svikram 	line_t		*kernelp;
83426448Svikram 	char		*new_arg;
83436448Svikram 	char		*old_args;
83446448Svikram 	char		*space;
83456448Svikram 	char		*new_path;
83466448Svikram 	char		old_space;
83476448Svikram 	size_t		old_kernel_len;
83486448Svikram 	size_t		new_str_len;
83496448Svikram 	char		*fstype;
83506448Svikram 	char		*osdev;
83516448Svikram 	char		*sign;
83526448Svikram 	char		signbuf[PATH_MAX];
83536448Svikram 	int		ret;
83546448Svikram 	const char	*fcn = "get_set_kernel()";
83553446Smrj 
83563446Smrj 	assert(bufsize > 0);
83573446Smrj 
83583446Smrj 	ptr = kernelp = NULL;
83593446Smrj 	new_arg = old_args = space = NULL;
83606448Svikram 	new_path = NULL;
83613446Smrj 	buf[0] = '\0';
83623446Smrj 
83636448Svikram 	INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
83646448Svikram 	    bam_direct = BAM_DIRECT_MULTIBOOT);
83653446Smrj 	if (bam_direct != BAM_DIRECT_DBOOT) {
83663446Smrj 		bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
83673446Smrj 		return (BAM_ERROR);
83683446Smrj 	}
83693446Smrj 
83703446Smrj 	/*
83713446Smrj 	 * If a user changed the default entry to a non-bootadm controlled
83723446Smrj 	 * one, we don't want to mess with it.  Just print an error and
83733446Smrj 	 * return.
83743446Smrj 	 */
83753446Smrj 	if (mp->curdefault) {
83763446Smrj 		entryNum = s_strtol(mp->curdefault->arg);
83773446Smrj 		for (entryp = mp->entries; entryp; entryp = entryp->next) {
83783446Smrj 			if (entryp->entryNum == entryNum)
83793446Smrj 				break;
83803446Smrj 		}
83813446Smrj 		if ((entryp != NULL) &&
83823446Smrj 		    ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
83833446Smrj 			bam_error(DEFAULT_NOT_BAM);
83843446Smrj 			return (BAM_ERROR);
83853446Smrj 		}
83863446Smrj 	}
83873446Smrj 
83886448Svikram 	entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
83896448Svikram 	    0, &entryNum);
83903446Smrj 
83913446Smrj 	if (entryp != NULL) {
83923446Smrj 		for (ptr = entryp->start; ptr && ptr != entryp->end;
83933446Smrj 		    ptr = ptr->next) {
83943446Smrj 			if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
83953446Smrj 			    sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
83963446Smrj 				kernelp = ptr;
83973446Smrj 				break;
83983446Smrj 			}
83993446Smrj 		}
84003446Smrj 		if (kernelp == NULL) {
84013446Smrj 			bam_error(NO_KERNEL, entryNum);
84023446Smrj 			return (BAM_ERROR);
84033446Smrj 		}
84043446Smrj 
84053446Smrj 		old_kernel_len = strcspn(kernelp->arg, " \t");
84063446Smrj 		space = old_args = kernelp->arg + old_kernel_len;
84073446Smrj 		while ((*old_args == ' ') || (*old_args == '\t'))
84083446Smrj 			old_args++;
84093446Smrj 	}
84103446Smrj 
84113446Smrj 	if (path == NULL) {
84126448Svikram 		if (entryp == NULL) {
84136448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
84146448Svikram 			BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
84153446Smrj 			return (BAM_SUCCESS);
84166448Svikram 		}
84176448Svikram 		assert(kernelp);
84183446Smrj 		if (optnum == ARGS_CMD) {
84196448Svikram 			if (old_args[0] != '\0') {
84203446Smrj 				(void) strlcpy(buf, old_args, bufsize);
84216448Svikram 				BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
84226448Svikram 			}
84233446Smrj 		} else {
84243446Smrj 			/*
84253446Smrj 			 * We need to print the kernel, so we just turn the
84263446Smrj 			 * first space into a '\0' and print the beginning.
84273446Smrj 			 * We don't print anything if it's the default kernel.
84283446Smrj 			 */
84293446Smrj 			old_space = *space;
84303446Smrj 			*space = '\0';
84316448Svikram 			if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
84323446Smrj 				(void) strlcpy(buf, kernelp->arg, bufsize);
84336448Svikram 				BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
84346448Svikram 			}
84353446Smrj 			*space = old_space;
84363446Smrj 		}
84376448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
84383446Smrj 		return (BAM_SUCCESS);
84393446Smrj 	}
84403446Smrj 
84413446Smrj 	/*
84423446Smrj 	 * First, check if we're resetting an entry to the default.
84433446Smrj 	 */
84443446Smrj 	if ((path[0] == '\0') ||
84453446Smrj 	    ((optnum == KERNEL_CMD) &&
84463446Smrj 	    (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
84473446Smrj 		if ((entryp == NULL) || (kernelp == NULL)) {
84483446Smrj 			/* No previous entry, it's already the default */
84496448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
84503446Smrj 			return (BAM_SUCCESS);
84513446Smrj 		}
84523446Smrj 
84533446Smrj 		/*
84543446Smrj 		 * Check if we can delete the entry.  If we're resetting the
84553446Smrj 		 * kernel command, and the args is already empty, or if we're
84563446Smrj 		 * resetting the args command, and the kernel is already the
84573446Smrj 		 * default, we can restore the old default and delete the entry.
84583446Smrj 		 */
84593446Smrj 		if (((optnum == KERNEL_CMD) &&
84603446Smrj 		    ((old_args == NULL) || (old_args[0] == '\0'))) ||
84613446Smrj 		    ((optnum == ARGS_CMD) &&
84623446Smrj 		    (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
84633446Smrj 		    sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
84643446Smrj 			kernelp = NULL;
84653446Smrj 			(void) do_delete(mp, entryNum);
84663446Smrj 			restore_default_entry(mp, BAM_OLD_RC_DEF,
84673446Smrj 			    mp->old_rc_default);
84683446Smrj 			mp->old_rc_default = NULL;
84693446Smrj 			rv = BAM_WRITE;
84706448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
84713446Smrj 			goto done;
84723446Smrj 		}
84733446Smrj 
84743446Smrj 		if (optnum == KERNEL_CMD) {
84753446Smrj 			/*
84763446Smrj 			 * At this point, we've already checked that old_args
84773446Smrj 			 * and entryp are valid pointers.  The "+ 2" is for
84783446Smrj 			 * a space a the string termination character.
84793446Smrj 			 */
84803446Smrj 			new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
84813446Smrj 			    strlen(old_args) + 2;
84823446Smrj 			new_arg = s_calloc(1, new_str_len);
84833446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
84843446Smrj 			    DIRECT_BOOT_KERNEL, old_args);
84853446Smrj 			free(kernelp->arg);
84863446Smrj 			kernelp->arg = new_arg;
84873446Smrj 
84883446Smrj 			/*
84893446Smrj 			 * We have changed the kernel line, so we may need
84903446Smrj 			 * to update the archive line as well.
84913446Smrj 			 */
84923446Smrj 			set_archive_line(entryp, kernelp);
84936448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
84946448Svikram 			    fcn, kernelp->arg));
84953446Smrj 		} else {
84963446Smrj 			/*
84973446Smrj 			 * We're resetting the boot args to nothing, so
84983446Smrj 			 * we only need to copy the kernel.  We've already
84993446Smrj 			 * checked that the kernel is not the default.
85003446Smrj 			 */
85013446Smrj 			new_arg = s_calloc(1, old_kernel_len + 1);
85023446Smrj 			(void) snprintf(new_arg, old_kernel_len + 1, "%s",
85033446Smrj 			    kernelp->arg);
85043446Smrj 			free(kernelp->arg);
85053446Smrj 			kernelp->arg = new_arg;
85066448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
85076448Svikram 			    fcn, kernelp->arg));
85083446Smrj 		}
85093446Smrj 		rv = BAM_WRITE;
85103446Smrj 		goto done;
85113446Smrj 	}
85123446Smrj 
85133446Smrj 	/*
85143446Smrj 	 * Expand the kernel file to a full path, if necessary
85153446Smrj 	 */
85163446Smrj 	if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
85173446Smrj 		new_path = expand_path(path);
85183446Smrj 		if (new_path == NULL) {
85194346Srscott 			bam_error(UNKNOWN_KERNEL, path);
85206448Svikram 			BAM_DPRINTF((D_RETURN_FAILURE, fcn));
85213446Smrj 			return (BAM_ERROR);
85223446Smrj 		}
85233446Smrj 		free_new_path = 1;
85243446Smrj 	} else {
85253446Smrj 		new_path = path;
85263446Smrj 		free_new_path = 0;
85273446Smrj 	}
85283446Smrj 
85293446Smrj 	/*
85303446Smrj 	 * At this point, we know we're setting a new value.  First, take care
85313446Smrj 	 * of the case where there was no previous entry.
85323446Smrj 	 */
85333446Smrj 	if (entryp == NULL) {
85346448Svikram 
85353446Smrj 		/* Similar to code in update_temp */
85366448Svikram 		fstype = get_fstype("/");
85376448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
85386448Svikram 		if (fstype == NULL) {
85396448Svikram 			bam_error(BOOTENV_FSTYPE_FAILED);
85406448Svikram 			rv = BAM_ERROR;
85416448Svikram 			goto done;
85426448Svikram 		}
85436448Svikram 
85446448Svikram 		osdev = get_special("/");
85456448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
85466448Svikram 		if (osdev == NULL) {
85476448Svikram 			free(fstype);
85486448Svikram 			bam_error(BOOTENV_SPECIAL_FAILED);
85493446Smrj 			rv = BAM_ERROR;
85503446Smrj 			goto done;
85513446Smrj 		}
85526448Svikram 
85536448Svikram 		sign = find_existing_sign("/", osdev, fstype);
85546448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
85556448Svikram 		if (sign == NULL) {
85566448Svikram 			free(fstype);
85576448Svikram 			free(osdev);
85586448Svikram 			bam_error(BOOTENV_SIGN_FAILED);
85596448Svikram 			rv = BAM_ERROR;
85606448Svikram 			goto done;
85616448Svikram 		}
85626448Svikram 
85636448Svikram 		free(fstype);
85646448Svikram 		free(osdev);
85656448Svikram 		(void) strlcpy(signbuf, sign, sizeof (signbuf));
85666448Svikram 		free(sign);
85676448Svikram 		assert(strchr(signbuf, '(') == NULL &&
85686448Svikram 		    strchr(signbuf, ',') == NULL &&
85696448Svikram 		    strchr(signbuf, ')') == NULL);
85706448Svikram 
85713446Smrj 		if (optnum == KERNEL_CMD) {
85726448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, new_path));
85733446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
85746448Svikram 			    signbuf, new_path, NULL, NULL);
85753446Smrj 		} else {
85763446Smrj 			new_str_len = strlen(DIRECT_BOOT_KERNEL) +
85773446Smrj 			    strlen(path) + 8;
85783446Smrj 			new_arg = s_calloc(1, new_str_len);
85793446Smrj 
85803446Smrj 			(void) snprintf(new_arg, new_str_len, "%s %s",
85813446Smrj 			    DIRECT_BOOT_KERNEL, path);
85826448Svikram 			BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
85833446Smrj 			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
85846448Svikram 			    signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE);
85856448Svikram 			free(new_arg);
85866448Svikram 		}
85876448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
85886448Svikram 		    entryNum = BAM_ERROR);
85896448Svikram 		if (entryNum == BAM_ERROR) {
85906448Svikram 			bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
85916448Svikram 			    BOOTENV_RC_TITLE);
85926448Svikram 			rv = BAM_ERROR;
85936448Svikram 			goto done;
85943446Smrj 		}
85953446Smrj 		save_default_entry(mp, BAM_OLD_RC_DEF);
85966448Svikram 		ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
85976448Svikram 		INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
85986448Svikram 		if (ret == BAM_ERROR) {
85996448Svikram 			bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
86006448Svikram 		}
86013446Smrj 		rv = BAM_WRITE;
86023446Smrj 		goto done;
86033446Smrj 	}
86043446Smrj 
86053446Smrj 	/*
86063446Smrj 	 * There was already an bootenv entry which we need to edit.
86073446Smrj 	 */
86083446Smrj 	if (optnum == KERNEL_CMD) {
86093446Smrj 		new_str_len = strlen(new_path) + strlen(old_args) + 2;
86103446Smrj 		new_arg = s_calloc(1, new_str_len);
86113446Smrj 		(void) snprintf(new_arg, new_str_len, "%s %s", new_path,
86123446Smrj 		    old_args);
86133446Smrj 		free(kernelp->arg);
86143446Smrj 		kernelp->arg = new_arg;
86153446Smrj 
86163446Smrj 		/*
86173446Smrj 		 * If we have changed the kernel line, we may need to update
86183446Smrj 		 * the archive line as well.
86193446Smrj 		 */
86203446Smrj 		set_archive_line(entryp, kernelp);
86216448Svikram 		BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
86226448Svikram 		    kernelp->arg));
86233446Smrj 	} else {
86243446Smrj 		new_str_len = old_kernel_len + strlen(path) + 8;
86253446Smrj 		new_arg = s_calloc(1, new_str_len);
86263446Smrj 		(void) strncpy(new_arg, kernelp->arg, old_kernel_len);
86273446Smrj 		(void) strlcat(new_arg, " ", new_str_len);
86283446Smrj 		(void) strlcat(new_arg, path, new_str_len);
86293446Smrj 		free(kernelp->arg);
86303446Smrj 		kernelp->arg = new_arg;
86316448Svikram 		BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
86326448Svikram 		    kernelp->arg));
86333446Smrj 	}
86343446Smrj 	rv = BAM_WRITE;
86353446Smrj 
86363446Smrj done:
86373446Smrj 	if ((rv == BAM_WRITE) && kernelp)
86383446Smrj 		update_line(kernelp);
86393446Smrj 	if (free_new_path)
86403446Smrj 		free(new_path);
86416448Svikram 	if (rv == BAM_WRITE) {
86426448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
86436448Svikram 	} else {
86446448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
86456448Svikram 	}
86463446Smrj 	return (rv);
86473446Smrj }
86483446Smrj 
86496448Svikram static error_t
86506448Svikram get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
86516448Svikram {
86526448Svikram 	const char	*fcn = "get_kernel()";
86536448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
86546448Svikram 	return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
86556448Svikram }
86566448Svikram 
86576448Svikram static error_t
86586448Svikram set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
86596448Svikram {
86606448Svikram 	const char	*fcn = "set_kernel()";
86616448Svikram 	assert(path != NULL);
86626448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
86636448Svikram 	return (get_set_kernel(mp, optnum, path, buf, bufsize));
86646448Svikram }
86656448Svikram 
86660Sstevel@tonic-gate /*ARGSUSED*/
86670Sstevel@tonic-gate static error_t
86686448Svikram set_option(menu_t *mp, char *dummy, char *opt)
86696448Svikram {
86706448Svikram 	int		optnum;
86716448Svikram 	int		optval;
86726448Svikram 	char		*val;
86736448Svikram 	char		buf[BUFSIZ] = "";
86746448Svikram 	error_t		rv;
86756448Svikram 	const char	*fcn = "set_option()";
86760Sstevel@tonic-gate 
86770Sstevel@tonic-gate 	assert(mp);
86780Sstevel@tonic-gate 	assert(opt);
86796448Svikram 	assert(dummy == NULL);
86806448Svikram 
86816448Svikram 	/* opt is set from bam_argv[0] and is always non-NULL */
86826448Svikram 	BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
86830Sstevel@tonic-gate 
86840Sstevel@tonic-gate 	val = strchr(opt, '=');
86853446Smrj 	if (val != NULL) {
86863446Smrj 		*val = '\0';
86870Sstevel@tonic-gate 	}
86880Sstevel@tonic-gate 
86890Sstevel@tonic-gate 	if (strcmp(opt, "default") == 0) {
86900Sstevel@tonic-gate 		optnum = DEFAULT_CMD;
86910Sstevel@tonic-gate 	} else if (strcmp(opt, "timeout") == 0) {
86920Sstevel@tonic-gate 		optnum = TIMEOUT_CMD;
86933446Smrj 	} else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
86943446Smrj 		optnum = KERNEL_CMD;
86953446Smrj 	} else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
86963446Smrj 		optnum = ARGS_CMD;
86970Sstevel@tonic-gate 	} else {
86986448Svikram 		bam_error(INVALID_OPTION, opt);
86990Sstevel@tonic-gate 		return (BAM_ERROR);
87000Sstevel@tonic-gate 	}
87010Sstevel@tonic-gate 
87023446Smrj 	/*
87033446Smrj 	 * kernel and args are allowed without "=new_value" strings.  All
87043446Smrj 	 * others cause errors
87053446Smrj 	 */
87063446Smrj 	if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
87076448Svikram 		bam_error(NO_OPTION_ARG, opt);
87083446Smrj 		return (BAM_ERROR);
87093446Smrj 	} else if (val != NULL) {
87103446Smrj 		*val = '=';
87113446Smrj 	}
87123446Smrj 
87133446Smrj 	if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
87146448Svikram 		BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
87156448Svikram 		    val ? val + 1 : "NULL"));
87166448Svikram 
87176448Svikram 		if (val)
87186448Svikram 			rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
87196448Svikram 		else
87206448Svikram 			rv = get_kernel(mp, optnum, buf, sizeof (buf));
87213446Smrj 		if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
87223446Smrj 			(void) printf("%s\n", buf);
87233446Smrj 	} else {
87243446Smrj 		optval = s_strtol(val + 1);
87256448Svikram 		BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
87266448Svikram 		rv = set_global(mp, menu_cmds[optnum], optval);
87276448Svikram 	}
87286448Svikram 
87296448Svikram 	if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
87306448Svikram 		BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
87316448Svikram 	} else {
87326448Svikram 		BAM_DPRINTF((D_RETURN_FAILURE, fcn));
87336448Svikram 	}
87346448Svikram 
87356448Svikram 	return (rv);
87360Sstevel@tonic-gate }
87370Sstevel@tonic-gate 
87380Sstevel@tonic-gate /*
87390Sstevel@tonic-gate  * The quiet argument suppresses messages. This is used
87400Sstevel@tonic-gate  * when invoked in the context of other commands (e.g. list_entry)
87410Sstevel@tonic-gate  */
87420Sstevel@tonic-gate static error_t
87430Sstevel@tonic-gate read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
87440Sstevel@tonic-gate {
87450Sstevel@tonic-gate 	line_t *lp;
87460Sstevel@tonic-gate 	char *arg;
87470Sstevel@tonic-gate 	int done, ret = BAM_SUCCESS;
87480Sstevel@tonic-gate 
87490Sstevel@tonic-gate 	assert(mp);
87500Sstevel@tonic-gate 	assert(menu_path);
87510Sstevel@tonic-gate 	assert(globalcmd);
87520Sstevel@tonic-gate 
87530Sstevel@tonic-gate 	if (mp->start == NULL) {
87540Sstevel@tonic-gate 		if (!quiet)
87550Sstevel@tonic-gate 			bam_error(NO_MENU, menu_path);
87560Sstevel@tonic-gate 		return (BAM_ERROR);
87570Sstevel@tonic-gate 	}
87580Sstevel@tonic-gate 
87590Sstevel@tonic-gate 	done = 0;
87600Sstevel@tonic-gate 	for (lp = mp->start; lp; lp = lp->next) {
87610Sstevel@tonic-gate 		if (lp->flags != BAM_GLOBAL)
87620Sstevel@tonic-gate 			continue;
87630Sstevel@tonic-gate 
87640Sstevel@tonic-gate 		if (lp->cmd == NULL) {
87650Sstevel@tonic-gate 			if (!quiet)
87660Sstevel@tonic-gate 				bam_error(NO_CMD, lp->lineNum);
87670Sstevel@tonic-gate 			continue;
87680Sstevel@tonic-gate 		}
87690Sstevel@tonic-gate 
87700Sstevel@tonic-gate 		if (strcmp(globalcmd, lp->cmd) != 0)
87710Sstevel@tonic-gate 			continue;
87720Sstevel@tonic-gate 
87730Sstevel@tonic-gate 		/* Found global. Check for duplicates */
87740Sstevel@tonic-gate 		if (done && !quiet) {
87750Sstevel@tonic-gate 			bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
87760Sstevel@tonic-gate 			ret = BAM_ERROR;
87770Sstevel@tonic-gate 		}
87780Sstevel@tonic-gate 
87790Sstevel@tonic-gate 		arg = lp->arg ? lp->arg : "";
87800Sstevel@tonic-gate 		bam_print(GLOBAL_CMD, globalcmd, arg);
87810Sstevel@tonic-gate 		done = 1;
87820Sstevel@tonic-gate 	}
87830Sstevel@tonic-gate 
87840Sstevel@tonic-gate 	if (!done && bam_verbose)
87850Sstevel@tonic-gate 		bam_print(NO_ENTRY, globalcmd);
87860Sstevel@tonic-gate 
87870Sstevel@tonic-gate 	return (ret);
87880Sstevel@tonic-gate }
87890Sstevel@tonic-gate 
87900Sstevel@tonic-gate static error_t
87910Sstevel@tonic-gate menu_write(char *root, menu_t *mp)
87920Sstevel@tonic-gate {
87936448Svikram 	const char *fcn = "menu_write()";
87946448Svikram 
87956448Svikram 	BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
87960Sstevel@tonic-gate 	return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
87970Sstevel@tonic-gate }
87980Sstevel@tonic-gate 
87996448Svikram void
88000Sstevel@tonic-gate line_free(line_t *lp)
88010Sstevel@tonic-gate {
88020Sstevel@tonic-gate 	if (lp == NULL)
88030Sstevel@tonic-gate 		return;
88040Sstevel@tonic-gate 
88050Sstevel@tonic-gate 	if (lp->cmd)
88060Sstevel@tonic-gate 		free(lp->cmd);
88070Sstevel@tonic-gate 	if (lp->sep)
88080Sstevel@tonic-gate 		free(lp->sep);
88090Sstevel@tonic-gate 	if (lp->arg)
88100Sstevel@tonic-gate 		free(lp->arg);
88110Sstevel@tonic-gate 	if (lp->line)
88120Sstevel@tonic-gate 		free(lp->line);
88130Sstevel@tonic-gate 	free(lp);
88140Sstevel@tonic-gate }
88150Sstevel@tonic-gate 
88160Sstevel@tonic-gate static void
88170Sstevel@tonic-gate linelist_free(line_t *start)
88180Sstevel@tonic-gate {
88190Sstevel@tonic-gate 	line_t *lp;
88200Sstevel@tonic-gate 
88210Sstevel@tonic-gate 	while (start) {
88220Sstevel@tonic-gate 		lp = start;
88230Sstevel@tonic-gate 		start = start->next;
88240Sstevel@tonic-gate 		line_free(lp);
88250Sstevel@tonic-gate 	}
88260Sstevel@tonic-gate }
88270Sstevel@tonic-gate 
88280Sstevel@tonic-gate static void
88290Sstevel@tonic-gate filelist_free(filelist_t *flistp)
88300Sstevel@tonic-gate {
88310Sstevel@tonic-gate 	linelist_free(flistp->head);
88320Sstevel@tonic-gate 	flistp->head = NULL;
88330Sstevel@tonic-gate 	flistp->tail = NULL;
88340Sstevel@tonic-gate }
88350Sstevel@tonic-gate 
88360Sstevel@tonic-gate static void
88370Sstevel@tonic-gate menu_free(menu_t *mp)
88380Sstevel@tonic-gate {
8839662Sszhou 	entry_t *ent, *tmp;
88400Sstevel@tonic-gate 	assert(mp);
88410Sstevel@tonic-gate 
88420Sstevel@tonic-gate 	if (mp->start)
88430Sstevel@tonic-gate 		linelist_free(mp->start);
8844662Sszhou 	ent = mp->entries;
8845662Sszhou 	while (ent) {
8846662Sszhou 		tmp = ent;
8847662Sszhou 		ent = tmp->next;
8848662Sszhou 		free(tmp);
8849662Sszhou 	}
8850662Sszhou 
88510Sstevel@tonic-gate 	free(mp);
88520Sstevel@tonic-gate }
88530Sstevel@tonic-gate 
88540Sstevel@tonic-gate /*
88550Sstevel@tonic-gate  * Utility routines
88560Sstevel@tonic-gate  */
88570Sstevel@tonic-gate 
88580Sstevel@tonic-gate 
88590Sstevel@tonic-gate /*
88600Sstevel@tonic-gate  * Returns 0 on success
88610Sstevel@tonic-gate  * Any other value indicates an error
88620Sstevel@tonic-gate  */
88630Sstevel@tonic-gate static int
88645648Ssetje exec_cmd(char *cmdline, filelist_t *flistp)
88650Sstevel@tonic-gate {
88660Sstevel@tonic-gate 	char buf[BUFSIZ];
88670Sstevel@tonic-gate 	int ret;
88680Sstevel@tonic-gate 	FILE *ptr;
88690Sstevel@tonic-gate 	sigset_t set;
88700Sstevel@tonic-gate 	void (*disp)(int);
88710Sstevel@tonic-gate 
88720Sstevel@tonic-gate 	/*
88730Sstevel@tonic-gate 	 * For security
88740Sstevel@tonic-gate 	 * - only absolute paths are allowed
88750Sstevel@tonic-gate 	 * - set IFS to space and tab
88760Sstevel@tonic-gate 	 */
88770Sstevel@tonic-gate 	if (*cmdline != '/') {
88780Sstevel@tonic-gate 		bam_error(ABS_PATH_REQ, cmdline);
88790Sstevel@tonic-gate 		return (-1);
88800Sstevel@tonic-gate 	}
88810Sstevel@tonic-gate 	(void) putenv("IFS= \t");
88820Sstevel@tonic-gate 
88830Sstevel@tonic-gate 	/*
88840Sstevel@tonic-gate 	 * We may have been exec'ed with SIGCHLD blocked
88850Sstevel@tonic-gate 	 * unblock it here
88860Sstevel@tonic-gate 	 */
88870Sstevel@tonic-gate 	(void) sigemptyset(&set);
88880Sstevel@tonic-gate 	(void) sigaddset(&set, SIGCHLD);
88890Sstevel@tonic-gate 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
88900Sstevel@tonic-gate 		bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
88910Sstevel@tonic-gate 		return (-1);
88920Sstevel@tonic-gate 	}
88930Sstevel@tonic-gate 
88940Sstevel@tonic-gate 	/*
88950Sstevel@tonic-gate 	 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
88960Sstevel@tonic-gate 	 */
88970Sstevel@tonic-gate 	disp = sigset(SIGCHLD, SIG_DFL);
88980Sstevel@tonic-gate 	if (disp == SIG_ERR) {
88990Sstevel@tonic-gate 		bam_error(FAILED_SIG, strerror(errno));
89000Sstevel@tonic-gate 		return (-1);
89010Sstevel@tonic-gate 	}
89020Sstevel@tonic-gate 	if (disp == SIG_HOLD) {
89030Sstevel@tonic-gate 		bam_error(BLOCKED_SIG, cmdline);
89040Sstevel@tonic-gate 		return (-1);
89050Sstevel@tonic-gate 	}
89060Sstevel@tonic-gate 
89070Sstevel@tonic-gate 	ptr = popen(cmdline, "r");
89080Sstevel@tonic-gate 	if (ptr == NULL) {
89090Sstevel@tonic-gate 		bam_error(POPEN_FAIL, cmdline, strerror(errno));
89100Sstevel@tonic-gate 		return (-1);
89110Sstevel@tonic-gate 	}
89120Sstevel@tonic-gate 
89130Sstevel@tonic-gate 	/*
89140Sstevel@tonic-gate 	 * If we simply do a pclose() following a popen(), pclose()
89150Sstevel@tonic-gate 	 * will close the reader end of the pipe immediately even
89160Sstevel@tonic-gate 	 * if the child process has not started/exited. pclose()
89170Sstevel@tonic-gate 	 * does wait for cmd to terminate before returning though.
89180Sstevel@tonic-gate 	 * When the executed command writes its output to the pipe
89190Sstevel@tonic-gate 	 * there is no reader process and the command dies with
89200Sstevel@tonic-gate 	 * SIGPIPE. To avoid this we read repeatedly until read
89210Sstevel@tonic-gate 	 * terminates with EOF. This indicates that the command
89220Sstevel@tonic-gate 	 * (writer) has closed the pipe and we can safely do a
89230Sstevel@tonic-gate 	 * pclose().
89240Sstevel@tonic-gate 	 *
89250Sstevel@tonic-gate 	 * Since pclose() does wait for the command to exit,
89260Sstevel@tonic-gate 	 * we can safely reap the exit status of the command
89270Sstevel@tonic-gate 	 * from the value returned by pclose()
89280Sstevel@tonic-gate 	 */
89295648Ssetje 	while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
89305648Ssetje 		if (flistp == NULL) {
89315648Ssetje 			/* s_fgets strips newlines, so insert them at the end */
89325648Ssetje 			bam_print(PRINT, buf);
89335648Ssetje 		} else {
89345648Ssetje 			append_to_flist(flistp, buf);
89350Sstevel@tonic-gate 		}
89360Sstevel@tonic-gate 	}
89370Sstevel@tonic-gate 
89380Sstevel@tonic-gate 	ret = pclose(ptr);
89390Sstevel@tonic-gate 	if (ret == -1) {
89400Sstevel@tonic-gate 		bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
89410Sstevel@tonic-gate 		return (-1);
89420Sstevel@tonic-gate 	}
89430Sstevel@tonic-gate 
89440Sstevel@tonic-gate 	if (WIFEXITED(ret)) {
89450Sstevel@tonic-gate 		return (WEXITSTATUS(ret));
89460Sstevel@tonic-gate 	} else {
89470Sstevel@tonic-gate 		bam_error(EXEC_FAIL, cmdline, ret);
89480Sstevel@tonic-gate 		return (-1);
89490Sstevel@tonic-gate 	}
89500Sstevel@tonic-gate }
89510Sstevel@tonic-gate 
89520Sstevel@tonic-gate /*
89530Sstevel@tonic-gate  * Since this function returns -1 on error
89540Sstevel@tonic-gate  * it cannot be used to convert -1. However,
89550Sstevel@tonic-gate  * that is sufficient for what we need.
89560Sstevel@tonic-gate  */
89570Sstevel@tonic-gate static long
89580Sstevel@tonic-gate s_strtol(char *str)
89590Sstevel@tonic-gate {
89600Sstevel@tonic-gate 	long l;
89610Sstevel@tonic-gate 	char *res = NULL;
89620Sstevel@tonic-gate 
89630Sstevel@tonic-gate 	if (str == NULL) {
89640Sstevel@tonic-gate 		return (-1);
89650Sstevel@tonic-gate 	}
89660Sstevel@tonic-gate 
89670Sstevel@tonic-gate 	errno = 0;
89680Sstevel@tonic-gate 	l = strtol(str, &res, 10);
89690Sstevel@tonic-gate 	if (errno || *res != '\0') {
89700Sstevel@tonic-gate 		return (-1);
89710Sstevel@tonic-gate 	}
89720Sstevel@tonic-gate 
89730Sstevel@tonic-gate 	return (l);
89740Sstevel@tonic-gate }
89750Sstevel@tonic-gate 
89760Sstevel@tonic-gate /*
89770Sstevel@tonic-gate  * Wrapper around fputs, that adds a newline (since fputs doesn't)
89780Sstevel@tonic-gate  */
89790Sstevel@tonic-gate static int
89800Sstevel@tonic-gate s_fputs(char *str, FILE *fp)
89810Sstevel@tonic-gate {
89820Sstevel@tonic-gate 	char linebuf[BAM_MAXLINE];
89830Sstevel@tonic-gate 
89840Sstevel@tonic-gate 	(void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
89850Sstevel@tonic-gate 	return (fputs(linebuf, fp));
89860Sstevel@tonic-gate }
89870Sstevel@tonic-gate 
89880Sstevel@tonic-gate /*
89890Sstevel@tonic-gate  * Wrapper around fgets, that strips newlines returned by fgets
89900Sstevel@tonic-gate  */
89913446Smrj char *
89920Sstevel@tonic-gate s_fgets(char *buf, int buflen, FILE *fp)
89930Sstevel@tonic-gate {
89940Sstevel@tonic-gate 	int n;
89950Sstevel@tonic-gate 
89960Sstevel@tonic-gate 	buf = fgets(buf, buflen, fp);
89970Sstevel@tonic-gate 	if (buf) {
89980Sstevel@tonic-gate 		n = strlen(buf);
89990Sstevel@tonic-gate 		if (n == buflen - 1 && buf[n-1] != '\n')
90000Sstevel@tonic-gate 			bam_error(TOO_LONG, buflen - 1, buf);
90010Sstevel@tonic-gate 		buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
90020Sstevel@tonic-gate 	}
90030Sstevel@tonic-gate 
90040Sstevel@tonic-gate 	return (buf);
90050Sstevel@tonic-gate }
90060Sstevel@tonic-gate 
90073446Smrj void *
90080Sstevel@tonic-gate s_calloc(size_t nelem, size_t sz)
90090Sstevel@tonic-gate {
90100Sstevel@tonic-gate 	void *ptr;
90110Sstevel@tonic-gate 
90120Sstevel@tonic-gate 	ptr = calloc(nelem, sz);
90130Sstevel@tonic-gate 	if (ptr == NULL) {
90140Sstevel@tonic-gate 		bam_error(NO_MEM, nelem*sz);
90150Sstevel@tonic-gate 		bam_exit(1);
90160Sstevel@tonic-gate 	}
90170Sstevel@tonic-gate 	return (ptr);
90180Sstevel@tonic-gate }
90190Sstevel@tonic-gate 
90203446Smrj void *
90213446Smrj s_realloc(void *ptr, size_t sz)
90223446Smrj {
90233446Smrj 	ptr = realloc(ptr, sz);
90243446Smrj 	if (ptr == NULL) {
90253446Smrj 		bam_error(NO_MEM, sz);
90263446Smrj 		bam_exit(1);
90273446Smrj 	}
90283446Smrj 	return (ptr);
90293446Smrj }
90303446Smrj 
90316448Svikram char *
90320Sstevel@tonic-gate s_strdup(char *str)
90330Sstevel@tonic-gate {
90340Sstevel@tonic-gate 	char *ptr;
90350Sstevel@tonic-gate 
90360Sstevel@tonic-gate 	if (str == NULL)
90370Sstevel@tonic-gate 		return (NULL);
90380Sstevel@tonic-gate 
90390Sstevel@tonic-gate 	ptr = strdup(str);
90400Sstevel@tonic-gate 	if (ptr == NULL) {
90410Sstevel@tonic-gate 		bam_error(NO_MEM, strlen(str) + 1);
90420Sstevel@tonic-gate 		bam_exit(1);
90430Sstevel@tonic-gate 	}
90440Sstevel@tonic-gate 	return (ptr);
90450Sstevel@tonic-gate }
90460Sstevel@tonic-gate 
90470Sstevel@tonic-gate /*
90480Sstevel@tonic-gate  * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
90490Sstevel@tonic-gate  * Returns 0 otherwise
90500Sstevel@tonic-gate  */
90510Sstevel@tonic-gate static int
90520Sstevel@tonic-gate is_amd64(void)
90530Sstevel@tonic-gate {
90540Sstevel@tonic-gate 	static int amd64 = -1;
90550Sstevel@tonic-gate 	char isabuf[257];	/* from sysinfo(2) manpage */
90560Sstevel@tonic-gate 
90570Sstevel@tonic-gate 	if (amd64 != -1)
90580Sstevel@tonic-gate 		return (amd64);
90590Sstevel@tonic-gate 
90606319Sjg 	if (bam_alt_platform) {
90616319Sjg 		if (strcmp(bam_platform, "i86pc") == 0) {
90626319Sjg 			amd64 = 1;		/* diskless server */
90636319Sjg 		}
90646319Sjg 	} else {
90656319Sjg 		if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
90666319Sjg 		    strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
90676319Sjg 			amd64 = 1;
90686319Sjg 		} else if (strstr(isabuf, "i386") == NULL) {
90696319Sjg 			amd64 = 1;		/* diskless server */
90706319Sjg 		}
90716319Sjg 	}
90726319Sjg 	if (amd64 == -1)
90730Sstevel@tonic-gate 		amd64 = 0;
90740Sstevel@tonic-gate 
90750Sstevel@tonic-gate 	return (amd64);
90760Sstevel@tonic-gate }
90770Sstevel@tonic-gate 
90786582Ssetje static char *
90796582Ssetje get_machine(void)
90806582Ssetje {
90816582Ssetje 	static int cached = -1;
90826582Ssetje 	static char mbuf[257];	/* from sysinfo(2) manpage */
90836582Ssetje 
90846582Ssetje 	if (cached == 0)
90856582Ssetje 		return (mbuf);
90866319Sjg 
90876319Sjg 	if (bam_alt_platform) {
90886582Ssetje 		return (bam_platform);
90896319Sjg 	} else {
90906582Ssetje 		if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
90916582Ssetje 			cached = 1;
90926582Ssetje 		}
90936582Ssetje 	}
90946582Ssetje 	if (cached == -1) {
90956582Ssetje 		mbuf[0] = '\0';
90966582Ssetje 		cached = 0;
90976582Ssetje 	}
90986582Ssetje 
90996582Ssetje 	return (mbuf);
91005648Ssetje }
91015648Ssetje 
91026448Svikram int
91036448Svikram is_sparc(void)
91046448Svikram {
91056582Ssetje 	static int issparc = -1;
91066582Ssetje 	char mbuf[257];	/* from sysinfo(2) manpage */
91076582Ssetje 
91086582Ssetje 	if (issparc != -1)
91096582Ssetje 		return (issparc);
91106582Ssetje 
91116582Ssetje 	if (bam_alt_platform) {
91126582Ssetje 		if (strncmp(bam_platform, "sun4", 4) == 0) {
91136582Ssetje 			issparc = 1;
91146582Ssetje 		}
91156582Ssetje 	} else {
91166582Ssetje 		if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
91177111Sjg 		    strcmp(mbuf, "sparc") == 0) {
91186582Ssetje 			issparc = 1;
91197111Sjg 		}
91207111Sjg 	}
91217111Sjg 	if (issparc == -1)
91227111Sjg 		issparc = 0;
91236582Ssetje 
91246582Ssetje 	return (issparc);
91256448Svikram }
91265648Ssetje 
91270Sstevel@tonic-gate static void
91280Sstevel@tonic-gate append_to_flist(filelist_t *flistp, char *s)
91290Sstevel@tonic-gate {
91300Sstevel@tonic-gate 	line_t *lp;
91310Sstevel@tonic-gate 
91320Sstevel@tonic-gate 	lp = s_calloc(1, sizeof (line_t));
91330Sstevel@tonic-gate 	lp->line = s_strdup(s);
91340Sstevel@tonic-gate 	if (flistp->head == NULL)
91350Sstevel@tonic-gate 		flistp->head = lp;
91360Sstevel@tonic-gate 	else
91370Sstevel@tonic-gate 		flistp->tail->next = lp;
91380Sstevel@tonic-gate 	flistp->tail = lp;
91390Sstevel@tonic-gate }
91404581Ssherrym 
91415648Ssetje #if !defined(_OPB)
91424581Ssherrym 
91434581Ssherrym UCODE_VENDORS;
91444581Ssherrym 
91454581Ssherrym /*ARGSUSED*/
91464581Ssherrym static void
91474581Ssherrym ucode_install(char *root)
91484581Ssherrym {
91494581Ssherrym 	int i;
91504581Ssherrym 
91514581Ssherrym 	for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
91524581Ssherrym 		int cmd_len = PATH_MAX + 256;
91534581Ssherrym 		char cmd[PATH_MAX + 256];
91544581Ssherrym 		char file[PATH_MAX];
91554581Ssherrym 		char timestamp[PATH_MAX];
91564581Ssherrym 		struct stat fstatus, tstatus;
91574581Ssherrym 		struct utimbuf u_times;
91584581Ssherrym 
91597605SMark.Johnson@Sun.COM 		(void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
91607605SMark.Johnson@Sun.COM 		    bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
91617605SMark.Johnson@Sun.COM 		    ucode_vendors[i].extstr);
91624581Ssherrym 
91634581Ssherrym 		if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
91644581Ssherrym 			continue;
91654581Ssherrym 
91664581Ssherrym 		(void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
91674581Ssherrym 
91684581Ssherrym 		if (stat(timestamp, &tstatus) == 0 &&
91694581Ssherrym 		    fstatus.st_mtime <= tstatus.st_mtime)
91704581Ssherrym 			continue;
91714581Ssherrym 
91724581Ssherrym 		(void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
91734581Ssherrym 		    "%s/%s/%s %s > /dev/null 2>&1", bam_root,
91744581Ssherrym 		    UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
91754581Ssherrym 		if (system(cmd) != 0)
91764581Ssherrym 			return;
91774581Ssherrym 
91784581Ssherrym 		if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
91794581Ssherrym 			return;
91804581Ssherrym 
91814581Ssherrym 		u_times.actime = fstatus.st_atime;
91824581Ssherrym 		u_times.modtime = fstatus.st_mtime;
91834581Ssherrym 		(void) utime(timestamp, &u_times);
91844581Ssherrym 	}
91854581Ssherrym }
91864581Ssherrym #endif
9187