bridge(4): document net.link.bridge.pfil_onlyip
[dragonfly.git] / lib / libefivar / efivar-dp-xlate.c
blob9ab9c63ce6e0b5297b92b3aa8c201d394ff51517
1 /*-
2 * Copyright (c) 2017 Netflix, Inc.
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.
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.
25 * $FreeBSD: head/lib/libefivar/efivar-dp-xlate.c 355546 2019-12-09 01:32:18Z imp $
28 #include <sys/param.h>
29 #include <sys/ucred.h>
30 #include <sys/mount.h>
31 #include <sys/sysctl.h>
33 #undef MAX
34 #undef MIN
36 #include <assert.h>
37 #include <efivar.h>
38 #include <errno.h>
39 #include <paths.h>
40 #include <stdio.h>
41 #include <string.h>
43 #include "libefivar_int.h"
45 #include "efi-osdep.h"
46 #include "efivar-dp.h"
48 #include "uefi-dplib.h"
50 #include "map.h"
51 #include "gpt.h"
53 #define MAX_DP_SANITY 4096 /* Biggest device path in bytes */
54 #define MAX_DP_TEXT_LEN 4096 /* Longest string rep of dp */
56 #define ValidLen(dp) (DevicePathNodeLength(dp) >= sizeof(EFI_DEVICE_PATH_PROTOCOL) && \
57 DevicePathNodeLength(dp) < MAX_DP_SANITY)
59 #if 0
60 #define G_PART "PART"
61 #define G_LABEL "LABEL"
62 #define G_DISK "DISK"
63 #endif
65 static int
66 find_slice_by_efimedia(char *buf, char **dev)
68 char disks[1024], efimedia[128], dev_path[256], part[256];
69 char *disk, *disk_ptr, *s;
70 size_t len;
71 int fd;
72 map_t *m;
73 uuid_t guid;
74 struct gpt_ent *ent;
76 len = 1024;
77 if (sysctlbyname("kern.disks", disks, &len, NULL, 0) < 0)
78 return -1;
79 disk_ptr = disks;
80 while ((disk = strsep(&disk_ptr, " ")) != NULL) {
81 if (disk[0] == '\0')
82 continue;
83 if (strncmp(disk, "md", 2) == 0 ||
84 strncmp(disk, "cd", 2) == 0 ||
85 strncmp(disk, "acd", 3) == 0 ||
86 strncmp(disk, "fd", 2) == 0)
87 continue;
88 snprintf(dev_path, sizeof(dev_path), "%s%s", _PATH_DEV, disk);
89 fd = gpt_open(dev_path);
90 if (fd == -1)
91 continue;
92 for (m = map_first(); m != NULL; m = m->map_next) {
93 if (m->map_index == NOENTRY ||
94 m->map_type != MAP_TYPE_GPT_PART)
95 continue;
96 ent = m->map_data;
97 s = NULL;
98 le_uuid_dec(&ent->ent_uuid, &guid);
99 uuid_to_string(&guid, &s, NULL);
100 snprintf(efimedia, sizeof(efimedia),
101 "HD(%d,GPT,%s,%#jx,%#jx)", m->map_index + 1, s,
102 (uintmax_t)m->map_start, (uintmax_t)m->map_size);
103 free(s);
104 if (strcasecmp(efimedia, buf) == 0) {
105 gpt_close(fd);
106 snprintf(part, sizeof(part), "%s%ss%d",
107 _PATH_DEV, disk, m->map_index);
108 *dev = strdup(part);
109 return 0;
112 gpt_close(fd);
114 return -1;
117 static int
118 efi_hd_to_unix(const_efidp dp, char **dev, char **relpath, char **abspath)
120 int error, rv = 0, n, i;
121 const_efidp media, file, walker;
122 size_t len, mntlen;
123 char buf[MAX_DP_TEXT_LEN];
124 char *pwalk;
125 struct statfs *mnt;
127 walker = media = dp;
130 * Now, we can either have a filepath node next, or the end.
131 * Otherwise, it's an error.
133 if (!ValidLen(walker))
134 return (EINVAL);
135 walker = (const_efidp)NextDevicePathNode(walker);
136 if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
137 return (EINVAL);
138 if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
139 DevicePathSubType(walker) == MEDIA_FILEPATH_DP)
140 file = walker;
141 else if (DevicePathType(walker) == MEDIA_DEVICE_PATH &&
142 DevicePathType(walker) == END_DEVICE_PATH_TYPE)
143 file = NULL;
144 else
145 return (EINVAL);
148 * Format this node. We're going to look for it as a efimedia
149 * attribute of some geom node. Once we find that node, we use it
150 * as the device it comes from, at least provisionally.
152 len = efidp_format_device_path_node(buf, sizeof(buf), media);
153 if (len > sizeof(buf))
154 return (EINVAL);
156 error = find_slice_by_efimedia(buf, dev);
157 if (error == -1) {
158 rv = ENOENT;
159 goto errout;
162 if (*dev == NULL) {
163 rv = ENOMEM;
164 goto errout;
168 * No file specified, just return the device. Don't even look
169 * for a mountpoint. XXX Sane?
171 if (file == NULL)
172 goto errout;
175 * Now extract the relative path. The next node in the device path should
176 * be a filesystem node. If not, we have issues.
178 *relpath = efidp_extract_file_path(file);
179 if (*relpath == NULL) {
180 rv = ENOMEM;
181 goto errout;
183 for (pwalk = *relpath; *pwalk; pwalk++)
184 if (*pwalk == '\\')
185 *pwalk = '/';
188 * To find the absolute path, we have to look for where we're mounted.
189 * We only look a little hard, since looking too hard can come up with
190 * false positives (imagine a graid, one of whose devices is *dev).
192 n = getfsstat(NULL, 0, MNT_NOWAIT) + 1;
193 if (n < 0) {
194 rv = errno;
195 goto errout;
197 mntlen = sizeof(struct statfs) * n;
198 mnt = malloc(mntlen);
199 n = getfsstat(mnt, mntlen, MNT_NOWAIT);
200 if (n < 0) {
201 rv = errno;
202 goto errout;
204 for (i = 0; i < n; i++) {
206 * Skip all pseudo filesystems. This also skips the real filesytsem
207 * of ZFS. There's no EFI designator for ZFS in the standard, so
208 * we'll need to invent one, but its decoding will be handled in
209 * a separate function.
211 if (mnt[i].f_mntfromname[0] != '/')
212 continue;
215 * First see if it is directly attached
217 if (strcmp(*dev, mnt[i].f_mntfromname) == 0)
218 break;
220 #if 0 /* XXX swildner */
222 * Next see if it is attached via one of the physical disk's
223 * labels.
225 LIST_FOREACH(cp, &provider->lg_consumers, lg_consumer) {
226 pp = cp->lg_provider;
227 if (strcmp(pp->lg_geom->lg_class->lg_name, G_LABEL) != 0)
228 continue;
229 if (strcmp(g_device_path(pp->lg_name), mnt[i].f_mntfromname) == 0)
230 goto break2;
232 /* Not the one, try the next mount point */
233 #endif
235 #if 0
236 break2:
237 #endif
240 * No mountpoint found, no absolute path possible
242 if (i >= n)
243 goto errout;
246 * Construct absolute path and we're finally done.
248 if (strcmp(mnt[i].f_mntonname, "/") == 0)
249 asprintf(abspath, "/%s", *relpath);
250 else
251 asprintf(abspath, "%s/%s", mnt[i].f_mntonname, *relpath);
253 errout:
254 if (rv != 0) {
255 free(*dev);
256 *dev = NULL;
257 free(*relpath);
258 *relpath = NULL;
260 return (rv);
264 * Translate the passed in device_path to a unix path via the following
265 * algorithm.
267 * If dp, dev or path NULL, return EDOOFUS. XXX wise?
269 * Set *path = NULL; *dev = NULL;
271 * Walk through the device_path until we find either a media device path.
272 * Return EINVAL if not found. Return EINVAL if walking dp would
273 * land us more than sanity size away from the start (4k).
275 * If we find a media descriptor, we search through the GPTs to see if we
276 * can find a matching node. If no match is found in the GPTs that matches,
277 * return ENXIO.
279 * Once we find a matching node, we search to see if there is a filesystem
280 * mounted on it. If we find nothing, then search each of the devices that are
281 * mounted to see if we can work up the geom tree to find the matching node. if
282 * we still can't find anything, *dev = sprintf("/dev/%s", slice_name
283 * of the original node we found), but return ENOTBLK.
285 * Record the dev of the mountpoint in *dev.
287 * Once we find something, check to see if the next node in the device path is
288 * the end of list. If so, return the mountpoint.
290 * If the next node isn't a File path node, return EFTYPE.
292 * Extract the path from the File path node(s). translate any \ file separators
293 * to /. Append the result to the mount point. Copy the resulting path into
294 * *path. Stat that path. If it is not found, return the errorr from stat.
296 * Finally, check to make sure the resulting path is still on the same
297 * device. If not, return ENODEV.
299 * Otherwise return 0.
301 * The dev or full path that's returned is malloced, so needs to be freed when
302 * the caller is done about it. Unlike many other functions, we can return data
303 * with an error code, so pay attention.
306 efivar_device_path_to_unix_path(const_efidp dp, char **dev, char **relpath, char **abspath)
308 const_efidp walker;
309 int rv = 0;
312 * Sanity check args, fail early
314 if (dp == NULL || dev == NULL || relpath == NULL || abspath == NULL)
315 return (EDOOFUS);
317 *dev = NULL;
318 *relpath = NULL;
319 *abspath = NULL;
322 * Find the first media device path we can. If we go too far,
323 * assume the passed in device path is bogus. If we hit the end
324 * then we didn't find a media device path, so signal that error.
326 walker = dp;
327 if (!ValidLen(walker))
328 return (EINVAL);
329 while (DevicePathType(walker) != MEDIA_DEVICE_PATH &&
330 DevicePathType(walker) != END_DEVICE_PATH_TYPE) {
331 walker = (const_efidp)NextDevicePathNode(walker);
332 if ((uintptr_t)walker - (uintptr_t)dp > MAX_DP_SANITY)
333 return (EINVAL);
334 if (!ValidLen(walker))
335 return (EINVAL);
337 if (DevicePathType(walker) != MEDIA_DEVICE_PATH)
338 return (EINVAL);
341 * There's several types of media paths. We're only interested in the
342 * hard disk path, as it's really the only relevant one to booting. The
343 * CD path just might also be relevant, and would be easy to add, but
344 * isn't supported. A file path too is relevant, but at this stage, it's
345 * premature because we're trying to translate a specification for a device
346 * and path on that device into a unix path, or at the very least, a
347 * device : path-on-device.
349 rv = EINVAL;
350 if (DevicePathSubType(walker) == MEDIA_HARDDRIVE_DP)
351 rv = efi_hd_to_unix(walker, dev, relpath, abspath);
352 #ifdef notyet
353 else if (is_cdrom_device(walker))
354 rv = efi_cdrom_to_unix(walker, dev, relpath, abspath);
355 else if (is_floppy_device(walker))
356 rv = efi_floppy_to_unix(walker, dev, relpath, abspath);
357 else if (is_zpool_device(walker))
358 rv = efi_zpool_to_unix(walker, dev, relpath, abspath);
359 #endif
361 return (rv);
365 * Construct the EFI path to a current unix path as follows.
367 * The path may be of one of three forms:
368 * 1) /path/to/file -- full path to a file. The file need not be present,
369 * but /path/to must be. It must reside on a local filesystem
370 * mounted on a GPT or MBR partition.
371 * 2) //path/to/file -- Shorthand for 'On the EFI partition, \path\to\file'
372 * where 'The EFI Partition' is a partiton that's type is 'efi'
373 * on the same disk that / is mounted from. If there are multiple
374 * or no 'efi' parittions on that disk, or / isn't on a disk that
375 * we can trace back to a physical device, an error will result
376 * 3) [/dev/]geom-name:/path/to/file -- Use the specified partition
377 * (and it must be a GPT or MBR partition) with the specified
378 * path. The latter is not authenticated.
379 * all path forms translate any \ characters to / before further processing.
380 * When a file path node is created, all / characters are translated back
381 * to \.
383 * For paths of the first form:
384 * find where the filesystem is mount (either the file directly, or
385 * its parent directory).
386 * translate any logical device name (eg lable) to a physical one
387 * If not possible, return ENXIO
388 * If the physical path is unsupported (Eg not on a GPT or MBR disk),
389 * return ENXIO
390 * Create a media device path node.
391 * append the relative path from the mountpoint to the media device node
392 * as a file path.
394 * For paths matching the second form:
395 * find the EFI partition corresponding to the root fileystem.
396 * If none found, return ENXIO
397 * Create a media device path node for the found partition
398 * Append a File Path to the end for the rest of the file.
400 * For paths of the third form
401 * Translate the geom-name passed in into a physical partition
402 * name.
403 * Return ENXIO if the translation fails
404 * Make a media device path for it
405 * append the part after the : as a File path node.
408 static char *
409 path_to_file_dp(const char *relpath)
411 char *rv;
413 asprintf(&rv, "File(%s)", relpath);
414 return rv;
417 static char *
418 find_geom_efi_on_root(void)
420 /* Didn't return anything but NULL in FreeBSD */
421 return (NULL);
425 static char *
426 find_slice_efimedia(const char *slice)
428 char disk_path[1024], disks[1024], efimedia[128];
429 char *disk, *disk_ptr, *ep, *s;
430 int fd;
431 u_long sliceno;
432 size_t len;
433 map_t *m;
434 uuid_t guid;
435 struct gpt_ent *ent;
437 len = 1024;
438 if (sysctlbyname("kern.disks", disks, &len, NULL, 0) < 0)
439 return (NULL);
440 disk_ptr = disks;
441 while ((disk = strsep(&disk_ptr, " ")) != NULL) {
442 if (disk[0] == '\0')
443 continue;
444 if (strncmp(disk, "md", 2) == 0 ||
445 strncmp(disk, "cd", 2) == 0 ||
446 strncmp(disk, "acd", 3) == 0 ||
447 strncmp(disk, "fd", 2) == 0)
448 continue;
449 if (strncmp(slice, disk, strlen(disk)) == 0) {
450 snprintf(disk_path, sizeof(disk_path), "%s%s",
451 _PATH_DEV, disk);
452 /* XXX should base be 36 here? */
453 sliceno = strtoul(slice + sizeof(disk) + 1, &ep, 10);
454 if (*ep != 0)
455 return (NULL);
456 break;
459 if (disk == NULL)
460 return (NULL);
461 fd = gpt_open(disk_path);
462 if (fd == -1)
463 return (NULL);
464 for (m = map_first(); m != NULL; m = m->map_next) {
465 if (m->map_index == NOENTRY ||
466 m->map_type != MAP_TYPE_GPT_PART)
467 continue;
468 if (m->map_index == sliceno) {
469 ent = m->map_data;
470 s = NULL;
471 le_uuid_dec(&ent->ent_uuid, &guid);
472 uuid_to_string(&guid, &s, NULL);
473 snprintf(efimedia, sizeof(efimedia),
474 "HD(%d,GPT,%s,%#jx,%#jx)", m->map_index + 1, s,
475 (uintmax_t)m->map_start, (uintmax_t)m->map_size);
476 free(s);
477 gpt_close(fd);
478 return strdup(efimedia);
481 gpt_close(fd);
482 return (NULL);
485 static int
486 build_dp(const char *efimedia, const char *relpath, efidp *dp)
488 char *fp, *dptxt = NULL, *cp, *rp;
489 int rv = 0;
490 efidp out = NULL;
491 size_t len;
493 rp = strdup(relpath);
494 for (cp = rp; *cp; cp++)
495 if (*cp == '/')
496 *cp = '\\';
497 fp = path_to_file_dp(rp);
498 free(rp);
499 if (fp == NULL) {
500 rv = ENOMEM;
501 goto errout;
504 asprintf(&dptxt, "%s/%s", efimedia, fp);
505 out = malloc(8192);
506 len = efidp_parse_device_path(dptxt, out, 8192);
507 if (len > 8192) {
508 rv = ENOMEM;
509 goto errout;
511 if (len == 0) {
512 rv = EINVAL;
513 goto errout;
516 *dp = out;
517 errout:
518 if (rv) {
519 free(out);
521 free(dptxt);
522 free(fp);
524 return rv;
527 /* Handles //path/to/file */
529 * Which means: find the disk that has /. Then look for a EFI partition
530 * and use that for the efimedia and /path/to/file as relative to that.
531 * Not sure how ZFS will work here since we can't easily make the leap
532 * to the geom from the zpool.
534 static int
535 efipart_to_dp(char *path, efidp *dp)
537 char *efimedia = NULL;
538 int rv;
540 efimedia = find_geom_efi_on_root();
541 #ifdef notyet
542 if (efimedia == NULL)
543 efimedia = find_efi_on_zfsroot(dev);
544 #endif
545 if (efimedia == NULL) {
546 rv = ENOENT;
547 goto errout;
550 rv = build_dp(efimedia, path + 1, dp);
551 errout:
552 free(efimedia);
554 return rv;
557 /* Handles [/dev/]geom:[/]path/to/file */
558 /* Handles zfs-dataset:[/]path/to/file (this may include / ) */
559 static int
560 dev_path_to_dp(char *path, efidp *dp)
562 char *relpath, *dev, *efimedia = NULL;
563 int rv = 0;
565 relpath = strchr(path, ':');
566 assert(relpath != NULL);
567 *relpath++ = '\0';
569 dev = path;
570 if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
571 dev += sizeof(_PATH_DEV) -1;
573 efimedia = find_slice_efimedia(dev);
574 #ifdef notyet
575 if (efimedia == NULL)
576 find_zfs_efi_media(dev);
577 #endif
578 if (efimedia == NULL) {
579 rv = ENOENT;
580 goto errout;
582 rv = build_dp(efimedia, relpath, dp);
583 errout:
584 free(efimedia);
586 return rv;
589 /* Handles /path/to/file */
590 static int
591 path_to_dp(char *path, efidp *dp)
593 struct statfs buf;
594 char *rp = NULL, *ep, *dev, *efimedia = NULL;
595 int rv = 0;
597 rp = realpath(path, NULL);
598 if (rp == NULL) {
599 rv = errno;
600 goto errout;
603 if (statfs(rp, &buf) != 0) {
604 rv = errno;
605 goto errout;
608 dev = buf.f_mntfromname;
609 if (strncmp(dev, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
610 dev += sizeof(_PATH_DEV) -1;
611 ep = rp + strlen(buf.f_mntonname);
613 efimedia = find_slice_efimedia(dev);
614 #ifdef notyet
615 if (efimedia == NULL)
616 find_zfs_efi_media(dev);
617 #endif
618 if (efimedia == NULL) {
619 rv = ENOENT;
620 goto errout;
623 rv = build_dp(efimedia, ep, dp);
624 errout:
625 free(efimedia);
626 free(rp);
627 if (rv != 0) {
628 free(*dp);
629 *dp = NULL;
632 return (rv);
636 efivar_unix_path_to_device_path(const char *path, efidp *dp)
638 char *modpath = NULL, *cp;
639 int rv = ENOMEM;
642 * Fail early for clearly bogus things
644 if (path == NULL || dp == NULL)
645 return (EDOOFUS);
648 * Convert all \ to /. We'll convert them back again when
649 * we encode the file. Boot loaders are expected to cope.
651 modpath = strdup(path);
652 if (modpath == NULL)
653 goto out;
654 for (cp = modpath; *cp; cp++)
655 if (*cp == '\\')
656 *cp = '/';
658 if (modpath[0] == '/' && modpath[1] == '/') /* Handle //foo/bar/baz */
659 rv = efipart_to_dp(modpath, dp);
660 else if (strchr(modpath, ':')) /* Handle dev:/bar/baz */
661 rv = dev_path_to_dp(modpath, dp);
662 else /* Handle /a/b/c */
663 rv = path_to_dp(modpath, dp);
665 out:
666 free(modpath);
668 return (rv);