1use std::io::{self, Read, Seek, SeekFrom as IoSeekFrom, Write};
2use std::path::Path;
3
4use crate::traits::{validate_read_results, validate_write_results};
5use crate::{Backend, ReadOp, SeekFrom, VfFile, VfOffset};
6
7fn io_error(error: crate::VfError) -> io::Error {
8 let kind = match error.err_no() {
9 crate::ERR_NOENT => io::ErrorKind::NotFound,
10 crate::ERR_EXIST => io::ErrorKind::AlreadyExists,
11 crate::ERR_ACCES => io::ErrorKind::PermissionDenied,
12 crate::ERR_NOTDIR => io::ErrorKind::NotADirectory,
13 crate::ERR_ISDIR => io::ErrorKind::IsADirectory,
14 crate::ERR_INVAL | crate::ERR_EBADF => io::ErrorKind::InvalidInput,
15 _ => io::ErrorKind::Other,
16 };
17 io::Error::new(kind, error)
18}
19
20#[bitfields::bitfield(u8)]
21#[derive(PartialEq, Eq)]
22struct VfOpenFlags {
23 read: bool,
24 write: bool,
25 append: bool,
26 truncate: bool,
27 create: bool,
28 create_new: bool,
29 #[bits(2)]
30 _reserved: u8,
31}
32
33#[derive(Clone, Debug)]
35pub struct VfOpenOptions {
36 flags: VfOpenFlags,
37 mode: u32,
38}
39
40impl Default for VfOpenOptions {
41 fn default() -> Self {
42 Self {
43 flags: VfOpenFlags::new(),
44 mode: 0o666,
45 }
46 }
47}
48
49impl VfOpenOptions {
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn read(&mut self, value: bool) -> &mut Self {
55 self.flags.set_read(value);
56 self
57 }
58
59 pub fn write(&mut self, value: bool) -> &mut Self {
60 self.flags.set_write(value);
61 self
62 }
63
64 pub fn append(&mut self, value: bool) -> &mut Self {
65 self.flags.set_append(value);
66 self
67 }
68
69 pub fn truncate(&mut self, value: bool) -> &mut Self {
70 self.flags.set_truncate(value);
71 self
72 }
73
74 pub fn create(&mut self, value: bool) -> &mut Self {
75 self.flags.set_create(value);
76 self
77 }
78
79 pub fn create_new(&mut self, value: bool) -> &mut Self {
80 self.flags.set_create_new(value);
81 self
82 }
83
84 pub fn mode(&mut self, mode: u32) -> &mut Self {
86 self.mode = mode;
87 self
88 }
89
90 fn flags(&self) -> io::Result<i32> {
91 let writable = self.flags.write() || self.flags.append();
92 let mut flags = match (self.flags.read(), writable) {
93 (true, true) => libc::O_RDWR,
94 (false, true) => libc::O_WRONLY,
95 (true, false) => libc::O_RDONLY,
96 (false, false) => {
97 return Err(io::Error::new(
98 io::ErrorKind::InvalidInput,
99 "at least one of read, write, or append must be enabled",
100 ));
101 }
102 };
103 if self.flags.truncate() && !writable {
104 return Err(io::Error::new(
105 io::ErrorKind::InvalidInput,
106 "truncate requires write or append access",
107 ));
108 }
109 if (self.flags.create() || self.flags.create_new()) && !writable {
110 return Err(io::Error::new(
111 io::ErrorKind::InvalidInput,
112 "create requires write or append access",
113 ));
114 }
115 if self.flags.append() {
116 flags |= libc::O_APPEND;
117 }
118 if self.flags.truncate() {
119 flags |= libc::O_TRUNC;
120 }
121 if self.flags.create() || self.flags.create_new() {
122 flags |= libc::O_CREAT;
123 }
124 if self.flags.create_new() {
125 flags |= libc::O_EXCL;
126 }
127 Ok(flags)
128 }
129
130 pub fn open<'a, F: Backend + ?Sized>(
132 &self,
133 filesystem: &'a mut F,
134 path: impl AsRef<Path>,
135 ) -> io::Result<VfFileHandle<'a, F>> {
136 let file = filesystem
137 .open_raw_impl(path.as_ref(), self.flags()?, self.mode)
138 .map_err(io_error)?;
139 Ok(VfFileHandle {
140 filesystem,
141 file: Some(file),
142 })
143 }
144}
145
146pub struct VfFileHandle<'a, F: Backend + ?Sized> {
151 filesystem: &'a mut F,
152 file: Option<VfFile>,
153}
154
155impl<F: Backend + ?Sized> VfFileHandle<'_, F> {
156 pub fn descriptor(&self) -> &VfFile {
159 self.file.as_ref().expect("open handle has a descriptor")
160 }
161
162 pub fn try_descriptor(&self) -> io::Result<&VfFile> {
163 self.file
164 .as_ref()
165 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "file handle is closed"))
166 }
167
168 pub fn try_close(&mut self) -> io::Result<()> {
171 let Some(file) = self.file.as_ref() else {
172 return Ok(());
173 };
174 self.filesystem.close_impl(file).map_err(io_error)?;
175 self.file = None;
176 Ok(())
177 }
178
179 pub fn close(mut self) -> io::Result<()> {
181 self.try_close()
182 }
183}
184
185impl<F: Backend + ?Sized> Read for VfFileHandle<'_, F> {
186 fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
187 if buffer.is_empty() {
188 return Ok(0);
189 }
190 let file = self.try_descriptor()?.clone();
191 let requests = [ReadOp::new(file, VfOffset::Cur, buffer.len())];
192 let mut results = self.filesystem.vread_impl(&requests).map_err(io_error)?;
193 validate_read_results("Read::read", &requests, &results).map_err(io_error)?;
194 let result = results.pop().expect("validated one read result");
195 buffer[..result.data.len()].copy_from_slice(&result.data);
196 Ok(result.data.len())
197 }
198}
199
200impl<F: Backend + ?Sized> Write for VfFileHandle<'_, F> {
201 fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
202 if buffer.is_empty() {
203 return Ok(0);
204 }
205 let file = self.try_descriptor()?.clone();
206 let requests = [crate::WriteOpRef::new(&file, VfOffset::Cur, buffer)];
207 let mut results = self.filesystem.vwrite_impl(&requests).map_err(io_error)?;
208 validate_write_results("Write::write", &requests, &results).map_err(io_error)?;
209 Ok(results.pop().expect("validated one write result").written)
210 }
211
212 fn flush(&mut self) -> io::Result<()> {
213 let file = self.try_descriptor()?.clone();
214 self.filesystem.sync_data(&file).map_err(io_error)
215 }
216}
217
218impl<F: Backend + ?Sized> Seek for VfFileHandle<'_, F> {
219 fn seek(&mut self, position: IoSeekFrom) -> io::Result<u64> {
220 let file = self.try_descriptor()?.clone();
221 let (offset, whence) = match position {
222 IoSeekFrom::Start(offset) => (
223 i64::try_from(offset).map_err(|_| {
224 io::Error::new(io::ErrorKind::InvalidInput, "seek offset exceeds i64")
225 })?,
226 SeekFrom::Set,
227 ),
228 IoSeekFrom::End(offset) => (offset, SeekFrom::End),
229 IoSeekFrom::Current(offset) => (offset, SeekFrom::Cur),
230 };
231 let result = self
232 .filesystem
233 .seek_raw_impl(&file, offset, whence)
234 .map_err(io_error)?;
235 u64::try_from(result)
236 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "negative seek result"))
237 }
238}
239
240impl<F: Backend + ?Sized> Drop for VfFileHandle<'_, F> {
241 fn drop(&mut self) {
242 if let Some(file) = self.file.take() {
243 let _ = self.filesystem.close_impl(&file);
244 }
245 }
246}
247
248#[cfg(test)]
249mod option_layout_tests {
250 use super::*;
251 #[test]
252 fn packed_open_flags_match_all_access_and_creation_combinations() {
253 assert_eq!(std::mem::size_of::<VfOpenFlags>(), 1);
254 assert_eq!(std::mem::size_of::<VfOpenOptions>(), 8);
255 for bits in 0..64 {
256 let [read, write, append, truncate, create, exclusive] =
257 [0, 1, 2, 3, 4, 5].map(|bit| bits & (1 << bit) != 0);
258 let mut options = VfOpenOptions::new();
259 options
260 .read(read)
261 .write(write)
262 .append(append)
263 .truncate(truncate)
264 .create(create)
265 .create_new(exclusive);
266 let writable = write || append;
267 let invalid = (!read && !writable) || ((truncate || create || exclusive) && !writable);
268 let result = options.flags();
269 if invalid {
270 assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidInput);
271 continue;
272 }
273 let mut expected = match (read, writable) {
274 (true, true) => libc::O_RDWR,
275 (false, true) => libc::O_WRONLY,
276 _ => libc::O_RDONLY,
277 };
278 if append {
279 expected |= libc::O_APPEND;
280 }
281 if truncate {
282 expected |= libc::O_TRUNC;
283 }
284 if create || exclusive {
285 expected |= libc::O_CREAT;
286 }
287 if exclusive {
288 expected |= libc::O_EXCL;
289 }
290 assert_eq!(result.unwrap(), expected);
291 options
292 .read(true)
293 .append(false)
294 .truncate(false)
295 .create(false)
296 .create_new(false);
297 assert_eq!(
298 options.flags().unwrap()
299 & (libc::O_APPEND | libc::O_TRUNC | libc::O_CREAT | libc::O_EXCL),
300 0
301 );
302 }
303 }
304}