Skip to content

Commit 317728a

Browse files
committed
Changes FileInfo to DST
1 parent 0530593 commit 317728a

File tree

4 files changed

+119
-95
lines changed

4 files changed

+119
-95
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ Keep in mind that you need to put everything your test needed in the same functi
199199
to update otherwise you will got a fat pointer, which is Rust specific. You can get a pointer to
200200
`EFI_DEVICE_PATH_PROTOCOL` via `Path::as_bytes()`.
201201
- `FileInfo` is changed from sized type to unsized type in the same way as `Path`.
202-
- `File::info()` now return `FileInfoBuf` instead of `Owned<FileInfo>` when success.
202+
- `File::info()` now return `Box<FileInfo>` instead of `Owned<FileInfo>` when success.
203203
- The second parameter of `Owned::new()` is changed to `Dtor`.
204204

205205
## Example Projects

src/filesystem.rs

+93-94
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
use crate::{Dtor, EfiStr, Guid, Owned, Status, Time};
22
use alloc::alloc::{alloc, dealloc, handle_alloc_error};
3-
use alloc::borrow::ToOwned;
3+
use alloc::boxed::Box;
44
use bitflags::bitflags;
55
use core::alloc::Layout;
6-
use core::borrow::{Borrow, BorrowMut};
76
use core::fmt::{Display, Formatter};
87
use core::mem::zeroed;
9-
use core::ops::{Deref, DerefMut};
10-
use core::ptr::{null_mut, read, slice_from_raw_parts, slice_from_raw_parts_mut};
8+
use core::ptr::null_mut;
119

1210
/// Represents an `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL`.
1311
#[repr(C)]
@@ -141,23 +139,23 @@ impl File {
141139
}
142140
}
143141

