-
-
Notifications
You must be signed in to change notification settings - Fork 170
Introducing a high-level FS abstraction #472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I haven't had a chance to look at the details of this yet, but I'm definitely on board with the idea of a high-level FS API! The motivation you described sounds very reasonable to me. I'll give some more detailed feedback once I get some time to peruse :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for contributing this code! I've left some comments, based on an initial review of the PR.
src/fs/file_system.rs
Outdated
use uefi::table::boot::ScopedProtocol; | ||
use uefi::{CString16}; | ||
|
||
// #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for keeping this comment around?
src/fs/mod.rs
Outdated
//! - create files | ||
//! - iterate directories and walk the file system tree | ||
//! | ||
//! Unlike UNIX-based file systems, there is no virtual file system. Thus, users perform actions on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of just saying that this is not like UNIX, we could also affirmatively indicate that it's a look like Windows, where you also have distinct "drive letters" for each volume :)
src/fs/mod.rs
Outdated
//! the heap (such as file info). There is no synchronization as it is untypically that users | ||
//! bootstrap Application Processors (AP) during the UEFI stage, i.e., before hand-off to a kernel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though it's unlikely that an UEFI app will start the other processors, during the boot stage, sync is still a problem because of interrupts. You could have code which modifies an internal data structure, gets interrupted by an event, and then that event handler tries to access the same data structure (which is currently in an invalid, in progress state!).
Fortunately, event handlers aren't exactly the usual place to do FS operations...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. I don't know how interrupts in UEFI are handled and where they are used. If an interrupt handler wants to access the FileSystem
abstraction, while the lock is contended, then.. what to do? Force an unlock the File System? @GabrielMajeri
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, it is the users'/developers's responsibility to find a locking policy. A user might wrap the FileSystem
implementation in a SpinLock or not.
The documentation says that there is no out-of-the-box synchronization.
src/fs/file_system.rs
Outdated
/// Removes an empty directory. | ||
pub fn remove_dir(&mut self, path: &str) -> FileSystemResult<()> { | ||
let _path = Path::new(path)?; | ||
todo!() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've noticed these todo!
calls. Are you planning to implement them before finalizing the PR, or leave them for future implementation post-merge? (if it's the first case, then I'd advise marking the PR as a draft so we don't accidentally merge it before it's ready 😅)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They will be implemented, for sure! I just wanted to clarify if we want this thing at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having recently done something with the "raw" filesystem API I can confirm that a simpler fs interface would be nice :)
3050d50
to
c5ef515
Compare
386aade
to
16e3360
Compare
@nicholasbishop @GabrielMajeri I finally had time working on this. It is still not ready to merge but much closer now. I add it as a new workspace member API examples from the new integration test: let mut fs = FileSystem::new(sfs);
fs.create_dir("test_file_system_abs").unwrap();
// slash is transparently transformed to backslash
fs.write("test_file_system_abs/foo", "hello").unwrap();
// absolute or relative paths are supported; ./ is ignored
fs.copy("\\test_file_system_abs/foo", "\\test_file_system_abs/./bar").unwrap();
let read = fs.read("\\test_file_system_abs\\bar").unwrap();
let read = String::from_utf8(read).unwrap();
fs.rename("test_file_system_abs\\bar", "test_file_system_abs\\barfoo").unwrap();
let entries = fs.read_dir("test_file_system_abs").unwrap()
.map(|e| e.unwrap().file_name().to_string())
.collect::<Vec<_>>();
assert_eq!(&[".", "..", "foo", "barfoo"], entries.as_slice()); |
Looks nice! A few thoughts and questions:
|
uefi-fs/src/lib.rs
Outdated
mod path; | ||
mod uefi_types; | ||
|
||
pub use file_system::{FileSystem, FileSystemError, FileSystemResult}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed from looking at the cargo doc
output, probably want a pub use
for Path
here.
16e3360
to
62ee08a
Compare
So far, the API a user sees is just a Internally, the separator is
I thought about it, and it doesn't massively increase compilation times nor requires it a feature flag. It will be just part of the normal API.
Ah, I didn't know that, thanks. PS: This feature heavily uses (small and medium) allocations. In the long term, we might get some feedback in case our allocator does something wrong or so. |
Ping @nicholasbishop ? :) |
uefi/src/fs/file_system.rs
Outdated
pub struct FileSystem<'boot_services> { | ||
/// Underlying UEFI protocol. | ||
proto: ScopedProtocol<'boot_services, SimpleFileSystemProtocol>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since FileSystem
now just contains the open SimpleFileSystemProtocol
, would it makes sense to drop the new type and instead add the FileSystem
methods directly to SimpleFileSystemProtocol
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to keep thinks separate. Low-level API and high-level API should not exist on the same type, especially, as mixing them together might break stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you say more about what might break when using both? If there are things the end user must avoid doing then we should add that info to the docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought again about it and there are probably no safety issues, just convenience issues and a weird mixture of low-level and high-level APIs. However, I mixed up the SimpleFileSystem protocol with the FileProtocol. As the SimpleFileSystem protocol is so simple, it only has open_volume
, integrating the new FileSystem abstraction into the SimpleFileSystem protocol abstraction is a possible design choice. However, the following properties do not feel right:
- mix of low-level API with high-level API
- I need additional helpers for the Filesystem (
Path
,FileSystemError
, ...) and it feels wrong to integrate them intouefi::proto::media
. They are types of a different level.
Instead, I'd allow constructing a FileSystem
from a SimpleFileSystem
(via .into() or .to_file_ystem()
) for a high developer convenience.
What do you say?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, I especially agree that it would feel a little weird to have things like Path
mixed into uefi::proto::media
. It's a little unfortunate that the names are kinda reversed; SimpleFileSystem
is really more complex to use than FileSystem
. But that's probably fine, I don't have any better suggestion :) Anyway, +1 to the separate module.
b34efe5
to
6b1bc4c
Compare
uefi/src/fs/file_system.rs
Outdated
/// High-level file-system abstraction for UEFI volumes with an API that is | ||
/// close to `std::fs`. It acts as convenient accessor around the | ||
/// [`SimpleFileSystemProtocol`]. | ||
pub struct FileSystem<'boot_services> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this lifetime is technically wrong. It only lifes as long "as it lives". Should be the same the underlying protocol, hence, 'a
.
xtask/src/qemu.rs
Outdated
eprintln!("HELLO"); | ||
eprintln!("HELLO"); | ||
eprintln!("HELLO"); | ||
fs::copy(&test_disk, "/home/pschuster/Desktop/qemu.img").unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo, I used this for testing. remove
6b1bc4c
to
4bc5821
Compare
@nicholasbishop I think this is ready for review and merging, when we keep the following things in mind:
Open Questions/Blockers:
|
4bc5821
to
1f47d4d
Compare
uefi/src/table/boot.rs
Outdated
@@ -17,6 +17,7 @@ use core::mem::{self, MaybeUninit}; | |||
use core::ops::{Deref, DerefMut}; | |||
use core::ptr::NonNull; | |||
use core::{ptr, slice}; | |||
use uefi::fs::FileSystem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[cfg(feature = "alloc")]
uefi/src/fs/file_system.rs
Outdated
/// carefully! | ||
pub fn remove_dir_all(&mut self, _path: impl AsRef<Path>) -> FileSystemResult<()> { | ||
todo!() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the interest of getting it merged, maybe just drop this method for now?
I'm in favor of merging this once the tests are passing and the one |
I've created a tracking issue: #747
I want to refactor the all my existing abstractions, namely |
ce533e7
to
81299de
Compare
pub fn create_dir_all(&mut self, path: impl AsRef<Path>) -> FileSystemResult<()> { | ||
let path = path.as_ref(); | ||
|
||
let normalized_path = NormalizedPath::new("\\", path)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is ugly but will be replaced once there is a new and better Path/PAthBuf abstraction
@@ -0,0 +1,239 @@ | |||
//! Module for path normalization. See [`NormalizedPath`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole module will be refactored in a follow-up PR.
@@ -0,0 +1,154 @@ | |||
//! Module for handling file-system paths in [`super::FileSystem`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole module will be refactored in a follow-up PR.
8a54c28
to
78a32a0
Compare
78a32a0
to
509dc71
Compare
This reverts a small change from: rust-osdev#472 If using the library without the `alloc` feature enabled, `FileSystem` isn't available, but you might still want access to the image's file system via the underlying protocol. The high-level API is still easily accessible via `FileSystem::new`.
This reverts a small change from: rust-osdev#472 If using the library without the `alloc` feature enabled, `FileSystem` isn't available, but you might still want access to the image's file system via the underlying protocol. The high-level API is still easily accessible via `FileSystem::new`.
This reverts a small change from: #472 If using the library without the `alloc` feature enabled, `FileSystem` isn't available, but you might still want access to the image's file system via the underlying protocol. The high-level API is still easily accessible via `FileSystem::new`.
This reverts a small change from: rust-osdev#472 If using the library without the `alloc` feature enabled, `FileSystem` isn't available, but you might still want access to the image's file system via the underlying protocol. The high-level API is still easily accessible via `FileSystem::new`.
Hi! This originated in a personal learning project and I'd like to know what others think about it. Would be cool if we can bring this upstream. It's about creating a high level FS abstraction over the existing FS protocol. It aims to be as close to top-level functions provided by
std::fs
as possible.Motivation
Usually, UEFI-application hackers want to do something like this:
Code example / API demonstration
From the module description (initial version; a few changes in the meantime):
Open Questions
Steps to Undraft