Skip to content

Commit 78a32a0

Browse files
committed
fs: add high-level file system abstraction
1 parent 4db6d17 commit 78a32a0

File tree

14 files changed

+909
-18
lines changed

14 files changed

+909
-18
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
## uefi - [Unreleased]
44

5+
### Added
6+
7+
- There is a new `fs` module that provides a high-level API for file-system
8+
access. The API is close to the `std::fs` module.
9+
510
### Changed
611

712
- The `global_allocator` module has been renamed to `allocator`, and is now
813
available regardless of whether the `global_allocator` feature is enabled. The
914
`global_allocator` feature now only controls whether `allocator::Allocator` is
1015
set as Rust's global allocator.
16+
- `Image::get_image_file_system` now returns a `fs::FileSystem` instead of the
17+
protocol.
1118

1219
## uefi-macros - [Unreleased]
1320

Cargo.lock

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//! Tests functionality from the `uefi::fs` module. See function [`test`].
2+
3+
use alloc::string::{String, ToString};
4+
use alloc::vec::Vec;
5+
use uefi::fs::{FileSystem, FileSystemError};
6+
use uefi::proto::media::fs::SimpleFileSystem;
7+
use uefi::table::boot::ScopedProtocol;
8+
9+
/// Tests functionality from the `uefi::fs` module. This test relies on a
10+
/// working File System Protocol, which is tested at a dedicated place.
11+
pub fn test(sfs: ScopedProtocol<SimpleFileSystem>) -> Result<(), FileSystemError> {
12+
let mut fs = FileSystem::new(sfs);
13+
14+
fs.create_dir("test_file_system_abs")?;
15+
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")?;
21+
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+
33+
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+
})
40+
.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")?;
50+
// 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);
55+
56+
Ok(())
57+
}

uefi-test-runner/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use uefi::Result;
1313
use uefi_services::{print, println};
1414

1515
mod boot;
16+
mod fs;
1617
mod proto;
1718
mod runtime;
1819

uefi-test-runner/src/proto/media.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,12 @@ pub fn test(bt: &BootServices) {
433433
test_partition_info(bt, handle);
434434
}
435435

436-
// Close the `SimpleFileSystem` protocol so that the raw disk tests work.
437-
drop(sfs);
436+
// Invoke the fs test after the basic low-level file system protocol
437+
// tests succeeded.
438+
439+
// This will also drop the `SimpleFileSystem` protocol so that the raw disk
440+
// tests work.
441+
crate::fs::test(sfs).unwrap();
438442

439443
test_raw_disk_io(handle, bt);
440444
test_raw_disk_io2(handle, bt);

uefi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ unstable = []
2525

2626
[dependencies]
2727
bitflags = "1.3.1"
28+
derive_more = { version = "0.99.17", features = ["display"] }
2829
log = { version = "0.4.5", default-features = false }
2930
ptr_meta = { version = "0.2.0", default-features = false }
3031
ucs2 = "0.3.2"

uefi/src/fs/dir_entry_iter.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//! Module for directory iteration. See [`UefiDirectoryIter`].
2+
3+
use super::*;
4+
use alloc::boxed::Box;
5+
use uefi::Result;
6+
7+
/// Iterates over the entries of an UEFI directory. It returns boxed values of
8+
/// type [`UefiFileInfo`].
9+
#[derive(Debug)]
10+
pub struct UefiDirectoryIter(UefiDirectoryHandle);
11+
12+
impl UefiDirectoryIter {
13+
/// Constructor.
14+
pub fn new(handle: UefiDirectoryHandle) -> Self {
15+
Self(handle)
16+
}
17+
}
18+
19+
impl Iterator for UefiDirectoryIter {
20+
type Item = Result<Box<UefiFileInfo>, ()>;
21+
22+
fn next(&mut self) -> Option<Self::Item> {
23+
let e = self.0.read_entry_boxed();
24+
match e {
25+
// no more entries
26+
Ok(None) => None,
27+
Ok(Some(e)) => Some(Ok(e)),
28+
Err(e) => Some(Err(e)),
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)