Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libefivar/efivar-dp-xlate.c
105688 views
1
/*-
2
* Copyright (c) 2017 Netflix, Inc.
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
12
*
13
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
* SUCH DAMAGE.
24
*/
25
26
#include <sys/param.h>
27
#include <sys/ucred.h>
28
#include <sys/mount.h>
29
30
#undef MAX
31
#undef MIN
32
33
#include <assert.h>
34
#include <efivar.h>
35
#include <errno.h>
36
#include <libgeom.h>
37
#include <paths.h>
38
#include <stdio.h>
39
#include <string.h>
40
41
#include "efichar.h"
42
#include "efivar-dp.h"
43
#include "uefi-dplib.h"
44
45
#define MAX_DP_SANITY 4096 /* Biggest device path in bytes */
46
#define MAX_DP_TEXT_LEN 4096 /* Longest string rep of dp */
47
48
#define ValidLen(dp) (DevicePathNodeLength(dp) >= sizeof(EFI_DEVICE_PATH_PROTOCOL) && \
49
DevicePathNodeLength(dp) < MAX_DP_SANITY)
50
51
#define G_PART "PART"
52
#define G_LABEL "LABEL"
53
#define G_DISK "DISK"
54
55
static const char *
56
geom_pp_attr(struct gmesh *mesh, struct gprovider *pp, const char *attr)
57
{
58
struct gconfig *conf;
59
60
LIST_FOREACH(conf, &pp->lg_config, lg_config) {
61
if (strcmp(conf->lg_name, attr) != 0)
62
continue;
63
return (conf->lg_val);
64
}
65
return (NULL);
66
}
67
68
static struct gprovider *
69
find_provider_by_efimedia(struct gmesh *mesh, const char *efimedia)
70
{
71
struct gclass *classp;
72
struct ggeom *gp;
73
struct gprovider *pp;
74
const char *val;
75
76
/*
77
* Find the partition class so we can search it...
78
*/
79
LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
80
if (strcasecmp(classp->lg_name, G_PART) == 0)
81
break;
82
}
83
if (classp == NULL)
84
return (NULL);
85
86
/*
87
* Each geom will have a number of providers, search each
88
* one of them for the efimedia that matches.
89
*/
90
/* XXX just used gpart class since I know it's the only one, but maybe I should search all classes */
91
LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
92
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
93
val = geom_pp_attr(mesh, pp, "efimedia");
94
if (val == NULL)
95
continue;
96
if (strcasecmp(efimedia, val) == 0)
97
return (pp);
98
}
99
}
100
101
return (NULL);
102
}
103
104
static struct gprovider *
105
find_provider_by_name(struct gmesh *mesh, const char *name)
106
{
107
struct gclass *classp;
108
struct ggeom *gp;
109
struct gprovider *pp;
110
111
LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
112
LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
113
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
114
if (strcmp(pp->lg_name, name) == 0)
115
return (pp);
116
}
117
}
118
}
119
120
return (NULL);
121
}
122
123
124
static int
125
efi_hd_to_unix(struct gmesh *mesh, const_efidp dp, char **dev, char **relpath, char **abspath)
126
{
127
int rv = 0, n, i;
128
const_efidp media, file, walker;
129
size_t len, mntlen;
130
char buf[MAX_DP_TEXT_LEN];
131
char *pwalk, *newdev = NULL;
132
struct gprovider *pp, *provider;
133
struct statfs *mnt;
134
struct gclass *glabel;
135
struct ggeom *gp;
136
137
walker = media = dp;
138
*dev = NULL;
139
*relpath = NULL;
140
141
/*
142
* Now, we can either have a filepath node next, or the end.
143
* Otherwise, it's an error.
144
*/
145
if (!ValidLen(walker))
146
return (EINVAL);
147
walker = (const_efidp)NextDevicePathNode(walker);
148
if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
149
return (EINVAL);
150
if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
151
DevicePathSubType(walker) == MEDIA_FILEPATH_DP)
152
file = walker;
153
else if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
154
DevicePathType(walker) == END_DEVICE_PATH_TYPE)
155
file = NULL;
156
else
157
return (EINVAL);
158
159
/*
160
* Format this node. We're going to look for it as a efimedia
161
* attribute of some geom node. Once we find that node, we use it
162
* as the device it comes from, at least provisionally.
163
*/
164
len = efidp_format_device_path_node(buf, sizeof(buf), media);
165
if (len > sizeof(buf))
166
return (EINVAL);
167
168
pp = find_provider_by_efimedia(mesh, buf);
169
if (pp == NULL) {
170
rv = ENOENT;
171
goto errout;
172
}
173
174
/*
175
* No file specified, just return the device. Don't even look
176
* for a mountpoint. XXX Sane?
177
*/
178
if (file == NULL)
179
goto errout;
180
181
/*
182
* Now extract the relative path. The next node in the device path should
183
* be a filesystem node. If not, we have issues.
184
*/
185
*relpath = efidp_extract_file_path(file);
186
if (*relpath == NULL) {
187
rv = ENOMEM;
188
goto errout;
189
}
190
for (pwalk = *relpath; *pwalk; pwalk++)
191
if (*pwalk == '\\')
192
*pwalk = '/';
193
194
/*
195
* To find the absolute path, we have to look for where we're mounted.
196
* We only look a little hard, since looking too hard can come up with
197
* false positives (imagine a graid, one of whose devices is *dev).
198
*/
199
n = getfsstat(NULL, 0, MNT_NOWAIT) + 1;
200
if (n < 0) {
201
rv = errno;
202
goto errout;
203
}
204
mntlen = sizeof(struct statfs) * n;
205
mnt = malloc(mntlen);
206
n = getfsstat(mnt, mntlen, MNT_NOWAIT);
207
if (n < 0) {
208
rv = errno;
209
goto errout;
210
}
211
212
/*
213
* Find glabel, if it exists. It's OK if not: we'll skip searching for
214
* labels.
215
*/
216
LIST_FOREACH(glabel, &mesh->lg_class, lg_class) {
217
if (strcmp(glabel->lg_name, G_LABEL) == 0)
218
break;
219
}
220
221
provider = pp;
222
for (i = 0; i < n; i++) {
223
/*
224
* Skip all pseudo filesystems. This also skips the real filesystem
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 error from stat.
332
*
333
* Finally, check to make sure the resulting path is still on the same
334
* device. If not, return ENODEV.
335
*
336
* Otherwise return 0.
337
*
338
* The dev or full path that's returned is malloced, so needs to be freed when
339
* the caller is done about it. Unlike many other functions, we can return data
340
* with an error code, so pay attention.
341
*/
342
int
343
efivar_device_path_to_unix_path(const_efidp dp, char **dev, char **relpath, char **abspath)
344
{
345
const_efidp walker;
346
struct gmesh mesh;
347
int rv = 0;
348
349
/*
350
* Sanity check args, fail early
351
*/
352
if (dp == NULL || dev == NULL || relpath == NULL || abspath == NULL)
353
return (EDOOFUS);
354
355
*dev = NULL;
356
*relpath = NULL;
357
*abspath = NULL;
358
359
/*
360
* Find the first media device path we can. If we go too far,
361
* assume the passed in device path is bogus. If we hit the end
362
* then we didn't find a media device path, so signal that error.
363
*/
364
walker = dp;
365
if (!ValidLen(walker))
366
return (EINVAL);
367
while (DevicePathType(walker) != MEDIA_DEVICE_PATH &&
368
DevicePathType(walker) != END_DEVICE_PATH_TYPE) {
369
walker = (const_efidp)NextDevicePathNode(walker);
370
if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
371
return (EINVAL);
372
if (!ValidLen(walker))
373
return (EINVAL);
374
}
375
if (DevicePathType(walker) != MEDIA_DEVICE_PATH)
376
return (EINVAL);
377
378
/*
379
* There's several types of media paths. We're only interested in the
380
* hard disk path, as it's really the only relevant one to booting. The
381
* CD path just might also be relevant, and would be easy to add, but
382
* isn't supported. A file path too is relevant, but at this stage, it's
383
* premature because we're trying to translate a specification for a device
384
* and path on that device into a unix path, or at the very least, a
385
* geom device : path-on-device.
386
*
387
* Also, ZFS throws a bit of a monkey wrench in here since it doesn't have
388
* a device path type (it creates a new virtual device out of one or more
389
* storage devices).
390
*
391
* For all of them, we'll need to know the geoms, so allocate / free the
392
* geom mesh here since it's safer than doing it in each sub-function
393
* which may have many error exits.
394
*/
395
if (geom_gettree(&mesh))
396
return (ENOMEM);
397
398
rv = EINVAL;
399
if (DevicePathSubType(walker) == MEDIA_HARDDRIVE_DP)
400
rv = efi_hd_to_unix(&mesh, walker, dev, relpath, abspath);
401
#ifdef notyet
402
else if (is_cdrom_device(walker))
403
rv = efi_cdrom_to_unix(&mesh, walker, dev, relpath, abspath);
404
else if (is_floppy_device(walker))
405
rv = efi_floppy_to_unix(&mesh, walker, dev, relpath, abspath);
406
else if (is_zpool_device(walker))
407
rv = efi_zpool_to_unix(&mesh, walker, dev, relpath, abspath);
408
#endif
409
geom_deletetree(&mesh);
410
411
return (rv);
412
}
413
414
/*
415
* Construct the EFI path to a current unix path as follows.
416
*
417
* The path may be of one of three forms:
418
* 1) /path/to/file -- full path to a file. The file need not be present,
419
* but /path/to must be. It must reside on a local filesystem
420
* mounted on a GPT or MBR partition.
421
* 2) //path/to/file -- Shorthand for 'On the EFI partition, \path\to\file'
422
* where 'The EFI Partition' is a partition that's type is 'efi'
423
* on the same disk that / is mounted from. If there are multiple
424
* or no 'efi' parittions on that disk, or / isn't on a disk that
425
* we can trace back to a physical device, an error will result
426
* 3) [/dev/]geom-name:/path/to/file -- Use the specified partition
427
* (and it must be a GPT or MBR partition) with the specified
428
* path. The latter is not authenticated.
429
* all path forms translate any \ characters to / before further processing.
430
* When a file path node is created, all / characters are translated back
431
* to \.
432
*
433
* For paths of the first form:
434
* find where the filesystem is mount (either the file directly, or
435
* its parent directory).
436
* translate any logical device name (eg label) 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 filesystem.
446
* If none found, return ENXIO
447
* Create a media device path node for the found partition
448
* Append a File Path to the end for the rest of the file.
449
*
450
* For paths of the third form
451
* Translate the geom-name passed in into a physical partition
452
* name.
453
* Return ENXIO if the translation fails
454
* Make a media device path for it
455
* append the part after the : as a File path node.
456
*/
457
458
static char *
459
path_to_file_dp(const char *relpath)
460
{
461
char *rv;
462
463
asprintf(&rv, "File(%s)", relpath);
464
return rv;
465
}
466
467
static char *
468
find_geom_efi_on_root(struct gmesh *mesh)
469
{
470
struct statfs buf;
471
const char *dev;
472
struct gprovider *pp;
473
// struct ggeom *disk;
474
struct gconsumer *cp;
475
476
/*
477
* Find /'s geom. Assume it's mounted on /dev/ and filter out all the
478
* filesystems that aren't.
479
*/
480
if (statfs("/", &buf) != 0)
481
return (NULL);
482
dev = buf.f_mntfromname;
483
if (*dev != '/' || strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
484
return (NULL);
485
dev += sizeof(_PATH_DEV) -1;
486
pp = find_provider_by_name(mesh, dev);
487
if (pp == NULL)
488
return (NULL);
489
490
/*
491
* If the provider is a LABEL, find it's outer PART class, if any. We
492
* only operate on partitions.
493
*/
494
if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0) {
495
LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
496
if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_PART) == 0) {
497
pp = cp->lg_provider;
498
break;
499
}
500
}
501
}
502
if (strcmp(pp->lg_geom->lg_class->lg_name, G_PART) != 0)
503
return (NULL);
504
505
#if 0
506
/* This doesn't work because we can't get the data to walk UP the tree it seems */
507
508
/*
509
* Now that we've found the PART that we have mounted as root, find the
510
* first efi typed partition that's a peer, if any.
511
*/
512
LIST_FOREACH(cp, &pp->lg_consumers, lg_consumer) {
513
if (strcmp(cp->lg_provider->lg_geom->lg_class->lg_name, G_DISK) == 0) {
514
disk = cp->lg_provider->lg_geom;
515
break;
516
}
517
}
518
if (disk == NULL) /* This is very bad -- old nested partitions -- no support ? */
519
return (NULL);
520
#endif
521
522
#if 0
523
/* This doesn't work because we can't get the data to walk UP the tree it seems */
524
525
/*
526
* With the disk provider, we can look for its consumers to see if any are the proper type.
527
*/
528
LIST_FOREACH(pp, &disk->lg_consumer, lg_consumer) {
529
type = geom_pp_attr(mesh, pp, "type");
530
if (type == NULL)
531
continue;
532
if (strcmp(type, "efi") != 0)
533
continue;
534
efimedia = geom_pp_attr(mesh, pp, "efimedia");
535
if (efimedia == NULL)
536
return (NULL);
537
return strdup(efimedia);
538
}
539
#endif
540
return (NULL);
541
}
542
543
544
static char *
545
find_geom_efimedia(struct gmesh *mesh, const char *dev)
546
{
547
struct gprovider *pp;
548
const char *efimedia;
549
550
pp = find_provider_by_name(mesh, dev);
551
if (pp == NULL)
552
return (NULL);
553
efimedia = geom_pp_attr(mesh, pp, "efimedia");
554
555
/*
556
* If this device doesn't have an efimedia attribute, see if it is a
557
* glabel node, and if so look for the underlying provider to get the
558
* efimedia attribute from.
559
*/
560
if (efimedia == NULL &&
561
strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) == 0)
562
efimedia = find_geom_efimedia(mesh, pp->lg_geom->lg_name);
563
if (efimedia == NULL)
564
return (NULL);
565
return strdup(efimedia);
566
}
567
568
static int
569
build_dp(const char *efimedia, const char *relpath, efidp *dp)
570
{
571
char *fp = NULL, *dptxt = NULL, *cp, *rp = NULL;
572
int rv = 0;
573
efidp out = NULL;
574
size_t len;
575
576
if (relpath != NULL) {
577
rp = strdup(relpath);
578
for (cp = rp; *cp; cp++)
579
if (*cp == '/')
580
*cp = '\\';
581
fp = path_to_file_dp(rp);
582
free(rp);
583
if (fp == NULL) {
584
rv = ENOMEM;
585
goto errout;
586
}
587
}
588
589
asprintf(&dptxt, "%s/%s", efimedia, fp == NULL ? "" : fp);
590
out = malloc(8192);
591
len = efidp_parse_device_path(dptxt, out, 8192);
592
if (len > 8192) {
593
rv = ENOMEM;
594
goto errout;
595
}
596
if (len == 0) {
597
rv = EINVAL;
598
goto errout;
599
}
600
601
*dp = out;
602
errout:
603
if (rv) {
604
free(out);
605
}
606
free(dptxt);
607
free(fp);
608
609
return rv;
610
}
611
612
/* Handles //path/to/file */
613
/*
614
* Which means: find the disk that has /. Then look for a EFI partition
615
* and use that for the efimedia and /path/to/file as relative to that.
616
* Not sure how ZFS will work here since we can't easily make the leap
617
* to the geom from the zpool.
618
*/
619
static int
620
efipart_to_dp(struct gmesh *mesh, char *path, efidp *dp)
621
{
622
char *efimedia = NULL;
623
int rv;
624
625
efimedia = find_geom_efi_on_root(mesh);
626
#ifdef notyet
627
if (efimedia == NULL)
628
efimedia = find_efi_on_zfsroot(dev);
629
#endif
630
if (efimedia == NULL) {
631
rv = ENOENT;
632
goto errout;
633
}
634
635
rv = build_dp(efimedia, path + 1, dp);
636
errout:
637
free(efimedia);
638
639
return rv;
640
}
641
642
/* Handles [/dev/]geom:[/]path/to/file */
643
/* Handles zfs-dataset:[/]path/to/file (this may include / ) */
644
static int
645
dev_path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
646
{
647
char *relpath, *dev, *efimedia = NULL;
648
int rv = 0;
649
650
relpath = strchr(path, ':');
651
assert(relpath != NULL);
652
*relpath++ = '\0';
653
654
dev = path;
655
if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
656
dev += sizeof(_PATH_DEV) -1;
657
658
efimedia = find_geom_efimedia(mesh, dev);
659
#ifdef notyet
660
if (efimedia == NULL)
661
find_zfs_efi_media(dev);
662
#endif
663
if (efimedia == NULL) {
664
rv = ENOENT;
665
goto errout;
666
}
667
rv = build_dp(efimedia, relpath, dp);
668
errout:
669
free(efimedia);
670
671
return rv;
672
}
673
674
/* Handles /path/to/file */
675
/* Handles /dev/foo/bar */
676
static int
677
path_to_dp(struct gmesh *mesh, char *path, efidp *dp)
678
{
679
struct statfs buf;
680
char *rp = NULL, *ep, *dev, *efimedia = NULL;
681
int rv = 0;
682
683
rp = realpath(path, NULL);
684
if (rp == NULL) {
685
rv = errno;
686
goto errout;
687
}
688
689
if (statfs(rp, &buf) != 0) {
690
rv = errno;
691
goto errout;
692
}
693
694
dev = buf.f_mntfromname;
695
/*
696
* If we're fed a raw /dev/foo/bar, then devfs is returned from the
697
* statfs call. In that case, use that dev and assume we have a path
698
* of nothing.
699
*/
700
if (strcmp(dev, "devfs") == 0) {
701
dev = rp + sizeof(_PATH_DEV) - 1;
702
ep = NULL;
703
} else {
704
if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
705
dev += sizeof(_PATH_DEV) - 1;
706
ep = rp + strlen(buf.f_mntonname);
707
}
708
709
efimedia = find_geom_efimedia(mesh, dev);
710
#ifdef notyet
711
if (efimedia == NULL)
712
find_zfs_efi_media(dev);
713
#endif
714
if (efimedia == NULL) {
715
rv = ENOENT;
716
goto errout;
717
}
718
719
rv = build_dp(efimedia, ep, dp);
720
errout:
721
free(efimedia);
722
free(rp);
723
if (rv != 0) {
724
free(*dp);
725
*dp = NULL;
726
}
727
return (rv);
728
}
729
730
int
731
efivar_unix_path_to_device_path(const char *path, efidp *dp)
732
{
733
char *modpath = NULL, *cp;
734
int rv = ENOMEM;
735
struct gmesh mesh;
736
737
/*
738
* Fail early for clearly bogus things
739
*/
740
if (path == NULL || dp == NULL)
741
return (EDOOFUS);
742
743
/*
744
* We'll need the goem mesh to grovel through it to find the
745
* efimedia attribute for any devices we find. Grab it here
746
* and release it to simplify the error paths out of the
747
* subordinate functions
748
*/
749
if (geom_gettree(&mesh))
750
return (errno);
751
752
/*
753
* Convert all \ to /. We'll convert them back again when
754
* we encode the file. Boot loaders are expected to cope.
755
*/
756
modpath = strdup(path);
757
if (modpath == NULL)
758
goto out;
759
for (cp = modpath; *cp; cp++)
760
if (*cp == '\\')
761
*cp = '/';
762
763
if (modpath[0] == '/' && modpath[1] == '/') /* Handle //foo/bar/baz */
764
rv = efipart_to_dp(&mesh, modpath, dp);
765
else if (strchr(modpath, ':')) /* Handle dev:/bar/baz */
766
rv = dev_path_to_dp(&mesh, modpath, dp);
767
else /* Handle /a/b/c */
768
rv = path_to_dp(&mesh, modpath, dp);
769
770
out:
771
geom_deletetree(&mesh);
772
free(modpath);
773
774
return (rv);
775
}
776
777