1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! DXE Core Filesystem
//!
//! ## License
//!
//! Copyright (c) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!
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;
/// Provides a wrapper for interacting with SimpleFileSystem
pub struct SimpleFile<'a> {
file: &'a mut efi::protocols::file::Protocol,
}
impl SimpleFile<'_> {
/// Opens the given filename with appropriate mode/attributes and returns a new instance of SimpleFile for it.
pub fn open(&mut self, filename: Vec<u16>, mode: u64, attributes: u64) -> Result<Self, EfiError> {
let mut file_ptr = core::ptr::null_mut();
// SAFETY: self.file is a valid pointer to a file protocol instance
// obtained from the protocol database during the construction of this
// SimpleFile instance.
let status = unsafe {
(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)?;
// SAFETY: file_ptr is filled by the open call above on success.
let file = unsafe { file_ptr.as_mut().ok_or(EfiError::NotFound)? };
Ok(Self { file })
}
/// Opens the root of a Simple File System and returns a SimpleFile object for it.
pub fn open_volume(handle: efi::Handle) -> Result<Self, EfiError> {
// SAFETY: Protocol database returns a valid interface pointer for the handle.
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();
// SAFETY: sfs is a valid pointer to a simple file system protocol instance
// obtained from the protocol database above.
let status = unsafe { (sfs.open_volume)(sfs, core::ptr::addr_of_mut!(file_system_ptr)) };
EfiError::status_to_result(status)?;
// SAFETY: file_system_ptr is filled by the open_volume call above on success.
let root = unsafe { file_system_ptr.as_mut().ok_or(EfiError::NotFound)? };
Ok(Self { file: root })
}
// returns a byte buffer containing the file info for this SimpleFile instance.
fn get_info(&mut self) -> Result<Vec<u8>, EfiError> {
let mut info_size = 0;
// SAFETY: self.file is a valid pointer to a file protocol instance
// obtained from the protocol database during the construction of this
// SimpleFile instance.
let status = unsafe {
(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 => (), // expected
efi::Status::SUCCESS => Err(EfiError::DeviceError)?, // unexpected success.
err => EfiError::status_to_result(err)?, // unexpected failure.
}
let mut file_info_buffer = vec![0u8; info_size];
// SAFETY: self.file is a valid pointer to a file protocol instance
// obtained from the protocol database during the construction of this
// SimpleFile instance.
let status = unsafe {
(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)
}
/// Returns the size of the file
pub fn get_size(&mut self) -> Result<u64, EfiError> {
let file_info_buffer = self.get_info()?;
//to avoid an unsafe transmute, read the file size directly from the buffer instead of trying to convert the whole
//buffer efi::protocols::file::Info. The file size is the second u64 in that buffer. TODO: proper conversion routine
//for byte buffer -> efi::protocols::file::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))?))
}
/// Returns the file attributes
pub fn get_attribute(&mut self) -> Result<u64, EfiError> {
let file_info_buffer = self.get_info()?;
//to avoid an unsafe transmute, read the attribute directly from the buffer instead of trying to convert the whole
//buffer efi::protocols::file::Info. The attribute is the 10th u64 in that buffer. TODO: proper conversion routine
//for byte buffer -> efi::protocols::file::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))?))
}
/// Reads the entire file into a byte vector and returns it
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];
// SAFETY: self.file is a valid pointer to a file protocol instance
// obtained from the protocol database during the construction of this
// SimpleFile instance.
let status = unsafe { (self.file.set_position)(self.file, 0) };
EfiError::status_to_result(status)?;
// SAFETY: self.file is a valid pointer to a file protocol instance
// obtained from the protocol database during the construction of this
// SimpleFile instance.
let status = unsafe {
(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)?;
//in case the read somehow returned fewer bytes than indicated by get_size, truncate the vector returned to the
//actual read size.
if file_size < file_buffer.len() {
Ok(file_buffer.get(0..file_size).ok_or(EfiError::DeviceError)?.to_vec())
} else {
Ok(file_buffer)
}
}
}