1
1
//! Module for [`FileSystem`].
2
2
3
3
use super :: * ;
4
+ use crate :: fs:: path:: { validate_path, PathError } ;
4
5
use crate :: proto:: media:: file:: { FileAttribute , FileInfo , FileType } ;
5
6
use crate :: table:: boot:: ScopedProtocol ;
6
7
use alloc:: boxed:: Box ;
7
8
use alloc:: string:: { FromUtf8Error , String , ToString } ;
9
+ use alloc:: vec;
8
10
use alloc:: vec:: Vec ;
9
- use alloc:: { format, vec} ;
10
11
use core:: fmt;
11
12
use core:: fmt:: { Debug , Formatter } ;
12
13
use core:: ops:: Deref ;
13
14
use derive_more:: Display ;
14
- use log:: info ;
15
+ use log:: debug ;
15
16
16
17
/// All errors that can happen when working with the [`FileSystem`].
17
18
#[ derive( Debug , Clone , Display , PartialEq , Eq ) ]
18
19
pub enum FileSystemError {
19
20
/// Can't open the root directory of the underlying volume.
20
21
CantOpenVolume ,
21
22
/// The path is invalid because of the underlying [`PathError`].
23
+ ///
24
+ /// [`PathError`]: path::PathError
22
25
IllegalPath ( PathError ) ,
23
26
/// The file or directory was not found in the underlying volume.
24
27
FileNotFound ( String ) ,
@@ -40,12 +43,28 @@ pub enum FileSystemError {
40
43
ReadFailure ,
41
44
/// Can't parse file content as UTF-8.
42
45
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
+ } ,
45
55
}
46
56
47
57
#[ 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
+ }
49
68
50
69
impl From < PathError > for FileSystemError {
51
70
fn from ( err : PathError ) -> Self {
@@ -90,44 +109,45 @@ impl<'a> FileSystem<'a> {
90
109
let path = path. as_ref ( ) ;
91
110
self . open ( path, UefiFileMode :: CreateReadWrite , true )
92
111
. map ( |_| ( ) )
93
- . map_err ( |err| {
94
- log:: debug!( "failed to fetch file info: {err:#?}" ) ;
95
- FileSystemError :: OpenError ( path. to_string ( ) )
96
- } )
97
112
}
98
113
99
114
/// Recursively create a directory and all of its parent components if they
100
115
/// are missing.
101
116
pub fn create_dir_all ( & mut self , path : impl AsRef < Path > ) -> FileSystemResult < ( ) > {
102
117
let path = path. as_ref ( ) ;
103
118
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
+ }
107
136
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 ( ( ) )
119
138
}
120
139
121
140
/// Given a path, query the file system to get information about a file,
122
141
/// directory, etc. Returns [`UefiFileInfo`].
123
142
pub fn metadata ( & mut self , path : impl AsRef < Path > ) -> FileSystemResult < Box < UefiFileInfo > > {
124
143
let path = path. as_ref ( ) ;
125
- let file = self . open ( path, UefiFileMode :: Read , false ) ?;
126
- log:: debug!( "{:#?}" , & file. into_type( ) . unwrap( ) ) ;
127
144
let mut file = self . open ( path, UefiFileMode :: Read , false ) ?;
128
145
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
+ }
131
151
} )
132
152
}
133
153
@@ -138,11 +158,13 @@ impl<'a> FileSystem<'a> {
138
158
let mut file = self
139
159
. open ( path, UefiFileMode :: Read , false ) ?
140
160
. 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
+ } ) ?;
146
168
147
169
let mut vec = vec ! [ 0 ; info. file_size( ) as usize ] ;
148
170
let read_bytes = file. read ( vec. as_mut_slice ( ) ) . map_err ( |e| {
@@ -164,7 +186,7 @@ impl<'a> FileSystem<'a> {
164
186
let dir = self
165
187
. open ( path, UefiFileMode :: Read , false ) ?
166
188
. into_directory ( )
167
- . ok_or ( FileSystemError :: NotADirectory ( path. as_str ( ) . to_string ( ) ) ) ?;
189
+ . ok_or ( FileSystemError :: NotADirectory ( path. to_cstr16 ( ) . to_string ( ) ) ) ?;
168
190
Ok ( UefiDirectoryIter :: new ( dir) )
169
191
}
170
192
@@ -185,16 +207,18 @@ impl<'a> FileSystem<'a> {
185
207
match file {
186
208
FileType :: Dir ( dir) => dir. delete ( ) . map_err ( |e| {
187
209
log:: error!( "error removing dir: {e:?}" ) ;
188
- FileSystemError :: CantDeleteDirectory ( path. as_str ( ) . to_string ( ) )
210
+ FileSystemError :: CantDeleteDirectory ( path. to_cstr16 ( ) . to_string ( ) )
189
211
} ) ,
190
- FileType :: Regular ( _) => Err ( FileSystemError :: NotADirectory ( path. as_str ( ) . to_string ( ) ) ) ,
212
+ FileType :: Regular ( _) => {
213
+ Err ( FileSystemError :: NotADirectory ( path. to_cstr16 ( ) . to_string ( ) ) )
214
+ }
191
215
}
192
216
}
193
217
194
218
/*/// Removes a directory at this path, after removing all its contents. Use
195
219
/// 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();
198
222
}*/
199
223
200
224
/// Removes a file from the filesystem.
@@ -209,9 +233,9 @@ impl<'a> FileSystem<'a> {
209
233
match file {
210
234
FileType :: Regular ( file) => file. delete ( ) . map_err ( |e| {
211
235
log:: error!( "error removing file: {e:?}" ) ;
212
- FileSystemError :: CantDeleteFile ( path. as_str ( ) . to_string ( ) )
236
+ FileSystemError :: CantDeleteFile ( path. to_cstr16 ( ) . to_string ( ) )
213
237
} ) ,
214
- FileType :: Dir ( _) => Err ( FileSystemError :: NotAFile ( path. as_str ( ) . to_string ( ) ) ) ,
238
+ FileType :: Dir ( _) => Err ( FileSystemError :: NotAFile ( path. to_cstr16 ( ) . to_string ( ) ) ) ,
215
239
}
216
240
}
217
241
@@ -278,19 +302,24 @@ impl<'a> FileSystem<'a> {
278
302
mode : UefiFileMode ,
279
303
is_dir : bool ,
280
304
) -> 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}" ) ;
283
307
284
308
let attr = if mode == UefiFileMode :: CreateReadWrite && is_dir {
285
309
FileAttribute :: DIRECTORY
286
310
} else {
287
311
FileAttribute :: empty ( )
288
312
} ;
289
313
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
+ } )
294
323
}
295
324
}
296
325
0 commit comments