19160SSherry.Moore@Sun.COM /*
29160SSherry.Moore@Sun.COM  * CDDL HEADER START
39160SSherry.Moore@Sun.COM  *
49160SSherry.Moore@Sun.COM  * The contents of this file are subject to the terms of the
59160SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
69160SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
79160SSherry.Moore@Sun.COM  *
89160SSherry.Moore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99160SSherry.Moore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109160SSherry.Moore@Sun.COM  * See the License for the specific language governing permissions
119160SSherry.Moore@Sun.COM  * and limitations under the License.
129160SSherry.Moore@Sun.COM  *
139160SSherry.Moore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149160SSherry.Moore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159160SSherry.Moore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169160SSherry.Moore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179160SSherry.Moore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189160SSherry.Moore@Sun.COM  *
199160SSherry.Moore@Sun.COM  * CDDL HEADER END
209160SSherry.Moore@Sun.COM  */
219160SSherry.Moore@Sun.COM /*
229160SSherry.Moore@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
239160SSherry.Moore@Sun.COM  * Use is subject to license terms.
249160SSherry.Moore@Sun.COM  */
259160SSherry.Moore@Sun.COM 
269160SSherry.Moore@Sun.COM /*
279160SSherry.Moore@Sun.COM  * This file contains all the functions that manipualte the file
289160SSherry.Moore@Sun.COM  * system where the GRUB menu resides.
299160SSherry.Moore@Sun.COM  */
309160SSherry.Moore@Sun.COM #include <stdio.h>
319160SSherry.Moore@Sun.COM #include <errno.h>
329160SSherry.Moore@Sun.COM #include <stdlib.h>
339160SSherry.Moore@Sun.COM #include <strings.h>
349160SSherry.Moore@Sun.COM #include <unistd.h>
359160SSherry.Moore@Sun.COM #include <fcntl.h>
369160SSherry.Moore@Sun.COM #include <assert.h>
379160SSherry.Moore@Sun.COM #include <sys/types.h>
389160SSherry.Moore@Sun.COM #include <sys/stat.h>
399160SSherry.Moore@Sun.COM #include <sys/mount.h>
409160SSherry.Moore@Sun.COM #include <sys/mntent.h>
419160SSherry.Moore@Sun.COM #include <sys/mnttab.h>
429160SSherry.Moore@Sun.COM #include <sys/fs/ufs_mount.h>
439160SSherry.Moore@Sun.COM #include <sys/dktp/fdisk.h>
449160SSherry.Moore@Sun.COM #include <libfstyp.h>
459160SSherry.Moore@Sun.COM 
469160SSherry.Moore@Sun.COM #include "libgrub_impl.h"
479160SSherry.Moore@Sun.COM 
489160SSherry.Moore@Sun.COM static int
499160SSherry.Moore@Sun.COM slice_match(const char *physpath, int slice)
509160SSherry.Moore@Sun.COM {
519160SSherry.Moore@Sun.COM 	const char *pos;
529160SSherry.Moore@Sun.COM 
539160SSherry.Moore@Sun.COM 	return ((pos = strrchr(physpath, slice)) == NULL ||
549160SSherry.Moore@Sun.COM 	    pos[1] != 0 || pos[-1] != ':');
559160SSherry.Moore@Sun.COM }
569160SSherry.Moore@Sun.COM 
579160SSherry.Moore@Sun.COM /*
589160SSherry.Moore@Sun.COM  * Returns zero if path contains ufs
599160SSherry.Moore@Sun.COM  */
609160SSherry.Moore@Sun.COM static int
619160SSherry.Moore@Sun.COM slice_ufs(const char *path)
629160SSherry.Moore@Sun.COM {
639160SSherry.Moore@Sun.COM 	int fd, ret;
649160SSherry.Moore@Sun.COM 	const char *id;
659160SSherry.Moore@Sun.COM 	fstyp_handle_t hdl;
669160SSherry.Moore@Sun.COM 
679160SSherry.Moore@Sun.COM 	fd = open(path, O_RDONLY);
689160SSherry.Moore@Sun.COM 	if ((ret = fstyp_init(fd, 0, NULL, &hdl)) == 0) {
699160SSherry.Moore@Sun.COM 		ret = fstyp_ident(hdl, "ufs", &id);
709160SSherry.Moore@Sun.COM 		fstyp_fini(hdl);
719160SSherry.Moore@Sun.COM 	}
729160SSherry.Moore@Sun.COM 	(void) close(fd);
739160SSherry.Moore@Sun.COM 	return (ret);
749160SSherry.Moore@Sun.COM }
759160SSherry.Moore@Sun.COM 
769160SSherry.Moore@Sun.COM 
779160SSherry.Moore@Sun.COM static int
789160SSherry.Moore@Sun.COM get_sol_prtnum(const char *physpath)
799160SSherry.Moore@Sun.COM {
809160SSherry.Moore@Sun.COM 	int i, fd;
819160SSherry.Moore@Sun.COM 	char *pos;
829160SSherry.Moore@Sun.COM 	size_t sz;
839160SSherry.Moore@Sun.COM 	struct mboot *mb;
849160SSherry.Moore@Sun.COM 	struct ipart *ipart;
859160SSherry.Moore@Sun.COM 	char boot_sect[512];
869160SSherry.Moore@Sun.COM 	char rdev[MAXNAMELEN];
879160SSherry.Moore@Sun.COM 
889160SSherry.Moore@Sun.COM 	(void) snprintf(rdev, sizeof (rdev), "/devices%s,raw", physpath);
899160SSherry.Moore@Sun.COM 
909160SSherry.Moore@Sun.COM 	if ((pos = strrchr(rdev, ':')) == NULL)
919160SSherry.Moore@Sun.COM 		return (PRTNUM_INVALID);
929160SSherry.Moore@Sun.COM 
939160SSherry.Moore@Sun.COM 	pos[1] = SLCNUM_WHOLE_DISK;
949160SSherry.Moore@Sun.COM 
959160SSherry.Moore@Sun.COM 	fd = open(rdev, O_RDONLY);
969160SSherry.Moore@Sun.COM 	sz = read(fd, boot_sect, sizeof (boot_sect));
979160SSherry.Moore@Sun.COM 	(void) close(fd);
989160SSherry.Moore@Sun.COM 
999160SSherry.Moore@Sun.COM 	if (sz != sizeof (boot_sect))
1009160SSherry.Moore@Sun.COM 		return (PRTNUM_INVALID);
1019160SSherry.Moore@Sun.COM 
1029160SSherry.Moore@Sun.COM 	/* parse fdisk table */
1039160SSherry.Moore@Sun.COM 	mb = (struct mboot *)(uintptr_t)boot_sect;
1049160SSherry.Moore@Sun.COM 	ipart = (struct ipart *)(uintptr_t)mb->parts;
1059160SSherry.Moore@Sun.COM 	for (i = 0; i < FD_NUMPART; ++i) {
1069160SSherry.Moore@Sun.COM 		if (ipart[i].systid == SUNIXOS || ipart[i].systid == SUNIXOS2)
1079160SSherry.Moore@Sun.COM 			return (i);
1089160SSherry.Moore@Sun.COM 	}
1099160SSherry.Moore@Sun.COM 	return (PRTNUM_INVALID);
1109160SSherry.Moore@Sun.COM }
1119160SSherry.Moore@Sun.COM 
1129160SSherry.Moore@Sun.COM /*
1139160SSherry.Moore@Sun.COM  * Get physpath, topfs and bootfs for ZFS root dataset.
1149160SSherry.Moore@Sun.COM  * Return 0 on success, non-zero (not errno) on failure.
1159160SSherry.Moore@Sun.COM  */
1169160SSherry.Moore@Sun.COM static int
1179160SSherry.Moore@Sun.COM get_zfs_root(zfs_handle_t *zfh, grub_fs_t *fs, grub_root_t *root)
1189160SSherry.Moore@Sun.COM {
1199160SSherry.Moore@Sun.COM 	int ret;
1209160SSherry.Moore@Sun.COM 	zpool_handle_t *zph;
1219160SSherry.Moore@Sun.COM 	const char *name;
1229160SSherry.Moore@Sun.COM 
1239160SSherry.Moore@Sun.COM 	if (zfs_get_type(zfh) != ZFS_TYPE_FILESYSTEM ||
1249160SSherry.Moore@Sun.COM 	    (name = zfs_get_name(zfh)) == NULL ||
1259160SSherry.Moore@Sun.COM 	    (zph = zpool_open(fs->gf_lzfh, name)) == NULL)
1269160SSherry.Moore@Sun.COM 		return (-1);
1279160SSherry.Moore@Sun.COM 
1289160SSherry.Moore@Sun.COM 	if ((ret = zpool_get_physpath(zph, root->gr_physpath,
1299160SSherry.Moore@Sun.COM 	    sizeof (root->gr_physpath))) == 0 &&
1309160SSherry.Moore@Sun.COM 	    (ret = zpool_get_prop(zph, ZPOOL_PROP_BOOTFS,
1319160SSherry.Moore@Sun.COM 	    root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev,
1329160SSherry.Moore@Sun.COM 	    sizeof (root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev), NULL)) == 0) {
1339160SSherry.Moore@Sun.COM 
1349160SSherry.Moore@Sun.COM 		(void) strlcpy(root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev, name,
1359160SSherry.Moore@Sun.COM 		    sizeof (root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev));
1369160SSherry.Moore@Sun.COM 		(void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_BOOTFS,
1379160SSherry.Moore@Sun.COM 		    MNTTYPE_ZFS);
1389160SSherry.Moore@Sun.COM 		(void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_TOPFS,
1399160SSherry.Moore@Sun.COM 		    MNTTYPE_ZFS);
1409160SSherry.Moore@Sun.COM 	}
1419160SSherry.Moore@Sun.COM 
1429160SSherry.Moore@Sun.COM 	zpool_close(zph);
1439160SSherry.Moore@Sun.COM 	return (ret);
1449160SSherry.Moore@Sun.COM }
1459160SSherry.Moore@Sun.COM 
1469160SSherry.Moore@Sun.COM /*
1479160SSherry.Moore@Sun.COM  * On entry physpath parameter supposed to contain:
1489160SSherry.Moore@Sun.COM  * <disk_physpath>[<space><disk_physpath>]*.
1499160SSherry.Moore@Sun.COM  * Retireives first <disk_physpath> that matches both partition and slice.
1509160SSherry.Moore@Sun.COM  * If any partition and slice is acceptable, first <disk_physpath> is returned.
1519160SSherry.Moore@Sun.COM  */
1529160SSherry.Moore@Sun.COM static int
1539160SSherry.Moore@Sun.COM get_one_physpath(char *physpath, uint_t prtnum, uint_t slcnum)
1549160SSherry.Moore@Sun.COM {
1559160SSherry.Moore@Sun.COM 	int ret;
1569160SSherry.Moore@Sun.COM 	char *tmp, *tok;
1579160SSherry.Moore@Sun.COM 
1589160SSherry.Moore@Sun.COM 	if (!IS_SLCNUM_VALID(slcnum) && !IS_PRTNUM_VALID(prtnum)) {
1599160SSherry.Moore@Sun.COM 		(void) strtok(physpath, " ");
1609160SSherry.Moore@Sun.COM 		return (0);
1619160SSherry.Moore@Sun.COM 	}
1629160SSherry.Moore@Sun.COM 
1639160SSherry.Moore@Sun.COM 	if ((tmp = strdup(physpath)) == NULL)
1649160SSherry.Moore@Sun.COM 		return (errno);
1659160SSherry.Moore@Sun.COM 
1669160SSherry.Moore@Sun.COM 	ret = ENODEV;
1679160SSherry.Moore@Sun.COM 	for (tok = strtok(tmp, " "); tok != NULL; tok = strtok(NULL, " ")) {
1689160SSherry.Moore@Sun.COM 		if ((ret = (slice_match(tok, slcnum) != 0 ||
1699160SSherry.Moore@Sun.COM 		    get_sol_prtnum(tok) != prtnum)) == 0) {
1709160SSherry.Moore@Sun.COM 			(void) strcpy(physpath, tok);
1719160SSherry.Moore@Sun.COM 			break;
1729160SSherry.Moore@Sun.COM 		}
1739160SSherry.Moore@Sun.COM 	}
1749160SSherry.Moore@Sun.COM 
1759160SSherry.Moore@Sun.COM 	free(tmp);
176*9996SSherry.Moore@Sun.COM 	if (ret)
177*9996SSherry.Moore@Sun.COM 		ret = ENODEV;
1789160SSherry.Moore@Sun.COM 	return (ret);
1799160SSherry.Moore@Sun.COM }
1809160SSherry.Moore@Sun.COM 
1819160SSherry.Moore@Sun.COM static int
1829160SSherry.Moore@Sun.COM zfs_bootsign(zfs_handle_t *zfh, void *data)
1839160SSherry.Moore@Sun.COM {
1849160SSherry.Moore@Sun.COM 	grub_barg_t *barg;
1859160SSherry.Moore@Sun.COM 	grub_menu_t *menu;
1869160SSherry.Moore@Sun.COM 	struct stat st;
1879160SSherry.Moore@Sun.COM 	char path[MAXPATHLEN];
1889160SSherry.Moore@Sun.COM 
1899160SSherry.Moore@Sun.COM 	barg = (grub_barg_t *)data;
1909160SSherry.Moore@Sun.COM 	menu = barg->gb_entry->ge_menu;
1919160SSherry.Moore@Sun.COM 
1929160SSherry.Moore@Sun.COM 	do {
1939160SSherry.Moore@Sun.COM 		if (get_zfs_root(zfh, &menu->gm_fs, &barg->gb_root) != 0 ||
1949160SSherry.Moore@Sun.COM 		    get_one_physpath(barg->gb_root.gr_physpath, barg->gb_prtnum,
1959160SSherry.Moore@Sun.COM 		    barg->gb_slcnum) != 0)
1969160SSherry.Moore@Sun.COM 			break;
1979160SSherry.Moore@Sun.COM 
1989160SSherry.Moore@Sun.COM 		/*
1999160SSherry.Moore@Sun.COM 		 * if top zfs dataset is not mounted, mount it now
2009160SSherry.Moore@Sun.COM 		 */
2019160SSherry.Moore@Sun.COM 		if (barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp[0] == 0) {
2029160SSherry.Moore@Sun.COM 			if (grub_fsd_mount_tmp(barg->gb_root.gr_fs +
2039160SSherry.Moore@Sun.COM 			    GRBM_ZFS_TOPFS, MNTTYPE_ZFS) != 0)
2049160SSherry.Moore@Sun.COM 				break;
2059160SSherry.Moore@Sun.COM 		}
2069160SSherry.Moore@Sun.COM 
2079160SSherry.Moore@Sun.COM 		/* check that bootsign exists and it is a regular file */
2089160SSherry.Moore@Sun.COM 		(void) snprintf(path, sizeof (path), "%s%s",
2099160SSherry.Moore@Sun.COM 		    barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp,
2109160SSherry.Moore@Sun.COM 		    barg->gb_bootsign);
2119160SSherry.Moore@Sun.COM 
2129160SSherry.Moore@Sun.COM 		if (lstat(path, &st) != 0 || S_ISREG(st.st_mode) == 0 ||
2139160SSherry.Moore@Sun.COM 		    (st.st_mode & S_IRUSR) == 0)
2149160SSherry.Moore@Sun.COM 			break;
2159160SSherry.Moore@Sun.COM 
2169160SSherry.Moore@Sun.COM 		(void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_ZFS,
2179160SSherry.Moore@Sun.COM 		    sizeof (barg->gb_root.gr_fstyp));
2189160SSherry.Moore@Sun.COM 		barg->gb_walkret = 0;
2199160SSherry.Moore@Sun.COM 	/* LINTED: E_CONSTANT_CONDITION */
2209160SSherry.Moore@Sun.COM 	} while (0);
2219160SSherry.Moore@Sun.COM 
2229160SSherry.Moore@Sun.COM 	grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_ZFS_TOPFS);
2239160SSherry.Moore@Sun.COM 	zfs_close(zfh);
2249160SSherry.Moore@Sun.COM 
2259160SSherry.Moore@Sun.COM 	/* return non-zero to terminate the walk */
2269160SSherry.Moore@Sun.COM 	return (barg->gb_walkret == 0);
2279160SSherry.Moore@Sun.COM }
2289160SSherry.Moore@Sun.COM 
2299160SSherry.Moore@Sun.COM static int
2309160SSherry.Moore@Sun.COM get_devlink(di_devlink_t dl, void *arg)
2319160SSherry.Moore@Sun.COM {
2329160SSherry.Moore@Sun.COM 	const char *path;
2339160SSherry.Moore@Sun.COM 	grub_barg_t *barg;
2349160SSherry.Moore@Sun.COM 
2359160SSherry.Moore@Sun.COM 	barg = (grub_barg_t *)arg;
2369160SSherry.Moore@Sun.COM 	if ((path = di_devlink_path(dl)) != NULL)
2379160SSherry.Moore@Sun.COM 		(void) strlcpy(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev, path,
2389160SSherry.Moore@Sun.COM 		    sizeof (barg->gb_root.gr_fs[GRBM_UFS].gfs_dev));
2399160SSherry.Moore@Sun.COM 	return (DI_WALK_TERMINATE);
2409160SSherry.Moore@Sun.COM }
2419160SSherry.Moore@Sun.COM 
2429160SSherry.Moore@Sun.COM static int
2439160SSherry.Moore@Sun.COM ufs_bootsign_check(grub_barg_t *barg)
2449160SSherry.Moore@Sun.COM {
2459160SSherry.Moore@Sun.COM 	int ret;
2469160SSherry.Moore@Sun.COM 	struct stat st;
2479160SSherry.Moore@Sun.COM 	grub_menu_t *mp;
2489160SSherry.Moore@Sun.COM 	char path[MAXPATHLEN];
2499160SSherry.Moore@Sun.COM 
2509160SSherry.Moore@Sun.COM 	mp = barg->gb_entry->ge_menu;
2519160SSherry.Moore@Sun.COM 
2529160SSherry.Moore@Sun.COM 	/* get /dev/dsk link */
2539160SSherry.Moore@Sun.COM 	if (di_devlink_walk(mp->gm_fs.gf_dvlh, "^dsk/",
2549160SSherry.Moore@Sun.COM 	    barg->gb_root.gr_physpath, DI_PRIMARY_LINK, barg, get_devlink) != 0)
2559160SSherry.Moore@Sun.COM 		return (errno);
2569160SSherry.Moore@Sun.COM 	/*
2579160SSherry.Moore@Sun.COM 	 * if disk is not mounted, mount it now
2589160SSherry.Moore@Sun.COM 	 */
2599160SSherry.Moore@Sun.COM 	if (grub_fsd_get_mountp(barg->gb_root.gr_fs + GRBM_UFS,
2609160SSherry.Moore@Sun.COM 	    MNTTYPE_UFS) != 0) {
2619160SSherry.Moore@Sun.COM 		if ((ret =
2629160SSherry.Moore@Sun.COM 		    slice_ufs(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)) != 0 ||
2639160SSherry.Moore@Sun.COM 		    (ret = grub_fsd_mount_tmp(barg->gb_root.gr_fs + GRBM_UFS,
2649160SSherry.Moore@Sun.COM 		    MNTTYPE_UFS)) != 0)
2659160SSherry.Moore@Sun.COM 			return (ret);
2669160SSherry.Moore@Sun.COM 	}
2679160SSherry.Moore@Sun.COM 
2689160SSherry.Moore@Sun.COM 	(void) snprintf(path, sizeof (path), "%s%s",
2699160SSherry.Moore@Sun.COM 	    barg->gb_root.gr_fs[GRBM_UFS].gfs_mountp, barg->gb_bootsign);
2709160SSherry.Moore@Sun.COM 
2719160SSherry.Moore@Sun.COM 	if (lstat(path, &st) == 0 && S_ISREG(st.st_mode) &&
2729160SSherry.Moore@Sun.COM 	    (st.st_mode & S_IRUSR) != 0) {
2739160SSherry.Moore@Sun.COM 		barg->gb_walkret = 0;
2749160SSherry.Moore@Sun.COM 		(void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_UFS,
2759160SSherry.Moore@Sun.COM 		    sizeof (barg->gb_root.gr_fstyp));
2769160SSherry.Moore@Sun.COM 	}
2779160SSherry.Moore@Sun.COM 
2789160SSherry.Moore@Sun.COM 	grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_UFS);
2799160SSherry.Moore@Sun.COM 	return (barg->gb_walkret);
2809160SSherry.Moore@Sun.COM }
2819160SSherry.Moore@Sun.COM 
2829160SSherry.Moore@Sun.COM static int
2839160SSherry.Moore@Sun.COM ufs_bootsign(di_node_t node, di_minor_t minor, void *arg)
2849160SSherry.Moore@Sun.COM {
2859160SSherry.Moore@Sun.COM 	uint_t prtnum;
2869160SSherry.Moore@Sun.COM 	char *name, *path;
2879160SSherry.Moore@Sun.COM 	grub_barg_t *barg;
2889160SSherry.Moore@Sun.COM 
2899160SSherry.Moore@Sun.COM 	barg = (grub_barg_t *)arg;
2909160SSherry.Moore@Sun.COM 
2919160SSherry.Moore@Sun.COM 	if (di_minor_spectype(minor) != S_IFBLK)
2929160SSherry.Moore@Sun.COM 		return (DI_WALK_CONTINUE);
2939160SSherry.Moore@Sun.COM 
2949160SSherry.Moore@Sun.COM 	name = di_minor_name(minor);
2959160SSherry.Moore@Sun.COM 	if (name[0] != barg->gb_slcnum || name[1] != 0)
2969160SSherry.Moore@Sun.COM 		return (DI_WALK_CONTINUE);
2979160SSherry.Moore@Sun.COM 
2989160SSherry.Moore@Sun.COM 	path = di_devfs_path(node);
2999160SSherry.Moore@Sun.COM 	(void) snprintf(barg->gb_root.gr_physpath,
3009160SSherry.Moore@Sun.COM 	    sizeof (barg->gb_root.gr_physpath), "%s:%c", path, barg->gb_slcnum);
3019160SSherry.Moore@Sun.COM 	di_devfs_path_free(path);
3029160SSherry.Moore@Sun.COM 
3039160SSherry.Moore@Sun.COM 	prtnum = get_sol_prtnum(barg->gb_root.gr_physpath);
3049160SSherry.Moore@Sun.COM 	if (!IS_PRTNUM_VALID(prtnum))
3059160SSherry.Moore@Sun.COM 		return (DI_WALK_CONTINUE);
3069160SSherry.Moore@Sun.COM 
3079160SSherry.Moore@Sun.COM 	/*
3089160SSherry.Moore@Sun.COM 	 * check only specified partition, slice
3099160SSherry.Moore@Sun.COM 	 */
3109160SSherry.Moore@Sun.COM 
3119160SSherry.Moore@Sun.COM 	if (IS_PRTNUM_VALID(barg->gb_prtnum)) {
3129160SSherry.Moore@Sun.COM 		if (prtnum != barg->gb_prtnum || ufs_bootsign_check(barg) != 0)
3139160SSherry.Moore@Sun.COM 			return (DI_WALK_CONTINUE);
3149160SSherry.Moore@Sun.COM 		return (DI_WALK_TERMINATE);
3159160SSherry.Moore@Sun.COM 	}
3169160SSherry.Moore@Sun.COM 
3179160SSherry.Moore@Sun.COM 	/*
3189160SSherry.Moore@Sun.COM 	 * Walk through all slices in found solaris partition
3199160SSherry.Moore@Sun.COM 	 */
3209160SSherry.Moore@Sun.COM 
3219160SSherry.Moore@Sun.COM 	barg->gb_prtnum = prtnum;
3229160SSherry.Moore@Sun.COM 	minor = DI_MINOR_NIL;
3239160SSherry.Moore@Sun.COM 
3249160SSherry.Moore@Sun.COM 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
3259160SSherry.Moore@Sun.COM 
3269160SSherry.Moore@Sun.COM 		if (di_minor_spectype(minor) != S_IFBLK)
3279160SSherry.Moore@Sun.COM 			continue;
3289160SSherry.Moore@Sun.COM 
3299160SSherry.Moore@Sun.COM 		name = di_minor_name(minor);
3309160SSherry.Moore@Sun.COM 		if (!IS_SLCNUM_VALID(name[0]) || name[1] != 0)
3319160SSherry.Moore@Sun.COM 			continue;
3329160SSherry.Moore@Sun.COM 
3339160SSherry.Moore@Sun.COM 		barg->gb_slcnum = name[0];
3349160SSherry.Moore@Sun.COM 		path = strrchr(barg->gb_root.gr_physpath, ':');
3359160SSherry.Moore@Sun.COM 		path[1] = barg->gb_slcnum;
3369160SSherry.Moore@Sun.COM 
3379160SSherry.Moore@Sun.COM 		if (ufs_bootsign_check(barg) == 0)
3389160SSherry.Moore@Sun.COM 			return (DI_WALK_TERMINATE);
3399160SSherry.Moore@Sun.COM 	}
3409160SSherry.Moore@Sun.COM 
3419160SSherry.Moore@Sun.COM 	barg->gb_prtnum = (uint_t)PRTNUM_INVALID;
3429160SSherry.Moore@Sun.COM 	barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK;
3439160SSherry.Moore@Sun.COM 	return (DI_WALK_CONTINUE);
3449160SSherry.Moore@Sun.COM }
3459160SSherry.Moore@Sun.COM 
3469160SSherry.Moore@Sun.COM /*
3479160SSherry.Moore@Sun.COM  * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios
3489160SSherry.Moore@Sun.COM  * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root,
3499160SSherry.Moore@Sun.COM  * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found
3509160SSherry.Moore@Sun.COM  * on other slices.
3519160SSherry.Moore@Sun.COM  * That function first searches through all top datasets of active zpools,
3529160SSherry.Moore@Sun.COM  * then if bootsign still not found walks through all disks and tries to
3539160SSherry.Moore@Sun.COM  * find ufs slice with the bootsign.
3549160SSherry.Moore@Sun.COM  */
3559160SSherry.Moore@Sun.COM int
3569160SSherry.Moore@Sun.COM grub_find_bootsign(grub_barg_t *barg)
3579160SSherry.Moore@Sun.COM {
3589160SSherry.Moore@Sun.COM 	grub_menu_t *mp;
3599160SSherry.Moore@Sun.COM 	mp = barg->gb_entry->ge_menu;
3609160SSherry.Moore@Sun.COM 
3619160SSherry.Moore@Sun.COM 	/* try to find bootsign over zfs pools */
3629160SSherry.Moore@Sun.COM 	barg->gb_walkret = EG_BOOTSIGN;
3639160SSherry.Moore@Sun.COM 	(void) zfs_iter_root(mp->gm_fs.gf_lzfh, zfs_bootsign, barg);
3649160SSherry.Moore@Sun.COM 
3659160SSherry.Moore@Sun.COM 	/* try ufs now */
3669160SSherry.Moore@Sun.COM 	if (barg->gb_walkret != 0 && di_walk_minor(mp->gm_fs.gf_diroot,
3679160SSherry.Moore@Sun.COM 	    DDI_NT_BLOCK, 0, barg, ufs_bootsign) != 0)
3689160SSherry.Moore@Sun.COM 		return (errno);
3699160SSherry.Moore@Sun.COM 
3709160SSherry.Moore@Sun.COM 	return (barg->gb_walkret);
3719160SSherry.Moore@Sun.COM }
3729160SSherry.Moore@Sun.COM 
3739160SSherry.Moore@Sun.COM /*
3749160SSherry.Moore@Sun.COM  * Get current root file system.
3759160SSherry.Moore@Sun.COM  * Return 0 on success, errno code on failure.
3769160SSherry.Moore@Sun.COM  */
3779160SSherry.Moore@Sun.COM int
3789160SSherry.Moore@Sun.COM grub_current_root(grub_fs_t *fs, grub_root_t *root)
3799160SSherry.Moore@Sun.COM {
3809160SSherry.Moore@Sun.COM 	int rc = 0;
3819160SSherry.Moore@Sun.COM 	FILE *fp = NULL;
3829160SSherry.Moore@Sun.COM 	char *name = NULL;
3839160SSherry.Moore@Sun.COM 	zfs_handle_t *zfh = NULL;
3849160SSherry.Moore@Sun.COM 	struct mnttab mp = {0};
3859160SSherry.Moore@Sun.COM 	struct mnttab mpref = {0};
3869160SSherry.Moore@Sun.COM 	char buf[MAXNAMELEN] = {0};
3879160SSherry.Moore@Sun.COM 
3889160SSherry.Moore@Sun.COM 	mpref.mnt_mountp = "/";
3899160SSherry.Moore@Sun.COM 
3909160SSherry.Moore@Sun.COM 	if ((fp = fopen(MNTTAB, "r")) == NULL)
3919160SSherry.Moore@Sun.COM 		return (errno);
3929160SSherry.Moore@Sun.COM 
3939160SSherry.Moore@Sun.COM 	/*
3949160SSherry.Moore@Sun.COM 	 * getmntany returns non-zero for failure, and sets errno
3959160SSherry.Moore@Sun.COM 	 */
3969160SSherry.Moore@Sun.COM 	rc = getmntany(fp, &mp, &mpref);
3979160SSherry.Moore@Sun.COM 	if (rc != 0)
3989160SSherry.Moore@Sun.COM 		rc = errno;
3999160SSherry.Moore@Sun.COM 
4009160SSherry.Moore@Sun.COM 	(void) fclose(fp);
4019160SSherry.Moore@Sun.COM 
4029160SSherry.Moore@Sun.COM 	if (rc != 0)
4039160SSherry.Moore@Sun.COM 		return (rc);
4049160SSherry.Moore@Sun.COM 
4059160SSherry.Moore@Sun.COM 	(void) strlcpy(root->gr_fstyp, mp.mnt_fstype, sizeof (root->gr_fstyp));
4069160SSherry.Moore@Sun.COM 
4079160SSherry.Moore@Sun.COM 	if (strcmp(root->gr_fstyp, MNTTYPE_ZFS) == 0) {
4089160SSherry.Moore@Sun.COM 
4099160SSherry.Moore@Sun.COM 		(void) strlcpy(buf, mp.mnt_special, sizeof (buf));
4109160SSherry.Moore@Sun.COM 		if ((name = strtok(buf, "/")) == NULL)
4119160SSherry.Moore@Sun.COM 			return (EG_CURROOT);
4129160SSherry.Moore@Sun.COM 
4139160SSherry.Moore@Sun.COM 		if ((zfh = zfs_open(fs->gf_lzfh, name, ZFS_TYPE_FILESYSTEM)) ==
4149160SSherry.Moore@Sun.COM 		    NULL)
4159160SSherry.Moore@Sun.COM 			return (EG_OPENZFS);
4169160SSherry.Moore@Sun.COM 
4179160SSherry.Moore@Sun.COM 		/*
418*9996SSherry.Moore@Sun.COM 		 * get_zfs_root returns non-zero on failure, not errno.
4199160SSherry.Moore@Sun.COM 		 */
4209160SSherry.Moore@Sun.COM 		if (get_zfs_root(zfh, fs, root))
4219160SSherry.Moore@Sun.COM 			rc = EG_CURROOT;
422*9996SSherry.Moore@Sun.COM 		else
423*9996SSherry.Moore@Sun.COM 			/*
424*9996SSherry.Moore@Sun.COM 			 * For mirrored root physpath would contain the list of
425*9996SSherry.Moore@Sun.COM 			 * all bootable devices, pick up the first one.
426*9996SSherry.Moore@Sun.COM 			 */
427*9996SSherry.Moore@Sun.COM 			rc = get_one_physpath(root->gr_physpath, SLCNUM_INVALID,
428*9996SSherry.Moore@Sun.COM 			    PRTNUM_INVALID);
4299160SSherry.Moore@Sun.COM 
4309160SSherry.Moore@Sun.COM 		zfs_close(zfh);
4319160SSherry.Moore@Sun.COM 
4329160SSherry.Moore@Sun.COM 	} else if (strcmp(mp.mnt_fstype, MNTTYPE_UFS) == 0) {
4339160SSherry.Moore@Sun.COM 		(void) strlcpy(root->gr_fs[GRBM_UFS].gfs_dev, mp.mnt_special,
4349160SSherry.Moore@Sun.COM 		    sizeof (root->gr_fs[GRBM_UFS].gfs_dev));
4359160SSherry.Moore@Sun.COM 		(void) strlcpy(root->gr_fs[GRBM_UFS].gfs_mountp, mp.mnt_mountp,
4369160SSherry.Moore@Sun.COM 		    sizeof (root->gr_fs[GRBM_UFS].gfs_mountp));
4379160SSherry.Moore@Sun.COM 	} else {
4389160SSherry.Moore@Sun.COM 		rc = EG_UNKNOWNFS;
4399160SSherry.Moore@Sun.COM 	}
4409160SSherry.Moore@Sun.COM 
4419160SSherry.Moore@Sun.COM 	return (rc);
4429160SSherry.Moore@Sun.COM }
4439160SSherry.Moore@Sun.COM 
4449160SSherry.Moore@Sun.COM grub_fsdesc_t *
4459160SSherry.Moore@Sun.COM grub_get_rootfsd(const grub_root_t *root)
4469160SSherry.Moore@Sun.COM {
4479160SSherry.Moore@Sun.COM 	grub_fsdesc_t *fsd = NULL;
4489160SSherry.Moore@Sun.COM 
4499160SSherry.Moore@Sun.COM 	assert(root);
4509160SSherry.Moore@Sun.COM 	if (strcmp(MNTTYPE_UFS, root->gr_fstyp) == 0)
4519160SSherry.Moore@Sun.COM 		fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_UFS;
4529160SSherry.Moore@Sun.COM 	else if (strcmp(MNTTYPE_ZFS, root->gr_fstyp) == 0)
4539160SSherry.Moore@Sun.COM 		fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_ZFS_BOOTFS;
4549160SSherry.Moore@Sun.COM 
4559160SSherry.Moore@Sun.COM 	return (fsd);
4569160SSherry.Moore@Sun.COM }
4579160SSherry.Moore@Sun.COM 
4589160SSherry.Moore@Sun.COM /*
4599160SSherry.Moore@Sun.COM  * Gets file systems mount point if any.
4609160SSherry.Moore@Sun.COM  * Return 0 if filesystem is mounted, errno on failure.
4619160SSherry.Moore@Sun.COM  */
4629160SSherry.Moore@Sun.COM int
4639160SSherry.Moore@Sun.COM grub_fsd_get_mountp(grub_fsdesc_t *fsd, char *fstyp)
4649160SSherry.Moore@Sun.COM {
4659160SSherry.Moore@Sun.COM 	int rc;
4669160SSherry.Moore@Sun.COM 	FILE *fp = NULL;
4679160SSherry.Moore@Sun.COM 	struct mnttab mp = {0};
4689160SSherry.Moore@Sun.COM 	struct mnttab mpref = {0};
4699160SSherry.Moore@Sun.COM 
4709160SSherry.Moore@Sun.COM 	fsd->gfs_mountp[0] = 0;
4719160SSherry.Moore@Sun.COM 
4729160SSherry.Moore@Sun.COM 	if ((fp = fopen(MNTTAB, "r")) == NULL)
4739160SSherry.Moore@Sun.COM 		return (errno);
4749160SSherry.Moore@Sun.COM 
4759160SSherry.Moore@Sun.COM 	mpref.mnt_special = fsd->gfs_dev;
4769160SSherry.Moore@Sun.COM 	mpref.mnt_fstype = fstyp;
4779160SSherry.Moore@Sun.COM 
4789160SSherry.Moore@Sun.COM 	if ((rc = getmntany(fp, &mp, &mpref)) == 0)
4799160SSherry.Moore@Sun.COM 		(void) strlcpy(fsd->gfs_mountp, mp.mnt_mountp,
4809160SSherry.Moore@Sun.COM 		    sizeof (fsd->gfs_mountp));
4819160SSherry.Moore@Sun.COM 	else
4829160SSherry.Moore@Sun.COM 		rc = EG_GETMNTTAB;
4839160SSherry.Moore@Sun.COM 
4849160SSherry.Moore@Sun.COM 	(void) fclose(fp);
4859160SSherry.Moore@Sun.COM 	return (rc);
4869160SSherry.Moore@Sun.COM }
4879160SSherry.Moore@Sun.COM 
4889160SSherry.Moore@Sun.COM static const char tmp_mountp[] = "/tmp/.libgrubmgmt.%s.XXXXXX";
4899160SSherry.Moore@Sun.COM 
4909160SSherry.Moore@Sun.COM /*
4919160SSherry.Moore@Sun.COM  * Mount file system at tmp_mountp.
4929160SSherry.Moore@Sun.COM  * Return 0 on success, errno on failure.
4939160SSherry.Moore@Sun.COM  */
4949160SSherry.Moore@Sun.COM int
4959160SSherry.Moore@Sun.COM grub_fsd_mount_tmp(grub_fsdesc_t *fsd, const char *fstyp)
4969160SSherry.Moore@Sun.COM {
4979160SSherry.Moore@Sun.COM 	const char *pos;
4989160SSherry.Moore@Sun.COM 	void *data = NULL;
4999160SSherry.Moore@Sun.COM 	int dtsz = 0;
5009160SSherry.Moore@Sun.COM 	struct ufs_args ufs_args = {UFSMNT_LARGEFILES};
5019160SSherry.Moore@Sun.COM 	char mntopts[MNT_LINE_MAX] = "";
5029160SSherry.Moore@Sun.COM 	int rc = 0;
5039160SSherry.Moore@Sun.COM 
5049160SSherry.Moore@Sun.COM 	assert(fsd);
5059160SSherry.Moore@Sun.COM 	assert(!fsd->gfs_is_tmp_mounted);
5069160SSherry.Moore@Sun.COM 
5079160SSherry.Moore@Sun.COM 	fsd->gfs_mountp[0] = 0;
5089160SSherry.Moore@Sun.COM 
5099160SSherry.Moore@Sun.COM 	if (strcmp(fstyp, MNTTYPE_UFS) == 0) {
5109160SSherry.Moore@Sun.COM 		(void) strlcpy(mntopts, MNTOPT_LARGEFILES, sizeof (mntopts));
5119160SSherry.Moore@Sun.COM 		data = &ufs_args;
5129160SSherry.Moore@Sun.COM 		dtsz = sizeof (ufs_args);
5139160SSherry.Moore@Sun.COM 	} else if (strcmp(fstyp, MNTTYPE_ZFS) != 0) {
5149160SSherry.Moore@Sun.COM 		return (EG_UNKNOWNFS);
5159160SSherry.Moore@Sun.COM 	}
5169160SSherry.Moore@Sun.COM 
5179160SSherry.Moore@Sun.COM 	/* construct name for temporary mount point */
5189160SSherry.Moore@Sun.COM 	pos = strrchr(fsd->gfs_dev, '/');
5199160SSherry.Moore@Sun.COM 	pos = (pos == NULL) ? fsd->gfs_dev : pos + 1;
5209160SSherry.Moore@Sun.COM 
5219160SSherry.Moore@Sun.COM 	(void) snprintf(fsd->gfs_mountp, sizeof (fsd->gfs_mountp),
5229160SSherry.Moore@Sun.COM 	    tmp_mountp, pos);
5239160SSherry.Moore@Sun.COM 	if (mkdtemp(fsd->gfs_mountp) != NULL) {
5249160SSherry.Moore@Sun.COM 		if ((rc = mount(fsd->gfs_dev, fsd->gfs_mountp,
5259160SSherry.Moore@Sun.COM 		    MS_DATA | MS_OPTIONSTR | MS_RDONLY,
5269160SSherry.Moore@Sun.COM 		    fstyp, data, dtsz, mntopts, sizeof (mntopts))) != 0) {
5279160SSherry.Moore@Sun.COM 			/*
5289160SSherry.Moore@Sun.COM 			 * mount failed, collect errno and remove temp dir
5299160SSherry.Moore@Sun.COM 			 */
5309160SSherry.Moore@Sun.COM 			rc = errno;
5319160SSherry.Moore@Sun.COM 			(void) rmdir(fsd->gfs_mountp);
5329160SSherry.Moore@Sun.COM 		}
5339160SSherry.Moore@Sun.COM 	} else {
5349160SSherry.Moore@Sun.COM 		rc = errno;
5359160SSherry.Moore@Sun.COM 	}
5369160SSherry.Moore@Sun.COM 
5379160SSherry.Moore@Sun.COM 	if (rc != 0)
5389160SSherry.Moore@Sun.COM 		fsd->gfs_mountp[0] = 0;
5399160SSherry.Moore@Sun.COM 
5409160SSherry.Moore@Sun.COM 	/*
5419160SSherry.Moore@Sun.COM 	 * Note that valid values for gfs_is_tmp_mounted are 0,1.
5429160SSherry.Moore@Sun.COM 	 * Any other value indicates that something bad happened.
5439160SSherry.Moore@Sun.COM 	 * Probably grub_fsd_umount_tmp() wasn't called or didn't
5449160SSherry.Moore@Sun.COM 	 * work as expected.
5459160SSherry.Moore@Sun.COM 	 */
5469160SSherry.Moore@Sun.COM 	fsd->gfs_is_tmp_mounted += (rc == 0);
5479160SSherry.Moore@Sun.COM 	return (rc);
5489160SSherry.Moore@Sun.COM }
5499160SSherry.Moore@Sun.COM 
5509160SSherry.Moore@Sun.COM /*
5519160SSherry.Moore@Sun.COM  * Unmount file system at tmp_mountp.
5529160SSherry.Moore@Sun.COM  */
5539160SSherry.Moore@Sun.COM void
5549160SSherry.Moore@Sun.COM grub_fsd_umount_tmp(grub_fsdesc_t *fsd)
5559160SSherry.Moore@Sun.COM {
5569160SSherry.Moore@Sun.COM 	if (fsd == NULL)
5579160SSherry.Moore@Sun.COM 		return;
5589160SSherry.Moore@Sun.COM 
5599160SSherry.Moore@Sun.COM 	if (fsd->gfs_is_tmp_mounted) {
5609160SSherry.Moore@Sun.COM 		if (fsd->gfs_mountp[0] != 0) {
5619160SSherry.Moore@Sun.COM 			(void) umount2(fsd->gfs_mountp, 0);
5629160SSherry.Moore@Sun.COM 			(void) rmdir(fsd->gfs_mountp);
5639160SSherry.Moore@Sun.COM 			fsd->gfs_mountp[0] = 0;
5649160SSherry.Moore@Sun.COM 		}
5659160SSherry.Moore@Sun.COM 		fsd->gfs_is_tmp_mounted = 0;
5669160SSherry.Moore@Sun.COM 	}
5679160SSherry.Moore@Sun.COM }
568