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
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
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>
43 #include "libefivar_int.h"
45 #include "efi-osdep.h"
46 #include "efivar-dp.h"
48 #include "uefi-dplib.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)
61 #define G_LABEL "LABEL"
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
;
77 if (sysctlbyname("kern.disks", disks
, &len
, NULL
, 0) < 0)
80 while ((disk
= strsep(&disk_ptr
, " ")) != NULL
) {
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)
88 snprintf(dev_path
, sizeof(dev_path
), "%s%s", _PATH_DEV
, disk
);
89 fd
= gpt_open(dev_path
);
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
)
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
);
104 if (strcasecmp(efimedia
, buf
) == 0) {
106 snprintf(part
, sizeof(part
), "%s%ss%d",
107 _PATH_DEV
, disk
, m
->map_index
);
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
;
123 char buf
[MAX_DP_TEXT_LEN
];
130 * Now, we can either have a filepath node next, or the end.
131 * Otherwise, it's an error.
133 if (!ValidLen(walker
))
135 walker
= (const_efidp
)NextDevicePathNode(walker
);
136 if ((uintptr_t)walker
- (uintptr_t)dp
> MAX_DP_SANITY
)
138 if (DevicePathType(walker
) == MEDIA_DEVICE_PATH
&&
139 DevicePathSubType(walker
) == MEDIA_FILEPATH_DP
)
141 else if (DevicePathType(walker
) == MEDIA_DEVICE_PATH
&&
142 DevicePathType(walker
) == END_DEVICE_PATH_TYPE
)
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
))
156 error
= find_slice_by_efimedia(buf
, dev
);
168 * No file specified, just return the device. Don't even look
169 * for a mountpoint. XXX Sane?
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
) {
183 for (pwalk
= *relpath
; *pwalk
; 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;
197 mntlen
= sizeof(struct statfs
) * n
;
198 mnt
= malloc(mntlen
);
199 n
= getfsstat(mnt
, mntlen
, MNT_NOWAIT
);
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] != '/')
215 * First see if it is directly attached
217 if (strcmp(*dev
, mnt
[i
].f_mntfromname
) == 0)
220 #if 0 /* XXX swildner */
222 * Next see if it is attached via one of the physical disk's
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)
229 if (strcmp(g_device_path(pp
->lg_name
), mnt
[i
].f_mntfromname
) == 0)
232 /* Not the one, try the next mount point */
240 * No mountpoint found, no absolute path possible
246 * Construct absolute path and we're finally done.
248 if (strcmp(mnt
[i
].f_mntonname
, "/") == 0)
249 asprintf(abspath
, "/%s", *relpath
);
251 asprintf(abspath
, "%s/%s", mnt
[i
].f_mntonname
, *relpath
);
264 * Translate the passed in device_path to a unix path via the following
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,
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
)
312 * Sanity check args, fail early
314 if (dp
== NULL
|| dev
== NULL
|| relpath
== NULL
|| 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.
327 if (!ValidLen(walker
))
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
)
334 if (!ValidLen(walker
))
337 if (DevicePathType(walker
) != MEDIA_DEVICE_PATH
)
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.
350 if (DevicePathSubType(walker
) == MEDIA_HARDDRIVE_DP
)
351 rv
= efi_hd_to_unix(walker
, dev
, relpath
, abspath
);
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
);
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
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),
390 * Create a media device path node.
391 * append the relative path from the mountpoint to the media device node
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
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.
409 path_to_file_dp(const char *relpath
)
413 asprintf(&rv
, "File(%s)", relpath
);
418 find_geom_efi_on_root(void)
420 /* Didn't return anything but NULL in FreeBSD */
426 find_slice_efimedia(const char *slice
)
428 char disk_path
[1024], disks
[1024], efimedia
[128];
429 char *disk
, *disk_ptr
, *ep
, *s
;
438 if (sysctlbyname("kern.disks", disks
, &len
, NULL
, 0) < 0)
441 while ((disk
= strsep(&disk_ptr
, " ")) != NULL
) {
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)
449 if (strncmp(slice
, disk
, strlen(disk
)) == 0) {
450 snprintf(disk_path
, sizeof(disk_path
), "%s%s",
452 /* XXX should base be 36 here? */
453 sliceno
= strtoul(slice
+ sizeof(disk
) + 1, &ep
, 10);
461 fd
= gpt_open(disk_path
);
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
)
468 if (m
->map_index
== sliceno
) {
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
);
478 return strdup(efimedia
);
486 build_dp(const char *efimedia
, const char *relpath
, efidp
*dp
)
488 char *fp
, *dptxt
= NULL
, *cp
, *rp
;
493 rp
= strdup(relpath
);
494 for (cp
= rp
; *cp
; cp
++)
497 fp
= path_to_file_dp(rp
);
504 asprintf(&dptxt
, "%s/%s", efimedia
, fp
);
506 len
= efidp_parse_device_path(dptxt
, out
, 8192);
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.
535 efipart_to_dp(char *path
, efidp
*dp
)
537 char *efimedia
= NULL
;
540 efimedia
= find_geom_efi_on_root();
542 if (efimedia
== NULL
)
543 efimedia
= find_efi_on_zfsroot(dev
);
545 if (efimedia
== NULL
) {
550 rv
= build_dp(efimedia
, path
+ 1, dp
);
557 /* Handles [/dev/]geom:[/]path/to/file */
558 /* Handles zfs-dataset:[/]path/to/file (this may include / ) */
560 dev_path_to_dp(char *path
, efidp
*dp
)
562 char *relpath
, *dev
, *efimedia
= NULL
;
565 relpath
= strchr(path
, ':');
566 assert(relpath
!= NULL
);
570 if (strncmp(dev
, _PATH_DEV
, sizeof(_PATH_DEV
) - 1) == 0)
571 dev
+= sizeof(_PATH_DEV
) -1;
573 efimedia
= find_slice_efimedia(dev
);
575 if (efimedia
== NULL
)
576 find_zfs_efi_media(dev
);
578 if (efimedia
== NULL
) {
582 rv
= build_dp(efimedia
, relpath
, dp
);
589 /* Handles /path/to/file */
591 path_to_dp(char *path
, efidp
*dp
)
594 char *rp
= NULL
, *ep
, *dev
, *efimedia
= NULL
;
597 rp
= realpath(path
, NULL
);
603 if (statfs(rp
, &buf
) != 0) {
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
);
615 if (efimedia
== NULL
)
616 find_zfs_efi_media(dev
);
618 if (efimedia
== NULL
) {
623 rv
= build_dp(efimedia
, ep
, dp
);
636 efivar_unix_path_to_device_path(const char *path
, efidp
*dp
)
638 char *modpath
= NULL
, *cp
;
642 * Fail early for clearly bogus things
644 if (path
== NULL
|| dp
== NULL
)
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
);
654 for (cp
= modpath
; *cp
; 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
);