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