Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uefi: fs: Implement exists #135368

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 126 additions & 2 deletions library/std/src/sys/pal/uefi/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,13 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
unsupported()
}

pub fn exists(_path: &Path) -> io::Result<bool> {
unsupported()
pub fn exists(path: &Path) -> io::Result<bool> {
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<PathBuf> {
Expand Down Expand Up @@ -342,3 +347,122 @@ pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
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<file::Protocol>);

impl File {
pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result<Self> {
let absoulte = crate::path::absolute(path)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absoulte -> absolute


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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a docstring, and include information about what's being returned. Include an example of the device path, e.g.

For example, given "PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi", this will open the volume ""PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)" and return the remaining file path "\abc\run.efi".

Also, are there restrictions on path, like needing to be absolute? If so state that as well.

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<device_path::Protocol> =
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<crate::ffi::c_void>) -> io::Result<Self> {
let simple_file_system_protocol = helpers::open_protocol::<simple_file_system::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<Self> {
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>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add docstring

source: &helpers::BorrowedDevicePath<'a>,
target: &helpers::BorrowedDevicePath<'a>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: <'_> and drop the named lifetime?

) -> Option<Box<[u16]>> {
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,
}
}
}
}
152 changes: 152 additions & 0 deletions library/std/src/sys/pal/uefi/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -216,6 +217,60 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found"))
}

fn device_node_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
fn node_to_text(
protocol: NonNull<device_path_to_text::Protocol>,
path: NonNull<device_path::Protocol>,
) -> io::Result<OsString> {
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<r_efi::efi::BootServices> = boot_services.cast();
unsafe {
((*boot_services.as_ptr()).free_pool)(path_ptr.cast());
}
}

Ok(path)
}

static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
AtomicPtr::new(crate::ptr::null_mut());

if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) {
if let Ok(protocol) = open_protocol::<device_path_to_text::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::<device_path_to_text::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<NonNull<r_efi::efi::RuntimeServices>> {
let system_table: NonNull<r_efi::efi::SystemTable> =
Expand Down Expand Up @@ -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<OsString> {
device_path_to_text(self.protocol)
}
Expand All @@ -330,6 +389,99 @@ impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
}
}

pub(crate) struct DevicePathIterator<'a>(Option<DevicePathNode<'a>>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move the addition of the iterator to a separate PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I have limited bandwidth rn, so will do it in a few weeks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have created the PR: #137424


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<Self::Item> {
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<r_efi::protocols::device_path::Protocol>,
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
}

impl<'a> DevicePathNode<'a> {
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> 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::<u8>()
.add(self.length().into())
.cast::<r_efi::protocols::device_path::Protocol>()
};
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<T> {
guid: r_efi::efi::Guid,
handle: NonNull<crate::ffi::c_void>,
Expand Down
Loading