diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4dc4359ce6..7d07592d9d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -87,6 +87,19 @@ jobs: - run: cargo fuzz run instantiate_translated --release --debug-assertions -- -runs=0 - run: cargo fuzz run api_calls --release --debug-assertions -- -runs=0 + # Install wasm32-unknown-emscripten target, and ensure `crates/wasi-common` + # compiles to Emscripten. + emscripten: + name: Emscripten + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: true + - uses: ./.github/actions/install-rust + - run: rustup target add wasm32-unknown-emscripten + - run: cargo build --target wasm32-unknown-emscripten -p wasi-common + # Perform all tests (debug mode) for `wasmtime`. This runs stable/beta/nightly # channels of Rust as well as macOS/Linux/Windows. test: diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs index f6fa7be1da..1707f180eb 100644 --- a/crates/wasi-common/src/error.rs +++ b/crates/wasi-common/src/error.rs @@ -149,6 +149,12 @@ impl From for Error { } } +impl From for Error { + fn from(_: ffi::NulError) -> Self { + Self::Wasi(WasiError::EILSEQ) + } +} + impl From<&ffi::NulError> for Error { fn from(_: &ffi::NulError) -> Self { Self::Wasi(WasiError::EILSEQ) diff --git a/crates/wasi-common/src/old/snapshot_0/error.rs b/crates/wasi-common/src/old/snapshot_0/error.rs index 33dce550f5..54185df5d8 100644 --- a/crates/wasi-common/src/old/snapshot_0/error.rs +++ b/crates/wasi-common/src/old/snapshot_0/error.rs @@ -149,6 +149,12 @@ impl From for Error { } } +impl From for Error { + fn from(_: ffi::NulError) -> Self { + Self::Wasi(WasiError::EILSEQ) + } +} + impl From<&ffi::NulError> for Error { fn from(_: &ffi::NulError) -> Self { Self::Wasi(WasiError::EILSEQ) diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/filetime.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/filetime.rs index 8825e97a66..943aba9f7b 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/filetime.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/filetime.rs @@ -1,6 +1,6 @@ //! This internal module consists of helper types and functions for dealing //! with setting the file times specific to BSD-style *nixes. -use super::super::filetime::FileTime; +use crate::old::snapshot_0::{sys::unix::filetime::FileTime, Result}; use cfg_if::cfg_if; use std::ffi::CStr; use std::fs::File; @@ -41,8 +41,8 @@ pub(crate) fn utimensat( atime: FileTime, mtime: FileTime, symlink_nofollow: bool, -) -> io::Result<()> { - use super::super::filetime::{to_timespec, utimesat}; +) -> Result<()> { + use crate::old::snapshot_0::sys::unix::filetime::to_timespec; use std::ffi::CString; use std::os::unix::prelude::*; @@ -56,16 +56,16 @@ pub(crate) fn utimensat( }; let p = CString::new(path.as_bytes())?; - let times = [to_timespec(&atime), to_timespec(&mtime)]; + let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; let rc = unsafe { func(dirfd.as_raw_fd(), p.as_ptr(), times.as_ptr(), flags) }; if rc == 0 { return Ok(()); } else { - return Err(io::Error::last_os_error()); + return Err(io::Error::last_os_error().into()); } } - utimesat(dirfd, path, atime, mtime, symlink_nofollow) + super::utimesat::utimesat(dirfd, path, atime, mtime, symlink_nofollow) } /// Wraps `fetch` specifically targetting `utimensat` symbol. If the symbol exists diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/host_impl.rs new file mode 100644 index 0000000000..b151e703e1 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/host_impl.rs @@ -0,0 +1,16 @@ +use crate::old::snapshot_0::{wasi, Result}; +use std::convert::TryFrom; + +pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC; + +pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { + wasi::__wasi_device_t::try_from(dev).map_err(Into::into) +} + +pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { + wasi::__wasi_device_t::try_from(ino).map_err(Into::into) +} + +pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> Result { + Ok(nlink.into()) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs index 7b14ae69ad..39a4046a74 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs @@ -1,18 +1,6 @@ pub(crate) mod filetime; +pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; - -pub(crate) mod host_impl { - use crate::old::snapshot_0::{wasi, Result}; - use std::convert::TryFrom; - - pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC; - - pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { - wasi::__wasi_device_t::try_from(dev).map_err(Into::into) - } - - pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { - wasi::__wasi_device_t::try_from(ino).map_err(Into::into) - } -} +#[path = "../linux/utimesat.rs"] +pub(crate) mod utimesat; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/filetime.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/filetime.rs new file mode 100644 index 0000000000..adc48a3493 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/filetime.rs @@ -0,0 +1,36 @@ +//! This internal module consists of helper types and functions for dealing +//! with setting the file times specific to Emscripten. +use crate::old::snapshot_0::{sys::unix::filetime::FileTime, Result}; +use std::fs::File; +use std::io; + +pub(crate) const UTIME_NOW: i32 = 1_073_741_823; +pub(crate) const UTIME_OMIT: i32 = 1_073_741_822; + +/// Wrapper for `utimensat` syscall. In Emscripten, there is no point in dynamically resolving +/// if `utimensat` is available as it always was and will be. +pub(crate) fn utimensat( + dirfd: &File, + path: &str, + atime: FileTime, + mtime: FileTime, + symlink_nofollow: bool, +) -> Result<()> { + use crate::old::snapshot_0::sys::unix::filetime::to_timespec; + use std::ffi::CString; + use std::os::unix::prelude::*; + + let flags = if symlink_nofollow { + libc::AT_SYMLINK_NOFOLLOW + } else { + 0 + }; + let p = CString::new(path.as_bytes())?; + let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; + let rc = unsafe { libc::utimensat(dirfd.as_raw_fd(), p.as_ptr(), times.as_ptr(), flags) }; + if rc == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error().into()) + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/host_impl.rs new file mode 100644 index 0000000000..412280a519 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/host_impl.rs @@ -0,0 +1,15 @@ +use crate::old::snapshot_0::{wasi, Result}; + +pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC; + +pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { + Ok(wasi::__wasi_device_t::from(dev)) +} + +pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { + Ok(wasi::__wasi_device_t::from(ino)) +} + +pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> Result { + Ok(nlink) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/mod.rs new file mode 100644 index 0000000000..c9c3841b9a --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/emscripten/mod.rs @@ -0,0 +1,6 @@ +pub(crate) mod filetime; +pub(crate) mod host_impl; +#[path = "../linux/hostcalls_impl.rs"] +pub(crate) mod hostcalls_impl; +#[path = "../linux/oshandle.rs"] +pub(crate) mod oshandle; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs index 5adaf22f30..a46177c3c5 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs @@ -1,24 +1,11 @@ use crate::old::snapshot_0::fdentry::{Descriptor, OsHandleRef}; -use crate::old::snapshot_0::{wasi, Error, Result}; +use crate::old::snapshot_0::{sys::unix::sys_impl, wasi, Error, Result}; use std::fs::File; use std::io; use std::mem::ManuallyDrop; use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd}; -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - pub(crate) use super::linux::oshandle::*; - } else if #[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" - ))] { - pub(crate) use super::bsd::oshandle::*; - } -} +pub(crate) use sys_impl::oshandle::*; impl AsRawFd for Descriptor { fn as_raw_fd(&self) -> RawFd { diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/filetime.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/filetime.rs index 061537b3b8..15530e1f0c 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/filetime.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/filetime.rs @@ -6,21 +6,30 @@ //! Kudos @alexcrichton! //! //! [filetime]: https://github.com/alexcrichton/filetime -use std::fs::{self, File}; -use std::io; +use crate::old::snapshot_0::Result; +use std::convert::TryInto; + +pub(crate) use super::sys_impl::filetime::*; cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - pub(crate) use super::linux::filetime::*; - } else if #[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" - ))] { - pub(crate) use super::bsd::filetime::*; + if #[cfg(not(target_os = "emscripten"))] { + fn filetime_to_timespec(ft: &filetime::FileTime) -> Result { + Ok( + libc::timespec { + tv_sec: ft.seconds(), + tv_nsec: ft.nanoseconds().try_into()?, + } + ) + } + } else { + fn filetime_to_timespec(ft: &filetime::FileTime) -> Result { + Ok( + libc::timespec { + tv_sec: ft.seconds().try_into()?, + tv_nsec: ft.nanoseconds().try_into()?, + } + ) + } } } @@ -35,91 +44,6 @@ pub(crate) enum FileTime { FileTime(filetime::FileTime), } -/// For a provided pair of access and modified `FileTime`s, converts the input to -/// `filetime::FileTime` used later in `utimensat` function. For variants `FileTime::Now` -/// and `FileTime::Omit`, this function will make two syscalls: either accessing current -/// system time, or accessing the file's metadata. -/// -/// The original implementation can be found here: [filetime::unix::get_times]. -/// -/// [filetime::unix::get_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L42 -fn get_times( - atime: FileTime, - mtime: FileTime, - current: impl Fn() -> io::Result, -) -> io::Result<(filetime::FileTime, filetime::FileTime)> { - use std::time::SystemTime; - - let atime = match atime { - FileTime::Now => { - let time = SystemTime::now(); - filetime::FileTime::from_system_time(time) - } - FileTime::Omit => { - let meta = current()?; - filetime::FileTime::from_last_access_time(&meta) - } - FileTime::FileTime(ft) => ft, - }; - - let mtime = match mtime { - FileTime::Now => { - let time = SystemTime::now(); - filetime::FileTime::from_system_time(time) - } - FileTime::Omit => { - let meta = current()?; - filetime::FileTime::from_last_modification_time(&meta) - } - FileTime::FileTime(ft) => ft, - }; - - Ok((atime, mtime)) -} - -/// Combines `openat` with `utimes` to emulate `utimensat` on platforms where it is -/// not available. The logic for setting file times is based on [filetime::unix::set_file_handles_times]. -/// -/// [filetime::unix::set_file_handles_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L24 -pub(crate) fn utimesat( - dirfd: &File, - path: &str, - atime: FileTime, - mtime: FileTime, - symlink_nofollow: bool, -) -> io::Result<()> { - use std::ffi::CString; - use std::os::unix::prelude::*; - // emulate *at syscall by reading the path from a combination of - // (fd, path) - let p = CString::new(path.as_bytes())?; - let mut flags = libc::O_RDWR; - if symlink_nofollow { - flags |= libc::O_NOFOLLOW; - } - let fd = unsafe { libc::openat(dirfd.as_raw_fd(), p.as_ptr(), flags) }; - let f = unsafe { File::from_raw_fd(fd) }; - let (atime, mtime) = get_times(atime, mtime, || f.metadata())?; - let times = [to_timeval(atime), to_timeval(mtime)]; - let rc = unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) }; - return if rc == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - }; -} - -/// Converts `filetime::FileTime` to `libc::timeval`. This function was taken directly from -/// [filetime] crate. -/// -/// [filetime]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L93 -fn to_timeval(ft: filetime::FileTime) -> libc::timeval { - libc::timeval { - tv_sec: ft.seconds(), - tv_usec: (ft.nanoseconds() / 1000) as libc::suseconds_t, - } -} - /// Converts `FileTime` to `libc::timespec`. If `FileTime::Now` variant is specified, this /// resolves to `UTIME_NOW` special const, `FileTime::Omit` variant resolves to `UTIME_OMIT`, and /// `FileTime::FileTime(ft)` where `ft := filetime::FileTime` uses [filetime] crate's original @@ -127,8 +51,8 @@ fn to_timeval(ft: filetime::FileTime) -> libc::timeval { /// /// [filetime]: https://github.com/alexcrichton/filetime /// [filetime::unix::to_timespec]: https://github.com/alexcrichton/filetime/blob/master/src/unix/mod.rs#L30 -pub(crate) fn to_timespec(ft: &FileTime) -> libc::timespec { - match ft { +pub(crate) fn to_timespec(ft: &FileTime) -> Result { + let ts = match ft { FileTime::Now => libc::timespec { tv_sec: 0, tv_nsec: UTIME_NOW, @@ -137,13 +61,7 @@ pub(crate) fn to_timespec(ft: &FileTime) -> libc::timespec { tv_sec: 0, tv_nsec: UTIME_OMIT, }, - // `filetime::FileTime`'s fields are normalised by definition. `ft.seconds()` return the number - // of whole seconds, while `ft.nanoseconds()` returns only fractional part expressed in - // nanoseconds, as underneath it uses `std::time::Duration::subsec_nanos` to populate the - // `filetime::FileTime::nanoseconds` field. It is, therefore, OK to do an `as` cast here. - FileTime::FileTime(ft) => libc::timespec { - tv_sec: ft.seconds(), - tv_nsec: ft.nanoseconds() as _, - }, - } + FileTime::FileTime(ft) => filetime_to_timespec(ft)?, + }; + Ok(ts) } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs index cd15382587..eb0bf33689 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs @@ -3,7 +3,7 @@ #![allow(non_snake_case)] #![allow(dead_code)] use crate::old::snapshot_0::host::FileType; -use crate::old::snapshot_0::{helpers, wasi, Error, Result}; +use crate::old::snapshot_0::{helpers, sys::unix::sys_impl, wasi, Error, Result}; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; use yanix::{ @@ -11,20 +11,7 @@ use yanix::{ Errno, }; -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - pub(crate) use super::linux::host_impl::*; - } else if #[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" - ))] { - pub(crate) use super::bsd::host_impl::*; - } -} +pub(crate) use sys_impl::host_impl::*; pub(crate) fn errno_from_nix(errno: Errno) -> Error { match errno { @@ -197,7 +184,7 @@ pub(crate) fn filestat_from_nix(filestat: libc::stat) -> Result( dir.rewind(); } else { log::trace!(" | fd_readdir: doing seekdir to {}", cookie); - let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; + let loc = unsafe { SeekLoc::from_raw(cookie as i64)? }; dir.seek(loc); } @@ -356,7 +343,7 @@ pub(crate) fn fd_readdir<'a>( .to_owned(), ino: entry.ino(), ftype: entry.file_type().into(), - cookie: entry.seek_loc().to_raw().try_into()?, + cookie: entry.seek_loc()?.to_raw().try_into()?, }) })) } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/filetime.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/filetime.rs index 9fb0516b52..a286874413 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/filetime.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/filetime.rs @@ -1,6 +1,6 @@ //! This internal module consists of helper types and functions for dealing //! with setting the file times specific to Linux. -use super::super::filetime::FileTime; +use crate::old::snapshot_0::{sys::unix::filetime::FileTime, Result}; use std::fs::File; use std::io; use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; @@ -20,8 +20,8 @@ pub(crate) fn utimensat( atime: FileTime, mtime: FileTime, symlink_nofollow: bool, -) -> io::Result<()> { - use super::super::filetime::{to_timespec, utimesat}; +) -> Result<()> { + use crate::old::snapshot_0::sys::unix::filetime::to_timespec; use std::ffi::CString; use std::os::unix::prelude::*; @@ -53,9 +53,9 @@ pub(crate) fn utimensat( if err.raw_os_error() == Some(libc::ENOSYS) { INVALID.store(true, Relaxed); } else { - return Err(err); + return Err(err.into()); } } - utimesat(dirfd, path, atime, mtime, symlink_nofollow) + super::utimesat::utimesat(dirfd, path, atime, mtime, symlink_nofollow) } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/host_impl.rs new file mode 100644 index 0000000000..d867e51b6f --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/host_impl.rs @@ -0,0 +1,16 @@ +use crate::old::snapshot_0::{wasi, Result}; +use std::convert::TryInto; + +pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC; + +pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { + Ok(wasi::__wasi_device_t::from(dev)) +} + +pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { + Ok(wasi::__wasi_device_t::from(ino)) +} + +pub(crate) fn stnlink_from_nix(nlink: libc::nlink_t) -> Result { + nlink.try_into().map_err(Into::into) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs index c177e9d6e4..0b4b8fd0b9 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs @@ -1,17 +1,5 @@ pub(crate) mod filetime; +pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; - -pub(crate) mod host_impl { - use crate::old::snapshot_0::{wasi, Result}; - - pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC; - - pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { - Ok(wasi::__wasi_device_t::from(dev)) - } - - pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { - Ok(wasi::__wasi_device_t::from(ino)) - } -} +pub(crate) mod utimesat; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/utimesat.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/utimesat.rs new file mode 100644 index 0000000000..155d994578 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/utimesat.rs @@ -0,0 +1,88 @@ +use crate::old::snapshot_0::sys::unix::filetime::FileTime; +use crate::old::snapshot_0::Result; +use std::{fs, io}; + +/// Combines `openat` with `utimes` to emulate `utimensat` on platforms where it is +/// not available. The logic for setting file times is based on [filetime::unix::set_file_handles_times]. +/// +/// [filetime::unix::set_file_handles_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L24 +pub(crate) fn utimesat( + dirfd: &fs::File, + path: &str, + atime: FileTime, + mtime: FileTime, + symlink_nofollow: bool, +) -> Result<()> { + use std::ffi::CString; + use std::os::unix::prelude::*; + // emulate *at syscall by reading the path from a combination of + // (fd, path) + let p = CString::new(path.as_bytes())?; + let mut flags = libc::O_RDWR; + if symlink_nofollow { + flags |= libc::O_NOFOLLOW; + } + let fd = unsafe { libc::openat(dirfd.as_raw_fd(), p.as_ptr(), flags) }; + let f = unsafe { fs::File::from_raw_fd(fd) }; + let (atime, mtime) = get_times(atime, mtime, || f.metadata().map_err(Into::into))?; + let times = [to_timeval(atime), to_timeval(mtime)]; + let rc = unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) }; + if rc == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error().into()) + } +} + +/// Converts `filetime::FileTime` to `libc::timeval`. This function was taken directly from +/// [filetime] crate. +/// +/// [filetime]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L93 +fn to_timeval(ft: filetime::FileTime) -> libc::timeval { + libc::timeval { + tv_sec: ft.seconds(), + tv_usec: (ft.nanoseconds() / 1000) as libc::suseconds_t, + } +} + +/// For a provided pair of access and modified `FileTime`s, converts the input to +/// `filetime::FileTime` used later in `utimensat` function. For variants `FileTime::Now` +/// and `FileTime::Omit`, this function will make two syscalls: either accessing current +/// system time, or accessing the file's metadata. +/// +/// The original implementation can be found here: [filetime::unix::get_times]. +/// +/// [filetime::unix::get_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L42 +fn get_times( + atime: FileTime, + mtime: FileTime, + current: impl Fn() -> Result, +) -> Result<(filetime::FileTime, filetime::FileTime)> { + use std::time::SystemTime; + + let atime = match atime { + FileTime::Now => { + let time = SystemTime::now(); + filetime::FileTime::from_system_time(time) + } + FileTime::Omit => { + let meta = current()?; + filetime::FileTime::from_last_access_time(&meta) + } + FileTime::FileTime(ft) => ft, + }; + + let mtime = match mtime { + FileTime::Now => { + let time = SystemTime::now(); + filetime::FileTime::from_system_time(time) + } + FileTime::Omit => { + let meta = current()?; + filetime::FileTime::from_last_modification_time(&meta) + } + FileTime::FileTime(ft) => ft, + }; + + Ok((atime, mtime)) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs index 3a4db31aea..723c878950 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs @@ -4,17 +4,23 @@ pub(crate) mod hostcalls_impl; mod filetime; -#[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" -))] -mod bsd; -#[cfg(target_os = "linux")] -mod linux; +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + mod linux; + use self::linux as sys_impl; + } else if #[cfg(target_os = "emscripten")] { + mod emscripten; + use self::emscripten as sys_impl; + } else if #[cfg(any(target_os = "macos", + target_os = "netbsd", + target_os = "freebsd", + target_os = "openbsd", + target_os = "ios", + target_os = "dragonfly"))] { + mod bsd; + use self::bsd as sys_impl; + } +} use crate::old::snapshot_0::Result; use std::fs::{File, OpenOptions}; diff --git a/crates/wasi-common/src/sys/unix/bsd/filetime.rs b/crates/wasi-common/src/sys/unix/bsd/filetime.rs index 8825e97a66..4fb40c926b 100644 --- a/crates/wasi-common/src/sys/unix/bsd/filetime.rs +++ b/crates/wasi-common/src/sys/unix/bsd/filetime.rs @@ -1,6 +1,6 @@ //! This internal module consists of helper types and functions for dealing //! with setting the file times specific to BSD-style *nixes. -use super::super::filetime::FileTime; +use crate::{sys::unix::filetime::FileTime, Result}; use cfg_if::cfg_if; use std::ffi::CStr; use std::fs::File; @@ -41,8 +41,8 @@ pub(crate) fn utimensat( atime: FileTime, mtime: FileTime, symlink_nofollow: bool, -) -> io::Result<()> { - use super::super::filetime::{to_timespec, utimesat}; +) -> Result<()> { + use crate::sys::unix::filetime::to_timespec; use std::ffi::CString; use std::os::unix::prelude::*; @@ -56,16 +56,16 @@ pub(crate) fn utimensat( }; let p = CString::new(path.as_bytes())?; - let times = [to_timespec(&atime), to_timespec(&mtime)]; + let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; let rc = unsafe { func(dirfd.as_raw_fd(), p.as_ptr(), times.as_ptr(), flags) }; if rc == 0 { return Ok(()); } else { - return Err(io::Error::last_os_error()); + return Err(io::Error::last_os_error().into()); } } - utimesat(dirfd, path, atime, mtime, symlink_nofollow) + super::utimesat::utimesat(dirfd, path, atime, mtime, symlink_nofollow) } /// Wraps `fetch` specifically targetting `utimensat` symbol. If the symbol exists diff --git a/crates/wasi-common/src/sys/unix/bsd/host_impl.rs b/crates/wasi-common/src/sys/unix/bsd/host_impl.rs new file mode 100644 index 0000000000..53f0782deb --- /dev/null +++ b/crates/wasi-common/src/sys/unix/bsd/host_impl.rs @@ -0,0 +1,12 @@ +use crate::{wasi, Result}; +use std::convert::TryFrom; + +pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC; + +pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { + wasi::__wasi_device_t::try_from(dev).map_err(Into::into) +} + +pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { + wasi::__wasi_device_t::try_from(ino).map_err(Into::into) +} diff --git a/crates/wasi-common/src/sys/unix/bsd/mod.rs b/crates/wasi-common/src/sys/unix/bsd/mod.rs index bdb8dc6f68..39a4046a74 100644 --- a/crates/wasi-common/src/sys/unix/bsd/mod.rs +++ b/crates/wasi-common/src/sys/unix/bsd/mod.rs @@ -1,18 +1,6 @@ pub(crate) mod filetime; +pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; - -pub(crate) mod host_impl { - use crate::{wasi, Result}; - use std::convert::TryFrom; - - pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::SYNC; - - pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { - wasi::__wasi_device_t::try_from(dev).map_err(Into::into) - } - - pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { - wasi::__wasi_device_t::try_from(ino).map_err(Into::into) - } -} +#[path = "../linux/utimesat.rs"] +pub(crate) mod utimesat; diff --git a/crates/wasi-common/src/sys/unix/emscripten/filetime.rs b/crates/wasi-common/src/sys/unix/emscripten/filetime.rs new file mode 100644 index 0000000000..08f7c24a79 --- /dev/null +++ b/crates/wasi-common/src/sys/unix/emscripten/filetime.rs @@ -0,0 +1,36 @@ +//! This internal module consists of helper types and functions for dealing +//! with setting the file times specific to Emscripten. +use crate::{sys::unix::filetime::FileTime, Result}; +use std::fs::File; +use std::io; + +pub(crate) const UTIME_NOW: i32 = 1_073_741_823; +pub(crate) const UTIME_OMIT: i32 = 1_073_741_822; + +/// Wrapper for `utimensat` syscall. In Emscripten, there is no point in dynamically resolving +/// if `utimensat` is available as it always was and will be. +pub(crate) fn utimensat( + dirfd: &File, + path: &str, + atime: FileTime, + mtime: FileTime, + symlink_nofollow: bool, +) -> Result<()> { + use crate::sys::unix::filetime::to_timespec; + use std::ffi::CString; + use std::os::unix::prelude::*; + + let flags = if symlink_nofollow { + libc::AT_SYMLINK_NOFOLLOW + } else { + 0 + }; + let p = CString::new(path.as_bytes())?; + let times = [to_timespec(&atime)?, to_timespec(&mtime)?]; + let rc = unsafe { libc::utimensat(dirfd.as_raw_fd(), p.as_ptr(), times.as_ptr(), flags) }; + if rc == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error().into()) + } +} diff --git a/crates/wasi-common/src/sys/unix/emscripten/mod.rs b/crates/wasi-common/src/sys/unix/emscripten/mod.rs new file mode 100644 index 0000000000..82a7ed93a8 --- /dev/null +++ b/crates/wasi-common/src/sys/unix/emscripten/mod.rs @@ -0,0 +1,7 @@ +pub(crate) mod filetime; +#[path = "../linux/host_impl.rs"] +pub(crate) mod host_impl; +#[path = "../linux/hostcalls_impl.rs"] +pub(crate) mod hostcalls_impl; +#[path = "../linux/oshandle.rs"] +pub(crate) mod oshandle; diff --git a/crates/wasi-common/src/sys/unix/fdentry_impl.rs b/crates/wasi-common/src/sys/unix/fdentry_impl.rs index b178fcb38e..e44f54c076 100644 --- a/crates/wasi-common/src/sys/unix/fdentry_impl.rs +++ b/crates/wasi-common/src/sys/unix/fdentry_impl.rs @@ -1,24 +1,11 @@ use crate::fdentry::{Descriptor, OsHandleRef}; -use crate::{wasi, Error, Result}; +use crate::{sys::unix::sys_impl, wasi, Error, Result}; use std::fs::File; use std::io; use std::mem::ManuallyDrop; use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd}; -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - pub(crate) use super::linux::oshandle::*; - } else if #[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" - ))] { - pub(crate) use super::bsd::oshandle::*; - } -} +pub(crate) use sys_impl::oshandle::*; impl AsRawFd for Descriptor { fn as_raw_fd(&self) -> RawFd { diff --git a/crates/wasi-common/src/sys/unix/filetime.rs b/crates/wasi-common/src/sys/unix/filetime.rs index 061537b3b8..9a70740a36 100644 --- a/crates/wasi-common/src/sys/unix/filetime.rs +++ b/crates/wasi-common/src/sys/unix/filetime.rs @@ -6,21 +6,30 @@ //! Kudos @alexcrichton! //! //! [filetime]: https://github.com/alexcrichton/filetime -use std::fs::{self, File}; -use std::io; +use crate::Result; +use std::convert::TryInto; + +pub(crate) use super::sys_impl::filetime::*; cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - pub(crate) use super::linux::filetime::*; - } else if #[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" - ))] { - pub(crate) use super::bsd::filetime::*; + if #[cfg(not(target_os = "emscripten"))] { + fn filetime_to_timespec(ft: &filetime::FileTime) -> Result { + Ok( + libc::timespec { + tv_sec: ft.seconds(), + tv_nsec: ft.nanoseconds().try_into()?, + } + ) + } + } else { + fn filetime_to_timespec(ft: &filetime::FileTime) -> Result { + Ok( + libc::timespec { + tv_sec: ft.seconds().try_into()?, + tv_nsec: ft.nanoseconds().try_into()?, + } + ) + } } } @@ -35,91 +44,6 @@ pub(crate) enum FileTime { FileTime(filetime::FileTime), } -/// For a provided pair of access and modified `FileTime`s, converts the input to -/// `filetime::FileTime` used later in `utimensat` function. For variants `FileTime::Now` -/// and `FileTime::Omit`, this function will make two syscalls: either accessing current -/// system time, or accessing the file's metadata. -/// -/// The original implementation can be found here: [filetime::unix::get_times]. -/// -/// [filetime::unix::get_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L42 -fn get_times( - atime: FileTime, - mtime: FileTime, - current: impl Fn() -> io::Result, -) -> io::Result<(filetime::FileTime, filetime::FileTime)> { - use std::time::SystemTime; - - let atime = match atime { - FileTime::Now => { - let time = SystemTime::now(); - filetime::FileTime::from_system_time(time) - } - FileTime::Omit => { - let meta = current()?; - filetime::FileTime::from_last_access_time(&meta) - } - FileTime::FileTime(ft) => ft, - }; - - let mtime = match mtime { - FileTime::Now => { - let time = SystemTime::now(); - filetime::FileTime::from_system_time(time) - } - FileTime::Omit => { - let meta = current()?; - filetime::FileTime::from_last_modification_time(&meta) - } - FileTime::FileTime(ft) => ft, - }; - - Ok((atime, mtime)) -} - -/// Combines `openat` with `utimes` to emulate `utimensat` on platforms where it is -/// not available. The logic for setting file times is based on [filetime::unix::set_file_handles_times]. -/// -/// [filetime::unix::set_file_handles_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L24 -pub(crate) fn utimesat( - dirfd: &File, - path: &str, - atime: FileTime, - mtime: FileTime, - symlink_nofollow: bool, -) -> io::Result<()> { - use std::ffi::CString; - use std::os::unix::prelude::*; - // emulate *at syscall by reading the path from a combination of - // (fd, path) - let p = CString::new(path.as_bytes())?; - let mut flags = libc::O_RDWR; - if symlink_nofollow { - flags |= libc::O_NOFOLLOW; - } - let fd = unsafe { libc::openat(dirfd.as_raw_fd(), p.as_ptr(), flags) }; - let f = unsafe { File::from_raw_fd(fd) }; - let (atime, mtime) = get_times(atime, mtime, || f.metadata())?; - let times = [to_timeval(atime), to_timeval(mtime)]; - let rc = unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) }; - return if rc == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - }; -} - -/// Converts `filetime::FileTime` to `libc::timeval`. This function was taken directly from -/// [filetime] crate. -/// -/// [filetime]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L93 -fn to_timeval(ft: filetime::FileTime) -> libc::timeval { - libc::timeval { - tv_sec: ft.seconds(), - tv_usec: (ft.nanoseconds() / 1000) as libc::suseconds_t, - } -} - /// Converts `FileTime` to `libc::timespec`. If `FileTime::Now` variant is specified, this /// resolves to `UTIME_NOW` special const, `FileTime::Omit` variant resolves to `UTIME_OMIT`, and /// `FileTime::FileTime(ft)` where `ft := filetime::FileTime` uses [filetime] crate's original @@ -127,8 +51,8 @@ fn to_timeval(ft: filetime::FileTime) -> libc::timeval { /// /// [filetime]: https://github.com/alexcrichton/filetime /// [filetime::unix::to_timespec]: https://github.com/alexcrichton/filetime/blob/master/src/unix/mod.rs#L30 -pub(crate) fn to_timespec(ft: &FileTime) -> libc::timespec { - match ft { +pub(crate) fn to_timespec(ft: &FileTime) -> Result { + let ts = match ft { FileTime::Now => libc::timespec { tv_sec: 0, tv_nsec: UTIME_NOW, @@ -137,13 +61,7 @@ pub(crate) fn to_timespec(ft: &FileTime) -> libc::timespec { tv_sec: 0, tv_nsec: UTIME_OMIT, }, - // `filetime::FileTime`'s fields are normalised by definition. `ft.seconds()` return the number - // of whole seconds, while `ft.nanoseconds()` returns only fractional part expressed in - // nanoseconds, as underneath it uses `std::time::Duration::subsec_nanos` to populate the - // `filetime::FileTime::nanoseconds` field. It is, therefore, OK to do an `as` cast here. - FileTime::FileTime(ft) => libc::timespec { - tv_sec: ft.seconds(), - tv_nsec: ft.nanoseconds() as _, - }, - } + FileTime::FileTime(ft) => filetime_to_timespec(ft)?, + }; + Ok(ts) } diff --git a/crates/wasi-common/src/sys/unix/host_impl.rs b/crates/wasi-common/src/sys/unix/host_impl.rs index 41306837b0..34ca49a1f4 100644 --- a/crates/wasi-common/src/sys/unix/host_impl.rs +++ b/crates/wasi-common/src/sys/unix/host_impl.rs @@ -3,7 +3,7 @@ #![allow(non_snake_case)] #![allow(dead_code)] use crate::host::FileType; -use crate::{helpers, wasi, Error, Result}; +use crate::{helpers, sys::unix::sys_impl, wasi, Error, Result}; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; use yanix::{ @@ -11,20 +11,7 @@ use yanix::{ Errno, }; -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - pub(crate) use super::linux::host_impl::*; - } else if #[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" - ))] { - pub(crate) use super::bsd::host_impl::*; - } -} +pub(crate) use sys_impl::host_impl::*; pub(crate) fn errno_from_nix(errno: Errno) -> Error { match errno { diff --git a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs index 8e6baa7bf5..e4bf973502 100644 --- a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs @@ -3,27 +3,14 @@ use crate::helpers::systemtime_to_timestamp; use crate::host::{Dirent, FileType}; use crate::hostcalls_impl::PathGet; -use crate::sys::{fdentry_impl::OsHandle, host_impl}; +use crate::sys::{fdentry_impl::OsHandle, host_impl, unix::sys_impl}; use crate::{wasi, Error, Result}; use std::convert::TryInto; use std::fs::{File, Metadata}; use std::os::unix::fs::FileExt; use std::os::unix::prelude::{AsRawFd, FromRawFd}; -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - pub(crate) use super::super::linux::hostcalls_impl::*; - } else if #[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" - ))] { - pub(crate) use super::super::bsd::hostcalls_impl::*; - } -} +pub(crate) use sys_impl::hostcalls_impl::*; pub(crate) fn fd_pread( file: &File, @@ -342,7 +329,7 @@ pub(crate) fn fd_readdir<'a>( dir.rewind(); } else { log::trace!(" | fd_readdir: doing seekdir to {}", cookie); - let loc = unsafe { SeekLoc::from_raw(cookie as i64) }; + let loc = unsafe { SeekLoc::from_raw(cookie as i64)? }; dir.seek(loc); } @@ -356,7 +343,7 @@ pub(crate) fn fd_readdir<'a>( .to_owned(), ino: entry.ino(), ftype: entry.file_type().into(), - cookie: entry.seek_loc().to_raw().try_into()?, + cookie: entry.seek_loc()?.to_raw().try_into()?, }) })) } diff --git a/crates/wasi-common/src/sys/unix/linux/filetime.rs b/crates/wasi-common/src/sys/unix/linux/filetime.rs index 9fb0516b52..48413a5514 100644 --- a/crates/wasi-common/src/sys/unix/linux/filetime.rs +++ b/crates/wasi-common/src/sys/unix/linux/filetime.rs @@ -1,6 +1,6 @@ //! This internal module consists of helper types and functions for dealing //! with setting the file times specific to Linux. -use super::super::filetime::FileTime; +use crate::{sys::unix::filetime::FileTime, Result}; use std::fs::File; use std::io; use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; @@ -20,8 +20,8 @@ pub(crate) fn utimensat( atime: FileTime, mtime: FileTime, symlink_nofollow: bool, -) -> io::Result<()> { - use super::super::filetime::{to_timespec, utimesat}; +) -> Result<()> { + use crate::sys::unix::filetime::to_timespec; use std::ffi::CString; use std::os::unix::prelude::*; @@ -53,9 +53,9 @@ pub(crate) fn utimensat( if err.raw_os_error() == Some(libc::ENOSYS) { INVALID.store(true, Relaxed); } else { - return Err(err); + return Err(err.into()); } } - utimesat(dirfd, path, atime, mtime, symlink_nofollow) + super::utimesat::utimesat(dirfd, path, atime, mtime, symlink_nofollow) } diff --git a/crates/wasi-common/src/sys/unix/linux/host_impl.rs b/crates/wasi-common/src/sys/unix/linux/host_impl.rs new file mode 100644 index 0000000000..d7a3e46fdb --- /dev/null +++ b/crates/wasi-common/src/sys/unix/linux/host_impl.rs @@ -0,0 +1,11 @@ +use crate::{wasi, Result}; + +pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC; + +pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { + Ok(wasi::__wasi_device_t::from(dev)) +} + +pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { + Ok(wasi::__wasi_device_t::from(ino)) +} diff --git a/crates/wasi-common/src/sys/unix/linux/mod.rs b/crates/wasi-common/src/sys/unix/linux/mod.rs index a9fd0557d5..0b4b8fd0b9 100644 --- a/crates/wasi-common/src/sys/unix/linux/mod.rs +++ b/crates/wasi-common/src/sys/unix/linux/mod.rs @@ -1,17 +1,5 @@ pub(crate) mod filetime; +pub(crate) mod host_impl; pub(crate) mod hostcalls_impl; pub(crate) mod oshandle; - -pub(crate) mod host_impl { - use crate::{wasi, Result}; - - pub(crate) const O_RSYNC: yanix::file::OFlag = yanix::file::OFlag::RSYNC; - - pub(crate) fn stdev_from_nix(dev: libc::dev_t) -> Result { - Ok(wasi::__wasi_device_t::from(dev)) - } - - pub(crate) fn stino_from_nix(ino: libc::ino_t) -> Result { - Ok(wasi::__wasi_device_t::from(ino)) - } -} +pub(crate) mod utimesat; diff --git a/crates/wasi-common/src/sys/unix/linux/utimesat.rs b/crates/wasi-common/src/sys/unix/linux/utimesat.rs new file mode 100644 index 0000000000..c99e6e0610 --- /dev/null +++ b/crates/wasi-common/src/sys/unix/linux/utimesat.rs @@ -0,0 +1,88 @@ +use crate::sys::unix::filetime::FileTime; +use crate::Result; +use std::{fs, io}; + +/// Combines `openat` with `utimes` to emulate `utimensat` on platforms where it is +/// not available. The logic for setting file times is based on [filetime::unix::set_file_handles_times]. +/// +/// [filetime::unix::set_file_handles_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L24 +pub(crate) fn utimesat( + dirfd: &fs::File, + path: &str, + atime: FileTime, + mtime: FileTime, + symlink_nofollow: bool, +) -> Result<()> { + use std::ffi::CString; + use std::os::unix::prelude::*; + // emulate *at syscall by reading the path from a combination of + // (fd, path) + let p = CString::new(path.as_bytes())?; + let mut flags = libc::O_RDWR; + if symlink_nofollow { + flags |= libc::O_NOFOLLOW; + } + let fd = unsafe { libc::openat(dirfd.as_raw_fd(), p.as_ptr(), flags) }; + let f = unsafe { fs::File::from_raw_fd(fd) }; + let (atime, mtime) = get_times(atime, mtime, || f.metadata().map_err(Into::into))?; + let times = [to_timeval(atime), to_timeval(mtime)]; + let rc = unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) }; + if rc == 0 { + Ok(()) + } else { + Err(io::Error::last_os_error().into()) + } +} + +/// Converts `filetime::FileTime` to `libc::timeval`. This function was taken directly from +/// [filetime] crate. +/// +/// [filetime]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L93 +fn to_timeval(ft: filetime::FileTime) -> libc::timeval { + libc::timeval { + tv_sec: ft.seconds(), + tv_usec: (ft.nanoseconds() / 1000) as libc::suseconds_t, + } +} + +/// For a provided pair of access and modified `FileTime`s, converts the input to +/// `filetime::FileTime` used later in `utimensat` function. For variants `FileTime::Now` +/// and `FileTime::Omit`, this function will make two syscalls: either accessing current +/// system time, or accessing the file's metadata. +/// +/// The original implementation can be found here: [filetime::unix::get_times]. +/// +/// [filetime::unix::get_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L42 +fn get_times( + atime: FileTime, + mtime: FileTime, + current: impl Fn() -> Result, +) -> Result<(filetime::FileTime, filetime::FileTime)> { + use std::time::SystemTime; + + let atime = match atime { + FileTime::Now => { + let time = SystemTime::now(); + filetime::FileTime::from_system_time(time) + } + FileTime::Omit => { + let meta = current()?; + filetime::FileTime::from_last_access_time(&meta) + } + FileTime::FileTime(ft) => ft, + }; + + let mtime = match mtime { + FileTime::Now => { + let time = SystemTime::now(); + filetime::FileTime::from_system_time(time) + } + FileTime::Omit => { + let meta = current()?; + filetime::FileTime::from_last_modification_time(&meta) + } + FileTime::FileTime(ft) => ft, + }; + + Ok((atime, mtime)) +} diff --git a/crates/wasi-common/src/sys/unix/mod.rs b/crates/wasi-common/src/sys/unix/mod.rs index 7aa05706ff..60b08ab355 100644 --- a/crates/wasi-common/src/sys/unix/mod.rs +++ b/crates/wasi-common/src/sys/unix/mod.rs @@ -4,17 +4,23 @@ pub(crate) mod hostcalls_impl; mod filetime; -#[cfg(any( - target_os = "macos", - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "ios", - target_os = "dragonfly" -))] -mod bsd; -#[cfg(target_os = "linux")] -mod linux; +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + mod linux; + use self::linux as sys_impl; + } else if #[cfg(target_os = "emscripten")] { + mod emscripten; + use self::emscripten as sys_impl; + } else if #[cfg(any(target_os = "macos", + target_os = "netbsd", + target_os = "freebsd", + target_os = "openbsd", + target_os = "ios", + target_os = "dragonfly"))] { + mod bsd; + use self::bsd as sys_impl; + } +} use crate::Result; use std::fs::{File, OpenOptions}; diff --git a/crates/wasi-common/yanix/src/dir.rs b/crates/wasi-common/yanix/src/dir.rs index e395491f4e..a62a451d2a 100644 --- a/crates/wasi-common/yanix/src/dir.rs +++ b/crates/wasi-common/yanix/src/dir.rs @@ -84,14 +84,10 @@ impl Entry { #[cfg(not(target_os = "android"))] #[derive(Clone, Copy, Debug)] -pub struct SeekLoc(libc::c_long); +pub struct SeekLoc(pub(crate) libc::c_long); #[cfg(not(target_os = "android"))] impl SeekLoc { - pub unsafe fn from_raw(loc: i64) -> Self { - Self(loc.into()) - } - pub fn to_raw(&self) -> i64 { self.0.into() } diff --git a/crates/wasi-common/yanix/src/sys/bsd/dir.rs b/crates/wasi-common/yanix/src/sys/bsd/dir.rs index fee320e3e5..f9255eac4a 100644 --- a/crates/wasi-common/yanix/src/sys/bsd/dir.rs +++ b/crates/wasi-common/yanix/src/sys/bsd/dir.rs @@ -46,7 +46,14 @@ impl EntryExt for Entry { self.0.d_ino.into() } - fn seek_loc(&self) -> SeekLoc { - self.0.loc + fn seek_loc(&self) -> Result { + Ok(self.0.loc) + } +} + +impl SeekLoc { + pub unsafe fn from_raw(loc: i64) -> Result { + let loc = loc.into(); + Ok(Self(loc)) } } diff --git a/crates/wasi-common/yanix/src/sys/emscripten/mod.rs b/crates/wasi-common/yanix/src/sys/emscripten/mod.rs new file mode 100644 index 0000000000..b9edc97e32 --- /dev/null +++ b/crates/wasi-common/yanix/src/sys/emscripten/mod.rs @@ -0,0 +1,16 @@ +#[path = "../linux/dir.rs"] +pub(crate) mod dir; +#[path = "../linux/fadvise.rs"] +pub(crate) mod fadvise; +#[path = "../linux/file.rs"] +pub(crate) mod file; + +use crate::{dir::SeekLoc, Result}; +use std::convert::TryInto; + +impl SeekLoc { + pub unsafe fn from_raw(loc: i64) -> Result { + let loc = loc.try_into()?; + Ok(Self(loc)) + } +} diff --git a/crates/wasi-common/yanix/src/sys/linux/dir.rs b/crates/wasi-common/yanix/src/sys/linux/dir.rs index 72349e3cf4..90fb45a3b6 100644 --- a/crates/wasi-common/yanix/src/sys/linux/dir.rs +++ b/crates/wasi-common/yanix/src/sys/linux/dir.rs @@ -20,7 +20,7 @@ impl EntryExt for Entry { self.0.d_ino.into() } - fn seek_loc(&self) -> SeekLoc { + fn seek_loc(&self) -> Result { unsafe { SeekLoc::from_raw(self.0.d_off) } } } diff --git a/crates/wasi-common/yanix/src/sys/linux/mod.rs b/crates/wasi-common/yanix/src/sys/linux/mod.rs index 3e6611f7b9..d7beaa7cbe 100644 --- a/crates/wasi-common/yanix/src/sys/linux/mod.rs +++ b/crates/wasi-common/yanix/src/sys/linux/mod.rs @@ -1,3 +1,12 @@ pub(crate) mod dir; pub(crate) mod fadvise; pub(crate) mod file; + +use crate::{dir::SeekLoc, Result}; + +impl SeekLoc { + pub unsafe fn from_raw(loc: i64) -> Result { + let loc = loc.into(); + Ok(Self(loc)) + } +} diff --git a/crates/wasi-common/yanix/src/sys/mod.rs b/crates/wasi-common/yanix/src/sys/mod.rs index 523e9bf754..72cdcb44fa 100644 --- a/crates/wasi-common/yanix/src/sys/mod.rs +++ b/crates/wasi-common/yanix/src/sys/mod.rs @@ -1,19 +1,20 @@ -use crate::dir::SeekLoc; +use crate::{dir::SeekLoc, Result}; use cfg_if::cfg_if; cfg_if! { if #[cfg(any(target_os = "linux", - target_os = "android", - target_os = "emscripten"))] { + target_os = "android"))] { mod linux; pub(crate) use self::linux::*; - } - else if #[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "dragonfly"))] { + } else if #[cfg(target_os = "emscripten")] { + mod emscripten; + pub(crate) use self::emscripten::*; + } else if #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly"))] { mod bsd; pub(crate) use self::bsd::*; } else { @@ -23,5 +24,5 @@ cfg_if! { pub trait EntryExt { fn ino(&self) -> u64; - fn seek_loc(&self) -> SeekLoc; + fn seek_loc(&self) -> Result; }