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