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