Skip to main content

vfsi_sync/
client.rs

1//! Owned, shareable synchronous client and file handles.
2
3use std::fmt;
4use std::io::{self, Read, Seek, SeekFrom as IoSeekFrom, Write};
5use std::path::{Component, Path, PathBuf};
6use std::sync::{Arc, Mutex};
7use std::time::SystemTime;
8
9use crate::traits::{validate_read_results, validate_write_results};
10use crate::{
11    AttrMask, Capabilities, CopyFileSystem, DEFAULT_READ_MAX_BYTES, DirEntry, DirectoryFileSystem,
12    FileSystem, LinkFileSystem, Metadata, MetadataFileSystem, MetadataQuery, MetadataUpdate,
13    NamespaceFileSystem, OpenFlags, OpenRequest, Permissions, ReadDirOptions, ReadOp, ReadResult,
14    SetAttributes, VectorFileSystem, VfError, VfFile, VfOffset, VfResult, WriteOpRef, WriteResult,
15};
16
17fn io_error(error: VfError) -> io::Error {
18    error.into()
19}
20
21fn poisoned() -> VfError {
22    VfError::client(0, crate::ERR_IO)
23}
24
25fn wrong_result_count(operation: &str, expected: usize, actual: usize) -> VfError {
26    VfError::transport(
27        None,
28        format!("{operation} backend returned {actual} results for {expected} requests"),
29    )
30}
31
32/// Cloneable owner of one synchronous backend connection.
33pub struct FsClient<F> {
34    inner: Arc<Mutex<F>>,
35}
36
37impl<F> Clone for FsClient<F> {
38    fn clone(&self) -> Self {
39        Self {
40            inner: Arc::clone(&self.inner),
41        }
42    }
43}
44
45impl<F> fmt::Debug for FsClient<F> {
46    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
47        formatter.debug_struct("FsClient").finish_non_exhaustive()
48    }
49}
50
51impl<F> FsClient<F> {
52    pub fn new(filesystem: F) -> Self {
53        Self {
54            inner: Arc::new(Mutex::new(filesystem)),
55        }
56    }
57
58    fn lock(&self) -> VfResult<std::sync::MutexGuard<'_, F>> {
59        self.inner.lock().map_err(|_| poisoned())
60    }
61
62    pub fn into_inner(self) -> Result<F, Self> {
63        match Arc::try_unwrap(self.inner) {
64            Ok(mutex) => match mutex.into_inner() {
65                Ok(filesystem) => Ok(filesystem),
66                Err(poisoned) => Ok(poisoned.into_inner()),
67            },
68            Err(inner) => Err(Self { inner }),
69        }
70    }
71}
72
73impl<F: FileSystem> FsClient<F> {
74    /// Open a path read-only.
75    pub fn open(&self, path: impl AsRef<Path>) -> VfResult<FsFile<F>> {
76        self.open_with(OpenRequest::new(path.as_ref(), OpenFlags::READ))
77    }
78
79    pub fn open_with(&self, request: OpenRequest) -> VfResult<FsFile<F>> {
80        let file = self.lock()?.open_one(&request)?;
81        Ok(FsFile {
82            inner: Arc::clone(&self.inner),
83            file: Some(file),
84            path: request.path,
85        })
86    }
87
88    pub fn open_options(&self) -> OpenOptions<'_, F> {
89        OpenOptions::new(self)
90    }
91
92    pub fn create(&self, path: impl AsRef<Path>) -> VfResult<FsFile<F>> {
93        self.open_with(OpenRequest::new(
94            path.as_ref(),
95            OpenFlags::WRITE | OpenFlags::CREATE | OpenFlags::TRUNCATE,
96        ))
97    }
98
99    pub fn read(&self, path: impl AsRef<Path>) -> VfResult<Vec<u8>> {
100        self.read_with_limit(path, DEFAULT_READ_MAX_BYTES)
101    }
102
103    /// Read one complete file while limiting the returned allocation.
104    ///
105    /// Use [`FsFile::read_native`] or [`std::io::Read`] to stream files that
106    /// should not be held in one allocation.
107    pub fn read_with_limit(&self, path: impl AsRef<Path>, max_bytes: usize) -> VfResult<Vec<u8>> {
108        let path = path.as_ref();
109        let mut file = self.open(path)?;
110        let mut output = Vec::new();
111        let mut chunk = vec![0; 64 * 1024];
112        loop {
113            let remaining = max_bytes.saturating_sub(output.len());
114            let request = if remaining == 0 {
115                1
116            } else {
117                remaining.min(chunk.len())
118            };
119            let read = file.read_native(&mut chunk[..request])?;
120            if read == 0 {
121                break;
122            }
123            if read > remaining {
124                return Err(VfError::failure(0, libc::EFBIG as u32).with_context("read", path));
125            }
126            output.extend_from_slice(&chunk[..read]);
127        }
128        file.close()?;
129        Ok(output)
130    }
131
132    pub fn read_to_string(&self, path: impl AsRef<Path>) -> VfResult<String> {
133        self.read_to_string_with_limit(path, DEFAULT_READ_MAX_BYTES)
134    }
135
136    /// Read one complete UTF-8 file with a caller-selected allocation limit.
137    pub fn read_to_string_with_limit(
138        &self,
139        path: impl AsRef<Path>,
140        max_bytes: usize,
141    ) -> VfResult<String> {
142        let path = path.as_ref();
143        String::from_utf8(self.read_with_limit(path, max_bytes)?)
144            .map_err(|_| VfError::client(0, crate::ERR_INVAL).with_context("read_to_string", path))
145    }
146
147    pub fn write(&self, path: impl AsRef<Path>, data: &[u8]) -> VfResult<()> {
148        let path = path.as_ref();
149        let mut file = self.create(path)?;
150        let mut written = 0;
151        while written < data.len() {
152            let count = file.write_native(&data[written..])?;
153            if count == 0 {
154                return Err(VfError::client(0, crate::ERR_IO).with_context("write", path));
155            }
156            written += count;
157        }
158        file.close()
159    }
160}
161
162impl<F: MetadataFileSystem> FsClient<F> {
163    pub fn metadata(&self, path: impl AsRef<Path>) -> VfResult<Metadata> {
164        self.lock()?.metadata_path(path.as_ref(), true)
165    }
166
167    pub fn symlink_metadata(&self, path: impl AsRef<Path>) -> VfResult<Metadata> {
168        self.lock()?.metadata_path(path.as_ref(), false)
169    }
170
171    pub fn set_metadata(&self, path: impl AsRef<Path>) -> SetMetadata<'_, F> {
172        SetMetadata {
173            client: self,
174            path: path.as_ref().to_path_buf(),
175            update: MetadataUpdate::new(),
176            follow: true,
177        }
178    }
179}
180
181impl<F: DirectoryFileSystem> FsClient<F> {
182    pub fn create_dir(&self, path: impl AsRef<Path>) -> VfResult<()> {
183        self.create_dir_with_mode(path, 0o777)
184    }
185
186    pub fn create_dir_with_mode(&self, path: impl AsRef<Path>, mode: u32) -> VfResult<()> {
187        self.lock()?.create_dir_one(path.as_ref(), mode)
188    }
189
190    pub fn read_dir(&self, path: impl AsRef<Path>) -> VfResult<Vec<DirEntry>> {
191        self.read_dir_with_options(path, ReadDirOptions::default())
192    }
193
194    /// Read one directory with explicit entry and path-storage limits.
195    pub fn read_dir_with_options(
196        &self,
197        path: impl AsRef<Path>,
198        options: ReadDirOptions,
199    ) -> VfResult<Vec<DirEntry>> {
200        self.lock()?.read_dir_one(path.as_ref(), options)
201    }
202}
203
204impl<F: DirectoryFileSystem + MetadataFileSystem> FsClient<F> {
205    pub fn create_dir_all(&self, path: impl AsRef<Path>) -> VfResult<()> {
206        let path = path.as_ref();
207        let mut current = PathBuf::new();
208        for component in path.components() {
209            match component {
210                Component::RootDir => {
211                    current.push(Path::new("/"));
212                    continue;
213                }
214                Component::Normal(part) => current.push(part),
215                Component::CurDir => continue,
216                Component::ParentDir => {
217                    // Preserve `..` so the backend resolves it relative to
218                    // its own rooted namespace. Lexically popping `/` would
219                    // accidentally turn a later absolute component into a
220                    // current-directory-relative path.
221                    current.push("..");
222                    continue;
223                }
224                Component::Prefix(_) => return Err(VfError::client(0, crate::ERR_INVAL)),
225            }
226            match self.create_dir(&current) {
227                Ok(()) => {}
228                Err(error) if error.err_no() == crate::ERR_EXIST => {
229                    if !self.metadata(&current)?.is_dir() {
230                        return Err(VfError::client(0, crate::ERR_NOTDIR)
231                            .with_context("create_dir_all", &current));
232                    }
233                }
234                Err(error) => return Err(error),
235            }
236        }
237        Ok(())
238    }
239}
240
241impl<F: NamespaceFileSystem + MetadataFileSystem> FsClient<F> {
242    fn removal_metadata(&self, path: &Path, operation: &'static str) -> VfResult<Metadata> {
243        let mut filesystem = self.lock()?;
244        let follow = !filesystem.capabilities().contains(Capabilities::LSTAT);
245        filesystem
246            .metadata_path(path, follow)
247            .map_err(|error| error.with_context(operation, path))
248    }
249
250    pub fn remove_file(&self, path: impl AsRef<Path>) -> VfResult<()> {
251        let path = path.as_ref();
252        if self.removal_metadata(path, "remove_file")?.is_dir() {
253            return Err(VfError::client(0, crate::ERR_ISDIR).with_context("remove_file", path));
254        }
255        self.lock()?.remove_one(path, false)
256    }
257
258    pub fn remove_dir(&self, path: impl AsRef<Path>) -> VfResult<()> {
259        let path = path.as_ref();
260        if !self.removal_metadata(path, "remove_dir")?.is_dir() {
261            return Err(VfError::client(0, crate::ERR_NOTDIR).with_context("remove_dir", path));
262        }
263        self.lock()?.remove_one(path, false)
264    }
265
266    pub fn remove_dir_all(&self, path: impl AsRef<Path>) -> VfResult<()> {
267        let path = path.as_ref();
268        if !self.removal_metadata(path, "remove_dir_all")?.is_dir() {
269            return Err(VfError::client(0, crate::ERR_NOTDIR).with_context("remove_dir_all", path));
270        }
271        self.lock()?.remove_one(path, true)
272    }
273
274    pub fn rename(&self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> VfResult<()> {
275        self.lock()?.rename_one(from.as_ref(), to.as_ref())
276    }
277}
278
279impl<F: LinkFileSystem> FsClient<F> {
280    pub fn symlink(&self, target: impl AsRef<Path>, link: impl AsRef<Path>) -> VfResult<()> {
281        self.lock()?.symlink_one(target.as_ref(), link.as_ref())
282    }
283
284    pub fn hard_link(&self, source: impl AsRef<Path>, link: impl AsRef<Path>) -> VfResult<()> {
285        self.lock()?.hard_link_one(source.as_ref(), link.as_ref())
286    }
287
288    pub fn read_link(&self, path: impl AsRef<Path>) -> VfResult<PathBuf> {
289        self.lock()?.read_link_one(path.as_ref())
290    }
291}
292
293impl<F: CopyFileSystem> FsClient<F> {
294    pub fn copy(&self, source: impl AsRef<Path>, destination: impl AsRef<Path>) -> VfResult<()> {
295        self.lock()?.copy_one(source.as_ref(), destination.as_ref())
296    }
297}
298
299impl<F: VectorFileSystem> FsClient<F> {
300    /// Open an ordered vector of files.
301    ///
302    /// Success returns one RAII handle per request. Failure returns no
303    /// handles; VFSI does not promise transactional rollback of other
304    /// filesystem effects such as file creation.
305    pub fn openv(&self, requests: &[OpenRequest]) -> VfResult<Vec<FsFile<F>>> {
306        let mut filesystem = self.lock()?;
307        let files = filesystem.open_many(requests).map_err(|error| {
308            error
309                .index_opt()
310                .and_then(|index| requests.get(index))
311                .map_or(error.clone(), |request| {
312                    error.with_context("openv", &request.path)
313                })
314        })?;
315        if files.len() != requests.len() {
316            let error = wrong_result_count("openv", requests.len(), files.len());
317            for file in &files {
318                let _ = filesystem.close_one(file);
319            }
320            return Err(error);
321        }
322        drop(filesystem);
323        Ok(files
324            .into_iter()
325            .zip(requests)
326            .map(|(file, request)| FsFile {
327                inner: Arc::clone(&self.inner),
328                file: Some(file),
329                path: request.path.clone(),
330            })
331            .collect())
332    }
333
334    /// Close a group of files through one vector operation.
335    ///
336    /// On failure, each handle remains armed for best-effort cleanup on drop;
337    /// explicitly closed prefix handles may consequently receive a harmless
338    /// second close attempt.
339    pub fn closev(&self, mut files: Vec<FsFile<F>>) -> VfResult<()> {
340        for (index, file) in files.iter().enumerate() {
341            self.validate_owner(file, index)?;
342        }
343        let descriptors: Vec<VfFile> = files.iter().map(|file| file.raw().clone()).collect();
344        self.lock()?.close_many(&descriptors).map_err(|error| {
345            error
346                .index_opt()
347                .and_then(|index| files.get(index))
348                .map_or(error.clone(), |file| {
349                    error.with_context("closev", file.path())
350                })
351        })?;
352        for file in &mut files {
353            file.file = None;
354        }
355        Ok(())
356    }
357
358    pub fn readv(&self, requests: &[FsRead<'_, F>]) -> VfResult<Vec<ReadResult>> {
359        let reads = self.read_ops(requests)?;
360        let results = self.lock()?.read_many(&reads).map_err(|error| {
361            error
362                .index_opt()
363                .and_then(|index| requests.get(index))
364                .map_or(error.clone(), |request| {
365                    error.with_context("readv", request.file.path())
366                })
367        })?;
368        if results.len() != requests.len() {
369            return Err(wrong_result_count("readv", requests.len(), results.len()));
370        }
371        validate_read_results("readv", &reads, &results).map_err(|error| {
372            error
373                .index_opt()
374                .and_then(|index| requests.get(index))
375                .map_or(error.clone(), |request| {
376                    error.with_context("readv", request.file.path())
377                })
378        })?;
379        Ok(results)
380    }
381
382    fn read_ops(&self, requests: &[FsRead<'_, F>]) -> VfResult<Vec<ReadOp>> {
383        let mut reads = Vec::with_capacity(requests.len());
384        for (index, request) in requests.iter().enumerate() {
385            self.validate_owner(request.file, index)?;
386            reads.push(ReadOp::new(
387                request.file.raw().clone(),
388                request.offset,
389                request.length,
390            ));
391        }
392        Ok(reads)
393    }
394
395    pub fn readv_into(&self, requests: &mut [FsReadInto<'_, F>]) -> VfResult<Vec<usize>> {
396        let mut reads = Vec::with_capacity(requests.len());
397        for (index, request) in requests.iter().enumerate() {
398            self.validate_owner(request.file, index)?;
399            reads.push(ReadOp::new(
400                request.file.raw().clone(),
401                request.offset,
402                request.buffer.len(),
403            ));
404        }
405        let results = self.lock()?.read_many(&reads).map_err(|error| {
406            error
407                .index_opt()
408                .and_then(|index| requests.get(index))
409                .map_or(error.clone(), |request| {
410                    error.with_context("readv_into", request.file.path())
411                })
412        })?;
413        validate_read_results("readv_into", &reads, &results).map_err(|error| {
414            error
415                .index_opt()
416                .and_then(|index| requests.get(index))
417                .map_or(error.clone(), |request| {
418                    error.with_context("readv_into", request.file.path())
419                })
420        })?;
421        let mut lengths = Vec::with_capacity(results.len());
422        for (index, (request, result)) in requests.iter_mut().zip(results).enumerate() {
423            if result.data.len() > request.buffer.len() {
424                return Err(VfError::client(index, crate::ERR_IO)
425                    .with_context("readv_into", request.file.path()));
426            }
427            request.buffer[..result.data.len()].copy_from_slice(&result.data);
428            lengths.push(result.data.len());
429        }
430        Ok(lengths)
431    }
432
433    pub fn writev(&self, requests: &[FsWrite<'_, F>]) -> VfResult<Vec<WriteResult>> {
434        let writes = self.write_ops(requests)?;
435        let results = self.lock()?.write_many(&writes).map_err(|error| {
436            error
437                .index_opt()
438                .and_then(|index| requests.get(index))
439                .map_or(error.clone(), |request| {
440                    error.with_context("writev", request.file.path())
441                })
442        })?;
443        if results.len() != requests.len() {
444            return Err(wrong_result_count("writev", requests.len(), results.len()));
445        }
446        validate_write_results("writev", &writes, &results).map_err(|error| {
447            error
448                .index_opt()
449                .and_then(|index| requests.get(index))
450                .map_or(error.clone(), |request| {
451                    error.with_context("writev", request.file.path())
452                })
453        })?;
454        Ok(results)
455    }
456
457    fn write_ops<'a>(&self, requests: &'a [FsWrite<'a, F>]) -> VfResult<Vec<WriteOpRef<'a>>> {
458        let mut writes = Vec::with_capacity(requests.len());
459        for (index, request) in requests.iter().enumerate() {
460            self.validate_owner(request.file, index)?;
461            writes.push(WriteOpRef::new(
462                request.file.raw(),
463                request.offset,
464                request.data,
465            ));
466        }
467        Ok(writes)
468    }
469
470    fn validate_owner(&self, file: &FsFile<F>, index: usize) -> VfResult<()> {
471        if Arc::ptr_eq(&self.inner, &file.inner) {
472            Ok(())
473        } else {
474            Err(VfError::client(index, crate::ERR_INVAL))
475        }
476    }
477}
478
479/// `std::fs::OpenOptions`-style builder tied to an [`FsClient`].
480pub struct OpenOptions<'a, F: FileSystem> {
481    client: &'a FsClient<F>,
482    flags: OpenFlags,
483    mode: u32,
484}
485
486impl<F: FileSystem> Clone for OpenOptions<'_, F> {
487    fn clone(&self) -> Self {
488        Self {
489            client: self.client,
490            flags: self.flags,
491            mode: self.mode,
492        }
493    }
494}
495
496impl<F: FileSystem> fmt::Debug for OpenOptions<'_, F> {
497    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
498        formatter
499            .debug_struct("OpenOptions")
500            .field("flags", &self.flags)
501            .field("mode", &format_args!("{:#o}", self.mode))
502            .finish()
503    }
504}
505
506impl<'a, F: FileSystem> OpenOptions<'a, F> {
507    fn new(client: &'a FsClient<F>) -> Self {
508        Self {
509            client,
510            flags: OpenFlags::empty(),
511            mode: 0o666,
512        }
513    }
514
515    pub fn read(&mut self, enabled: bool) -> &mut Self {
516        self.flags.set(OpenFlags::READ, enabled);
517        self
518    }
519
520    pub fn write(&mut self, enabled: bool) -> &mut Self {
521        self.flags.set(OpenFlags::WRITE, enabled);
522        self
523    }
524
525    pub fn append(&mut self, enabled: bool) -> &mut Self {
526        self.flags.set(OpenFlags::APPEND, enabled);
527        self
528    }
529
530    pub fn truncate(&mut self, enabled: bool) -> &mut Self {
531        self.flags.set(OpenFlags::TRUNCATE, enabled);
532        self
533    }
534
535    pub fn create(&mut self, enabled: bool) -> &mut Self {
536        self.flags.set(OpenFlags::CREATE, enabled);
537        self
538    }
539
540    pub fn create_new(&mut self, enabled: bool) -> &mut Self {
541        self.flags.set(OpenFlags::CREATE_NEW, enabled);
542        self
543    }
544
545    pub fn mode(&mut self, mode: u32) -> &mut Self {
546        self.mode = mode;
547        self
548    }
549
550    pub fn open(&self, path: impl AsRef<Path>) -> VfResult<FsFile<F>> {
551        self.client
552            .open_with(OpenRequest::new(path.as_ref(), self.flags).mode(self.mode))
553    }
554}
555
556impl<F: VectorFileSystem> OpenOptions<'_, F> {
557    pub fn openv<P: AsRef<Path>>(&self, paths: &[P]) -> VfResult<Vec<FsFile<F>>> {
558        let requests: Vec<OpenRequest> = paths
559            .iter()
560            .map(|path| OpenRequest::new(path.as_ref(), self.flags).mode(self.mode))
561            .collect();
562        self.client.openv(&requests)
563    }
564}
565
566/// Builder for an atomic metadata update request.
567pub struct SetMetadata<'a, F: MetadataFileSystem> {
568    client: &'a FsClient<F>,
569    path: PathBuf,
570    update: MetadataUpdate,
571    follow: bool,
572}
573
574impl<F: MetadataFileSystem> Clone for SetMetadata<'_, F> {
575    fn clone(&self) -> Self {
576        Self {
577            client: self.client,
578            path: self.path.clone(),
579            update: self.update.clone(),
580            follow: self.follow,
581        }
582    }
583}
584
585impl<F: MetadataFileSystem> fmt::Debug for SetMetadata<'_, F> {
586    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
587        formatter
588            .debug_struct("SetMetadata")
589            .field("path", &self.path)
590            .field("update", &self.update)
591            .field("follow", &self.follow)
592            .finish_non_exhaustive()
593    }
594}
595
596impl<F: MetadataFileSystem> SetMetadata<'_, F> {
597    pub fn permissions(&mut self, permissions: Permissions) -> &mut Self {
598        self.update.permissions = Some(permissions);
599        self
600    }
601
602    pub fn len(&mut self, len: u64) -> &mut Self {
603        self.update.len = Some(len);
604        self
605    }
606
607    pub fn accessed(&mut self, accessed: SystemTime) -> &mut Self {
608        self.update.accessed = Some(accessed);
609        self
610    }
611
612    pub fn modified(&mut self, modified: SystemTime) -> &mut Self {
613        self.update.modified = Some(modified);
614        self
615    }
616
617    pub fn follow_symlinks(&mut self, follow: bool) -> &mut Self {
618        self.follow = follow;
619        self
620    }
621
622    pub fn apply(&self) -> VfResult<()> {
623        self.client
624            .lock()?
625            .set_metadata_path(&self.path, self.update.clone(), self.follow)
626    }
627}
628
629/// Owned RAII file which does not borrow the client.
630pub struct FsFile<F: FileSystem> {
631    inner: Arc<Mutex<F>>,
632    file: Option<VfFile>,
633    path: PathBuf,
634}
635
636impl<F: FileSystem> fmt::Debug for FsFile<F> {
637    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
638        formatter
639            .debug_struct("FsFile")
640            .field("path", &self.path)
641            .field("is_open", &self.file.is_some())
642            .finish()
643    }
644}
645
646impl<F: FileSystem> FsFile<F> {
647    fn raw(&self) -> &VfFile {
648        self.file.as_ref().expect("open file")
649    }
650
651    /// Positional read which does not alter the file cursor.
652    pub fn read_at(&self, buffer: &mut [u8], offset: u64) -> VfResult<usize> {
653        self.read_into(buffer, VfOffset::At(offset))
654    }
655
656    /// Positional write which does not alter the file cursor.
657    pub fn write_at(&self, buffer: &[u8], offset: u64) -> VfResult<usize> {
658        self.write_from(buffer, VfOffset::At(offset))
659    }
660
661    pub fn read_request_at(&self, offset: u64, length: usize) -> FsRead<'_, F> {
662        FsRead {
663            file: self,
664            offset: VfOffset::At(offset),
665            length,
666        }
667    }
668
669    pub fn path(&self) -> &Path {
670        &self.path
671    }
672
673    /// Query metadata for the open object without resolving its path again.
674    pub fn metadata(&self) -> VfResult<Metadata> {
675        let attributes = AttrMask::MODE
676            | AttrMask::SIZE
677            | AttrMask::NLINK
678            | AttrMask::FILEID
679            | AttrMask::UID
680            | AttrMask::GID
681            | AttrMask::ATIME
682            | AttrMask::MTIME
683            | AttrMask::CTIME
684            | AttrMask::CHANGE;
685        self.inner
686            .lock()
687            .map_err(|_| poisoned())?
688            .metadata(MetadataQuery::new(self.raw().clone(), attributes))
689            .map(Metadata::from)
690            .map_err(|error| error.with_context("metadata", &self.path))
691    }
692
693    /// Truncate or extend the open file.
694    pub fn set_len(&self, len: u64) -> VfResult<()> {
695        let mut update = SetAttributes::new(self.raw().clone());
696        update.size = Some(len);
697        self.inner
698            .lock()
699            .map_err(|_| poisoned())?
700            .set_attributes(update)
701            .map_err(|error| error.with_context("set_len", &self.path))
702    }
703
704    /// Change permissions on the open file.
705    pub fn set_permissions(&self, permissions: Permissions) -> VfResult<()> {
706        let mut update = SetAttributes::new(self.raw().clone());
707        update.mode = Some(permissions.mode());
708        self.inner
709            .lock()
710            .map_err(|_| poisoned())?
711            .set_attributes(update)
712            .map_err(|error| error.with_context("set_permissions", &self.path))
713    }
714
715    pub fn write_request_at<'a>(&'a self, offset: u64, data: &'a [u8]) -> FsWrite<'a, F> {
716        FsWrite {
717            file: self,
718            offset: VfOffset::At(offset),
719            data,
720        }
721    }
722
723    pub fn read_request_at_into<'a>(
724        &'a self,
725        offset: u64,
726        buffer: &'a mut [u8],
727    ) -> FsReadInto<'a, F> {
728        FsReadInto {
729            file: self,
730            offset: VfOffset::At(offset),
731            buffer,
732        }
733    }
734
735    pub fn read_native(&mut self, buffer: &mut [u8]) -> VfResult<usize> {
736        self.read_into(buffer, VfOffset::Cur)
737    }
738
739    pub fn write_native(&mut self, buffer: &[u8]) -> VfResult<usize> {
740        self.write_from(buffer, VfOffset::Cur)
741    }
742
743    fn read_into(&self, buffer: &mut [u8], offset: VfOffset) -> VfResult<usize> {
744        if buffer.is_empty() {
745            return Ok(0);
746        }
747        let result = self
748            .inner
749            .lock()
750            .map_err(|_| poisoned())?
751            .read_one(&ReadOp::new(self.raw().clone(), offset, buffer.len()))
752            .map_err(|error| error.with_context("read", &self.path))?;
753        if result.data.len() > buffer.len() {
754            return Err(VfError::client(0, crate::ERR_IO).with_context("read", &self.path));
755        }
756        buffer[..result.data.len()].copy_from_slice(&result.data);
757        Ok(result.data.len())
758    }
759
760    fn write_from(&self, buffer: &[u8], offset: VfOffset) -> VfResult<usize> {
761        if buffer.is_empty() {
762            return Ok(0);
763        }
764        let file = self.raw().clone();
765        let result = self
766            .inner
767            .lock()
768            .map_err(|_| poisoned())?
769            .write_one(WriteOpRef::new(&file, offset, buffer))
770            .map_err(|error| error.with_context("write", &self.path))?;
771        if result.written > buffer.len() {
772            return Err(VfError::client(0, crate::ERR_IO).with_context("write", &self.path));
773        }
774        Ok(result.written)
775    }
776
777    pub fn sync_data(&self) -> VfResult<()> {
778        self.inner
779            .lock()
780            .map_err(|_| poisoned())?
781            .sync_data(self.raw())
782            .map_err(|error| error.with_context("sync_data", &self.path))
783    }
784
785    pub fn sync_all(&self) -> VfResult<()> {
786        self.inner
787            .lock()
788            .map_err(|_| poisoned())?
789            .sync_all(self.raw())
790            .map_err(|error| error.with_context("sync_all", &self.path))
791    }
792
793    pub fn close(mut self) -> VfResult<()> {
794        let file = self.file.as_ref().expect("open file");
795        self.inner
796            .lock()
797            .map_err(|_| poisoned())?
798            .close_one(file)
799            .map_err(|error| error.with_context("close", &self.path))?;
800        self.file = None;
801        Ok(())
802    }
803
804    /// Seek while retaining [`VfError`] protocol and path information.
805    pub fn seek_native(&mut self, position: IoSeekFrom) -> VfResult<u64> {
806        self.inner
807            .lock()
808            .map_err(|_| poisoned())?
809            .seek_one(self.raw(), position)
810            .map_err(|error| error.with_context("seek", &self.path))
811    }
812}
813
814impl<F: FileSystem> Read for FsFile<F> {
815    fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
816        self.read_native(buffer).map_err(io_error)
817    }
818}
819
820impl<F: FileSystem> Write for FsFile<F> {
821    fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
822        self.write_native(buffer).map_err(io_error)
823    }
824
825    fn flush(&mut self) -> io::Result<()> {
826        self.sync_data().map_err(io_error)
827    }
828}
829
830impl<F: FileSystem> Seek for FsFile<F> {
831    fn seek(&mut self, position: IoSeekFrom) -> io::Result<u64> {
832        self.seek_native(position).map_err(io_error)
833    }
834}
835
836/// Typed read request for [`FsClient::readv`].
837pub struct FsRead<'a, F: FileSystem> {
838    file: &'a FsFile<F>,
839    offset: VfOffset,
840    length: usize,
841}
842
843/// Typed borrowed write request for [`FsClient::writev`].
844pub struct FsWrite<'a, F: FileSystem> {
845    file: &'a FsFile<F>,
846    offset: VfOffset,
847    data: &'a [u8],
848}
849
850/// Typed vector read into caller-provided storage.
851pub struct FsReadInto<'a, F: FileSystem> {
852    file: &'a FsFile<F>,
853    offset: VfOffset,
854    buffer: &'a mut [u8],
855}
856
857impl<F: FileSystem> Drop for FsFile<F> {
858    fn drop(&mut self) {
859        if let Some(file) = self.file.take()
860            && let Ok(mut filesystem) = self.inner.lock()
861        {
862            let _ = filesystem.close_one(&file);
863        }
864    }
865}