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