1 use crate::{
2     Error, ErrorExt,
3     file::{Advice, FdFlags, FileType, Filestat, WasiFile},
4 };
5 use cap_fs_ext::MetadataExt;
6 use fs_set_times::{SetTimes, SystemTimeSpec};
7 use io_lifetimes::AsFilelike;
8 use std::any::Any;
9 use std::io::{self, IsTerminal};
10 use system_interface::{
11     fs::{FileIoExt, GetSetFdFlags},
12     io::{IoExt, ReadReady},
13 };
14 
15 pub struct File(cap_std::fs::File);
16 
17 impl File {
from_cap_std(file: cap_std::fs::File) -> Self18     pub fn from_cap_std(file: cap_std::fs::File) -> Self {
19         File(file)
20     }
21 }
22 
23 #[async_trait::async_trait]
24 impl WasiFile for File {
as_any(&self) -> &dyn Any25     fn as_any(&self) -> &dyn Any {
26         self
27     }
28     #[cfg(unix)]
pollable(&self) -> Option<rustix::fd::BorrowedFd<'_>>29     fn pollable(&self) -> Option<rustix::fd::BorrowedFd<'_>> {
30         Some(self.0.as_fd())
31     }
32     #[cfg(windows)]
pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket>33     fn pollable(&self) -> Option<io_extras::os::windows::RawHandleOrSocket> {
34         Some(self.0.as_raw_handle_or_socket())
35     }
datasync(&self) -> Result<(), Error>36     async fn datasync(&self) -> Result<(), Error> {
37         self.0.sync_data()?;
38         Ok(())
39     }
sync(&self) -> Result<(), Error>40     async fn sync(&self) -> Result<(), Error> {
41         self.0.sync_all()?;
42         Ok(())
43     }
get_filetype(&self) -> Result<FileType, Error>44     async fn get_filetype(&self) -> Result<FileType, Error> {
45         let meta = self.0.metadata()?;
46         Ok(filetype_from(&meta.file_type()))
47     }
get_fdflags(&self) -> Result<FdFlags, Error>48     async fn get_fdflags(&self) -> Result<FdFlags, Error> {
49         let fdflags = get_fd_flags(&self.0)?;
50         Ok(fdflags)
51     }
set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error>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     }
get_filestat(&self) -> Result<Filestat, Error>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     }
set_filestat_size(&self, size: u64) -> Result<(), Error>75     async fn set_filestat_size(&self, size: u64) -> Result<(), Error> {
76         self.0.set_len(size)?;
77         Ok(())
78     }
advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error>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     }
set_times( &self, atime: Option<crate::SystemTimeSpec>, mtime: Option<crate::SystemTimeSpec>, ) -> Result<(), Error>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     }
read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error>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     }
read_vectored_at<'a>( &self, bufs: &mut [io::IoSliceMut<'a>], offset: u64, ) -> Result<u64, Error>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     }
write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error>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     }
write_vectored_at<'a>( &self, bufs: &[io::IoSlice<'a>], offset: u64, ) -> Result<u64, Error>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     }
seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error>119     async fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
120         Ok(self.0.seek(pos)?)
121     }
peek(&self, buf: &mut [u8]) -> Result<u64, Error>122     async fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
123         let n = self.0.peek(buf)?;
124         Ok(n.try_into()?)
125     }
num_ready_bytes(&self) -> Result<u64, Error>126     fn num_ready_bytes(&self) -> Result<u64, Error> {
127         Ok(self.0.num_ready_bytes()?)
128     }
isatty(&self) -> bool129     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 
filetype_from(ft: &cap_std::fs::FileType) -> FileType137 pub 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)]
161 use io_lifetimes::{AsHandle, BorrowedHandle};
162 #[cfg(windows)]
163 impl AsHandle for File {
as_handle(&self) -> BorrowedHandle<'_>164     fn as_handle(&self) -> BorrowedHandle<'_> {
165         self.0.as_handle()
166     }
167 }
168 
169 #[cfg(windows)]
170 use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket};
171 #[cfg(windows)]
172 impl AsRawHandleOrSocket for File {
173     #[inline]
as_raw_handle_or_socket(&self) -> RawHandleOrSocket174     fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
175         self.0.as_raw_handle_or_socket()
176     }
177 }
178 
179 #[cfg(unix)]
180 use io_lifetimes::{AsFd, BorrowedFd};
181 
182 #[cfg(unix)]
183 impl AsFd for File {
as_fd(&self) -> BorrowedFd<'_>184     fn as_fd(&self) -> BorrowedFd<'_> {
185         self.0.as_fd()
186     }
187 }
188 
convert_systimespec(t: Option<crate::SystemTimeSpec>) -> Option<SystemTimeSpec>189 pub(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 
to_sysif_fdflags(f: crate::file::FdFlags) -> system_interface::fs::FdFlags197 pub(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 
217 /// Return the file-descriptor flags for a given file-like object.
218 ///
219 /// This returns the flags needed to implement [`WasiFile::get_fdflags`].
get_fd_flags<Filelike: AsFilelike>(f: Filelike) -> io::Result<crate::file::FdFlags>220 pub 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 
convert_advice(advice: Advice) -> system_interface::fs::Advice241 fn 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 }
251