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