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