1use crate::traits::{validate_read_into_results, validate_read_results, validate_write_results};
7use crate::*;
8
9pub trait FileSystem {
11 fn take_notifications(&mut self) -> Vec<Box<dyn FnOnce() + Send>> {
14 Vec::new()
15 }
16 fn capabilities(&self) -> Capabilities;
17 fn read_file(&mut self, file: &VfFile, max_bytes: usize) -> VfResult<Vec<u8>> {
20 let mut output = Vec::new();
21 loop {
22 let remaining = max_bytes.saturating_sub(output.len());
23 let request = ReadOp::new(
24 file.clone(),
25 VfOffset::At(output.len() as u64),
26 remaining.clamp(1, 1024 * 1024),
27 );
28 let result = self.read_one(&request)?;
29 validate_read_results(
30 "read_file",
31 std::slice::from_ref(&request),
32 std::slice::from_ref(&result),
33 )?;
34 if result.data.len() > remaining {
35 return Err(VfError::client(0, libc::EFBIG as u32));
36 }
37 if result.data.is_empty() && !result.eof {
38 return Err(VfError::client(0, ERR_IO));
39 }
40 output.extend_from_slice(&result.data);
41 if result.eof {
42 return Ok(output);
43 }
44 }
45 }
46 fn open_one(&mut self, request: &OpenRequest) -> VfResult<VfFile>;
47 fn close_one(&mut self, file: &VfFile) -> VfResult<()>;
48 fn close_deferred(&mut self, file: &VfFile) -> VfResult<()> {
52 self.close_one(file)
53 }
54 fn sync_data(&mut self, file: &VfFile) -> VfResult<()>;
55 fn sync_all(&mut self, file: &VfFile) -> VfResult<()>;
56 fn read_one(&mut self, request: &ReadOp) -> VfResult<ReadResult>;
57 fn read_one_into(&mut self, request: &ReadOp, buffer: &mut [u8]) -> VfResult<ReadIntoResult> {
59 if request.length != buffer.len() {
60 return Err(VfError::client(0, ERR_INVAL));
61 }
62 let result = self.read_one(request)?;
63 if result.data.len() > buffer.len() {
64 return Err(VfError::client(0, ERR_IO));
65 }
66 validate_read_results(
67 "read_one_into",
68 std::slice::from_ref(request),
69 std::slice::from_ref(&result),
70 )?;
71 buffer[..result.data.len()].copy_from_slice(&result.data);
72 Ok(ReadIntoResult {
73 file: result.file,
74 offset: result.offset,
75 read: result.data.len(),
76 eof: result.eof,
77 })
78 }
79 fn write_one(&mut self, request: WriteOpRef<'_>) -> VfResult<WriteResult>;
80 fn seek_one(&mut self, file: &VfFile, position: std::io::SeekFrom) -> VfResult<u64>;
81 fn metadata(&mut self, query: MetadataQuery) -> VfResult<VfAttrs>;
82 fn set_attributes(&mut self, update: SetAttributes) -> VfResult<()>;
83}
84
85pub trait MetadataFileSystem: FileSystem {
87 fn metadata_path(&mut self, path: &std::path::Path, follow: bool) -> VfResult<Metadata>;
88 fn set_metadata_path(
89 &mut self,
90 path: &std::path::Path,
91 update: MetadataUpdate,
92 follow: bool,
93 ) -> VfResult<()>;
94}
95
96pub trait DirectoryFileSystem: FileSystem {
98 fn create_dir_one(&mut self, path: &std::path::Path, mode: u32) -> VfResult<()>;
99 fn read_dir_one(
100 &mut self,
101 path: &std::path::Path,
102 options: ReadDirOptions,
103 ) -> VfResult<Vec<DirEntry>>;
104
105 fn read_dir_page(
108 &mut self,
109 path: &std::path::Path,
110 cursor: Option<DirPageCursor>,
111 page_size: usize,
112 max_entries: usize,
113 ) -> VfResult<(Vec<DirEntry>, Option<DirPageCursor>)>;
114
115 fn read_dir_page_with_fields(
118 &mut self,
119 path: &std::path::Path,
120 fields: AttrMask,
121 cursor: Option<DirPageCursor>,
122 page_size: usize,
123 max_entries: usize,
124 ) -> VfResult<(Vec<DirEntry>, Option<DirPageCursor>)> {
125 let _ = fields;
126 self.read_dir_page(path, cursor, page_size, max_entries)
127 }
128}
129
130pub trait NamespaceFileSystem: FileSystem {
132 fn remove_one(&mut self, path: &std::path::Path, recursive: bool) -> VfResult<()>;
133 fn remove_dir_contents(&mut self, path: &std::path::Path) -> VfResult<()>;
135 fn rename_one(&mut self, from: &std::path::Path, to: &std::path::Path) -> VfResult<()>;
136}
137
138pub trait LinkFileSystem: FileSystem {
140 fn symlink_one(&mut self, target: &std::path::Path, link: &std::path::Path) -> VfResult<()>;
141 fn hard_link_one(&mut self, source: &std::path::Path, link: &std::path::Path) -> VfResult<()>;
142 fn read_link_one(&mut self, path: &std::path::Path) -> VfResult<std::path::PathBuf>;
143}
144
145pub trait CopyFileSystem: FileSystem {
147 fn copy_one(&mut self, source: &std::path::Path, destination: &std::path::Path)
148 -> VfResult<()>;
149}
150
151pub trait NativeFileSystem:
153 FileSystem
154 + MetadataFileSystem
155 + DirectoryFileSystem
156 + NamespaceFileSystem
157 + LinkFileSystem
158 + CopyFileSystem
159{
160}
161
162impl<T> NativeFileSystem for T where
163 T: FileSystem
164 + MetadataFileSystem
165 + DirectoryFileSystem
166 + NamespaceFileSystem
167 + LinkFileSystem
168 + CopyFileSystem
169 + ?Sized
170{
171}
172
173pub trait VectorFileSystem: FileSystem {
177 #[doc(hidden)]
184 fn open_many(&mut self, requests: &[OpenRequest]) -> VfResult<Vec<VfFile>>;
185
186 #[doc(hidden)]
188 fn close_many(&mut self, files: &[VfFile]) -> VfResult<()>;
189
190 #[doc(hidden)]
192 fn read_many(&mut self, requests: &[ReadOp]) -> VfResult<Vec<ReadResult>>;
193
194 fn read_many_into(
197 &mut self,
198 requests: &[ReadOp],
199 buffers: &mut [&mut [u8]],
200 ) -> VfResult<Vec<ReadIntoResult>> {
201 if requests.len() != buffers.len() {
202 return Err(VfError::client(0, ERR_INVAL));
203 }
204 for (index, (request, buffer)) in requests.iter().zip(buffers.iter()).enumerate() {
205 if request.length != buffer.len() {
206 return Err(VfError::client(index, ERR_INVAL));
207 }
208 }
209 let results = self.read_many(requests)?;
210 validate_read_results("read_many_into", requests, &results)?;
211 Ok(results
212 .into_iter()
213 .zip(buffers.iter_mut())
214 .map(|(result, buffer)| {
215 buffer[..result.data.len()].copy_from_slice(&result.data);
216 ReadIntoResult {
217 file: result.file,
218 offset: result.offset,
219 read: result.data.len(),
220 eof: result.eof,
221 }
222 })
223 .collect())
224 }
225
226 #[doc(hidden)]
228 fn write_many(&mut self, requests: &[WriteOpRef<'_>]) -> VfResult<Vec<WriteResult>>;
229}
230
231pub(crate) fn metadata_mask() -> AttrMask {
232 AttrMask::MODE
233 | AttrMask::SIZE
234 | AttrMask::NLINK
235 | AttrMask::FILEID
236 | AttrMask::UID
237 | AttrMask::GID
238 | AttrMask::ATIME
239 | AttrMask::MTIME
240 | AttrMask::CTIME
241 | AttrMask::CHANGE
242}
243
244impl<T: VecFs + ?Sized> FileSystem for T {
245 fn take_notifications(&mut self) -> Vec<Box<dyn FnOnce() + Send>> {
246 VecFs::take_notifications(self)
247 }
248 fn capabilities(&self) -> Capabilities {
249 self.typed_capabilities()
250 }
251
252 fn read_file(&mut self, file: &VfFile, max_bytes: usize) -> VfResult<Vec<u8>> {
253 self.read_allv_with_options(
254 std::slice::from_ref(file),
255 ReadAllOptions::new().max_total_bytes(max_bytes),
256 )
257 .and_then(|mut results| {
258 if results.len() != 1 {
259 return Err(VfError::transport(
260 None,
261 "read_file backend returned an invalid result count",
262 ));
263 }
264 let data = results.pop().expect("validated result count");
265 if data.len() > max_bytes {
266 return Err(VfError::client(0, libc::EFBIG as u32));
267 }
268 Ok(data)
269 })
270 }
271
272 fn open_one(&mut self, request: &OpenRequest) -> VfResult<VfFile> {
273 self.open(
274 request.path.as_path(),
275 vfsi_core::open_flags_to_libc(request.flags)?,
276 request.mode,
277 )
278 .map_err(|error| error.with_context("open", &request.path))
279 }
280
281 fn close_one(&mut self, file: &VfFile) -> VfResult<()> {
282 self.close(file)
283 }
284 fn close_deferred(&mut self, file: &VfFile) -> VfResult<()> {
285 VecFs::close_deferred(self, file)
286 }
287 fn sync_data(&mut self, file: &VfFile) -> VfResult<()> {
288 VecFs::sync_data(self, file)
289 }
290 fn sync_all(&mut self, file: &VfFile) -> VfResult<()> {
291 VecFs::sync_all(self, file)
292 }
293
294 fn read_one(&mut self, request: &ReadOp) -> VfResult<ReadResult> {
295 let mut results = self.readv(std::slice::from_ref(request))?;
296 validate_read_results("read_one", std::slice::from_ref(request), &results)?;
297 Ok(results.pop().expect("validated one result"))
298 }
299
300 fn read_one_into(&mut self, request: &ReadOp, buffer: &mut [u8]) -> VfResult<ReadIntoResult> {
301 let mut results = self.readv_into(std::slice::from_ref(request), &mut [buffer])?;
302 validate_read_into_results("read_one_into", std::slice::from_ref(request), &results)?;
303 Ok(results.pop().expect("validated one result"))
304 }
305
306 fn write_one(&mut self, request: WriteOpRef<'_>) -> VfResult<WriteResult> {
307 let mut results = self.writev_borrowed(std::slice::from_ref(&request))?;
308 validate_write_results("write_one", std::slice::from_ref(&request), &results)?;
309 Ok(results.pop().expect("validated one result"))
310 }
311
312 fn seek_one(&mut self, file: &VfFile, position: std::io::SeekFrom) -> VfResult<u64> {
313 let (offset, whence) = match position {
314 std::io::SeekFrom::Start(offset) => (
315 i64::try_from(offset).map_err(|_| VfError::failure(0, libc::EOVERFLOW as u32))?,
316 SeekFrom::Set,
317 ),
318 std::io::SeekFrom::End(offset) => (offset, SeekFrom::End),
319 std::io::SeekFrom::Current(offset) => (offset, SeekFrom::Cur),
320 };
321 u64::try_from(self.fseek(file, offset, whence)?).map_err(|_| VfError::failure(0, ERR_INVAL))
322 }
323
324 fn metadata(&mut self, query: MetadataQuery) -> VfResult<VfAttrs> {
325 let path = query.file.path().map(std::path::Path::to_path_buf);
326 let mut attrs = VfAttrs {
327 file: query.file,
328 masks: query.attributes,
329 ..VfAttrs::default()
330 };
331 let result = if query.follow_symlinks {
332 self.getattrsv(std::slice::from_mut(&mut attrs))
333 } else {
334 self.lgetattrsv(std::slice::from_mut(&mut attrs))
335 };
336 result.map_err(|error| match path {
337 Some(path) => error.with_context("metadata", path),
338 None => error,
339 })?;
340 Ok(attrs)
341 }
342
343 fn set_attributes(&mut self, update: SetAttributes) -> VfResult<()> {
344 let follow = update.follow_symlinks;
345 let attrs = update.into_legacy();
346 let path = attrs.file.path().map(std::path::Path::to_path_buf);
347 let result = if follow {
348 self.setattrsv(&[attrs])
349 } else {
350 self.lsetattrsv(&[attrs])
351 };
352 result.map_err(|error| match path {
353 Some(path) => error.with_context("set_attributes", path),
354 None => error,
355 })
356 }
357}
358
359impl<T: VecFs + ?Sized> MetadataFileSystem for T {
360 fn metadata_path(&mut self, path: &std::path::Path, follow: bool) -> VfResult<Metadata> {
361 let mut attributes = VfAttrs {
362 file: VfFile::from_os_path(path),
363 masks: metadata_mask(),
364 ..VfAttrs::default()
365 };
366 let result = if follow {
367 self.getattrsv(std::slice::from_mut(&mut attributes))
368 } else {
369 self.lgetattrsv(std::slice::from_mut(&mut attributes))
370 };
371 result
372 .map_err(|error| error.with_context("metadata", path))
373 .map(|()| vfsi_core::metadata_from_attrs(attributes))
374 }
375
376 fn set_metadata_path(
377 &mut self,
378 path: &std::path::Path,
379 update: MetadataUpdate,
380 follow: bool,
381 ) -> VfResult<()> {
382 let mut attributes = SetAttributes::new(VfFile::from_os_path(path));
383 attributes.follow_symlinks = follow;
384 attributes.mode = update.permissions.map(Permissions::mode);
385 attributes.size = update.len;
386 attributes.atime = update.accessed.map(system_time_parts).transpose()?;
387 attributes.mtime = update.modified.map(system_time_parts).transpose()?;
388 self.set_attributes(attributes)
389 .map_err(|error| error.with_context("set_metadata", path))
390 }
391}
392
393fn system_time_parts(time: std::time::SystemTime) -> VfResult<(i64, u32)> {
394 match time.duration_since(std::time::UNIX_EPOCH) {
395 Ok(duration) => Ok((
396 i64::try_from(duration.as_secs())
397 .map_err(|_| VfError::client(0, libc::EOVERFLOW as u32))?,
398 duration.subsec_nanos(),
399 )),
400 Err(error) => {
401 let duration = error.duration();
402 let seconds = i64::try_from(duration.as_secs())
403 .map_err(|_| VfError::client(0, libc::EOVERFLOW as u32))?;
404 if duration.subsec_nanos() == 0 {
405 Ok((-seconds, 0))
406 } else {
407 let seconds = seconds
408 .checked_add(1)
409 .ok_or_else(|| VfError::client(0, libc::EOVERFLOW as u32))?;
410 Ok((-seconds, 1_000_000_000 - duration.subsec_nanos()))
411 }
412 }
413 }
414}
415
416impl<T: VecFs + ?Sized> DirectoryFileSystem for T {
417 fn create_dir_one(&mut self, path: &std::path::Path, mode: u32) -> VfResult<()> {
418 self.mkdir(path, mode)
419 .map_err(|error| error.with_context("create_dir", path))
420 }
421
422 fn read_dir_one(
423 &mut self,
424 path: &std::path::Path,
425 options: ReadDirOptions,
426 ) -> VfResult<Vec<DirEntry>> {
427 let requested = options.entry_limit().saturating_add(1);
428 let entries = self
429 .listdir(path, metadata_mask(), requested, false)
430 .map_err(|error| error.with_context("read_dir", path))?;
431 if entries.len() > options.entry_limit() {
432 return Err(VfError::failure(options.entry_limit(), libc::EFBIG as u32)
433 .with_context("read_dir", path));
434 }
435 let mut path_bytes = 0usize;
436 entries
437 .into_iter()
438 .enumerate()
439 .map(|(index, attributes)| {
440 let entry_path = attributes
441 .file
442 .path()
443 .map(std::path::Path::to_path_buf)
444 .ok_or_else(|| VfError::client(index, ERR_IO).with_context("read_dir", path))?;
445 path_bytes = path_bytes
446 .checked_add(entry_path.as_os_str().len())
447 .ok_or_else(|| {
448 VfError::failure(index, libc::EFBIG as u32).with_context("read_dir", path)
449 })?;
450 if path_bytes > options.path_byte_limit() {
451 return Err(
452 VfError::failure(index, libc::EFBIG as u32).with_context("read_dir", path)
453 );
454 }
455 Ok(DirEntry::new(
456 entry_path,
457 vfsi_core::metadata_from_attrs(attributes),
458 ))
459 })
460 .collect()
461 }
462
463 fn read_dir_page(
464 &mut self,
465 path: &std::path::Path,
466 cursor: Option<DirPageCursor>,
467 page_size: usize,
468 max_entries: usize,
469 ) -> VfResult<(Vec<DirEntry>, Option<DirPageCursor>)> {
470 self.read_dir_page_with_fields(path, metadata_mask(), cursor, page_size, max_entries)
471 }
472
473 fn read_dir_page_with_fields(
474 &mut self,
475 path: &std::path::Path,
476 fields: AttrMask,
477 cursor: Option<DirPageCursor>,
478 page_size: usize,
479 max_entries: usize,
480 ) -> VfResult<(Vec<DirEntry>, Option<DirPageCursor>)> {
481 let (attributes, next) = self
482 .listdir_page(
483 path,
484 fields | AttrMask::MODE,
485 cursor,
486 page_size,
487 max_entries,
488 )
489 .map_err(|error| error.with_context("visit_dir", path))?;
490 if attributes.len() > page_size {
491 return Err(VfError::transport(
492 None,
493 "listdir_page returned more entries than requested",
494 ));
495 }
496 let entries = attributes
497 .into_iter()
498 .enumerate()
499 .map(|(index, attributes)| {
500 let entry_path = attributes
501 .file
502 .path()
503 .map(std::path::Path::to_path_buf)
504 .ok_or_else(|| {
505 VfError::client(index, ERR_IO).with_context("visit_dir", path)
506 })?;
507 Ok(DirEntry::new(
508 entry_path,
509 vfsi_core::metadata_from_attrs(attributes),
510 ))
511 })
512 .collect::<VfResult<Vec<_>>>()?;
513 Ok((entries, next))
514 }
515}
516
517impl<T: VecFs + ?Sized> NamespaceFileSystem for T {
518 fn remove_one(&mut self, path: &std::path::Path, recursive: bool) -> VfResult<()> {
519 self.rm(&[path], recursive)
520 .map_err(|error| error.with_context("remove", path))
521 }
522
523 fn remove_dir_contents(&mut self, path: &std::path::Path) -> VfResult<()> {
524 self.rm_contents(path)
525 .map_err(|error| error.with_context("remove_dir_contents", path))
526 }
527
528 fn rename_one(&mut self, from: &std::path::Path, to: &std::path::Path) -> VfResult<()> {
529 self.renamev(&[(VfFile::from_os_path(from), VfFile::from_os_path(to))])
530 .map_err(|error| error.with_context("rename", from))
531 }
532}
533
534impl<T: VecFs + ?Sized> LinkFileSystem for T {
535 fn symlink_one(&mut self, target: &std::path::Path, link: &std::path::Path) -> VfResult<()> {
536 self.symlink(target, link)
537 .map_err(|error| error.with_context("symlink", link))
538 }
539
540 fn hard_link_one(&mut self, source: &std::path::Path, link: &std::path::Path) -> VfResult<()> {
541 self.hardlinkv(&[source], &[link])
542 .map_err(|error| error.with_context("hard_link", link))
543 }
544
545 fn read_link_one(&mut self, path: &std::path::Path) -> VfResult<std::path::PathBuf> {
546 self.readlink(path)
547 .map(bytes_to_path)
548 .map_err(|error| error.with_context("read_link", path))
549 }
550}
551
552#[cfg(unix)]
553fn bytes_to_path(bytes: Vec<u8>) -> std::path::PathBuf {
554 use std::os::unix::ffi::OsStringExt;
555 std::ffi::OsString::from_vec(bytes).into()
556}
557
558#[cfg(not(unix))]
559fn bytes_to_path(bytes: Vec<u8>) -> std::path::PathBuf {
560 String::from_utf8_lossy(&bytes).into_owned().into()
561}
562
563impl<T: VecFs + ?Sized> CopyFileSystem for T {
564 fn copy_one(
565 &mut self,
566 source: &std::path::Path,
567 destination: &std::path::Path,
568 ) -> VfResult<()> {
569 self.copyv(&[ExtentPair::from_os_paths(source, 0, destination, 0, None)])
570 .map_err(|error| error.with_context("copy", source))
571 }
572}
573
574impl<T: VecFs + ?Sized> VectorFileSystem for T {
575 fn open_many(&mut self, requests: &[OpenRequest]) -> VfResult<Vec<VfFile>> {
576 let paths: Vec<&std::path::Path> = requests
577 .iter()
578 .map(|request| request.path.as_path())
579 .collect();
580 let flags = translate_open_flags(requests)?;
581 let modes: Vec<u32> = requests.iter().map(|request| request.mode).collect();
582 VecFs::openv(self, &paths, &flags, &modes)
583 }
584
585 fn close_many(&mut self, files: &[VfFile]) -> VfResult<()> {
586 self.closev(files)
587 }
588
589 fn read_many(&mut self, requests: &[ReadOp]) -> VfResult<Vec<ReadResult>> {
590 self.readv(requests)
591 }
592
593 fn read_many_into(
594 &mut self,
595 requests: &[ReadOp],
596 buffers: &mut [&mut [u8]],
597 ) -> VfResult<Vec<ReadIntoResult>> {
598 self.readv_into(requests, buffers)
599 }
600
601 fn write_many(&mut self, requests: &[WriteOpRef<'_>]) -> VfResult<Vec<WriteResult>> {
602 self.writev_borrowed(requests)
603 }
604}
605
606fn translate_open_flags(requests: &[OpenRequest]) -> VfResult<Vec<i32>> {
607 requests
608 .iter()
609 .enumerate()
610 .map(|(index, request)| {
611 vfsi_core::open_flags_to_libc(request.flags).map_err(|error| error.with_index(index))
612 })
613 .collect()
614}
615
616#[cfg(test)]
617mod tests {
618 use super::*;
619
620 #[test]
621 fn invalid_open_flags_retain_their_request_index() {
622 let error = translate_open_flags(&[
623 OpenRequest::new("/valid", OpenFlags::READ),
624 OpenRequest::new("/invalid", OpenFlags::empty()),
625 ])
626 .unwrap_err();
627 assert_eq!(error.index(), Some(1));
628 }
629}