1use super::{File, FileHandle, FileInternal};
2use crate::{Result, Status};
3
4/// A `FileHandle` that is also a regular (data) file.
5///
6/// Use `FileHandle::into_type` or `RegularFile::new` to create a `RegularFile`.
7/// In addition to supporting the normal `File` operations, `RegularFile`
8/// supports direct reading and writing.
9#[repr(transparent)]
10#[derive(Debug)]
11pub struct RegularFile(FileHandle);
12
13impl RegularFile {
14 /// A special position used to seek to the end of a file with `set_position()`.
15 pub const END_OF_FILE: u64 = u64::MAX;
16
17 /// Coverts a `FileHandle` into a `RegularFile` without checking the file kind.
18 /// # Safety
19 /// This function should only be called on handles which ARE NOT directories,
20 /// doing otherwise is unsafe.
21 #[must_use]
22 pub unsafe fn new(handle: FileHandle) -> Self {
23 Self(handle)
24 }
25
26 /// Read data from file.
27 ///
28 /// Try to read as much as possible into `buffer`. Returns the number of bytes that were
29 /// actually read.
30 ///
31 /// # Arguments
32 /// * `buffer` The target buffer of the read operation
33 ///
34 /// # Errors
35 ///
36 /// See section `EFI_FILE_PROTOCOL.Read()` in the UEFI Specification for more details.
37 ///
38 /// * [`uefi::Status::NO_MEDIA`]
39 /// * [`uefi::Status::DEVICE_ERROR`]
40 /// * [`uefi::Status::VOLUME_CORRUPTED`]
41 /// * [`uefi::Status::BUFFER_TOO_SMALL`]
42 pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Option<usize>> {
43 let mut buffer_size = buffer.len();
44 let status =
45 unsafe { (self.imp().read)(self.imp(), &mut buffer_size, buffer.as_mut_ptr()) };
46
47 status.into_with(
48 || buffer_size,
49 |s| {
50 if s == Status::BUFFER_TOO_SMALL {
51 // `buffer_size` was updated to the required buffer size by the underlying read
52 // function.
53 Some(buffer_size)
54 } else {
55 None
56 }
57 },
58 )
59 }
60
61 /// Write data to file
62 ///
63 /// Write `buffer` to file, increment the file pointer.
64 ///
65 /// If an error occurs, returns the number of bytes that were actually written. If no error
66 /// occurred, the entire buffer is guaranteed to have been written successfully.
67 ///
68 /// # Arguments
69 /// * `buffer` Buffer to write to file
70 ///
71 /// # Errors
72 ///
73 /// See section `EFI_FILE_PROTOCOL.Write()` in the UEFI Specification for more details.
74 ///
75 /// * [`uefi::Status::NO_MEDIA`]
76 /// * [`uefi::Status::DEVICE_ERROR`]
77 /// * [`uefi::Status::VOLUME_CORRUPTED`]
78 /// * [`uefi::Status::WRITE_PROTECTED`]
79 /// * [`uefi::Status::ACCESS_DENIED`]
80 /// * [`uefi::Status::VOLUME_FULL`]
81 pub fn write(&mut self, buffer: &[u8]) -> Result<(), usize> {
82 let mut buffer_size = buffer.len();
83 unsafe { (self.imp().write)(self.imp(), &mut buffer_size, buffer.as_ptr()) }
84 .into_with_err(|_| buffer_size)
85 }
86
87 /// Get the file's current position
88 ///
89 /// # Errors
90 ///
91 /// See section `EFI_FILE_PROTOCOL.GetPosition()` in the UEFI Specification for more details.
92 ///
93 /// * [`uefi::Status::DEVICE_ERROR`]
94 pub fn get_position(&mut self) -> Result<u64> {
95 let mut pos = 0u64;
96 (self.imp().get_position)(self.imp(), &mut pos).into_with_val(|| pos)
97 }
98
99 /// Sets the file's current position
100 ///
101 /// Set the position of this file handle to the absolute position specified by `position`.
102 ///
103 /// Seeking past the end of the file is allowed, it will trigger file growth on the next write.
104 /// Using a position of RegularFile::END_OF_FILE will seek to the end of the file.
105 ///
106 /// # Arguments
107 /// * `position` The new absolution position of the file handle
108 ///
109 /// # Errors
110 ///
111 /// See section `EFI_FILE_PROTOCOL.SetPosition()` in the UEFI Specification for more details.
112 ///
113 /// * [`uefi::Status::DEVICE_ERROR`]
114 pub fn set_position(&mut self, position: u64) -> Result {
115 (self.imp().set_position)(self.imp(), position).into()
116 }
117}
118
119impl File for RegularFile {
120 #[inline]
121 fn handle(&mut self) -> &mut FileHandle {
122 &mut self.0
123 }
124
125 fn is_regular_file(&self) -> Result<bool> {
126 Ok(true)
127 }
128
129 fn is_directory(&self) -> Result<bool> {
130 Ok(false)
131 }
132}
133