1use crate::traits::{validate_read_into_results, validate_read_results, validate_write_results};
7use crate::*;
8
9pub trait FileSystem {
11 fn capabilities(&self) -> Capabilities;
12 fn open_one(&mut self, request: &OpenRequest) -> VfResult<VfFile>;
13 fn close_one(&mut self, file: &VfFile) -> VfResult<()>;
14 fn sync_data(&mut self, file: &VfFile) -> VfResult<()>;
15 fn sync_all(&mut self, file: &VfFile) -> VfResult<()>;
16 fn read_one(&mut self, request: &ReadOp) -> VfResult<ReadResult>;
17 fn read_one_into(&mut self, request: &ReadOp, buffer: &mut [u8]) -> VfResult<ReadIntoResult> {
19 if request.length != buffer.len() {
20 return Err(VfError::client(0, ERR_INVAL));
21 }
22 let result = self.read_one(request)?;
23 if result.data.len() > buffer.len() {
24 return Err(VfError::client(0, ERR_IO));
25 }
26 validate_read_results(
27 "read_one_into",
28 std::slice::from_ref(request),
29 std::slice::from_ref(&result),
30 )?;
31 buffer[..result.data.len()].copy_from_slice(&result.data);
32 Ok(ReadIntoResult {
33 file: result.file,
34 offset: result.offset,
35 read: result.data.len(),
36 eof: result.eof,
37 })
38 }
39 fn write_one(&mut self, request: WriteOpRef<'_>) -> VfResult<WriteResult>;
40 fn seek_one(&mut self, file: &VfFile, position: std::io::SeekFrom) -> VfResult<u64>;
41 fn metadata(&mut self, query: MetadataQuery) -> VfResult<VfAttrs>;
42 fn set_attributes(&mut self, update: SetAttributes) -> VfResult<()>;
43}
44
45pub trait MetadataFileSystem: FileSystem {
47 fn metadata_path(&mut self, path: &std::path::Path, follow: bool) -> VfResult<Metadata>;
48 fn set_metadata_path(
49 &mut self,
50 path: &std::path::Path,
51 update: MetadataUpdate,
52 follow: bool,
53 ) -> VfResult<()>;
54}
55
56pub trait DirectoryFileSystem: FileSystem {
58 fn create_dir_one(&mut self, path: &std::path::Path, mode: u32) -> VfResult<()>;
59 fn read_dir_one(
60 &mut self,
61 path: &std::path::Path,
62 options: ReadDirOptions,
63 ) -> VfResult<Vec<DirEntry>>;
64}
65
66pub trait NamespaceFileSystem: FileSystem {
68 fn remove_one(&mut self, path: &std::path::Path, recursive: bool) -> VfResult<()>;
69 fn remove_dir_contents(&mut self, path: &std::path::Path) -> VfResult<()>;
71 fn rename_one(&mut self, from: &std::path::Path, to: &std::path::Path) -> VfResult<()>;
72}
73
74pub trait LinkFileSystem: FileSystem {
76 fn symlink_one(&mut self, target: &std::path::Path, link: &std::path::Path) -> VfResult<()>;
77 fn hard_link_one(&mut self, source: &std::path::Path, link: &std::path::Path) -> VfResult<()>;
78 fn read_link_one(&mut self, path: &std::path::Path) -> VfResult<std::path::PathBuf>;
79}
80
81pub trait CopyFileSystem: FileSystem {
83 fn copy_one(&mut self, source: &std::path::Path, destination: &std::path::Path)
84 -> VfResult<()>;
85}
86
87pub trait NativeFileSystem:
89 FileSystem
90 + MetadataFileSystem
91 + DirectoryFileSystem
92 + NamespaceFileSystem
93 + LinkFileSystem
94 + CopyFileSystem
95{
96}
97
98impl<T> NativeFileSystem for T where
99 T: FileSystem
100 + MetadataFileSystem
101 + DirectoryFileSystem
102 + NamespaceFileSystem
103 + LinkFileSystem
104 + CopyFileSystem
105 + ?Sized
106{
107}
108
109pub trait VectorFileSystem: FileSystem {
113 #[doc(hidden)]
120 fn open_many(&mut self, requests: &[OpenRequest]) -> VfResult<Vec<VfFile>>;
121
122 #[doc(hidden)]
124 fn close_many(&mut self, files: &[VfFile]) -> VfResult<()>;
125
126 #[doc(hidden)]
128 fn read_many(&mut self, requests: &[ReadOp]) -> VfResult<Vec<ReadResult>>;
129
130 fn read_many_into(
133 &mut self,
134 requests: &[ReadOp],
135 buffers: &mut [&mut [u8]],
136 ) -> VfResult<Vec<ReadIntoResult>> {
137 if requests.len() != buffers.len() {
138 return Err(VfError::client(0, ERR_INVAL));
139 }
140 for (index, (request, buffer)) in requests.iter().zip(buffers.iter()).enumerate() {
141 if request.length != buffer.len() {
142 return Err(VfError::client(index, ERR_INVAL));
143 }
144 }
145 let results = self.read_many(requests)?;
146 validate_read_results("read_many_into", requests, &results)?;
147 Ok(results
148 .into_iter()
149 .zip(buffers.iter_mut())
150 .map(|(result, buffer)| {
151 buffer[..result.data.len()].copy_from_slice(&result.data);
152 ReadIntoResult {
153 file: result.file,
154 offset: result.offset,
155 read: result.data.len(),
156 eof: result.eof,
157 }
158 })
159 .collect())
160 }
161
162 #[doc(hidden)]
164 fn write_many(&mut self, requests: &[WriteOpRef<'_>]) -> VfResult<Vec<WriteResult>>;
165}
166
167fn metadata_mask() -> AttrMask {
168 AttrMask::MODE
169 | AttrMask::SIZE
170 | AttrMask::NLINK
171 | AttrMask::FILEID
172 | AttrMask::UID
173 | AttrMask::GID
174 | AttrMask::ATIME
175 | AttrMask::MTIME
176 | AttrMask::CTIME
177 | AttrMask::CHANGE
178}
179
180impl<T: VecFs + ?Sized> FileSystem for T {
181 fn capabilities(&self) -> Capabilities {
182 self.typed_capabilities()
183 }
184
185 fn open_one(&mut self, request: &OpenRequest) -> VfResult<VfFile> {
186 self.open(
187 request.path.as_path(),
188 request.flags.to_libc()?,
189 request.mode,
190 )
191 .map_err(|error| error.with_context("open", &request.path))
192 }
193
194 fn close_one(&mut self, file: &VfFile) -> VfResult<()> {
195 self.close(file)
196 }
197 fn sync_data(&mut self, file: &VfFile) -> VfResult<()> {
198 VecFs::sync_data(self, file)
199 }
200 fn sync_all(&mut self, file: &VfFile) -> VfResult<()> {
201 VecFs::sync_all(self, file)
202 }
203
204 fn read_one(&mut self, request: &ReadOp) -> VfResult<ReadResult> {
205 let mut results = self.readv(std::slice::from_ref(request))?;
206 validate_read_results("read_one", std::slice::from_ref(request), &results)?;
207 Ok(results.pop().expect("validated one result"))
208 }
209
210 fn read_one_into(&mut self, request: &ReadOp, buffer: &mut [u8]) -> VfResult<ReadIntoResult> {
211 let mut results = self.readv_into(std::slice::from_ref(request), &mut [buffer])?;
212 validate_read_into_results("read_one_into", std::slice::from_ref(request), &results)?;
213 Ok(results.pop().expect("validated one result"))
214 }
215
216 fn write_one(&mut self, request: WriteOpRef<'_>) -> VfResult<WriteResult> {
217 let mut results = self.writev_borrowed(std::slice::from_ref(&request))?;
218 validate_write_results("write_one", std::slice::from_ref(&request), &results)?;
219 Ok(results.pop().expect("validated one result"))
220 }
221
222 fn seek_one(&mut self, file: &VfFile, position: std::io::SeekFrom) -> VfResult<u64> {
223 let (offset, whence) = match position {
224 std::io::SeekFrom::Start(offset) => (
225 i64::try_from(offset).map_err(|_| VfError::failure(0, libc::EOVERFLOW as u32))?,
226 SeekFrom::Set,
227 ),
228 std::io::SeekFrom::End(offset) => (offset, SeekFrom::End),
229 std::io::SeekFrom::Current(offset) => (offset, SeekFrom::Cur),
230 };
231 u64::try_from(self.fseek(file, offset, whence)?).map_err(|_| VfError::failure(0, ERR_INVAL))
232 }
233
234 fn metadata(&mut self, query: MetadataQuery) -> VfResult<VfAttrs> {
235 let path = query.file.path().map(std::path::Path::to_path_buf);
236 let mut attrs = VfAttrs {
237 file: query.file,
238 masks: query.attributes,
239 ..VfAttrs::default()
240 };
241 let result = if query.follow_symlinks {
242 self.getattrsv(std::slice::from_mut(&mut attrs))
243 } else {
244 self.lgetattrsv(std::slice::from_mut(&mut attrs))
245 };
246 result.map_err(|error| match path {
247 Some(path) => error.with_context("metadata", path),
248 None => error,
249 })?;
250 Ok(attrs)
251 }
252
253 fn set_attributes(&mut self, update: SetAttributes) -> VfResult<()> {
254 let follow = update.follow_symlinks;
255 let attrs = update.into_legacy();
256 let path = attrs.file.path().map(std::path::Path::to_path_buf);
257 let result = if follow {
258 self.setattrsv(&[attrs])
259 } else {
260 self.lsetattrsv(&[attrs])
261 };
262 result.map_err(|error| match path {
263 Some(path) => error.with_context("set_attributes", path),
264 None => error,
265 })
266 }
267}
268
269impl<T: VecFs + ?Sized> MetadataFileSystem for T {
270 fn metadata_path(&mut self, path: &std::path::Path, follow: bool) -> VfResult<Metadata> {
271 let mut attributes = VfAttrs {
272 file: VfFile::from_os_path(path),
273 masks: metadata_mask(),
274 ..VfAttrs::default()
275 };
276 let result = if follow {
277 self.getattrsv(std::slice::from_mut(&mut attributes))
278 } else {
279 self.lgetattrsv(std::slice::from_mut(&mut attributes))
280 };
281 result
282 .map_err(|error| error.with_context("metadata", path))
283 .map(|()| attributes.into())
284 }
285
286 fn set_metadata_path(
287 &mut self,
288 path: &std::path::Path,
289 update: MetadataUpdate,
290 follow: bool,
291 ) -> VfResult<()> {
292 let mut attributes = SetAttributes::new(VfFile::from_os_path(path));
293 attributes.follow_symlinks = follow;
294 attributes.mode = update.permissions.map(Permissions::mode);
295 attributes.size = update.len;
296 attributes.atime = update.accessed.map(system_time_parts).transpose()?;
297 attributes.mtime = update.modified.map(system_time_parts).transpose()?;
298 self.set_attributes(attributes)
299 .map_err(|error| error.with_context("set_metadata", path))
300 }
301}
302
303fn system_time_parts(time: std::time::SystemTime) -> VfResult<(i64, u32)> {
304 match time.duration_since(std::time::UNIX_EPOCH) {
305 Ok(duration) => Ok((
306 i64::try_from(duration.as_secs())
307 .map_err(|_| VfError::client(0, libc::EOVERFLOW as u32))?,
308 duration.subsec_nanos(),
309 )),
310 Err(error) => {
311 let duration = error.duration();
312 let seconds = i64::try_from(duration.as_secs())
313 .map_err(|_| VfError::client(0, libc::EOVERFLOW as u32))?;
314 if duration.subsec_nanos() == 0 {
315 Ok((-seconds, 0))
316 } else {
317 let seconds = seconds
318 .checked_add(1)
319 .ok_or_else(|| VfError::client(0, libc::EOVERFLOW as u32))?;
320 Ok((-seconds, 1_000_000_000 - duration.subsec_nanos()))
321 }
322 }
323 }
324}
325
326impl<T: VecFs + ?Sized> DirectoryFileSystem for T {
327 fn create_dir_one(&mut self, path: &std::path::Path, mode: u32) -> VfResult<()> {
328 self.mkdir(path, mode)
329 .map_err(|error| error.with_context("create_dir", path))
330 }
331
332 fn read_dir_one(
333 &mut self,
334 path: &std::path::Path,
335 options: ReadDirOptions,
336 ) -> VfResult<Vec<DirEntry>> {
337 let requested = options.entry_limit().saturating_add(1);
338 let entries = self
339 .listdir(path, metadata_mask(), requested, false)
340 .map_err(|error| error.with_context("read_dir", path))?;
341 if entries.len() > options.entry_limit() {
342 return Err(VfError::failure(options.entry_limit(), libc::EFBIG as u32)
343 .with_context("read_dir", path));
344 }
345 let mut path_bytes = 0usize;
346 entries
347 .into_iter()
348 .enumerate()
349 .map(|(index, attributes)| {
350 let entry_path = attributes
351 .file
352 .path()
353 .map(std::path::Path::to_path_buf)
354 .ok_or_else(|| VfError::client(index, ERR_IO).with_context("read_dir", path))?;
355 path_bytes = path_bytes
356 .checked_add(entry_path.as_os_str().len())
357 .ok_or_else(|| {
358 VfError::failure(index, libc::EFBIG as u32).with_context("read_dir", path)
359 })?;
360 if path_bytes > options.path_byte_limit() {
361 return Err(
362 VfError::failure(index, libc::EFBIG as u32).with_context("read_dir", path)
363 );
364 }
365 Ok(DirEntry::new(entry_path, attributes.into()))
366 })
367 .collect()
368 }
369}
370
371impl<T: VecFs + ?Sized> NamespaceFileSystem for T {
372 fn remove_one(&mut self, path: &std::path::Path, recursive: bool) -> VfResult<()> {
373 self.rm(&[path], recursive)
374 .map_err(|error| error.with_context("remove", path))
375 }
376
377 fn remove_dir_contents(&mut self, path: &std::path::Path) -> VfResult<()> {
378 self.rm_contents(path)
379 .map_err(|error| error.with_context("remove_dir_contents", path))
380 }
381
382 fn rename_one(&mut self, from: &std::path::Path, to: &std::path::Path) -> VfResult<()> {
383 self.renamev(&[(VfFile::from_os_path(from), VfFile::from_os_path(to))])
384 .map_err(|error| error.with_context("rename", from))
385 }
386}
387
388impl<T: VecFs + ?Sized> LinkFileSystem for T {
389 fn symlink_one(&mut self, target: &std::path::Path, link: &std::path::Path) -> VfResult<()> {
390 self.symlink(target, link)
391 .map_err(|error| error.with_context("symlink", link))
392 }
393
394 fn hard_link_one(&mut self, source: &std::path::Path, link: &std::path::Path) -> VfResult<()> {
395 self.hardlinkv(&[source], &[link])
396 .map_err(|error| error.with_context("hard_link", link))
397 }
398
399 fn read_link_one(&mut self, path: &std::path::Path) -> VfResult<std::path::PathBuf> {
400 self.readlink(path)
401 .map(bytes_to_path)
402 .map_err(|error| error.with_context("read_link", path))
403 }
404}
405
406#[cfg(unix)]
407fn bytes_to_path(bytes: Vec<u8>) -> std::path::PathBuf {
408 use std::os::unix::ffi::OsStringExt;
409 std::ffi::OsString::from_vec(bytes).into()
410}
411
412#[cfg(not(unix))]
413fn bytes_to_path(bytes: Vec<u8>) -> std::path::PathBuf {
414 String::from_utf8_lossy(&bytes).into_owned().into()
415}
416
417impl<T: VecFs + ?Sized> CopyFileSystem for T {
418 fn copy_one(
419 &mut self,
420 source: &std::path::Path,
421 destination: &std::path::Path,
422 ) -> VfResult<()> {
423 self.copyv(&[ExtentPair::from_os_paths(source, 0, destination, 0, None)])
424 .map_err(|error| error.with_context("copy", source))
425 }
426}
427
428impl<T: VecFs + ?Sized> VectorFileSystem for T {
429 fn open_many(&mut self, requests: &[OpenRequest]) -> VfResult<Vec<VfFile>> {
430 let paths: Vec<&std::path::Path> = requests
431 .iter()
432 .map(|request| request.path.as_path())
433 .collect();
434 let flags = translate_open_flags(requests)?;
435 let modes: Vec<u32> = requests.iter().map(|request| request.mode).collect();
436 VecFs::openv(self, &paths, &flags, &modes)
437 }
438
439 fn close_many(&mut self, files: &[VfFile]) -> VfResult<()> {
440 self.closev(files)
441 }
442
443 fn read_many(&mut self, requests: &[ReadOp]) -> VfResult<Vec<ReadResult>> {
444 self.readv(requests)
445 }
446
447 fn read_many_into(
448 &mut self,
449 requests: &[ReadOp],
450 buffers: &mut [&mut [u8]],
451 ) -> VfResult<Vec<ReadIntoResult>> {
452 self.readv_into(requests, buffers)
453 }
454
455 fn write_many(&mut self, requests: &[WriteOpRef<'_>]) -> VfResult<Vec<WriteResult>> {
456 self.writev_borrowed(requests)
457 }
458}
459
460fn translate_open_flags(requests: &[OpenRequest]) -> VfResult<Vec<i32>> {
461 requests
462 .iter()
463 .enumerate()
464 .map(|(index, request)| {
465 request
466 .flags
467 .to_libc()
468 .map_err(|error| error.with_index(index))
469 })
470 .collect()
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476
477 #[test]
478 fn invalid_open_flags_retain_their_request_index() {
479 let error = translate_open_flags(&[
480 OpenRequest::new("/valid", OpenFlags::READ),
481 OpenRequest::new("/invalid", OpenFlags::empty()),
482 ])
483 .unwrap_err();
484 assert_eq!(error.index_opt(), Some(1));
485 }
486}