Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8846387

Browse files
committedJan 11, 2025
uefi: fs: Implement exists
Also adds the initial file abstractions. The file opening algorithm is inspired from UEFI shell. It starts by classifying if the Path is Shell mapping, text representation of device path protocol, or a relative path and converts into an absolute text representation of device path protocol. After that, it queries all handles supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and opens the volume that matches the device path protocol prefix (similar to Windows drive). After that, it opens the file in the volume using the remaining pat. It also introduces OwnedDevicePath and BorrowedDevicePath abstractions to allow working with the base UEFI and Shell device paths efficiently. DevicePath in UEFI behaves like an a group of nodes laied out in the memory contiguously and thus can be modeled using iterators. Signed-off-by: Ayush Singh <ayush@beagleboard.org>
1 parent ce55b20 commit 8846387

File tree

3 files changed

+395
-13
lines changed

3 files changed

+395
-13
lines changed
 

‎library/std/src/sys/pal/uefi/fs.rs

+189-2
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
311311
unsupported()
312312
}
313313

314-
pub fn exists(_path: &Path) -> io::Result<bool> {
315-
unsupported()
314+
pub fn exists(path: &Path) -> io::Result<bool> {
315+
let f = uefi_fs::File::from_path(path, r_efi::protocols::file::MODE_READ, 0);
316+
match f {
317+
Ok(_) => Ok(true),
318+
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false),
319+
Err(e) => Err(e),
320+
}
316321
}
317322

