wasmer_vfs/
host_fs.rs

1use crate::{
2    DirEntry, FileDescriptor, FileType, FsError, Metadata, OpenOptions, OpenOptionsConfig, ReadDir,
3    Result, VirtualFile,
4};
5#[cfg(feature = "enable-serde")]
6use serde::{de, Deserialize, Serialize};
7use std::convert::TryInto;
8use std::fs;
9use std::io::{self, Read, Seek, Write};
10#[cfg(unix)]
11use std::os::unix::io::{AsRawFd, RawFd};
12#[cfg(windows)]
13use std::os::windows::io::{AsRawHandle, RawHandle};
14use std::path::{Path, PathBuf};
15use std::time::{SystemTime, UNIX_EPOCH};
16use tracing::debug;
17
18trait TryIntoFileDescriptor {
19    type Error;
20
21    fn try_into_filedescriptor(&self) -> std::result::Result<FileDescriptor, Self::Error>;
22}
23
24#[cfg(unix)]
25impl<T> TryIntoFileDescriptor for T
26where
27    T: AsRawFd,
28{
29    type Error = FsError;
30
31    fn try_into_filedescriptor(&self) -> std::result::Result<FileDescriptor, Self::Error> {
32        Ok(FileDescriptor(
33            self.as_raw_fd()
34                .try_into()
35                .map_err(|_| FsError::InvalidFd)?,
36        ))
37    }
38}
39
40#[cfg(unix)]
41impl TryInto<RawFd> for FileDescriptor {
42    type Error = FsError;
43
44    fn try_into(self) -> std::result::Result<RawFd, Self::Error> {
45        self.0.try_into().map_err(|_| FsError::InvalidFd)
46    }
47}
48
49#[cfg(windows)]
50impl<T> TryIntoFileDescriptor for T
51where
52    T: AsRawHandle,
53{
54    type Error = FsError;
55
56    fn try_into_filedescriptor(&self) -> std::result::Result<FileDescriptor, Self::Error> {
57        Ok(FileDescriptor(self.as_raw_handle() as usize))
58    }
59}
60
61#[cfg(windows)]
62impl TryInto<RawHandle> for FileDescriptor {
63    type Error = FsError;
64
65    fn try_into(self) -> std::result::Result<RawHandle, Self::Error> {
66        Ok(self.0 as RawHandle)
67    }
68}
69
70#[derive(Debug, Default, Clone)]
71#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
72pub struct FileSystem;
73
74impl crate::FileSystem for FileSystem {
75    fn read_dir(&self, path: &Path) -> Result<ReadDir> {
76        let read_dir = fs::read_dir(path)?;
77        let data = read_dir
78            .map(|entry| {
79                let entry = entry?;
80                let metadata = entry.metadata()?;
81                Ok(DirEntry {
82                    path: entry.path(),
83                    metadata: Ok(metadata.try_into()?),
84                })
85            })
86            .collect::<std::result::Result<Vec<DirEntry>, io::Error>>()
87            .map_err::<FsError, _>(Into::into)?;
88        Ok(ReadDir::new(data))
89    }
90
91    fn create_dir(&self, path: &Path) -> Result<()> {
92        fs::create_dir(path).map_err(Into::into)
93    }
94
95    fn remove_dir(&self, path: &Path) -> Result<()> {
96        fs::remove_dir(path).map_err(Into::into)
97    }
98
99    fn rename(&self, from: &Path, to: &Path) -> Result<()> {
100        fs::rename(from, to).map_err(Into::into)
101    }
102
103    fn remove_file(&self, path: &Path) -> Result<()> {
104        fs::remove_file(path).map_err(Into::into)
105    }
106
107    fn new_open_options(&self) -> OpenOptions {
108        OpenOptions::new(Box::new(FileOpener))
109    }
110
111    fn metadata(&self, path: &Path) -> Result<Metadata> {
112        fs::metadata(path)
113            .and_then(TryInto::try_into)
114            .map_err(Into::into)
115    }
116}
117
118impl TryInto<Metadata> for fs::Metadata {
119    type Error = io::Error;
120
121    fn try_into(self) -> std::result::Result<Metadata, Self::Error> {
122        let filetype = self.file_type();
123        let (char_device, block_device, socket, fifo) = {
124            #[cfg(unix)]
125            {
126                use std::os::unix::fs::FileTypeExt;
127                (
128                    filetype.is_char_device(),
129                    filetype.is_block_device(),
130                    filetype.is_socket(),
131                    filetype.is_fifo(),
132                )
133            }
134            #[cfg(not(unix))]
135            {
136                (false, false, false, false)
137            }
138        };
139
140        Ok(Metadata {
141            ft: FileType {
142                dir: filetype.is_dir(),
143                file: filetype.is_file(),
144                symlink: filetype.is_symlink(),
145                char_device,
146                block_device,
147                socket,
148                fifo,
149            },
150            accessed: self
151                .accessed()
152                .and_then(|time| {
153                    time.duration_since(UNIX_EPOCH)
154                        .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
155                })
156                .map_or(0, |time| time.as_nanos() as u64),
157            created: self
158                .created()
159                .and_then(|time| {
160                    time.duration_since(UNIX_EPOCH)
161                        .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
162                })
163                .map_or(0, |time| time.as_nanos() as u64),
164            modified: self
165                .modified()
166                .and_then(|time| {
167                    time.duration_since(UNIX_EPOCH)
168                        .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
169                })
170                .map_or(0, |time| time.as_nanos() as u64),
171            len: self.len(),
172        })
173    }
174}
175
176#[derive(Debug, Clone)]
177pub struct FileOpener;
178
179impl crate::FileOpener for FileOpener {
180    fn open(
181        &mut self,
182        path: &Path,
183        conf: &OpenOptionsConfig,
184    ) -> Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
185        // TODO: handle create implying write, etc.
186        let read = conf.read();
187        let write = conf.write();
188        let append = conf.append();
189        let mut oo = fs::OpenOptions::new();
190        oo.read(conf.read())
191            .write(conf.write())
192            .create_new(conf.create_new())
193            .create(conf.create())
194            .append(conf.append())
195            .truncate(conf.truncate())
196            .open(path)
197            .map_err(Into::into)
198            .map(|file| {
199                Box::new(File::new(file, path.to_owned(), read, write, append))
200                    as Box<dyn VirtualFile + Send + Sync + 'static>
201            })
202    }
203}
204
205/// A thin wrapper around `std::fs::File`
206#[derive(Debug)]
207#[cfg_attr(feature = "enable-serde", derive(Serialize))]
208pub struct File {
209    #[cfg_attr(feature = "enable-serde", serde(skip_serializing))]
210    pub inner: fs::File,
211    pub host_path: PathBuf,
212    #[cfg(feature = "enable-serde")]
213    flags: u16,
214}
215
216#[cfg(feature = "enable-serde")]
217impl<'de> Deserialize<'de> for File {
218    fn deserialize<D>(deserializer: D) -> std::result::Result<File, D::Error>
219    where
220        D: serde::Deserializer<'de>,
221    {
222        #[derive(Deserialize)]
223        #[serde(field_identifier, rename_all = "snake_case")]
224        enum Field {
225            HostPath,
226            Flags,
227        }
228
229        struct FileVisitor;
230
231        impl<'de> de::Visitor<'de> for FileVisitor {
232            type Value = File;
233
234            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
235                formatter.write_str("struct File")
236            }
237
238            fn visit_seq<V>(self, mut seq: V) -> std::result::Result<Self::Value, V::Error>
239            where
240                V: de::SeqAccess<'de>,
241            {
242                let host_path = seq
243                    .next_element()?
244                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
245                let flags = seq
246                    .next_element()?
247                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;
248                let inner = fs::OpenOptions::new()
249                    .read(flags & File::READ != 0)
250                    .write(flags & File::WRITE != 0)
251                    .append(flags & File::APPEND != 0)
252                    .open(&host_path)
253                    .map_err(|_| de::Error::custom("Could not open file on this system"))?;
254                Ok(File {
255                    inner,
256                    host_path,
257                    flags,
258                })
259            }
260
261            fn visit_map<V>(self, mut map: V) -> std::result::Result<Self::Value, V::Error>
262            where
263                V: de::MapAccess<'de>,
264            {
265                let mut host_path = None;
266                let mut flags = None;
267                while let Some(key) = map.next_key()? {
268                    match key {
269                        Field::HostPath => {
270                            if host_path.is_some() {
271                                return Err(de::Error::duplicate_field("host_path"));
272                            }
273                            host_path = Some(map.next_value()?);
274                        }
275                        Field::Flags => {
276                            if flags.is_some() {
277                                return Err(de::Error::duplicate_field("flags"));
278                            }
279                            flags = Some(map.next_value()?);
280                        }
281                    }
282                }
283                let host_path = host_path.ok_or_else(|| de::Error::missing_field("host_path"))?;
284                let flags = flags.ok_or_else(|| de::Error::missing_field("flags"))?;
285                let inner = fs::OpenOptions::new()
286                    .read(flags & File::READ != 0)
287                    .write(flags & File::WRITE != 0)
288                    .append(flags & File::APPEND != 0)
289                    .open(&host_path)
290                    .map_err(|_| de::Error::custom("Could not open file on this system"))?;
291                Ok(File {
292                    inner,
293                    host_path,
294                    flags,
295                })
296            }
297        }
298
299        const FIELDS: &[&str] = &["host_path", "flags"];
300        deserializer.deserialize_struct("File", FIELDS, FileVisitor)
301    }
302}
303
304impl File {
305    const READ: u16 = 1;
306    const WRITE: u16 = 2;
307    const APPEND: u16 = 4;
308
309    /// creates a new host file from a `std::fs::File` and a path
310    pub fn new(file: fs::File, host_path: PathBuf, read: bool, write: bool, append: bool) -> Self {
311        let mut _flags = 0;
312
313        if read {
314            _flags |= Self::READ;
315        }
316
317        if write {
318            _flags |= Self::WRITE;
319        }
320
321        if append {
322            _flags |= Self::APPEND;
323        }
324
325        Self {
326            inner: file,
327            host_path,
328            #[cfg(feature = "enable-serde")]
329            flags: _flags,
330        }
331    }
332
333    pub fn metadata(&self) -> fs::Metadata {
334        self.inner.metadata().unwrap()
335    }
336}
337
338impl Read for File {
339    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
340        self.inner.read(buf)
341    }
342
343    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
344        self.inner.read_to_end(buf)
345    }
346
347    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
348        self.inner.read_to_string(buf)
349    }
350
351    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
352        self.inner.read_exact(buf)
353    }
354}
355
356impl Seek for File {
357    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
358        self.inner.seek(pos)
359    }
360}
361
362impl Write for File {
363    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
364        self.inner.write(buf)
365    }
366
367    fn flush(&mut self) -> io::Result<()> {
368        self.inner.flush()
369    }
370
371    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
372        self.inner.write_all(buf)
373    }
374
375    fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
376        self.inner.write_fmt(fmt)
377    }
378}
379
380//#[cfg_attr(feature = "enable-serde", typetag::serde)]
381impl VirtualFile for File {
382    fn last_accessed(&self) -> u64 {
383        self.metadata()
384            .accessed()
385            .ok()
386            .and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
387            .map(|ct| ct.as_nanos() as u64)
388            .unwrap_or(0)
389    }
390
391    fn last_modified(&self) -> u64 {
392        self.metadata()
393            .modified()
394            .ok()
395            .and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
396            .map(|ct| ct.as_nanos() as u64)
397            .unwrap_or(0)
398    }
399
400    fn created_time(&self) -> u64 {
401        self.metadata()
402            .created()
403            .ok()
404            .and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
405            .map(|ct| ct.as_nanos() as u64)
406            .unwrap_or(0)
407    }
408
409    fn size(&self) -> u64 {
410        self.metadata().len()
411    }
412
413    fn set_len(&mut self, new_size: u64) -> Result<()> {
414        fs::File::set_len(&self.inner, new_size).map_err(Into::into)
415    }
416
417    fn unlink(&mut self) -> Result<()> {
418        fs::remove_file(&self.host_path).map_err(Into::into)
419    }
420    fn sync_to_disk(&self) -> Result<()> {
421        self.inner.sync_all().map_err(Into::into)
422    }
423
424    fn bytes_available(&self) -> Result<usize> {
425        host_file_bytes_available(self.inner.try_into_filedescriptor()?)
426    }
427}
428
429#[cfg(unix)]
430fn host_file_bytes_available(host_fd: FileDescriptor) -> Result<usize> {
431    let mut bytes_found: libc::c_int = 0;
432    let result = unsafe { libc::ioctl(host_fd.try_into()?, libc::FIONREAD, &mut bytes_found) };
433
434    match result {
435        // success
436        0 => Ok(bytes_found.try_into().unwrap_or(0)),
437        libc::EBADF => Err(FsError::InvalidFd),
438        libc::EFAULT => Err(FsError::InvalidData),
439        libc::EINVAL => Err(FsError::InvalidInput),
440        _ => Err(FsError::IOError),
441    }
442}
443
444#[cfg(not(unix))]
445fn host_file_bytes_available(_host_fd: FileDescriptor) -> Result<usize> {
446    unimplemented!("host_file_bytes_available not yet implemented for non-Unix-like targets.  This probably means the program tried to use wasi::poll_oneoff")
447}
448
449/// A wrapper type around Stdout that implements `VirtualFile` and
450/// `Serialize` + `Deserialize`.
451#[derive(Debug, Default)]
452#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
453pub struct Stdout;
454
455impl Read for Stdout {
456    fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
457        Err(io::Error::new(
458            io::ErrorKind::Other,
459            "can not read from stdout",
460        ))
461    }
462
463    fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
464        Err(io::Error::new(
465            io::ErrorKind::Other,
466            "can not read from stdout",
467        ))
468    }
469
470    fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
471        Err(io::Error::new(
472            io::ErrorKind::Other,
473            "can not read from stdout",
474        ))
475    }
476
477    fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
478        Err(io::Error::new(
479            io::ErrorKind::Other,
480            "can not read from stdout",
481        ))
482    }
483}
484
485impl Seek for Stdout {
486    fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
487        Err(io::Error::new(io::ErrorKind::Other, "can not seek stdout"))
488    }
489}
490
491impl Write for Stdout {
492    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
493        io::stdout().write(buf)
494    }
495
496    fn flush(&mut self) -> io::Result<()> {
497        io::stdout().flush()
498    }
499
500    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
501        io::stdout().write_all(buf)
502    }
503
504    fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
505        io::stdout().write_fmt(fmt)
506    }
507}
508
509//#[cfg_attr(feature = "enable-serde", typetag::serde)]
510impl VirtualFile for Stdout {
511    fn last_accessed(&self) -> u64 {
512        0
513    }
514
515    fn last_modified(&self) -> u64 {
516        0
517    }
518
519    fn created_time(&self) -> u64 {
520        0
521    }
522
523    fn size(&self) -> u64 {
524        0
525    }
526
527    fn set_len(&mut self, _new_size: u64) -> Result<()> {
528        debug!("Calling VirtualFile::set_len on stdout; this is probably a bug");
529        Err(FsError::PermissionDenied)
530    }
531
532    fn unlink(&mut self) -> Result<()> {
533        Ok(())
534    }
535
536    fn bytes_available(&self) -> Result<usize> {
537        host_file_bytes_available(io::stdout().try_into_filedescriptor()?)
538    }
539
540    fn get_fd(&self) -> Option<FileDescriptor> {
541        io::stdout().try_into_filedescriptor().ok()
542    }
543}
544
545/// A wrapper type around Stderr that implements `VirtualFile` and
546/// `Serialize` + `Deserialize`.
547#[derive(Debug, Default)]
548#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
549pub struct Stderr;
550
551impl Read for Stderr {
552    fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
553        Err(io::Error::new(
554            io::ErrorKind::Other,
555            "can not read from stderr",
556        ))
557    }
558
559    fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
560        Err(io::Error::new(
561            io::ErrorKind::Other,
562            "can not read from stderr",
563        ))
564    }
565
566    fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
567        Err(io::Error::new(
568            io::ErrorKind::Other,
569            "can not read from stderr",
570        ))
571    }
572
573    fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
574        Err(io::Error::new(
575            io::ErrorKind::Other,
576            "can not read from stderr",
577        ))
578    }
579}
580
581impl Seek for Stderr {
582    fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
583        Err(io::Error::new(io::ErrorKind::Other, "can not seek stderr"))
584    }
585}
586
587impl Write for Stderr {
588    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
589        io::stderr().write(buf)
590    }
591
592    fn flush(&mut self) -> io::Result<()> {
593        io::stderr().flush()
594    }
595
596    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
597        io::stderr().write_all(buf)
598    }
599
600    fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
601        io::stderr().write_fmt(fmt)
602    }
603}
604
605//#[cfg_attr(feature = "enable-serde", typetag::serde)]
606impl VirtualFile for Stderr {
607    fn last_accessed(&self) -> u64 {
608        0
609    }
610
611    fn last_modified(&self) -> u64 {
612        0
613    }
614
615    fn created_time(&self) -> u64 {
616        0
617    }
618
619    fn size(&self) -> u64 {
620        0
621    }
622
623    fn set_len(&mut self, _new_size: u64) -> Result<()> {
624        debug!("Calling VirtualFile::set_len on stderr; this is probably a bug");
625        Err(FsError::PermissionDenied)
626    }
627
628    fn unlink(&mut self) -> Result<()> {
629        Ok(())
630    }
631
632    fn bytes_available(&self) -> Result<usize> {
633        host_file_bytes_available(io::stderr().try_into_filedescriptor()?)
634    }
635
636    fn get_fd(&self) -> Option<FileDescriptor> {
637        io::stderr().try_into_filedescriptor().ok()
638    }
639}
640
641/// A wrapper type around Stdin that implements `VirtualFile` and
642/// `Serialize` + `Deserialize`.
643#[derive(Debug, Default)]
644#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
645pub struct Stdin;
646impl Read for Stdin {
647    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
648        io::stdin().read(buf)
649    }
650
651    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
652        io::stdin().read_to_end(buf)
653    }
654
655    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
656        io::stdin().read_to_string(buf)
657    }
658
659    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
660        io::stdin().read_exact(buf)
661    }
662}
663
664impl Seek for Stdin {
665    fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
666        Err(io::Error::new(io::ErrorKind::Other, "can not seek stdin"))
667    }
668}
669
670impl Write for Stdin {
671    fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
672        Err(io::Error::new(
673            io::ErrorKind::Other,
674            "can not write to stdin",
675        ))
676    }
677
678    fn flush(&mut self) -> io::Result<()> {
679        Err(io::Error::new(
680            io::ErrorKind::Other,
681            "can not write to stdin",
682        ))
683    }
684
685    fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
686        Err(io::Error::new(
687            io::ErrorKind::Other,
688            "can not write to stdin",
689        ))
690    }
691
692    fn write_fmt(&mut self, _fmt: ::std::fmt::Arguments) -> io::Result<()> {
693        Err(io::Error::new(
694            io::ErrorKind::Other,
695            "can not write to stdin",
696        ))
697    }
698}
699
700//#[cfg_attr(feature = "enable-serde", typetag::serde)]
701impl VirtualFile for Stdin {
702    fn last_accessed(&self) -> u64 {
703        0
704    }
705
706    fn last_modified(&self) -> u64 {
707        0
708    }
709
710    fn created_time(&self) -> u64 {
711        0
712    }
713
714    fn size(&self) -> u64 {
715        0
716    }
717
718    fn set_len(&mut self, _new_size: u64) -> Result<()> {
719        debug!("Calling VirtualFile::set_len on stdin; this is probably a bug");
720        Err(FsError::PermissionDenied)
721    }
722
723    fn unlink(&mut self) -> Result<()> {
724        Ok(())
725    }
726
727    fn bytes_available(&self) -> Result<usize> {
728        host_file_bytes_available(io::stdin().try_into_filedescriptor()?)
729    }
730
731    fn get_fd(&self) -> Option<FileDescriptor> {
732        io::stdin().try_into_filedescriptor().ok()
733    }
734}