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