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
use crate::fs::{error::wasi_errno_to_io_error, Metadata};
use crate::{host, hostcalls, wasi, WasiCtx};
use std::io;

/// A reference to an open file on the filesystem.
///
/// This corresponds to [`std::fs::File`].
///
/// Note that this `File` has no `open` or `create` methods. To open or create
/// a file, you must first obtain a [`Dir`] containing the file, and then call
/// [`Dir::open_file`] or [`Dir::create_file`].
///
/// [`std::fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html
/// [`Dir`]: struct.Dir.html
/// [`Dir::open_file`]: struct.Dir.html#method.open_file
/// [`Dir::create_file`]: struct.Dir.html#method.create_file
pub struct File<'ctx> {
    ctx: &'ctx mut WasiCtx,
    fd: wasi::__wasi_fd_t,
}

impl<'ctx> File<'ctx> {
    /// Constructs a new instance of `Self` from the given raw WASI file descriptor.
    ///
    /// This corresponds to [`std::fs::File::from_raw_fd`].
    ///
    /// [`std::fs::File::from_raw_fd`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.from_raw_fd
    pub unsafe fn from_raw_wasi_fd(ctx: &'ctx mut WasiCtx, fd: wasi::__wasi_fd_t) -> Self {
        Self { ctx, fd }
    }

    /// Attempts to sync all OS-internal metadata to disk.
    ///
    /// This corresponds to [`std::fs::File::sync_all`].
    ///
    /// [`std::fs::File::sync_all`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_all
    pub fn sync_all(&self) -> io::Result<()> {
        wasi_errno_to_io_error(unsafe { hostcalls::fd_sync(self.ctx, &mut [], self.fd) })
    }

    /// This function is similar to `sync_all`, except that it may not synchronize
    /// file metadata to the filesystem.
    ///
    /// This corresponds to [`std::fs::File::sync_data`].
    ///
    /// [`std::fs::File::sync_data`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_data
    pub fn sync_data(&self) -> io::Result<()> {
        wasi_errno_to_io_error(unsafe { hostcalls::fd_datasync(self.ctx, &mut [], self.fd) })
    }

    /// Truncates or extends the underlying file, updating the size of this file
    /// to become size.
    ///
    /// This corresponds to [`std::fs::File::set_len`].
    ///
    /// [`std::fs::File::set_len`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.set_len
    pub fn set_len(&self, size: u64) -> io::Result<()> {
        wasi_errno_to_io_error(unsafe {
            hostcalls::fd_filestat_set_size(self.ctx, &mut [], self.fd, size)
        })
    }

    /// Queries metadata about the underlying file.
    ///
    /// This corresponds to [`std::fs::File::metadata`].
    ///
    /// [`std::fs::File::metadata`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.metadata
    pub fn metadata(&self) -> io::Result<Metadata> {
        Ok(Metadata {})
    }
}

impl<'ctx> Drop for File<'ctx> {
    fn drop(&mut self) {
        // Note that errors are ignored when closing a file descriptor. The
        // reason for this is that if an error occurs we don't actually know if
        // the file descriptor was closed or not, and if we retried (for
        // something like EINTR), we might close another valid file descriptor
        // opened after we closed ours.
        let _ = unsafe { hostcalls::fd_close(self.ctx, &mut [], self.fd) };
    }
}

impl<'ctx> io::Read for File<'ctx> {
    /// TODO: Not yet implemented. See the comment in `Dir::open_file`.
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let iov = [host::__wasi_iovec_t {
            buf: buf.as_mut_ptr() as *mut u8,
            buf_len: buf.len(),
        }];
        let mut nread = 0;

        // TODO: See the comment in `Dir::open_file`.
        unimplemented!("File::read");
        /*
        wasi_errno_to_io_error(unsafe {
            hostcalls::fd_read(self.ctx, self.fd, &iov, 1, &mut nread)
        })?;
        */

        Ok(nread)
    }
}

// TODO: traits to implement: Write, Seek

// TODO: functions from FileExt?

// TODO: impl Debug for File