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  */
25*01f9868aSMarcel Telka /*
26*01f9868aSMarcel Telka  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
27*01f9868aSMarcel Telka  */
28753a6d45SSherry Moore 
29753a6d45SSherry Moore /*
30*01f9868aSMarcel Telka  * This file contains all the functions that manipulate the file
31753a6d45SSherry Moore  * system where the GRUB menu resides.
32753a6d45SSherry Moore  */
33753a6d45SSherry Moore #include <stdio.h>
34753a6d45SSherry Moore #include <errno.h>
35753a6d45SSherry Moore #include <stdlib.h>
36753a6d45SSherry Moore #include <strings.h>
37753a6d45SSherry Moore #include <unistd.h>
38753a6d45SSherry Moore #include <fcntl.h>
39753a6d45SSherry Moore #include <assert.h>
40753a6d45SSherry Moore #include <sys/types.h>
41753a6d45SSherry Moore #include <sys/stat.h>
42753a6d45SSherry Moore #include <sys/mount.h>
43753a6d45SSherry Moore #include <sys/mntent.h>
44753a6d45SSherry Moore #include <sys/mnttab.h>
45753a6d45SSherry Moore #include <sys/fs/ufs_mount.h>
46753a6d45SSherry Moore #include <sys/dktp/fdisk.h>
47753a6d45SSherry Moore #include <libfstyp.h>
48*01f9868aSMarcel Telka #if defined(i386) || defined(__amd64)
49*01f9868aSMarcel Telka #include <libfdisk.h>
50*01f9868aSMarcel Telka #endif
51753a6d45SSherry Moore 
52753a6d45SSherry Moore #include "libgrub_impl.h"
53753a6d45SSherry Moore 
54753a6d45SSherry Moore static int
55753a6d45SSherry Moore slice_match(const char *physpath, int slice)
56753a6d45SSherry Moore {
57753a6d45SSherry Moore 	const char *pos;
58753a6d45SSherry Moore 
59753a6d45SSherry Moore 	return ((pos = strrchr(physpath, slice)) == NULL ||
60753a6d45SSherry Moore 	    pos[1] != 0 || pos[-1] != ':');
61753a6d45SSherry Moore }
62753a6d45SSherry Moore 
63753a6d45SSherry Moore /*
64753a6d45SSherry Moore  * Returns zero if path contains ufs
65753a6d45SSherry Moore  */
66753a6d45SSherry Moore static int
67753a6d45SSherry Moore slice_ufs(const char *path)
68753a6d45SSherry Moore {
69753a6d45SSherry Moore 	int fd, ret;
70753a6d45SSherry Moore 	const char *id;
71753a6d45SSherry Moore 	fstyp_handle_t hdl;
72753a6d45SSherry Moore 
73753a6d45SSherry Moore 	fd = open(path, O_RDONLY);
74753a6d45SSherry Moore 	if ((ret = fstyp_init(fd, 0, NULL, &hdl)) == 0) {
75753a6d45SSherry Moore 		ret = fstyp_ident(hdl, "ufs", &id);
76753a6d45SSherry Moore 		fstyp_fini(hdl);
77753a6d45SSherry Moore 	}
78753a6d45SSherry Moore 	(void) close(fd);
79753a6d45SSherry Moore 	return (ret);
80753a6d45SSherry Moore }
81753a6d45SSherry Moore 
82753a6d45SSherry Moore 
83753a6d45SSherry Moore static int
84753a6d45SSherry Moore get_sol_prtnum(const char *physpath)
85753a6d45SSherry Moore {
86753a6d45SSherry Moore 	int i, fd;
87753a6d45SSherry Moore 	char *pos;
88753a6d45SSherry Moore 	size_t sz;
89753a6d45SSherry Moore 	struct mboot *mb;
90753a6d45SSherry Moore 	struct ipart *ipart;
91753a6d45SSherry Moore 	char boot_sect[512];
92753a6d45SSherry Moore 	char rdev[MAXNAMELEN];
93*01f9868aSMarcel Telka #if defined(i386) || defined(__amd64)
94*01f9868aSMarcel Telka 	ext_part_t *epp;
95*01f9868aSMarcel Telka 	int ext_part_found = 0;
96*01f9868aSMarcel Telka #endif
97753a6d45SSherry Moore 
98753a6d45SSherry Moore 	(void) snprintf(rdev, sizeof (rdev), "/devices%s,raw", physpath);
99753a6d45SSherry Moore 
100753a6d45SSherry Moore 	if ((pos = strrchr(rdev, ':')) == NULL)
101753a6d45SSherry Moore 		return (PRTNUM_INVALID);
102753a6d45SSherry Moore 
103753a6d45SSherry Moore 	pos[1] = SLCNUM_WHOLE_DISK;
104753a6d45SSherry Moore 
105753a6d45SSherry Moore 	fd = open(rdev, O_RDONLY);
106753a6d45SSherry Moore 	sz = read(fd, boot_sect, sizeof (boot_sect));
107753a6d45SSherry Moore 	(void) close(fd);
108753a6d45SSherry Moore 
109753a6d45SSherry Moore 	if (sz != sizeof (boot_sect))
110753a6d45SSherry Moore 		return (PRTNUM_INVALID);
111753a6d45SSherry Moore 
112753a6d45SSherry Moore 	/* parse fdisk table */
113753a6d45SSherry Moore 	mb = (struct mboot *)(uintptr_t)boot_sect;
114753a6d45SSherry Moore 	ipart = (struct ipart *)(uintptr_t)mb->parts;
115753a6d45SSherry Moore 	for (i = 0; i < FD_NUMPART; ++i) {
116753a6d45SSherry Moore 		if (ipart[i].systid == SUNIXOS || ipart[i].systid == SUNIXOS2)
117753a6d45SSherry Moore 			return (i);
118*01f9868aSMarcel Telka 
119*01f9868aSMarcel Telka #if defined(i386) || defined(__amd64)
120*01f9868aSMarcel Telka 		if (!fdisk_is_dos_extended(ipart[i].systid) ||
121*01f9868aSMarcel Telka 		    (ext_part_found == 1))
122*01f9868aSMarcel Telka 			continue;
123*01f9868aSMarcel Telka 
124*01f9868aSMarcel Telka 		ext_part_found = 1;
125*01f9868aSMarcel Telka 
126*01f9868aSMarcel Telka 		if (libfdisk_init(&epp, rdev, NULL, FDISK_READ_DISK) ==
127*01f9868aSMarcel Telka 		    FDISK_SUCCESS) {
128*01f9868aSMarcel Telka 			uint32_t begs, nums;
129*01f9868aSMarcel Telka 			int pno;
130*01f9868aSMarcel Telka 			int rval;
131*01f9868aSMarcel Telka 
132*01f9868aSMarcel Telka 			rval = fdisk_get_solaris_part(epp, &pno, &begs, &nums);
133*01f9868aSMarcel Telka 
134*01f9868aSMarcel Telka 			libfdisk_fini(&epp);
135*01f9868aSMarcel Telka 
136*01f9868aSMarcel Telka 			if (rval == FDISK_SUCCESS)
137*01f9868aSMarcel Telka 				return (pno - 1);
138*01f9868aSMarcel Telka 		}
139*01f9868aSMarcel Telka #endif
140753a6d45SSherry Moore 	}
141753a6d45SSherry Moore 	return (PRTNUM_INVALID);
142753a6d45SSherry Moore }
143753a6d45SSherry Moore 
144753a6d45SSherry Moore /*
145753a6d45SSherry Moore  * Get physpath, topfs and bootfs for ZFS root dataset.
146753a6d45SSherry Moore  * Return 0 on success, non-zero (not errno) on failure.
147753a6d45SSherry Moore  */
148753a6d45SSherry Moore static int
149753a6d45SSherry Moore get_zfs_root(zfs_handle_t *zfh, grub_fs_t *fs, grub_root_t *root)
150753a6d45SSherry Moore {
151753a6d45SSherry Moore 	int ret;
152753a6d45SSherry Moore 	zpool_handle_t *zph;
153753a6d45SSherry Moore 	const char *name;
154753a6d45SSherry Moore 
155753a6d45SSherry Moore 	if (zfs_get_type(zfh) != ZFS_TYPE_FILESYSTEM ||
156753a6d45SSherry Moore 	    (name = zfs_get_name(zfh)) == NULL ||
157753a6d45SSherry Moore 	    (zph = zpool_open(fs->gf_lzfh, name)) == NULL)
158753a6d45SSherry Moore 		return (-1);
159753a6d45SSherry Moore 
160753a6d45SSherry Moore 	if ((ret = zpool_get_physpath(zph, root->gr_physpath,
161753a6d45SSherry Moore 	    sizeof (root->gr_physpath))) == 0 &&
162753a6d45SSherry Moore 	    (ret = zpool_get_prop(zph, ZPOOL_PROP_BOOTFS,
163753a6d45SSherry Moore 	    root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev,
164753a6d45SSherry Moore 	    sizeof (root->gr_fs[GRBM_ZFS_BOOTFS].gfs_dev), NULL)) == 0) {
165753a6d45SSherry Moore 
166753a6d45SSherry Moore 		(void) strlcpy(root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev, name,
167753a6d45SSherry Moore 		    sizeof (root->gr_fs[GRBM_ZFS_TOPFS].gfs_dev));
168753a6d45SSherry Moore 		(void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_BOOTFS,
169753a6d45SSherry Moore 		    MNTTYPE_ZFS);
170753a6d45SSherry Moore 		(void) grub_fsd_get_mountp(root->gr_fs + GRBM_ZFS_TOPFS,
171753a6d45SSherry Moore 		    MNTTYPE_ZFS);
172753a6d45SSherry Moore 	}
173753a6d45SSherry Moore 
174753a6d45SSherry Moore 	zpool_close(zph);
175753a6d45SSherry Moore 	return (ret);
176753a6d45SSherry Moore }
177753a6d45SSherry Moore 
178753a6d45SSherry Moore /*
179753a6d45SSherry Moore  * On entry physpath parameter supposed to contain:
180753a6d45SSherry Moore  * <disk_physpath>[<space><disk_physpath>]*.
181*01f9868aSMarcel Telka  * Retrieves first <disk_physpath> that matches both partition and slice.
182753a6d45SSherry Moore  * If any partition and slice is acceptable, first <disk_physpath> is returned.
183753a6d45SSherry Moore  */
184753a6d45SSherry Moore static int
185753a6d45SSherry Moore get_one_physpath(char *physpath, uint_t prtnum, uint_t slcnum)
186753a6d45SSherry Moore {
187753a6d45SSherry Moore 	int ret;
188753a6d45SSherry Moore 	char *tmp, *tok;
189753a6d45SSherry Moore 
190753a6d45SSherry Moore 	if (!IS_SLCNUM_VALID(slcnum) && !IS_PRTNUM_VALID(prtnum)) {
191753a6d45SSherry Moore 		(void) strtok(physpath, " ");
192753a6d45SSherry Moore 		return (0);
193753a6d45SSherry Moore 	}
194753a6d45SSherry Moore 
195753a6d45SSherry Moore 	if ((tmp = strdup(physpath)) == NULL)
196753a6d45SSherry Moore 		return (errno);
197753a6d45SSherry Moore 
198753a6d45SSherry Moore 	ret = ENODEV;
199753a6d45SSherry Moore 	for (tok = strtok(tmp, " "); tok != NULL; tok = strtok(NULL, " ")) {
200753a6d45SSherry Moore 		if ((ret = (slice_match(tok, slcnum) != 0 ||
201753a6d45SSherry Moore 		    get_sol_prtnum(tok) != prtnum)) == 0) {
202753a6d45SSherry Moore 			(void) strcpy(physpath, tok);
203753a6d45SSherry Moore 			break;
204753a6d45SSherry Moore 		}
205753a6d45SSherry Moore 	}
206753a6d45SSherry Moore 
207753a6d45SSherry Moore 	free(tmp);
2088c7cfd88SSherry Moore 	if (ret)
2098c7cfd88SSherry Moore 		ret = ENODEV;
210753a6d45SSherry Moore 	return (ret);
211753a6d45SSherry Moore }
212753a6d45SSherry Moore 
213753a6d45SSherry Moore static int
214753a6d45SSherry Moore zfs_bootsign(zfs_handle_t *zfh, void *data)
215753a6d45SSherry Moore {
216753a6d45SSherry Moore 	grub_barg_t *barg;
217753a6d45SSherry Moore 	grub_menu_t *menu;
218753a6d45SSherry Moore 	struct stat st;
219753a6d45SSherry Moore 	char path[MAXPATHLEN];
220753a6d45SSherry Moore 
221753a6d45SSherry Moore 	barg = (grub_barg_t *)data;
222753a6d45SSherry Moore 	menu = barg->gb_entry->ge_menu;
223753a6d45SSherry Moore 
224753a6d45SSherry Moore 	do {
225753a6d45SSherry Moore 		if (get_zfs_root(zfh, &menu->gm_fs, &barg->gb_root) != 0 ||
226753a6d45SSherry Moore 		    get_one_physpath(barg->gb_root.gr_physpath, barg->gb_prtnum,
227753a6d45SSherry Moore 		    barg->gb_slcnum) != 0)
228753a6d45SSherry Moore 			break;
229753a6d45SSherry Moore 
230753a6d45SSherry Moore 		/*
231753a6d45SSherry Moore 		 * if top zfs dataset is not mounted, mount it now
232753a6d45SSherry Moore 		 */
233753a6d45SSherry Moore 		if (barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp[0] == 0) {
234753a6d45SSherry Moore 			if (grub_fsd_mount_tmp(barg->gb_root.gr_fs +
235753a6d45SSherry Moore 			    GRBM_ZFS_TOPFS, MNTTYPE_ZFS) != 0)
236753a6d45SSherry Moore 				break;
237753a6d45SSherry Moore 		}
238753a6d45SSherry Moore 
239753a6d45SSherry Moore 		/* check that bootsign exists and it is a regular file */
240753a6d45SSherry Moore 		(void) snprintf(path, sizeof (path), "%s%s",
241753a6d45SSherry Moore 		    barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_mountp,
242753a6d45SSherry Moore 		    barg->gb_bootsign);
243753a6d45SSherry Moore 
244753a6d45SSherry Moore 		if (lstat(path, &st) != 0 || S_ISREG(st.st_mode) == 0 ||
245753a6d45SSherry Moore 		    (st.st_mode & S_IRUSR) == 0)
246753a6d45SSherry Moore 			break;
247753a6d45SSherry Moore 
248753a6d45SSherry Moore 		(void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_ZFS,
249753a6d45SSherry Moore 		    sizeof (barg->gb_root.gr_fstyp));
250753a6d45SSherry Moore 		barg->gb_walkret = 0;
251753a6d45SSherry Moore 	/* LINTED: E_CONSTANT_CONDITION */
252753a6d45SSherry Moore 	} while (0);
253753a6d45SSherry Moore 
254753a6d45SSherry Moore 	grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_ZFS_TOPFS);
255753a6d45SSherry Moore 	zfs_close(zfh);
256753a6d45SSherry Moore 
257753a6d45SSherry Moore 	/* return non-zero to terminate the walk */
258753a6d45SSherry Moore 	return (barg->gb_walkret == 0);
259753a6d45SSherry Moore }
260753a6d45SSherry Moore 
261753a6d45SSherry Moore static int
262753a6d45SSherry Moore get_devlink(di_devlink_t dl, void *arg)
263753a6d45SSherry Moore {
264753a6d45SSherry Moore 	const char *path;
265753a6d45SSherry Moore 	grub_barg_t *barg;
266753a6d45SSherry Moore 
267753a6d45SSherry Moore 	barg = (grub_barg_t *)arg;
268753a6d45SSherry Moore 	if ((path = di_devlink_path(dl)) != NULL)
269753a6d45SSherry Moore 		(void) strlcpy(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev, path,
270753a6d45SSherry Moore 		    sizeof (barg->gb_root.gr_fs[GRBM_UFS].gfs_dev));
271753a6d45SSherry Moore 	return (DI_WALK_TERMINATE);
272753a6d45SSherry Moore }
273753a6d45SSherry Moore 
274753a6d45SSherry Moore static int
275753a6d45SSherry Moore ufs_bootsign_check(grub_barg_t *barg)
276753a6d45SSherry Moore {
277753a6d45SSherry Moore 	int ret;
278753a6d45SSherry Moore 	struct stat st;
279753a6d45SSherry Moore 	grub_menu_t *mp;
280753a6d45SSherry Moore 	char path[MAXPATHLEN];
281753a6d45SSherry Moore 
282753a6d45SSherry Moore 	mp = barg->gb_entry->ge_menu;
283753a6d45SSherry Moore 
284753a6d45SSherry Moore 	/* get /dev/dsk link */
285753a6d45SSherry Moore 	if (di_devlink_walk(mp->gm_fs.gf_dvlh, "^dsk/",
286753a6d45SSherry Moore 	    barg->gb_root.gr_physpath, DI_PRIMARY_LINK, barg, get_devlink) != 0)
287753a6d45SSherry Moore 		return (errno);
288753a6d45SSherry Moore 	/*
289753a6d45SSherry Moore 	 * if disk is not mounted, mount it now
290753a6d45SSherry Moore 	 */
291753a6d45SSherry Moore 	if (grub_fsd_get_mountp(barg->gb_root.gr_fs + GRBM_UFS,
292753a6d45SSherry Moore 	    MNTTYPE_UFS) != 0) {
293753a6d45SSherry Moore 		if ((ret =
294753a6d45SSherry Moore 		    slice_ufs(barg->gb_root.gr_fs[GRBM_UFS].gfs_dev)) != 0 ||
295753a6d45SSherry Moore 		    (ret = grub_fsd_mount_tmp(barg->gb_root.gr_fs + GRBM_UFS,
296753a6d45SSherry Moore 		    MNTTYPE_UFS)) != 0)
297753a6d45SSherry Moore 			return (ret);
298753a6d45SSherry Moore 	}
299753a6d45SSherry Moore 
300753a6d45SSherry Moore 	(void) snprintf(path, sizeof (path), "%s%s",
301753a6d45SSherry Moore 	    barg->gb_root.gr_fs[GRBM_UFS].gfs_mountp, barg->gb_bootsign);
302753a6d45SSherry Moore 
303753a6d45SSherry Moore 	if (lstat(path, &st) == 0 && S_ISREG(st.st_mode) &&
304753a6d45SSherry Moore 	    (st.st_mode & S_IRUSR) != 0) {
305753a6d45SSherry Moore 		barg->gb_walkret = 0;
306753a6d45SSherry Moore 		(void) strlcpy(barg->gb_root.gr_fstyp, MNTTYPE_UFS,
307753a6d45SSherry Moore 		    sizeof (barg->gb_root.gr_fstyp));
308753a6d45SSherry Moore 	}
309753a6d45SSherry Moore 
310753a6d45SSherry Moore 	grub_fsd_umount_tmp(barg->gb_root.gr_fs + GRBM_UFS);
311753a6d45SSherry Moore 	return (barg->gb_walkret);
312753a6d45SSherry Moore }
313753a6d45SSherry Moore 
314753a6d45SSherry Moore static int
315753a6d45SSherry Moore ufs_bootsign(di_node_t node, di_minor_t minor, void *arg)
316753a6d45SSherry Moore {
317753a6d45SSherry Moore 	uint_t prtnum;
318753a6d45SSherry Moore 	char *name, *path;
319753a6d45SSherry Moore 	grub_barg_t *barg;
320753a6d45SSherry Moore 
321753a6d45SSherry Moore 	barg = (grub_barg_t *)arg;
322753a6d45SSherry Moore 
323753a6d45SSherry Moore 	if (di_minor_spectype(minor) != S_IFBLK)
324753a6d45SSherry Moore 		return (DI_WALK_CONTINUE);
325753a6d45SSherry Moore 
326753a6d45SSherry Moore 	name = di_minor_name(minor);
327753a6d45SSherry Moore 	if (name[0] != barg->gb_slcnum || name[1] != 0)
328753a6d45SSherry Moore 		return (DI_WALK_CONTINUE);
329753a6d45SSherry Moore 
330753a6d45SSherry Moore 	path = di_devfs_path(node);
331753a6d45SSherry Moore 	(void) snprintf(barg->gb_root.gr_physpath,
332753a6d45SSherry Moore 	    sizeof (barg->gb_root.gr_physpath), "%s:%c", path, barg->gb_slcnum);
333753a6d45SSherry Moore 	di_devfs_path_free(path);
334753a6d45SSherry Moore 
335753a6d45SSherry Moore 	prtnum = get_sol_prtnum(barg->gb_root.gr_physpath);
336753a6d45SSherry Moore 	if (!IS_PRTNUM_VALID(prtnum))
337753a6d45SSherry Moore 		return (DI_WALK_CONTINUE);
338753a6d45SSherry Moore 
339753a6d45SSherry Moore 	/*
340753a6d45SSherry Moore 	 * check only specified partition, slice
341753a6d45SSherry Moore 	 */
342753a6d45SSherry Moore 
343753a6d45SSherry Moore 	if (IS_PRTNUM_VALID(barg->gb_prtnum)) {
344753a6d45SSherry Moore 		if (prtnum != barg->gb_prtnum || ufs_bootsign_check(barg) != 0)
345753a6d45SSherry Moore 			return (DI_WALK_CONTINUE);
346753a6d45SSherry Moore 		return (DI_WALK_TERMINATE);
347753a6d45SSherry Moore 	}
348753a6d45SSherry Moore 
349753a6d45SSherry Moore 	/*
350753a6d45SSherry Moore 	 * Walk through all slices in found solaris partition
351753a6d45SSherry Moore 	 */
352753a6d45SSherry Moore 
353753a6d45SSherry Moore 	barg->gb_prtnum = prtnum;
354753a6d45SSherry Moore 	minor = DI_MINOR_NIL;
355753a6d45SSherry Moore 
356753a6d45SSherry Moore 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
357753a6d45SSherry Moore 
358753a6d45SSherry Moore 		if (di_minor_spectype(minor) != S_IFBLK)
359753a6d45SSherry Moore 			continue;
360753a6d45SSherry Moore 
361753a6d45SSherry Moore 		name = di_minor_name(minor);
362753a6d45SSherry Moore 		if (!IS_SLCNUM_VALID(name[0]) || name[1] != 0)
363753a6d45SSherry Moore 			continue;
364753a6d45SSherry Moore 
365753a6d45SSherry Moore 		barg->gb_slcnum = name[0];
366753a6d45SSherry Moore 		path = strrchr(barg->gb_root.gr_physpath, ':');
367753a6d45SSherry Moore 		path[1] = barg->gb_slcnum;
368753a6d45SSherry Moore 
369753a6d45SSherry Moore 		if (ufs_bootsign_check(barg) == 0)
370753a6d45SSherry Moore 			return (DI_WALK_TERMINATE);
371753a6d45SSherry Moore 	}
372753a6d45SSherry Moore 
373753a6d45SSherry Moore 	barg->gb_prtnum = (uint_t)PRTNUM_INVALID;
374753a6d45SSherry Moore 	barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK;
375753a6d45SSherry Moore 	return (DI_WALK_CONTINUE);
376753a6d45SSherry Moore }
377753a6d45SSherry Moore 
378753a6d45SSherry Moore /*
379753a6d45SSherry Moore  * Differs from what GRUB is doing: GRUB searchs through all disks seen by bios
380753a6d45SSherry Moore  * for bootsign, if bootsign is found on ufs slice GRUB sets it as a root,
381753a6d45SSherry Moore  * if on zfs, then GRUB uses zfs slice as root only if bootsign wasn't found
382753a6d45SSherry Moore  * on other slices.
383753a6d45SSherry Moore  * That function first searches through all top datasets of active zpools,
384753a6d45SSherry Moore  * then if bootsign still not found walks through all disks and tries to
385753a6d45SSherry Moore  * find ufs slice with the bootsign.
386753a6d45SSherry Moore  */
387753a6d45SSherry Moore int
388753a6d45SSherry Moore grub_find_bootsign(grub_barg_t *barg)
389753a6d45SSherry Moore {
390753a6d45SSherry Moore 	grub_menu_t *mp;
391753a6d45SSherry Moore 	mp = barg->gb_entry->ge_menu;
392753a6d45SSherry Moore 
393753a6d45SSherry Moore 	/* try to find bootsign over zfs pools */
394753a6d45SSherry Moore 	barg->gb_walkret = EG_BOOTSIGN;
395753a6d45SSherry Moore 	(void) zfs_iter_root(mp->gm_fs.gf_lzfh, zfs_bootsign, barg);
396753a6d45SSherry Moore 
397753a6d45SSherry Moore 	/* try ufs now */
398753a6d45SSherry Moore 	if (barg->gb_walkret != 0 && di_walk_minor(mp->gm_fs.gf_diroot,
399753a6d45SSherry Moore 	    DDI_NT_BLOCK, 0, barg, ufs_bootsign) != 0)
400753a6d45SSherry Moore 		return (errno);
401753a6d45SSherry Moore 
402753a6d45SSherry Moore 	return (barg->gb_walkret);
403753a6d45SSherry Moore }
404753a6d45SSherry Moore 
405753a6d45SSherry Moore /*
406753a6d45SSherry Moore  * Get current root file system.
407753a6d45SSherry Moore  * Return 0 on success, errno code on failure.
408753a6d45SSherry Moore  */
409753a6d45SSherry Moore int
410753a6d45SSherry Moore grub_current_root(grub_fs_t *fs, grub_root_t *root)
411753a6d45SSherry Moore {
412753a6d45SSherry Moore 	int rc = 0;
413753a6d45SSherry Moore 	FILE *fp = NULL;
414753a6d45SSherry Moore 	char *name = NULL;
415753a6d45SSherry Moore 	zfs_handle_t *zfh = NULL;
416753a6d45SSherry Moore 	struct mnttab mp = {0};
417753a6d45SSherry Moore 	struct mnttab mpref = {0};
418753a6d45SSherry Moore 	char buf[MAXNAMELEN] = {0};
419753a6d45SSherry Moore 
420753a6d45SSherry Moore 	mpref.mnt_mountp = "/";
421753a6d45SSherry Moore 
422753a6d45SSherry Moore 	if ((fp = fopen(MNTTAB, "r")) == NULL)
423753a6d45SSherry Moore 		return (errno);
424753a6d45SSherry Moore 
425753a6d45SSherry Moore 	/*
426753a6d45SSherry Moore 	 * getmntany returns non-zero for failure, and sets errno
427753a6d45SSherry Moore 	 */
428753a6d45SSherry Moore 	rc = getmntany(fp, &mp, &mpref);
429753a6d45SSherry Moore 	if (rc != 0)
430753a6d45SSherry Moore 		rc = errno;
431753a6d45SSherry Moore 
432753a6d45SSherry Moore 	(void) fclose(fp);
433753a6d45SSherry Moore 
434753a6d45SSherry Moore 	if (rc != 0)
435753a6d45SSherry Moore 		return (rc);
436753a6d45SSherry Moore 
437753a6d45SSherry Moore 	(void) strlcpy(root->gr_fstyp, mp.mnt_fstype, sizeof (root->gr_fstyp));
438753a6d45SSherry Moore 
439753a6d45SSherry Moore 	if (strcmp(root->gr_fstyp, MNTTYPE_ZFS) == 0) {
440753a6d45SSherry Moore 
441753a6d45SSherry Moore 		(void) strlcpy(buf, mp.mnt_special, sizeof (buf));
442753a6d45SSherry Moore 		if ((name = strtok(buf, "/")) == NULL)
443753a6d45SSherry Moore 			return (EG_CURROOT);
444753a6d45SSherry Moore 
445753a6d45SSherry Moore 		if ((zfh = zfs_open(fs->gf_lzfh, name, ZFS_TYPE_FILESYSTEM)) ==
446753a6d45SSherry Moore 		    NULL)
447753a6d45SSherry Moore 			return (EG_OPENZFS);
448753a6d45SSherry Moore 
449753a6d45SSherry Moore 		/*
4508c7cfd88SSherry Moore 		 * get_zfs_root returns non-zero on failure, not errno.
451753a6d45SSherry Moore 		 */
452753a6d45SSherry Moore 		if (get_zfs_root(zfh, fs, root))
453753a6d45SSherry Moore 			rc = EG_CURROOT;
4548c7cfd88SSherry Moore 		else
4558c7cfd88SSherry Moore 			/*
4568c7cfd88SSherry Moore 			 * For mirrored root physpath would contain the list of
4578c7cfd88SSherry Moore 			 * all bootable devices, pick up the first one.
4588c7cfd88SSherry Moore 			 */
4598c7cfd88SSherry Moore 			rc = get_one_physpath(root->gr_physpath, SLCNUM_INVALID,
4608c7cfd88SSherry Moore 			    PRTNUM_INVALID);
461753a6d45SSherry Moore 
462753a6d45SSherry Moore 		zfs_close(zfh);
463753a6d45SSherry Moore 
464753a6d45SSherry Moore 	} else if (strcmp(mp.mnt_fstype, MNTTYPE_UFS) == 0) {
465753a6d45SSherry Moore 		(void) strlcpy(root->gr_fs[GRBM_UFS].gfs_dev, mp.mnt_special,
466753a6d45SSherry Moore 		    sizeof (root->gr_fs[GRBM_UFS].gfs_dev));
467753a6d45SSherry Moore 		(void) strlcpy(root->gr_fs[GRBM_UFS].gfs_mountp, mp.mnt_mountp,
468753a6d45SSherry Moore 		    sizeof (root->gr_fs[GRBM_UFS].gfs_mountp));
469753a6d45SSherry Moore 	} else {
470753a6d45SSherry Moore 		rc = EG_UNKNOWNFS;
471753a6d45SSherry Moore 	}
472753a6d45SSherry Moore 
473753a6d45SSherry Moore 	return (rc);
474753a6d45SSherry Moore }
475753a6d45SSherry Moore 
476753a6d45SSherry Moore grub_fsdesc_t *
477753a6d45SSherry Moore grub_get_rootfsd(const grub_root_t *root)
478753a6d45SSherry Moore {
479753a6d45SSherry Moore 	grub_fsdesc_t *fsd = NULL;
480753a6d45SSherry Moore 
481753a6d45SSherry Moore 	assert(root);
482753a6d45SSherry Moore 	if (strcmp(MNTTYPE_UFS, root->gr_fstyp) == 0)
483753a6d45SSherry Moore 		fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_UFS;
484753a6d45SSherry Moore 	else if (strcmp(MNTTYPE_ZFS, root->gr_fstyp) == 0)
485753a6d45SSherry Moore 		fsd = (grub_fsdesc_t *)root->gr_fs + GRBM_ZFS_BOOTFS;
486753a6d45SSherry Moore 
487753a6d45SSherry Moore 	return (fsd);
488753a6d45SSherry Moore }
489753a6d45SSherry Moore 
490753a6d45SSherry Moore /*
491753a6d45SSherry Moore  * Gets file systems mount point if any.
492753a6d45SSherry Moore  * Return 0 if filesystem is mounted, errno on failure.
493753a6d45SSherry Moore  */
494753a6d45SSherry Moore int
495753a6d45SSherry Moore grub_fsd_get_mountp(grub_fsdesc_t *fsd, char *fstyp)
496753a6d45SSherry Moore {
497753a6d45SSherry Moore 	int rc;
498753a6d45SSherry Moore 	FILE *fp = NULL;
499753a6d45SSherry Moore 	struct mnttab mp = {0};
500753a6d45SSherry Moore 	struct mnttab mpref = {0};
501753a6d45SSherry Moore 
502753a6d45SSherry Moore 	fsd->gfs_mountp[0] = 0;
503753a6d45SSherry Moore 
504753a6d45SSherry Moore 	if ((fp = fopen(MNTTAB, "r")) == NULL)
505753a6d45SSherry Moore 		return (errno);
506753a6d45SSherry Moore 
507753a6d45SSherry Moore 	mpref.mnt_special = fsd->gfs_dev;
508753a6d45SSherry Moore 	mpref.mnt_fstype = fstyp;
509753a6d45SSherry Moore 
510753a6d45SSherry Moore 	if ((rc = getmntany(fp, &mp, &mpref)) == 0)
511753a6d45SSherry Moore 		(void) strlcpy(fsd->gfs_mountp, mp.mnt_mountp,
512753a6d45SSherry Moore 		    sizeof (fsd->gfs_mountp));
513753a6d45SSherry Moore 	else
514753a6d45SSherry Moore 		rc = EG_GETMNTTAB;
515753a6d45SSherry Moore 
516753a6d45SSherry Moore 	(void) fclose(fp);
517753a6d45SSherry Moore 	return (rc);
518753a6d45SSherry Moore }
519753a6d45SSherry Moore 
520753a6d45SSherry Moore static const char tmp_mountp[] = "/tmp/.libgrubmgmt.%s.XXXXXX";
521753a6d45SSherry Moore 
522753a6d45SSherry Moore /*
523753a6d45SSherry Moore  * Mount file system at tmp_mountp.
524753a6d45SSherry Moore  * Return 0 on success, errno on failure.
525753a6d45SSherry Moore  */
526753a6d45SSherry Moore int
527753a6d45SSherry Moore grub_fsd_mount_tmp(grub_fsdesc_t *fsd, const char *fstyp)
528753a6d45SSherry Moore {
529753a6d45SSherry Moore 	const char *pos;
530753a6d45SSherry Moore 	void *data = NULL;
531753a6d45SSherry Moore 	int dtsz = 0;
532753a6d45SSherry Moore 	struct ufs_args ufs_args = {UFSMNT_LARGEFILES};
533753a6d45SSherry Moore 	char mntopts[MNT_LINE_MAX] = "";
534753a6d45SSherry Moore 	int rc = 0;
535753a6d45SSherry Moore 
536753a6d45SSherry Moore 	assert(fsd);
537753a6d45SSherry Moore 	assert(!fsd->gfs_is_tmp_mounted);
538753a6d45SSherry Moore 
539753a6d45SSherry Moore 	fsd->gfs_mountp[0] = 0;
540753a6d45SSherry Moore 
541753a6d45SSherry Moore 	if (strcmp(fstyp, MNTTYPE_UFS) == 0) {
542753a6d45SSherry Moore 		(void) strlcpy(mntopts, MNTOPT_LARGEFILES, sizeof (mntopts));
543753a6d45SSherry Moore 		data = &ufs_args;
544753a6d45SSherry Moore 		dtsz = sizeof (ufs_args);
545753a6d45SSherry Moore 	} else if (strcmp(fstyp, MNTTYPE_ZFS) != 0) {
546753a6d45SSherry Moore 		return (EG_UNKNOWNFS);
547753a6d45SSherry Moore 	}
548753a6d45SSherry Moore 
549753a6d45SSherry Moore 	/* construct name for temporary mount point */
550753a6d45SSherry Moore 	pos = strrchr(fsd->gfs_dev, '/');
551753a6d45SSherry Moore 	pos = (pos == NULL) ? fsd->gfs_dev : pos + 1;
552753a6d45SSherry Moore 
553753a6d45SSherry Moore 	(void) snprintf(fsd->gfs_mountp, sizeof (fsd->gfs_mountp),
554753a6d45SSherry Moore 	    tmp_mountp, pos);
555753a6d45SSherry Moore 	if (mkdtemp(fsd->gfs_mountp) != NULL) {
556753a6d45SSherry Moore 		if ((rc = mount(fsd->gfs_dev, fsd->gfs_mountp,
557753a6d45SSherry Moore 		    MS_DATA | MS_OPTIONSTR | MS_RDONLY,
558753a6d45SSherry Moore 		    fstyp, data, dtsz, mntopts, sizeof (mntopts))) != 0) {
559753a6d45SSherry Moore 			/*
560753a6d45SSherry Moore 			 * mount failed, collect errno and remove temp dir
561753a6d45SSherry Moore 			 */
562753a6d45SSherry Moore 			rc = errno;
563753a6d45SSherry Moore 			(void) rmdir(fsd->gfs_mountp);
564753a6d45SSherry Moore 		}
565753a6d45SSherry Moore 	} else {
566753a6d45SSherry Moore 		rc = errno;
567753a6d45SSherry Moore 	}
568753a6d45SSherry Moore 
569753a6d45SSherry Moore 	if (rc != 0)
570753a6d45SSherry Moore 		fsd->gfs_mountp[0] = 0;
571753a6d45SSherry Moore 
572753a6d45SSherry Moore 	/*
573753a6d45SSherry Moore 	 * Note that valid values for gfs_is_tmp_mounted are 0,1.
574753a6d45SSherry Moore 	 * Any other value indicates that something bad happened.
575753a6d45SSherry Moore 	 * Probably grub_fsd_umount_tmp() wasn't called or didn't
576753a6d45SSherry Moore 	 * work as expected.
577753a6d45SSherry Moore 	 */
578753a6d45SSherry Moore 	fsd->gfs_is_tmp_mounted += (rc == 0);
579753a6d45SSherry Moore 	return (rc);
580753a6d45SSherry Moore }
581753a6d45SSherry Moore 
582753a6d45SSherry Moore /*
583753a6d45SSherry Moore  * Unmount file system at tmp_mountp.
584753a6d45SSherry Moore  */
585753a6d45SSherry Moore void
586753a6d45SSherry Moore grub_fsd_umount_tmp(grub_fsdesc_t *fsd)
587753a6d45SSherry Moore {
588753a6d45SSherry Moore 	if (fsd == NULL)
589753a6d45SSherry Moore 		return;
590753a6d45SSherry Moore 
591753a6d45SSherry Moore 	if (fsd->gfs_is_tmp_mounted) {
592753a6d45SSherry Moore 		if (fsd->gfs_mountp[0] != 0) {
593753a6d45SSherry Moore 			(void) umount2(fsd->gfs_mountp, 0);
594753a6d45SSherry Moore 			(void) rmdir(fsd->gfs_mountp);
595753a6d45SSherry Moore 			fsd->gfs_mountp[0] = 0;
596753a6d45SSherry Moore 		}
597753a6d45SSherry Moore 		fsd->gfs_is_tmp_mounted = 0;
598753a6d45SSherry Moore 	}
599753a6d45SSherry Moore }
600