diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b711d0c..a71b09091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ### Added - Added `AbsolutePointerProtocol`. +- Added `SimpleFileSystemProtocol` and related types. ### Changed - `{install,reinstall,uninstall}_protocol_interface` now take `const` interface pointers. diff --git a/uefi-raw/src/protocol/file_system.rs b/uefi-raw/src/protocol/file_system.rs new file mode 100644 index 000000000..a8b1367fb --- /dev/null +++ b/uefi-raw/src/protocol/file_system.rs @@ -0,0 +1,184 @@ +use crate::time::Time; +use crate::{guid, Char16, Event, Guid, Status}; +use bitflags::bitflags; +use core::ffi::c_void; + +#[derive(Debug)] +#[repr(C)] +pub struct SimpleFileSystemProtocol { + pub revision: u64, + pub open_volume: + unsafe extern "efiapi" fn(this: *mut Self, root: *mut *mut FileProtocolV1) -> Status, +} + +impl SimpleFileSystemProtocol { + pub const GUID: Guid = guid!("964e5b22-6459-11d2-8e39-00a0c969723b"); +} + +newtype_enum! { + pub enum FileProtocolRevision: u64 => { + REVISION_1 = 0x00010000, + REVISION_2 = 0x00020000, + } +} + +#[derive(Debug)] +#[repr(C)] +pub struct FileProtocolV1 { + pub revision: FileProtocolRevision, + pub open: unsafe extern "efiapi" fn( + this: *mut Self, + new_handle: *mut *mut Self, + file_name: *const Char16, + open_mode: FileMode, + attributes: FileAttribute, + ) -> Status, + pub close: unsafe extern "efiapi" fn(this: *mut Self) -> Status, + pub delete: unsafe extern "efiapi" fn(this: *mut Self) -> Status, + pub read: unsafe extern "efiapi" fn( + this: *mut Self, + buffer_size: *mut usize, + buffer: *mut c_void, + ) -> Status, + pub write: unsafe extern "efiapi" fn( + this: *mut Self, + buffer_size: *mut usize, + buffer: *const c_void, + ) -> Status, + pub get_position: unsafe extern "efiapi" fn(this: *const Self, position: *mut u64) -> Status, + pub set_position: unsafe extern "efiapi" fn(this: *mut Self, position: u64) -> Status, + pub get_info: unsafe extern "efiapi" fn( + this: *mut Self, + information_type: *const Guid, + buffer_size: *mut usize, + buffer: *mut c_void, + ) -> Status, + pub set_info: unsafe extern "efiapi" fn( + this: *mut Self, + information_type: *const Guid, + buffer_size: usize, + buffer: *const c_void, + ) -> Status, + pub flush: unsafe extern "efiapi" fn(this: *mut Self) -> Status, +} + +#[derive(Debug)] +#[repr(C)] +pub struct FileProtocolV2 { + pub v1: FileProtocolV1, + pub open_ex: unsafe extern "efiapi" fn( + this: *mut Self, + new_handle: *mut *mut Self, + file_name: *const Char16, + open_mode: FileMode, + attributes: FileAttribute, + token: *mut FileIoToken, + ) -> Status, + pub read_ex: unsafe extern "efiapi" fn(this: *mut Self, token: *mut FileIoToken) -> Status, + pub write_ex: unsafe extern "efiapi" fn(this: *mut Self, token: *mut FileIoToken) -> Status, + pub flush_ex: unsafe extern "efiapi" fn(this: *mut Self, token: *mut FileIoToken) -> Status, +} + +#[derive(Debug)] +#[repr(C)] +pub struct FileIoToken { + pub event: Event, + pub status: Status, + pub buffer_size: usize, + pub buffer: *mut c_void, +} + +bitflags! { + /// File attributes. + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] + #[repr(transparent)] + pub struct FileAttribute: u64 { + /// The file cannot be opened for modification. + const READ_ONLY = 0x0000000000000001; + + /// The file is hidden from normal directory views. + const HIDDEN = 0x0000000000000002; + + /// The file belongs to the system and must not be physically moved. + const SYSTEM = 0x0000000000000004; + + /// The file is a directory. + const DIRECTORY = 0x0000000000000010; + + /// The file is marked for archival by backup software. + const ARCHIVE = 0x0000000000000020; + + /// Mask combining all the valid attributes. + const VALID_ATTR = 0x0000000000000037; + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] + #[repr(transparent)] + pub struct FileMode: u64 { + const READ = 0x0000000000000001; + const WRITE = 0x0000000000000002; + const CREATE = 0x8000000000000000; + } +} + +#[derive(Debug)] +#[repr(C)] +pub struct FileInfo { + pub size: u64, + pub file_size: u64, + pub physical_size: u64, + pub create_time: Time, + pub last_access_time: Time, + pub modification_time: Time, + pub attribute: FileAttribute, + + /// The null-terminated name of the file. For a root directory, this is an + /// empty string. + /// + /// Note that this field is actually a variable-length array. In order to + /// avoid making this struct a DST, the field is represented as a + /// zero-length array here. + pub file_name: [Char16; 0], +} + +impl FileInfo { + pub const ID: Guid = guid!("09576e92-6d3f-11d2-8e39-00a0c969723b"); +} + +#[derive(Debug)] +#[repr(C)] +pub struct FileSystemInfo { + pub size: u64, + pub read_only: u8, + pub volume_size: u64, + pub free_space: u64, + pub block_size: u32, + + /// The null-terminated label of the volume. + /// + /// Note that this field is actually a variable-length array. In order to + /// avoid making this struct a DST, the field is represented as a + /// zero-length array here. + pub volume_label: [Char16; 0], +} + +impl FileSystemInfo { + pub const ID: Guid = guid!("09576e93-6d3f-11d2-8e39-00a0c969723b"); +} + +#[derive(Debug)] +#[repr(C)] +pub struct FileSystemVolumeLabel { + /// The null-terminated label of the volume. + /// + /// Note that this field is actually a variable-length array. In order to + /// avoid making this struct a DST, the field is represented as a + /// zero-length array here. + pub volume_label: [Char16; 0], +} + +impl FileSystemVolumeLabel { + pub const ID: Guid = guid!("db47d7d3-fe81-11d3-9a35-0090273fc14d"); +} diff --git a/uefi-raw/src/protocol/mod.rs b/uefi-raw/src/protocol/mod.rs index 396ef53ed..03459bd18 100644 --- a/uefi-raw/src/protocol/mod.rs +++ b/uefi-raw/src/protocol/mod.rs @@ -9,6 +9,7 @@ pub mod console; pub mod device_path; pub mod disk; pub mod driver; +pub mod file_system; pub mod loaded_image; pub mod memory_protection; pub mod rng; diff --git a/uefi/src/proto/media/file/info.rs b/uefi/src/proto/media/file/info.rs index 10cd7bb6e..847c02d95 100644 --- a/uefi/src/proto/media/file/info.rs +++ b/uefi/src/proto/media/file/info.rs @@ -1,7 +1,7 @@ use super::FileAttribute; use crate::data_types::Align; use crate::table::runtime::Time; -use crate::{guid, CStr16, Char16, Guid, Identify}; +use crate::{CStr16, Char16, Guid, Identify}; use core::ffi::c_void; use core::fmt::{self, Display, Formatter}; use core::{mem, ptr}; @@ -266,7 +266,7 @@ impl Align for FileInfo { } unsafe impl Identify for FileInfo { - const GUID: Guid = guid!("09576e92-6d3f-11d2-8e39-00a0c969723b"); + const GUID: Guid = uefi_raw::protocol::file_system::FileInfo::ID; } impl InfoInternal for FileInfo { @@ -361,7 +361,7 @@ impl Align for FileSystemInfo { } unsafe impl Identify for FileSystemInfo { - const GUID: Guid = guid!("09576e93-6d3f-11d2-8e39-00a0c969723b"); + const GUID: Guid = uefi_raw::protocol::file_system::FileSystemInfo::ID; } impl InfoInternal for FileSystemInfo { @@ -412,7 +412,7 @@ impl Align for FileSystemVolumeLabel { } unsafe impl Identify for FileSystemVolumeLabel { - const GUID: Guid = guid!("db47d7d3-fe81-11d3-9a35-0090273fc14d"); + const GUID: Guid = uefi_raw::protocol::file_system::FileSystemVolumeLabel::ID; } impl InfoInternal for FileSystemVolumeLabel { diff --git a/uefi/src/proto/media/file/mod.rs b/uefi/src/proto/media/file/mod.rs index c6acb792c..2628e0443 100644 --- a/uefi/src/proto/media/file/mod.rs +++ b/uefi/src/proto/media/file/mod.rs @@ -10,11 +10,11 @@ mod dir; mod info; mod regular; -use crate::{CStr16, Char16, Guid, Result, Status, StatusExt}; -use bitflags::bitflags; +use crate::{CStr16, Result, Status, StatusExt}; use core::ffi::c_void; use core::fmt::Debug; use core::{mem, ptr}; +use uefi_raw::protocol::file_system::FileProtocolV1; #[cfg(all(feature = "unstable", feature = "alloc"))] use {alloc::alloc::Global, core::alloc::Allocator}; #[cfg(feature = "alloc")] @@ -26,6 +26,7 @@ pub use self::info::{ FromUefi, }; pub use self::regular::RegularFile; +pub use uefi_raw::protocol::file_system::FileAttribute; /// Common interface to `FileHandle`, `RegularFile`, and `Directory`. /// @@ -74,8 +75,8 @@ pub trait File: Sized { (self.imp().open)( self.imp(), &mut ptr, - filename.as_ptr(), - open_mode, + filename.as_ptr().cast(), + uefi_raw::protocol::file_system::FileMode::from_bits_truncate(open_mode as u64), attributes, ) } @@ -93,7 +94,7 @@ pub trait File: Sized { /// /// * [`uefi::Status::WARN_DELETE_FAILURE`] fn delete(mut self) -> Result { - let result = (self.imp().delete)(self.imp()).to_result(); + let result = unsafe { (self.imp().delete)(self.imp()) }.to_result(); mem::forget(self); result } @@ -128,7 +129,7 @@ pub trait File: Sized { self.imp(), &Info::GUID, &mut buffer_size, - buffer.as_mut_ptr(), + buffer.as_mut_ptr().cast(), ) } .to_result_with( @@ -184,7 +185,7 @@ pub trait File: Sized { /// * [`uefi::Status::ACCESS_DENIED`] /// * [`uefi::Status::VOLUME_FULL`] fn flush(&mut self) -> Result { - (self.imp().flush)(self.imp()).to_result() + unsafe { (self.imp().flush)(self.imp()) }.to_result() } /// Read the dynamically allocated info for a file. @@ -224,7 +225,7 @@ pub trait File: Sized { // Internal File helper methods to access the function pointer table. trait FileInternal: File { - fn imp(&mut self) -> &mut FileImpl { + fn imp(&mut self) -> &mut FileProtocolV1 { unsafe { &mut *self.handle().0 } } } @@ -240,10 +241,10 @@ impl FileInternal for T {} /// Dropping this structure will result in the file handle being closed. #[repr(transparent)] #[derive(Debug)] -pub struct FileHandle(*mut FileImpl); +pub struct FileHandle(*mut FileProtocolV1); impl FileHandle { - pub(super) const unsafe fn new(ptr: *mut FileImpl) -> Self { + pub(super) const unsafe fn new(ptr: *mut FileProtocolV1) -> Self { Self(ptr) } @@ -296,7 +297,7 @@ impl File for FileHandle { // - get_position fails with EFI_UNSUPPORTED on directories // - result is an error if the underlying file was already closed or deleted. let mut pos = 0; - match (this.get_position)(this, &mut pos) { + match unsafe { (this.get_position)(this, &mut pos) } { Status::SUCCESS => Ok(true), Status::UNSUPPORTED => Ok(false), s => Err(s.into()), @@ -310,65 +311,12 @@ impl File for FileHandle { impl Drop for FileHandle { fn drop(&mut self) { - let result: Result = (self.imp().close)(self.imp()).to_result(); + let result: Result = unsafe { (self.imp().close)(self.imp()) }.to_result(); // The spec says this always succeeds. result.expect("Failed to close file"); } } -/// The function pointer table for the File protocol. -#[repr(C)] -pub(super) struct FileImpl { - revision: u64, - open: unsafe extern "efiapi" fn( - this: &mut FileImpl, - new_handle: &mut *mut FileImpl, - filename: *const Char16, - open_mode: FileMode, - attributes: FileAttribute, - ) -> Status, - close: extern "efiapi" fn(this: &mut FileImpl) -> Status, - delete: extern "efiapi" fn(this: &mut FileImpl) -> Status, - /// # Read from Regular Files - /// If `self` is not a directory, the function reads the requested number of bytes from the file - /// at the file’s current position and returns them in `buffer`. If the read goes beyond the end - /// of the file, the read length is truncated to the end of the file. The file’s current - /// position is increased by the number of bytes returned. - /// - /// # Read from Directory - /// If `self` is a directory, the function reads the directory entry at the file’s current - /// position and returns the entry in `buffer`. If the `buffer` is not large enough to hold the - /// current directory entry, then `EFI_BUFFER_TOO_SMALL` is returned and the current file - /// position is not updated. `buffer_size` is set to be the size of the buffer needed to read - /// the entry. On success, the current position is updated to the next directory entry. If there - /// are no more directory entries, the read returns a zero-length buffer. - read: unsafe extern "efiapi" fn( - this: &mut FileImpl, - buffer_size: &mut usize, - buffer: *mut u8, - ) -> Status, - write: unsafe extern "efiapi" fn( - this: &mut FileImpl, - buffer_size: &mut usize, - buffer: *const u8, - ) -> Status, - get_position: extern "efiapi" fn(this: &mut FileImpl, position: &mut u64) -> Status, - set_position: extern "efiapi" fn(this: &mut FileImpl, position: u64) -> Status, - get_info: unsafe extern "efiapi" fn( - this: &mut FileImpl, - information_type: &Guid, - buffer_size: &mut usize, - buffer: *mut u8, - ) -> Status, - set_info: unsafe extern "efiapi" fn( - this: &mut FileImpl, - information_type: &Guid, - buffer_size: usize, - buffer: *const c_void, - ) -> Status, - flush: extern "efiapi" fn(this: &mut FileImpl) -> Status, -} - /// Disambiguate the file type. Returned by `File::into_type()`. #[derive(Debug)] pub enum FileType { @@ -395,40 +343,21 @@ pub enum FileMode { CreateReadWrite = (1 << 63) | 2 | 1, } -bitflags! { - /// Attributes describing the properties of a file on the file system. - #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] - #[repr(transparent)] - pub struct FileAttribute: u64 { - /// File can only be opened in [`FileMode::READ`] mode. - const READ_ONLY = 1; - /// Hidden file, not normally visible to the user. - const HIDDEN = 1 << 1; - /// System file, indicates this file is an internal operating system file. - const SYSTEM = 1 << 2; - /// This file is a directory. - const DIRECTORY = 1 << 4; - /// This file is compressed. - const ARCHIVE = 1 << 5; - /// Mask combining all the valid attributes. - const VALID_ATTR = 0x37; - } -} - #[cfg(test)] mod tests { use super::*; use crate::table::runtime::Time; - use crate::{CString16, Identify}; + use crate::{CString16, Guid, Identify}; use ::alloc::vec; + use uefi_raw::protocol::file_system::FileProtocolRevision; // Test `get_boxed_info` by setting up a fake file, which is mostly // just function pointers. Most of the functions can be empty, only // get_info is actually implemented to return useful data. #[test] fn test_get_boxed_info() { - let mut file_impl = FileImpl { - revision: 0, + let mut file_impl = FileProtocolV1 { + revision: FileProtocolRevision::REVISION_1, open: stub_open, close: stub_close, delete: stub_delete, @@ -448,13 +377,13 @@ mod tests { assert_eq!(info.file_name(), CString16::try_from("test_file").unwrap()); } - extern "efiapi" fn stub_get_info( - _this: &mut FileImpl, - information_type: &Guid, - buffer_size: &mut usize, - buffer: *mut u8, + unsafe extern "efiapi" fn stub_get_info( + _this: *mut FileProtocolV1, + information_type: *const Guid, + buffer_size: *mut usize, + buffer: *mut c_void, ) -> Status { - assert_eq!(*information_type, FileInfo::GUID); + assert_eq!(unsafe { *information_type }, FileInfo::GUID); // Use a temporary buffer to get some file info, then copy that // data to the output buffer. @@ -487,57 +416,60 @@ mod tests { } extern "efiapi" fn stub_open( - _this: &mut FileImpl, - _new_handle: &mut *mut FileImpl, - _filename: *const Char16, - _open_mode: FileMode, + _this: *mut FileProtocolV1, + _new_handle: *mut *mut FileProtocolV1, + _filename: *const uefi_raw::Char16, + _open_mode: uefi_raw::protocol::file_system::FileMode, _attributes: FileAttribute, ) -> Status { Status::UNSUPPORTED } - extern "efiapi" fn stub_close(_this: &mut FileImpl) -> Status { + extern "efiapi" fn stub_close(_this: *mut FileProtocolV1) -> Status { Status::SUCCESS } - extern "efiapi" fn stub_delete(_this: &mut FileImpl) -> Status { + extern "efiapi" fn stub_delete(_this: *mut FileProtocolV1) -> Status { Status::UNSUPPORTED } extern "efiapi" fn stub_read( - _this: &mut FileImpl, - _buffer_size: &mut usize, - _buffer: *mut u8, + _this: *mut FileProtocolV1, + _buffer_size: *mut usize, + _buffer: *mut c_void, ) -> Status { Status::UNSUPPORTED } extern "efiapi" fn stub_write( - _this: &mut FileImpl, - _buffer_size: &mut usize, - _buffer: *const u8, + _this: *mut FileProtocolV1, + _buffer_size: *mut usize, + _buffer: *const c_void, ) -> Status { Status::UNSUPPORTED } - extern "efiapi" fn stub_get_position(_this: &mut FileImpl, _position: &mut u64) -> Status { + extern "efiapi" fn stub_get_position( + _this: *const FileProtocolV1, + _position: *mut u64, + ) -> Status { Status::UNSUPPORTED } - extern "efiapi" fn stub_set_position(_this: &mut FileImpl, _position: u64) -> Status { + extern "efiapi" fn stub_set_position(_this: *mut FileProtocolV1, _position: u64) -> Status { Status::UNSUPPORTED } extern "efiapi" fn stub_set_info( - _this: &mut FileImpl, - _information_type: &Guid, + _this: *mut FileProtocolV1, + _information_type: *const Guid, _buffer_size: usize, _buffer: *const c_void, ) -> Status { Status::UNSUPPORTED } - extern "efiapi" fn stub_flush(_this: &mut FileImpl) -> Status { + extern "efiapi" fn stub_flush(_this: *mut FileProtocolV1) -> Status { Status::UNSUPPORTED } } diff --git a/uefi/src/proto/media/file/regular.rs b/uefi/src/proto/media/file/regular.rs index 18895fba9..c3f23a5d8 100644 --- a/uefi/src/proto/media/file/regular.rs +++ b/uefi/src/proto/media/file/regular.rs @@ -50,7 +50,7 @@ impl RegularFile { let chunk_size = 1024 * 1024; read_chunked(buffer, chunk_size, |buf, buf_size| unsafe { - (self.imp().read)(self.imp(), buf_size, buf) + (self.imp().read)(self.imp(), buf_size, buf.cast()) }) } @@ -59,7 +59,7 @@ impl RegularFile { pub(super) fn read_unchunked(&mut self, buffer: &mut [u8]) -> Result> { let mut buffer_size = buffer.len(); let status = - unsafe { (self.imp().read)(self.imp(), &mut buffer_size, buffer.as_mut_ptr()) }; + unsafe { (self.imp().read)(self.imp(), &mut buffer_size, buffer.as_mut_ptr().cast()) }; status.to_result_with( || buffer_size, @@ -97,7 +97,7 @@ impl RegularFile { /// * [`uefi::Status::VOLUME_FULL`] pub fn write(&mut self, buffer: &[u8]) -> Result<(), usize> { let mut buffer_size = buffer.len(); - unsafe { (self.imp().write)(self.imp(), &mut buffer_size, buffer.as_ptr()) } + unsafe { (self.imp().write)(self.imp(), &mut buffer_size, buffer.as_ptr().cast()) } .to_result_with_err(|_| buffer_size) } @@ -110,7 +110,7 @@ impl RegularFile { /// * [`uefi::Status::DEVICE_ERROR`] pub fn get_position(&mut self) -> Result { let mut pos = 0u64; - (self.imp().get_position)(self.imp(), &mut pos).to_result_with_val(|| pos) + unsafe { (self.imp().get_position)(self.imp(), &mut pos) }.to_result_with_val(|| pos) } /// Sets the file's current position @@ -129,7 +129,7 @@ impl RegularFile { /// /// * [`uefi::Status::DEVICE_ERROR`] pub fn set_position(&mut self, position: u64) -> Result { - (self.imp().set_position)(self.imp(), position).to_result() + unsafe { (self.imp().set_position)(self.imp(), position) }.to_result() } } diff --git a/uefi/src/proto/media/fs.rs b/uefi/src/proto/media/fs.rs index 266f86562..b624732e7 100644 --- a/uefi/src/proto/media/fs.rs +++ b/uefi/src/proto/media/fs.rs @@ -1,9 +1,10 @@ //! File system support protocols. -use super::file::{Directory, FileHandle, FileImpl}; +use super::file::{Directory, FileHandle}; use crate::proto::unsafe_protocol; -use crate::{Result, Status, StatusExt}; +use crate::{Result, StatusExt}; use core::ptr; +use uefi_raw::protocol::file_system::SimpleFileSystemProtocol; /// Allows access to a FAT-12/16/32 file system. /// @@ -20,13 +21,9 @@ use core::ptr; /// [`BootServices::get_image_file_system`]: crate::table::boot::BootServices::get_image_file_system /// [`BootServices`]: crate::table::boot::BootServices#accessing-protocols #[derive(Debug)] -#[repr(C)] -#[unsafe_protocol("964e5b22-6459-11d2-8e39-00a0c969723b")] -pub struct SimpleFileSystem { - revision: u64, - open_volume: - extern "efiapi" fn(this: &mut SimpleFileSystem, root: &mut *mut FileImpl) -> Status, -} +#[repr(transparent)] +#[unsafe_protocol(SimpleFileSystemProtocol::GUID)] +pub struct SimpleFileSystem(SimpleFileSystemProtocol); impl SimpleFileSystem { /// Open the root directory on a volume. @@ -49,7 +46,7 @@ impl SimpleFileSystem { /// * [`uefi::Status::MEDIA_CHANGED`] pub fn open_volume(&mut self) -> Result { let mut ptr = ptr::null_mut(); - (self.open_volume)(self, &mut ptr) - .to_result_with_val(|| unsafe { Directory::new(FileHandle::new(ptr)) }) + unsafe { (self.0.open_volume)(&mut self.0, &mut ptr) } + .to_result_with_val(|| unsafe { Directory::new(FileHandle::new(ptr.cast())) }) } }