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