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 *
geom_pp_attr(struct gmesh * mesh,struct gprovider * pp,const char * attr)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 *
find_provider_by_efimedia(struct gmesh * mesh,const char * efimedia)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 *
find_provider_by_name(struct gmesh * mesh,const char * name)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
efi_hd_to_unix(struct gmesh * mesh,const_efidp dp,char ** dev,char ** relpath,char ** abspath)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
efivar_device_path_to_unix_path(const_efidp dp,char ** dev,char ** relpath,char ** abspath)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 *
path_to_file_dp(const char * relpath)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 *
find_geom_efi_on_root(struct gmesh * mesh)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 *
find_geom_efimedia(struct gmesh * mesh,const char * dev)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
build_dp(const char * efimedia,const char * relpath,efidp * dp)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
efipart_to_dp(struct gmesh * mesh,char * path,efidp * dp)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
dev_path_to_dp(struct gmesh * mesh,char * path,efidp * dp)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
path_to_dp(struct gmesh * mesh,char * path,efidp * dp)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
efivar_unix_path_to_device_path(const char * path,efidp * dp)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