wasi_cap_std_sync/
file.rs

1use cap_fs_ext::MetadataExt;
2use fs_set_times::{SetTimes, SystemTimeSpec};
3use io_lifetimes::AsFilelike;
4use std::any::Any;
5use std::convert::TryInto;
6use std::io::{self, IsTerminal};
7use system_interface::{
8    fs::{FileIoExt, GetSetFdFlags},
9    io::{IoExt, ReadReady},
10};
11use wasi_common::{
12    file::{Advice, FdFlags, FileType, Filestat, WasiFile},
13    Error, ErrorExt,
14};
15
16pub struct File(cap_std::fs::File);
17
18impl File {
19    pub fn from_cap_std(file: cap_std::fs::File) -> Self {
20        File(file)
21    }
22}
23
24#[async_trait::async_trait]
25impl WasiFile for File {
26    fn as_any(&self) -> &dyn Any {
27        self
28    }
29    #[cfg(unix)]
30    fn pollable(&self) -> Option<rustix::fd::BorrowedFd> {
31        Some(self.0.as_fd())
32    }
33    #[cfg(windows)]
34    fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> {
35        Some(self.0.as_raw_handle_or_socket())
36    }
37    async fn datasync(&self) -> Result<(), Error> {
38        self.0.sync_data()?;
39        Ok(())
40    }
41    async fn sync(&self) -> Result<(), Error> {
42        self.0.sync_all()?;
43        Ok(())
44    }
45    async fn get_filetype(&self) -> Result<FileType, Error> {
46        let meta = self.0.metadata()?;
47        Ok(filetype_from(&meta.file_type()))
48    }
49    async fn get_fdflags(&self) -> Result<FdFlags, Error> {
50        let fdflags = get_fd_flags(&self.0)?;
51        Ok(fdflags)
52    }
53    async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
54        if fdflags.intersects(
55            wasi_common::file::FdFlags::DSYNC
56                | wasi_common::file::FdFlags::SYNC
57                | wasi_common::file::FdFlags::RSYNC,
58        ) {
59            return Err(Error::invalid_argument().context("cannot set DSYNC, SYNC, or RSYNC flag"));
60        }
61        let set_fd_flags = self.0.new_set_fd_flags(to_sysif_fdflags(fdflags))?;
62        self.0.set_fd_flags(set_fd_flags)?;
63        Ok(())
64    }
65    async fn get_filestat(&self) -> Result<Filestat, Error> {
66        let meta = self.0.metadata()?;
67        Ok(Filestat {
68            device_id: meta.dev(),
69            inode: meta.ino(),
70            filetype: filetype_from(&meta.file_type()),
71            nlink: meta.nlink(),
72            size: meta.len(),
73            atim: meta.accessed().map(|t| Some(t.into_std())).unwrap_or(None),
74            mtim: meta.modified().map(|t| Some(t.into_std())).unwrap_or(None),
75            ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
76        })
77    }
78    async fn set_filestat_size(&self, size: u64) -> Result<(), Error> {
79        self.0.set_len(size)?;
80        Ok(())
81    }
82    async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
83        self.0.advise(offset, len, convert_advice(advice))?;
84        Ok(())
85    }
86    async fn set_times(
87        &self,
88        atime: Option<wasi_common::SystemTimeSpec>,
89        mtime: Option<wasi_common::SystemTimeSpec>,
90    ) -> Result<(), Error> {
91        self.0
92            .set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
93        Ok(())
94    }
95    async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
96        let n = self.0.read_vectored(bufs)?;
97        Ok(n.try_into()?)
98    }
99    async fn read_vectored_at<'a>(
100        &self,
101        bufs: &mut [io::IoSliceMut<'a>],
102        offset: u64,
103    ) -> Result<u64, Error> {
104        let n = self.0.read_vectored_at(bufs, offset)?;
105        Ok(n.try_into()?)
106    }
107    async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
108        let n = self.0.write_vectored(bufs)?;
109        Ok(n.try_into()?)
110    }
111    async fn write_vectored_at<'a>(
112        &self,
113        bufs: &[io::IoSlice<'a>],
114        offset: u64,
115    ) -> Result<u64, Error> {
116        let n = self.0.write_vectored_at(bufs, offset)?;
117        Ok(n.try_into()?)
118    }
119    async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
120        Ok(self.0.seek(pos)?)
121    }
122    async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
123        let n = self.0.peek(buf)?;
124        Ok(n.try_into()?)
125    }
126    fn num_ready_bytes(&self) -> Result<u64, Error> {
127        Ok(self.0.num_ready_bytes()?)
128    }
129    fn isatty(&self) -> bool {
130        #[cfg(unix)]
131        return self.0.as_fd().is_terminal();
132        #[cfg(windows)]
133        return self.0.as_handle().is_terminal();
134    }
135}
136
137pub fn filetype_from(ft: &cap_std::fs::FileType) -> FileType {
138    use cap_fs_ext::FileTypeExt;
139    if ft.is_dir() {
140        FileType::Directory
141    } else if ft.is_symlink() {
142        FileType::SymbolicLink
143    } else if ft.is_socket() {
144        if ft.is_block_device() {
145            FileType::SocketDgram
146        } else {
147            FileType::SocketStream
148        }
149    } else if ft.is_block_device() {
150        FileType::BlockDevice
151    } else if ft.is_char_device() {
152        FileType::CharacterDevice
153    } else if ft.is_file() {
154        FileType::RegularFile
155    } else {
156        FileType::Unknown
157    }
158}
159
160#[cfg(windows)]
161use io_lifetimes::{AsHandle, BorrowedHandle};
162#[cfg(windows)]
163impl AsHandle for File {
164    fn as_handle(&self) -> BorrowedHandle<'_> {
165        self.0.as_handle()
166    }
167}
168
169#[cfg(windows)]
170use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket};
171#[cfg(windows)]
172impl AsRawHandleOrSocket for File {
173    #[inline]
174    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
175        self.0.as_raw_handle_or_socket()
176    }
177}
178
179#[cfg(unix)]
180use io_lifetimes::{AsFd, BorrowedFd};
181
182#[cfg(unix)]
183impl AsFd for File {
184    fn as_fd(&self) -> BorrowedFd<'_> {
185        self.0.as_fd()
186    }
187}
188
189pub(crate) fn convert_systimespec(
190    t: Option<wasi_common::SystemTimeSpec>,
191) -> Option<SystemTimeSpec> {
192    match t {
193        Some(wasi_common::SystemTimeSpec::Absolute(t)) => {
194            Some(SystemTimeSpec::Absolute(t.into_std()))
195        }
196        Some(wasi_common::SystemTimeSpec::SymbolicNow) => Some(SystemTimeSpec::SymbolicNow),
197        None => None,
198    }
199}
200
201pub(crate) fn to_sysif_fdflags(f: wasi_common::file::FdFlags) -> system_interface::fs::FdFlags {
202    let mut out = system_interface::fs::FdFlags::empty();
203    if f.contains(wasi_common::file::FdFlags::APPEND) {
204        out |= system_interface::fs::FdFlags::APPEND;
205    }
206    if f.contains(wasi_common::file::FdFlags::DSYNC) {
207        out |= system_interface::fs::FdFlags::DSYNC;
208    }
209    if f.contains(wasi_common::file::FdFlags::NONBLOCK) {
210        out |= system_interface::fs::FdFlags::NONBLOCK;
211    }
212    if f.contains(wasi_common::file::FdFlags::RSYNC) {
213        out |= system_interface::fs::FdFlags::RSYNC;
214    }
215    if f.contains(wasi_common::file::FdFlags::SYNC) {
216        out |= system_interface::fs::FdFlags::SYNC;
217    }
218    out
219}
220
221/// Return the file-descriptor flags for a given file-like object.
222///
223/// This returns the flags needed to implement [`WasiFile::get_fdflags`].
224pub fn get_fd_flags<Filelike: AsFilelike>(f: Filelike) -> io::Result<wasi_common::file::FdFlags> {
225    let f = f.as_filelike().get_fd_flags()?;
226    let mut out = wasi_common::file::FdFlags::empty();
227    if f.contains(system_interface::fs::FdFlags::APPEND) {
228        out |= wasi_common::file::FdFlags::APPEND;
229    }
230    if f.contains(system_interface::fs::FdFlags::DSYNC) {
231        out |= wasi_common::file::FdFlags::DSYNC;
232    }
233    if f.contains(system_interface::fs::FdFlags::NONBLOCK) {
234        out |= wasi_common::file::FdFlags::NONBLOCK;
235    }
236    if f.contains(system_interface::fs::FdFlags::RSYNC) {
237        out |= wasi_common::file::FdFlags::RSYNC;
238    }
239    if f.contains(system_interface::fs::FdFlags::SYNC) {
240        out |= wasi_common::file::FdFlags::SYNC;
241    }
242    Ok(out)
243}
244
245fn convert_advice(advice: Advice) -> system_interface::fs::Advice {
246    match advice {
247        Advice::Normal => system_interface::fs::Advice::Normal,
248        Advice::Sequential => system_interface::fs::Advice::Sequential,
249        Advice::Random => system_interface::fs::Advice::Random,
250        Advice::WillNeed => system_interface::fs::Advice::WillNeed,
251        Advice::DontNeed => system_interface::fs::Advice::DontNeed,
252        Advice::NoReuse => system_interface::fs::Advice::NoReuse,
253    }
254}