cap_primitives/fs/
file.rs

1use std::io;
2
3/// Unix-specific extensions to [`fs::File`].
4#[cfg(unix)]
5pub trait FileExt {
6    /// Reads a number of bytes starting from a given offset.
7    fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
8
9    /// Like `read_at`, except that it reads into a slice of buffers.
10    #[cfg(unix_file_vectored_at)]
11    fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
12        default_read_vectored(|b| self.read_at(b, offset), bufs)
13    }
14
15    /// Reads the exact number of bytes required to fill `buf` from the given offset.
16    fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
17        while !buf.is_empty() {
18            match self.read_at(buf, offset) {
19                Ok(0) => break,
20                Ok(n) => {
21                    let tmp = buf;
22                    buf = &mut tmp[n..];
23                    offset += n as u64;
24                }
25                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
26                Err(e) => return Err(e),
27            }
28        }
29        if !buf.is_empty() {
30            Err(io::Error::new(
31                io::ErrorKind::UnexpectedEof,
32                "failed to fill whole buffer",
33            ))
34        } else {
35            Ok(())
36        }
37    }
38
39    /// Writes a number of bytes starting from a given offset.
40    fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
41
42    /// Like `write_at`, except that it writes from a slice of buffers.
43    #[cfg(unix_file_vectored_at)]
44    fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
45        default_write_vectored(|b| self.write_at(b, offset), bufs)
46    }
47
48    /// Attempts to write an entire buffer starting from a given offset.
49    fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
50        while !buf.is_empty() {
51            match self.write_at(buf, offset) {
52                Ok(0) => {
53                    return Err(io::Error::new(
54                        io::ErrorKind::WriteZero,
55                        "failed to write whole buffer",
56                    ));
57                }
58                Ok(n) => {
59                    buf = &buf[n..];
60                    offset += n as u64
61                }
62                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
63                Err(e) => return Err(e),
64            }
65        }
66        Ok(())
67    }
68}
69
70#[cfg(unix_file_vectored_at)]
71fn default_read_vectored<F>(read: F, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize>
72where
73    F: FnOnce(&mut [u8]) -> io::Result<usize>,
74{
75    let buf = bufs
76        .iter_mut()
77        .find(|b| !b.is_empty())
78        .map_or(&mut [][..], |b| &mut **b);
79    read(buf)
80}
81
82#[cfg(unix_file_vectored_at)]
83fn default_write_vectored<F>(write: F, bufs: &[io::IoSlice<'_>]) -> io::Result<usize>
84where
85    F: FnOnce(&[u8]) -> io::Result<usize>,
86{
87    let buf = bufs
88        .iter()
89        .find(|b| !b.is_empty())
90        .map_or(&[][..], |b| &**b);
91    write(buf)
92}
93
94/// WASI-specific extensions to [`fs::File`].
95#[cfg(target_os = "wasi")]
96pub trait FileExt {
97    /// Reads a number of bytes starting from a given offset.
98    fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
99        let bufs = &mut [io::IoSliceMut::new(buf)];
100        self.read_vectored_at(bufs, offset)
101    }
102
103    /// Reads a number of bytes starting from a given offset.
104    fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
105
106    /// Reads the exact number of byte required to fill `buf` from the given offset.
107    fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
108        while !buf.is_empty() {
109            match self.read_at(buf, offset) {
110                Ok(0) => break,
111                Ok(n) => {
112                    let tmp = buf;
113                    buf = &mut tmp[n..];
114                    offset += n as u64;
115                }
116                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
117                Err(e) => return Err(e),
118            }
119        }
120        if !buf.is_empty() {
121            Err(io::Error::new(
122                io::ErrorKind::UnexpectedEof,
123                "failed to fill whole buffer",
124            ))
125        } else {
126            Ok(())
127        }
128    }
129
130    /// Writes a number of bytes starting from a given offset.
131    fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
132        let bufs = &[io::IoSlice::new(buf)];
133        self.write_vectored_at(bufs, offset)
134    }
135
136    /// Writes a number of bytes starting from a given offset.
137    fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize>;
138
139    /// Attempts to write an entire buffer starting from a given offset.
140    fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
141        while !buf.is_empty() {
142            match self.write_at(buf, offset) {
143                Ok(0) => {
144                    return Err(io::Error::new(
145                        io::ErrorKind::WriteZero,
146                        "failed to write whole buffer",
147                    ));
148                }
149                Ok(n) => {
150                    buf = &buf[n..];
151                    offset += n as u64
152                }
153                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
154                Err(e) => return Err(e),
155            }
156        }
157        Ok(())
158    }
159
160    /// Returns the current position within the file.
161    fn tell(&self) -> io::Result<u64>;
162
163    /// Adjust the flags associated with this file.
164    fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>;
165
166    /// Adjust the rights associated with this file.
167    fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
168
169    /// Provide file advisory information on a file descriptor.
170    fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
171
172    /// Force the allocation of space in a file.
173    fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
174
175    /// Create a directory.
176    fn create_directory<P: AsRef<std::path::Path>>(&self, dir: P) -> io::Result<()>;
177
178    /// Read the contents of a symbolic link.
179    fn read_link<P: AsRef<std::path::Path>>(&self, path: P) -> io::Result<std::path::PathBuf>;
180
181    /// Return the attributes of a file or directory.
182    fn metadata_at<P: AsRef<std::path::Path>>(
183        &self,
184        lookup_flags: u32,
185        path: P,
186    ) -> io::Result<std::fs::Metadata>;
187
188    /// Unlink a file.
189    fn remove_file<P: AsRef<std::path::Path>>(&self, path: P) -> io::Result<()>;
190
191    /// Remove a directory.
192    fn remove_directory<P: AsRef<std::path::Path>>(&self, path: P) -> io::Result<()>;
193}
194
195/// Windows-specific extensions to [`fs::File`].
196#[cfg(windows)]
197pub trait FileExt {
198    /// Seeks to a given position and reads a number of bytes.
199    fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
200
201    /// Seeks to a given position and writes a number of bytes.
202    fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
203}