diff --git a/library/std/src/sys/pal/uefi/fs.rs b/library/std/src/sys/pal/uefi/fs.rs index 9585ec24f687d..06ddd288cd4c3 100644 --- a/library/std/src/sys/pal/uefi/fs.rs +++ b/library/std/src/sys/pal/uefi/fs.rs @@ -311,8 +311,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> { unsupported() } -pub fn exists(_path: &Path) -> io::Result { - unsupported() +pub fn exists(path: &Path) -> io::Result { + let f = uefi_fs::File::from_path(path, r_efi::protocols::file::MODE_READ, 0); + match f { + Ok(_) => Ok(true), + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false), + Err(e) => Err(e), + } } pub fn readlink(_p: &Path) -> io::Result { @@ -342,3 +347,122 @@ pub fn canonicalize(_p: &Path) -> io::Result { pub fn copy(_from: &Path, _to: &Path) -> io::Result { unsupported() } + +mod uefi_fs { + use r_efi::protocols::{device_path, file, simple_file_system}; + + use super::super::helpers; + use crate::boxed::Box; + use crate::io; + use crate::mem::MaybeUninit; + use crate::path::Path; + use crate::ptr::NonNull; + + pub(crate) struct File(NonNull); + + impl File { + pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result { + let absoulte = crate::path::absolute(path)?; + + let p = helpers::OwnedDevicePath::from_text(absoulte.as_os_str())?; + let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?; + + vol.open(&mut path_remaining, open_mode, attr) + } + + fn open_volume_from_device_path( + path: helpers::BorrowedDevicePath<'_>, + ) -> io::Result<(Self, Box<[u16]>)> { + let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) { + Ok(x) => x, + Err(e) => return Err(e), + }; + for handle in handles { + let volume_device_path: NonNull = + match helpers::open_protocol(handle, device_path::PROTOCOL_GUID) { + Ok(x) => x, + Err(_) => continue, + }; + let volume_device_path = helpers::BorrowedDevicePath::new(volume_device_path); + + if let Some(left_path) = path_best_match(&volume_device_path, &path) { + return Ok((Self::open_volume(handle)?, left_path)); + } + } + + Err(io::const_error!(io::ErrorKind::NotFound, "Volume Not Found")) + } + + // Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL + fn open_volume(device_handle: NonNull) -> io::Result { + let simple_file_system_protocol = helpers::open_protocol::( + device_handle, + simple_file_system::PROTOCOL_GUID, + )?; + + let mut file_protocol: MaybeUninit<*mut file::Protocol> = MaybeUninit::uninit(); + let r = unsafe { + ((*simple_file_system_protocol.as_ptr()).open_volume)( + simple_file_system_protocol.as_ptr(), + file_protocol.as_mut_ptr(), + ) + }; + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + // Since no error was returned, file protocol should be non-NULL. + let p = NonNull::new(unsafe { file_protocol.assume_init() }).unwrap(); + Ok(Self(p)) + } + + fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result { + let file_ptr = self.0.as_ptr(); + let mut file_opened: MaybeUninit<*mut file::Protocol> = MaybeUninit::uninit(); + + let r = unsafe { + ((*file_ptr).open)( + file_ptr, + file_opened.as_mut_ptr(), + path.as_mut_ptr(), + open_mode, + attr, + ) + }; + + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + // Since no error was returned, file protocol should be non-NULL. + let p = NonNull::new(unsafe { file_opened.assume_init() }).unwrap(); + Ok(File(p)) + } + } + + impl Drop for File { + fn drop(&mut self) { + let file_ptr = self.0.as_ptr(); + let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) }; + } + } + + fn path_best_match<'a>( + source: &helpers::BorrowedDevicePath<'a>, + target: &helpers::BorrowedDevicePath<'a>, + ) -> Option> { + let mut source_iter = source.iter().take_while(|x| !x.is_end_instance()); + let mut target_iter = target.iter().take_while(|x| !x.is_end_instance()); + + loop { + match (source_iter.next(), target_iter.next()) { + (Some(x), Some(y)) if x == y => continue, + (None, Some(y)) => { + let p = y.to_path().to_text().ok()?; + return helpers::os_string_to_raw(&p); + } + _ => return None, + } + } + } +} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index dccc137d6f561..44728fc9ed0fb 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -14,6 +14,7 @@ use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_error}; +use crate::iter::Iterator; use crate::marker::PhantomData; use crate::mem::{MaybeUninit, size_of}; use crate::os::uefi::env::boot_services; @@ -216,6 +217,60 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } +fn device_node_to_text(path: NonNull) -> io::Result { + fn node_to_text( + protocol: NonNull, + path: NonNull, + ) -> io::Result { + let path_ptr: *mut r_efi::efi::Char16 = unsafe { + ((*protocol.as_ptr()).convert_device_node_to_text)( + path.as_ptr(), + // DisplayOnly + r_efi::efi::Boolean::FALSE, + // AllowShortcuts + r_efi::efi::Boolean::FALSE, + ) + }; + + let path = os_string_from_raw(path_ptr) + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?; + + if let Some(boot_services) = crate::os::uefi::env::boot_services() { + let boot_services: NonNull = boot_services.cast(); + unsafe { + ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); + } + } + + Ok(path) + } + + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + return node_to_text(protocol, path); + } + } + + let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; + for handle in device_path_to_text_handles { + if let Ok(protocol) = open_protocol::( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return node_to_text(protocol, path); + } + } + + Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) +} + /// Gets RuntimeServices. pub(crate) fn runtime_services() -> Option> { let system_table: NonNull = @@ -316,6 +371,10 @@ impl<'a> BorrowedDevicePath<'a> { Self { protocol, phantom: PhantomData } } + pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> { + DevicePathIterator::new(DevicePathNode::new(self.protocol)) + } + pub(crate) fn to_text(&self) -> io::Result { device_path_to_text(self.protocol) } @@ -330,6 +389,99 @@ impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { } } +pub(crate) struct DevicePathIterator<'a>(Option>); + +impl<'a> DevicePathIterator<'a> { + const fn new(node: DevicePathNode<'a>) -> Self { + if node.is_end() { Self(None) } else { Self(Some(node)) } + } +} + +impl<'a> Iterator for DevicePathIterator<'a> { + type Item = DevicePathNode<'a>; + + fn next(&mut self) -> Option { + let cur_node = self.0?; + + let next_node = unsafe { cur_node.next_node() }; + self.0 = if next_node.is_end() { None } else { Some(next_node) }; + + Some(cur_node) + } +} + +#[derive(Copy, Clone)] +pub(crate) struct DevicePathNode<'a> { + protocol: NonNull, + phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, +} + +impl<'a> DevicePathNode<'a> { + pub(crate) const fn new(protocol: NonNull) -> Self { + Self { protocol, phantom: PhantomData } + } + + pub(crate) const fn length(&self) -> u16 { + let len = unsafe { (*self.protocol.as_ptr()).length }; + u16::from_le_bytes(len) + } + + pub(crate) const fn node_type(&self) -> u8 { + unsafe { (*self.protocol.as_ptr()).r#type } + } + + pub(crate) const fn sub_type(&self) -> u8 { + unsafe { (*self.protocol.as_ptr()).sub_type } + } + + pub(crate) const fn is_end(&self) -> bool { + self.node_type() == r_efi::protocols::device_path::TYPE_END + && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE + } + + pub(crate) const fn is_end_instance(&self) -> bool { + self.node_type() == r_efi::protocols::device_path::TYPE_END + && self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_INSTANCE + } + + pub(crate) unsafe fn next_node(&self) -> Self { + let node = unsafe { + self.protocol + .cast::() + .add(self.length().into()) + .cast::() + }; + Self::new(node) + } + + pub(crate) fn to_path(&'a self) -> BorrowedDevicePath<'a> { + BorrowedDevicePath::new(self.protocol) + } +} + +impl<'a> PartialEq for DevicePathNode<'a> { + fn eq(&self, other: &Self) -> bool { + let self_len = self.length(); + let other_len = other.length(); + + self_len == other_len + && unsafe { + compiler_builtins::mem::memcmp( + self.protocol.as_ptr().cast(), + other.protocol.as_ptr().cast(), + usize::from(self_len), + ) == 0 + } + } +} + +impl<'a> crate::fmt::Debug for DevicePathNode<'a> { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + let p = device_node_to_text(self.protocol).unwrap(); + p.fmt(f) + } +} + pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull,