Skip to content

Commit e156e8b

Browse files
committed
fs: add method read_entry_boxed to Directory + test
1 parent 3a42bd1 commit e156e8b

File tree

2 files changed

+126
-13
lines changed

2 files changed

+126
-13
lines changed

src/proto/media/file/dir.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ use crate::data_types::Align;
33
use crate::Result;
44
use core::ffi::c_void;
55

6+
#[cfg(feature = "alloc")]
7+
use crate::{ResultExt, Status};
8+
#[cfg(feature = "alloc")]
9+
use alloc_api::alloc;
10+
#[cfg(feature = "alloc")]
11+
use alloc_api::boxed::Box;
12+
#[cfg(feature = "alloc")]
13+
use core::alloc::Layout;
14+
#[cfg(feature = "alloc")]
15+
use core::ptr::NonNull;
16+
#[cfg(feature = "alloc")]
17+
use core::slice;
18+
619
/// A `FileHandle` that is also a directory.
720
///
821
/// Use `File::into_type` or `Directory::new` to create a `Directory`. In
@@ -20,7 +33,7 @@ impl Directory {
2033
Self(RegularFile::new(handle))
2134
}
2235

23-
/// Read the next directory entry
36+
/// Read the next directory entry.
2437
///
2538
/// Try to read the next directory entry into `buffer`. If the buffer is too small, report the
2639
/// required buffer size as part of the error. If there are no more directory entries, return
@@ -56,6 +69,64 @@ impl Directory {
5669
})
5770
}
5871

72+
/// Wrapper around [`Self::read_entry`] that returns an owned copy of the data. It has the same
73+
/// implications and requirements. On failure, the payload of `Err` is `()´.
74+
#[cfg(feature = "alloc")]
75+
pub fn read_entry_boxed(&mut self) -> Result<Option<Box<FileInfo>>> {
76+
let read_entry_res = self.read_entry(&mut []);
77+
78+
// If no more entries are available, return early.
79+
if let Ok(None) = read_entry_res {
80+
return Ok(None);
81+
}
82+
83+
let required_size = match read_entry_res
84+
.expect_err("zero sized read unexpectedly succeeded")
85+
.split()
86+
{
87+
// Early return if something has failed.
88+
(s, None) => return Err(s.into()),
89+
(_, Some(required_size)) => required_size,
90+
};
91+
92+
// We add trailing padding because the size of a rust structure must
93+
// always be a multiple of alignment.
94+
let layout = Layout::from_size_align(required_size, FileInfo::alignment())
95+
.unwrap()
96+
.pad_to_align();
97+
98+
// Allocate the buffer.
99+
let heap_buf: NonNull<u8> = unsafe {
100+
let ptr = alloc::alloc(layout);
101+
match NonNull::new(ptr) {
102+
None => return Err(Status::OUT_OF_RESOURCES.into()),
103+
Some(ptr) => ptr,
104+
}
105+
};
106+
107+
// Get the file info using the allocated buffer for storage.
108+
let info = {
109+
let buffer = unsafe { slice::from_raw_parts_mut(heap_buf.as_ptr(), layout.size()) };
110+
self.read_entry(buffer).discard_errdata()
111+
};
112+
113+
// If an error occurred, deallocate the memory before returning.
114+
let info = match info {
115+
Ok(info) => info,
116+
Err(err) => {
117+
unsafe { alloc::dealloc(heap_buf.as_ptr(), layout) };
118+
return Err(err);
119+
}
120+
};
121+
122+
// Wrap the file info in a box so that it will be deallocated on
123+
// drop. This is valid because the memory was allocated with the
124+
// global allocator.
125+
let info = info.map(|info| unsafe { Box::from_raw(info) });
126+
127+
Ok(info)
128+
}
129+
59130
/// Start over the process of enumerating directory entries
60131
pub fn reset_entry_readout(&mut self) -> Result {
61132
self.0.set_position(0)

uefi-test-runner/src/proto/media/known_disk.rs

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use alloc::string::ToString;
2+
use core::cell::RefCell;
23
use core::ptr::NonNull;
34
use uefi::prelude::*;
45
use uefi::proto::media::block::BlockIO;
@@ -21,20 +22,61 @@ fn test_existing_dir(directory: &mut Directory) {
2122

2223
assert!(dir.is_directory().unwrap());
2324

24-
let mut dir = dir.into_directory().expect("not a directory");
25-
26-
// Collect and validate the directory entries.
27-
let mut entry_names = vec![];
28-
let mut buf = vec![0; 200];
29-
loop {
30-
let entry = dir.read_entry(&mut buf).expect("failed to read directory");
31-
if let Some(entry) = entry {
32-
entry_names.push(entry.file_name().to_string());
33-
} else {
34-
break;
25+
let dir = dir.into_directory().expect("Should be a directory");
26+
27+
let dir = RefCell::new(dir);
28+
29+
// Backing memory to read the file info data into.
30+
let mut stack_buf = [0; 200];
31+
32+
// The file names that the test read from the directory.
33+
let entry_names = RefCell::new(vec![]);
34+
35+
// Expected file names in the directory.
36+
const EXPECTED: &[&str] = &[".", "..", "test_input.txt"];
37+
38+
// Reads the whole directory with provided backing memory.
39+
let mut test_read_dir_stack_mem = || {
40+
let mut dir = dir.borrow_mut();
41+
let mut entry_names = entry_names.borrow_mut();
42+
loop {
43+
let entry = dir
44+
.read_entry(&mut stack_buf)
45+
.expect("failed to read directory");
46+
if let Some(entry) = entry {
47+
entry_names.push(entry.file_name().to_string());
48+
} else {
49+
break;
50+
}
3551
}
52+
assert_eq!(&*entry_names, EXPECTED);
53+
};
54+
55+
// Reads the whole directory but returns owned memory on the heap.
56+
let test_read_dir_heap_mem = || {
57+
let mut dir = dir.borrow_mut();
58+
let mut entry_names = entry_names.borrow_mut();
59+
loop {
60+
let entry = dir.read_entry_boxed().expect("failed to read directory");
61+
if let Some(entry) = entry {
62+
entry_names.push(entry.file_name().to_string());
63+
} else {
64+
break;
65+
}
66+
}
67+
assert_eq!(&*entry_names, EXPECTED);
68+
};
69+
70+
// Tests all read dir test functions three times.
71+
for _ in 0..3 {
72+
entry_names.borrow_mut().clear();
73+
dir.borrow_mut().reset_entry_readout().unwrap();
74+
test_read_dir_stack_mem();
75+
76+
entry_names.borrow_mut().clear();
77+
dir.borrow_mut().reset_entry_readout().unwrap();
78+
test_read_dir_heap_mem();
3679
}
37-
assert_eq!(entry_names, [".", "..", "test_input.txt"]);
3880
}
3981

4082
/// Test that deleting a file opened in read-only mode fails with a

0 commit comments

Comments
 (0)