use super::FileAttribute;
use crate::data_types::{chars::NUL_16, Align};
use crate::table::runtime::Time;
use crate::{unsafe_guid, CStr16, Char16, Identify};
use core::cmp;
use core::convert::TryInto;
use core::ffi::c_void;
use core::mem;
use core::slice;
pub trait FileProtocolInfo: Align + Identify + FromUefi {}
pub trait FromUefi {
unsafe fn from_uefi<'ptr>(ptr: *mut c_void) -> &'ptr mut Self;
}
#[derive(Debug)]
#[repr(C)]
pub struct NamedFileProtocolInfo<Header> {
header: Header,
name: [Char16],
}
impl<Header> NamedFileProtocolInfo<Header> {
#[allow(clippy::cast_ptr_alignment)]
fn new_impl<'buf>(
storage: &'buf mut [u8],
header: Header,
name: &str,
) -> core::result::Result<&'buf mut Self, FileInfoCreationError> {
Self::assert_aligned(storage);
let name_length_ucs2 = name.chars().count() + 1;
let name_size = name_length_ucs2 * mem::size_of::<Char16>();
let info_size = mem::size_of::<Header>() + name_size;
if storage.len() < info_size {
return Err(FileInfoCreationError::InsufficientStorage(info_size));
}
let header_ptr = storage.as_mut_ptr() as *mut Header;
unsafe {
header_ptr.write(header);
}
debug_assert!(!mem::needs_drop::<Char16>());
let info_ptr = unsafe {
slice::from_raw_parts_mut(storage.as_mut_ptr() as *mut Char16, name_length_ucs2)
as *mut [Char16] as *mut Self
};
let info = unsafe { &mut *info_ptr };
debug_assert_eq!(info.name.len(), name_length_ucs2);
for (target, ch) in info.name.iter_mut().zip(name.chars()) {
*target = ch
.try_into()
.map_err(|_| FileInfoCreationError::InvalidChar(ch))?;
}
info.name[name_length_ucs2 - 1] = NUL_16;
Ok(info)
}
}
impl<Header> Align for NamedFileProtocolInfo<Header> {
fn alignment() -> usize {
cmp::max(mem::align_of::<Header>(), mem::align_of::<Char16>())
}
}
impl<Header> FromUefi for NamedFileProtocolInfo<Header> {
#[allow(clippy::cast_ptr_alignment)]
unsafe fn from_uefi<'ptr>(ptr: *mut c_void) -> &'ptr mut Self {
let byte_ptr = ptr as *mut u8;
let name_ptr = byte_ptr.add(mem::size_of::<Header>()) as *mut Char16;
let name = CStr16::from_ptr(name_ptr);
let name_len = name.to_u16_slice_with_nul().len();
let fat_ptr = slice::from_raw_parts_mut(ptr as *mut Char16, name_len);
let self_ptr = fat_ptr as *mut [Char16] as *mut Self;
&mut *self_ptr
}
}
pub enum FileInfoCreationError {
InsufficientStorage(usize),
InvalidChar(char),
}
#[unsafe_guid("09576e92-6d3f-11d2-8e39-00a0c969723b")]
pub type FileInfo = NamedFileProtocolInfo<FileInfoHeader>;
#[derive(Debug)]
#[repr(C)]
pub struct FileInfoHeader {
size: u64,
file_size: u64,
physical_size: u64,
create_time: Time,
last_access_time: Time,
modification_time: Time,
attribute: FileAttribute,
}
impl FileInfo {
#[allow(clippy::too_many_arguments)]
pub fn new<'buf>(
storage: &'buf mut [u8],
file_size: u64,
physical_size: u64,
create_time: Time,
last_access_time: Time,
modification_time: Time,
attribute: FileAttribute,
file_name: &str,
) -> core::result::Result<&'buf mut Self, FileInfoCreationError> {
let header = FileInfoHeader {
size: 0,
file_size,
physical_size,
create_time,
last_access_time,
modification_time,
attribute,
};
let info = Self::new_impl(storage, header, file_name)?;
info.header.size = mem::size_of_val(&info) as u64;
Ok(info)
}
pub fn file_size(&self) -> u64 {
self.header.file_size
}
pub fn physical_size(&self) -> u64 {
self.header.physical_size
}
pub fn create_time(&self) -> &Time {
&self.header.create_time
}
pub fn last_access_time(&self) -> &Time {
&self.header.last_access_time
}
pub fn modification_time(&self) -> &Time {
&self.header.modification_time
}
pub fn attribute(&self) -> FileAttribute {
self.header.attribute
}
pub fn file_name(&self) -> &CStr16 {
unsafe { CStr16::from_ptr(&self.name[0]) }
}
}
impl FileProtocolInfo for FileInfo {}
#[unsafe_guid("09576e93-6d3f-11d2-8e39-00a0c969723b")]
pub type FileSystemInfo = NamedFileProtocolInfo<FileSystemInfoHeader>;
#[derive(Debug)]
#[repr(C)]
pub struct FileSystemInfoHeader {
size: u64,
read_only: bool,
volume_size: u64,
free_space: u64,
block_size: u32,
}
impl FileSystemInfo {
#[allow(clippy::too_many_arguments)]
pub fn new<'buf>(
storage: &'buf mut [u8],
read_only: bool,
volume_size: u64,
free_space: u64,
block_size: u32,
volume_label: &str,
) -> core::result::Result<&'buf mut Self, FileInfoCreationError> {
let header = FileSystemInfoHeader {
size: 0,
read_only,
volume_size,
free_space,
block_size,
};
let info = Self::new_impl(storage, header, volume_label)?;
info.header.size = mem::size_of_val(&info) as u64;
Ok(info)
}
pub fn read_only(&self) -> bool {
self.header.read_only
}
pub fn volume_size(&self) -> u64 {
self.header.volume_size
}
pub fn free_space(&self) -> u64 {
self.header.free_space
}
pub fn block_size(&self) -> u32 {
self.header.block_size
}
pub fn volume_label(&self) -> &CStr16 {
unsafe { CStr16::from_ptr(&self.name[0]) }
}
}
impl FileProtocolInfo for FileSystemInfo {}
#[unsafe_guid("db47d7d3-fe81-11d3-9a35-0090273fc14d")]
pub type FileSystemVolumeLabel = NamedFileProtocolInfo<FileSystemVolumeLabelHeader>;
#[derive(Debug)]
#[repr(C)]
pub struct FileSystemVolumeLabelHeader {}
impl FileSystemVolumeLabel {
pub fn new<'buf>(
storage: &'buf mut [u8],
volume_label: &str,
) -> core::result::Result<&'buf mut Self, FileInfoCreationError> {
let header = FileSystemVolumeLabelHeader {};
Self::new_impl(storage, header, volume_label)
}
pub fn volume_label(&self) -> &CStr16 {
unsafe { CStr16::from_ptr(&self.name[0]) }
}
}
impl FileProtocolInfo for FileSystemVolumeLabel {}