]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libefivar/efivar-dp-xlate.c
amd64: use register macros for gdb_cpu_getreg()
[FreeBSD/FreeBSD.git] / lib / libefivar / efivar-dp-xlate.c
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
61 static const char *
62 geom_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
74 static struct gprovider *
75 find_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
110 static struct gprovider *
111 find_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
130 static int
131 efi_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         }
250 break2:
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
266 errout:
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  */
318 int
319 efivar_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
434 static char *
435 path_to_file_dp(const char *relpath)
436 {
437         char *rv;
438
439         asprintf(&rv, "File(%s)", relpath);
440         return rv;
441 }
442
443 static char *
444 find_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
520 static char *
521 find_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
535 static int
536 build_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;
567 errout:
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  */
584 static int
585 efipart_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);
601 errout:
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 / ) */
609 static int
610 dev_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);
633 errout:
634         free(efimedia);
635
636         return rv;
637 }
638
639 /* Handles /path/to/file */
640 static int
641 path_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);
674 errout:
675         free(efimedia);
676         free(rp);
677         if (rv != 0) {
678                 free(*dp);
679                 *dp = NULL;
680         }
681         return (rv);
682 }
683
684 int
685 efivar_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
724 out:
725         geom_deletetree(&mesh);
726         free(modpath);
727
728         return (rv);
729 }