109ee5d8aSWarner Losh /*-
209ee5d8aSWarner Losh * Copyright (c) 2017 Netflix, Inc.
309ee5d8aSWarner Losh *
409ee5d8aSWarner Losh * Redistribution and use in source and binary forms, with or without
509ee5d8aSWarner Losh * modification, are permitted provided that the following conditions
609ee5d8aSWarner Losh * are met:
709ee5d8aSWarner Losh * 1. Redistributions of source code must retain the above copyright
86decf2ccSEd Maste * notice, this list of conditions and the following disclaimer.
909ee5d8aSWarner Losh * 2. Redistributions in binary form must reproduce the above copyright
1009ee5d8aSWarner Losh * notice, this list of conditions and the following disclaimer in the
1109ee5d8aSWarner Losh * documentation and/or other materials provided with the distribution.
1209ee5d8aSWarner Losh *
136decf2ccSEd Maste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
146decf2ccSEd Maste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
156decf2ccSEd Maste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
166decf2ccSEd Maste * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
176decf2ccSEd Maste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
186decf2ccSEd Maste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
196decf2ccSEd Maste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
206decf2ccSEd Maste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
216decf2ccSEd Maste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
226decf2ccSEd Maste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
236decf2ccSEd Maste * SUCH DAMAGE.
2409ee5d8aSWarner Losh */
2509ee5d8aSWarner Losh
2609ee5d8aSWarner Losh #include <sys/param.h>
2709ee5d8aSWarner Losh #include <sys/ucred.h>
2809ee5d8aSWarner Losh #include <sys/mount.h>
2909ee5d8aSWarner Losh
3009ee5d8aSWarner Losh #undef MAX
3109ee5d8aSWarner Losh #undef MIN
3209ee5d8aSWarner Losh
3309ee5d8aSWarner Losh #include <assert.h>
3409ee5d8aSWarner Losh #include <efivar.h>
3509ee5d8aSWarner Losh #include <errno.h>
3609ee5d8aSWarner Losh #include <libgeom.h>
3709ee5d8aSWarner Losh #include <paths.h>
3809ee5d8aSWarner Losh #include <stdio.h>
3909ee5d8aSWarner Losh #include <string.h>
4009ee5d8aSWarner Losh
4109ee5d8aSWarner Losh #include "efichar.h"
4209ee5d8aSWarner Losh
4309ee5d8aSWarner Losh #include "efi-osdep.h"
4409ee5d8aSWarner Losh #include "efivar-dp.h"
4509ee5d8aSWarner Losh
4609ee5d8aSWarner Losh #include "uefi-dplib.h"
4709ee5d8aSWarner Losh
4809ee5d8aSWarner Losh #define MAX_DP_SANITY 4096 /* Biggest device path in bytes */
4909ee5d8aSWarner Losh #define MAX_DP_TEXT_LEN 4096 /* Longest string rep of dp */
5009ee5d8aSWarner Losh
51f17a98a6SWarner Losh #define ValidLen(dp) (DevicePathNodeLength(dp) >= sizeof(EFI_DEVICE_PATH_PROTOCOL) && \
52f17a98a6SWarner Losh DevicePathNodeLength(dp) < MAX_DP_SANITY)
53f17a98a6SWarner Losh
5409ee5d8aSWarner Losh #define G_PART "PART"
5509ee5d8aSWarner Losh #define G_LABEL "LABEL"
5609ee5d8aSWarner Losh #define G_DISK "DISK"
5709ee5d8aSWarner Losh
5809ee5d8aSWarner Losh static const char *
geom_pp_attr(struct gmesh * mesh,struct gprovider * pp,const char * attr)5909ee5d8aSWarner Losh geom_pp_attr(struct gmesh *mesh, struct gprovider *pp, const char *attr)
6009ee5d8aSWarner Losh {
6109ee5d8aSWarner Losh struct gconfig *conf;
6209ee5d8aSWarner Losh
6309ee5d8aSWarner Losh LIST_FOREACH(conf, &pp->lg_config, lg_config) {
6409ee5d8aSWarner Losh if (strcmp(conf->lg_name, attr) != 0)
6509ee5d8aSWarner Losh continue;
6609ee5d8aSWarner Losh return (conf->lg_val);
6709ee5d8aSWarner Losh }
6809ee5d8aSWarner Losh return (NULL);
6909ee5d8aSWarner Losh }
7009ee5d8aSWarner Losh
7109ee5d8aSWarner Losh static struct gprovider *
find_provider_by_efimedia(struct gmesh * mesh,const char * efimedia)7209ee5d8aSWarner Losh find_provider_by_efimedia(struct gmesh *mesh, const char *efimedia)
7309ee5d8aSWarner Losh {
7409ee5d8aSWarner Losh struct gclass *classp;
7509ee5d8aSWarner Losh struct ggeom *gp;
7609ee5d8aSWarner Losh struct gprovider *pp;
7709ee5d8aSWarner Losh const char *val;
7809ee5d8aSWarner Losh
7909ee5d8aSWarner Losh /*
8009ee5d8aSWarner Losh * Find the partition class so we can search it...
8109ee5d8aSWarner Losh */
8209ee5d8aSWarner Losh LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
8309ee5d8aSWarner Losh if (strcasecmp(classp->lg_name, G_PART) == 0)
8409ee5d8aSWarner Losh break;
8509ee5d8aSWarner Losh }
8609ee5d8aSWarner Losh if (classp == NULL)
8709ee5d8aSWarner Losh return (NULL);
8809ee5d8aSWarner Losh
8909ee5d8aSWarner Losh /*
9009ee5d8aSWarner Losh * Each geom will have a number of providers, search each
9109ee5d8aSWarner Losh * one of them for the efimedia that matches.
9209ee5d8aSWarner Losh */
9309ee5d8aSWarner Losh /* XXX just used gpart class since I know it's the only one, but maybe I should search all classes */
9409ee5d8aSWarner Losh LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
9509ee5d8aSWarner Losh LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
9609ee5d8aSWarner Losh val = geom_pp_attr(mesh, pp, "efimedia");
9709ee5d8aSWarner Losh if (val == NULL)
9809ee5d8aSWarner Losh continue;
9909ee5d8aSWarner Losh if (strcasecmp(efimedia, val) == 0)
10009ee5d8aSWarner Losh return (pp);
10109ee5d8aSWarner Losh }
10209ee5d8aSWarner Losh }
10309ee5d8aSWarner Losh
10409ee5d8aSWarner Losh return (NULL);
10509ee5d8aSWarner Losh }
10609ee5d8aSWarner Losh
10709ee5d8aSWarner Losh static struct gprovider *
find_provider_by_name(struct gmesh * mesh,const char * name)10809ee5d8aSWarner Losh find_provider_by_name(struct gmesh *mesh, const char *name)
10909ee5d8aSWarner Losh {
11009ee5d8aSWarner Losh struct gclass *classp;
11109ee5d8aSWarner Losh struct ggeom *gp;
11209ee5d8aSWarner Losh struct gprovider *pp;
11309ee5d8aSWarner Losh
11409ee5d8aSWarner Losh LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
11509ee5d8aSWarner Losh LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
11609ee5d8aSWarner Losh LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
11709ee5d8aSWarner Losh if (strcmp(pp->lg_name, name) == 0)
11809ee5d8aSWarner Losh return (pp);
11909ee5d8aSWarner Losh }
12009ee5d8aSWarner Losh }
12109ee5d8aSWarner Losh }
12209ee5d8aSWarner Losh
12309ee5d8aSWarner Losh return (NULL);
12409ee5d8aSWarner Losh }
12509ee5d8aSWarner Losh
12609ee5d8aSWarner Losh
12709ee5d8aSWarner Losh static int
efi_hd_to_unix(struct gmesh * mesh,const_efidp dp,char ** dev,char ** relpath,char ** abspath)12809ee5d8aSWarner Losh efi_hd_to_unix(struct gmesh *mesh, const_efidp dp, char **dev, char **relpath, char **abspath)
12909ee5d8aSWarner Losh {
13009ee5d8aSWarner Losh int rv = 0, n, i;
13109ee5d8aSWarner Losh const_efidp media, file, walker;
13209ee5d8aSWarner Losh size_t len, mntlen;
13309ee5d8aSWarner Losh char buf[MAX_DP_TEXT_LEN];
1342b460910SWarner Losh char *pwalk, *newdev = NULL;
13509ee5d8aSWarner Losh struct gprovider *pp, *provider;
13609ee5d8aSWarner Losh struct statfs *mnt;
1372b460910SWarner Losh struct gclass *glabel;
1382b460910SWarner Losh struct ggeom *gp;
13909ee5d8aSWarner Losh
14009ee5d8aSWarner Losh walker = media = dp;
1412b460910SWarner Losh *dev = NULL;
1422b460910SWarner Losh *relpath = NULL;
14309ee5d8aSWarner Losh
14409ee5d8aSWarner Losh /*
14509ee5d8aSWarner Losh * Now, we can either have a filepath node next, or the end.
14609ee5d8aSWarner Losh * Otherwise, it's an error.
14709ee5d8aSWarner Losh */
148f17a98a6SWarner Losh if (!ValidLen(walker))
149f17a98a6SWarner Losh return (EINVAL);
15009ee5d8aSWarner Losh walker = (const_efidp)NextDevicePathNode(walker);
15109ee5d8aSWarner Losh if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
15209ee5d8aSWarner Losh return (EINVAL);
15309ee5d8aSWarner Losh if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
15409ee5d8aSWarner Losh DevicePathSubType(walker) == MEDIA_FILEPATH_DP)
15509ee5d8aSWarner Losh file = walker;
15609ee5d8aSWarner Losh else if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
15709ee5d8aSWarner Losh DevicePathType(walker) == END_DEVICE_PATH_TYPE)
15809ee5d8aSWarner Losh file = NULL;
15909ee5d8aSWarner Losh else
16009ee5d8aSWarner Losh return (EINVAL);
16109ee5d8aSWarner Losh
16209ee5d8aSWarner Losh /*
16309ee5d8aSWarner Losh * Format this node. We're going to look for it as a efimedia
16409ee5d8aSWarner Losh * attribute of some geom node. Once we find that node, we use it
16509ee5d8aSWarner Losh * as the device it comes from, at least provisionally.
16609ee5d8aSWarner Losh */
16709ee5d8aSWarner Losh len = efidp_format_device_path_node(buf, sizeof(buf), media);
16809ee5d8aSWarner Losh if (len > sizeof(buf))
16909ee5d8aSWarner Losh return (EINVAL);
17009ee5d8aSWarner Losh
17109ee5d8aSWarner Losh pp = find_provider_by_efimedia(mesh, buf);
17209ee5d8aSWarner Losh if (pp == NULL) {
17309ee5d8aSWarner Losh rv = ENOENT;
17409ee5d8aSWarner Losh goto errout;
17509ee5d8aSWarner Losh }
17609ee5d8aSWarner Losh
17709ee5d8aSWarner Losh /*
17809ee5d8aSWarner Losh * No file specified, just return the device. Don't even look
17909ee5d8aSWarner Losh * for a mountpoint. XXX Sane?
18009ee5d8aSWarner Losh */
18109ee5d8aSWarner Losh if (file == NULL)
18209ee5d8aSWarner Losh goto errout;
18309ee5d8aSWarner Losh
18409ee5d8aSWarner Losh /*
18509ee5d8aSWarner Losh * Now extract the relative path. The next node in the device path should
18609ee5d8aSWarner Losh * be a filesystem node. If not, we have issues.
18709ee5d8aSWarner Losh */
18809ee5d8aSWarner Losh *relpath = efidp_extract_file_path(file);
18909ee5d8aSWarner Losh if (*relpath == NULL) {
19009ee5d8aSWarner Losh rv = ENOMEM;
19109ee5d8aSWarner Losh goto errout;
19209ee5d8aSWarner Losh }
19309ee5d8aSWarner Losh for (pwalk = *relpath; *pwalk; pwalk++)
19409ee5d8aSWarner Losh if (*pwalk == '\\')
19509ee5d8aSWarner Losh *pwalk = '/';
19609ee5d8aSWarner Losh
19709ee5d8aSWarner Losh /*
19809ee5d8aSWarner Losh * To find the absolute path, we have to look for where we're mounted.
19909ee5d8aSWarner Losh * We only look a little hard, since looking too hard can come up with
20009ee5d8aSWarner Losh * false positives (imagine a graid, one of whose devices is *dev).
20109ee5d8aSWarner Losh */
20209ee5d8aSWarner Losh n = getfsstat(NULL, 0, MNT_NOWAIT) + 1;
20309ee5d8aSWarner Losh if (n < 0) {
20409ee5d8aSWarner Losh rv = errno;
20509ee5d8aSWarner Losh goto errout;
20609ee5d8aSWarner Losh }
20709ee5d8aSWarner Losh mntlen = sizeof(struct statfs) * n;
20809ee5d8aSWarner Losh mnt = malloc(mntlen);
20909ee5d8aSWarner Losh n = getfsstat(mnt, mntlen, MNT_NOWAIT);
21009ee5d8aSWarner Losh if (n < 0) {
21109ee5d8aSWarner Losh rv = errno;
21209ee5d8aSWarner Losh goto errout;
21309ee5d8aSWarner Losh }
2142b460910SWarner Losh
2152b460910SWarner Losh /*
2162b460910SWarner Losh * Find glabel, if it exists. It's OK if not: we'll skip searching for
2172b460910SWarner Losh * labels.
2182b460910SWarner Losh */
2192b460910SWarner Losh LIST_FOREACH(glabel, &mesh->lg_class, lg_class) {
2202b460910SWarner Losh if (strcmp(glabel->lg_name, G_LABEL) == 0)
2212b460910SWarner Losh break;
2222b460910SWarner Losh }
2232b460910SWarner Losh
22409ee5d8aSWarner Losh provider = pp;
22509ee5d8aSWarner Losh for (i = 0; i < n; i++) {
22609ee5d8aSWarner Losh /*
22709ee5d8aSWarner Losh * Skip all pseudo filesystems. This also skips the real filesytsem
22809ee5d8aSWarner Losh * of ZFS. There's no EFI designator for ZFS in the standard, so
22909ee5d8aSWarner Losh * we'll need to invent one, but its decoding will be handled in
23009ee5d8aSWarner Losh * a separate function.
23109ee5d8aSWarner Losh */
2322b460910SWarner Losh if (strncmp(mnt[i].f_mntfromname, "/dev/", 5) != 0)
23309ee5d8aSWarner Losh continue;
23409ee5d8aSWarner Losh
23509ee5d8aSWarner Losh /*
23609ee5d8aSWarner Losh * First see if it is directly attached
23709ee5d8aSWarner Losh */
2382b460910SWarner Losh if (strcmp(provider->lg_name, mnt[i].f_mntfromname + 5) == 0) {
2392b460910SWarner Losh newdev = provider->lg_name;
24009ee5d8aSWarner Losh break;
2412b460910SWarner Losh }
24209ee5d8aSWarner Losh
24309ee5d8aSWarner Losh /*
2442b460910SWarner Losh * Next see if it is attached via one of the physical disk's labels.
2452b460910SWarner Losh * We can't search directly from the pointers we have for the
2462b460910SWarner Losh * provider, so we have to cast a wider net for all labels and
2472b460910SWarner Losh * filter those down to geoms whose name matches the PART provider
2482b460910SWarner Losh * we found the efimedia attribute on.
24909ee5d8aSWarner Losh */
2502b460910SWarner Losh if (glabel == NULL)
25109ee5d8aSWarner Losh continue;
2522b460910SWarner Losh LIST_FOREACH(gp, &glabel->lg_geom, lg_geom) {
2532b460910SWarner Losh if (strcmp(gp->lg_name, provider->lg_name) != 0) {
2542b460910SWarner Losh continue;
2552b460910SWarner Losh }
2562b460910SWarner Losh LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
2572b460910SWarner Losh if (strcmp(pp->lg_name, mnt[i].f_mntfromname + 5) == 0) {
2582b460910SWarner Losh newdev = pp->lg_name;
25909ee5d8aSWarner Losh goto break2;
26009ee5d8aSWarner Losh }
2612b460910SWarner Losh }
2622b460910SWarner Losh }
26309ee5d8aSWarner Losh /* Not the one, try the next mount point */
26409ee5d8aSWarner Losh }
26509ee5d8aSWarner Losh break2:
26609ee5d8aSWarner Losh
26709ee5d8aSWarner Losh /*
2682b460910SWarner Losh * If nothing better was mounted, then use the provider we found as
2692b460910SWarner Losh * is. It's the most correct thing we can return in that acse.
2702b460910SWarner Losh */
2712b460910SWarner Losh if (newdev == NULL)
2722b460910SWarner Losh newdev = provider->lg_name;
2732b460910SWarner Losh *dev = strdup(newdev);
2742b460910SWarner Losh if (*dev == NULL) {
2752b460910SWarner Losh rv = ENOMEM;
2762b460910SWarner Losh goto errout;
2772b460910SWarner Losh }
2782b460910SWarner Losh
2792b460910SWarner Losh /*
28009ee5d8aSWarner Losh * No mountpoint found, no absolute path possible
28109ee5d8aSWarner Losh */
28209ee5d8aSWarner Losh if (i >= n)
28309ee5d8aSWarner Losh goto errout;
28409ee5d8aSWarner Losh
28509ee5d8aSWarner Losh /*
28609ee5d8aSWarner Losh * Construct absolute path and we're finally done.
28709ee5d8aSWarner Losh */
28809ee5d8aSWarner Losh if (strcmp(mnt[i].f_mntonname, "/") == 0)
28909ee5d8aSWarner Losh asprintf(abspath, "/%s", *relpath);
29009ee5d8aSWarner Losh else
29109ee5d8aSWarner Losh asprintf(abspath, "%s/%s", mnt[i].f_mntonname, *relpath);
29209ee5d8aSWarner Losh
29309ee5d8aSWarner Losh errout:
29409ee5d8aSWarner Losh if (rv != 0) {
29509ee5d8aSWarner Losh free(*dev);
29609ee5d8aSWarner Losh *dev = NULL;
29709ee5d8aSWarner Losh free(*relpath);
29809ee5d8aSWarner Losh *relpath = NULL;
29909ee5d8aSWarner Losh }
30009ee5d8aSWarner Losh return (rv);
30109ee5d8aSWarner Losh }
30209ee5d8aSWarner Losh
30309ee5d8aSWarner Losh /*
30409ee5d8aSWarner Losh * Translate the passed in device_path to a unix path via the following
30509ee5d8aSWarner Losh * algorithm.
30609ee5d8aSWarner Losh *
30709ee5d8aSWarner Losh * If dp, dev or path NULL, return EDOOFUS. XXX wise?
30809ee5d8aSWarner Losh *
30909ee5d8aSWarner Losh * Set *path = NULL; *dev = NULL;
31009ee5d8aSWarner Losh *
31109ee5d8aSWarner Losh * Walk through the device_path until we find either a media device path.
31209ee5d8aSWarner Losh * Return EINVAL if not found. Return EINVAL if walking dp would
31309ee5d8aSWarner Losh * land us more than sanity size away from the start (4k).
31409ee5d8aSWarner Losh *
31509ee5d8aSWarner Losh * If we find a media descriptor, we search through the geom mesh to see if we
31609ee5d8aSWarner Losh * can find a matching node. If no match is found in the mesh that matches,
31709ee5d8aSWarner Losh * return ENXIO.
31809ee5d8aSWarner Losh *
31909ee5d8aSWarner Losh * Once we find a matching node, we search to see if there is a filesystem
32009ee5d8aSWarner Losh * mounted on it. If we find nothing, then search each of the devices that are
32109ee5d8aSWarner Losh * mounted to see if we can work up the geom tree to find the matching node. if
32209ee5d8aSWarner Losh * we still can't find anything, *dev = sprintf("/dev/%s", provider_name
32309ee5d8aSWarner Losh * of the original node we found), but return ENOTBLK.
32409ee5d8aSWarner Losh *
32509ee5d8aSWarner Losh * Record the dev of the mountpoint in *dev.
32609ee5d8aSWarner Losh *
32709ee5d8aSWarner Losh * Once we find something, check to see if the next node in the device path is
32809ee5d8aSWarner Losh * the end of list. If so, return the mountpoint.
32909ee5d8aSWarner Losh *
33009ee5d8aSWarner Losh * If the next node isn't a File path node, return EFTYPE.
33109ee5d8aSWarner Losh *
33209ee5d8aSWarner Losh * Extract the path from the File path node(s). translate any \ file separators
33309ee5d8aSWarner Losh * to /. Append the result to the mount point. Copy the resulting path into
33409ee5d8aSWarner Losh * *path. Stat that path. If it is not found, return the errorr from stat.
33509ee5d8aSWarner Losh *
33609ee5d8aSWarner Losh * Finally, check to make sure the resulting path is still on the same
33709ee5d8aSWarner Losh * device. If not, return ENODEV.
33809ee5d8aSWarner Losh *
33909ee5d8aSWarner Losh * Otherwise return 0.
34009ee5d8aSWarner Losh *
34109ee5d8aSWarner Losh * The dev or full path that's returned is malloced, so needs to be freed when
34209ee5d8aSWarner Losh * the caller is done about it. Unlike many other functions, we can return data
34309ee5d8aSWarner Losh * with an error code, so pay attention.
34409ee5d8aSWarner Losh */
34509ee5d8aSWarner Losh int
efivar_device_path_to_unix_path(const_efidp dp,char ** dev,char ** relpath,char ** abspath)34609ee5d8aSWarner Losh efivar_device_path_to_unix_path(const_efidp dp, char **dev, char **relpath, char **abspath)
34709ee5d8aSWarner Losh {
34809ee5d8aSWarner Losh const_efidp walker;
34909ee5d8aSWarner Losh struct gmesh mesh;
35009ee5d8aSWarner Losh int rv = 0;
35109ee5d8aSWarner Losh
35209ee5d8aSWarner Losh /*
35309ee5d8aSWarner Losh * Sanity check args, fail early
35409ee5d8aSWarner Losh */
35509ee5d8aSWarner Losh if (dp == NULL || dev == NULL || relpath == NULL || abspath == NULL)
35609ee5d8aSWarner Losh return (EDOOFUS);
35709ee5d8aSWarner Losh
35809ee5d8aSWarner Losh *dev = NULL;
35909ee5d8aSWarner Losh *relpath = NULL;
36009ee5d8aSWarner Losh *abspath = NULL;
36109ee5d8aSWarner Losh
36209ee5d8aSWarner Losh /*
36309ee5d8aSWarner Losh * Find the first media device path we can. If we go too far,
36409ee5d8aSWarner Losh * assume the passed in device path is bogus. If we hit the end
36509ee5d8aSWarner Losh * then we didn't find a media device path, so signal that error.
36609ee5d8aSWarner Losh */
36709ee5d8aSWarner Losh walker = dp;
368f17a98a6SWarner Losh if (!ValidLen(walker))
369f17a98a6SWarner Losh return (EINVAL);
37009ee5d8aSWarner Losh while (DevicePathType(walker) != MEDIA_DEVICE_PATH &&
37109ee5d8aSWarner Losh DevicePathType(walker) != END_DEVICE_PATH_TYPE) {
37209ee5d8aSWarner Losh walker = (const_efidp)NextDevicePathNode(walker);
37309ee5d8aSWarner Losh if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
37409ee5d8aSWarner Losh return (EINVAL);
375f17a98a6SWarner Losh if (!ValidLen(walker))
376f17a98a6SWarner Losh return (EINVAL);
37709ee5d8aSWarner Losh }
37809ee5d8aSWarner Losh if (DevicePathType(walker) != MEDIA_DEVICE_PATH)
37909ee5d8aSWarner Losh return (EINVAL);
38009ee5d8aSWarner Losh
38109ee5d8aSWarner Losh /*
38209ee5d8aSWarner Losh * There's several types of media paths. We're only interested in the
38309ee5d8aSWarner Losh * hard disk path, as it's really the only relevant one to booting. The
38409ee5d8aSWarner Losh * CD path just might also be relevant, and would be easy to add, but
38509ee5d8aSWarner Losh * isn't supported. A file path too is relevant, but at this stage, it's
38609ee5d8aSWarner Losh * premature because we're trying to translate a specification for a device
38709ee5d8aSWarner Losh * and path on that device into a unix path, or at the very least, a
38809ee5d8aSWarner Losh * geom device : path-on-device.
38909ee5d8aSWarner Losh *
39009ee5d8aSWarner Losh * Also, ZFS throws a bit of a monkey wrench in here since it doesn't have
39109ee5d8aSWarner Losh * a device path type (it creates a new virtual device out of one or more
39209ee5d8aSWarner Losh * storage devices).
39309ee5d8aSWarner Losh *
39409ee5d8aSWarner Losh * For all of them, we'll need to know the geoms, so allocate / free the
39509ee5d8aSWarner Losh * geom mesh here since it's safer than doing it in each sub-function
39609ee5d8aSWarner Losh * which may have many error exits.
39709ee5d8aSWarner Losh */
39809ee5d8aSWarner Losh if (geom_gettree(&mesh))
39909ee5d8aSWarner Losh return (ENOMEM);
40009ee5d8aSWarner Losh
40109ee5d8aSWarner Losh rv = EINVAL;
40209ee5d8aSWarner Losh if (DevicePathSubType(walker) == MEDIA_HARDDRIVE_DP)
40309ee5d8aSWarner Losh rv = efi_hd_to_unix(&mesh, walker, dev, relpath, abspath);
40409ee5d8aSWarner Losh #ifdef notyet
40509ee5d8aSWarner Losh else if (is_cdrom_device(walker))
40609ee5d8aSWarner Losh rv = efi_cdrom_to_unix(&mesh, walker, dev, relpath, abspath);
40709ee5d8aSWarner Losh else if (is_floppy_device(walker))
40809ee5d8aSWarner Losh rv = efi_floppy_to_unix(&mesh, walker, dev, relpath, abspath);
40909ee5d8aSWarner Losh else if (is_zpool_device(walker))
41009ee5d8aSWarner Losh rv = efi_zpool_to_unix(&mesh, walker, dev, relpath, abspath);
41109ee5d8aSWarner Losh #endif
41209ee5d8aSWarner Losh geom_deletetree(&mesh);
41309ee5d8aSWarner Losh
41409ee5d8aSWarner Losh return (rv);
41509ee5d8aSWarner Losh }
41609ee5d8aSWarner Losh
41709ee5d8aSWarner Losh /*
41809ee5d8aSWarner Losh * Construct the EFI path to a current unix path as follows.
41909ee5d8aSWarner Losh *
42009ee5d8aSWarner Losh * The path may be of one of three forms:
42109ee5d8aSWarner Losh * 1) /path/to/file -- full path to a file. The file need not be present,
42209ee5d8aSWarner Losh * but /path/to must be. It must reside on a local filesystem
42309ee5d8aSWarner Losh * mounted on a GPT or MBR partition.
42409ee5d8aSWarner Losh * 2) //path/to/file -- Shorthand for 'On the EFI partition, \path\to\file'
425b1c5f60cSGordon Bergling * where 'The EFI Partition' is a partition that's type is 'efi'
42609ee5d8aSWarner Losh * on the same disk that / is mounted from. If there are multiple
42709ee5d8aSWarner Losh * or no 'efi' parittions on that disk, or / isn't on a disk that
42809ee5d8aSWarner Losh * we can trace back to a physical device, an error will result
42909ee5d8aSWarner Losh * 3) [/dev/]geom-name:/path/to/file -- Use the specified partition
43009ee5d8aSWarner Losh * (and it must be a GPT or MBR partition) with the specified
43109ee5d8aSWarner Losh * path. The latter is not authenticated.
43209ee5d8aSWarner Losh * all path forms translate any \ characters to / before further processing.
43309ee5d8aSWarner Losh * When a file path node is created, all / characters are translated back
43409ee5d8aSWarner Losh * to \.
43509ee5d8aSWarner Losh *
43609ee5d8aSWarner Losh * For paths of the first form:
43709ee5d8aSWarner Losh * find where the filesystem is mount (either the file directly, or
43809ee5d8aSWarner Losh * its parent directory).
43909ee5d8aSWarner Losh * translate any logical device name (eg lable) to a physical one
44009ee5d8aSWarner Losh * If not possible, return ENXIO
44109ee5d8aSWarner Losh * If the physical path is unsupported (Eg not on a GPT or MBR disk),
44209ee5d8aSWarner Losh * return ENXIO
44309ee5d8aSWarner Losh * Create a media device path node.
44409ee5d8aSWarner Losh * append the relative path from the mountpoint to the media device node
44509ee5d8aSWarner Losh * as a file path.
44609ee5d8aSWarner Losh *
44709ee5d8aSWarner Losh * For paths matching the second form:
44809ee5d8aSWarner Losh * find the EFI partition corresponding to the root fileystem.
44909ee5d8aSWarner Losh * If none found, return ENXIO
45009ee5d8aSWarner Losh * Create a media device path node for the found partition
45109ee5d8aSWarner Losh * Append a File Path to the end for the rest of the file.
45209ee5d8aSWarner Losh *
45309ee5d8aSWarner Losh * For paths of the third form
45409ee5d8aSWarner Losh * Translate the geom-name passed in into a physical partition
45509ee5d8aSWarner Losh * name.
45609ee5d8aSWarner Losh * Return ENXIO if the translation fails
45709ee5d8aSWarner Losh * Make a media device path for it
45809ee5d8aSWarner Losh * append the part after the : as a File path node.
45909ee5d8aSWarner Losh */
46009ee5d8aSWarner Losh
46109ee5d8aSWarner Losh static char *
path_to_file_dp(const char * relpath)46209ee5d8aSWarner Losh path_to_file_dp(const char *relpath)
46309ee5d8aSWarner Losh {
46409ee5d8aSWarner Losh char *rv;
46509ee5d8aSWarner Losh
46609ee5d8aSWarner Losh asprintf(&rv, "File(%s)", relpath);
46709ee5d8aSWarner Losh return rv;
46809ee5d8aSWarner Losh }
46909ee5d8aSWarner Losh
47009ee5d8aSWarner Losh static char *
find_geom_efi_on_root(struct gmesh * mesh)47109ee5d8aSWarner Losh find_geom_efi_on_root(struct gmesh *mesh)
47209ee5d8aSWarner Losh {
47309ee5d8aSWarner Losh struct statfs buf;
47409ee5d8aSWarner Losh const char *dev;
47509ee5d8aSWarner Losh struct gprovider *pp;
47609ee5d8aSWarner Losh // struct ggeom *disk;
47709ee5d8aSWarner Losh struct gconsumer *cp;
47809ee5d8aSWarner Losh
47909ee5d8aSWarner Losh /*
48009ee5d8aSWarner Losh * Find /'s geom. Assume it's mounted on /dev/ and filter out all the
48109ee5d8aSWarner Losh * filesystems that aren't.
48209ee5d8aSWarner Losh */
48309ee5d8aSWarner Losh if (statfs("/", &buf) != 0)
48409ee5d8aSWarner Losh return (NULL);
48509ee5d8aSWarner Losh dev = buf.f_mntfromname;
48609ee5d8aSWarner Losh if (*dev != '/' || strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
48709ee5d8aSWarner Losh return (NULL);
48809ee5d8aSWarner Losh dev += sizeof(_PATH_DEV) -1;
48909ee5d8aSWarner Losh pp = find_provider_by_name(mesh, dev);
49009ee5d8aSWarner Losh if (pp == NULL)
49109ee5d8aSWarner Losh return (NULL);
49209ee5d8aSWarner Losh
49309ee5d8aSWarner Losh /*
49409ee5d8aSWarner Losh * If the provider is a LABEL, find it's outer PART class, if any. We
49509ee5d8aSWarner Losh * only operate on partitions.
49609ee5d8aSWarner Losh */
49709ee5d8aSWarner Losh if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0) {
49809ee5d8aSWarner Losh LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
49909ee5d8aSWarner Losh if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_PART) == 0) {
50009ee5d8aSWarner Losh pp = cp->lg_provider;
50109ee5d8aSWarner Losh break;
50209ee5d8aSWarner Losh }
50309ee5d8aSWarner Losh }
50409ee5d8aSWarner Losh }
50509ee5d8aSWarner Losh if (strcmp(pp->lg_geom->lg_class->lg_name, G_PART) != 0)
50609ee5d8aSWarner Losh return (NULL);
50709ee5d8aSWarner Losh
50809ee5d8aSWarner Losh #if 0
50909ee5d8aSWarner Losh /* This doesn't work because we can't get the data to walk UP the tree it seems */
51009ee5d8aSWarner Losh
51109ee5d8aSWarner Losh /*
51209ee5d8aSWarner Losh * Now that we've found the PART that we have mounted as root, find the
51309ee5d8aSWarner Losh * first efi typed partition that's a peer, if any.
51409ee5d8aSWarner Losh */
51509ee5d8aSWarner Losh LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
51609ee5d8aSWarner Losh if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_DISK) == 0) {
51709ee5d8aSWarner Losh disk = cp->lg_provider->lg_geom;
51809ee5d8aSWarner Losh break;
51909ee5d8aSWarner Losh }
52009ee5d8aSWarner Losh }
52109ee5d8aSWarner Losh if (disk == NULL) /* This is very bad -- old nested partitions -- no support ? */
52209ee5d8aSWarner Losh return (NULL);
52309ee5d8aSWarner Losh #endif
52409ee5d8aSWarner Losh
52509ee5d8aSWarner Losh #if 0
52609ee5d8aSWarner Losh /* This doesn't work because we can't get the data to walk UP the tree it seems */
52709ee5d8aSWarner Losh
52809ee5d8aSWarner Losh /*
52909ee5d8aSWarner Losh * With the disk provider, we can look for its consumers to see if any are the proper type.
53009ee5d8aSWarner Losh */
53109ee5d8aSWarner Losh LIST_FOREACH(pp, &disk->lg_consumer, lg_consumer) {
53209ee5d8aSWarner Losh type = geom_pp_attr(mesh, pp, "type");
53309ee5d8aSWarner Losh if (type == NULL)
53409ee5d8aSWarner Losh continue;
53509ee5d8aSWarner Losh if (strcmp(type, "efi") != 0)
53609ee5d8aSWarner Losh continue;
53709ee5d8aSWarner Losh efimedia = geom_pp_attr(mesh, pp, "efimedia");
53809ee5d8aSWarner Losh if (efimedia == NULL)
53909ee5d8aSWarner Losh return (NULL);
54009ee5d8aSWarner Losh return strdup(efimedia);
54109ee5d8aSWarner Losh }
54209ee5d8aSWarner Losh #endif
54309ee5d8aSWarner Losh return (NULL);
54409ee5d8aSWarner Losh }
54509ee5d8aSWarner Losh
54609ee5d8aSWarner Losh
54709ee5d8aSWarner Losh static char *
find_geom_efimedia(struct gmesh * mesh,const char * dev)54809ee5d8aSWarner Losh find_geom_efimedia(struct gmesh *mesh, const char *dev)
54909ee5d8aSWarner Losh {
55009ee5d8aSWarner Losh struct gprovider *pp;
55109ee5d8aSWarner Losh const char *efimedia;
55209ee5d8aSWarner Losh
55309ee5d8aSWarner Losh pp = find_provider_by_name(mesh, dev);
55409ee5d8aSWarner Losh if (pp == NULL)
55509ee5d8aSWarner Losh return (NULL);
55609ee5d8aSWarner Losh efimedia = geom_pp_attr(mesh, pp, "efimedia");
557ccf2121dSWarner Losh
558ccf2121dSWarner Losh /*
559ccf2121dSWarner Losh * If this device doesn't hav an efimedia attribute, see if it is a
560ccf2121dSWarner Losh * glabel node, and if so look for the underlying provider to get the
561ccf2121dSWarner Losh * efimedia attribute from.
562ccf2121dSWarner Losh */
563ccf2121dSWarner Losh if (efimedia == NULL &&
564ccf2121dSWarner Losh strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0)
565ccf2121dSWarner Losh efimedia = find_geom_efimedia(mesh, pp->lg_geom->lg_name);
56609ee5d8aSWarner Losh if (efimedia == NULL)
56709ee5d8aSWarner Losh return (NULL);
56809ee5d8aSWarner Losh return strdup(efimedia);
56909ee5d8aSWarner Losh }
57009ee5d8aSWarner Losh
57109ee5d8aSWarner Losh static int
build_dp(const char * efimedia,const char * relpath,efidp * dp)57209ee5d8aSWarner Losh build_dp(const char *efimedia, const char *relpath, efidp *dp)
57309ee5d8aSWarner Losh {
57449fd6affSWarner Losh char *fp = NULL, *dptxt = NULL, *cp, *rp = NULL;
57509ee5d8aSWarner Losh int rv = 0;
5763e4f07fcSMark Johnston efidp out = NULL;
57709ee5d8aSWarner Losh size_t len;
57809ee5d8aSWarner Losh
57949fd6affSWarner Losh if (relpath != NULL) {
580670a4056SWarner Losh rp = strdup(relpath);
581670a4056SWarner Losh for (cp = rp; *cp; cp++)
582670a4056SWarner Losh if (*cp == '/')
583670a4056SWarner Losh *cp = '\\';
584670a4056SWarner Losh fp = path_to_file_dp(rp);
585670a4056SWarner Losh free(rp);
58609ee5d8aSWarner Losh if (fp == NULL) {
58709ee5d8aSWarner Losh rv = ENOMEM;
58809ee5d8aSWarner Losh goto errout;
58909ee5d8aSWarner Losh }
59049fd6affSWarner Losh }
59109ee5d8aSWarner Losh
59249fd6affSWarner Losh asprintf(&dptxt, "%s/%s", efimedia, fp == NULL ? "" : fp);
59309ee5d8aSWarner Losh out = malloc(8192);
59409ee5d8aSWarner Losh len = efidp_parse_device_path(dptxt, out, 8192);
59509ee5d8aSWarner Losh if (len > 8192) {
59609ee5d8aSWarner Losh rv = ENOMEM;
59709ee5d8aSWarner Losh goto errout;
59809ee5d8aSWarner Losh }
59909ee5d8aSWarner Losh if (len == 0) {
60009ee5d8aSWarner Losh rv = EINVAL;
60109ee5d8aSWarner Losh goto errout;
60209ee5d8aSWarner Losh }
60309ee5d8aSWarner Losh
60409ee5d8aSWarner Losh *dp = out;
60509ee5d8aSWarner Losh errout:
60609ee5d8aSWarner Losh if (rv) {
60709ee5d8aSWarner Losh free(out);
60809ee5d8aSWarner Losh }
60909ee5d8aSWarner Losh free(dptxt);
61009ee5d8aSWarner Losh free(fp);
61109ee5d8aSWarner Losh
61209ee5d8aSWarner Losh return rv;
61309ee5d8aSWarner Losh }
61409ee5d8aSWarner Losh
61509ee5d8aSWarner Losh /* Handles //path/to/file */
61609ee5d8aSWarner Losh /*
61709ee5d8aSWarner Losh * Which means: find the disk that has /. Then look for a EFI partition
61809ee5d8aSWarner Losh * and use that for the efimedia and /path/to/file as relative to that.
61909ee5d8aSWarner Losh * Not sure how ZFS will work here since we can't easily make the leap
62009ee5d8aSWarner Losh * to the geom from the zpool.
62109ee5d8aSWarner Losh */
62209ee5d8aSWarner Losh static int
efipart_to_dp(struct gmesh * mesh,char * path,efidp * dp)62309ee5d8aSWarner Losh efipart_to_dp(struct gmesh *mesh, char *path, efidp *dp)
62409ee5d8aSWarner Losh {
62509ee5d8aSWarner Losh char *efimedia = NULL;
62609ee5d8aSWarner Losh int rv;
62709ee5d8aSWarner Losh
62809ee5d8aSWarner Losh efimedia = find_geom_efi_on_root(mesh);
62909ee5d8aSWarner Losh #ifdef notyet
63009ee5d8aSWarner Losh if (efimedia == NULL)
63109ee5d8aSWarner Losh efimedia = find_efi_on_zfsroot(dev);
63209ee5d8aSWarner Losh #endif
63309ee5d8aSWarner Losh if (efimedia == NULL) {
63409ee5d8aSWarner Losh rv = ENOENT;
63509ee5d8aSWarner Losh goto errout;
63609ee5d8aSWarner Losh }
63709ee5d8aSWarner Losh
63809ee5d8aSWarner Losh rv = build_dp(efimedia, path + 1, dp);
63909ee5d8aSWarner Losh errout:
64009ee5d8aSWarner Losh free(efimedia);
64109ee5d8aSWarner Losh
64209ee5d8aSWarner Losh return rv;
64309ee5d8aSWarner Losh }
64409ee5d8aSWarner Losh
64509ee5d8aSWarner Losh /* Handles [/dev/]geom:[/]path/to/file */
64609ee5d8aSWarner Losh /* Handles zfs-dataset:[/]path/to/file (this may include / ) */
64709ee5d8aSWarner Losh static int
dev_path_to_dp(struct gmesh * mesh,char * path,efidp * dp)64809ee5d8aSWarner Losh dev_path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
64909ee5d8aSWarner Losh {
65009ee5d8aSWarner Losh char *relpath, *dev, *efimedia = NULL;
65109ee5d8aSWarner Losh int rv = 0;
65209ee5d8aSWarner Losh
65309ee5d8aSWarner Losh relpath = strchr(path, ':');
65409ee5d8aSWarner Losh assert(relpath != NULL);
65509ee5d8aSWarner Losh *relpath++ = '\0';
65609ee5d8aSWarner Losh
65709ee5d8aSWarner Losh dev = path;
65809ee5d8aSWarner Losh if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
65909ee5d8aSWarner Losh dev += sizeof(_PATH_DEV) -1;
66009ee5d8aSWarner Losh
66109ee5d8aSWarner Losh efimedia = find_geom_efimedia(mesh, dev);
66209ee5d8aSWarner Losh #ifdef notyet
66309ee5d8aSWarner Losh if (efimedia == NULL)
66409ee5d8aSWarner Losh find_zfs_efi_media(dev);
66509ee5d8aSWarner Losh #endif
66609ee5d8aSWarner Losh if (efimedia == NULL) {
66709ee5d8aSWarner Losh rv = ENOENT;
66809ee5d8aSWarner Losh goto errout;
66909ee5d8aSWarner Losh }
67009ee5d8aSWarner Losh rv = build_dp(efimedia, relpath, dp);
67109ee5d8aSWarner Losh errout:
67209ee5d8aSWarner Losh free(efimedia);
67309ee5d8aSWarner Losh
67409ee5d8aSWarner Losh return rv;
67509ee5d8aSWarner Losh }
67609ee5d8aSWarner Losh
67709ee5d8aSWarner Losh /* Handles /path/to/file */
67857d5ca4eSWarner Losh /* Handles /dev/foo/bar */
67909ee5d8aSWarner Losh static int
path_to_dp(struct gmesh * mesh,char * path,efidp * dp)68009ee5d8aSWarner Losh path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
68109ee5d8aSWarner Losh {
68209ee5d8aSWarner Losh struct statfs buf;
68309ee5d8aSWarner Losh char *rp = NULL, *ep, *dev, *efimedia = NULL;
68409ee5d8aSWarner Losh int rv = 0;
68509ee5d8aSWarner Losh
68609ee5d8aSWarner Losh rp = realpath(path, NULL);
68709ee5d8aSWarner Losh if (rp == NULL) {
68809ee5d8aSWarner Losh rv = errno;
68909ee5d8aSWarner Losh goto errout;
69009ee5d8aSWarner Losh }
69109ee5d8aSWarner Losh
69209ee5d8aSWarner Losh if (statfs(rp, &buf) != 0) {
69309ee5d8aSWarner Losh rv = errno;
69409ee5d8aSWarner Losh goto errout;
69509ee5d8aSWarner Losh }
69609ee5d8aSWarner Losh
69709ee5d8aSWarner Losh dev = buf.f_mntfromname;
69857d5ca4eSWarner Losh /*
69957d5ca4eSWarner Losh * If we're fed a raw /dev/foo/bar, then devfs is returned from the
70057d5ca4eSWarner Losh * statfs call. In that case, use that dev and assume we have a path
70157d5ca4eSWarner Losh * of nothing.
70257d5ca4eSWarner Losh */
70357d5ca4eSWarner Losh if (strcmp(dev, "devfs") == 0) {
70457d5ca4eSWarner Losh dev = rp + sizeof(_PATH_DEV) - 1;
70557d5ca4eSWarner Losh ep = NULL;
70657d5ca4eSWarner Losh } else {
70709ee5d8aSWarner Losh if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
70809ee5d8aSWarner Losh dev += sizeof(_PATH_DEV) - 1;
70909ee5d8aSWarner Losh ep = rp + strlen(buf.f_mntonname);
71057d5ca4eSWarner Losh }
71109ee5d8aSWarner Losh
71209ee5d8aSWarner Losh efimedia = find_geom_efimedia(mesh, dev);
71309ee5d8aSWarner Losh #ifdef notyet
71409ee5d8aSWarner Losh if (efimedia == NULL)
71509ee5d8aSWarner Losh find_zfs_efi_media(dev);
71609ee5d8aSWarner Losh #endif
71709ee5d8aSWarner Losh if (efimedia == NULL) {
71809ee5d8aSWarner Losh rv = ENOENT;
71909ee5d8aSWarner Losh goto errout;
72009ee5d8aSWarner Losh }
72109ee5d8aSWarner Losh
72209ee5d8aSWarner Losh rv = build_dp(efimedia, ep, dp);
72309ee5d8aSWarner Losh errout:
72409ee5d8aSWarner Losh free(efimedia);
72509ee5d8aSWarner Losh free(rp);
72609ee5d8aSWarner Losh if (rv != 0) {
72709ee5d8aSWarner Losh free(*dp);
728167b7a41SWarner Losh *dp = NULL;
72909ee5d8aSWarner Losh }
73009ee5d8aSWarner Losh return (rv);
73109ee5d8aSWarner Losh }
73209ee5d8aSWarner Losh
73309ee5d8aSWarner Losh int
efivar_unix_path_to_device_path(const char * path,efidp * dp)73409ee5d8aSWarner Losh efivar_unix_path_to_device_path(const char *path, efidp *dp)
73509ee5d8aSWarner Losh {
73609ee5d8aSWarner Losh char *modpath = NULL, *cp;
73709ee5d8aSWarner Losh int rv = ENOMEM;
73809ee5d8aSWarner Losh struct gmesh mesh;
73909ee5d8aSWarner Losh
74009ee5d8aSWarner Losh /*
74109ee5d8aSWarner Losh * Fail early for clearly bogus things
74209ee5d8aSWarner Losh */
74309ee5d8aSWarner Losh if (path == NULL || dp == NULL)
74409ee5d8aSWarner Losh return (EDOOFUS);
74509ee5d8aSWarner Losh
74609ee5d8aSWarner Losh /*
74709ee5d8aSWarner Losh * We'll need the goem mesh to grovel through it to find the
74809ee5d8aSWarner Losh * efimedia attribute for any devices we find. Grab it here
74909ee5d8aSWarner Losh * and release it to simplify the error paths out of the
75009ee5d8aSWarner Losh * subordinate functions
75109ee5d8aSWarner Losh */
75209ee5d8aSWarner Losh if (geom_gettree(&mesh))
75309ee5d8aSWarner Losh return (errno);
75409ee5d8aSWarner Losh
75509ee5d8aSWarner Losh /*
75609ee5d8aSWarner Losh * Convert all \ to /. We'll convert them back again when
75709ee5d8aSWarner Losh * we encode the file. Boot loaders are expected to cope.
75809ee5d8aSWarner Losh */
75909ee5d8aSWarner Losh modpath = strdup(path);
76009ee5d8aSWarner Losh if (modpath == NULL)
76109ee5d8aSWarner Losh goto out;
76209ee5d8aSWarner Losh for (cp = modpath; *cp; cp++)
76309ee5d8aSWarner Losh if (*cp == '\\')
76409ee5d8aSWarner Losh *cp = '/';
76509ee5d8aSWarner Losh
76609ee5d8aSWarner Losh if (modpath[0] == '/' && modpath[1] == '/') /* Handle //foo/bar/baz */
76709ee5d8aSWarner Losh rv = efipart_to_dp(&mesh, modpath, dp);
76809ee5d8aSWarner Losh else if (strchr(modpath, ':')) /* Handle dev:/bar/baz */
76909ee5d8aSWarner Losh rv = dev_path_to_dp(&mesh, modpath, dp);
77009ee5d8aSWarner Losh else /* Handle /a/b/c */
77109ee5d8aSWarner Losh rv = path_to_dp(&mesh, modpath, dp);
77209ee5d8aSWarner Losh
77309ee5d8aSWarner Losh out:
77409ee5d8aSWarner Losh geom_deletetree(&mesh);
77509ee5d8aSWarner Losh free(modpath);
77609ee5d8aSWarner Losh
77709ee5d8aSWarner Losh return (rv);
77809ee5d8aSWarner Losh }
779