1326458Simp/*- 2326458Simp * Copyright (c) 2017 Netflix, Inc. 3326458Simp * All rights reserved. 4326458Simp * 5326458Simp * Redistribution and use in source and binary forms, with or without 6326458Simp * modification, are permitted provided that the following conditions 7326458Simp * are met: 8326458Simp * 1. Redistributions of source code must retain the above copyright 9332123Skevans * notice, this list of conditions and the following disclaimer. 10326458Simp * 2. Redistributions in binary form must reproduce the above copyright 11326458Simp * notice, this list of conditions and the following disclaimer in the 12326458Simp * documentation and/or other materials provided with the distribution. 13326458Simp * 14332123Skevans * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15332123Skevans * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16332123Skevans * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17332123Skevans * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18332123Skevans * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19332123Skevans * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20332123Skevans * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21332123Skevans * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22332123Skevans * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23332123Skevans * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24332123Skevans * SUCH DAMAGE. 25326458Simp */ 26326458Simp 27326458Simp#include <sys/cdefs.h> 28326458Simp__FBSDID("$FreeBSD: stable/11/lib/libefivar/efivar-dp-xlate.c 332123 2018-04-06 18:10:38Z kevans $"); 29326458Simp 30326458Simp#include <sys/param.h> 31326458Simp#include <sys/ucred.h> 32326458Simp#include <sys/mount.h> 33326458Simp 34326458Simp#undef MAX 35326458Simp#undef MIN 36326458Simp 37326458Simp#include <assert.h> 38326458Simp#include <efivar.h> 39326458Simp#include <errno.h> 40326458Simp#include <libgeom.h> 41326458Simp#include <paths.h> 42326458Simp#include <stdio.h> 43326458Simp#include <string.h> 44326458Simp 45326458Simp#include "efichar.h" 46326458Simp 47326458Simp#include "efi-osdep.h" 48326458Simp#include "efivar-dp.h" 49326458Simp 50326458Simp#include "uefi-dplib.h" 51326458Simp 52326458Simp#define MAX_DP_SANITY 4096 /* Biggest device path in bytes */ 53326458Simp#define MAX_DP_TEXT_LEN 4096 /* Longest string rep of dp */ 54326458Simp 55326458Simp#define G_PART "PART" 56326458Simp#define G_LABEL "LABEL" 57326458Simp#define G_DISK "DISK" 58326458Simp 59326458Simpstatic const char * 60326458Simpgeom_pp_attr(struct gmesh *mesh, struct gprovider *pp, const char *attr) 61326458Simp{ 62326458Simp struct gconfig *conf; 63326458Simp 64326458Simp LIST_FOREACH(conf, &pp->lg_config, lg_config) { 65326458Simp if (strcmp(conf->lg_name, attr) != 0) 66326458Simp continue; 67326458Simp return (conf->lg_val); 68326458Simp } 69326458Simp return (NULL); 70326458Simp} 71326458Simp 72326458Simpstatic struct gprovider * 73326458Simpfind_provider_by_efimedia(struct gmesh *mesh, const char *efimedia) 74326458Simp{ 75326458Simp struct gclass *classp; 76326458Simp struct ggeom *gp; 77326458Simp struct gprovider *pp; 78326458Simp const char *val; 79326458Simp 80326458Simp /* 81326458Simp * Find the partition class so we can search it... 82326458Simp */ 83326458Simp LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 84326458Simp if (strcasecmp(classp->lg_name, G_PART) == 0) 85326458Simp break; 86326458Simp } 87326458Simp if (classp == NULL) 88326458Simp return (NULL); 89326458Simp 90326458Simp /* 91326458Simp * Each geom will have a number of providers, search each 92326458Simp * one of them for the efimedia that matches. 93326458Simp */ 94326458Simp /* XXX just used gpart class since I know it's the only one, but maybe I should search all classes */ 95326458Simp LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 96326458Simp LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 97326458Simp val = geom_pp_attr(mesh, pp, "efimedia"); 98326458Simp if (val == NULL) 99326458Simp continue; 100326458Simp if (strcasecmp(efimedia, val) == 0) 101326458Simp return (pp); 102326458Simp } 103326458Simp } 104326458Simp 105326458Simp return (NULL); 106326458Simp} 107326458Simp 108326458Simpstatic struct gprovider * 109326458Simpfind_provider_by_name(struct gmesh *mesh, const char *name) 110326458Simp{ 111326458Simp struct gclass *classp; 112326458Simp struct ggeom *gp; 113326458Simp struct gprovider *pp; 114326458Simp 115326458Simp LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 116326458Simp LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 117326458Simp LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 118326458Simp if (strcmp(pp->lg_name, name) == 0) 119326458Simp return (pp); 120326458Simp } 121326458Simp } 122326458Simp } 123326458Simp 124326458Simp return (NULL); 125326458Simp} 126326458Simp 127326458Simp 128326458Simpstatic int 129326458Simpefi_hd_to_unix(struct gmesh *mesh, const_efidp dp, char **dev, char **relpath, char **abspath) 130326458Simp{ 131326458Simp int rv = 0, n, i; 132326458Simp const_efidp media, file, walker; 133326458Simp size_t len, mntlen; 134326458Simp char buf[MAX_DP_TEXT_LEN]; 135326458Simp char *pwalk; 136326458Simp struct gprovider *pp, *provider; 137326458Simp struct gconsumer *cp; 138326458Simp struct statfs *mnt; 139326458Simp 140326458Simp walker = media = dp; 141326458Simp 142326458Simp /* 143326458Simp * Now, we can either have a filepath node next, or the end. 144326458Simp * Otherwise, it's an error. 145326458Simp */ 146326458Simp walker = (const_efidp)NextDevicePathNode(walker); 147326458Simp if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY) 148326458Simp return (EINVAL); 149326458Simp if (DevicePathType(walker) == MEDIA_DEVICE_PATH && 150326458Simp DevicePathSubType(walker) == MEDIA_FILEPATH_DP) 151326458Simp file = walker; 152326458Simp else if (DevicePathType(walker) == MEDIA_DEVICE_PATH && 153326458Simp DevicePathType(walker) == END_DEVICE_PATH_TYPE) 154326458Simp file = NULL; 155326458Simp else 156326458Simp return (EINVAL); 157326458Simp 158326458Simp /* 159326458Simp * Format this node. We're going to look for it as a efimedia 160326458Simp * attribute of some geom node. Once we find that node, we use it 161326458Simp * as the device it comes from, at least provisionally. 162326458Simp */ 163326458Simp len = efidp_format_device_path_node(buf, sizeof(buf), media); 164326458Simp if (len > sizeof(buf)) 165326458Simp return (EINVAL); 166326458Simp 167326458Simp pp = find_provider_by_efimedia(mesh, buf); 168326458Simp if (pp == NULL) { 169326458Simp rv = ENOENT; 170326458Simp goto errout; 171326458Simp } 172326458Simp 173326458Simp *dev = strdup(pp->lg_name); 174326458Simp if (*dev == NULL) { 175326458Simp rv = ENOMEM; 176326458Simp goto errout; 177326458Simp } 178326458Simp 179326458Simp /* 180326458Simp * No file specified, just return the device. Don't even look 181326458Simp * for a mountpoint. XXX Sane? 182326458Simp */ 183326458Simp if (file == NULL) 184326458Simp goto errout; 185326458Simp 186326458Simp /* 187326458Simp * Now extract the relative path. The next node in the device path should 188326458Simp * be a filesystem node. If not, we have issues. 189326458Simp */ 190326458Simp *relpath = efidp_extract_file_path(file); 191326458Simp if (*relpath == NULL) { 192326458Simp rv = ENOMEM; 193326458Simp goto errout; 194326458Simp } 195326458Simp for (pwalk = *relpath; *pwalk; pwalk++) 196326458Simp if (*pwalk == '\\') 197326458Simp *pwalk = '/'; 198326458Simp 199326458Simp /* 200326458Simp * To find the absolute path, we have to look for where we're mounted. 201326458Simp * We only look a little hard, since looking too hard can come up with 202326458Simp * false positives (imagine a graid, one of whose devices is *dev). 203326458Simp */ 204326458Simp n = getfsstat(NULL, 0, MNT_NOWAIT) + 1; 205326458Simp if (n < 0) { 206326458Simp rv = errno; 207326458Simp goto errout; 208326458Simp } 209326458Simp mntlen = sizeof(struct statfs) * n; 210326458Simp mnt = malloc(mntlen); 211326458Simp n = getfsstat(mnt, mntlen, MNT_NOWAIT); 212326458Simp if (n < 0) { 213326458Simp rv = errno; 214326458Simp goto errout; 215326458Simp } 216326458Simp provider = pp; 217326458Simp for (i = 0; i < n; i++) { 218326458Simp /* 219326458Simp * Skip all pseudo filesystems. This also skips the real filesytsem 220326458Simp * of ZFS. There's no EFI designator for ZFS in the standard, so 221326458Simp * we'll need to invent one, but its decoding will be handled in 222326458Simp * a separate function. 223326458Simp */ 224326458Simp if (mnt[i].f_mntfromname[0] != '/') 225326458Simp continue; 226326458Simp 227326458Simp /* 228326458Simp * First see if it is directly attached 229326458Simp */ 230326458Simp if (strcmp(provider->lg_name, mnt[i].f_mntfromname + 5) == 0) 231326458Simp break; 232326458Simp 233326458Simp /* 234326458Simp * Next see if it is attached via one of the physical disk's 235326458Simp * labels. 236326458Simp */ 237326458Simp LIST_FOREACH(cp, &provider->lg_consumers, lg_consumer) { 238326458Simp pp = cp->lg_provider; 239326458Simp if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) != 0) 240326458Simp continue; 241326458Simp if (strcmp(g_device_path(pp->lg_name), mnt[i].f_mntfromname) == 0) 242326458Simp goto break2; 243326458Simp } 244326458Simp /* Not the one, try the next mount point */ 245326458Simp } 246326458Simpbreak2: 247326458Simp 248326458Simp /* 249326458Simp * No mountpoint found, no absolute path possible 250326458Simp */ 251326458Simp if (i >= n) 252326458Simp goto errout; 253326458Simp 254326458Simp /* 255326458Simp * Construct absolute path and we're finally done. 256326458Simp */ 257326458Simp if (strcmp(mnt[i].f_mntonname, "/") == 0) 258326458Simp asprintf(abspath, "/%s", *relpath); 259326458Simp else 260326458Simp asprintf(abspath, "%s/%s", mnt[i].f_mntonname, *relpath); 261326458Simp 262326458Simperrout: 263326458Simp if (rv != 0) { 264326458Simp free(*dev); 265326458Simp *dev = NULL; 266326458Simp free(*relpath); 267326458Simp *relpath = NULL; 268326458Simp } 269326458Simp return (rv); 270326458Simp} 271326458Simp 272326458Simp/* 273326458Simp * Translate the passed in device_path to a unix path via the following 274326458Simp * algorithm. 275326458Simp * 276326458Simp * If dp, dev or path NULL, return EDOOFUS. XXX wise? 277326458Simp * 278326458Simp * Set *path = NULL; *dev = NULL; 279326458Simp * 280326458Simp * Walk through the device_path until we find either a media device path. 281326458Simp * Return EINVAL if not found. Return EINVAL if walking dp would 282326458Simp * land us more than sanity size away from the start (4k). 283326458Simp * 284326458Simp * If we find a media descriptor, we search through the geom mesh to see if we 285326458Simp * can find a matching node. If no match is found in the mesh that matches, 286326458Simp * return ENXIO. 287326458Simp * 288326458Simp * Once we find a matching node, we search to see if there is a filesystem 289326458Simp * mounted on it. If we find nothing, then search each of the devices that are 290326458Simp * mounted to see if we can work up the geom tree to find the matching node. if 291326458Simp * we still can't find anything, *dev = sprintf("/dev/%s", provider_name 292326458Simp * of the original node we found), but return ENOTBLK. 293326458Simp * 294326458Simp * Record the dev of the mountpoint in *dev. 295326458Simp * 296326458Simp * Once we find something, check to see if the next node in the device path is 297326458Simp * the end of list. If so, return the mountpoint. 298326458Simp * 299326458Simp * If the next node isn't a File path node, return EFTYPE. 300326458Simp * 301326458Simp * Extract the path from the File path node(s). translate any \ file separators 302326458Simp * to /. Append the result to the mount point. Copy the resulting path into 303326458Simp * *path. Stat that path. If it is not found, return the errorr from stat. 304326458Simp * 305326458Simp * Finally, check to make sure the resulting path is still on the same 306326458Simp * device. If not, return ENODEV. 307326458Simp * 308326458Simp * Otherwise return 0. 309326458Simp * 310326458Simp * The dev or full path that's returned is malloced, so needs to be freed when 311326458Simp * the caller is done about it. Unlike many other functions, we can return data 312326458Simp * with an error code, so pay attention. 313326458Simp */ 314326458Simpint 315326458Simpefivar_device_path_to_unix_path(const_efidp dp, char **dev, char **relpath, char **abspath) 316326458Simp{ 317326458Simp const_efidp walker; 318326458Simp struct gmesh mesh; 319326458Simp int rv = 0; 320326458Simp 321326458Simp /* 322326458Simp * Sanity check args, fail early 323326458Simp */ 324326458Simp if (dp == NULL || dev == NULL || relpath == NULL || abspath == NULL) 325326458Simp return (EDOOFUS); 326326458Simp 327326458Simp *dev = NULL; 328326458Simp *relpath = NULL; 329326458Simp *abspath = NULL; 330326458Simp 331326458Simp /* 332326458Simp * Find the first media device path we can. If we go too far, 333326458Simp * assume the passed in device path is bogus. If we hit the end 334326458Simp * then we didn't find a media device path, so signal that error. 335326458Simp */ 336326458Simp walker = dp; 337326458Simp while (DevicePathType(walker) != MEDIA_DEVICE_PATH && 338326458Simp DevicePathType(walker) != END_DEVICE_PATH_TYPE) { 339326458Simp walker = (const_efidp)NextDevicePathNode(walker); 340326458Simp if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY) 341326458Simp return (EINVAL); 342326458Simp } 343326458Simp if (DevicePathType(walker) != MEDIA_DEVICE_PATH) 344326458Simp return (EINVAL); 345326458Simp 346326458Simp /* 347326458Simp * There's several types of media paths. We're only interested in the 348326458Simp * hard disk path, as it's really the only relevant one to booting. The 349326458Simp * CD path just might also be relevant, and would be easy to add, but 350326458Simp * isn't supported. A file path too is relevant, but at this stage, it's 351326458Simp * premature because we're trying to translate a specification for a device 352326458Simp * and path on that device into a unix path, or at the very least, a 353326458Simp * geom device : path-on-device. 354326458Simp * 355326458Simp * Also, ZFS throws a bit of a monkey wrench in here since it doesn't have 356326458Simp * a device path type (it creates a new virtual device out of one or more 357326458Simp * storage devices). 358326458Simp * 359326458Simp * For all of them, we'll need to know the geoms, so allocate / free the 360326458Simp * geom mesh here since it's safer than doing it in each sub-function 361326458Simp * which may have many error exits. 362326458Simp */ 363326458Simp if (geom_gettree(&mesh)) 364326458Simp return (ENOMEM); 365326458Simp 366326458Simp rv = EINVAL; 367326458Simp if (DevicePathSubType(walker) == MEDIA_HARDDRIVE_DP) 368326458Simp rv = efi_hd_to_unix(&mesh, walker, dev, relpath, abspath); 369326458Simp#ifdef notyet 370326458Simp else if (is_cdrom_device(walker)) 371326458Simp rv = efi_cdrom_to_unix(&mesh, walker, dev, relpath, abspath); 372326458Simp else if (is_floppy_device(walker)) 373326458Simp rv = efi_floppy_to_unix(&mesh, walker, dev, relpath, abspath); 374326458Simp else if (is_zpool_device(walker)) 375326458Simp rv = efi_zpool_to_unix(&mesh, walker, dev, relpath, abspath); 376326458Simp#endif 377326458Simp geom_deletetree(&mesh); 378326458Simp 379326458Simp return (rv); 380326458Simp} 381326458Simp 382326458Simp/* 383326458Simp * Construct the EFI path to a current unix path as follows. 384326458Simp * 385326458Simp * The path may be of one of three forms: 386326458Simp * 1) /path/to/file -- full path to a file. The file need not be present, 387326458Simp * but /path/to must be. It must reside on a local filesystem 388326458Simp * mounted on a GPT or MBR partition. 389326458Simp * 2) //path/to/file -- Shorthand for 'On the EFI partition, \path\to\file' 390326458Simp * where 'The EFI Partition' is a partiton that's type is 'efi' 391326458Simp * on the same disk that / is mounted from. If there are multiple 392326458Simp * or no 'efi' parittions on that disk, or / isn't on a disk that 393326458Simp * we can trace back to a physical device, an error will result 394326458Simp * 3) [/dev/]geom-name:/path/to/file -- Use the specified partition 395326458Simp * (and it must be a GPT or MBR partition) with the specified 396326458Simp * path. The latter is not authenticated. 397326458Simp * all path forms translate any \ characters to / before further processing. 398326458Simp * When a file path node is created, all / characters are translated back 399326458Simp * to \. 400326458Simp * 401326458Simp * For paths of the first form: 402326458Simp * find where the filesystem is mount (either the file directly, or 403326458Simp * its parent directory). 404326458Simp * translate any logical device name (eg lable) to a physical one 405326458Simp * If not possible, return ENXIO 406326458Simp * If the physical path is unsupported (Eg not on a GPT or MBR disk), 407326458Simp * return ENXIO 408326458Simp * Create a media device path node. 409326458Simp * append the relative path from the mountpoint to the media device node 410326458Simp * as a file path. 411326458Simp * 412326458Simp * For paths matching the second form: 413326458Simp * find the EFI partition corresponding to the root fileystem. 414326458Simp * If none found, return ENXIO 415326458Simp * Create a media device path node for the found partition 416326458Simp * Append a File Path to the end for the rest of the file. 417326458Simp * 418326458Simp * For paths of the third form 419326458Simp * Translate the geom-name passed in into a physical partition 420326458Simp * name. 421326458Simp * Return ENXIO if the translation fails 422326458Simp * Make a media device path for it 423326458Simp * append the part after the : as a File path node. 424326458Simp */ 425326458Simp 426326458Simpstatic char * 427326458Simppath_to_file_dp(const char *relpath) 428326458Simp{ 429326458Simp char *rv; 430326458Simp 431326458Simp asprintf(&rv, "File(%s)", relpath); 432326458Simp return rv; 433326458Simp} 434326458Simp 435326458Simpstatic char * 436326458Simpfind_geom_efi_on_root(struct gmesh *mesh) 437326458Simp{ 438326458Simp struct statfs buf; 439326458Simp const char *dev; 440326458Simp struct gprovider *pp; 441326458Simp// struct ggeom *disk; 442326458Simp struct gconsumer *cp; 443326458Simp 444326458Simp /* 445326458Simp * Find /'s geom. Assume it's mounted on /dev/ and filter out all the 446326458Simp * filesystems that aren't. 447326458Simp */ 448326458Simp if (statfs("/", &buf) != 0) 449326458Simp return (NULL); 450326458Simp dev = buf.f_mntfromname; 451326458Simp if (*dev != '/' || strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0) 452326458Simp return (NULL); 453326458Simp dev += sizeof(_PATH_DEV) -1; 454326458Simp pp = find_provider_by_name(mesh, dev); 455326458Simp if (pp == NULL) 456326458Simp return (NULL); 457326458Simp 458326458Simp /* 459326458Simp * If the provider is a LABEL, find it's outer PART class, if any. We 460326458Simp * only operate on partitions. 461326458Simp */ 462326458Simp if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0) { 463326458Simp LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) { 464326458Simp if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_PART) == 0) { 465326458Simp pp = cp->lg_provider; 466326458Simp break; 467326458Simp } 468326458Simp } 469326458Simp } 470326458Simp if (strcmp(pp->lg_geom->lg_class->lg_name, G_PART) != 0) 471326458Simp return (NULL); 472326458Simp 473326458Simp#if 0 474326458Simp /* This doesn't work because we can't get the data to walk UP the tree it seems */ 475326458Simp 476326458Simp /* 477326458Simp * Now that we've found the PART that we have mounted as root, find the 478326458Simp * first efi typed partition that's a peer, if any. 479326458Simp */ 480326458Simp LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) { 481326458Simp if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_DISK) == 0) { 482326458Simp disk = cp->lg_provider->lg_geom; 483326458Simp break; 484326458Simp } 485326458Simp } 486326458Simp if (disk == NULL) /* This is very bad -- old nested partitions -- no support ? */ 487326458Simp return (NULL); 488326458Simp#endif 489326458Simp 490326458Simp#if 0 491326458Simp /* This doesn't work because we can't get the data to walk UP the tree it seems */ 492326458Simp 493326458Simp /* 494326458Simp * With the disk provider, we can look for its consumers to see if any are the proper type. 495326458Simp */ 496326458Simp LIST_FOREACH(pp, &disk->lg_consumer, lg_consumer) { 497326458Simp type = geom_pp_attr(mesh, pp, "type"); 498326458Simp if (type == NULL) 499326458Simp continue; 500326458Simp if (strcmp(type, "efi") != 0) 501326458Simp continue; 502326458Simp efimedia = geom_pp_attr(mesh, pp, "efimedia"); 503326458Simp if (efimedia == NULL) 504326458Simp return (NULL); 505326458Simp return strdup(efimedia); 506326458Simp } 507326458Simp#endif 508326458Simp return (NULL); 509326458Simp} 510326458Simp 511326458Simp 512326458Simpstatic char * 513326458Simpfind_geom_efimedia(struct gmesh *mesh, const char *dev) 514326458Simp{ 515326458Simp struct gprovider *pp; 516326458Simp const char *efimedia; 517326458Simp 518326458Simp pp = find_provider_by_name(mesh, dev); 519326458Simp if (pp == NULL) 520326458Simp return (NULL); 521326458Simp efimedia = geom_pp_attr(mesh, pp, "efimedia"); 522326458Simp if (efimedia == NULL) 523326458Simp return (NULL); 524326458Simp return strdup(efimedia); 525326458Simp} 526326458Simp 527326458Simpstatic int 528326458Simpbuild_dp(const char *efimedia, const char *relpath, efidp *dp) 529326458Simp{ 530332123Skevans char *fp, *dptxt = NULL, *cp, *rp; 531326458Simp int rv = 0; 532332123Skevans efidp out = NULL; 533326458Simp size_t len; 534326458Simp 535332123Skevans rp = strdup(relpath); 536332123Skevans for (cp = rp; *cp; cp++) 537332123Skevans if (*cp == '/') 538332123Skevans *cp = '\\'; 539332123Skevans fp = path_to_file_dp(rp); 540332123Skevans free(rp); 541326458Simp if (fp == NULL) { 542326458Simp rv = ENOMEM; 543326458Simp goto errout; 544326458Simp } 545326458Simp 546326458Simp asprintf(&dptxt, "%s/%s", efimedia, fp); 547326458Simp out = malloc(8192); 548326458Simp len = efidp_parse_device_path(dptxt, out, 8192); 549326458Simp if (len > 8192) { 550326458Simp rv = ENOMEM; 551326458Simp goto errout; 552326458Simp } 553326458Simp if (len == 0) { 554326458Simp rv = EINVAL; 555326458Simp goto errout; 556326458Simp } 557326458Simp 558326458Simp *dp = out; 559326458Simperrout: 560326458Simp if (rv) { 561326458Simp free(out); 562326458Simp } 563326458Simp free(dptxt); 564326458Simp free(fp); 565326458Simp 566326458Simp return rv; 567326458Simp} 568326458Simp 569326458Simp/* Handles //path/to/file */ 570326458Simp/* 571326458Simp * Which means: find the disk that has /. Then look for a EFI partition 572326458Simp * and use that for the efimedia and /path/to/file as relative to that. 573326458Simp * Not sure how ZFS will work here since we can't easily make the leap 574326458Simp * to the geom from the zpool. 575326458Simp */ 576326458Simpstatic int 577326458Simpefipart_to_dp(struct gmesh *mesh, char *path, efidp *dp) 578326458Simp{ 579326458Simp char *efimedia = NULL; 580326458Simp int rv; 581326458Simp 582326458Simp efimedia = find_geom_efi_on_root(mesh); 583326458Simp#ifdef notyet 584326458Simp if (efimedia == NULL) 585326458Simp efimedia = find_efi_on_zfsroot(dev); 586326458Simp#endif 587326458Simp if (efimedia == NULL) { 588326458Simp rv = ENOENT; 589326458Simp goto errout; 590326458Simp } 591326458Simp 592326458Simp rv = build_dp(efimedia, path + 1, dp); 593326458Simperrout: 594326458Simp free(efimedia); 595326458Simp 596326458Simp return rv; 597326458Simp} 598326458Simp 599326458Simp/* Handles [/dev/]geom:[/]path/to/file */ 600326458Simp/* Handles zfs-dataset:[/]path/to/file (this may include / ) */ 601326458Simpstatic int 602326458Simpdev_path_to_dp(struct gmesh *mesh, char *path, efidp *dp) 603326458Simp{ 604326458Simp char *relpath, *dev, *efimedia = NULL; 605326458Simp int rv = 0; 606326458Simp 607326458Simp relpath = strchr(path, ':'); 608326458Simp assert(relpath != NULL); 609326458Simp *relpath++ = '\0'; 610326458Simp 611326458Simp dev = path; 612326458Simp if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 613326458Simp dev += sizeof(_PATH_DEV) -1; 614326458Simp 615326458Simp efimedia = find_geom_efimedia(mesh, dev); 616326458Simp#ifdef notyet 617326458Simp if (efimedia == NULL) 618326458Simp find_zfs_efi_media(dev); 619326458Simp#endif 620326458Simp if (efimedia == NULL) { 621326458Simp rv = ENOENT; 622326458Simp goto errout; 623326458Simp } 624326458Simp rv = build_dp(efimedia, relpath, dp); 625326458Simperrout: 626326458Simp free(efimedia); 627326458Simp 628326458Simp return rv; 629326458Simp} 630326458Simp 631326458Simp/* Handles /path/to/file */ 632326458Simpstatic int 633326458Simppath_to_dp(struct gmesh *mesh, char *path, efidp *dp) 634326458Simp{ 635326458Simp struct statfs buf; 636326458Simp char *rp = NULL, *ep, *dev, *efimedia = NULL; 637326458Simp int rv = 0; 638326458Simp 639326458Simp rp = realpath(path, NULL); 640326458Simp if (rp == NULL) { 641326458Simp rv = errno; 642326458Simp goto errout; 643326458Simp } 644326458Simp 645326458Simp if (statfs(rp, &buf) != 0) { 646326458Simp rv = errno; 647326458Simp goto errout; 648326458Simp } 649326458Simp 650326458Simp dev = buf.f_mntfromname; 651326458Simp if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 652326458Simp dev += sizeof(_PATH_DEV) -1; 653326458Simp ep = rp + strlen(buf.f_mntonname); 654326458Simp 655326458Simp efimedia = find_geom_efimedia(mesh, dev); 656326458Simp#ifdef notyet 657326458Simp if (efimedia == NULL) 658326458Simp find_zfs_efi_media(dev); 659326458Simp#endif 660326458Simp if (efimedia == NULL) { 661326458Simp rv = ENOENT; 662326458Simp goto errout; 663326458Simp } 664326458Simp 665326458Simp rv = build_dp(efimedia, ep, dp); 666326458Simperrout: 667326458Simp free(efimedia); 668326458Simp free(rp); 669326458Simp if (rv != 0) { 670326458Simp free(*dp); 671332123Skevans *dp = NULL; 672326458Simp } 673326458Simp return (rv); 674326458Simp} 675326458Simp 676326458Simpint 677326458Simpefivar_unix_path_to_device_path(const char *path, efidp *dp) 678326458Simp{ 679326458Simp char *modpath = NULL, *cp; 680326458Simp int rv = ENOMEM; 681326458Simp struct gmesh mesh; 682326458Simp 683326458Simp /* 684326458Simp * Fail early for clearly bogus things 685326458Simp */ 686326458Simp if (path == NULL || dp == NULL) 687326458Simp return (EDOOFUS); 688326458Simp 689326458Simp /* 690326458Simp * We'll need the goem mesh to grovel through it to find the 691326458Simp * efimedia attribute for any devices we find. Grab it here 692326458Simp * and release it to simplify the error paths out of the 693326458Simp * subordinate functions 694326458Simp */ 695326458Simp if (geom_gettree(&mesh)) 696326458Simp return (errno); 697326458Simp 698326458Simp /* 699326458Simp * Convert all \ to /. We'll convert them back again when 700326458Simp * we encode the file. Boot loaders are expected to cope. 701326458Simp */ 702326458Simp modpath = strdup(path); 703326458Simp if (modpath == NULL) 704326458Simp goto out; 705326458Simp for (cp = modpath; *cp; cp++) 706326458Simp if (*cp == '\\') 707326458Simp *cp = '/'; 708326458Simp 709326458Simp if (modpath[0] == '/' && modpath[1] == '/') /* Handle //foo/bar/baz */ 710326458Simp rv = efipart_to_dp(&mesh, modpath, dp); 711326458Simp else if (strchr(modpath, ':')) /* Handle dev:/bar/baz */ 712326458Simp rv = dev_path_to_dp(&mesh, modpath, dp); 713326458Simp else /* Handle /a/b/c */ 714326458Simp rv = path_to_dp(&mesh, modpath, dp); 715326458Simp 716326458Simpout: 717326458Simp geom_deletetree(&mesh); 718326458Simp free(modpath); 719326458Simp 720326458Simp return (rv); 721326458Simp} 722