wasmtime_vfs_file/
lib.rs

1use std::any::Any;
2use std::cmp::min;
3use std::io::{IoSlice, IoSliceMut, SeekFrom};
4use std::ops::{Deref, DerefMut};
5use std::sync::Arc;
6
7use wasi_common::file::{Advice, FdFlags, FileType, Filestat};
8use wasi_common::{Error, ErrorExt, ErrorKind, SystemTimeSpec, WasiDir, WasiFile};
9use wasmtime_vfs_ledger::InodeId;
10use wasmtime_vfs_memory::{Data, Inode, Link, Node, Open, State};
11
12pub struct File(Link<Vec<u8>>);
13
14impl Deref for File {
15    type Target = Link<Vec<u8>>;
16
17    fn deref(&self) -> &Self::Target {
18        &self.0
19    }
20}
21
22impl DerefMut for File {
23    fn deref_mut(&mut self) -> &mut Self::Target {
24        &mut self.0
25    }
26}
27
28#[async_trait::async_trait]
29impl Node for File {
30    fn to_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
31        self
32    }
33
34    fn parent(&self) -> Option<Arc<dyn Node>> {
35        self.parent.upgrade()
36    }
37
38    fn filetype(&self) -> FileType {
39        FileType::RegularFile
40    }
41
42    fn id(&self) -> Arc<InodeId> {
43        self.inode.id.clone()
44    }
45
46    async fn open_dir(self: Arc<Self>) -> Result<Box<dyn WasiDir>, Error> {
47        Err(Error::not_dir())
48    }
49
50    async fn open_file(
51        self: Arc<Self>,
52        _path: &str,
53        dir: bool,
54        read: bool,
55        write: bool,
56        flags: FdFlags,
57    ) -> Result<Box<dyn WasiFile>, Error> {
58        if dir {
59            return Err(Error::not_dir());
60        }
61
62        Ok(Box::new(OpenFile(Open {
63            root: self.root(),
64            link: self,
65            state: State::from(flags).into(),
66            write,
67            read,
68        })))
69    }
70}
71
72impl File {
73    #[allow(clippy::new_ret_no_self)]
74    pub fn new(parent: Arc<dyn Node>) -> Arc<dyn Node> {
75        Self::with_data(parent, [])
76    }
77
78    pub fn with_data(parent: Arc<dyn Node>, data: impl Into<Vec<u8>>) -> Arc<dyn Node> {
79        let id = parent.id().device().create_inode();
80
81        let inode = Inode {
82            data: Data::from(data.into()).into(),
83            id,
84        };
85
86        Arc::new(Self(Link {
87            parent: Arc::downgrade(&parent),
88            inode: inode.into(),
89        }))
90    }
91}
92
93struct OpenFile(Open<File>);
94
95impl Deref for OpenFile {
96    type Target = Open<File>;
97
98    fn deref(&self) -> &Self::Target {
99        &self.0
100    }
101}
102
103#[async_trait::async_trait]
104impl WasiFile for OpenFile {
105    fn as_any(&self) -> &dyn Any {
106        self
107    }
108
109    async fn get_filetype(&mut self) -> Result<FileType, Error> {
110        Ok(FileType::RegularFile)
111    }
112
113    async fn datasync(&mut self) -> Result<(), Error> {
114        Ok(())
115    }
116
117    async fn sync(&mut self) -> Result<(), Error> {
118        Ok(())
119    }
120
121    async fn get_fdflags(&mut self) -> Result<FdFlags, Error> {
122        Ok(self.state.read().await.flags)
123    }
124
125    async fn set_fdflags(&mut self, flags: FdFlags) -> Result<(), Error> {
126        if !self.write {
127            return Err(Error::io()); // FIXME: errorno
128        }
129
130        self.state.write().await.flags = flags;
131        Ok(())
132    }
133
134    async fn get_filestat(&mut self) -> Result<Filestat, Error> {
135        let ilock = self.link.inode.data.read().await;
136
137        Ok(Filestat {
138            device_id: **self.link.inode.id.device(),
139            inode: **self.link.inode.id,
140            filetype: FileType::RegularFile,
141            nlink: Arc::strong_count(&self.link.inode) as u64,
142            size: ilock.content.len() as u64,
143            atim: Some(ilock.access),
144            mtim: Some(ilock.modify),
145            ctim: Some(ilock.create),
146        })
147    }
148
149    async fn set_filestat_size(&mut self, size: u64) -> Result<(), Error> {
150        let size: usize = size.try_into().map_err(|_| Error::invalid_argument())?;
151
152        if !self.write {
153            return Err(Error::io()); // FIXME: errorno
154        }
155
156        self.link.inode.data.write().await.content.resize(size, 0);
157        Ok(())
158    }
159
160    async fn advise(&mut self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
161        Ok(())
162    }
163
164    async fn allocate(&mut self, offset: u64, len: u64) -> Result<(), Error> {
165        if !self.write {
166            return Err(Error::io()); // FIXME: errorno
167        }
168
169        let offset: usize = offset.try_into().map_err(|_| Error::invalid_argument())?;
170        let len: usize = len.try_into().map_err(|_| Error::invalid_argument())?;
171        offset
172            .checked_add(len)
173            .ok_or_else(Error::invalid_argument)?;
174        Ok(())
175    }
176
177    async fn set_times(
178        &mut self,
179        atime: Option<SystemTimeSpec>,
180        mtime: Option<SystemTimeSpec>,
181    ) -> Result<(), Error> {
182        if !self.write {
183            return Err(Error::io()); // FIXME: errorno
184        }
185
186        self.link.inode.data.write().await.set_times(atime, mtime)
187    }
188
189    async fn read_vectored<'a>(&mut self, bufs: &mut [IoSliceMut<'a>]) -> Result<u64, Error> {
190        if !self.read {
191            return Err(Error::io()); // FIXME: errorno
192        }
193
194        let mut total = 0;
195
196        let mut olock = self.state.write().await;
197        let ilock = self.link.inode.data.read().await;
198        for buf in bufs {
199            let len = min(buf.len(), ilock.content.len() - olock.pos);
200            buf[..len].copy_from_slice(&ilock.content[olock.pos..][..len]);
201            total += len as u64;
202            olock.pos += len;
203        }
204
205        Ok(total)
206    }
207
208    async fn read_vectored_at<'a>(
209        &mut self,
210        bufs: &mut [IoSliceMut<'a>],
211        offset: u64,
212    ) -> Result<u64, Error> {
213        if !self.read {
214            return Err(Error::io()); // FIXME: errorno
215        }
216
217        let mut position: usize = offset.try_into().map_err(|_| Error::invalid_argument())?;
218        let mut total = 0;
219
220        let data = &self.link.inode.data.read().await.content[..];
221        for buf in bufs {
222            let len = min(buf.len(), data.len() - position);
223            buf[..len].copy_from_slice(&data[position..][..len]);
224            total += len as u64;
225            position += len;
226        }
227
228        Ok(total)
229    }
230
231    async fn write_vectored<'a>(&mut self, bufs: &[IoSlice<'a>]) -> Result<u64, Error> {
232        if !self.write {
233            return Err(Error::io()); // FIXME: errorno
234        }
235
236        let mut total = 0;
237
238        let mut olock = self.state.write().await;
239        let mut ilock = self.link.inode.data.write().await;
240        for buf in bufs {
241            let pos = match olock.flags.contains(FdFlags::APPEND) {
242                true => ilock.content.len(),
243                false => olock.pos,
244            };
245
246            if pos + buf.len() > ilock.content.len() {
247                ilock.content.resize(pos + buf.len(), 0);
248            }
249
250            ilock.content[pos..][..buf.len()].copy_from_slice(buf);
251            total += buf.len() as u64;
252
253            if !olock.flags.contains(FdFlags::APPEND) {
254                olock.pos += buf.len();
255            }
256        }
257
258        Ok(total)
259    }
260
261    // FIXME: we need to decide on a behavior for O_APPEND. WASI doesn't
262    // specify a behavior. POSIX defines one behavior. Linux has a different
263    // one. See: https://linux.die.net/man/2/pwrite
264    async fn write_vectored_at<'a>(
265        &mut self,
266        bufs: &[IoSlice<'a>],
267        offset: u64,
268    ) -> Result<u64, Error> {
269        if !self.write {
270            return Err(Error::io()); // FIXME: errorno
271        }
272
273        let mut pos: usize = offset.try_into().map_err(|_| Error::invalid_argument())?;
274        let mut total = 0;
275
276        let mut ilock = self.link.inode.data.write().await;
277        for buf in bufs {
278            if pos + buf.len() > ilock.content.len() {
279                ilock.content.resize(pos + buf.len(), 0);
280            }
281
282            ilock.content[pos..][..buf.len()].copy_from_slice(buf);
283            total += buf.len() as u64;
284            pos += buf.len();
285        }
286
287        Ok(total)
288    }
289
290    async fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error> {
291        let mut olock = self.state.write().await;
292        let ilock = self.link.inode.data.read().await;
293
294        let cur = match pos {
295            SeekFrom::Current(_) => i64::try_from(olock.pos),
296            SeekFrom::Start(_) => Ok(0),
297            SeekFrom::End(_) => i64::try_from(ilock.content.len()),
298        };
299
300        let off = match pos {
301            SeekFrom::Current(off) => Ok(off),
302            SeekFrom::Start(off) => i64::try_from(off),
303            SeekFrom::End(off) => Ok(off),
304        };
305
306        let pos = cur.map_err(|_| ErrorKind::Inval)? + off.map_err(|_| ErrorKind::Inval)?;
307        let pos = usize::try_from(pos).map_err(|_| ErrorKind::Inval)?;
308        olock.pos = pos;
309
310        Ok(pos as u64)
311    }
312
313    async fn peek(&mut self, buf: &mut [u8]) -> Result<u64, Error> {
314        if !self.read {
315            return Err(Error::io()); // FIXME: errorno
316        }
317
318        let mut total = 0;
319
320        let olock = self.state.read().await;
321        let ilock = self.link.inode.data.read().await;
322        let len = min(buf.len(), ilock.content.len() - olock.pos);
323        buf[..len].copy_from_slice(&ilock.content[olock.pos..][..len]);
324        total += len as u64;
325
326        Ok(total)
327    }
328
329    async fn num_ready_bytes(&self) -> Result<u64, Error> {
330        if !self.read {
331            return Err(Error::io()); // FIXME: errorno
332        }
333
334        let olock = self.state.read().await;
335        let ilock = self.link.inode.data.read().await;
336        let len = min(ilock.content.len(), olock.pos);
337        let len = ilock.content.len() - len;
338        Ok(len as u64)
339    }
340
341    async fn readable(&self) -> Result<(), Error> {
342        Ok(())
343    }
344
345    async fn writable(&self) -> Result<(), Error> {
346        Ok(())
347    }
348}