xref: /freebsd/lib/libefivar/efivar-dp-xlate.c (revision 670a4056)
109ee5d8aSWarner Losh /*-
209ee5d8aSWarner Losh  * Copyright (c) 2017 Netflix, Inc.
309ee5d8aSWarner Losh  * All rights reserved.
409ee5d8aSWarner Losh  *
509ee5d8aSWarner Losh  * Redistribution and use in source and binary forms, with or without
609ee5d8aSWarner Losh  * modification, are permitted provided that the following conditions
709ee5d8aSWarner Losh  * are met:
809ee5d8aSWarner Losh  * 1. Redistributions of source code must retain the above copyright
909ee5d8aSWarner Losh  *    notice, this list of conditions and the following disclaimer
1009ee5d8aSWarner Losh  *    in this position and unchanged.
1109ee5d8aSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
1209ee5d8aSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
1309ee5d8aSWarner Losh  *    documentation and/or other materials provided with the distribution.
1409ee5d8aSWarner Losh  *
1509ee5d8aSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1609ee5d8aSWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1709ee5d8aSWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1809ee5d8aSWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1909ee5d8aSWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2009ee5d8aSWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2109ee5d8aSWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2209ee5d8aSWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2309ee5d8aSWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2409ee5d8aSWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2509ee5d8aSWarner Losh  */
2609ee5d8aSWarner Losh 
2709ee5d8aSWarner Losh #include <sys/cdefs.h>
2809ee5d8aSWarner Losh __FBSDID("$FreeBSD$");
2909ee5d8aSWarner Losh 
3009ee5d8aSWarner Losh #include <sys/param.h>
3109ee5d8aSWarner Losh #include <sys/ucred.h>
3209ee5d8aSWarner Losh #include <sys/mount.h>
3309ee5d8aSWarner Losh 
3409ee5d8aSWarner Losh #undef MAX
3509ee5d8aSWarner Losh #undef MIN
3609ee5d8aSWarner Losh 
3709ee5d8aSWarner Losh #include <assert.h>
3809ee5d8aSWarner Losh #include <efivar.h>
3909ee5d8aSWarner Losh #include <errno.h>
4009ee5d8aSWarner Losh #include <libgeom.h>
4109ee5d8aSWarner Losh #include <paths.h>
4209ee5d8aSWarner Losh #include <stdio.h>
4309ee5d8aSWarner Losh #include <string.h>
4409ee5d8aSWarner Losh 
4509ee5d8aSWarner Losh #include "efichar.h"
4609ee5d8aSWarner Losh 
4709ee5d8aSWarner Losh #include "efi-osdep.h"
4809ee5d8aSWarner Losh #include "efivar-dp.h"
4909ee5d8aSWarner Losh 
5009ee5d8aSWarner Losh #include "uefi-dplib.h"
5109ee5d8aSWarner Losh 
5209ee5d8aSWarner Losh #define MAX_DP_SANITY	4096		/* Biggest device path in bytes */
5309ee5d8aSWarner Losh #define MAX_DP_TEXT_LEN	4096		/* Longest string rep of dp */
5409ee5d8aSWarner Losh 
5509ee5d8aSWarner Losh #define	G_PART	"PART"
5609ee5d8aSWarner Losh #define	G_LABEL "LABEL"
5709ee5d8aSWarner Losh #define G_DISK	"DISK"
5809ee5d8aSWarner Losh 
5909ee5d8aSWarner Losh static const char *
6009ee5d8aSWarner Losh geom_pp_attr(struct gmesh *mesh, struct gprovider *pp, const char *attr)
6109ee5d8aSWarner Losh {
6209ee5d8aSWarner Losh 	struct gconfig *conf;
6309ee5d8aSWarner Losh 
6409ee5d8aSWarner Losh 	LIST_FOREACH(conf, &pp->lg_config, lg_config) {
6509ee5d8aSWarner Losh 		if (strcmp(conf->lg_name, attr) != 0)
6609ee5d8aSWarner Losh 			continue;
6709ee5d8aSWarner Losh 		return (conf->lg_val);
6809ee5d8aSWarner Losh 	}
6909ee5d8aSWarner Losh 	return (NULL);
7009ee5d8aSWarner Losh }
7109ee5d8aSWarner Losh 
7209ee5d8aSWarner Losh static struct gprovider *
7309ee5d8aSWarner Losh find_provider_by_efimedia(struct gmesh *mesh, const char *efimedia)
7409ee5d8aSWarner Losh {
7509ee5d8aSWarner Losh 	struct gclass *classp;
7609ee5d8aSWarner Losh 	struct ggeom *gp;
7709ee5d8aSWarner Losh 	struct gprovider *pp;
7809ee5d8aSWarner Losh 	const char *val;
7909ee5d8aSWarner Losh 
8009ee5d8aSWarner Losh 	/*
8109ee5d8aSWarner Losh 	 * Find the partition class so we can search it...
8209ee5d8aSWarner Losh 	 */
8309ee5d8aSWarner Losh 	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
8409ee5d8aSWarner Losh 		if (strcasecmp(classp->lg_name, G_PART) == 0)
8509ee5d8aSWarner Losh 			break;
8609ee5d8aSWarner Losh 	}
8709ee5d8aSWarner Losh 	if (classp == NULL)
8809ee5d8aSWarner Losh 		return (NULL);
8909ee5d8aSWarner Losh 
9009ee5d8aSWarner Losh 	/*
9109ee5d8aSWarner Losh 	 * Each geom will have a number of providers, search each
9209ee5d8aSWarner Losh 	 * one of them for the efimedia that matches.
9309ee5d8aSWarner Losh 	 */
9409ee5d8aSWarner Losh 	/* XXX just used gpart class since I know it's the only one, but maybe I should search all classes */
9509ee5d8aSWarner Losh 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
9609ee5d8aSWarner Losh 		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
9709ee5d8aSWarner Losh 			val = geom_pp_attr(mesh, pp, "efimedia");
9809ee5d8aSWarner Losh 			if (val == NULL)
9909ee5d8aSWarner Losh 				continue;
10009ee5d8aSWarner Losh 			if (strcasecmp(efimedia, val) == 0)
10109ee5d8aSWarner Losh 				return (pp);
10209ee5d8aSWarner Losh 		}
10309ee5d8aSWarner Losh 	}
10409ee5d8aSWarner Losh 
10509ee5d8aSWarner Losh 	return (NULL);
10609ee5d8aSWarner Losh }
10709ee5d8aSWarner Losh 
10809ee5d8aSWarner Losh static struct gprovider *
10909ee5d8aSWarner Losh find_provider_by_name(struct gmesh *mesh, const char *name)
11009ee5d8aSWarner Losh {
11109ee5d8aSWarner Losh 	struct gclass *classp;
11209ee5d8aSWarner Losh 	struct ggeom *gp;
11309ee5d8aSWarner Losh 	struct gprovider *pp;
11409ee5d8aSWarner Losh 
11509ee5d8aSWarner Losh 	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
11609ee5d8aSWarner Losh 		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
11709ee5d8aSWarner Losh 			LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
11809ee5d8aSWarner Losh 				if (strcmp(pp->lg_name, name) == 0)
11909ee5d8aSWarner Losh 					return (pp);
12009ee5d8aSWarner Losh 			}
12109ee5d8aSWarner Losh 		}
12209ee5d8aSWarner Losh 	}
12309ee5d8aSWarner Losh 
12409ee5d8aSWarner Losh 	return (NULL);
12509ee5d8aSWarner Losh }
12609ee5d8aSWarner Losh 
12709ee5d8aSWarner Losh 
12809ee5d8aSWarner Losh static int
12909ee5d8aSWarner Losh efi_hd_to_unix(struct gmesh *mesh, const_efidp dp, char **dev, char **relpath, char **abspath)
13009ee5d8aSWarner Losh {
13109ee5d8aSWarner Losh 	int rv = 0, n, i;
13209ee5d8aSWarner Losh 	const_efidp media, file, walker;
13309ee5d8aSWarner Losh 	size_t len, mntlen;
13409ee5d8aSWarner Losh 	char buf[MAX_DP_TEXT_LEN];
13509ee5d8aSWarner Losh 	char *pwalk;
13609ee5d8aSWarner Losh 	struct gprovider *pp, *provider;
13709ee5d8aSWarner Losh 	struct gconsumer *cp;
13809ee5d8aSWarner Losh 	struct statfs *mnt;
13909ee5d8aSWarner Losh 
14009ee5d8aSWarner Losh 	walker = media = dp;
14109ee5d8aSWarner Losh 
14209ee5d8aSWarner Losh 	/*
14309ee5d8aSWarner Losh 	 * Now, we can either have a filepath node next, or the end.
14409ee5d8aSWarner Losh 	 * Otherwise, it's an error.
14509ee5d8aSWarner Losh 	 */
14609ee5d8aSWarner Losh 	walker = (const_efidp)NextDevicePathNode(walker);
14709ee5d8aSWarner Losh 	if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
14809ee5d8aSWarner Losh 		return (EINVAL);
14909ee5d8aSWarner Losh 	if (DevicePathType(walker) ==  MEDIA_DEVICE_PATH &&
15009ee5d8aSWarner Losh 	    DevicePathSubType(walker) == MEDIA_FILEPATH_DP)
15109ee5d8aSWarner Losh 		file = walker;
15209ee5d8aSWarner Losh 	else if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
15309ee5d8aSWarner Losh 	    DevicePathType(walker) == END_DEVICE_PATH_TYPE)
15409ee5d8aSWarner Losh 		file = NULL;
15509ee5d8aSWarner Losh 	else
15609ee5d8aSWarner Losh 		return (EINVAL);
15709ee5d8aSWarner Losh 
15809ee5d8aSWarner Losh 	/*
15909ee5d8aSWarner Losh 	 * Format this node. We're going to look for it as a efimedia
16009ee5d8aSWarner Losh 	 * attribute of some geom node. Once we find that node, we use it
16109ee5d8aSWarner Losh 	 * as the device it comes from, at least provisionally.
16209ee5d8aSWarner Losh 	 */
16309ee5d8aSWarner Losh 	len = efidp_format_device_path_node(buf, sizeof(buf), media);
16409ee5d8aSWarner Losh 	if (len > sizeof(buf))
16509ee5d8aSWarner Losh 		return (EINVAL);
16609ee5d8aSWarner Losh 
16709ee5d8aSWarner Losh 	pp = find_provider_by_efimedia(mesh, buf);
16809ee5d8aSWarner Losh 	if (pp == NULL) {
16909ee5d8aSWarner Losh 		rv = ENOENT;
17009ee5d8aSWarner Losh 		goto errout;
17109ee5d8aSWarner Losh 	}
17209ee5d8aSWarner Losh 
17309ee5d8aSWarner Losh 	*dev = strdup(pp->lg_name);
17409ee5d8aSWarner Losh 	if (*dev == NULL) {
17509ee5d8aSWarner Losh 		rv = ENOMEM;
17609ee5d8aSWarner Losh 		goto errout;
17709ee5d8aSWarner Losh 	}
17809ee5d8aSWarner Losh 
17909ee5d8aSWarner Losh 	/*
18009ee5d8aSWarner Losh 	 * No file specified, just return the device. Don't even look
18109ee5d8aSWarner Losh 	 * for a mountpoint. XXX Sane?
18209ee5d8aSWarner Losh 	 */
18309ee5d8aSWarner Losh 	if (file == NULL)
18409ee5d8aSWarner Losh 		goto errout;
18509ee5d8aSWarner Losh 
18609ee5d8aSWarner Losh 	/*
18709ee5d8aSWarner Losh 	 * Now extract the relative path. The next node in the device path should
18809ee5d8aSWarner Losh 	 * be a filesystem node. If not, we have issues.
18909ee5d8aSWarner Losh 	 */
19009ee5d8aSWarner Losh 	*relpath = efidp_extract_file_path(file);
19109ee5d8aSWarner Losh 	if (*relpath == NULL) {
19209ee5d8aSWarner Losh 		rv = ENOMEM;
19309ee5d8aSWarner Losh 		goto errout;
19409ee5d8aSWarner Losh 	}
19509ee5d8aSWarner Losh 	for (pwalk = *relpath; *pwalk; pwalk++)
19609ee5d8aSWarner Losh 		if (*pwalk == '\\')
19709ee5d8aSWarner Losh 			*pwalk = '/';
19809ee5d8aSWarner Losh 
19909ee5d8aSWarner Losh 	/*
20009ee5d8aSWarner Losh 	 * To find the absolute path, we have to look for where we're mounted.
20109ee5d8aSWarner Losh 	 * We only look a little hard, since looking too hard can come up with
20209ee5d8aSWarner Losh 	 * false positives (imagine a graid, one of whose devices is *dev).
20309ee5d8aSWarner Losh 	 */
20409ee5d8aSWarner Losh 	n = getfsstat(NULL, 0, MNT_NOWAIT) + 1;
20509ee5d8aSWarner Losh 	if (n < 0) {
20609ee5d8aSWarner Losh 		rv = errno;
20709ee5d8aSWarner Losh 		goto errout;
20809ee5d8aSWarner Losh 	}
20909ee5d8aSWarner Losh 	mntlen = sizeof(struct statfs) * n;
21009ee5d8aSWarner Losh 	mnt = malloc(mntlen);
21109ee5d8aSWarner Losh 	n = getfsstat(mnt, mntlen, MNT_NOWAIT);
21209ee5d8aSWarner Losh 	if (n < 0) {
21309ee5d8aSWarner Losh 		rv = errno;
21409ee5d8aSWarner Losh 		goto errout;
21509ee5d8aSWarner Losh 	}
21609ee5d8aSWarner Losh 	provider = pp;
21709ee5d8aSWarner Losh 	for (i = 0; i < n; i++) {
21809ee5d8aSWarner Losh 		/*
21909ee5d8aSWarner Losh 		 * Skip all pseudo filesystems. This also skips the real filesytsem
22009ee5d8aSWarner Losh 		 * of ZFS. There's no EFI designator for ZFS in the standard, so
22109ee5d8aSWarner Losh 		 * we'll need to invent one, but its decoding will be handled in
22209ee5d8aSWarner Losh 		 * a separate function.
22309ee5d8aSWarner Losh 		 */
22409ee5d8aSWarner Losh 		if (mnt[i].f_mntfromname[0] != '/')
22509ee5d8aSWarner Losh 			continue;
22609ee5d8aSWarner Losh 
22709ee5d8aSWarner Losh 		/*
22809ee5d8aSWarner Losh 		 * First see if it is directly attached
22909ee5d8aSWarner Losh 		 */
23009ee5d8aSWarner Losh 		if (strcmp(provider->lg_name, mnt[i].f_mntfromname + 5) == 0)
23109ee5d8aSWarner Losh 			break;
23209ee5d8aSWarner Losh 
23309ee5d8aSWarner Losh 		/*
23409ee5d8aSWarner Losh 		 * Next see if it is attached via one of the physical disk's
23509ee5d8aSWarner Losh 		 * labels.
23609ee5d8aSWarner Losh 		 */
23709ee5d8aSWarner Losh 		LIST_FOREACH(cp, &provider->lg_consumers, lg_consumer) {
23809ee5d8aSWarner Losh 			pp = cp->lg_provider;
23909ee5d8aSWarner Losh 			if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) != 0)
24009ee5d8aSWarner Losh 				continue;
24109ee5d8aSWarner Losh 			if (strcmp(g_device_path(pp->lg_name), mnt[i].f_mntfromname) == 0)
24209ee5d8aSWarner Losh 				goto break2;
24309ee5d8aSWarner Losh 		}
24409ee5d8aSWarner Losh 		/* Not the one, try the next mount point */
24509ee5d8aSWarner Losh 	}
24609ee5d8aSWarner Losh break2:
24709ee5d8aSWarner Losh 
24809ee5d8aSWarner Losh 	/*
24909ee5d8aSWarner Losh 	 * No mountpoint found, no absolute path possible
25009ee5d8aSWarner Losh 	 */
25109ee5d8aSWarner Losh 	if (i >= n)
25209ee5d8aSWarner Losh 		goto errout;
25309ee5d8aSWarner Losh 
25409ee5d8aSWarner Losh 	/*
25509ee5d8aSWarner Losh 	 * Construct absolute path and we're finally done.
25609ee5d8aSWarner Losh 	 */
25709ee5d8aSWarner Losh 	if (strcmp(mnt[i].f_mntonname, "/") == 0)
25809ee5d8aSWarner Losh 		asprintf(abspath, "/%s", *relpath);
25909ee5d8aSWarner Losh 	else
26009ee5d8aSWarner Losh 		asprintf(abspath, "%s/%s", mnt[i].f_mntonname, *relpath);
26109ee5d8aSWarner Losh 
26209ee5d8aSWarner Losh errout:
26309ee5d8aSWarner Losh 	if (rv != 0) {
26409ee5d8aSWarner Losh 		free(*dev);
26509ee5d8aSWarner Losh 		*dev = NULL;
26609ee5d8aSWarner Losh 		free(*relpath);
26709ee5d8aSWarner Losh 		*relpath = NULL;
26809ee5d8aSWarner Losh 	}
26909ee5d8aSWarner Losh 	return (rv);
27009ee5d8aSWarner Losh }
27109ee5d8aSWarner Losh 
27209ee5d8aSWarner Losh /*
27309ee5d8aSWarner Losh  * Translate the passed in device_path to a unix path via the following
27409ee5d8aSWarner Losh  * algorithm.
27509ee5d8aSWarner Losh  *
27609ee5d8aSWarner Losh  * If dp, dev or path NULL, return EDOOFUS. XXX wise?
27709ee5d8aSWarner Losh  *
27809ee5d8aSWarner Losh  * Set *path = NULL; *dev = NULL;
27909ee5d8aSWarner Losh  *
28009ee5d8aSWarner Losh  * Walk through the device_path until we find either a media device path.
28109ee5d8aSWarner Losh  * Return EINVAL if not found. Return EINVAL if walking dp would
28209ee5d8aSWarner Losh  * land us more than sanity size away from the start (4k).
28309ee5d8aSWarner Losh  *
28409ee5d8aSWarner Losh  * If we find a media descriptor, we search through the geom mesh to see if we
28509ee5d8aSWarner Losh  * can find a matching node. If no match is found in the mesh that matches,
28609ee5d8aSWarner Losh  * return ENXIO.
28709ee5d8aSWarner Losh  *
28809ee5d8aSWarner Losh  * Once we find a matching node, we search to see if there is a filesystem
28909ee5d8aSWarner Losh  * mounted on it. If we find nothing, then search each of the devices that are
29009ee5d8aSWarner Losh  * mounted to see if we can work up the geom tree to find the matching node. if
29109ee5d8aSWarner Losh  * we still can't find anything, *dev = sprintf("/dev/%s", provider_name
29209ee5d8aSWarner Losh  * of the original node we found), but return ENOTBLK.
29309ee5d8aSWarner Losh  *
29409ee5d8aSWarner Losh  * Record the dev of the mountpoint in *dev.
29509ee5d8aSWarner Losh  *
29609ee5d8aSWarner Losh  * Once we find something, check to see if the next node in the device path is
29709ee5d8aSWarner Losh  * the end of list. If so, return the mountpoint.
29809ee5d8aSWarner Losh  *
29909ee5d8aSWarner Losh  * If the next node isn't a File path node, return EFTYPE.
30009ee5d8aSWarner Losh  *
30109ee5d8aSWarner Losh  * Extract the path from the File path node(s). translate any \ file separators
30209ee5d8aSWarner Losh  * to /. Append the result to the mount point. Copy the resulting path into
30309ee5d8aSWarner Losh  * *path.  Stat that path. If it is not found, return the errorr from stat.
30409ee5d8aSWarner Losh  *
30509ee5d8aSWarner Losh  * Finally, check to make sure the resulting path is still on the same
30609ee5d8aSWarner Losh  * device. If not, return ENODEV.
30709ee5d8aSWarner Losh  *
30809ee5d8aSWarner Losh  * Otherwise return 0.
30909ee5d8aSWarner Losh  *
31009ee5d8aSWarner Losh  * The dev or full path that's returned is malloced, so needs to be freed when
31109ee5d8aSWarner Losh  * the caller is done about it. Unlike many other functions, we can return data
31209ee5d8aSWarner Losh  * with an error code, so pay attention.
31309ee5d8aSWarner Losh  */
31409ee5d8aSWarner Losh int
31509ee5d8aSWarner Losh efivar_device_path_to_unix_path(const_efidp dp, char **dev, char **relpath, char **abspath)
31609ee5d8aSWarner Losh {
31709ee5d8aSWarner Losh 	const_efidp walker;
31809ee5d8aSWarner Losh 	struct gmesh mesh;
31909ee5d8aSWarner Losh 	int rv = 0;
32009ee5d8aSWarner Losh 
32109ee5d8aSWarner Losh 	/*
32209ee5d8aSWarner Losh 	 * Sanity check args, fail early
32309ee5d8aSWarner Losh 	 */
32409ee5d8aSWarner Losh 	if (dp == NULL || dev == NULL || relpath == NULL || abspath == NULL)
32509ee5d8aSWarner Losh 		return (EDOOFUS);
32609ee5d8aSWarner Losh 
32709ee5d8aSWarner Losh 	*dev = NULL;
32809ee5d8aSWarner Losh 	*relpath = NULL;
32909ee5d8aSWarner Losh 	*abspath = NULL;
33009ee5d8aSWarner Losh 
33109ee5d8aSWarner Losh 	/*
33209ee5d8aSWarner Losh 	 * Find the first media device path we can. If we go too far,
33309ee5d8aSWarner Losh 	 * assume the passed in device path is bogus. If we hit the end
33409ee5d8aSWarner Losh 	 * then we didn't find a media device path, so signal that error.
33509ee5d8aSWarner Losh 	 */
33609ee5d8aSWarner Losh 	walker = dp;
33709ee5d8aSWarner Losh 	while (DevicePathType(walker) != MEDIA_DEVICE_PATH &&
33809ee5d8aSWarner Losh 	    DevicePathType(walker) != END_DEVICE_PATH_TYPE) {
33909ee5d8aSWarner Losh 		walker = (const_efidp)NextDevicePathNode(walker);
34009ee5d8aSWarner Losh 		if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
34109ee5d8aSWarner Losh 			return (EINVAL);
34209ee5d8aSWarner Losh 	}
34309ee5d8aSWarner Losh 	if (DevicePathType(walker) !=  MEDIA_DEVICE_PATH)
34409ee5d8aSWarner Losh 		return (EINVAL);
34509ee5d8aSWarner Losh 
34609ee5d8aSWarner Losh 	/*
34709ee5d8aSWarner Losh 	 * There's several types of media paths. We're only interested in the
34809ee5d8aSWarner Losh 	 * hard disk path, as it's really the only relevant one to booting. The
34909ee5d8aSWarner Losh 	 * CD path just might also be relevant, and would be easy to add, but
35009ee5d8aSWarner Losh 	 * isn't supported. A file path too is relevant, but at this stage, it's
35109ee5d8aSWarner Losh 	 * premature because we're trying to translate a specification for a device
35209ee5d8aSWarner Losh 	 * and path on that device into a unix path, or at the very least, a
35309ee5d8aSWarner Losh 	 * geom device : path-on-device.
35409ee5d8aSWarner Losh 	 *
35509ee5d8aSWarner Losh 	 * Also, ZFS throws a bit of a monkey wrench in here since it doesn't have
35609ee5d8aSWarner Losh 	 * a device path type (it creates a new virtual device out of one or more
35709ee5d8aSWarner Losh 	 * storage devices).
35809ee5d8aSWarner Losh 	 *
35909ee5d8aSWarner Losh 	 * For all of them, we'll need to know the geoms, so allocate / free the
36009ee5d8aSWarner Losh 	 * geom mesh here since it's safer than doing it in each sub-function
36109ee5d8aSWarner Losh 	 * which may have many error exits.
36209ee5d8aSWarner Losh 	 */
36309ee5d8aSWarner Losh 	if (geom_gettree(&mesh))
36409ee5d8aSWarner Losh 		return (ENOMEM);
36509ee5d8aSWarner Losh 
36609ee5d8aSWarner Losh 	rv = EINVAL;
36709ee5d8aSWarner Losh 	if (DevicePathSubType(walker) == MEDIA_HARDDRIVE_DP)
36809ee5d8aSWarner Losh 		rv = efi_hd_to_unix(&mesh, walker, dev, relpath, abspath);
36909ee5d8aSWarner Losh #ifdef notyet
37009ee5d8aSWarner Losh 	else if (is_cdrom_device(walker))
37109ee5d8aSWarner Losh 		rv = efi_cdrom_to_unix(&mesh, walker, dev, relpath, abspath);
37209ee5d8aSWarner Losh 	else if (is_floppy_device(walker))
37309ee5d8aSWarner Losh 		rv = efi_floppy_to_unix(&mesh, walker, dev, relpath, abspath);
37409ee5d8aSWarner Losh 	else if (is_zpool_device(walker))
37509ee5d8aSWarner Losh 		rv = efi_zpool_to_unix(&mesh, walker, dev, relpath, abspath);
37609ee5d8aSWarner Losh #endif
37709ee5d8aSWarner Losh 	geom_deletetree(&mesh);
37809ee5d8aSWarner Losh 
37909ee5d8aSWarner Losh 	return (rv);
38009ee5d8aSWarner Losh }
38109ee5d8aSWarner Losh 
38209ee5d8aSWarner Losh /*
38309ee5d8aSWarner Losh  * Construct the EFI path to a current unix path as follows.
38409ee5d8aSWarner Losh  *
38509ee5d8aSWarner Losh  * The path may be of one of three forms:
38609ee5d8aSWarner Losh  *	1) /path/to/file -- full path to a file. The file need not be present,
38709ee5d8aSWarner Losh  *		but /path/to must be. It must reside on a local filesystem
38809ee5d8aSWarner Losh  *		mounted on a GPT or MBR partition.
38909ee5d8aSWarner Losh  *	2) //path/to/file -- Shorthand for 'On the EFI partition, \path\to\file'
39009ee5d8aSWarner Losh  *		where 'The EFI Partition' is a partiton that's type is 'efi'
39109ee5d8aSWarner Losh  *		on the same disk that / is mounted from. If there are multiple
39209ee5d8aSWarner Losh  *		or no 'efi' parittions on that disk, or / isn't on a disk that
39309ee5d8aSWarner Losh  *		we can trace back to a physical device, an error will result
39409ee5d8aSWarner Losh  *	3) [/dev/]geom-name:/path/to/file -- Use the specified partition
39509ee5d8aSWarner Losh  *		(and it must be a GPT or MBR partition) with the specified
39609ee5d8aSWarner Losh  *		path. The latter is not authenticated.
39709ee5d8aSWarner Losh  * all path forms translate any \ characters to / before further processing.
39809ee5d8aSWarner Losh  * When a file path node is created, all / characters are translated back
39909ee5d8aSWarner Losh  * to \.
40009ee5d8aSWarner Losh  *
40109ee5d8aSWarner Losh  * For paths of the first form:
40209ee5d8aSWarner Losh  *	find where the filesystem is mount (either the file directly, or
40309ee5d8aSWarner Losh  *		its parent directory).
40409ee5d8aSWarner Losh  *	translate any logical device name (eg lable) to a physical one
40509ee5d8aSWarner Losh  *	If not possible, return ENXIO
40609ee5d8aSWarner Losh  *	If the physical path is unsupported (Eg not on a GPT or MBR disk),
40709ee5d8aSWarner Losh  *		return ENXIO
40809ee5d8aSWarner Losh  *	Create a media device path node.
40909ee5d8aSWarner Losh  *	append the relative path from the mountpoint to the media device node
41009ee5d8aSWarner Losh  * 		as a file path.
41109ee5d8aSWarner Losh  *
41209ee5d8aSWarner Losh  * For paths matching the second form:
41309ee5d8aSWarner Losh  *	find the EFI partition corresponding to the root fileystem.
41409ee5d8aSWarner Losh  *	If none found, return ENXIO
41509ee5d8aSWarner Losh  *	Create a media device path node for the found partition
41609ee5d8aSWarner Losh  *	Append a File Path to the end for the rest of the file.
41709ee5d8aSWarner Losh  *
41809ee5d8aSWarner Losh  * For paths of the third form
41909ee5d8aSWarner Losh  *	Translate the geom-name passed in into a physical partition
42009ee5d8aSWarner Losh  *		name.
42109ee5d8aSWarner Losh  *	Return ENXIO if the translation fails
42209ee5d8aSWarner Losh  *	Make a media device path for it
42309ee5d8aSWarner Losh  *	append the part after the : as a File path node.
42409ee5d8aSWarner Losh  */
42509ee5d8aSWarner Losh 
42609ee5d8aSWarner Losh static char *
42709ee5d8aSWarner Losh path_to_file_dp(const char *relpath)
42809ee5d8aSWarner Losh {
42909ee5d8aSWarner Losh 	char *rv;
43009ee5d8aSWarner Losh 
43109ee5d8aSWarner Losh 	asprintf(&rv, "File(%s)", relpath);
43209ee5d8aSWarner Losh 	return rv;
43309ee5d8aSWarner Losh }
43409ee5d8aSWarner Losh 
43509ee5d8aSWarner Losh static char *
43609ee5d8aSWarner Losh find_geom_efi_on_root(struct gmesh *mesh)
43709ee5d8aSWarner Losh {
43809ee5d8aSWarner Losh 	struct statfs buf;
43909ee5d8aSWarner Losh 	const char *dev;
44009ee5d8aSWarner Losh 	struct gprovider *pp;
44109ee5d8aSWarner Losh //	struct ggeom *disk;
44209ee5d8aSWarner Losh 	struct gconsumer *cp;
44309ee5d8aSWarner Losh 
44409ee5d8aSWarner Losh 	/*
44509ee5d8aSWarner Losh 	 * Find /'s geom. Assume it's mounted on /dev/ and filter out all the
44609ee5d8aSWarner Losh 	 * filesystems that aren't.
44709ee5d8aSWarner Losh 	 */
44809ee5d8aSWarner Losh 	if (statfs("/", &buf) != 0)
44909ee5d8aSWarner Losh 		return (NULL);
45009ee5d8aSWarner Losh 	dev = buf.f_mntfromname;
45109ee5d8aSWarner Losh 	if (*dev != '/' || strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
45209ee5d8aSWarner Losh 		return (NULL);
45309ee5d8aSWarner Losh 	dev += sizeof(_PATH_DEV) -1;
45409ee5d8aSWarner Losh 	pp = find_provider_by_name(mesh, dev);
45509ee5d8aSWarner Losh 	if (pp == NULL)
45609ee5d8aSWarner Losh 		return (NULL);
45709ee5d8aSWarner Losh 
45809ee5d8aSWarner Losh 	/*
45909ee5d8aSWarner Losh 	 * If the provider is a LABEL, find it's outer PART class, if any. We
46009ee5d8aSWarner Losh 	 * only operate on partitions.
46109ee5d8aSWarner Losh 	 */
46209ee5d8aSWarner Losh 	if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0) {
46309ee5d8aSWarner Losh 		LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
46409ee5d8aSWarner Losh 			if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_PART) == 0) {
46509ee5d8aSWarner Losh 				pp = cp->lg_provider;
46609ee5d8aSWarner Losh 				break;
46709ee5d8aSWarner Losh 			}
46809ee5d8aSWarner Losh 		}
46909ee5d8aSWarner Losh 	}
47009ee5d8aSWarner Losh 	if (strcmp(pp->lg_geom->lg_class->lg_name, G_PART) != 0)
47109ee5d8aSWarner Losh 		return (NULL);
47209ee5d8aSWarner Losh 
47309ee5d8aSWarner Losh #if 0
47409ee5d8aSWarner Losh 	/* This doesn't work because we can't get the data to walk UP the tree it seems */
47509ee5d8aSWarner Losh 
47609ee5d8aSWarner Losh 	/*
47709ee5d8aSWarner Losh 	 * Now that we've found the PART that we have mounted as root, find the
47809ee5d8aSWarner Losh 	 * first efi typed partition that's a peer, if any.
47909ee5d8aSWarner Losh 	 */
48009ee5d8aSWarner Losh 	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
48109ee5d8aSWarner Losh 		if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_DISK) == 0) {
48209ee5d8aSWarner Losh 			disk = cp->lg_provider->lg_geom;
48309ee5d8aSWarner Losh 			break;
48409ee5d8aSWarner Losh 		}
48509ee5d8aSWarner Losh 	}
48609ee5d8aSWarner Losh 	if (disk == NULL)	/* This is very bad -- old nested partitions -- no support ? */
48709ee5d8aSWarner Losh 		return (NULL);
48809ee5d8aSWarner Losh #endif
48909ee5d8aSWarner Losh 
49009ee5d8aSWarner Losh #if 0
49109ee5d8aSWarner Losh 	/* This doesn't work because we can't get the data to walk UP the tree it seems */
49209ee5d8aSWarner Losh 
49309ee5d8aSWarner Losh 	/*
49409ee5d8aSWarner Losh 	 * With the disk provider, we can look for its consumers to see if any are the proper type.
49509ee5d8aSWarner Losh 	 */
49609ee5d8aSWarner Losh 	LIST_FOREACH(pp, &disk->lg_consumer, lg_consumer) {
49709ee5d8aSWarner Losh 		type = geom_pp_attr(mesh, pp, "type");
49809ee5d8aSWarner Losh 		if (type == NULL)
49909ee5d8aSWarner Losh 			continue;
50009ee5d8aSWarner Losh 		if (strcmp(type, "efi") != 0)
50109ee5d8aSWarner Losh 			continue;
50209ee5d8aSWarner Losh 		efimedia = geom_pp_attr(mesh, pp, "efimedia");
50309ee5d8aSWarner Losh 		if (efimedia == NULL)
50409ee5d8aSWarner Losh 			return (NULL);
50509ee5d8aSWarner Losh 		return strdup(efimedia);
50609ee5d8aSWarner Losh 	}
50709ee5d8aSWarner Losh #endif
50809ee5d8aSWarner Losh 	return (NULL);
50909ee5d8aSWarner Losh }
51009ee5d8aSWarner Losh 
51109ee5d8aSWarner Losh 
51209ee5d8aSWarner Losh static char *
51309ee5d8aSWarner Losh find_geom_efimedia(struct gmesh *mesh, const char *dev)
51409ee5d8aSWarner Losh {
51509ee5d8aSWarner Losh 	struct gprovider *pp;
51609ee5d8aSWarner Losh 	const char *efimedia;
51709ee5d8aSWarner Losh 
51809ee5d8aSWarner Losh 	pp = find_provider_by_name(mesh, dev);
51909ee5d8aSWarner Losh 	if (pp == NULL)
52009ee5d8aSWarner Losh 		return (NULL);
52109ee5d8aSWarner Losh 	efimedia = geom_pp_attr(mesh, pp, "efimedia");
52209ee5d8aSWarner Losh 	if (efimedia == NULL)
52309ee5d8aSWarner Losh 		return (NULL);
52409ee5d8aSWarner Losh 	return strdup(efimedia);
52509ee5d8aSWarner Losh }
52609ee5d8aSWarner Losh 
52709ee5d8aSWarner Losh static int
52809ee5d8aSWarner Losh build_dp(const char *efimedia, const char *relpath, efidp *dp)
52909ee5d8aSWarner Losh {
530670a4056SWarner Losh 	char *fp, *dptxt = NULL, *cp, *rp;
53109ee5d8aSWarner Losh 	int rv = 0;
5323e4f07fcSMark Johnston 	efidp out = NULL;
53309ee5d8aSWarner Losh 	size_t len;
53409ee5d8aSWarner Losh 
535670a4056SWarner Losh 	rp = strdup(relpath);
536670a4056SWarner Losh 	for (cp = rp; *cp; cp++)
537670a4056SWarner Losh 		if (*cp == '/')
538670a4056SWarner Losh 			*cp = '\\';
539670a4056SWarner Losh 	fp = path_to_file_dp(rp);
540670a4056SWarner Losh 	free(rp);
54109ee5d8aSWarner Losh 	if (fp == NULL) {
54209ee5d8aSWarner Losh 		rv = ENOMEM;
54309ee5d8aSWarner Losh 		goto errout;
54409ee5d8aSWarner Losh 	}
54509ee5d8aSWarner Losh 
54609ee5d8aSWarner Losh 	asprintf(&dptxt, "%s/%s", efimedia, fp);
54709ee5d8aSWarner Losh 	out = malloc(8192);
54809ee5d8aSWarner Losh 	len = efidp_parse_device_path(dptxt, out, 8192);
54909ee5d8aSWarner Losh 	if (len > 8192) {
55009ee5d8aSWarner Losh 		rv = ENOMEM;
55109ee5d8aSWarner Losh 		goto errout;
55209ee5d8aSWarner Losh 	}
55309ee5d8aSWarner Losh 	if (len == 0) {
55409ee5d8aSWarner Losh 		rv = EINVAL;
55509ee5d8aSWarner Losh 		goto errout;
55609ee5d8aSWarner Losh 	}
55709ee5d8aSWarner Losh 
55809ee5d8aSWarner Losh 	*dp = out;
55909ee5d8aSWarner Losh errout:
56009ee5d8aSWarner Losh 	if (rv) {
56109ee5d8aSWarner Losh 		free(out);
56209ee5d8aSWarner Losh 	}
56309ee5d8aSWarner Losh 	free(dptxt);
56409ee5d8aSWarner Losh 	free(fp);
56509ee5d8aSWarner Losh 
56609ee5d8aSWarner Losh 	return rv;
56709ee5d8aSWarner Losh }
56809ee5d8aSWarner Losh 
56909ee5d8aSWarner Losh /* Handles //path/to/file */
57009ee5d8aSWarner Losh /*
57109ee5d8aSWarner Losh  * Which means: find the disk that has /. Then look for a EFI partition
57209ee5d8aSWarner Losh  * and use that for the efimedia and /path/to/file as relative to that.
57309ee5d8aSWarner Losh  * Not sure how ZFS will work here since we can't easily make the leap
57409ee5d8aSWarner Losh  * to the geom from the zpool.
57509ee5d8aSWarner Losh  */
57609ee5d8aSWarner Losh static int
57709ee5d8aSWarner Losh efipart_to_dp(struct gmesh *mesh, char *path, efidp *dp)
57809ee5d8aSWarner Losh {
57909ee5d8aSWarner Losh 	char *efimedia = NULL;
58009ee5d8aSWarner Losh 	int rv;
58109ee5d8aSWarner Losh 
58209ee5d8aSWarner Losh 	efimedia = find_geom_efi_on_root(mesh);
58309ee5d8aSWarner Losh #ifdef notyet
58409ee5d8aSWarner Losh 	if (efimedia == NULL)
58509ee5d8aSWarner Losh 		efimedia = find_efi_on_zfsroot(dev);
58609ee5d8aSWarner Losh #endif
58709ee5d8aSWarner Losh 	if (efimedia == NULL) {
58809ee5d8aSWarner Losh 		rv = ENOENT;
58909ee5d8aSWarner Losh 		goto errout;
59009ee5d8aSWarner Losh 	}
59109ee5d8aSWarner Losh 
59209ee5d8aSWarner Losh 	rv = build_dp(efimedia, path + 1, dp);
59309ee5d8aSWarner Losh errout:
59409ee5d8aSWarner Losh 	free(efimedia);
59509ee5d8aSWarner Losh 
59609ee5d8aSWarner Losh 	return rv;
59709ee5d8aSWarner Losh }
59809ee5d8aSWarner Losh 
59909ee5d8aSWarner Losh /* Handles [/dev/]geom:[/]path/to/file */
60009ee5d8aSWarner Losh /* Handles zfs-dataset:[/]path/to/file (this may include / ) */
60109ee5d8aSWarner Losh static int
60209ee5d8aSWarner Losh dev_path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
60309ee5d8aSWarner Losh {
60409ee5d8aSWarner Losh 	char *relpath, *dev, *efimedia = NULL;
60509ee5d8aSWarner Losh 	int rv = 0;
60609ee5d8aSWarner Losh 
60709ee5d8aSWarner Losh 	relpath = strchr(path, ':');
60809ee5d8aSWarner Losh 	assert(relpath != NULL);
60909ee5d8aSWarner Losh 	*relpath++ = '\0';
61009ee5d8aSWarner Losh 
61109ee5d8aSWarner Losh 	dev = path;
61209ee5d8aSWarner Losh 	if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
61309ee5d8aSWarner Losh 		dev += sizeof(_PATH_DEV) -1;
61409ee5d8aSWarner Losh 
61509ee5d8aSWarner Losh 	efimedia = find_geom_efimedia(mesh, dev);
61609ee5d8aSWarner Losh #ifdef notyet
61709ee5d8aSWarner Losh 	if (efimedia == NULL)
61809ee5d8aSWarner Losh 		find_zfs_efi_media(dev);
61909ee5d8aSWarner Losh #endif
62009ee5d8aSWarner Losh 	if (efimedia == NULL) {
62109ee5d8aSWarner Losh 		rv = ENOENT;
62209ee5d8aSWarner Losh 		goto errout;
62309ee5d8aSWarner Losh 	}
62409ee5d8aSWarner Losh 	rv = build_dp(efimedia, relpath, dp);
62509ee5d8aSWarner Losh errout:
62609ee5d8aSWarner Losh 	free(efimedia);
62709ee5d8aSWarner Losh 
62809ee5d8aSWarner Losh 	return rv;
62909ee5d8aSWarner Losh }
63009ee5d8aSWarner Losh 
63109ee5d8aSWarner Losh /* Handles /path/to/file */
63209ee5d8aSWarner Losh static int
63309ee5d8aSWarner Losh path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
63409ee5d8aSWarner Losh {
63509ee5d8aSWarner Losh 	struct statfs buf;
63609ee5d8aSWarner Losh 	char *rp = NULL, *ep, *dev, *efimedia = NULL;
63709ee5d8aSWarner Losh 	int rv = 0;
63809ee5d8aSWarner Losh 
63909ee5d8aSWarner Losh 	rp = realpath(path, NULL);
64009ee5d8aSWarner Losh 	if (rp == NULL) {
64109ee5d8aSWarner Losh 		rv = errno;
64209ee5d8aSWarner Losh 		goto errout;
64309ee5d8aSWarner Losh 	}
64409ee5d8aSWarner Losh 
64509ee5d8aSWarner Losh 	if (statfs(rp, &buf) != 0) {
64609ee5d8aSWarner Losh 		rv = errno;
64709ee5d8aSWarner Losh 		goto errout;
64809ee5d8aSWarner Losh 	}
64909ee5d8aSWarner Losh 
65009ee5d8aSWarner Losh 	dev = buf.f_mntfromname;
65109ee5d8aSWarner Losh 	if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
65209ee5d8aSWarner Losh 		dev += sizeof(_PATH_DEV) -1;
65309ee5d8aSWarner Losh 	ep = rp + strlen(buf.f_mntonname);
65409ee5d8aSWarner Losh 
65509ee5d8aSWarner Losh 	efimedia = find_geom_efimedia(mesh, dev);
65609ee5d8aSWarner Losh #ifdef notyet
65709ee5d8aSWarner Losh 	if (efimedia == NULL)
65809ee5d8aSWarner Losh 		find_zfs_efi_media(dev);
65909ee5d8aSWarner Losh #endif
66009ee5d8aSWarner Losh 	if (efimedia == NULL) {
66109ee5d8aSWarner Losh 		rv = ENOENT;
66209ee5d8aSWarner Losh 		goto errout;
66309ee5d8aSWarner Losh 	}
66409ee5d8aSWarner Losh 
66509ee5d8aSWarner Losh 	rv = build_dp(efimedia, ep, dp);
66609ee5d8aSWarner Losh errout:
66709ee5d8aSWarner Losh 	free(efimedia);
66809ee5d8aSWarner Losh 	free(rp);
66909ee5d8aSWarner Losh 	if (rv != 0) {
67009ee5d8aSWarner Losh 		free(*dp);
671167b7a41SWarner Losh 		*dp = NULL;
67209ee5d8aSWarner Losh 	}
67309ee5d8aSWarner Losh 	return (rv);
67409ee5d8aSWarner Losh }
67509ee5d8aSWarner Losh 
67609ee5d8aSWarner Losh int
67709ee5d8aSWarner Losh efivar_unix_path_to_device_path(const char *path, efidp *dp)
67809ee5d8aSWarner Losh {
67909ee5d8aSWarner Losh 	char *modpath = NULL, *cp;
68009ee5d8aSWarner Losh 	int rv = ENOMEM;
68109ee5d8aSWarner Losh 	struct gmesh mesh;
68209ee5d8aSWarner Losh 
68309ee5d8aSWarner Losh 	/*
68409ee5d8aSWarner Losh 	 * Fail early for clearly bogus things
68509ee5d8aSWarner Losh 	 */
68609ee5d8aSWarner Losh 	if (path == NULL || dp == NULL)
68709ee5d8aSWarner Losh 		return (EDOOFUS);
68809ee5d8aSWarner Losh 
68909ee5d8aSWarner Losh 	/*
69009ee5d8aSWarner Losh 	 * We'll need the goem mesh to grovel through it to find the
69109ee5d8aSWarner Losh 	 * efimedia attribute for any devices we find. Grab it here
69209ee5d8aSWarner Losh 	 * and release it to simplify the error paths out of the
69309ee5d8aSWarner Losh 	 * subordinate functions
69409ee5d8aSWarner Losh 	 */
69509ee5d8aSWarner Losh 	if (geom_gettree(&mesh))
69609ee5d8aSWarner Losh 		return (errno);
69709ee5d8aSWarner Losh 
69809ee5d8aSWarner Losh 	/*
69909ee5d8aSWarner Losh 	 * Convert all \ to /. We'll convert them back again when
70009ee5d8aSWarner Losh 	 * we encode the file. Boot loaders are expected to cope.
70109ee5d8aSWarner Losh 	 */
70209ee5d8aSWarner Losh 	modpath = strdup(path);
70309ee5d8aSWarner Losh 	if (modpath == NULL)
70409ee5d8aSWarner Losh 		goto out;
70509ee5d8aSWarner Losh 	for (cp = modpath; *cp; cp++)
70609ee5d8aSWarner Losh 		if (*cp == '\\')
70709ee5d8aSWarner Losh 			*cp = '/';
70809ee5d8aSWarner Losh 
70909ee5d8aSWarner Losh 	if (modpath[0] == '/' && modpath[1] == '/')	/* Handle //foo/bar/baz */
71009ee5d8aSWarner Losh 		rv = efipart_to_dp(&mesh, modpath, dp);
71109ee5d8aSWarner Losh 	else if (strchr(modpath, ':'))			/* Handle dev:/bar/baz */
71209ee5d8aSWarner Losh 		rv = dev_path_to_dp(&mesh, modpath, dp);
71309ee5d8aSWarner Losh 	else						/* Handle /a/b/c */
71409ee5d8aSWarner Losh 		rv = path_to_dp(&mesh, modpath, dp);
71509ee5d8aSWarner Losh 
71609ee5d8aSWarner Losh out:
71709ee5d8aSWarner Losh 	geom_deletetree(&mesh);
71809ee5d8aSWarner Losh 	free(modpath);
71909ee5d8aSWarner Losh 
72009ee5d8aSWarner Losh 	return (rv);
72109ee5d8aSWarner Losh }
722