11//! Module for [`FileSystem`].
22
33use super :: * ;
4+ use crate :: fs:: path:: { validate_path, PathError } ;
45use crate :: proto:: media:: file:: { FileAttribute , FileInfo , FileType } ;
56use crate :: table:: boot:: ScopedProtocol ;
67use alloc:: boxed:: Box ;
78use alloc:: string:: { FromUtf8Error , String , ToString } ;
9+ use alloc:: vec;
810use alloc:: vec:: Vec ;
9- use alloc:: { format, vec} ;
1011use core:: fmt;
1112use core:: fmt:: { Debug , Formatter } ;
1213use core:: ops:: Deref ;
1314use derive_more:: Display ;
14- use log:: info ;
15+ use log:: debug ;
1516
1617/// All errors that can happen when working with the [`FileSystem`].
1718#[ derive( Debug , Clone , Display , PartialEq , Eq ) ]
1819pub enum FileSystemError {
1920 /// Can't open the root directory of the underlying volume.
2021 CantOpenVolume ,
2122 /// The path is invalid because of the underlying [`PathError`].
23+ ///
24+ /// [`PathError`]: path::PathError
2225 IllegalPath ( PathError ) ,
2326 /// The file or directory was not found in the underlying volume.
2427 FileNotFound ( String ) ,
@@ -40,12 +43,28 @@ pub enum FileSystemError {
4043 ReadFailure ,
4144 /// Can't parse file content as UTF-8.
4245 Utf8Error ( FromUtf8Error ) ,
43- /// Could not open the given path.
44- OpenError ( String ) ,
46+ /// Could not open the given path. Carries the path that could not be opened
47+ /// and the underlying UEFI error.
48+ #[ display( fmt = "{path:?}" ) ]
49+ OpenError {
50+ /// Path that caused the failure.
51+ path : String ,
52+ /// More detailed failure description.
53+ error : crate :: Error ,
54+ } ,
4555}
4656
4757#[ cfg( feature = "unstable" ) ]
48- impl core:: error:: Error for FileSystemError { }
58+ impl core:: error:: Error for FileSystemError {
59+ fn source ( & self ) -> Option < & ( dyn core:: error:: Error + ' static ) > {
60+ match self {
61+ FileSystemError :: IllegalPath ( e) => Some ( e) ,
62+ FileSystemError :: Utf8Error ( e) => Some ( e) ,
63+ FileSystemError :: OpenError { path : _path, error } => Some ( error) ,
64+ _ => None ,
65+ }
66+ }
67+ }
4968
5069impl From < PathError > for FileSystemError {
5170 fn from ( err : PathError ) -> Self {
@@ -90,44 +109,45 @@ impl<'a> FileSystem<'a> {
90109 let path = path. as_ref ( ) ;
91110 self . open ( path, UefiFileMode :: CreateReadWrite , true )
92111 . map ( |_| ( ) )
93- . map_err ( |err| {
94- log:: debug!( "failed to fetch file info: {err:#?}" ) ;
95- FileSystemError :: OpenError ( path. to_string ( ) )
96- } )
97112 }
98113
99114 /// Recursively create a directory and all of its parent components if they
100115 /// are missing.
101116 pub fn create_dir_all ( & mut self , path : impl AsRef < Path > ) -> FileSystemResult < ( ) > {
102117 let path = path. as_ref ( ) ;
103118
104- let normalized_path = NormalizedPath :: new ( "\\ " , path) ?;
105- let normalized_path_string = normalized_path. to_string ( ) ;
106- let normalized_path_pathref = Path :: new ( & normalized_path_string) ;
119+ // Collect all relevant sub paths in a vector.
120+ let mut dirs_to_create = vec ! [ path. to_path_buf( ) ] ;
121+ while let Some ( parent) = dirs_to_create. last ( ) . unwrap ( ) . parent ( ) {
122+ debug ! ( "parent={parent}" ) ;
123+ dirs_to_create. push ( parent)
124+ }
125+ // Now reverse, so that we have something like this:
126+ // - a
127+ // - a\\b
128+ // - a\\b\\c
129+ dirs_to_create. reverse ( ) ;
130+
131+ for parent in dirs_to_create {
132+ if self . try_exists ( & parent) . is_err ( ) {
133+ self . create_dir ( parent) ?;
134+ }
135+ }
107136
108- let iter = || normalized_path_pathref. components ( SEPARATOR ) ;
109- iter ( )
110- . scan ( String :: new ( ) , |path_acc, component| {
111- if component != Component :: RootDir {
112- * path_acc += SEPARATOR_STR ;
113- * path_acc += format ! ( "{component}" ) . as_str ( ) ;
114- }
115- info ! ( "path_acc: {path_acc}, component: {component}" ) ;
116- Some ( ( component, path_acc. clone ( ) ) )
117- } )
118- . try_for_each ( |( _component, full_path) | self . create_dir ( full_path. as_str ( ) ) )
137+ Ok ( ( ) )
119138 }
120139
121140 /// Given a path, query the file system to get information about a file,
122141 /// directory, etc. Returns [`UefiFileInfo`].
123142 pub fn metadata ( & mut self , path : impl AsRef < Path > ) -> FileSystemResult < Box < UefiFileInfo > > {
124143 let path = path. as_ref ( ) ;
125- let file = self . open ( path, UefiFileMode :: Read , false ) ?;
126- log:: debug!( "{:#?}" , & file. into_type( ) . unwrap( ) ) ;
127144 let mut file = self . open ( path, UefiFileMode :: Read , false ) ?;
128145 file. get_boxed_info ( ) . map_err ( |err| {
129- log:: debug!( "failed to fetch file info: {err:#?}" ) ;
130- FileSystemError :: OpenError ( path. to_string ( ) )
146+ log:: trace!( "failed to fetch file info: {err:#?}" ) ;
147+ FileSystemError :: OpenError {
148+ path : path. to_cstr16 ( ) . to_string ( ) ,
149+ error : err,
150+ }
131151 } )
132152 }
133153
@@ -138,11 +158,13 @@ impl<'a> FileSystem<'a> {
138158 let mut file = self
139159 . open ( path, UefiFileMode :: Read , false ) ?
140160 . into_regular_file ( )
141- . ok_or ( FileSystemError :: NotAFile ( path. as_str ( ) . to_string ( ) ) ) ?;
142- let info = file. get_boxed_info :: < FileInfo > ( ) . map_err ( |e| {
143- log:: error!( "get info failed: {e:?}" ) ;
144- FileSystemError :: OpenError ( path. as_str ( ) . to_string ( ) )
145- } ) ?;
161+ . ok_or ( FileSystemError :: NotAFile ( path. to_cstr16 ( ) . to_string ( ) ) ) ?;
162+ let info = file
163+ . get_boxed_info :: < FileInfo > ( )
164+ . map_err ( |err| FileSystemError :: OpenError {
165+ path : path. to_cstr16 ( ) . to_string ( ) ,
166+ error : err,
167+ } ) ?;
146168
147169 let mut vec = vec ! [ 0 ; info. file_size( ) as usize ] ;
148170 let read_bytes = file. read ( vec. as_mut_slice ( ) ) . map_err ( |e| {
@@ -164,7 +186,7 @@ impl<'a> FileSystem<'a> {
164186 let dir = self
165187 . open ( path, UefiFileMode :: Read , false ) ?
166188 . into_directory ( )
167- . ok_or ( FileSystemError :: NotADirectory ( path. as_str ( ) . to_string ( ) ) ) ?;
189+ . ok_or ( FileSystemError :: NotADirectory ( path. to_cstr16 ( ) . to_string ( ) ) ) ?;
168190 Ok ( UefiDirectoryIter :: new ( dir) )
169191 }
170192
@@ -185,16 +207,18 @@ impl<'a> FileSystem<'a> {
185207 match file {
186208 FileType :: Dir ( dir) => dir. delete ( ) . map_err ( |e| {
187209 log:: error!( "error removing dir: {e:?}" ) ;
188- FileSystemError :: CantDeleteDirectory ( path. as_str ( ) . to_string ( ) )
210+ FileSystemError :: CantDeleteDirectory ( path. to_cstr16 ( ) . to_string ( ) )
189211 } ) ,
190- FileType :: Regular ( _) => Err ( FileSystemError :: NotADirectory ( path. as_str ( ) . to_string ( ) ) ) ,
212+ FileType :: Regular ( _) => {
213+ Err ( FileSystemError :: NotADirectory ( path. to_cstr16 ( ) . to_string ( ) ) )
214+ }
191215 }
192216 }
193217
194218 /*/// Removes a directory at this path, after removing all its contents. Use
195219 /// carefully!
196- pub fn remove_dir_all(&mut self, _path : impl AsRef<Path>) -> FileSystemResult<()> {
197- todo!()
220+ pub fn remove_dir_all(&mut self, path : impl AsRef<Path>) -> FileSystemResult<()> {
221+ let path = path.as_ref();
198222 }*/
199223
200224 /// Removes a file from the filesystem.
@@ -209,9 +233,9 @@ impl<'a> FileSystem<'a> {
209233 match file {
210234 FileType :: Regular ( file) => file. delete ( ) . map_err ( |e| {
211235 log:: error!( "error removing file: {e:?}" ) ;
212- FileSystemError :: CantDeleteFile ( path. as_str ( ) . to_string ( ) )
236+ FileSystemError :: CantDeleteFile ( path. to_cstr16 ( ) . to_string ( ) )
213237 } ) ,
214- FileType :: Dir ( _) => Err ( FileSystemError :: NotAFile ( path. as_str ( ) . to_string ( ) ) ) ,
238+ FileType :: Dir ( _) => Err ( FileSystemError :: NotAFile ( path. to_cstr16 ( ) . to_string ( ) ) ) ,
215239 }
216240 }
217241
@@ -278,19 +302,24 @@ impl<'a> FileSystem<'a> {
278302 mode : UefiFileMode ,
279303 is_dir : bool ,
280304 ) -> FileSystemResult < UefiFileHandle > {
281- let path = NormalizedPath :: new ( " \\ " , path) ?;
282- log:: debug !( "normalized path: {path}" ) ;
305+ validate_path ( path) ?;
306+ log:: trace !( "open validated path: {path}" ) ;
283307
284308 let attr = if mode == UefiFileMode :: CreateReadWrite && is_dir {
285309 FileAttribute :: DIRECTORY
286310 } else {
287311 FileAttribute :: empty ( )
288312 } ;
289313
290- self . open_root ( ) ?. open ( & path, mode, attr) . map_err ( |x| {
291- log:: trace!( "Can't open file {path}: {x:?}" ) ;
292- FileSystemError :: OpenError ( path. to_string ( ) )
293- } )
314+ self . open_root ( ) ?
315+ . open ( path. to_cstr16 ( ) , mode, attr)
316+ . map_err ( |err| {
317+ log:: trace!( "Can't open file {path}: {err:?}" ) ;
318+ FileSystemError :: OpenError {
319+ path : path. to_cstr16 ( ) . to_string ( ) ,
320+ error : err,
321+ }
322+ } )
294323 }
295324}
296325
0 commit comments