1 | use super::{File, FileHandle, FileInfo, FromUefi, RegularFile}; |
2 | use crate::data_types::Align; |
3 | use crate::Result; |
4 | use core::ffi::c_void; |
5 | #[cfg (feature = "alloc" )] |
6 | use {crate::mem::make_boxed, alloc::boxed::Box}; |
7 | #[cfg (all(feature = "unstable" , feature = "alloc" ))] |
8 | use {alloc::alloc::Global, core::alloc::Allocator}; |
9 | |
10 | /// A `FileHandle` that is also a directory. |
11 | /// |
12 | /// Use `File::into_type` or `Directory::new` to create a `Directory`. In |
13 | /// addition to supporting the normal `File` operations, `Directory` |
14 | /// supports iterating over its contained files. |
15 | #[repr (transparent)] |
16 | #[derive (Debug)] |
17 | pub struct Directory(RegularFile); |
18 | |
19 | impl Directory { |
20 | /// Coverts a `FileHandle` into a `Directory` without checking the file type. |
21 | /// # Safety |
22 | /// This function should only be called on files which ARE directories, |
23 | /// doing otherwise is unsafe. |
24 | #[must_use ] |
25 | pub unsafe fn new(handle: FileHandle) -> Self { |
26 | Self(RegularFile::new(handle)) |
27 | } |
28 | |
29 | /// Read the next directory entry. |
30 | /// |
31 | /// Try to read the next directory entry into `buffer`. If the buffer is too small, report the |
32 | /// required buffer size as part of the error. If there are no more directory entries, return |
33 | /// an empty optional. |
34 | /// |
35 | /// The input buffer must be correctly aligned for a `FileInfo`. You can query the required |
36 | /// alignment through the `Align` trait (`<FileInfo as Align>::alignment()`). |
37 | /// |
38 | /// # Arguments |
39 | /// * `buffer` The target buffer of the read operation |
40 | /// |
41 | /// # Errors |
42 | /// |
43 | /// All errors come from calls to [`RegularFile::read`]. |
44 | pub fn read_entry<'buf>( |
45 | &mut self, |
46 | buffer: &'buf mut [u8], |
47 | ) -> Result<Option<&'buf mut FileInfo>, Option<usize>> { |
48 | // Make sure that the storage is properly aligned |
49 | FileInfo::assert_aligned(buffer); |
50 | |
51 | // Read the directory entry into the aligned storage |
52 | self.0.read(buffer).map(|read_bytes| { |
53 | // 0 read bytes signals that the last directory entry was read |
54 | let last_directory_entry_read = read_bytes == 0; |
55 | if last_directory_entry_read { |
56 | None |
57 | } else { |
58 | unsafe { Some(FileInfo::from_uefi(buffer.as_mut_ptr().cast::<c_void>())) } |
59 | } |
60 | }) |
61 | } |
62 | |
63 | /// Wrapper around [`Self::read_entry`] that returns an owned copy of the data. It has the same |
64 | /// implications and requirements. On failure, the payload of `Err` is `()´. |
65 | #[cfg (feature = "alloc" )] |
66 | pub fn read_entry_boxed(&mut self) -> Result<Option<Box<FileInfo>>> { |
67 | let read_entry_res = self.read_entry(&mut []); |
68 | |
69 | // If no more entries are available, return early. |
70 | if let Ok(None) = read_entry_res { |
71 | return Ok(None); |
72 | } |
73 | |
74 | let fetch_data_fn = |buf| { |
75 | self.read_entry(buf) |
76 | // this is safe, as above, we checked that there are more entries |
77 | .map(|maybe_info: Option<&mut FileInfo>| { |
78 | maybe_info.expect("Should have more entries" ) |
79 | }) |
80 | }; |
81 | |
82 | #[cfg (not(feature = "unstable" ))] |
83 | let file_info = make_boxed::<FileInfo, _>(fetch_data_fn)?; |
84 | |
85 | #[cfg (feature = "unstable" )] |
86 | let file_info = make_boxed::<FileInfo, _, _>(fetch_data_fn, Global)?; |
87 | |
88 | Ok(Some(file_info)) |
89 | } |
90 | |
91 | /// Wrapper around [`Self::read_entry`] that returns an owned copy of the data. It has the same |
92 | /// implications and requirements. On failure, the payload of `Err` is `()´. |
93 | /// |
94 | /// It allows to use a custom allocator via the `allocator_api` feature. |
95 | #[cfg (all(feature = "unstable" , feature = "alloc" ))] |
96 | pub fn read_entry_boxed_in<A: Allocator>( |
97 | &mut self, |
98 | allocator: A, |
99 | ) -> Result<Option<Box<FileInfo>>> { |
100 | let read_entry_res = self.read_entry(&mut []); |
101 | |
102 | // If no more entries are available, return early. |
103 | if let Ok(None) = read_entry_res { |
104 | return Ok(None); |
105 | } |
106 | |
107 | let fetch_data_fn = |buf| { |
108 | self.read_entry(buf) |
109 | // this is safe, as above, we checked that there are more entries |
110 | .map(|maybe_info: Option<&mut FileInfo>| { |
111 | maybe_info.expect("Should have more entries" ) |
112 | }) |
113 | }; |
114 | |
115 | let file_info = make_boxed::<FileInfo, _, A>(fetch_data_fn, allocator)?; |
116 | |
117 | Ok(Some(file_info)) |
118 | } |
119 | |
120 | /// Start over the process of enumerating directory entries |
121 | /// |
122 | /// # Errors |
123 | /// |
124 | /// All errors come from calls to [`RegularFile::set_position`]. |
125 | pub fn reset_entry_readout(&mut self) -> Result { |
126 | self.0.set_position(0) |
127 | } |
128 | } |
129 | |
130 | impl File for Directory { |
131 | #[inline ] |
132 | fn handle(&mut self) -> &mut FileHandle { |
133 | self.0.handle() |
134 | } |
135 | |
136 | fn is_regular_file(&self) -> Result<bool> { |
137 | Ok(false) |
138 | } |
139 | |
140 | fn is_directory(&self) -> Result<bool> { |
141 | Ok(true) |
142 | } |
143 | } |
144 | |