318323
pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
@@ -342,3 +347,185 @@ pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
342347
pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
343348
unsupported()
344349
}
350+
351+
mod uefi_fs {
352+
use r_efi::protocols::{device_path, file, simple_file_system};
353+
354+
use super::super::helpers;
355+
use crate::boxed::Box;
356+
use crate::io;
357+
use crate::mem::MaybeUninit;
358+
use crate::path::{Path, PathBuf};
359+
use crate::ptr::NonNull;
360+
use crate::sys::unsupported_err;
361+
362+
const COLON: u8 = b':';
363+
364+
pub(crate) struct File(NonNull<file::Protocol>);
365+
366+
impl File {
367+
pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result<Self> {
368+
let absoulte = absolute_path(path)?;
369+
370+
let p = helpers::OwnedDevicePath::from_text(absoulte.as_os_str())?;
371+
let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?;
372+
373+
vol.open(&mut path_remaining, open_mode, attr)
374+
}
375+
376+
fn open_volume_from_device_path(
377+
path: helpers::BorrowedDevicePath<'_>,
378+
) -> io::Result<(Self, Box<[u16]>)> {
379+
let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) {
380+
Ok(x) => x,
381+
Err(e) => return Err(e),
382+
};
383+
for handle in handles {
384+
let volume_device_path: NonNull<device_path::Protocol> =
385+
match helpers::open_protocol(handle, device_path::PROTOCOL_GUID) {
386+
Ok(x) => x,
387+
Err(_) => continue,
388+
};
389+
let volume_device_path = helpers::BorrowedDevicePath::new(volume_device_path);
390+
391+
if let Some(left_path) = path_best_match(&volume_device_path, &path) {
392+
return Ok((Self::open_volume(handle)?, left_path));
393+
}
394+
}
395+
396+
Err(io::const_error!(io::ErrorKind::NotFound, "Volume Not Found"))
397+
}
398+
399+
// Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL
400+
fn open_volume(device_handle: NonNull<crate::ffi::c_void>) -> io::Result<Self> {
401+
let simple_file_system_protocol = helpers::open_protocol::<simple_file_system::Protocol>(
402+
device_handle,
403+
simple_file_system::PROTOCOL_GUID,
404+
)?;
405+
406+
let mut file_protocol: MaybeUninit<*mut file::Protocol> = MaybeUninit::uninit();
407+
let r = unsafe {
408+
((*simple_file_system_protocol.as_ptr()).open_volume)(
409+
simple_file_system_protocol.as_ptr(),
410+
file_protocol.as_mut_ptr(),
411+
)
412+
};
413+
if r.is_error() {
414+
return Err(io::Error::from_raw_os_error(r.as_usize()));
415+
}
416+
417+
// Since no error was returned, file protocol should be non-NULL.
418+
let p = NonNull::new(unsafe { file_protocol.assume_init() }).unwrap();
419+
Ok(Self(p))
420+
}
421+
422+
fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result<Self> {
423+
let file_ptr = self.0.as_ptr();
424+
let mut file_opened: MaybeUninit<*mut file::Protocol> = MaybeUninit::uninit();
425+
426+
let r = unsafe {
427+
((*file_ptr).open)(
428+
file_ptr,
429+
file_opened.as_mut_ptr(),
430+
path.as_mut_ptr(),
431+
open_mode,
432+
attr,
433+
)
434+
};
435+
436+
if r.is_error() {
437+
return Err(io::Error::from_raw_os_error(r.as_usize()));
438+
}
439+
440+
// Since no error was returned, file protocol should be non-NULL.
441+
let p = NonNull::new(unsafe { file_opened.assume_init() }).unwrap();
442+
Ok(File(p))
443+
}
444+
}
445+
446+
impl Drop for File {
447+
fn drop(&mut self) {
448+
let file_ptr = self.0.as_ptr();
449+
let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) };
450+
}
451+
}
452+
453+
fn path_best_match<'a>(
454+
source: &helpers::BorrowedDevicePath<'a>,
455+
target: &helpers::BorrowedDevicePath<'a>,
456+
) -> Option<Box<[u16]>> {
457+
let mut source_iter = source.iter().take_while(|x| !x.is_end_instance());
458+
let mut target_iter = target.iter().take_while(|x| !x.is_end_instance());
459+
460+
loop {
461+
match (source_iter.next(), target_iter.next()) {
462+
(Some(x), Some(y)) if x == y => continue,
463+
(None, Some(y)) => {
464+
return y.to_path().to_text_raw().ok();
465+
}
466+
_ => return None,
467+
}
468+
}
469+
}
470+
471+
/// Get device path protocol associated with shell mapping.
472+
///
473+
/// returns None in case no such mapping is exists
474+
fn get_device_path_from_map(map: &Path) -> io::Result<helpers::BorrowedDevicePath<'static>> {
475+
let shell = helpers::open_shell()
476+
.ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?;
477+
let mut path = helpers::os_string_to_raw(map.as_os_str()).ok_or(io::const_error!(
478+
io::ErrorKind::InvalidFilename,
479+
"Invalid UEFI shell mapping"
480+
))?;
481+
482+
let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) };
483+
let protocol = NonNull::new(protocol)
484+
.ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?;
485+
486+
Ok(helpers::BorrowedDevicePath::new(protocol))
487+
}
488+
489+
fn absolute_path(path: &Path) -> io::Result<PathBuf> {
490+
const FORWARD_SLASH: u8 = b'/';
491+
492+
// Absoulte Shell Path
493+
if path.as_os_str().as_encoded_bytes().contains(&COLON) {
494+
let mut path_components = path.components();
495+
// Since path is not empty, it has at least one Component
496+
let prefix = path_components.next().unwrap();
497+
498+
let dev_path = get_device_path_from_map(prefix.as_ref())?;
499+
let dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?;
500+
501+
let mut ans = PathBuf::new();
502+
ans.push(&dev_path_text);
503+
// UEFI Shell does not seem to end device path with `/`
504+
if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH {
505+
ans.push("/");
506+
}
507+
ans.push(path_components);
508+
509+
return Ok(ans);
510+
}
511+
512+
// Absoulte Device Path
513+
if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) {
514+
return Ok(path.to_path_buf());
515+
}
516+
517+
// cur_dir() always returns something
518+
let cur_dir = crate::env::current_dir().unwrap();
519+
let mut path_components = path.components();
520+
521+
// Relative Root
522+
if path_components.next().unwrap() == crate::path::Component::RootDir {
523+
let mut ans = PathBuf::new();
524+
ans.push(cur_dir.components().next().unwrap());
525+
ans.push(path_components);
526+
return absolute_path(&ans);
527+
}
528+
529+
absolute_path(&cur_dir.join(path))
530+
}
531+
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.