use alloc::{vec, vec::Vec};
use core::{ffi::c_void, mem::size_of};
use patina::error::EfiError;
use r_efi::efi;
use crate::protocols::PROTOCOL_DB;
pub struct SimpleFile<'a> {
file: &'a mut efi::protocols::file::Protocol,
}
impl SimpleFile<'_> {
pub fn open(&mut self, filename: Vec<u16>, mode: u64, attributes: u64) -> Result<Self, EfiError> {
let mut file_ptr = core::ptr::null_mut();
let status = (self.file.open)(
self.file,
core::ptr::addr_of_mut!(file_ptr),
filename.as_ptr() as *mut u16,
mode,
attributes,
);
EfiError::status_to_result(status)?;
let file = unsafe { file_ptr.as_mut().ok_or(EfiError::NotFound)? };
Ok(Self { file })
}
pub fn open_volume(handle: efi::Handle) -> Result<Self, EfiError> {
let sfs = unsafe {
let sfs_protocol_ptr =
PROTOCOL_DB.get_interface_for_handle(handle, efi::protocols::simple_file_system::PROTOCOL_GUID)?;
(sfs_protocol_ptr as *mut efi::protocols::simple_file_system::Protocol)
.as_mut()
.ok_or(EfiError::NotFound)?
};
let mut file_system_ptr = core::ptr::null_mut();
let status = (sfs.open_volume)(sfs, core::ptr::addr_of_mut!(file_system_ptr));
EfiError::status_to_result(status)?;
let root = unsafe { file_system_ptr.as_mut().ok_or(EfiError::NotFound)? };
Ok(Self { file: root })
}
fn get_info(&mut self) -> Result<Vec<u8>, EfiError> {
let mut info_size = 0;
let status = (self.file.get_info)(
self.file,
&efi::protocols::file::INFO_ID as *const efi::Guid as *mut efi::Guid,
core::ptr::addr_of_mut!(info_size),
core::ptr::null_mut(),
);
match status {
efi::Status::BUFFER_TOO_SMALL => (), efi::Status::SUCCESS => Err(EfiError::DeviceError)?, err => EfiError::status_to_result(err)?, }
let mut file_info_buffer = vec![0u8; info_size];
let status = (self.file.get_info)(
self.file,
&efi::protocols::file::INFO_ID as *const efi::Guid as *mut efi::Guid,
core::ptr::addr_of_mut!(info_size),
file_info_buffer.as_mut_ptr() as *mut c_void,
);
EfiError::status_to_result(status).map(|_| file_info_buffer)
}
pub fn get_size(&mut self) -> Result<u64, EfiError> {
let file_info_buffer = self.get_info()?;
let file_size_as_bytes = file_info_buffer.chunks_exact(size_of::<u64>()).nth(1).ok_or(EfiError::NotFound)?;
Ok(u64::from_le_bytes(file_size_as_bytes.try_into().or(Err(EfiError::InvalidParameter))?))
}
pub fn get_attribute(&mut self) -> Result<u64, EfiError> {
let file_info_buffer = self.get_info()?;
let file_attribute = file_info_buffer.chunks_exact(size_of::<u64>()).nth(9).ok_or(EfiError::NotFound)?;
Ok(u64::from_le_bytes(file_attribute.try_into().or(Err(EfiError::InvalidParameter))?))
}
pub fn read(&mut self) -> Result<Vec<u8>, EfiError> {
let file_attribute = self.get_attribute()?;
if (file_attribute & efi::protocols::file::DIRECTORY) != 0 {
Err(EfiError::NotFound)?;
}
let mut file_size = self.get_size()? as usize;
let mut file_buffer = vec![0u8; file_size];
let status = (self.file.set_position)(self.file, 0);
EfiError::status_to_result(status)?;
let status =
(self.file.read)(self.file, core::ptr::addr_of_mut!(file_size), file_buffer.as_mut_ptr() as *mut c_void);
EfiError::status_to_result(status)?;
assert!(file_size <= file_buffer.len());
if file_size < file_buffer.len() { Ok(file_buffer[0..file_size].to_vec()) } else { Ok(file_buffer) }
}
}