1use crate::{
2 file::{Advice, FdFlags, FileType, Filestat, WasiFile},
3 Error, ErrorExt,
4};
5use cap_fs_ext::MetadataExt;
6use fs_set_times::{SetTimes, SystemTimeSpec};
7use io_lifetimes::AsFilelike;
8use std::any::Any;
9use std::io::{self, IsTerminal};
10use system_interface::{
11 fs::{FileIoExt, GetSetFdFlags},
12 io::{IoExt, ReadReady},
13};
14
15pub struct File(cap_std::fs::File);
16
17impl File {
18 pub fn from_cap_std(file: cap_std::fs::File) -> Self {
19 File(file)
20 }
21}
22
23#[wiggle::async_trait]
24impl WasiFile for File {
25 fn as_any(&self) -> &dyn Any {
26 self
27 }
28 #[cfg(unix)]
29 fn pollable(&self) -> Option<rustix::fd::BorrowedFd> {
30 Some(self.0.as_fd())
31 }
32 #[cfg(windows)]
33 fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> {
34 Some(self.0.as_raw_handle_or_socket())
35 }
36 async fn datasync(&self) -> Result<(), Error> {
37 self.0.sync_data()?;
38 Ok(())
39 }
40 async fn sync(&self) -> Result<(), Error> {
41 self.0.sync_all()?;
42 Ok(())
43 }
44 async fn get_filetype(&self) -> Result<FileType, Error> {
45 let meta = self.0.metadata()?;
46 Ok(filetype_from(&meta.file_type()))
47 }
48 async fn get_fdflags(&self) -> Result<FdFlags, Error> {
49 let fdflags = get_fd_flags(&self.0)?;
50 Ok(fdflags)
51 }
52 async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
53 if fdflags.intersects(
54 crate::file::FdFlags::DSYNC | crate::file::FdFlags::SYNC | crate::file::FdFlags::RSYNC,
55 ) {
56 return Err(Error::invalid_argument().context("cannot set DSYNC, SYNC, or RSYNC flag"));
57 }
58 let set_fd_flags = self.0.new_set_fd_flags(to_sysif_fdflags(fdflags))?;
59 self.0.set_fd_flags(set_fd_flags)?;
60 Ok(())
61 }
62 async fn get_filestat(&self) -> Result<Filestat, Error> {
63 let meta = self.0.metadata()?;
64 Ok(Filestat {
65 device_id: meta.dev(),
66 inode: meta.ino(),
67 filetype: filetype_from(&meta.file_type()),
68 nlink: meta.nlink(),
69 size: meta.len(),
70 atim: meta.accessed().map(|t| Some(t.into_std())).unwrap_or(None),
71 mtim: meta.modified().map(|t| Some(t.into_std())).unwrap_or(None),
72 ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
73 })
74 }
75 async fn set_filestat_size(&self, size: u64) -> Result<(), Error> {
76 self.0.set_len(size)?;
77 Ok(())
78 }
79 async fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
80 self.0.advise(offset, len, convert_advice(advice))?;
81 Ok(())
82 }
83 async fn set_times(
84 &self,
85 atime: Option<crate::SystemTimeSpec>,
86 mtime: Option<crate::SystemTimeSpec>,
87 ) -> Result<(), Error> {
88 self.0
89 .set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
90 Ok(())
91 }
92 async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
93 let n = self.0.read_vectored(bufs)?;
94 Ok(n.try_into()?)
95 }
96 async fn read_vectored_at<'a>(
97 &self,
98 bufs: &mut [io::IoSliceMut<'a>],
99 offset: u64,
100 ) -> Result<u64, Error> {
101 let n = self.0.read_vectored_at(bufs, offset)?;
102 Ok(n.try_into()?)
103 }
104 async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
105 let n = self.0.write_vectored(bufs)?;
106 Ok(n.try_into()?)
107 }
108 async fn write_vectored_at<'a>(
109 &self,
110 bufs: &[io::IoSlice<'a>],
111 offset: u64,
112 ) -> Result<u64, Error> {
113 if bufs.iter().map(|i| i.len()).sum::<usize>() == 0 {
114 return Ok(0);
115 }
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(t: Option<crate::SystemTimeSpec>) -> Option<SystemTimeSpec> {
190 match t {
191 Some(crate::SystemTimeSpec::Absolute(t)) => Some(SystemTimeSpec::Absolute(t.into_std())),
192 Some(crate::SystemTimeSpec::SymbolicNow) => Some(SystemTimeSpec::SymbolicNow),
193 None => None,
194 }
195}
196
197pub(crate) fn to_sysif_fdflags(f: crate::file::FdFlags) -> system_interface::fs::FdFlags {
198 let mut out = system_interface::fs::FdFlags::empty();
199 if f.contains(crate::file::FdFlags::APPEND) {
200 out |= system_interface::fs::FdFlags::APPEND;
201 }
202 if f.contains(crate::file::FdFlags::DSYNC) {
203 out |= system_interface::fs::FdFlags::DSYNC;
204 }
205 if f.contains(crate::file::FdFlags::NONBLOCK) {
206 out |= system_interface::fs::FdFlags::NONBLOCK;
207 }
208 if f.contains(crate::file::FdFlags::RSYNC) {
209 out |= system_interface::fs::FdFlags::RSYNC;
210 }
211 if f.contains(crate::file::FdFlags::SYNC) {
212 out |= system_interface::fs::FdFlags::SYNC;
213 }
214 out
215}
216
217pub fn get_fd_flags<Filelike: AsFilelike>(f: Filelike) -> io::Result<crate::file::FdFlags> {
221 let f = f.as_filelike().get_fd_flags()?;
222 let mut out = crate::file::FdFlags::empty();
223 if f.contains(system_interface::fs::FdFlags::APPEND) {
224 out |= crate::file::FdFlags::APPEND;
225 }
226 if f.contains(system_interface::fs::FdFlags::DSYNC) {
227 out |= crate::file::FdFlags::DSYNC;
228 }
229 if f.contains(system_interface::fs::FdFlags::NONBLOCK) {
230 out |= crate::file::FdFlags::NONBLOCK;
231 }
232 if f.contains(system_interface::fs::FdFlags::RSYNC) {
233 out |= crate::file::FdFlags::RSYNC;
234 }
235 if f.contains(system_interface::fs::FdFlags::SYNC) {
236 out |= crate::file::FdFlags::SYNC;
237 }
238 Ok(out)
239}
240
241fn convert_advice(advice: Advice) -> system_interface::fs::Advice {
242 match advice {
243 Advice::Normal => system_interface::fs::Advice::Normal,
244 Advice::Sequential => system_interface::fs::Advice::Sequential,
245 Advice::Random => system_interface::fs::Advice::Random,
246 Advice::WillNeed => system_interface::fs::Advice::WillNeed,
247 Advice::DontNeed => system_interface::fs::Advice::DontNeed,
248 Advice::NoReuse => system_interface::fs::Advice::NoReuse,
249 }
250}