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