Skip to content

Commit d5d2322

Browse files
committed
fs/path: finalize implementation + integration test
Now, only remove_dir_all is missing. The reason for this is not technical. Someone just needs to put a little more effort into it.
1 parent f6caee4 commit d5d2322

File tree

7 files changed

+230
-131
lines changed

7 files changed

+230
-131
lines changed

uefi-test-runner/src/fs/mod.rs

+41-37
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
use alloc::string::{String, ToString};
44
use alloc::vec::Vec;
5-
use uefi::fs::{FileSystem, FileSystemError};
5+
use uefi::cstr16;
6+
use uefi::fs::{FileSystem, FileSystemError, PathBuf};
67
use uefi::proto::media::fs::SimpleFileSystem;
78
use uefi::table::boot::ScopedProtocol;
89

@@ -11,47 +12,50 @@ use uefi::table::boot::ScopedProtocol;
1112
pub fn test(sfs: ScopedProtocol<SimpleFileSystem>) -> Result<(), FileSystemError> {
1213
let mut fs = FileSystem::new(sfs);
1314

14-
fs.create_dir("test_file_system_abs")?;
15+
// test create dir
16+
fs.create_dir(cstr16!("foo_dir"))?;
1517

16-
// slash is transparently transformed to backslash
17-
fs.write("test_file_system_abs/foo", "hello")?;
18-
// absolute or relative paths are supported; ./ is ignored
19-
fs.copy("\\test_file_system_abs/foo", "\\test_file_system_abs/./bar")?;
20-
let read = fs.read("\\test_file_system_abs\\bar")?;
18+
// test write, copy, and read
19+
let data_to_write = "hello world";
20+
fs.write(cstr16!("foo_dir\\foo"), data_to_write)?;
21+
// Here, we additionally check that absolute paths work.
22+
fs.copy(cstr16!("\\foo_dir\\foo"), cstr16!("\\foo_dir\\foo_cpy"))?;
23+
let read = fs.read(cstr16!("foo_dir\\foo_cpy"))?;
2124
let read = String::from_utf8(read).expect("Should be valid utf8");
22-
assert_eq!(read, "hello");
23-
24-
assert_eq!(
25-
fs.try_exists("test_file_system_abs\\barfoo"),
26-
Err(FileSystemError::OpenError(
27-
"\\test_file_system_abs\\barfoo".to_string()
28-
))
29-
);
30-
fs.rename("test_file_system_abs\\bar", "test_file_system_abs\\barfoo")?;
31-
assert!(fs.try_exists("test_file_system_abs\\barfoo").is_ok());
32-
25+
assert_eq!(read.as_str(), data_to_write);
26+
27+
// test copy from non-existent file
28+
let err = fs.copy(cstr16!("not_found"), cstr16!("abc"));
29+
assert!(matches!(err, Err(FileSystemError::OpenError { .. })));
30+
31+
// test rename file + path buf replaces / with \
32+
fs.rename(
33+
PathBuf::from(cstr16!("/foo_dir/foo_cpy")),
34+
cstr16!("foo_dir\\foo_cpy2"),
35+
)?;
36+
// file should not be available after rename
37+
let err = fs.read(cstr16!("foo_dir\\foo_cpy"));
38+
assert!(matches!(err, Err(FileSystemError::OpenError { .. })));
39+
40+
// test read dir on a sub dir
3341
let entries = fs
34-
.read_dir("test_file_system_abs")?
35-
.map(|e| {
36-
e.expect("Should return boxed file info")
37-
.file_name()
38-
.to_string()
39-
})
42+
.read_dir(cstr16!("foo_dir"))?
43+
.map(|entry| entry.expect("Should be valid").file_name().to_string())
4044
.collect::<Vec<_>>();
41-
assert_eq!(&[".", "..", "foo", "barfoo"], entries.as_slice());
42-
43-
fs.create_dir("/deeply_nested_test")?;
44-
fs.create_dir("/deeply_nested_test/1")?;
45-
fs.create_dir("/deeply_nested_test/1/2")?;
46-
fs.create_dir("/deeply_nested_test/1/2/3")?;
47-
fs.create_dir("/deeply_nested_test/1/2/3/4")?;
48-
fs.create_dir_all("/deeply_nested_test/1/2/3/4/5/6/7")?;
49-
fs.try_exists("/deeply_nested_test/1/2/3/4/5/6/7")?;
45+
assert_eq!(&[".", "..", "foo", "foo_cpy2"], entries.as_slice());
46+
47+
// test create dir recursively
48+
fs.create_dir_all(cstr16!("foo_dir\\1\\2\\3\\4\\5\\6\\7"))?;
49+
fs.create_dir_all(cstr16!("foo_dir\\1\\2\\3\\4\\5\\6\\7\\8"))?;
50+
fs.write(
51+
cstr16!("foo_dir\\1\\2\\3\\4\\5\\6\\7\\8\\foobar"),
52+
data_to_write,
53+
)?;
54+
let boxinfo = fs.metadata(cstr16!("foo_dir\\1\\2\\3\\4\\5\\6\\7\\8\\foobar"))?;
55+
assert_eq!(boxinfo.file_size(), data_to_write.len() as u64);
56+
57+
// test remove dir all
5058
// TODO
51-
// fs.remove_dir_all("/deeply_nested_test/1/2/3/4/5/6/7")?;
52-
fs.remove_dir("/deeply_nested_test/1/2/3/4/5/6/7")?;
53-
let exists = matches!(fs.try_exists("/deeply_nested_test/1/2/3/4/5/6/7"), Ok(_));
54-
assert!(!exists);
5559

5660
Ok(())
5761
}

