mod dir;
mod info;
mod regular;
use crate::prelude::*;
use crate::{CStr16, Char16, Guid, Result, Status};
#[cfg(feature = "exts")]
use alloc_api::{alloc::Layout, boxed::Box};
use bitflags::bitflags;
use core::ffi::c_void;
use core::mem;
use core::ptr;
pub use self::info::{
FileInfo, FileProtocolInfo, FileSystemInfo, FileSystemVolumeLabel, FromUefi,
NamedFileProtocolInfo,
};
pub use self::{dir::Directory, regular::RegularFile};
pub trait File: Sized {
#[doc(hidden)]
fn handle(&mut self) -> &mut FileHandle;
fn open(
&mut self,
filename: &str,
open_mode: FileMode,
attributes: FileAttribute,
) -> Result<FileHandle> {
const BUF_SIZE: usize = 255;
if filename.len() > BUF_SIZE {
Err(Status::INVALID_PARAMETER.into())
} else {
let mut buf = [0u16; BUF_SIZE + 1];
let mut ptr = ptr::null_mut();
let len = ucs2::encode(filename, &mut buf)?;
let filename = unsafe { CStr16::from_u16_with_nul_unchecked(&buf[..=len]) };
unsafe {
(self.imp().open)(
self.imp(),
&mut ptr,
filename.as_ptr(),
open_mode,
attributes,
)
}
.into_with_val(|| unsafe { FileHandle::new(ptr) })
}
}
fn close(self) {}
fn delete(mut self) -> Result {
let result = (self.imp().delete)(self.imp()).into();
mem::forget(self);
result
}
fn get_info<'buf, Info: FileProtocolInfo + ?Sized>(
&mut self,
buffer: &'buf mut [u8],
) -> Result<&'buf mut Info, Option<usize>> {
let mut buffer_size = buffer.len();
Info::assert_aligned(buffer);
unsafe {
(self.imp().get_info)(
self.imp(),
&Info::GUID,
&mut buffer_size,
buffer.as_mut_ptr(),
)
}
.into_with(
|| unsafe { Info::from_uefi(buffer.as_ptr() as *mut c_void) },
|s| {
if s == Status::BUFFER_TOO_SMALL {
Some(buffer_size)
} else {
None
}
},
)
}
fn set_info<Info: FileProtocolInfo + ?Sized>(&mut self, info: &Info) -> Result {
let info_ptr = info as *const Info as *const c_void;
let info_size = mem::size_of_val(&info);
unsafe { (self.imp().set_info)(self.imp(), &Info::GUID, info_size, info_ptr).into() }
}
fn flush(&mut self) -> Result {
(self.imp().flush)(self.imp()).into()
}
#[cfg(feature = "exts")]
fn get_boxed_info<Info: FileProtocolInfo + ?Sized>(&mut self) -> Result<Box<Info>> {
let size = match self
.get_info::<Info>(&mut [])
.expect_error("zero sized get_info unexpectedly succeeded")
.split()
{
(s, None) => return Err(s.into()),
(_, Some(size)) => size,
};
let layout = Layout::from_size_align(size, Info::alignment())
.unwrap()
.pad_to_align();
let mut buffer = crate::exts::allocate_buffer(layout);
let buffer_start = buffer.as_ptr();
let info = self
.get_info(&mut buffer)
.discard_errdata()?
.map(|info_ref| {
assert_eq!(mem::size_of_val(info_ref), layout.size());
assert_eq!(info_ref as *const Info as *const u8, buffer_start);
unsafe { Box::from_raw(info_ref as *mut _) }
});
mem::forget(buffer);
Ok(info)
}
}
trait FileInternal: File {
fn imp(&mut self) -> &mut FileImpl {
unsafe { &mut *self.handle().0 }
}
}
impl<T: File> FileInternal for T {}
#[repr(transparent)]
pub struct FileHandle(*mut FileImpl);
impl FileHandle {
pub(super) unsafe fn new(ptr: *mut FileImpl) -> Self {
Self(ptr)
}
pub fn into_type(mut self) -> Result<FileType> {
use FileType::*;
let mut pos = 0;
match (self.imp().get_position)(self.imp(), &mut pos) {
Status::SUCCESS => unsafe { Ok(Regular(RegularFile::new(self)).into()) },
Status::UNSUPPORTED => unsafe { Ok(Dir(Directory::new(self)).into()) },
s => Err(s.into()),
}
}
}
impl File for FileHandle {
#[inline]
fn handle(&mut self) -> &mut FileHandle {
self
}
}
impl Drop for FileHandle {
fn drop(&mut self) {
let result: Result = (self.imp().close)(self.imp()).into();
result.expect_success("Failed to close file");
}
}
#[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: 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,
}
pub enum FileType {
Regular(RegularFile),
Dir(Directory),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u64)]
pub enum FileMode {
Read = 1,
ReadWrite = 2 | 1,
CreateReadWrite = (1 << 63) | 2 | 1,
}
bitflags! {
pub struct FileAttribute: u64 {
const READ_ONLY = 1;
const HIDDEN = 1 << 1;
const SYSTEM = 1 << 2;
const DIRECTORY = 1 << 4;
const ARCHIVE = 1 << 5;
const VALID_ATTR = 0x37;
}
}