diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 21ef70b43dcd6..1c65e3e2b1554 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -21,9 +21,13 @@ pub struct FileAttr { created: Option, } -pub struct ReadDir(!); +pub struct ReadDir(uefi_fs::File); -pub struct DirEntry(!); +pub struct DirEntry { + attr: FileAttr, + file_name: OsString, + path: PathBuf, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -143,8 +147,10 @@ impl FileType { } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut b = f.debug_struct("ReadDir"); + b.field("path", &self.0.path()); + b.finish() } } @@ -152,25 +158,35 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - self.0 + match self.0.read_dir_entry() { + Ok(None) => None, + Ok(Some(x)) => Some(Ok(DirEntry::from_uefi(x, self.0.path()))), + Err(e) => Some(Err(e)), + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.path.clone() } pub fn file_name(&self) -> OsString { - self.0 + self.file_name.clone() } pub fn metadata(&self) -> io::Result { - self.0 + Ok(self.attr.clone()) } pub fn file_type(&self) -> io::Result { - self.0 + Ok(self.attr.file_type()) + } + + fn from_uefi(info: helpers::UefiBox, parent: &Path) -> Self { + let file_name = uefi_fs::file_name_from_uefi(&info); + let path = parent.join(&file_name); + Self { file_name, path, attr: FileAttr::from_uefi(info) } } } @@ -344,8 +360,17 @@ impl fmt::Debug for File { } } -pub fn readdir(_p: &Path) -> io::Result { - unsupported() +pub fn readdir(p: &Path) -> io::Result { + let path = crate::path::absolute(p)?; + let f = uefi_fs::File::from_path(&path, file::MODE_READ, 0)?; + let file_info = f.file_info()?; + let file_attr = FileAttr::from_uefi(file_info); + + if file_attr.file_type().is_dir() { + Ok(ReadDir(f)) + } else { + Err(io::const_error!(io::ErrorKind::NotADirectory, "expected a directory but got a file")) + } } pub fn unlink(p: &Path) -> io::Result<()> { @@ -459,13 +484,18 @@ mod uefi_fs { use r_efi::protocols::{device_path, file, simple_file_system}; use crate::boxed::Box; + use crate::ffi::OsString; use crate::io; + use crate::os::uefi::ffi::OsStringExt; use crate::path::Path; use crate::ptr::NonNull; use crate::sys::helpers::{self, UefiBox}; use crate::sys::time::{self, SystemTime}; - pub(crate) struct File(NonNull); + pub(crate) struct File { + protocol: NonNull, + path: crate::path::PathBuf, + } impl File { pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result { @@ -474,7 +504,8 @@ mod uefi_fs { let p = helpers::OwnedDevicePath::from_text(absolute.as_os_str())?; let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?; - vol.open(&mut path_remaining, open_mode, attr) + let protocol = Self::open(vol, &mut path_remaining, open_mode, attr)?; + Ok(Self { protocol, path: absolute }) } /// Open Filesystem volume given a devicepath to the volume, or a file/directory in the @@ -490,7 +521,7 @@ mod uefi_fs { /// and return the remaining file path "\abc\run.efi". fn open_volume_from_device_path( path: helpers::BorrowedDevicePath<'_>, - ) -> io::Result<(Self, Box<[u16]>)> { + ) -> io::Result<(NonNull, Box<[u16]>)> { let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) { Ok(x) => x, Err(e) => return Err(e), @@ -512,7 +543,9 @@ mod uefi_fs { } // Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL - fn open_volume(device_handle: NonNull) -> io::Result { + fn open_volume( + device_handle: NonNull, + ) -> io::Result> { let simple_file_system_protocol = helpers::open_protocol::( device_handle, simple_file_system::PROTOCOL_GUID, @@ -531,11 +564,16 @@ mod uefi_fs { // Since no error was returned, file protocol should be non-NULL. let p = NonNull::new(file_protocol).unwrap(); - Ok(Self(p)) + Ok(p) } - fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result { - let file_ptr = self.0.as_ptr(); + fn open( + protocol: NonNull, + path: &mut [u16], + open_mode: u64, + attr: u64, + ) -> io::Result> { + let file_ptr = protocol.as_ptr(); let mut file_opened = crate::ptr::null_mut(); let r = unsafe { @@ -548,11 +586,37 @@ mod uefi_fs { // Since no error was returned, file protocol should be non-NULL. let p = NonNull::new(file_opened).unwrap(); - Ok(File(p)) + Ok(p) + } + + pub(crate) fn read_dir_entry(&self) -> io::Result>> { + let file_ptr = self.protocol.as_ptr(); + let mut buf_size = 0; + + let r = unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, crate::ptr::null_mut()) }; + + if buf_size == 0 { + return Ok(None); + } + + assert!(r.is_error()); + if r != r_efi::efi::Status::BUFFER_TOO_SMALL { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + let mut info: UefiBox = UefiBox::new(buf_size)?; + let r = + unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, info.as_mut_ptr().cast()) }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + Ok(Some(info)) + } } pub(crate) fn file_info(&self) -> io::Result> { - let file_ptr = self.0.as_ptr(); + let file_ptr = self.protocol.as_ptr(); let mut info_id = file::INFO_ID; let mut buf_size = 0; @@ -583,7 +647,7 @@ mod uefi_fs { } pub(crate) fn set_file_info(&self, mut info: UefiBox) -> io::Result<()> { - let file_ptr = self.0.as_ptr(); + let file_ptr = self.protocol.as_ptr(); let mut info_id = file::INFO_ID; let r = unsafe { @@ -594,7 +658,7 @@ mod uefi_fs { } pub(crate) fn delete(self) -> io::Result<()> { - let file_ptr = self.0.as_ptr(); + let file_ptr = self.protocol.as_ptr(); let r = unsafe { ((*file_ptr).delete)(file_ptr) }; // Spec states that even in case of failure, the file handle will be closed. @@ -602,12 +666,16 @@ mod uefi_fs { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } + + pub(crate) fn path(&self) -> &Path { + &self.path + } } impl Drop for File { fn drop(&mut self) { - let file_ptr = self.0.as_ptr(); - let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) }; + let file_ptr = self.protocol.as_ptr(); + let _ = unsafe { ((*file_ptr).close)(file_ptr) }; } } @@ -647,7 +715,7 @@ mod uefi_fs { let (vol, mut path_remaining) = File::open_volume_from_device_path(p.borrow())?; // Check if file exists - match vol.open(&mut path_remaining, file::MODE_READ, 0) { + match File::open(vol, &mut path_remaining, file::MODE_READ, 0) { Ok(_) => { return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Path already exists")); } @@ -655,7 +723,8 @@ mod uefi_fs { Err(e) => return Err(e), } - let _ = vol.open( + let _ = File::open( + vol, &mut path_remaining, file::MODE_READ | file::MODE_WRITE | file::MODE_CREATE, file::DIRECTORY, @@ -680,4 +749,14 @@ mod uefi_fs { let now = time::system_time_internal::now(); time.to_uefi_loose(now.timezone, now.daylight) } + + pub(crate) fn file_name_from_uefi(info: &UefiBox) -> OsString { + let file_name = { + let size = unsafe { (*info.as_ptr()).size }; + let strlen = (size as usize - crate::mem::size_of::>() - 1) / 2; + unsafe { crate::slice::from_raw_parts((*info.as_ptr()).file_name.as_ptr(), strlen) } + }; + + OsString::from_wide(file_name) + } }