Skip to content

Commit 60bba9b

Browse files
committed
std: sys: fs: uefi: Implement readdir
- Add path field to uefi file since this will also be required in implementing Debug for rust File. - Tested on OVMF. Signed-off-by: Ayush Singh <ayush@beagleboard.org>
1 parent 95a27ad commit 60bba9b

File tree

1 file changed

+105
-26
lines changed

1 file changed

+105
-26
lines changed

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

Lines changed: 105 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ pub struct FileAttr {
2121
created: Option<SystemTime>,
2222
}
2323

24-
pub struct ReadDir(!);
24+
pub struct ReadDir(uefi_fs::File);
2525

26-
pub struct DirEntry(!);
26+
pub struct DirEntry {
27+
attr: FileAttr,
28+
file_name: OsString,
29+
path: PathBuf,
30+
}
2731

2832
#[derive(Clone, Debug)]
2933
pub struct OpenOptions {
@@ -143,34 +147,46 @@ impl FileType {
143147
}
144148

145149
impl fmt::Debug for ReadDir {
146-
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
147-
self.0
150+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151+
let mut b = f.debug_struct("ReadDir");
152+
b.field("path", &self.0.path());
153+
b.finish()
148154
}
149155
}
150156

151157
impl Iterator for ReadDir {
152158
type Item = io::Result<DirEntry>;
153159

154160
fn next(&mut self) -> Option<io::Result<DirEntry>> {
155-
self.0
161+
match self.0.read_dir_entry() {
162+
Ok(None) => None,
163+
Ok(Some(x)) => Some(Ok(DirEntry::from_uefi(x, self.0.path()))),
164+
Err(e) => Some(Err(e)),
165+
}
156166
}
157167
}
158168

159169
impl DirEntry {
160170
pub fn path(&self) -> PathBuf {
161-
self.0
171+
self.path.clone()
162172
}
163173

164174
pub fn file_name(&self) -> OsString {
165-
self.0
175+
self.file_name.clone()
166176
}
167177

168178
pub fn metadata(&self) -> io::Result<FileAttr> {
169-
self.0
179+
Ok(self.attr.clone())
170180
}
171181

172182
pub fn file_type(&self) -> io::Result<FileType> {
173-
self.0
183+
Ok(self.attr.file_type())
184+
}
185+
186+
fn from_uefi(info: helpers::UefiBox<file::Info>, parent: &Path) -> Self {
187+
let file_name = uefi_fs::file_name_from_uefi(&info);
188+
let path = parent.join(&file_name);
189+
Self { file_name, path, attr: FileAttr::from_uefi(info) }
174190
}
175191
}
176192

@@ -344,8 +360,17 @@ impl fmt::Debug for File {
344360
}
345361
}
346362

347-
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
348-
unsupported()
363+
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
364+
let path = crate::path::absolute(p)?;
365+
let f = uefi_fs::File::from_path(&path, file::MODE_READ, 0)?;
366+
let file_info = f.file_info()?;
367+
let file_attr = FileAttr::from_uefi(file_info);
368+
369+
if file_attr.file_type().is_dir() {
370+
Ok(ReadDir(f))
371+
} else {
372+
Err(io::const_error!(io::ErrorKind::NotADirectory, "expected a directory but got a file"))
373+
}
349374
}
350375

