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]
new(handle: FileHandle) -> Self25     pub const 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`].
read_entry<'buf>( &mut self, buffer: &'buf mut [u8], ) -> Result<Option<&'buf mut FileInfo>, Option<usize>>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_unchunked(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")]
read_entry_boxed(&mut self) -> Result<Option<Box<FileInfo>>>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"))]
read_entry_boxed_in<A: Allocator>( &mut self, allocator: A, ) -> Result<Option<Box<FileInfo>>>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`].
reset_entry_readout(&mut self) -> Result125     pub fn reset_entry_readout(&mut self) -> Result {
126         self.0.set_position(0)
127     }
128 }
129 
130 impl File for Directory {
131     #[inline]
handle(&mut self) -> &mut FileHandle132     fn handle(&mut self) -> &mut FileHandle {
133         self.0.handle()
134     }
135 
is_regular_file(&self) -> Result<bool>136     fn is_regular_file(&self) -> Result<bool> {
137         Ok(false)
138     }
139 
is_directory(&self) -> Result<bool>140     fn is_directory(&self) -> Result<bool> {
141         Ok(true)
142     }
143 }
144