use super::FileAttribute;
use crate::data_types::Align;
use crate::runtime::Time;
use crate::{CStr16, Char16, Guid, Identify};
use core::ffi::c_void;
use core::fmt::{self, Display, Formatter};
use core::ptr;
use ptr_meta::Pointee;
pub trait FileProtocolInfo: Align + Identify + FromUefi {}
pub trait FromUefi {
unsafe fn from_uefi<'ptr>(ptr: *mut c_void) -> &'ptr mut Self;
}
trait InfoInternal: Align + ptr_meta::Pointee<Metadata = usize> {
fn name_offset() -> usize;
unsafe fn name_ptr(ptr: *mut u8) -> *mut Char16 {
let offset_of_str = Self::name_offset();
unsafe { ptr.add(offset_of_str).cast::<Char16>() }
}
unsafe fn new_impl<'buf, F>(
storage: &'buf mut [u8],
name: &CStr16,
init: F,
) -> Result<&'buf mut Self, FileInfoCreationError>
where
F: FnOnce(*mut Self, u64),
{
let name_length_ucs2 = name.as_slice_with_nul().len();
let name_size = size_of_val(name.as_slice_with_nul());
let info_size = Self::name_offset() + name_size;
let info_size = Self::round_up_to_alignment(info_size);
let storage = Self::align_buf(storage)
.ok_or(FileInfoCreationError::InsufficientStorage(info_size))?;
Self::assert_aligned(storage);
if storage.len() < info_size {
return Err(FileInfoCreationError::InsufficientStorage(info_size));
}
let info_ptr: *mut Self =
ptr_meta::from_raw_parts_mut(storage.as_mut_ptr().cast::<()>(), name_length_ucs2);
init(info_ptr, info_size as u64);
let info_name_ptr = unsafe { Self::name_ptr(info_ptr.cast::<u8>()) };
unsafe { ptr::copy(name.as_ptr(), info_name_ptr, name_length_ucs2) };
let info = unsafe { &mut *info_ptr };
Ok(info)
}
}
impl<T> FromUefi for T
where
T: InfoInternal + ?Sized,
{
unsafe fn from_uefi<'ptr>(ptr: *mut c_void) -> &'ptr mut Self {
let name_ptr = unsafe { Self::name_ptr(ptr.cast::<u8>()) };
let name = unsafe { CStr16::from_ptr(name_ptr) };
let name_len = name.as_slice_with_nul().len();
unsafe { &mut *ptr_meta::from_raw_parts_mut(ptr.cast::<()>(), name_len) }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FileInfoCreationError {
InsufficientStorage(usize),
}
impl Display for FileInfoCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InsufficientStorage(bytes) => write!(
f,
"provided buffer was too small. need at least {bytes} bytes"
),
}
}
}
impl core::error::Error for FileInfoCreationError {}
#[derive(Debug, Eq, PartialEq, Pointee)]
#[repr(C)]
pub struct FileInfo {
size: u64,
file_size: u64,
physical_size: u64,
create_time: Time,
last_access_time: Time,
modification_time: Time,
attribute: FileAttribute,
file_name: [Char16],
}
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: &CStr16,
) -> core::result::Result<&'buf mut Self, FileInfoCreationError> {
unsafe {
Self::new_impl(storage, file_name, |ptr, size| {
(&raw mut (*ptr).size).write(size);
(&raw mut (*ptr).file_size).write(file_size);
(&raw mut (*ptr).physical_size).write(physical_size);
(&raw mut (*ptr).create_time).write(create_time);
(&raw mut (*ptr).last_access_time).write(last_access_time);
(&raw mut (*ptr).modification_time).write(modification_time);
(&raw mut (*ptr).attribute).write(attribute);
})
}
}
#[must_use]
pub const fn file_size(&self) -> u64 {
self.file_size
}
#[must_use]
pub const fn physical_size(&self) -> u64 {
self.physical_size
}
#[must_use]
pub const fn create_time(&self) -> &Time {
&self.create_time
}
#[must_use]
pub const fn last_access_time(&self) -> &Time {
&self.last_access_time
}
#[must_use]
pub const fn modification_time(&self) -> &Time {
&self.modification_time
}
#[must_use]
pub const fn attribute(&self) -> FileAttribute {
self.attribute
}
#[must_use]
pub fn file_name(&self) -> &CStr16 {
unsafe { CStr16::from_ptr(self.file_name.as_ptr()) }
}
#[must_use]
pub const fn is_directory(&self) -> bool {
self.attribute.contains(FileAttribute::DIRECTORY)
}
#[must_use]
pub const fn is_regular_file(&self) -> bool {
!self.is_directory()
}
}
impl Align for FileInfo {
fn alignment() -> usize {
8
}
}
unsafe impl Identify for FileInfo {
const GUID: Guid = uefi_raw::protocol::file_system::FileInfo::ID;
}
impl InfoInternal for FileInfo {
fn name_offset() -> usize {
80
}
}
impl FileProtocolInfo for FileInfo {}
#[derive(Debug, Eq, PartialEq, Pointee)]
#[repr(C)]
pub struct FileSystemInfo {
size: u64,
read_only: bool,
volume_size: u64,
free_space: u64,
block_size: u32,
volume_label: [Char16],
}
impl FileSystemInfo {
pub fn new<'buf>(
storage: &'buf mut [u8],
read_only: bool,
volume_size: u64,
free_space: u64,
block_size: u32,
volume_label: &CStr16,
) -> core::result::Result<&'buf mut Self, FileInfoCreationError> {
unsafe {
Self::new_impl(storage, volume_label, |ptr, size| {
(&raw mut (*ptr).size).write(size);
(&raw mut (*ptr).read_only).write(read_only);
(&raw mut (*ptr).volume_size).write(volume_size);
(&raw mut (*ptr).free_space).write(free_space);
(&raw mut (*ptr).block_size).write(block_size);
})
}
}
#[must_use]
pub const fn read_only(&self) -> bool {
self.read_only
}
#[must_use]
pub const fn volume_size(&self) -> u64 {
self.volume_size
}
#[must_use]
pub const fn free_space(&self) -> u64 {
self.free_space
}
#[must_use]
pub const fn block_size(&self) -> u32 {
self.block_size
}
#[must_use]
pub fn volume_label(&self) -> &CStr16 {
unsafe { CStr16::from_ptr(self.volume_label.as_ptr()) }
}
}
impl Align for FileSystemInfo {
fn alignment() -> usize {
8
}
}
unsafe impl Identify for FileSystemInfo {
const GUID: Guid = uefi_raw::protocol::file_system::FileSystemInfo::ID;
}
impl InfoInternal for FileSystemInfo {
fn name_offset() -> usize {
36
}
}
impl FileProtocolInfo for FileSystemInfo {}
#[derive(Debug, Eq, PartialEq, Pointee)]
#[repr(C)]
pub struct FileSystemVolumeLabel {
volume_label: [Char16],
}
impl FileSystemVolumeLabel {
pub fn new<'buf>(
storage: &'buf mut [u8],
volume_label: &CStr16,
) -> core::result::Result<&'buf mut Self, FileInfoCreationError> {
unsafe { Self::new_impl(storage, volume_label, |_ptr, _size| {}) }
}
#[must_use]
pub fn volume_label(&self) -> &CStr16 {
unsafe { CStr16::from_ptr(self.volume_label.as_ptr()) }
}
}
impl Align for FileSystemVolumeLabel {
fn alignment() -> usize {
2
}
}
unsafe impl Identify for FileSystemVolumeLabel {
const GUID: Guid = uefi_raw::protocol::file_system::FileSystemVolumeLabel::ID;
}
impl InfoInternal for FileSystemVolumeLabel {
fn name_offset() -> usize {
0
}
}
impl FileProtocolInfo for FileSystemVolumeLabel {}
#[cfg(test)]
mod tests {
use super::*;
use crate::CString16;
use crate::runtime::{Daylight, Time, TimeParams};
use alloc::vec;
fn validate_layout<T: InfoInternal + ?Sized>(info: &T, name: &[Char16]) {
assert_eq!(align_of_val(info), T::alignment());
assert_eq!(
unsafe {
name.as_ptr()
.cast::<u8>()
.offset_from(core::ptr::from_ref(info).cast::<u8>())
},
T::name_offset() as isize
);
}
#[test]
fn test_file_info() {
let mut storage = vec![0; 128];
let file_size = 123;
let physical_size = 456;
let tp = TimeParams {
year: 1970,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
nanosecond: 0,
time_zone: None,
daylight: Daylight::IN_DAYLIGHT,
};
let create_time = Time::new(tp).unwrap();
let last_access_time = Time::new(TimeParams { year: 1971, ..tp }).unwrap();
let modification_time = Time::new(TimeParams { year: 1972, ..tp }).unwrap();
let attribute = FileAttribute::READ_ONLY;
let name = CString16::try_from("test_name").unwrap();
let info = FileInfo::new(
&mut storage,
file_size,
physical_size,
create_time,
last_access_time,
modification_time,
attribute,
&name,
)
.unwrap();
validate_layout(info, &info.file_name);
assert_eq!(info.size, 104);
assert_eq!(info.size, size_of_val(info) as u64);
assert_eq!(info.file_size(), file_size);
assert_eq!(info.physical_size(), physical_size);
assert_eq!(info.create_time(), &create_time);
assert_eq!(info.last_access_time(), &last_access_time);
assert_eq!(info.modification_time(), &modification_time);
assert_eq!(info.attribute(), attribute);
assert_eq!(info.file_name(), name);
}
#[test]
fn test_file_system_info() {
let mut storage = vec![0; 128];
let read_only = true;
let volume_size = 123;
let free_space = 456;
let block_size = 789;
let name = CString16::try_from("test_name2").unwrap();
let info = FileSystemInfo::new(
&mut storage,
read_only,
volume_size,
free_space,
block_size,
&name,
)
.unwrap();
validate_layout(info, &info.volume_label);
assert_eq!(info.size, 64);
assert_eq!(info.size, size_of_val(info) as u64);
assert_eq!(info.read_only, read_only);
assert_eq!(info.volume_size, volume_size);
assert_eq!(info.free_space, free_space);
assert_eq!(info.block_size, block_size);
assert_eq!(info.volume_label(), name);
}
#[test]
fn test_file_system_volume_label() {
let mut storage = vec![0; 128];
let name = CString16::try_from("test_name").unwrap();
let info = FileSystemVolumeLabel::new(&mut storage, &name).unwrap();
validate_layout(info, &info.volume_label);
assert_eq!(info.volume_label(), name);
}
}