1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use super::sys_impl::oshandle::RawOsHandle;
use super::{fd, AsFile};
use crate::handle::{Handle, HandleRights};
use crate::wasi::{types, Errno, Result};
use std::any::Any;
use std::cell::Cell;
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::Deref;

#[derive(Debug)]
/// A file backed by the operating system's file system. Dereferences to a
/// `RawOsHandle`.  Its impl of `Handle` uses Rust's `std` to implement all
/// file descriptor operations.
///
/// # Constructing `OsFile`
///
/// `OsFile` can currently only be constructed from `std::fs::File` using
/// the `std::convert::TryFrom` trait:
///
/// ```rust,no_run
/// use std::fs::OpenOptions;
/// use std::convert::TryFrom;
/// use wasi_common::OsFile;
///
/// let file = OpenOptions::new().read(true).open("some_file").unwrap();
/// let os_file = OsFile::try_from(file).unwrap();
/// ```
pub struct OsFile {
    rights: Cell<HandleRights>,
    handle: RawOsHandle,
}

impl OsFile {
    pub(super) fn new(rights: HandleRights, handle: RawOsHandle) -> Self {
        let rights = Cell::new(rights);
        Self { rights, handle }
    }
}

impl Deref for OsFile {
    type Target = RawOsHandle;

    fn deref(&self) -> &Self::Target {
        &self.handle
    }
}

impl Handle for OsFile {
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn try_clone(&self) -> io::Result<Box<dyn Handle>> {
        let handle = self.handle.try_clone()?;
        let rights = self.rights.clone();
        Ok(Box::new(Self { rights, handle }))
    }
    fn get_file_type(&self) -> types::Filetype {
        types::Filetype::RegularFile
    }
    fn get_rights(&self) -> HandleRights {
        self.rights.get()
    }
    fn set_rights(&self, rights: HandleRights) {
        self.rights.set(rights)
    }
    // FdOps
    fn advise(
        &self,
        advice: types::Advice,
        offset: types::Filesize,
        len: types::Filesize,
    ) -> Result<()> {
        fd::advise(self, advice, offset, len)
    }
    fn allocate(&self, offset: types::Filesize, len: types::Filesize) -> Result<()> {
        let fd = self.as_file()?;
        let metadata = fd.metadata()?;
        let current_size = metadata.len();
        let wanted_size = offset.checked_add(len).ok_or(Errno::TooBig)?;
        // This check will be unnecessary when rust-lang/rust#63326 is fixed
        if wanted_size > i64::max_value() as u64 {
            return Err(Errno::TooBig);
        }
        if wanted_size > current_size {
            fd.set_len(wanted_size)?;
        }
        Ok(())
    }
    fn datasync(&self) -> Result<()> {
        self.as_file()?.sync_data()?;
        Ok(())
    }
    fn fdstat_get(&self) -> Result<types::Fdflags> {
        fd::fdstat_get(&*self.as_file()?)
    }
    fn fdstat_set_flags(&self, fdflags: types::Fdflags) -> Result<()> {
        if let Some(new_handle) = fd::fdstat_set_flags(&*self.as_file()?, fdflags)? {
            self.handle.update_from(new_handle);
        }
        Ok(())
    }
    fn filestat_get(&self) -> Result<types::Filestat> {
        fd::filestat_get(&*self.as_file()?)
    }
    fn filestat_set_size(&self, size: types::Filesize) -> Result<()> {
        self.as_file()?.set_len(size)?;
        Ok(())
    }
    fn filestat_set_times(
        &self,
        atim: types::Timestamp,
        mtim: types::Timestamp,
        fst_flags: types::Fstflags,
    ) -> Result<()> {
        fd::filestat_set_times(&*self.as_file()?, atim, mtim, fst_flags)
    }
    fn preadv(&self, buf: &mut [io::IoSliceMut], offset: u64) -> Result<usize> {
        let mut fd: &File = &*self.as_file()?;
        let cur_pos = fd.seek(SeekFrom::Current(0))?;
        fd.seek(SeekFrom::Start(offset))?;
        let nread = self.read_vectored(buf)?;
        fd.seek(SeekFrom::Start(cur_pos))?;
        Ok(nread)
    }
    fn pwritev(&self, buf: &[io::IoSlice], offset: u64) -> Result<usize> {
        let mut fd: &File = &*self.as_file()?;
        let cur_pos = fd.seek(SeekFrom::Current(0))?;
        fd.seek(SeekFrom::Start(offset))?;
        let nwritten = self.write_vectored(&buf)?;
        fd.seek(SeekFrom::Start(cur_pos))?;
        Ok(nwritten)
    }
    fn read_vectored(&self, iovs: &mut [io::IoSliceMut]) -> Result<usize> {
        let nread = self.as_file()?.read_vectored(iovs)?;
        Ok(nread)
    }
    fn seek(&self, offset: SeekFrom) -> Result<u64> {
        let pos = self.as_file()?.seek(offset)?;
        Ok(pos)
    }
    fn sync(&self) -> Result<()> {
        self.as_file()?.sync_all()?;
        Ok(())
    }
    fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result<usize> {
        let nwritten = self.as_file()?.write_vectored(&iovs)?;
        Ok(nwritten)
    }
}