Skip to content

Commit 9f09a04

Browse files
committed
fs: add method read_entry_boxed to Directory + test
1 parent 3fe3639 commit 9f09a04

File tree

2 files changed

+123
-13
lines changed

2 files changed

+123
-13
lines changed

src/proto/media/file/dir.rs

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

6+
#[cfg(feature = "exts")]
7+
use {
8+
crate::{ResultExt, Status},
9+
alloc_api::alloc,
10+
alloc_api::boxed::Box,
11+
core::alloc::Layout,
12+
core::ptr::NonNull,
13+
core::slice,
14+
};
15+
616
/// A `FileHandle` that is also a directory.
717
///
818
/// Use `File::into_type` or `Directory::new` to create a `Directory`. In
@@ -20,7 +30,7 @@ impl Directory {
2030
Self(RegularFile::new(handle))
2131
}
2232

23-
/// Read the next directory entry
33+
/// Read the next directory entry.
2434
///
2535
/// Try to read the next directory entry into `buffer`. If the buffer is too small, report the
2636
/// required buffer size as part of the error. If there are no more directory entries, return
@@ -56,6 +66,64 @@ impl Directory {
5666
})
5767
}
5868

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