Skip to content

Commit ed0acb1

Browse files
committed
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 <[email protected]>
1 parent c37fbd8 commit ed0acb1

File tree

2 files changed

+278
-2
lines changed

2 files changed

+278
-2
lines changed

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

Lines changed: 126 additions & 2 deletions
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,122 @@ 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;
359+
use crate::ptr::NonNull;
360+
361+
pub(crate) struct File(NonNull<file::Protocol>);
362+
363+
impl File {
364+
pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result<Self> {
365+
let absoulte = crate::path::absolute(path)?;
366+
367+
let p = helpers::OwnedDevicePath::from_text(absoulte.as_os_str())?;
368+
let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?;
369+
370+
vol.open(&mut path_remaining, open_mode, attr)
371+
}
372+
373+
fn open_volume_from_device_path(
374+
path: helpers::BorrowedDevicePath<'_>,
375+
) -> io::Result<(Self, Box<[u16]>)> {
376+
let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) {
377+
Ok(x) => x,
378+
Err(e) => return Err(e),
379+
};
380+
for handle in handles {
381+
let volume_device_path: NonNull<device_path::Protocol> =
382+
match helpers::open_protocol(handle, device_path::PROTOCOL_GUID) {
383+
Ok(x) => x,
384+
Err(_) => continue,
385+
};
386+
let volume_device_path = helpers::BorrowedDevicePath::new(volume_device_path);
387+
388+
if let Some(left_path) = path_best_match(&volume_device_path, &path) {
389+
return Ok((Self::open_volume(handle)?, left_path));
390+
}
391+
}
392+
393+
Err(io::const_error!(io::ErrorKind::NotFound, "Volume Not Found"))
394+
}
395+
396+
// Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL
397+
fn open_volume(device_handle: NonNull<crate::ffi::c_void>) -> io::Result<Self> {
398+
let simple_file_system_protocol = helpers::open_protocol::<simple_file_system::Protocol>(
399+
device_handle,
400+
simple_file_system::PROTOCOL_GUID,
401+
)?;
402+
403+
let mut file_protocol: MaybeUninit<*mut file::Protocol> = MaybeUninit::uninit();
404+
let r = unsafe {
405+
((*simple_file_system_protocol.as_ptr()).open_volume)(
406+
simple_file_system_protocol.as_ptr(),
407+
file_protocol.as_mut_ptr(),
408+
)
409+
};
410+
if r.is_error() {
411+
return Err(io::Error::from_raw_os_error(r.as_usize()));
412+
}
413+
414+
// Since no error was returned, file protocol should be non-NULL.
415+
let p = NonNull::new(unsafe { file_protocol.assume_init() }).unwrap();
416+
Ok(Self(p))
417+
}
418+
419+
fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result<Self> {
420+
let file_ptr = self.0.as_ptr();
421+
let mut file_opened: MaybeUninit<*mut file::Protocol> = MaybeUninit::uninit();
422+
423+
let r = unsafe {
424+
((*file_ptr).open)(
425+
file_ptr,
426+
file_opened.as_mut_ptr(),
427+
path.as_mut_ptr(),
428+
open_mode,
429+
attr,
430+
)
431+
};
432+
433+
if r.is_error() {
434+
return Err(io::Error::from_raw_os_error(r.as_usize()));
435+
}
436+
437+
// Since no error was returned, file protocol should be non-NULL.
438+
let p = NonNull::new(unsafe { file_opened.assume_init() }).unwrap();
439+
Ok(File(p))
440+
}
441+
}
442+
443+
impl Drop for File {
444+
fn drop(&mut self) {
445+
let file_ptr = self.0.as_ptr();
446+
let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) };
447+
}
448+
}
449+
450+
fn path_best_match<'a>(
451+
source: &helpers::BorrowedDevicePath<'a>,
452+
target: &helpers::BorrowedDevicePath<'a>,
453+
) -> Option<Box<[u16]>> {
454+
let mut source_iter = source.iter().take_while(|x| !x.is_end_instance());
455+
let mut target_iter = target.iter().take_while(|x| !x.is_end_instance());
456+
457+
loop {
458+
match (source_iter.next(), target_iter.next()) {
459+
(Some(x), Some(y)) if x == y => continue,
460+
(None, Some(y)) => {
461+
let p = y.to_path().to_text().ok()?;
462+
return helpers::os_string_to_raw(&p);
463+
}
464+
_ => return None,
465+
}
466+
}
467+
}
468+
}

library/std/src/sys/pal/uefi/helpers.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use r_efi::protocols::{device_path, device_path_to_text, shell};
1414

1515
use crate::ffi::{OsStr, OsString};
1616
use crate::io::{self, const_error};
17+
use crate::iter::Iterator;
1718
use crate::marker::PhantomData;
1819
use crate::mem::{MaybeUninit, size_of};
1920
use crate::os::uefi::env::boot_services;
@@ -216,6 +217,60 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
216217
Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found"))
217218
}
218219