uefi/src/fs/file_system.rs

+41-35
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
//! Module for [`FileSystem`].
22
33
use super::*;
4-
use crate::fs;
54
use crate::fs::path::validation::{validate_path, PathError};
65
use crate::proto::media::file::{FileAttribute, FileInfo, FileType};
76
use crate::table::boot::ScopedProtocol;
87
use alloc::boxed::Box;
98
use alloc::string::{FromUtf8Error, String, ToString};
9+
use alloc::vec;
1010
use alloc::vec::Vec;
11-
use alloc::{format, vec};
1211
use core::fmt;
1312
use core::fmt::{Debug, Formatter};
1413
use core::ops::Deref;
1514
use derive_more::Display;
16-
use log::info;
17-
use uefi::Char16;
15+
use log::debug;
1816

1917
/// All errors that can happen when working with the [`FileSystem`].
2018
#[derive(Debug, Clone, Display, PartialEq, Eq)]
2119
pub enum FileSystemError {
2220
/// Can't open the root directory of the underlying volume.
2321
CantOpenVolume,
2422
/// The path is invalid because of the underlying [`PathError`].
23+
///
24+
/// [`PathError`]: path::validation::PathError
2525
IllegalPath(PathError),
2626
/// The file or directory was not found in the underlying volume.
2727
FileNotFound(String),
@@ -47,8 +47,10 @@ pub enum FileSystemError {
4747
/// and the underlying UEFI error.
4848
#[display(fmt = "{path:?}")]
4949
OpenError {
50+
/// Path that caused the failure.
5051
path: String,
51-
error: crate::Error<>
52+
/// More detailed failure description.
53+
error: crate::Error,
5254
},
5355
}
5456

@@ -112,37 +114,39 @@ impl<'a> FileSystem<'a> {
112114
/// Recursively create a directory and all of its parent components if they
113115
/// are missing.
114116
pub fn create_dir_all(&mut self, path: impl AsRef<Path>) -> FileSystemResult<()> {
115-
todo!()
116-
/*let path = path.as_ref();
117-
118-
let normalized_path = NormalizedPath::new("\\", path)?;
119-
let normalized_path_string = normalized_path.to_string();
120-
let normalized_path_pathref = Path::new(&normalized_path_string);
121-
122-
let iter = || normalized_path_pathref.components(SEPARATOR);
123-
iter()
124-
.scan(String::new(), |path_acc, component| {
125-
if component != Component::RootDir {
126-
*path_acc += SEPARATOR_STR;
127-
*path_acc += format!("{component}").as_str();
128-
}
129-
info!("path_acc: {path_acc}, component: {component}");
130-
Some((component, path_acc.clone()))
131-
})
132-
.try_for_each(|(_component, full_path)| self.create_dir(full_path.to_cstr16()))*/
117+
let path = path.as_ref();
118+
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+
}
136+
137+
Ok(())
133138
}
134139

135140
/// Given a path, query the file system to get information about a file,
136141
/// directory, etc. Returns [`UefiFileInfo`].
137142
pub fn metadata(&mut self, path: impl AsRef<Path>) -> FileSystemResult<Box<UefiFileInfo>> {
138143
let path = path.as_ref();
139-
let file = self.open(path, UefiFileMode::Read, false)?;
140144
let mut file = self.open(path, UefiFileMode::Read, false)?;
141145
file.get_boxed_info().map_err(|err| {
142-
log::debug!("failed to fetch file info: {err:#?}");
146+
log::trace!("failed to fetch file info: {err:#?}");
143147
FileSystemError::OpenError {
144148
path: path.to_cstr16().to_string(),
145-
error: err
149+
error: err,
146150
}
147151
})
148152
}
@@ -155,12 +159,12 @@ impl<'a> FileSystem<'a> {
155159
.open(path, UefiFileMode::Read, false)?
156160
.into_regular_file()
157161
.ok_or(FileSystemError::NotAFile(path.to_cstr16().to_string()))?;
158-
let info = file.get_boxed_info::<FileInfo>().map_err(|err| {
159-
FileSystemError::OpenError {
162+
let info = file
163+
.get_boxed_info::<FileInfo>()
164+
.map_err(|err| FileSystemError::OpenError {
160165
path: path.to_cstr16().to_string(),
161-
error: err
162-
}
163-
})?;
166+
error: err,
167+
})?;
164168

165169
let mut vec = vec![0; info.file_size() as usize];
166170
let read_bytes = file.read(vec.as_mut_slice()).map_err(|e| {
@@ -205,14 +209,16 @@ impl<'a> FileSystem<'a> {
205209
log::error!("error removing dir: {e:?}");
206210
FileSystemError::CantDeleteDirectory(path.to_cstr16().to_string())
207211
}),
208-
FileType::Regular(_) => Err(FileSystemError::NotADirectory(path.to_cstr16().to_string())),
212+
FileType::Regular(_) => {
213+
Err(FileSystemError::NotADirectory(path.to_cstr16().to_string()))
214+
}
209215
}
210216
}
211217

212218
/*/// Removes a directory at this path, after removing all its contents. Use
213219
/// carefully!
214-
pub fn remove_dir_all(&mut self, _path: impl AsRef<Path>) -> FileSystemResult<()> {
215-
todo!()
220+
pub fn remove_dir_all(&mut self, path: impl AsRef<Path>) -> FileSystemResult<()> {
221+
let path = path.as_ref();
216222
}*/
217223

218224
/// Removes a file from the filesystem.
@@ -311,7 +317,7 @@ impl<'a> FileSystem<'a> {
311317
log::trace!("Can't open file {path}: {err:?}");
312318
FileSystemError::OpenError {
313319
path: path.to_cstr16().to_string(),
314-
error: err
320+
error: err,
315321
}
316322
})
317323
}

