1/*-
2 * Copyright (c) 2017 Netflix, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/param.h>
27#include <sys/ucred.h>
28#include <sys/mount.h>
29
30#undef MAX
31#undef MIN
32
33#include <assert.h>
34#include <efivar.h>
35#include <errno.h>
36#include <libgeom.h>
37#include <paths.h>
38#include <stdio.h>
39#include <string.h>
40
41#include "efichar.h"
42
43#include "efi-osdep.h"
44#include "efivar-dp.h"
45
46#include "uefi-dplib.h"
47
48#define MAX_DP_SANITY	4096		/* Biggest device path in bytes */
49#define MAX_DP_TEXT_LEN	4096		/* Longest string rep of dp */
50
51#define ValidLen(dp) (DevicePathNodeLength(dp) >= sizeof(EFI_DEVICE_PATH_PROTOCOL) && \
52	    DevicePathNodeLength(dp) < MAX_DP_SANITY)
53
54#define	G_PART	"PART"
55#define	G_LABEL "LABEL"
56#define G_DISK	"DISK"
57
58static const char *
59geom_pp_attr(struct gmesh *mesh, struct gprovider *pp, const char *attr)
60{
61	struct gconfig *conf;
62
63	LIST_FOREACH(conf, &pp->lg_config, lg_config) {
64		if (strcmp(conf->lg_name, attr) != 0)
65			continue;
66		return (conf->lg_val);
67	}
68	return (NULL);
69}
70
71static struct gprovider *
72find_provider_by_efimedia(struct gmesh *mesh, const char *efimedia)
73{
74	struct gclass *classp;
75	struct ggeom *gp;
76	struct gprovider *pp;
77	const char *val;
78
79	/*
80	 * Find the partition class so we can search it...
81	 */
82	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
83		if (strcasecmp(classp->lg_name, G_PART) == 0)
84			break;
85	}
86	if (classp == NULL)
87		return (NULL);
88
89	/*
90	 * Each geom will have a number of providers, search each
91	 * one of them for the efimedia that matches.
92	 */
93	/* XXX just used gpart class since I know it's the only one, but maybe I should search all classes */
94	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
95		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
96			val = geom_pp_attr(mesh, pp, "efimedia");
97			if (val == NULL)
98				continue;
99			if (strcasecmp(efimedia, val) == 0)
100				return (pp);
101		}
102	}
103
104	return (NULL);
105}
106
107static struct gprovider *
108find_provider_by_name(struct gmesh *mesh, const char *name)
109{
110	struct gclass *classp;
111	struct ggeom *gp;
112	struct gprovider *pp;
113
114	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
115		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
116			LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
117				if (strcmp(pp->lg_name, name) == 0)
118					return (pp);
119			}
120		}
121	}
122
123	return (NULL);
124}
125
126
127static int
128efi_hd_to_unix(struct gmesh *mesh, const_efidp dp, char **dev, char **relpath, char **abspath)
129{
130	int rv = 0, n, i;
131	const_efidp media, file, walker;
132	size_t len, mntlen;
133	char buf[MAX_DP_TEXT_LEN];
134	char *pwalk, *newdev = NULL;
135	struct gprovider *pp, *provider;
136	struct statfs *mnt;
137	struct gclass *glabel;
138	struct ggeom *gp;
139
140	walker = media = dp;
141	*dev = NULL;
142	*relpath = NULL;
143
144	/*
145	 * Now, we can either have a filepath node next, or the end.
146	 * Otherwise, it's an error.
147	 */
148	if (!ValidLen(walker))
149		return (EINVAL);
150	walker = (const_efidp)NextDevicePathNode(walker);
151	if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
152		return (EINVAL);
153	if (DevicePathType(walker) ==  MEDIA_DEVICE_PATH &&
154	    DevicePathSubType(walker) == MEDIA_FILEPATH_DP)
155		file = walker;
156	else if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
157	    DevicePathType(walker) == END_DEVICE_PATH_TYPE)
158		file = NULL;
159	else
160		return (EINVAL);
161
162	/*
163	 * Format this node. We're going to look for it as a efimedia
164	 * attribute of some geom node. Once we find that node, we use it
165	 * as the device it comes from, at least provisionally.
166	 */
167	len = efidp_format_device_path_node(buf, sizeof(buf), media);
168	if (len > sizeof(buf))
169		return (EINVAL);
170
171	pp = find_provider_by_efimedia(mesh, buf);
172	if (pp == NULL) {
173		rv = ENOENT;
174		goto errout;
175	}
176
177	/*
178	 * No file specified, just return the device. Don't even look
179	 * for a mountpoint. XXX Sane?
180	 */
181	if (file == NULL)
182		goto errout;
183
184	/*
185	 * Now extract the relative path. The next node in the device path should
186	 * be a filesystem node. If not, we have issues.
187	 */
188	*relpath = efidp_extract_file_path(file);
189	if (*relpath == NULL) {
190		rv = ENOMEM;
191		goto errout;
192	}
193	for (pwalk = *relpath; *pwalk; pwalk++)
194		if (*pwalk == '\\')
195			*pwalk = '/';
196
197	/*
198	 * To find the absolute path, we have to look for where we're mounted.
199	 * We only look a little hard, since looking too hard can come up with
200	 * false positives (imagine a graid, one of whose devices is *dev).
201	 */
202	n = getfsstat(NULL, 0, MNT_NOWAIT) + 1;
203	if (n < 0) {
204		rv = errno;
205		goto errout;
206	}
207	mntlen = sizeof(struct statfs) * n;
208	mnt = malloc(mntlen);
209	n = getfsstat(mnt, mntlen, MNT_NOWAIT);
210	if (n < 0) {
211		rv = errno;
212		goto errout;
213	}
214
215	/*
216	 * Find glabel, if it exists. It's OK if not: we'll skip searching for
217	 * labels.
218	 */
219	LIST_FOREACH(glabel, &mesh->lg_class, lg_class) {
220		if (strcmp(glabel->lg_name, G_LABEL) == 0)
221			break;
222	}
223
224	provider = pp;
225	for (i = 0; i < n; i++) {
226		/*
227		 * Skip all pseudo filesystems. This also skips the real filesytsem
228		 * of ZFS. There's no EFI designator for ZFS in the standard, so
229		 * we'll need to invent one, but its decoding will be handled in
230		 * a separate function.
231		 */
232		if (strncmp(mnt[i].f_mntfromname, "/dev/", 5) != 0)
233			continue;
234
235		/*
236		 * First see if it is directly attached
237		 */
238		if (strcmp(provider->lg_name, mnt[i].f_mntfromname + 5) == 0) {
239			newdev = provider->lg_name;
240			break;
241		}
242
243		/*
244		 * Next see if it is attached via one of the physical disk's labels.
245		 * We can't search directly from the pointers we have for the
246		 * provider, so we have to cast a wider net for all labels and
247		 * filter those down to geoms whose name matches the PART provider
248		 * we found the efimedia attribute on.
249		 */
250		if (glabel == NULL)
251			continue;
252		LIST_FOREACH(gp, &glabel->lg_geom, lg_geom) {
253			if (strcmp(gp->lg_name, provider->lg_name) != 0) {
254				continue;
255			}
256			LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
257				if (strcmp(pp->lg_name, mnt[i].f_mntfromname + 5) == 0) {
258					newdev = pp->lg_name;
259					goto break2;
260				}
261			}
262		}
263		/* Not the one, try the next mount point */
264	}
265break2:
266
267	/*
268	 * If nothing better was mounted, then use the provider we found as
269	 * is. It's the most correct thing we can return in that acse.
270	 */
271	if (newdev == NULL)
272		newdev = provider->lg_name;
273	*dev = strdup(newdev);
274	if (*dev == NULL) {
275		rv = ENOMEM;
276		goto errout;
277	}
278
279	/*
280	 * No mountpoint found, no absolute path possible
281	 */
282	if (i >= n)
283		goto errout;
284
285	/*
286	 * Construct absolute path and we're finally done.
287	 */
288	if (strcmp(mnt[i].f_mntonname, "/") == 0)
289		asprintf(abspath, "/%s", *relpath);
290	else
291		asprintf(abspath, "%s/%s", mnt[i].f_mntonname, *relpath);
292
293errout:
294	if (rv != 0) {
295		free(*dev);
296		*dev = NULL;
297		free(*relpath);
298		*relpath = NULL;
299	}
300	return (rv);
301}
302
303/*
304 * Translate the passed in device_path to a unix path via the following
305 * algorithm.
306 *
307 * If dp, dev or path NULL, return EDOOFUS. XXX wise?
308 *
309 * Set *path = NULL; *dev = NULL;
310 *
311 * Walk through the device_path until we find either a media device path.
312 * Return EINVAL if not found. Return EINVAL if walking dp would
313 * land us more than sanity size away from the start (4k).
314 *
315 * If we find a media descriptor, we search through the geom mesh to see if we
316 * can find a matching node. If no match is found in the mesh that matches,
317 * return ENXIO.
318 *
319 * Once we find a matching node, we search to see if there is a filesystem
320 * mounted on it. If we find nothing, then search each of the devices that are
321 * mounted to see if we can work up the geom tree to find the matching node. if
322 * we still can't find anything, *dev = sprintf("/dev/%s", provider_name
323 * of the original node we found), but return ENOTBLK.
324 *
325 * Record the dev of the mountpoint in *dev.
326 *
327 * Once we find something, check to see if the next node in the device path is
328 * the end of list. If so, return the mountpoint.
329 *
330 * If the next node isn't a File path node, return EFTYPE.
331 *
332 * Extract the path from the File path node(s). translate any \ file separators
333 * to /. Append the result to the mount point. Copy the resulting path into
334 * *path.  Stat that path. If it is not found, return the errorr from stat.
335 *
336 * Finally, check to make sure the resulting path is still on the same
337 * device. If not, return ENODEV.
338 *
339 * Otherwise return 0.
340 *
341 * The dev or full path that's returned is malloced, so needs to be freed when
342 * the caller is done about it. Unlike many other functions, we can return data
343 * with an error code, so pay attention.
344 */
345int
346efivar_device_path_to_unix_path(const_efidp dp, char **dev, char **relpath, char **abspath)
347{
348	const_efidp walker;
349	struct gmesh mesh;
350	int rv = 0;
351
352	/*
353	 * Sanity check args, fail early
354	 */
355	if (dp == NULL || dev == NULL || relpath == NULL || abspath == NULL)
356		return (EDOOFUS);
357
358	*dev = NULL;
359	*relpath = NULL;
360	*abspath = NULL;
361
362	/*
363	 * Find the first media device path we can. If we go too far,
364	 * assume the passed in device path is bogus. If we hit the end
365	 * then we didn't find a media device path, so signal that error.
366	 */
367	walker = dp;
368	if (!ValidLen(walker))
369		return (EINVAL);
370	while (DevicePathType(walker) != MEDIA_DEVICE_PATH &&
371	    DevicePathType(walker) != END_DEVICE_PATH_TYPE) {
372		walker = (const_efidp)NextDevicePathNode(walker);
373		if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
374			return (EINVAL);
375		if (!ValidLen(walker))
376			return (EINVAL);
377	}
378	if (DevicePathType(walker) !=  MEDIA_DEVICE_PATH)
379		return (EINVAL);
380
381	/*
382	 * There's several types of media paths. We're only interested in the
383	 * hard disk path, as it's really the only relevant one to booting. The
384	 * CD path just might also be relevant, and would be easy to add, but
385	 * isn't supported. A file path too is relevant, but at this stage, it's
386	 * premature because we're trying to translate a specification for a device
387	 * and path on that device into a unix path, or at the very least, a
388	 * geom device : path-on-device.
389	 *
390	 * Also, ZFS throws a bit of a monkey wrench in here since it doesn't have
391	 * a device path type (it creates a new virtual device out of one or more
392	 * storage devices).
393	 *
394	 * For all of them, we'll need to know the geoms, so allocate / free the
395	 * geom mesh here since it's safer than doing it in each sub-function
396	 * which may have many error exits.
397	 */
398	if (geom_gettree(&mesh))
399		return (ENOMEM);
400
401	rv = EINVAL;
402	if (DevicePathSubType(walker) == MEDIA_HARDDRIVE_DP)
403		rv = efi_hd_to_unix(&mesh, walker, dev, relpath, abspath);
404#ifdef notyet
405	else if (is_cdrom_device(walker))
406		rv = efi_cdrom_to_unix(&mesh, walker, dev, relpath, abspath);
407	else if (is_floppy_device(walker))
408		rv = efi_floppy_to_unix(&mesh, walker, dev, relpath, abspath);
409	else if (is_zpool_device(walker))
410		rv = efi_zpool_to_unix(&mesh, walker, dev, relpath, abspath);
411#endif
412	geom_deletetree(&mesh);
413
414	return (rv);
415}
416
417/*
418 * Construct the EFI path to a current unix path as follows.
419 *
420 * The path may be of one of three forms:
421 *	1) /path/to/file -- full path to a file. The file need not be present,
422 *		but /path/to must be. It must reside on a local filesystem
423 *		mounted on a GPT or MBR partition.
424 *	2) //path/to/file -- Shorthand for 'On the EFI partition, \path\to\file'
425 *		where 'The EFI Partition' is a partition that's type is 'efi'
426 *		on the same disk that / is mounted from. If there are multiple
427 *		or no 'efi' parittions on that disk, or / isn't on a disk that
428 *		we can trace back to a physical device, an error will result
429 *	3) [/dev/]geom-name:/path/to/file -- Use the specified partition
430 *		(and it must be a GPT or MBR partition) with the specified
431 *		path. The latter is not authenticated.
432 * all path forms translate any \ characters to / before further processing.
433 * When a file path node is created, all / characters are translated back
434 * to \.
435 *
436 * For paths of the first form:
437 *	find where the filesystem is mount (either the file directly, or
438 *		its parent directory).
439 *	translate any logical device name (eg lable) to a physical one
440 *	If not possible, return ENXIO
441 *	If the physical path is unsupported (Eg not on a GPT or MBR disk),
442 *		return ENXIO
443 *	Create a media device path node.
444 *	append the relative path from the mountpoint to the media device node
445 * 		as a file path.
446 *
447 * For paths matching the second form:
448 *	find the EFI partition corresponding to the root fileystem.
449 *	If none found, return ENXIO
450 *	Create a media device path node for the found partition
451 *	Append a File Path to the end for the rest of the file.
452 *
453 * For paths of the third form
454 *	Translate the geom-name passed in into a physical partition
455 *		name.
456 *	Return ENXIO if the translation fails
457 *	Make a media device path for it
458 *	append the part after the : as a File path node.
459 */
460
461static char *
462path_to_file_dp(const char *relpath)
463{
464	char *rv;
465
466	asprintf(&rv, "File(%s)", relpath);
467	return rv;
468}
469
470static char *
471find_geom_efi_on_root(struct gmesh *mesh)
472{
473	struct statfs buf;
474	const char *dev;
475	struct gprovider *pp;
476//	struct ggeom *disk;
477	struct gconsumer *cp;
478
479	/*
480	 * Find /'s geom. Assume it's mounted on /dev/ and filter out all the
481	 * filesystems that aren't.
482	 */
483	if (statfs("/", &buf) != 0)
484		return (NULL);
485	dev = buf.f_mntfromname;
486	if (*dev != '/' || strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
487		return (NULL);
488	dev += sizeof(_PATH_DEV) -1;
489	pp = find_provider_by_name(mesh, dev);
490	if (pp == NULL)
491		return (NULL);
492
493	/*
494	 * If the provider is a LABEL, find it's outer PART class, if any. We
495	 * only operate on partitions.
496	 */
497	if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0) {
498		LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
499			if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_PART) == 0) {
500				pp = cp->lg_provider;
501				break;
502			}
503		}
504	}
505	if (strcmp(pp->lg_geom->lg_class->lg_name, G_PART) != 0)
506		return (NULL);
507
508#if 0
509	/* This doesn't work because we can't get the data to walk UP the tree it seems */
510
511	/*
512	 * Now that we've found the PART that we have mounted as root, find the
513	 * first efi typed partition that's a peer, if any.
514	 */
515	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
516		if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_DISK) == 0) {
517			disk = cp->lg_provider->lg_geom;
518			break;
519		}
520	}
521	if (disk == NULL)	/* This is very bad -- old nested partitions -- no support ? */
522		return (NULL);
523#endif
524
525#if 0
526	/* This doesn't work because we can't get the data to walk UP the tree it seems */
527
528	/*
529	 * With the disk provider, we can look for its consumers to see if any are the proper type.
530	 */
531	LIST_FOREACH(pp, &disk->lg_consumer, lg_consumer) {
532		type = geom_pp_attr(mesh, pp, "type");
533		if (type == NULL)
534			continue;
535		if (strcmp(type, "efi") != 0)
536			continue;
537		efimedia = geom_pp_attr(mesh, pp, "efimedia");
538		if (efimedia == NULL)
539			return (NULL);
540		return strdup(efimedia);
541	}
542#endif
543	return (NULL);
544}
545
546
547static char *
548find_geom_efimedia(struct gmesh *mesh, const char *dev)
549{
550	struct gprovider *pp;
551	const char *efimedia;
552
553	pp = find_provider_by_name(mesh, dev);
554	if (pp == NULL)
555		return (NULL);
556	efimedia = geom_pp_attr(mesh, pp, "efimedia");
557
558	/*
559	 * If this device doesn't hav an efimedia attribute, see if it is a
560	 * glabel node, and if so look for the underlying provider to get the
561	 * efimedia attribute from.
562	 */
563	if (efimedia == NULL &&
564	    strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0)
565		efimedia = find_geom_efimedia(mesh, pp->lg_geom->lg_name);
566	if (efimedia == NULL)
567		return (NULL);
568	return strdup(efimedia);
569}
570
571static int
572build_dp(const char *efimedia, const char *relpath, efidp *dp)
573{
574	char *fp = NULL, *dptxt = NULL, *cp, *rp = NULL;
575	int rv = 0;
576	efidp out = NULL;
577	size_t len;
578
579	if (relpath != NULL) {
580		rp = strdup(relpath);
581		for (cp = rp; *cp; cp++)
582			if (*cp == '/')
583				*cp = '\\';
584		fp = path_to_file_dp(rp);
585		free(rp);
586		if (fp == NULL) {
587			rv = ENOMEM;
588			goto errout;
589		}
590	}
591
592	asprintf(&dptxt, "%s/%s", efimedia, fp == NULL ? "" : fp);
593	out = malloc(8192);
594	len = efidp_parse_device_path(dptxt, out, 8192);
595	if (len > 8192) {
596		rv = ENOMEM;
597		goto errout;
598	}
599	if (len == 0) {
600		rv = EINVAL;
601		goto errout;
602	}
603
604	*dp = out;
605errout:
606	if (rv) {
607		free(out);
608	}
609	free(dptxt);
610	free(fp);
611
612	return rv;
613}
614
615/* Handles //path/to/file */
616/*
617 * Which means: find the disk that has /. Then look for a EFI partition
618 * and use that for the efimedia and /path/to/file as relative to that.
619 * Not sure how ZFS will work here since we can't easily make the leap
620 * to the geom from the zpool.
621 */
622static int
623efipart_to_dp(struct gmesh *mesh, char *path, efidp *dp)
624{
625	char *efimedia = NULL;
626	int rv;
627
628	efimedia = find_geom_efi_on_root(mesh);
629#ifdef notyet
630	if (efimedia == NULL)
631		efimedia = find_efi_on_zfsroot(dev);
632#endif
633	if (efimedia == NULL) {
634		rv = ENOENT;
635		goto errout;
636	}
637
638	rv = build_dp(efimedia, path + 1, dp);
639errout:
640	free(efimedia);
641
642	return rv;
643}
644
645/* Handles [/dev/]geom:[/]path/to/file */
646/* Handles zfs-dataset:[/]path/to/file (this may include / ) */
647static int
648dev_path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
649{
650	char *relpath, *dev, *efimedia = NULL;
651	int rv = 0;
652
653	relpath = strchr(path, ':');
654	assert(relpath != NULL);
655	*relpath++ = '\0';
656
657	dev = path;
658	if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
659		dev += sizeof(_PATH_DEV) -1;
660
661	efimedia = find_geom_efimedia(mesh, dev);
662#ifdef notyet
663	if (efimedia == NULL)
664		find_zfs_efi_media(dev);
665#endif
666	if (efimedia == NULL) {
667		rv = ENOENT;
668		goto errout;
669	}
670	rv = build_dp(efimedia, relpath, dp);
671errout:
672	free(efimedia);
673
674	return rv;
675}
676
677/* Handles /path/to/file */
678/* Handles /dev/foo/bar */
679static int
680path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
681{
682	struct statfs buf;
683	char *rp = NULL, *ep, *dev, *efimedia = NULL;
684	int rv = 0;
685
686	rp = realpath(path, NULL);
687	if (rp == NULL) {
688		rv = errno;
689		goto errout;
690	}
691
692	if (statfs(rp, &buf) != 0) {
693		rv = errno;
694		goto errout;
695	}
696
697	dev = buf.f_mntfromname;
698	/*
699	 * If we're fed a raw /dev/foo/bar, then devfs is returned from the
700	 * statfs call. In that case, use that dev and assume we have a path
701	 * of nothing.
702	 */
703	if (strcmp(dev, "devfs") == 0) {
704		dev = rp + sizeof(_PATH_DEV) - 1;
705		ep = NULL;
706	} else {
707		if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
708			dev += sizeof(_PATH_DEV) - 1;
709		ep = rp + strlen(buf.f_mntonname);
710	}
711
712	efimedia = find_geom_efimedia(mesh, dev);
713#ifdef notyet
714	if (efimedia == NULL)
715		find_zfs_efi_media(dev);
716#endif
717	if (efimedia == NULL) {
718		rv = ENOENT;
719		goto errout;
720	}
721
722	rv = build_dp(efimedia, ep, dp);
723errout:
724	free(efimedia);
725	free(rp);
726	if (rv != 0) {
727		free(*dp);
728		*dp = NULL;
729	}
730	return (rv);
731}
732
733int
734efivar_unix_path_to_device_path(const char *path, efidp *dp)
735{
736	char *modpath = NULL, *cp;
737	int rv = ENOMEM;
738	struct gmesh mesh;
739
740	/*
741	 * Fail early for clearly bogus things
742	 */
743	if (path == NULL || dp == NULL)
744		return (EDOOFUS);
745
746	/*
747	 * We'll need the goem mesh to grovel through it to find the
748	 * efimedia attribute for any devices we find. Grab it here
749	 * and release it to simplify the error paths out of the
750	 * subordinate functions
751	 */
752	if (geom_gettree(&mesh))
753		return (errno);
754
755	/*
756	 * Convert all \ to /. We'll convert them back again when
757	 * we encode the file. Boot loaders are expected to cope.
758	 */
759	modpath = strdup(path);
760	if (modpath == NULL)
761		goto out;
762	for (cp = modpath; *cp; cp++)
763		if (*cp == '\\')
764			*cp = '/';
765
766	if (modpath[0] == '/' && modpath[1] == '/')	/* Handle //foo/bar/baz */
767		rv = efipart_to_dp(&mesh, modpath, dp);
768	else if (strchr(modpath, ':'))			/* Handle dev:/bar/baz */
769		rv = dev_path_to_dp(&mesh, modpath, dp);
770	else						/* Handle /a/b/c */
771		rv = path_to_dp(&mesh, modpath, dp);
772
773out:
774	geom_deletetree(&mesh);
775	free(modpath);
776
777	return (rv);
778}
779