220+
fn device_node_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
221+
fn node_to_text(
222+
protocol: NonNull<device_path_to_text::Protocol>,
223+
path: NonNull<device_path::Protocol>,
224+
) -> io::Result<OsString> {
225+
let path_ptr: *mut r_efi::efi::Char16 = unsafe {
226+
((*protocol.as_ptr()).convert_device_node_to_text)(
227+
path.as_ptr(),
228+
// DisplayOnly
229+
r_efi::efi::Boolean::FALSE,
230+
// AllowShortcuts
231+
r_efi::efi::Boolean::FALSE,
232+
)
233+
};
234+
235+
let path = os_string_from_raw(path_ptr)
236+
.ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?;
237+
238+
if let Some(boot_services) = crate::os::uefi::env::boot_services() {
239+
let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast();
240+
unsafe {
241+
((*boot_services.as_ptr()).free_pool)(path_ptr.cast());
242+
}
243+
}
244+
245+
Ok(path)
246+
}
247+
248+
static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
249+
AtomicPtr::new(crate::ptr::null_mut());
250+
251+
if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) {
252+
if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>(
253+
handle,
254+
device_path_to_text::PROTOCOL_GUID,
255+
) {
256+
return node_to_text(protocol, path);
257+
}
258+
}
259+
260+
let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?;
261+
for handle in device_path_to_text_handles {
262+
if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>(
263+
handle,
264+
device_path_to_text::PROTOCOL_GUID,
265+
) {
266+
LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release);
267+
return node_to_text(protocol, path);
268+
}
269+
}
270+
271+
Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found"))
272+
}
273+
219274
/// Gets RuntimeServices.
220275
pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>> {
221276
let system_table: NonNull<r_efi::efi::SystemTable> =
@@ -316,6 +371,10 @@ impl<'a> BorrowedDevicePath<'a> {
316371
Self { protocol, phantom: PhantomData }
317372
}
318373

374+
pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> {
375+
DevicePathIterator::new(DevicePathNode::new(self.protocol))
376+
}
377+
319378
pub(crate) fn to_text(&self) -> io::Result<OsString> {
320379
device_path_to_text(self.protocol)
321380
}
@@ -330,6 +389,99 @@ impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
330389
}
331390
}
332391

392+
pub(crate) struct DevicePathIterator<'a>(Option<DevicePathNode<'a>>);
393+
394+
impl<'a> DevicePathIterator<'a> {
395+
const fn new(node: DevicePathNode<'a>) -> Self {
396+
if node.is_end() { Self(None) } else { Self(Some(node)) }
397+
}
398+
}
399+
400+
impl<'a> Iterator for DevicePathIterator<'a> {
401+
type Item = DevicePathNode<'a>;
402+
403+
fn next(&mut self) -> Option<Self::Item> {
404+
let cur_node = self.0?;
405+
406+
let next_node = unsafe { cur_node.next_node() };
407+
self.0 = if next_node.is_end() { None } else { Some(next_node) };
408+
409+
Some(cur_node)
410+
}
411+
}
412+
413+
#[derive(Copy, Clone)]
414+
pub(crate) struct DevicePathNode<'a> {
415+
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
416+
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
417+
}
418+
419+
impl<'a> DevicePathNode<'a> {
420+
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
421+
Self { protocol, phantom: PhantomData }
422+
}
423+
424+
pub(crate) const fn length(&self) -> u16 {
425+
let len = unsafe { (*self.protocol.as_ptr()).length };
426+
u16::from_le_bytes(len)
427+
}
428+
429+
pub(crate) const fn node_type(&self) -> u8 {
430+
unsafe { (*self.protocol.as_ptr()).r#type }
431+
}
432+
433+
pub(crate) const fn sub_type(&self) -> u8 {
434+
unsafe { (*self.protocol.as_ptr()).sub_type }
435+
}
436+
437+
pub(crate) const fn is_end(&self) -> bool {
438+
self.node_type() == r_efi::protocols::device_path::TYPE_END
439+
&& self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE
440+
}
441+
442+
pub(crate) const fn is_end_instance(&self) -> bool {
443+
self.node_type() == r_efi::protocols::device_path::TYPE_END
444+
&& self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_INSTANCE
445+
}
446+
447+
pub(crate) unsafe fn next_node(&self) -> Self {
448+
let node = unsafe {
449+
self.protocol
450+
.cast::<u8>()
451+
.add(self.length().into())
452+
.cast::<r_efi::protocols::device_path::Protocol>()
453+
};
454+
Self::new(node)
455+
}
456+
457+
pub(crate) fn to_path(&'a self) -> BorrowedDevicePath<'a> {
458+
BorrowedDevicePath::new(self.protocol)
459+
}
460+
}
461+
462+
impl<'a> PartialEq for DevicePathNode<'a> {
463+
fn eq(&self, other: &Self) -> bool {
464+
let self_len = self.length();
465+
let other_len = other.length();
466+
467+
self_len == other_len
468+
&& unsafe {
469+
compiler_builtins::mem::memcmp(
470+
self.protocol.as_ptr().cast(),
471+
other.protocol.as_ptr().cast(),
472+
usize::from(self_len),
473+
) == 0
474+
}
475+
}
476+
}
477+
478+
impl<'a> crate::fmt::Debug for DevicePathNode<'a> {
479+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
480+
let p = device_node_to_text(self.protocol).unwrap();
481+
p.fmt(f)
482+
}
483+
}
484+
333485
pub(crate) struct OwnedProtocol<T> {
334486
guid: r_efi::efi::Guid,
335487
handle: NonNull<crate::ffi::c_void>,

0 commit comments

Comments
 (0)