351376
pub fn unlink(p: &Path) -> io::Result<()> {
@@ -459,13 +484,18 @@ mod uefi_fs {
459484
use r_efi::protocols::{device_path, file, simple_file_system};
460485

461486
use crate::boxed::Box;
487+
use crate::ffi::OsString;
462488
use crate::io;
489+
use crate::os::uefi::ffi::OsStringExt;
463490
use crate::path::Path;
464491
use crate::ptr::NonNull;
465492
use crate::sys::helpers::{self, UefiBox};
466493
use crate::sys::time::{self, SystemTime};
467494

468-
pub(crate) struct File(NonNull<file::Protocol>);
495+
pub(crate) struct File {
496+
protocol: NonNull<file::Protocol>,
497+
path: crate::path::PathBuf,
498+
}
469499

470500
impl File {
471501
pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result<Self> {
@@ -474,7 +504,8 @@ mod uefi_fs {
474504
let p = helpers::OwnedDevicePath::from_text(absolute.as_os_str())?;
475505
let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?;
476506

477-
vol.open(&mut path_remaining, open_mode, attr)
507+
let protocol = Self::open(vol, &mut path_remaining, open_mode, attr)?;
508+
Ok(Self { protocol, path: absolute })
478509
}
479510

480511
/// Open Filesystem volume given a devicepath to the volume, or a file/directory in the
@@ -490,7 +521,7 @@ mod uefi_fs {
490521
/// and return the remaining file path "\abc\run.efi".
491522
fn open_volume_from_device_path(
492523
path: helpers::BorrowedDevicePath<'_>,
493-
) -> io::Result<(Self, Box<[u16]>)> {
524+
) -> io::Result<(NonNull<file::Protocol>, Box<[u16]>)> {
494525
let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) {
495526
Ok(x) => x,
496527
Err(e) => return Err(e),
@@ -512,7 +543,9 @@ mod uefi_fs {
512543
}
513544

514545
// Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL
515-
fn open_volume(device_handle: NonNull<crate::ffi::c_void>) -> io::Result<Self> {
546+
fn open_volume(
547+
device_handle: NonNull<crate::ffi::c_void>,
548+
) -> io::Result<NonNull<file::Protocol>> {
516549
let simple_file_system_protocol = helpers::open_protocol::<simple_file_system::Protocol>(
517550
device_handle,
518551
simple_file_system::PROTOCOL_GUID,
@@ -531,11 +564,16 @@ mod uefi_fs {
531564

532565
// Since no error was returned, file protocol should be non-NULL.
533566
let p = NonNull::new(file_protocol).unwrap();
534-
Ok(Self(p))
567+
Ok(p)
535568
}
536569

537-
fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result<Self> {
538-
let file_ptr = self.0.as_ptr();
570+
fn open(
571+
protocol: NonNull<file::Protocol>,
572+
path: &mut [u16],
573+
open_mode: u64,
574+
attr: u64,
575+
) -> io::Result<NonNull<file::Protocol>> {
576+
let file_ptr = protocol.as_ptr();
539577
let mut file_opened = crate::ptr::null_mut();
540578

541579
let r = unsafe {
@@ -548,11 +586,37 @@ mod uefi_fs {
548586

549587
// Since no error was returned, file protocol should be non-NULL.
550588
let p = NonNull::new(file_opened).unwrap();
551-
Ok(File(p))
589+
Ok(p)
590+
}
591+
592+
pub(crate) fn read_dir_entry(&self) -> io::Result<Option<UefiBox<file::Info>>> {
593+
let file_ptr = self.protocol.as_ptr();
594+
let mut buf_size = 0;
595+
596+
let r = unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, crate::ptr::null_mut()) };
597+
598+
if buf_size == 0 {
599+
return Ok(None);
600+
}
601+
602+
assert!(r.is_error());
603+
if r != r_efi::efi::Status::BUFFER_TOO_SMALL {
604+
return Err(io::Error::from_raw_os_error(r.as_usize()));
605+
}
606+
607+
let mut info: UefiBox<file::Info> = UefiBox::new(buf_size)?;
608+
let r =
609+
unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, info.as_mut_ptr().cast()) };
610+
611+
if r.is_error() {
612+
Err(io::Error::from_raw_os_error(r.as_usize()))
613+
} else {
614+
Ok(Some(info))
615+
}
552616
}
553617

554618
pub(crate) fn file_info(&self) -> io::Result<UefiBox<file::Info>> {
555-
let file_ptr = self.0.as_ptr();
619+
let file_ptr = self.protocol.as_ptr();
556620
let mut info_id = file::INFO_ID;
557621
let mut buf_size = 0;
558622

@@ -583,7 +647,7 @@ mod uefi_fs {
583647
}
584648

585649
pub(crate) fn set_file_info(&self, mut info: UefiBox<file::Info>) -> io::Result<()> {
586-
let file_ptr = self.0.as_ptr();
650+
let file_ptr = self.protocol.as_ptr();
587651
let mut info_id = file::INFO_ID;
588652

589653
let r = unsafe {
@@ -594,20 +658,24 @@ mod uefi_fs {
594658
}
595659

596660
pub(crate) fn delete(self) -> io::Result<()> {
597-
let file_ptr = self.0.as_ptr();
661+
let file_ptr = self.protocol.as_ptr();
598662
let r = unsafe { ((*file_ptr).delete)(file_ptr) };
599663

600664
// Spec states that even in case of failure, the file handle will be closed.
601665
crate::mem::forget(self);
602666

603667
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
604668
}
669+
670+
pub(crate) fn path(&self) -> &Path {
671+
&self.path
672+
}
605673
}
606674

607675
impl Drop for File {
608676
fn drop(&mut self) {
609-
let file_ptr = self.0.as_ptr();
610-
let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) };
677+
let file_ptr = self.protocol.as_ptr();
678+
let _ = unsafe { ((*file_ptr).close)(file_ptr) };
611679
}
612680
}
613681

@@ -647,15 +715,16 @@ mod uefi_fs {
647715
let (vol, mut path_remaining) = File::open_volume_from_device_path(p.borrow())?;
648716

649717
// Check if file exists
650-
match vol.open(&mut path_remaining, file::MODE_READ, 0) {
718+
match File::open(vol, &mut path_remaining, file::MODE_READ, 0) {
651719
Ok(_) => {
652720
return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Path already exists"));
653721
}
654722
Err(e) if e.kind() == io::ErrorKind::NotFound => {}
655723
Err(e) => return Err(e),
656724
}
657725

658-
let _ = vol.open(
726+
let _ = File::open(
727+
vol,
659728
&mut path_remaining,
660729
file::MODE_READ | file::MODE_WRITE | file::MODE_CREATE,
661730
file::DIRECTORY,
@@ -680,4 +749,14 @@ mod uefi_fs {
680749
let now = time::system_time_internal::now();
681750
time.to_uefi_loose(now.timezone, now.daylight)
682751
}
752+
753+
pub(crate) fn file_name_from_uefi(info: &UefiBox<file::Info>) -> OsString {
754+
let file_name = {
755+
let size = unsafe { (*info.as_ptr()).size };
756+
let strlen = (size as usize - crate::mem::size_of::<file::Info<0>>() - 1) / 2;
757+
unsafe { crate::slice::from_raw_parts((*info.as_ptr()).file_name.as_ptr(), strlen) }
758+
};
759+
760+
OsString::from_wide(file_name)
761+
}
683762
}

0 commit comments

Comments
 (0)