-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
base: master
Are you sure you want to change the base?
uefi: fs: Implement exists #135368
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> { | ||
|
@@ -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)?; | ||
|
||
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( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Also, are there restrictions on |
||
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>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add docstring |
||
source: &helpers::BorrowedDevicePath<'a>, | ||
target: &helpers::BorrowedDevicePath<'a>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
) -> 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, | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<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> = | ||
|
@@ -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) | ||
} | ||
|
@@ -330,6 +389,99 @@ impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { | |
} | ||
} | ||
|
||
pub(crate) struct DevicePathIterator<'a>(Option<DevicePathNode<'a>>); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you move the addition of the iterator to a separate PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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>, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absoulte -> absolute