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