wasi_cap_std_sync/
file.rs1use 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
221pub 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}