uefi/src/fs/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
3232
mod dir_entry_iter;
3333
mod file_system;
34-
mod uefi_types;
3534
mod path;
35+
mod uefi_types;
3636

3737
pub use file_system::{FileSystem, FileSystemError, FileSystemResult};
3838
pub use path::*;

uefi/src/fs/path/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,22 @@
1515
1616
mod path;
1717
mod pathbuf;
18-
pub(super) mod validation;
18+
pub mod validation;
1919

2020
pub use path::Path;
2121
pub use pathbuf::PathBuf;
2222

23-
use uefi::{Char16, CStr16};
23+
use uefi::{CStr16, Char16};
2424

2525
/// The default separator for paths.
2626
pub const SEPARATOR: char = '\\';
27+
/// [`SEPARATOR`] but as useful UEFI type.
2728
pub const SEPARATOR_C16: Char16 = unsafe { Char16::from_u16_unchecked('\\' as u16) };
2829

2930
/// Stringified version of [`SEPARATOR`].
3031
pub const SEPARATOR_STR: &str = "\\";
32+
/// [`SEPARATOR_STR`] but as useful UEFI type.
3133
pub const SEPARATOR_CSTR16: &CStr16 = uefi_macros::cstr16!("\\");
3234

3335
/// Denied characters in a path component.
34-
pub const CHARACTER_DENY_LIST: [char; 10] =
35-
['\0', '"', '*', '/', ':', '<', '>', '?', '\\', '|'];
36+
pub const CHARACTER_DENY_LIST: [char; 10] = ['\0', '"', '*', '/', ':', '<', '>', '?', '\\', '|'];

0 commit comments

Comments
 (0)