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::{ReadOp, SeekFrom, VecFs, 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#[derive(Clone, Debug)]
22pub struct VfOpenOptions {
23 read: bool,
24 write: bool,
25 append: bool,
26 truncate: bool,
27 create: bool,
28 create_new: bool,
29 mode: u32,
30}
31
32impl Default for VfOpenOptions {
33 fn default() -> Self {
34 Self {
35 read: false,
36 write: false,
37 append: false,
38 truncate: false,
39 create: false,
40 create_new: false,
41 mode: 0o666,
42 }
43 }
44}
45
46impl VfOpenOptions {
47 pub fn new() -> Self {
48 Self::default()
49 }
50
51 pub fn read(&mut self, value: bool) -> &mut Self {
52 self.read = value;
53 self
54 }
55
56 pub fn write(&mut self, value: bool) -> &mut Self {
57 self.write = value;
58 self
59 }
60
61 pub fn append(&mut self, value: bool) -> &mut Self {
62 self.append = value;
63 self
64 }
65
66 pub fn truncate(&mut self, value: bool) -> &mut Self {
67 self.truncate = value;
68 self
69 }
70
71 pub fn create(&mut self, value: bool) -> &mut Self {
72 self.create = value;
73 self
74 }
75
76 pub fn create_new(&mut self, value: bool) -> &mut Self {
77 self.create_new = value;
78 self
79 }
80
81 pub fn mode(&mut self, mode: u32) -> &mut Self {
83 self.mode = mode;
84 self
85 }
86
87 fn flags(&self) -> io::Result<i32> {
88 let writable = self.write || self.append;
89 let mut flags = match (self.read, writable) {
90 (true, true) => libc::O_RDWR,
91 (false, true) => libc::O_WRONLY,
92 (true, false) => libc::O_RDONLY,
93 (false, false) => {
94 return Err(io::Error::new(
95 io::ErrorKind::InvalidInput,
96 "at least one of read, write, or append must be enabled",
97 ));
98 }
99 };
100 if self.truncate && !writable {
101 return Err(io::Error::new(
102 io::ErrorKind::InvalidInput,
103 "truncate requires write or append access",
104 ));
105 }
106 if (self.create || self.create_new) && !writable {
107 return Err(io::Error::new(
108 io::ErrorKind::InvalidInput,
109 "create requires write or append access",
110 ));
111 }
112 if self.append {
113 flags |= libc::O_APPEND;
114 }
115 if self.truncate {
116 flags |= libc::O_TRUNC;
117 }
118 if self.create || self.create_new {
119 flags |= libc::O_CREAT;
120 }
121 if self.create_new {
122 flags |= libc::O_EXCL;
123 }
124 Ok(flags)
125 }
126
127 pub fn open<'a, F: VecFs + ?Sized>(
129 &self,
130 filesystem: &'a mut F,
131 path: impl AsRef<Path>,
132 ) -> io::Result<VfFileHandle<'a, F>> {
133 let file = filesystem
134 .open(path.as_ref(), self.flags()?, self.mode)
135 .map_err(io_error)?;
136 Ok(VfFileHandle {
137 filesystem,
138 file: Some(file),
139 })
140 }
141}
142
143pub struct VfFileHandle<'a, F: VecFs + ?Sized> {
148 filesystem: &'a mut F,
149 file: Option<VfFile>,
150}
151
152impl<F: VecFs + ?Sized> VfFileHandle<'_, F> {
153 pub fn descriptor(&self) -> &VfFile {
156 self.file.as_ref().expect("open handle has a descriptor")
157 }
158
159 pub fn try_descriptor(&self) -> io::Result<&VfFile> {
160 self.file
161 .as_ref()
162 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "file handle is closed"))
163 }
164
165 pub fn try_close(&mut self) -> io::Result<()> {
168 let Some(file) = self.file.as_ref() else {
169 return Ok(());
170 };
171 self.filesystem.close(file).map_err(io_error)?;
172 self.file = None;
173 Ok(())
174 }
175
176 pub fn close(mut self) -> io::Result<()> {
178 self.try_close()
179 }
180}
181
182impl<F: VecFs + ?Sized> Read for VfFileHandle<'_, F> {
183 fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
184 if buffer.is_empty() {
185 return Ok(0);
186 }
187 let file = self.try_descriptor()?.clone();
188 let requests = [ReadOp::new(file, VfOffset::Cur, buffer.len())];
189 let mut results = self.filesystem.readv(&requests).map_err(io_error)?;
190 validate_read_results("Read::read", &requests, &results).map_err(io_error)?;
191 let result = results.pop().expect("validated one read result");
192 buffer[..result.data.len()].copy_from_slice(&result.data);
193 Ok(result.data.len())
194 }
195}
196
197impl<F: VecFs + ?Sized> Write for VfFileHandle<'_, F> {
198 fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
199 if buffer.is_empty() {
200 return Ok(0);
201 }
202 let file = self.try_descriptor()?.clone();
203 let requests = [crate::WriteOpRef::new(&file, VfOffset::Cur, buffer)];
204 let mut results = self
205 .filesystem
206 .writev_borrowed(&requests)
207 .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: VecFs + ?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 .fseek(&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: VecFs + ?Sized> Drop for VfFileHandle<'_, F> {
241 fn drop(&mut self) {
242 if let Some(file) = self.file.take() {
243 let _ = self.filesystem.close(&file);
244 }
245 }
246}