1753a6d45SSherry Moore /*
2753a6d45SSherry Moore  * CDDL HEADER START
3753a6d45SSherry Moore  *
4753a6d45SSherry Moore  * The contents of this file are subject to the terms of the
5753a6d45SSherry Moore  * Common Development and Distribution License (the "License").
6753a6d45SSherry Moore  * You may not use this file except in compliance with the License.
7753a6d45SSherry Moore  *
8753a6d45SSherry Moore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9753a6d45SSherry Moore  * or http://www.opensolaris.org/os/licensing.
10753a6d45SSherry Moore  * See the License for the specific language governing permissions
11753a6d45SSherry Moore  * and limitations under the License.
12753a6d45SSherry Moore  *
13753a6d45SSherry Moore  * When distributing Covered Code, include this CDDL HEADER in each
14753a6d45SSherry Moore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15753a6d45SSherry Moore  * If applicable, add the following below this CDDL HEADER, with the
16753a6d45SSherry Moore  * fields enclosed by brackets "[]" replaced with your own identifying
17753a6d45SSherry Moore  * information: Portions Copyright [yyyy] [name of copyright owner]
18753a6d45SSherry Moore  *
19753a6d45SSherry Moore  * CDDL HEADER END
20753a6d45SSherry Moore  */
21753a6d45SSherry Moore /*
22753a6d45SSherry Moore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23753a6d45SSherry Moore  * Use is subject to license terms.
24753a6d45SSherry Moore  */
2501f9868aSMarcel Telka /*
2601f9868aSMarcel Telka  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
27*57631629SToomas Soome  * Copyright 2015 Toomas Soome <tsoome@me.com>
2801f9868aSMarcel Telka  */
29753a6d45SSherry Moore 
30753a6d45SSherry Moore /*
3101f9868aSMarcel Telka  * This file contains all the functions that manipulate the file
32753a6d45SSherry Moore  * system where the GRUB menu resides.
33753a6d45SSherry Moore  */
34753a6d45SSherry Moore #include <stdio.h>
35753a6d45SSherry Moore #include <errno.h>
36753a6d45SSherry Moore #include <stdlib.h>
37753a6d45SSherry Moore #include <strings.h>
38753a6d45SSherry Moore #include <unistd.h>
39753a6d45SSherry Moore #include <fcntl.h>
40753a6d45SSherry Moore #include <assert.h>
41753a6d45SSherry Moore #include <sys/types.h>
42753a6d45SSherry Moore #include <sys/stat.h>
43753a6d45SSherry Moore #include <sys/mount.h>
44753a6d45SSherry Moore #include <sys/mntent.h>
45753a6d45SSherry Moore #include <sys/mnttab.h>
46*57631629SToomas Soome #include <sys/efi_partition.h>
47*57631629SToomas Soome #include <sys/vtoc.h>
48753a6d45SSherry Moore #include <sys/fs/ufs_mount.h>
49753a6d45SSherry Moore #include <sys/dktp/fdisk.h>
50753a6d45SSherry Moore #include <libfstyp.h>
5101f9868aSMarcel Telka #if defined(i386) || defined(__amd64)
5201f9868aSMarcel Telka #include <libfdisk.h>
5301f9868aSMarcel Telka #endif
54753a6d45SSherry Moore 
55753a6d45SSherry Moore #include "libgrub_impl.h"
56753a6d45SSherry Moore 
57753a6d45SSherry Moore static int
slice_match(const char * physpath,int slice)58753a6d45SSherry Moore slice_match(const char *physpath, int slice)
59753a6d45SSherry Moore {
60753a6d45SSherry Moore 	const char *pos;
61753a6d45SSherry Moore 
62*57631629SToomas Soome 	/* always match whole disk slice */
63*57631629SToomas Soome 	if (slice == SLCNUM_WHOLE_DISK)
64*57631629SToomas Soome 		return (0);
65*57631629SToomas Soome 
66753a6d45SSherry Moore 	return ((pos = strrchr(physpath, slice)) == NULL ||
67753a6d45SSherry Moore 	    pos[1] != 0 || pos[-1] != ':');
68753a6d45SSherry Moore }
69753a6d45SSherry Moore 
70753a6d45SSherry Moore /*
71753a6d45SSherry Moore  * Returns zero if path contains ufs
72753a6d45SSherry Moore  */
73753a6d45SSherry Moore static int
slice_ufs(const char * path)74753a6d45SSherry Moore slice_ufs(const char *path)
75753a6d45SSherry Moore {
76753a6d45SSherry Moore 	int fd, ret;
77753a6d45SSherry Moore 	const char *id;
78753a6d45SSherry Moore 	fstyp_handle_t hdl;
79753a6d45SSherry Moore 
80753a6d45SSherry Moore 	fd = open(path, O_RDONLY);
81753a6d45SSherry Moore 	if ((ret = fstyp_init(fd, 0, NULL, &hdl)) == 0) {
82753a6d45SSherry Moore 		ret = fstyp_ident(hdl, "ufs", &id);
83753a6d45SSherry Moore 		fstyp_fini(hdl);
84753a6d45SSherry Moore 	}
85753a6d45SSherry Moore 	(void) close(fd);
86753a6d45SSherry Moore 	return (ret);
87753a6d45SSherry Moore }
88753a6d45SSherry Moore 
89753a6d45SSherry Moore 
90753a6d45SSherry Moore static int
get_sol_prtnum(const char * physpath)91753a6d45SSherry Moore get_sol_prtnum(const char *physpath)
92753a6d45SSherry Moore {
93753a6d45SSherry Moore 	int i, fd;
94753a6d45SSherry Moore 	char *pos;
95753a6d45SSherry Moore 	size_t sz;
96753a6d45SSherry Moore 	struct mboot *mb;
97753a6d45SSherry Moore 	struct ipart *ipart;
98753a6d45SSherry Moore 	char boot_sect[512];
99753a6d45SSherry Moore 	char rdev[MAXNAMELEN];
10001f9868aSMarcel Telka #if defined(i386) || defined(__amd64)
10101f9868aSMarcel Telka 	ext_part_t *epp;
10201f9868aSMarcel Telka 	int ext_part_found = 0;
10301f9868aSMarcel Telka #endif
104753a6d45SSherry Moore 
105753a6d45SSherry Moore 	(void) snprintf(rdev, sizeof (rdev), "/devices%s,raw", physpath);
106753a6d45SSherry Moore 
107753a6d45SSherry Moore 	if ((pos = strrchr(rdev, ':')) == NULL)
108753a6d45SSherry Moore 		return (PRTNUM_INVALID);
109753a6d45SSherry Moore 
110*57631629SToomas Soome 	/*
111*57631629SToomas Soome 	 * first check for EFI partitioning, efi_alloc_and_read()
112*57631629SToomas Soome 	 * will return partition number.
113*57631629SToomas Soome 	 */
114*57631629SToomas Soome 	if ((fd = open(rdev, O_RDONLY|O_NDELAY)) >= 0) {
115*57631629SToomas Soome 		struct dk_gpt *vtoc;
116*57631629SToomas Soome 
117*57631629SToomas Soome 		if ((i = efi_alloc_and_read(fd, &vtoc)) >= 0) {
118*57631629SToomas Soome 			/* zfs is using V_USR */
119*57631629SToomas Soome 			if (vtoc->efi_parts[i].p_tag != V_USR)
120*57631629SToomas Soome 				i = PRTNUM_INVALID; /* error */
121*57631629SToomas Soome 			efi_free(vtoc);
122*57631629SToomas Soome 			(void) close(fd);
123*57631629SToomas Soome 			return (i);
124*57631629SToomas Soome 		}
125*57631629SToomas Soome 		(void) close(fd);
126*57631629SToomas Soome 	} else {
127*57631629SToomas Soome 		return (PRTNUM_INVALID);
128*57631629SToomas Soome 	}
129*57631629SToomas Soome 
130753a6d45SSherry Moore 	pos[1] = SLCNUM_WHOLE_DISK;
131753a6d45SSherry Moore 
132753a6d45SSherry Moore 	fd = open(rdev, O_RDONLY);
133753a6d45SSherry Moore 	sz = read(fd, boot_sect, sizeof (boot_sect));
134753a6d45SSherry Moore 	(void) close(fd);
135753a6d45SSherry Moore 
136753a6d45SSherry Moore 	if (sz != sizeof (boot_sect))
137753a6d45SSherry Moore 		return (PRTNUM_INVALID);
138753a6d45SSherry Moore 
139753a6d45SSherry Moore 	/* parse fdisk table */
140753a6d45SSherry Moore 	mb = (struct mboot *)(uintptr_t)boot_sect;
141753a6d45SSherry Moore 	ipart = (struct ipart *)(uintptr_t)mb->parts;
142753a6d45SSherry Moore 	for (i = 0; i < FD_NUMPART; ++i) {
143753a6d45SSherry Moore 		if (ipart[i].systid == SUNIXOS || ipart[i].systid == SUNIXOS2)
144753a6d45SSherry Moore 			return (i);
14501f9868aSMarcel Telka 
14601f9868aSMarcel Telka #if defined(i386) || defined(__amd64)
14701f9868aSMarcel Telka 		if (!fdisk_is_dos_extended(ipart[i].systid) ||
14801f9868aSMarcel Telka 		    (ext_part_found == 1))
14901f9868aSMarcel Telka 			continue;
15001f9868aSMarcel Telka 
15101f9868aSMarcel Telka 		ext_part_found = 1;
15201f9868aSMarcel Telka 
15301f9868aSMarcel Telka 		if (libfdisk_init(&epp, rdev, NULL, FDISK_READ_DISK) ==
15401f9868aSMarcel Telka 		    FDISK_SUCCESS) {
15501f9868aSMarcel Telka 			uint32_t begs, nums;
15601f9868aSMarcel Telka 			int pno;
15701f9868aSMarcel Telka 			int rval;
15801f9868aSMarcel Telka 
15901f9868aSMarcel Telka 			rval = fdisk_get_solaris_part(epp, &pno, &begs, &nums);
16001f9868aSMarcel Telka 
16101f9868aSMarcel Telka 			libfdisk_fini(&epp);
16201f9868aSMarcel Telka 
16301f9868aSMarcel Telka 			if (rval == FDISK_SUCCESS)
16401f9868aSMarcel Telka 				return (pno - 1);
16501f9868aSMarcel Telka 		}
16601f9868aSMarcel Telka #endif
167753a6d45SSherry Moore 	}
168753a6d45SSherry Moore 	return (PRTNUM_INVALID);
169753a6d45SSherry Moore }
170753a6d45SSherry Moore 
171753a6d45SSherry Moore /*
172753a6d45SSherry Moore  * Get physpath, topfs and bootfs for ZFS root dataset.
173753a6d45SSherry Moore  * Return 0 on success, non-zero (not errno) on failure.
174753a6d45SSherry Moore  */
175753a6d45SSherry Moore static int
get_zfs_root(zfs_handle_t * zfh,grub_fs_t * fs,grub_root_t * root)176753a6d45SSherry Moore get_zfs_root(zfs_handle_t *zfh, grub_fs_t *fs, grub_root_t *root)
177753a6d45SSherry Moore {
178753a6d45SSherry Moore 	int ret;
179753a6d45SSherry Moore 	zpool_handle_t *zph;
180753a6d45SSherry Moore 	const char *name;
181753a6d45SSherry Moore 
182753a6d45SSherry Moore 	if (zfs_get_type(zfh) != ZFS_TYPE_FILESYSTEM ||
183753a6d45SSherry Moore 	    (name = zfs_get_name(zfh)) == NULL ||
184753a6d45SSherry Moore 	    (zph = zpool_open(fs->gf_lzfh, name)) == NULL)
185753a6d45SSherry Moore 		return (-1);
186753a6d45SSherry Moore 
187753a6d45SSherry Moore 	if ((ret = zpool_get_physpath(zph, root->gr_physpath,
188753a6d45SSherry Moore 	    sizeof (root->gr_physpath))) == 0 &&
189753a6d45SSherry Moore 	    (ret = zpool_get_prop(zph, ZPOOL_PROP_BOOTFS,
190753a6d45SSherry Moore 	    root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev,
191c58b3526SAdam Stevko 	    sizeof (root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev), NULL,
192c58b3526SAdam Stevko 	    B_FALSE)) == 0) {
193753a6d45SSherry Moore 
194753a6d45SSherry Moore 		(void) strlcpy(root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev, name,
195753a6d45SSherry Moore 		    sizeof (root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev));
196753a6d45SSherry Moore 		(void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_BOOTFS,
197753a6d45SSherry Moore 		    MNTTYPE_ZFS);
198753a6d45SSherry Moore 		(void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_TOPFS,
199753a6d45SSherry Moore 		    MNTTYPE_ZFS);
200753a6d45SSherry Moore 	}
201753a6d45SSherry Moore 
202753a6d45SSherry Moore 	zpool_close(zph);
203753a6d45SSherry Moore 	return (ret);
204753a6d45SSherry Moore }
205753a6d45SSherry Moore 
206753a6d45SSherry Moore /*
207753a6d45SSherry Moore  * On entry physpath parameter supposed to contain:
208753a6d45SSherry Moore  * <disk_physpath>[<space><disk_physpath>]*.
20901f9868aSMarcel Telka  * Retrieves first <disk_physpath> that matches both partition and slice.
210753a6d45SSherry Moore  * If any partition and slice is acceptable, first <disk_physpath> is returned.
211753a6d45SSherry Moore  */
212753a6d45SSherry Moore static int
get_one_physpath(char * physpath,uint_t prtnum,uint_t slcnum)213753a6d45SSherry Moore get_one_physpath(char *physpath, uint_t prtnum, uint_t slcnum)
214753a6d45SSherry Moore {
215753a6d45SSherry Moore 	int ret;
216753a6d45SSherry Moore 	char *tmp, *tok;
217753a6d45SSherry Moore 
218753a6d45SSherry Moore 	if (!IS_SLCNUM_VALID(slcnum) && !IS_PRTNUM_VALID(prtnum)) {
219753a6d45SSherry Moore 		(void) strtok(physpath, " ");
220753a6d45SSherry Moore 		return (0);
221753a6d45SSherry Moore 	}
222753a6d45SSherry Moore 
223753a6d45SSherry Moore 	if ((tmp = strdup(physpath)) == NULL)
224753a6d45SSherry Moore 		return (errno);
225753a6d45SSherry Moore 
226753a6d45SSherry Moore 	ret = ENODEV;
227753a6d45SSherry Moore 	for (tok = strtok(tmp, " "); tok != NULL; tok = strtok(NULL, " ")) {
228753a6d45SSherry Moore 		if ((ret = (slice_match(tok, slcnum) != 0 ||
229753a6d45SSherry Moore 		    get_sol_prtnum(tok) != prtnum)) == 0) {
230753a6d45SSherry Moore 			(void) strcpy(physpath, tok);
231753a6d45SSherry Moore 			break;
232753a6d45SSherry Moore 		}
233753a6d45SSherry Moore 	}
234753a6d45SSherry Moore 
235753a6d45SSherry Moore 	free(tmp);
2368c7cfd88SSherry Moore 	if (ret)
2378c7cfd88SSherry Moore 		ret = ENODEV;
238753a6d45SSherry Moore 	return (ret);
239753a6d45SSherry Moore }
240753a6d45SSherry Moore 
241753a6d45SSherry Moore static int
zfs_bootsign(zfs_handle_t * zfh,void * data)242753a6d45SSherry Moore zfs_bootsign(zfs_handle_t *zfh, void *data)
243753a6d45SSherry Moore {
244753a6d45SSherry Moore 	grub_barg_t *barg;
245753a6d45SSherry Moore 	grub_menu_t *menu;
246753a6d45SSherry Moore 	struct stat st;
247753a6d45SSherry Moore 	char path[MAXPATHLEN];
248753a6d45SSherry Moore 
249753a6d45SSherry Moore 	barg = (grub_barg_t *)data;
250753a6d45SSherry Moore 	menu = barg->gb_entry->ge_menu;
251753a6d45SSherry Moore 
252753a6d45SSherry Moore 	do {
253753a6d45SSherry Moore 		if (get_zfs_root(zfh, &menu->gm_fs, &barg->gb_root) != 0 ||
254753a6d45SSherry Moore 		    get_one_physpath(barg->gb_root.gr_physpath, barg->gb_prtnum,
255753a6d45SSherry Moore 		    barg->gb_slcnum) != 0)
256753a6d45SSherry Moore 			break;
257753a6d45SSherry Moore 
258753a6d45SSherry Moore 		/*
259753a6d45SSherry Moore 		 * if top zfs dataset is not mounted, mount it now
260753a6d45SSherry Moore 		 */
261753a6d45SSherry Moore 		if (barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp[0] == 0) {
262753a6d45SSherry Moore 			if (grub_fsd_mount_tmp(barg->gb_root.gr_fs +
263753a6d45SSherry Moore 			    GRBM_ZFS_TOPFS, MNTTYPE_ZFS) != 0)
264753a6d45SSherry Moore 				break;
265753a6d45SSherry Moore 		}
266753a6d45SSherry Moore 
267753a6d45SSherry Moore 		/* check that bootsign exists and it is a regular file */
268753a6d45SSherry Moore 		(void) snprintf(path, sizeof (path), "%s%s",
269753a6d45SSherry Moore 		    barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp,
270753a6d45SSherry Moore 		    barg->gb_bootsign);
271753a6d45SSherry Moore 
272753a6d45SSherry Moore 		if (lstat(path, &st) != 0 || S_ISREG(st.st_mode) == 0 ||
273753a6d45SSherry Moore 		    (st.st_mode & S_IRUSR) == 0)
274753a6d45SSherry Moore 			break;
275753a6d45SSherry Moore 
276753a6d45SSherry Moore 		(void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_ZFS,
277753a6d45SSherry Moore 		    sizeof (barg->gb_root.gr_fstyp));
278753a6d45SSherry Moore 		barg->gb_walkret = 0;
279753a6d45SSherry Moore 	/* LINTED: E_CONSTANT_CONDITION */
280753a6d45SSherry Moore 	} while (0);
281753a6d45SSherry Moore 
282753a6d45SSherry Moore 	grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_ZFS_TOPFS);
283753a6d45SSherry Moore 	zfs_close(zfh);
284753a6d45SSherry Moore 
285753a6d45SSherry Moore 	/* return non-zero to terminate the walk */
286753a6d45SSherry Moore 	return (barg->gb_walkret == 0);
287753a6d45SSherry Moore }
288753a6d45SSherry Moore 
289753a6d45SSherry Moore static int
get_devlink(di_devlink_t dl,void * arg)290753a6d45SSherry Moore get_devlink(di_devlink_t dl, void *arg)
291753a6d45SSherry Moore {
292753a6d45SSherry Moore 	const char *path;
293753a6d45SSherry Moore 	grub_barg_t *barg;
294753a6d45SSherry Moore 
295753a6d45SSherry Moore 	barg = (grub_barg_t *)arg;
296753a6d45SSherry Moore 	if ((path = di_devlink_path(dl)) != NULL)
297753a6d45SSherry Moore 		(void) strlcpy(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev, path,
298753a6d45SSherry Moore 		    sizeof (barg->gb_root.gr_fs[GRBM_UFS].gfs_dev));
299753a6d45SSherry Moore 	return (DI_WALK_TERMINATE);
300753a6d45SSherry Moore }
301753a6d45SSherry Moore 
302753a6d45SSherry Moore static int
ufs_bootsign_check(grub_barg_t * barg)303753a6d45SSherry Moore ufs_bootsign_check(grub_barg_t *barg)
304753a6d45SSherry Moore {
305753a6d45SSherry Moore 	int ret;
306753a6d45SSherry Moore 	struct stat st;
307753a6d45SSherry Moore 	grub_menu_t *mp;
308753a6d45SSherry Moore 	char path[MAXPATHLEN];
309753a6d45SSherry Moore 
310753a6d45SSherry Moore 	mp = barg->gb_entry->ge_menu;
311753a6d45SSherry Moore 
312753a6d45SSherry Moore 	/* get /dev/dsk link */
313753a6d45SSherry Moore 	if (di_devlink_walk(mp->gm_fs.gf_dvlh, "^dsk/",
314753a6d45SSherry Moore 	    barg->gb_root.gr_physpath, DI_PRIMARY_LINK, barg, get_devlink) != 0)
315753a6d45SSherry Moore 		return (errno);
316753a6d45SSherry Moore 	/*
317753a6d45SSherry Moore 	 * if disk is not mounted, mount it now
318753a6d45SSherry Moore 	 */
319753a6d45SSherry Moore 	if (grub_fsd_get_mountp(barg->gb_root.gr_fs + GRBM_UFS,
320753a6d45SSherry Moore 	    MNTTYPE_UFS) != 0) {
321753a6d45SSherry Moore 		if ((ret =
322753a6d45SSherry Moore 		    slice_ufs(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)) != 0 ||
323753a6d45SSherry Moore 		    (ret = grub_fsd_mount_tmp(barg->gb_root.gr_fs + GRBM_UFS,
324753a6d45SSherry Moore 		    MNTTYPE_UFS)) != 0)
325753a6d45SSherry Moore 			return (ret);
326753a6d45SSherry Moore 	}
327753a6d45SSherry Moore 
328753a6d45SSherry Moore 	(void) snprintf(path, sizeof (path), "%s%s",
329753a6d45SSherry Moore 	    barg->gb_root.gr_fs[GRBM_UFS].gfs_mountp, barg->gb_bootsign);
330753a6d45SSherry Moore 
331753a6d45SSherry Moore 	if (lstat(path, &st) == 0 && S_ISREG(st.st_mode) &&
332753a6d45SSherry Moore 	    (st.st_mode & S_IRUSR) != 0) {
333753a6d45SSherry Moore 		barg->gb_walkret = 0;
334753a6d45SSherry Moore 		(void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_UFS,
335753a6d45SSherry Moore 		    sizeof (barg->gb_root.gr_fstyp));
336753a6d45SSherry Moore 	}
337753a6d45SSherry Moore 
338753a6d45SSherry Moore 	grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_UFS);
339753a6d45SSherry Moore 	return (barg->gb_walkret);
340753a6d45SSherry Moore }
341753a6d45SSherry Moore 
342753a6d45SSherry Moore static int
ufs_bootsign(di_node_t node,di_minor_t minor,void * arg)343753a6d45SSherry Moore ufs_bootsign(di_node_t node, di_minor_t minor, void *arg)
344753a6d45SSherry Moore {
345753a6d45SSherry Moore 	uint_t prtnum;
346753a6d45SSherry Moore 	char *name, *path;
347753a6d45SSherry Moore 	grub_barg_t *barg;
348753a6d45SSherry Moore 
349753a6d45SSherry Moore 	barg = (grub_barg_t *)arg;
350753a6d45SSherry Moore 
351753a6d45SSherry Moore 	if (di_minor_spectype(minor) != S_IFBLK)
352753a6d45SSherry Moore 		return (DI_WALK_CONTINUE);
353753a6d45SSherry Moore 
354753a6d45SSherry Moore 	name = di_minor_name(minor);
355753a6d45SSherry Moore 	if (name[0] != barg->gb_slcnum || name[1] != 0)
356753a6d45SSherry Moore 		return (DI_WALK_CONTINUE);
357753a6d45SSherry Moore 
358753a6d45SSherry Moore 	path = di_devfs_path(node);
359753a6d45SSherry Moore 	(void) snprintf(barg->gb_root.gr_physpath,
360753a6d45SSherry Moore 	    sizeof (barg->gb_root.gr_physpath), "%s:%c", path, barg->gb_slcnum);
361753a6d45SSherry Moore 	di_devfs_path_free(path);
362753a6d45SSherry Moore 
363753a6d45SSherry Moore 	prtnum = get_sol_prtnum(barg->gb_root.gr_physpath);
364753a6d45SSherry Moore 	if (!IS_PRTNUM_VALID(prtnum))
365753a6d45SSherry Moore 		return (DI_WALK_CONTINUE);
366753a6d45SSherry Moore 
367753a6d45SSherry Moore 	/*
368753a6d45SSherry Moore 	 * check only specified partition, slice
369753a6d45SSherry Moore 	 */
370753a6d45SSherry Moore 
371753a6d45SSherry Moore 	if (IS_PRTNUM_VALID(barg->gb_prtnum)) {
372753a6d45SSherry Moore 		if (prtnum != barg->gb_prtnum || ufs_bootsign_check(barg) != 0)
373753a6d45SSherry Moore 			return (DI_WALK_CONTINUE);
374753a6d45SSherry Moore 		return (DI_WALK_TERMINATE);
375753a6d45SSherry Moore 	}
376753a6d45SSherry Moore 
377753a6d45SSherry Moore 	/*
378753a6d45SSherry Moore 	 * Walk through all slices in found solaris partition
379753a6d45SSherry Moore 	 */
380753a6d45SSherry Moore 
381753a6d45SSherry Moore 	barg->gb_prtnum = prtnum;
382753a6d45SSherry Moore 	minor = DI_MINOR_NIL;
383753a6d45SSherry Moore 
384753a6d45SSherry Moore 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
385753a6d45SSherry Moore 
386753a6d45SSherry Moore 		if (di_minor_spectype(minor) != S_IFBLK)
387753a6d45SSherry Moore 			continue;
388753a6d45SSherry Moore 
389753a6d45SSherry Moore 		name = di_minor_name(minor);
390753a6d45SSherry Moore 		if (!IS_SLCNUM_VALID(name[0]) || name[1] != 0)
391753a6d45SSherry Moore 			continue;
392753a6d45SSherry Moore 
393753a6d45SSherry Moore 		barg->gb_slcnum = name[0];
394753a6d45SSherry Moore 		path = strrchr(barg->gb_root.gr_physpath, ':');
395753a6d45SSherry Moore 		path[1] = barg->gb_slcnum;
396753a6d45SSherry Moore 
397753a6d45SSherry Moore 		if (ufs_bootsign_check(barg) == 0)
398753a6d45SSherry Moore 			return (DI_WALK_TERMINATE);
399753a6d45SSherry Moore 	}
400753a6d45SSherry Moore 
401753a6d45SSherry Moore 	barg->gb_prtnum = (uint_t)PRTNUM_INVALID;
402753a6d45SSherry Moore 	barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK;
403753a6d45SSherry Moore 	return (DI_WALK_CONTINUE);
404753a6d45SSherry Moore }
405753a6d45SSherry Moore 
406753a6d45SSherry Moore /*
407753a6d45SSherry Moore  * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios
408753a6d45SSherry Moore  * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root,
409753a6d45SSherry Moore  * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found
410753a6d45SSherry Moore  * on other slices.
411753a6d45SSherry Moore  * That function first searches through all top datasets of active zpools,
412753a6d45SSherry Moore  * then if bootsign still not found walks through all disks and tries to
413753a6d45SSherry Moore  * find ufs slice with the bootsign.
414753a6d45SSherry Moore  */
415753a6d45SSherry Moore int
grub_find_bootsign(grub_barg_t * barg)416753a6d45SSherry Moore grub_find_bootsign(grub_barg_t *barg)
417753a6d45SSherry Moore {
418753a6d45SSherry Moore 	grub_menu_t *mp;
419753a6d45SSherry Moore 	mp = barg->gb_entry->ge_menu;
420753a6d45SSherry Moore 
421753a6d45SSherry Moore 	/* try to find bootsign over zfs pools */
422753a6d45SSherry Moore 	barg->gb_walkret = EG_BOOTSIGN;
423753a6d45SSherry Moore 	(void) zfs_iter_root(mp->gm_fs.gf_lzfh, zfs_bootsign, barg);
424753a6d45SSherry Moore 
425753a6d45SSherry Moore 	/* try ufs now */
426753a6d45SSherry Moore 	if (barg->gb_walkret != 0 && di_walk_minor(mp->gm_fs.gf_diroot,
427753a6d45SSherry Moore 	    DDI_NT_BLOCK, 0, barg, ufs_bootsign) != 0)
428753a6d45SSherry Moore 		return (errno);
429753a6d45SSherry Moore 
430753a6d45SSherry Moore 	return (barg->gb_walkret);
431753a6d45SSherry Moore }
432753a6d45SSherry Moore 
433753a6d45SSherry Moore /*
434753a6d45SSherry Moore  * Get current root file system.
435753a6d45SSherry Moore  * Return 0 on success, errno code on failure.
436753a6d45SSherry Moore  */
437753a6d45SSherry Moore int
grub_current_root(grub_fs_t * fs,grub_root_t * root)438753a6d45SSherry Moore grub_current_root(grub_fs_t *fs, grub_root_t *root)
439753a6d45SSherry Moore {
440753a6d45SSherry Moore 	int rc = 0;
441753a6d45SSherry Moore 	FILE *fp = NULL;
442753a6d45SSherry Moore 	char *name = NULL;
443753a6d45SSherry Moore 	zfs_handle_t *zfh = NULL;
444753a6d45SSherry Moore 	struct mnttab mp = {0};
445753a6d45SSherry Moore 	struct mnttab mpref = {0};
446753a6d45SSherry Moore 	char buf[MAXNAMELEN] = {0};
447753a6d45SSherry Moore 
448753a6d45SSherry Moore 	mpref.mnt_mountp = "/";
449753a6d45SSherry Moore 
450753a6d45SSherry Moore 	if ((fp = fopen(MNTTAB, "r")) == NULL)
451753a6d45SSherry Moore 		return (errno);
452753a6d45SSherry Moore 
453753a6d45SSherry Moore 	/*
454753a6d45SSherry Moore 	 * getmntany returns non-zero for failure, and sets errno
455753a6d45SSherry Moore 	 */
456753a6d45SSherry Moore 	rc = getmntany(fp, &mp, &mpref);
457753a6d45SSherry Moore 	if (rc != 0)
458753a6d45SSherry Moore 		rc = errno;
459753a6d45SSherry Moore 
460753a6d45SSherry Moore 	(void) fclose(fp);
461753a6d45SSherry Moore 
462753a6d45SSherry Moore 	if (rc != 0)
463753a6d45SSherry Moore 		return (rc);
464753a6d45SSherry Moore 
465753a6d45SSherry Moore 	(void) strlcpy(root->gr_fstyp, mp.mnt_fstype, sizeof (root->gr_fstyp));
466753a6d45SSherry Moore 
467753a6d45SSherry Moore 	if (strcmp(root->gr_fstyp, MNTTYPE_ZFS) == 0) {
468753a6d45SSherry Moore 
469753a6d45SSherry Moore 		(void) strlcpy(buf, mp.mnt_special, sizeof (buf));
470753a6d45SSherry Moore 		if ((name = strtok(buf, "/")) == NULL)
471753a6d45SSherry Moore 			return (EG_CURROOT);
472753a6d45SSherry Moore 
473753a6d45SSherry Moore 		if ((zfh = zfs_open(fs->gf_lzfh, name, ZFS_TYPE_FILESYSTEM)) ==
474753a6d45SSherry Moore 		    NULL)
475753a6d45SSherry Moore 			return (EG_OPENZFS);
476753a6d45SSherry Moore 
477753a6d45SSherry Moore 		/*
4788c7cfd88SSherry Moore 		 * get_zfs_root returns non-zero on failure, not errno.
479753a6d45SSherry Moore 		 */
480753a6d45SSherry Moore 		if (get_zfs_root(zfh, fs, root))
481753a6d45SSherry Moore 			rc = EG_CURROOT;
4828c7cfd88SSherry Moore 		else
4838c7cfd88SSherry Moore 			/*
4848c7cfd88SSherry Moore 			 * For mirrored root physpath would contain the list of
4858c7cfd88SSherry Moore 			 * all bootable devices, pick up the first one.
4868c7cfd88SSherry Moore 			 */
4878c7cfd88SSherry Moore 			rc = get_one_physpath(root->gr_physpath, SLCNUM_INVALID,
4888c7cfd88SSherry Moore 			    PRTNUM_INVALID);
489753a6d45SSherry Moore 
490753a6d45SSherry Moore 		zfs_close(zfh);
491753a6d45SSherry Moore 
492753a6d45SSherry Moore 	} else if (strcmp(mp.mnt_fstype, MNTTYPE_UFS) == 0) {
493753a6d45SSherry Moore 		(void) strlcpy(root->gr_fs[GRBM_UFS].gfs_dev, mp.mnt_special,
494753a6d45SSherry Moore 		    sizeof (root->gr_fs[GRBM_UFS].gfs_dev));
495753a6d45SSherry Moore 		(void) strlcpy(root->gr_fs[GRBM_UFS].gfs_mountp, mp.mnt_mountp,
496753a6d45SSherry Moore 		    sizeof (root->gr_fs[GRBM_UFS].gfs_mountp));
497753a6d45SSherry Moore 	} else {
498753a6d45SSherry Moore 		rc = EG_UNKNOWNFS;
499753a6d45SSherry Moore 	}
500753a6d45SSherry Moore 
501753a6d45SSherry Moore 	return (rc);
502753a6d45SSherry Moore }
503753a6d45SSherry Moore 
504753a6d45SSherry Moore grub_fsdesc_t *
grub_get_rootfsd(const grub_root_t * root)505753a6d45SSherry Moore grub_get_rootfsd(const grub_root_t *root)
506753a6d45SSherry Moore {
507753a6d45SSherry Moore 	grub_fsdesc_t *fsd = NULL;
508753a6d45SSherry Moore 
509753a6d45SSherry Moore 	assert(root);
510753a6d45SSherry Moore 	if (strcmp(MNTTYPE_UFS, root->gr_fstyp) == 0)
511753a6d45SSherry Moore 		fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_UFS;
512753a6d45SSherry Moore 	else if (strcmp(MNTTYPE_ZFS, root->gr_fstyp) == 0)
513753a6d45SSherry Moore 		fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_ZFS_BOOTFS;
514753a6d45SSherry Moore 
515753a6d45SSherry Moore 	return (fsd);
516753a6d45SSherry Moore }
517753a6d45SSherry Moore 
518753a6d45SSherry Moore /*
519753a6d45SSherry Moore  * Gets file systems mount point if any.
520753a6d45SSherry Moore  * Return 0 if filesystem is mounted, errno on failure.
521753a6d45SSherry Moore  */
522753a6d45SSherry Moore int
grub_fsd_get_mountp(grub_fsdesc_t * fsd,char * fstyp)523753a6d45SSherry Moore grub_fsd_get_mountp(grub_fsdesc_t *fsd, char *fstyp)
524753a6d45SSherry Moore {
525753a6d45SSherry Moore 	int rc;
526753a6d45SSherry Moore 	FILE *fp = NULL;
527753a6d45SSherry Moore 	struct mnttab mp = {0};
528753a6d45SSherry Moore 	struct mnttab mpref = {0};
529753a6d45SSherry Moore 
530753a6d45SSherry Moore 	fsd->gfs_mountp[0] = 0;
531753a6d45SSherry Moore 
532753a6d45SSherry Moore 	if ((fp = fopen(MNTTAB, "r")) == NULL)
533753a6d45SSherry Moore 		return (errno);
534753a6d45SSherry Moore 
535753a6d45SSherry Moore 	mpref.mnt_special = fsd->gfs_dev;
536753a6d45SSherry Moore 	mpref.mnt_fstype = fstyp;
537753a6d45SSherry Moore 
538753a6d45SSherry Moore 	if ((rc = getmntany(fp, &mp, &mpref)) == 0)
539753a6d45SSherry Moore 		(void) strlcpy(fsd->gfs_mountp, mp.mnt_mountp,
540753a6d45SSherry Moore 		    sizeof (fsd->gfs_mountp));
541753a6d45SSherry Moore 	else
542753a6d45SSherry Moore 		rc = EG_GETMNTTAB;
543753a6d45SSherry Moore 
544753a6d45SSherry Moore 	(void) fclose(fp);
545753a6d45SSherry Moore 	return (rc);
546753a6d45SSherry Moore }
547753a6d45SSherry Moore 
548753a6d45SSherry Moore static const char tmp_mountp[] = "/tmp/.libgrubmgmt.%s.XXXXXX";
549753a6d45SSherry Moore 
550753a6d45SSherry Moore /*
551753a6d45SSherry Moore  * Mount file system at tmp_mountp.
552753a6d45SSherry Moore  * Return 0 on success, errno on failure.
553753a6d45SSherry Moore  */
554753a6d45SSherry Moore int
grub_fsd_mount_tmp(grub_fsdesc_t * fsd,const char * fstyp)555753a6d45SSherry Moore grub_fsd_mount_tmp(grub_fsdesc_t *fsd, const char *fstyp)
556753a6d45SSherry Moore {
557753a6d45SSherry Moore 	const char *pos;
558753a6d45SSherry Moore 	void *data = NULL;
559753a6d45SSherry Moore 	int dtsz = 0;
560753a6d45SSherry Moore 	struct ufs_args ufs_args = {UFSMNT_LARGEFILES};
561753a6d45SSherry Moore 	char mntopts[MNT_LINE_MAX] = "";
562753a6d45SSherry Moore 	int rc = 0;
563753a6d45SSherry Moore 
564753a6d45SSherry Moore 	assert(fsd);
565753a6d45SSherry Moore 	assert(!fsd->gfs_is_tmp_mounted);
566753a6d45SSherry Moore 
567753a6d45SSherry Moore 	fsd->gfs_mountp[0] = 0;
568753a6d45SSherry Moore 
569753a6d45SSherry Moore 	if (strcmp(fstyp, MNTTYPE_UFS) == 0) {
570753a6d45SSherry Moore 		(void) strlcpy(mntopts, MNTOPT_LARGEFILES, sizeof (mntopts));
571753a6d45SSherry Moore 		data = &ufs_args;
572753a6d45SSherry Moore 		dtsz = sizeof (ufs_args);
573753a6d45SSherry Moore 	} else if (strcmp(fstyp, MNTTYPE_ZFS) != 0) {
574753a6d45SSherry Moore 		return (EG_UNKNOWNFS);
575753a6d45SSherry Moore 	}
576753a6d45SSherry Moore 
577753a6d45SSherry Moore 	/* construct name for temporary mount point */
578753a6d45SSherry Moore 	pos = strrchr(fsd->gfs_dev, '/');
579753a6d45SSherry Moore 	pos = (pos == NULL) ? fsd->gfs_dev : pos + 1;
580753a6d45SSherry Moore 
581753a6d45SSherry Moore 	(void) snprintf(fsd->gfs_mountp, sizeof (fsd->gfs_mountp),
582753a6d45SSherry Moore 	    tmp_mountp, pos);
583753a6d45SSherry Moore 	if (mkdtemp(fsd->gfs_mountp) != NULL) {
584753a6d45SSherry Moore 		if ((rc = mount(fsd->gfs_dev, fsd->gfs_mountp,
585753a6d45SSherry Moore 		    MS_DATA | MS_OPTIONSTR | MS_RDONLY,
586753a6d45SSherry Moore 		    fstyp, data, dtsz, mntopts, sizeof (mntopts))) != 0) {
587753a6d45SSherry Moore 			/*
588753a6d45SSherry Moore 			 * mount failed, collect errno and remove temp dir
589753a6d45SSherry Moore 			 */
590753a6d45SSherry Moore 			rc = errno;
591753a6d45SSherry Moore 			(void) rmdir(fsd->gfs_mountp);
592753a6d45SSherry Moore 		}
593753a6d45SSherry Moore 	} else {
594753a6d45SSherry Moore 		rc = errno;
595753a6d45SSherry Moore 	}
596753a6d45SSherry Moore 
597753a6d45SSherry Moore 	if (rc != 0)
598753a6d45SSherry Moore 		fsd->gfs_mountp[0] = 0;
599753a6d45SSherry Moore 
600753a6d45SSherry Moore 	/*
601753a6d45SSherry Moore 	 * Note that valid values for gfs_is_tmp_mounted are 0,1.
602753a6d45SSherry Moore 	 * Any other value indicates that something bad happened.
603753a6d45SSherry Moore 	 * Probably grub_fsd_umount_tmp() wasn't called or didn't
604753a6d45SSherry Moore 	 * work as expected.
605753a6d45SSherry Moore 	 */
606753a6d45SSherry Moore 	fsd->gfs_is_tmp_mounted += (rc == 0);
607753a6d45SSherry Moore 	return (rc);
608753a6d45SSherry Moore }
609753a6d45SSherry Moore 
610753a6d45SSherry Moore /*
611753a6d45SSherry Moore  * Unmount file system at tmp_mountp.
612753a6d45SSherry Moore  */
613753a6d45SSherry Moore void
grub_fsd_umount_tmp(grub_fsdesc_t * fsd)614753a6d45SSherry Moore grub_fsd_umount_tmp(grub_fsdesc_t *fsd)
615753a6d45SSherry Moore {
616753a6d45SSherry Moore 	if (fsd == NULL)
617753a6d45SSherry Moore 		return;
618753a6d45SSherry Moore 
619753a6d45SSherry Moore 	if (fsd->gfs_is_tmp_mounted) {
620753a6d45SSherry Moore 		if (fsd->gfs_mountp[0] != 0) {
621753a6d45SSherry Moore 			(void) umount2(fsd->gfs_mountp, 0);
622753a6d45SSherry Moore 			(void) rmdir(fsd->gfs_mountp);
623753a6d45SSherry Moore 			fsd->gfs_mountp[0] = 0;
624753a6d45SSherry Moore 		}
625753a6d45SSherry Moore 		fsd->gfs_is_tmp_mounted = 0;
626753a6d45SSherry Moore 	}
627753a6d45SSherry Moore }
628