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