144-
pub fn info(&self) -> Result<FileInfoBuf, Status> {
142+
pub fn info(&self) -> Result<Box<FileInfo>, Status> {
145143
// Try until the buffer is enought.
146-
let mut layout = Layout::from_size_align(0x52, 8).unwrap();
147-
let info = loop {
144+
let mut layout = FileInfo::memory_layout(1);
145+
let (mut info, len) = loop {
148146
// Allocate a buffer.
149-
let mut len = layout.size();
150147
let info = unsafe { alloc(layout) };
151148

152149
if info.is_null() {
153150
handle_alloc_error(layout);
154151
}
155152

156153
// Get info.
154+
let mut len = layout.size();
157155
let status = unsafe { (self.get_info)(self, &FileInfo::ID, &mut len, info) };
158156

159157
if status == Status::SUCCESS {
160-
break info;
158+
break (info, len);
161159
}
162160

163161
// Check if we need to try again.
@@ -168,13 +166,37 @@ impl File {
168166
}
169167

170168
// Update memory layout and try again.
171-
layout = Layout::from_size_align(len, 8).unwrap();
169+
layout = FileInfo::memory_layout(len.checked_sub(0x50).unwrap() / 2);
172170
};
173171

174-
Ok(FileInfoBuf {
175-
buf: info,
176-
len: layout.size(),
177-
})
172+
// Check if layout matched.
173+
let name = len.checked_sub(0x50).unwrap() / 2;
174+
let new = FileInfo::memory_layout(name);
175+
176+
if new != layout {
177+
// Allocate a new buffer to match with final layout.
178+
let buf = unsafe { alloc(new) };
179+
180+
if buf.is_null() {
181+
handle_alloc_error(new)
182+
}
183+
184+
// Copy data.
185+
unsafe { buf.copy_from_nonoverlapping(info, len) };
186+
unsafe { dealloc(info, layout) };
187+
188+
info = buf;
189+
layout = new;
190+
}
191+
192+
// Cast to FileInfo. Pointer casting here may looks weird but it is how DST works.
193+
// See https://stackoverflow.com/a/64121094/1829232 for more details.
194+
let info = core::ptr::slice_from_raw_parts_mut::<u16>(info.cast(), name) as *mut FileInfo;
195+
let info = unsafe { Box::from_raw(info) };
196+
197+
assert_eq!(size_of_val(info.as_ref()), layout.size());
198+
199+
Ok(info)
178200
}
179201

180202
pub fn set_len(&mut self, len: u64) -> Result<(), FileSetLenError> {
@@ -189,13 +211,15 @@ impl File {
189211
}
190212

191213
// Update the info.
192-
*info.file_size_mut() = len;
193-
*info.create_time_mut() = unsafe { zeroed() };
194-
*info.last_accessed_mut() = unsafe { zeroed() };
195-
*info.last_modified_mut() = unsafe { zeroed() };
214+
info.set_file_size(len);
215+
info.set_create_time(unsafe { zeroed() });
216+
info.set_last_accessed(unsafe { zeroed() });
217+
info.set_last_modified(unsafe { zeroed() });
196218

197219
// Set the info.
198-
let status = unsafe { (self.set_info)(self, &FileInfo::ID, info.0.len(), info.0.as_ptr()) };
220+
let len = 0x50 + info.file_name.len() * 2;
221+
let info = info.as_ref() as *const FileInfo as *const u8;
222+
let status = unsafe { (self.set_info)(self, &FileInfo::ID, len, info) };
199223

200224
if status != Status::SUCCESS {
201225
Err(FileSetLenError::SetInfoFailed(status))
@@ -230,6 +254,7 @@ bitflags! {
230254
bitflags! {
231255
/// Attributes of the file to create.
232256
#[repr(transparent)]
257+
#[derive(Clone, Copy)]
233258
pub struct FileAttributes: u64 {
234259
const READ_ONLY = 0x0000000000000001;
235260
const HIDDEN = 0x0000000000000002;
@@ -240,12 +265,18 @@ bitflags! {
240265
}
241266
}
242267

243-
/// A borrowed `EFI_FILE_INFO`.
244-
///
245-
/// Do not depend on a transparent representation as a slice because it will be removed in the
246-
/// future when a Dynamically Sized Type can be safely construct on stable Rust.
247-
#[repr(transparent)]
248-
pub struct FileInfo([u8]);
268+
/// Represents an `EFI_FILE_INFO`.
269+
#[repr(C)]
270+
pub struct FileInfo {
271+
size: u64,
272+
file_size: u64,
273+
physical_size: u64,
274+
create_time: Time,
275+
last_access_time: Time,
276+
modification_time: Time,
277+
attribute: FileAttributes,
278+
file_name: [u16],
279+
}
249280

250281
impl FileInfo {
251282
pub const ID: Guid = Guid::new(
@@ -255,112 +286,80 @@ impl FileInfo {
255286
[0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b],
256287
);
257288

258-
pub fn as_bytes(&self) -> &[u8] {
259-
&self.0
289+
pub fn file_size(&self) -> u64 {
290+
self.file_size
260291
}
261292

262-
pub fn file_size(&self) -> u64 {
263-
unsafe { read(self.0.as_ptr().add(0x08) as _) }
293+
pub fn set_file_size(&mut self, v: u64) {
294+
self.file_size = v;
264295
}
265296

266297
pub fn file_size_mut(&mut self) -> &mut u64 {
267-
unsafe { &mut *(self.0.as_mut_ptr().add(0x08) as *mut u64) }
298+
&mut self.file_size
268299
}
269300

270301
pub fn physical_size(&self) -> u64 {
271-
unsafe { read(self.0.as_ptr().add(0x10) as _) }
302+
self.physical_size
272303
}
273304

274305
pub fn create_time(&self) -> &Time {
275-
unsafe { &*(self.0.as_ptr().add(0x18) as *const Time) }
276-
}
277-
278-
pub fn create_time_mut(&mut self) -> &mut Time {
279-
unsafe { &mut *(self.0.as_mut_ptr().add(0x18) as *mut Time) }
306+
&self.create_time
280307
}
281308

282-
pub fn last_accessed(&self) -> &Time {
283-
unsafe { &*(self.0.as_ptr().add(0x28) as *const Time) }
309+
pub fn set_create_time(&mut self, v: Time) {
310+
self.create_time = v;
284311
}
285312

286-
pub fn last_accessed_mut(&mut self) -> &mut Time {
287-
unsafe { &mut *(self.0.as_mut_ptr().add(0x28) as *mut Time) }
313+
pub fn create_time_mut(&mut self) -> &mut Time {
314+
&mut self.create_time
288315
}
289316

290-
pub fn last_modified(&self) -> &Time {
291-
unsafe { &*(self.0.as_ptr().add(0x38) as *const Time) }
317+
pub fn last_accessed(&self) -> &Time {
318+
&self.last_access_time
292319
}
293320

294-
pub fn last_modified_mut(&mut self) -> &mut Time {
295-
unsafe { &mut *(self.0.as_mut_ptr().add(0x38) as *mut Time) }
321+
pub fn set_last_accessed(&mut self, v: Time) {
322+
self.last_access_time = v;
296323
}
297324

298-
pub fn attributes(&self) -> FileAttributes {
299-
unsafe { read(self.0.as_ptr().add(0x48) as _) }
325+
pub fn last_accessed_mut(&mut self) -> &mut Time {
326+
&mut self.last_access_time
300327
}
301328

302-
pub fn attributes_mut(&mut self) -> &mut FileAttributes {
303-
unsafe { &mut *(self.0.as_mut_ptr().add(0x48) as *mut FileAttributes) }
329+
pub fn last_modified(&self) -> &Time {
330+
&self.modification_time
304331
}
305332

306-
pub fn file_name(&self) -> &EfiStr {
307-
unsafe { EfiStr::from_ptr(self.0.as_ptr().add(0x50) as _) }
333+
pub fn set_last_modified(&mut self, v: Time) {
334+
self.modification_time = v;
308335
}
309-
}
310-
311-
impl ToOwned for FileInfo {
312-
type Owned = FileInfoBuf;
313-
314-
fn to_owned(&self) -> Self::Owned {
315-
let len = self.0.len();
316-
let layout = Layout::from_size_align(len, 8).unwrap();
317-
let buf = unsafe { alloc(layout) };
318-
319-
if buf.is_null() {
320-
handle_alloc_error(layout);
321-
}
322-
323-
unsafe { buf.copy_from_nonoverlapping(self.0.as_ptr(), len) };
324336

325-
FileInfoBuf { buf, len }
337+
pub fn last_modified_mut(&mut self) -> &mut Time {
338+
&mut self.modification_time
326339
}
327-
}
328-
329-
/// An owned version of [`FileInfo`].
330-
pub struct FileInfoBuf {
331-
buf: *mut u8, // Must be 8 bytes aligment.
332-
len: usize,
333-
}
334340

335-
impl Drop for FileInfoBuf {
336-
fn drop(&mut self) {
337-
unsafe { dealloc(self.buf, Layout::from_size_align(self.len, 8).unwrap()) };
341+
pub fn attributes(&self) -> FileAttributes {
342+
self.attribute
338343
}
339-
}
340344

341-
impl Deref for FileInfoBuf {
342-
type Target = FileInfo;
343-
344-
fn deref(&self) -> &Self::Target {
345-
self.borrow()
345+
pub fn set_attributes(&mut self, v: FileAttributes) {
346+
self.attribute = v;
346347
}
347-
}
348348

349-
impl DerefMut for FileInfoBuf {
350-
fn deref_mut(&mut self) -> &mut Self::Target {
351-
self.borrow_mut()
349+
pub fn attributes_mut(&mut self) -> &mut FileAttributes {
350+
&mut self.attribute
352351
}
353-
}
354352

355-
impl Borrow<FileInfo> for FileInfoBuf {
356-
fn borrow(&self) -> &FileInfo {
357-
unsafe { &*(slice_from_raw_parts(self.buf, self.len) as *const FileInfo) }
353+
pub fn file_name(&self) -> &EfiStr {
354+
// SAFETY: UEFI specs guarantee null-terminated.
355+
unsafe { EfiStr::new_unchecked(&self.file_name) }
358356
}
359-
}
360357

361-
impl BorrowMut<FileInfo> for FileInfoBuf {
362-
fn borrow_mut(&mut self) -> &mut FileInfo {
363-
unsafe { &mut *(slice_from_raw_parts_mut(self.buf, self.len) as *mut FileInfo) }
358+
pub fn memory_layout(name: usize) -> Layout {
359+
Layout::from_size_align(0x50, 8)
360+
.and_then(move |b| b.extend(Layout::array::<u16>(name).unwrap()))
361+
.map(|v| v.0.pad_to_align())
362+
.unwrap()
364363
}
365364
}
366365

src/string.rs

+3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use core::slice::{from_raw_parts, IterMut};
88
use core::str::FromStr;
99

1010
/// A borrowed EFI string. The string is always have NUL at the end.
11+
///
12+
/// You can use [str](crate::str) macro to create a value of this type.
1113
#[repr(transparent)]
14+
#[derive(Debug, PartialEq, Eq)]
1215
pub struct EfiStr([u16]);
1316

1417
impl EfiStr {

tests/filesystem.rs

+22
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,25 @@ fn file_info() {
2121
assert_ne!(info.physical_size(), 0);
2222
assert_eq!(info.attributes().contains(FileAttributes::DIRECTORY), false);
2323
}
24+
25+
#[test]
26+
#[qemu]
27+
fn create() {
28+
use zfi::{str, FileAttributes, Image};
29+
30+
let image = Image::current().proto();
31+
let fs = image.device().file_system().unwrap();
32+
let root = fs.open().unwrap();
33+
34+
// Create non-empty file to see if the second call truncate the file.
35+
let path = str!("\\test-file.txt");
36+
let mut file = root.create(path, FileAttributes::empty()).unwrap();
37+
let mut data = b"Hello, world!".to_vec();
38+
39+
assert_eq!(file.write(&data).unwrap(), data.len());
40+
41+
// Create the same file again to see if it is truncated.
42+
let mut file = root.create(path, FileAttributes::empty()).unwrap();
43+
44+
assert_eq!(file.read(&mut data).unwrap(), 0);
45+
}

0 commit comments

Comments
 (0)