1use super::{File, FileHandle, FileInfo, FromUefi, RegularFile};
2use crate::data_types::Align;
3use crate::Result;
4use core::ffi::c_void;
5#[cfg(feature = "alloc")]
6use {crate::mem::make_boxed, alloc::boxed::Box};
7#[cfg(all(feature = "unstable", feature = "alloc"))]
8use {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)]
17pub struct Directory(RegularFile);
18
19impl 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
130impl 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