diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 779b07ce240a6..09e228dd62c74 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -75,7 +75,7 @@ hermit-abi = { version = "0.5.0", features = [ 'rustc-dep-of-std', ], public = true } -[target.'cfg(target_os = "wasi")'.dependencies] +[target.'cfg(all(target_os = "wasi", target_env = "p1"))'.dependencies] wasi = { version = "0.11.0", features = [ 'rustc-dep-of-std', ], default-features = false } diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 5ea91dd6521ad..981b29751409c 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -1,4 +1,4 @@ -//! WASI-specific extensions to primitives in the [`std::fs`] module. +//! WASIp1-specific extensions to primitives in the [`std::fs`] module. //! //! [`std::fs`]: crate::fs @@ -9,9 +9,11 @@ use io::{Read, Write}; use crate::ffi::OsStr; -use crate::fs::{self, File, Metadata, OpenOptions}; +use crate::fs::{self, File, OpenOptions}; use crate::io::{self, IoSlice, IoSliceMut}; +use crate::os::fd::AsRawFd; use crate::path::{Path, PathBuf}; +use crate::sys::err2io; use crate::sys_common::{AsInner, AsInnerMut, FromInner}; /// WASI-specific extensions to [`File`]. @@ -200,7 +202,7 @@ pub trait FileExt { /// /// This corresponds to the `path_filestat_get` syscall. #[doc(alias = "path_filestat_get")] - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; + fn metadata_at>(&self, lookup_flags: i32, path: P) -> io::Result; /// Unlinks a file. /// @@ -224,19 +226,24 @@ pub trait FileExt { impl FileExt for fs::File { fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - self.as_inner().as_inner().pread(bufs, offset) + let bufs = crate::sys::io::IoSliceMut::as_wasip1_slice(bufs); + unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, bufs, offset).map_err(err2io) } } fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - self.as_inner().as_inner().pwrite(bufs, offset) + let bufs = crate::sys::io::IoSlice::as_wasip1_slice(bufs); + unsafe { wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, bufs, offset).map_err(err2io) } } fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { - self.as_inner().as_inner().set_flags(flags) + unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } } fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { - self.as_inner().as_inner().set_rights(rights, inheriting) + unsafe { + wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, rights, inheriting) + .map_err(err2io) + } } fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { @@ -255,32 +262,37 @@ impl FileExt for fs::File { } }; - self.as_inner().as_inner().advise(offset, len, advice) + unsafe { + wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) + } } fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - self.as_inner().as_inner().allocate(offset, len) + unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) } } fn create_directory>(&self, dir: P) -> io::Result<()> { - self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?) + let path = osstr2str(dir.as_ref().as_ref())?; + unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } fn read_link>(&self, path: P) -> io::Result { - self.as_inner().read_link(path.as_ref()) + self.as_inner().readlink_at(path.as_ref()) } - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result { + fn metadata_at>(&self, lookup_flags: i32, path: P) -> io::Result { let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?; Ok(FromInner::from_inner(m)) } fn remove_file>(&self, path: P) -> io::Result<()> { - self.as_inner().as_inner().unlink_file(osstr2str(path.as_ref().as_ref())?) + let path = osstr2str(path.as_ref().as_ref())?; + unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } fn remove_directory>(&self, path: P) -> io::Result<()> { - self.as_inner().as_inner().remove_directory(osstr2str(path.as_ref().as_ref())?) + let path = osstr2str(path.as_ref().as_ref())?; + unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } } @@ -355,42 +367,42 @@ pub trait OpenOptionsExt { impl OpenOptionsExt for OpenOptions { fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions { - self.as_inner_mut().lookup_flags(flags); + self.as_inner_mut().wasip1_lookup_flags(flags); self } fn directory(&mut self, dir: bool) -> &mut OpenOptions { - self.as_inner_mut().directory(dir); + self.as_inner_mut().wasip1_directory(dir); self } fn dsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().dsync(enabled); + self.as_inner_mut().wasip1_dsync(enabled); self } fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().nonblock(enabled); + self.as_inner_mut().wasip1_nonblock(enabled); self } fn rsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().rsync(enabled); + self.as_inner_mut().wasip1_rsync(enabled); self } fn sync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().sync(enabled); + self.as_inner_mut().wasip1_sync(enabled); self } fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_base(rights); + self.as_inner_mut().wasip1_fs_rights_base(rights); self } fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_inheriting(rights); + self.as_inner_mut().wasip1_fs_rights_inheriting(rights); self } @@ -408,37 +420,17 @@ pub trait MetadataExt { fn ino(&self) -> u64; /// Returns the `st_nlink` field of the internal `filestat_t` fn nlink(&self) -> u64; - /// Returns the `st_size` field of the internal `filestat_t` - fn size(&self) -> u64; - /// Returns the `st_atim` field of the internal `filestat_t` - fn atim(&self) -> u64; - /// Returns the `st_mtim` field of the internal `filestat_t` - fn mtim(&self) -> u64; - /// Returns the `st_ctim` field of the internal `filestat_t` - fn ctim(&self) -> u64; } impl MetadataExt for fs::Metadata { fn dev(&self) -> u64 { - self.as_inner().as_wasi().dev + self.as_inner().as_libc().st_dev } fn ino(&self) -> u64 { - self.as_inner().as_wasi().ino + self.as_inner().as_libc().st_ino } fn nlink(&self) -> u64 { - self.as_inner().as_wasi().nlink - } - fn size(&self) -> u64 { - self.as_inner().as_wasi().size - } - fn atim(&self) -> u64 { - self.as_inner().as_wasi().atim - } - fn mtim(&self) -> u64 { - self.as_inner().as_wasi().mtim - } - fn ctim(&self) -> u64 { - self.as_inner().as_wasi().ctim + self.as_inner().as_libc().st_nlink } } @@ -451,28 +443,19 @@ pub trait FileTypeExt { fn is_block_device(&self) -> bool; /// Returns `true` if this file type is a character device. fn is_char_device(&self) -> bool; - /// Returns `true` if this file type is a socket datagram. - fn is_socket_dgram(&self) -> bool; - /// Returns `true` if this file type is a socket stream. - fn is_socket_stream(&self) -> bool; /// Returns `true` if this file type is any type of socket. - fn is_socket(&self) -> bool { - self.is_socket_stream() || self.is_socket_dgram() - } + fn is_socket(&self) -> bool; } impl FileTypeExt for fs::FileType { fn is_block_device(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE + self.as_inner().mode() == libc::S_IFBLK } fn is_char_device(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE - } - fn is_socket_dgram(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM + self.as_inner().mode() == libc::S_IFCHR } - fn is_socket_stream(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM + fn is_socket(&self) -> bool { + self.as_inner().mode() == libc::S_IFSOCK } } @@ -499,12 +482,16 @@ pub fn link, U: AsRef>( new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().as_inner().link( - old_flags, - osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().as_inner(), - osstr2str(new_path.as_ref().as_ref())?, - ) + unsafe { + wasi::path_link( + old_fd.as_raw_fd() as wasi::Fd, + old_flags, + osstr2str(old_path.as_ref().as_ref())?, + new_fd.as_raw_fd() as wasi::Fd, + osstr2str(new_path.as_ref().as_ref())?, + ) + .map_err(err2io) + } } /// Renames a file or directory. @@ -517,11 +504,15 @@ pub fn rename, U: AsRef>( new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().as_inner().rename( - osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().as_inner(), - osstr2str(new_path.as_ref().as_ref())?, - ) + unsafe { + wasi::path_rename( + old_fd.as_raw_fd() as wasi::Fd, + osstr2str(old_path.as_ref().as_ref())?, + new_fd.as_raw_fd() as wasi::Fd, + osstr2str(new_path.as_ref().as_ref())?, + ) + .map_err(err2io) + } } /// Creates a symbolic link. @@ -533,9 +524,14 @@ pub fn symlink, U: AsRef>( fd: &File, new_path: U, ) -> io::Result<()> { - fd.as_inner() - .as_inner() - .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) + unsafe { + wasi::path_symlink( + osstr2str(old_path.as_ref().as_ref())?, + fd.as_raw_fd() as wasi::Fd, + osstr2str(new_path.as_ref().as_ref())?, + ) + .map_err(err2io) + } } /// Creates a symbolic link. diff --git a/library/std/src/os/wasi/mod.rs b/library/std/src/os/wasi/mod.rs index 2ee6aa4660094..a922c4cadef89 100644 --- a/library/std/src/os/wasi/mod.rs +++ b/library/std/src/os/wasi/mod.rs @@ -34,6 +34,7 @@ #![doc(cfg(target_os = "wasi"))] pub mod ffi; +#[cfg(target_env = "p1")] pub mod fs; pub mod io; @@ -50,9 +51,11 @@ pub mod prelude { pub use super::ffi::{OsStrExt, OsStringExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(target_env = "p1")] pub use super::fs::FileTypeExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(target_env = "p1")] pub use super::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/wasi/net/mod.rs b/library/std/src/os/wasi/net/mod.rs index 4704dd574517a..9430cd3b05eee 100644 --- a/library/std/src/os/wasi/net/mod.rs +++ b/library/std/src/os/wasi/net/mod.rs @@ -2,7 +2,8 @@ #![unstable(feature = "wasi_ext", issue = "71213")] -use crate::sys_common::AsInner; +use crate::os::fd::AsRawFd; +use crate::sys::err2io; use crate::{io, net}; /// WASI-specific extensions to [`std::net::TcpListener`]. @@ -17,6 +18,6 @@ pub trait TcpListenerExt { impl TcpListenerExt for net::TcpListener { fn sock_accept(&self, flags: u16) -> io::Result { - self.as_inner().as_inner().as_inner().sock_accept(flags) + unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } } } diff --git a/library/std/src/sys/fd/wasi.rs b/library/std/src/sys/fd/wasi.rs index 80a5143ff0b00..febc8de83605b 100644 --- a/library/std/src/sys/fd/wasi.rs +++ b/library/std/src/sys/fd/wasi.rs @@ -1,10 +1,6 @@ -#![expect(dead_code)] - -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::net::Shutdown; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::pal::err2io; +use crate::sys::os::cvt; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[derive(Debug)] @@ -12,271 +8,34 @@ pub struct WasiFd { fd: OwnedFd, } -fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { - assert_eq!(size_of::>(), size_of::()); - assert_eq!(align_of::>(), align_of::()); - // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout. - // We decorate our `IoSliceMut` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSliceMut` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - unsafe { mem::transmute(a) } -} - -fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { - assert_eq!(size_of::>(), size_of::()); - assert_eq!(align_of::>(), align_of::()); - // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout. - // We decorate our `IoSlice` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSlice` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - unsafe { mem::transmute(a) } -} - impl WasiFd { - pub fn datasync(&self) -> io::Result<()> { - unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) } - } - - pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let bufs = crate::sys::io::IoSliceMut::as_libc_slice(bufs); unsafe { - wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io) + let n = cvt(libc::readv(self.as_raw_fd(), bufs.as_ptr(), bufs.len() as libc::c_int))?; + Ok(n as usize) } } - pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } - } - pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { unsafe { - let bufs = [wasi::Iovec { - buf: buf.as_mut().as_mut_ptr() as *mut u8, - buf_len: buf.capacity(), - }]; - match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) { - Ok(n) => { - buf.advance_unchecked(n); - Ok(()) - } - Err(e) => Err(err2io(e)), - } + let amt = cvt(libc::read( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr().cast(), + buf.as_mut().len(), + ))?; + buf.advance_unchecked(amt as usize); + Ok(()) } } pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) } - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - let (whence, offset) = match pos { - SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64), - SeekFrom::End(pos) => (wasi::WHENCE_END, pos), - SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos), - }; - unsafe { wasi::fd_seek(self.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) } - } - - pub fn tell(&self) -> io::Result { - unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - // FIXME: __wasi_fd_fdstat_get - - pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> { - unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } - } - - pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> { - unsafe { - wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, base, inheriting) - .map_err(err2io) - } - } - - pub fn sync(&self) -> io::Result<()> { - unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub(crate) fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { - unsafe { - wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) - } - } - - pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) } - } - - pub fn create_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn link( - &self, - old_flags: wasi::Lookupflags, - old_path: &str, - new_fd: &WasiFd, - new_path: &str, - ) -> io::Result<()> { - unsafe { - wasi::path_link( - self.as_raw_fd() as wasi::Fd, - old_flags, - old_path, - new_fd.as_raw_fd() as wasi::Fd, - new_path, - ) - .map_err(err2io) - } - } - - pub fn open( - &self, - dirflags: wasi::Lookupflags, - path: &str, - oflags: wasi::Oflags, - fs_rights_base: wasi::Rights, - fs_rights_inheriting: wasi::Rights, - fs_flags: wasi::Fdflags, - ) -> io::Result { - unsafe { - wasi::path_open( - self.as_raw_fd() as wasi::Fd, - dirflags, - path, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - ) - .map(|fd| WasiFd::from_raw_fd(fd as RawFd)) - .map_err(err2io) - } - } - - pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result { + let bufs = crate::sys::io::IoSlice::as_libc_slice(bufs); unsafe { - wasi::fd_readdir(self.as_raw_fd() as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie) - .map_err(err2io) + let n = cvt(libc::writev(self.as_raw_fd(), bufs.as_ptr(), bufs.len() as libc::c_int))?; + Ok(n as usize) } } - - pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result { - unsafe { - wasi::path_readlink(self.as_raw_fd() as wasi::Fd, path, buf.as_mut_ptr(), buf.len()) - .map_err(err2io) - } - } - - pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> { - unsafe { - wasi::path_rename( - self.as_raw_fd() as wasi::Fd, - old_path, - new_fd.as_raw_fd() as wasi::Fd, - new_path, - ) - .map_err(err2io) - } - } - - pub(crate) fn filestat_get(&self) -> io::Result { - unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub fn filestat_set_times( - &self, - atim: wasi::Timestamp, - mtim: wasi::Timestamp, - fstflags: wasi::Fstflags, - ) -> io::Result<()> { - unsafe { - wasi::fd_filestat_set_times(self.as_raw_fd() as wasi::Fd, atim, mtim, fstflags) - .map_err(err2io) - } - } - - pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { - unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } - } - - pub(crate) fn path_filestat_get( - &self, - flags: wasi::Lookupflags, - path: &str, - ) -> io::Result { - unsafe { - wasi::path_filestat_get(self.as_raw_fd() as wasi::Fd, flags, path).map_err(err2io) - } - } - - pub fn path_filestat_set_times( - &self, - flags: wasi::Lookupflags, - path: &str, - atim: wasi::Timestamp, - mtim: wasi::Timestamp, - fstflags: wasi::Fstflags, - ) -> io::Result<()> { - unsafe { - wasi::path_filestat_set_times( - self.as_raw_fd() as wasi::Fd, - flags, - path, - atim, - mtim, - fstflags, - ) - .map_err(err2io) - } - } - - pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> { - unsafe { - wasi::path_symlink(old_path, self.as_raw_fd() as wasi::Fd, new_path).map_err(err2io) - } - } - - pub fn unlink_file(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn remove_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn sock_accept(&self, flags: wasi::Fdflags) -> io::Result { - unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } - } - - pub fn sock_recv( - &self, - ri_data: &mut [IoSliceMut<'_>], - ri_flags: wasi::Riflags, - ) -> io::Result<(usize, wasi::Roflags)> { - unsafe { - wasi::sock_recv(self.as_raw_fd() as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io) - } - } - - pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result { - unsafe { - wasi::sock_send(self.as_raw_fd() as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io) - } - } - - pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Read => wasi::SDFLAGS_RD, - Shutdown::Write => wasi::SDFLAGS_WR, - Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD, - }; - unsafe { wasi::sock_shutdown(self.as_raw_fd() as wasi::Fd, how).map_err(err2io) } - } } impl AsInner for WasiFd { diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index 0b65b9cb389df..37201b0cf44a6 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -1,7 +1,7 @@ -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, ManuallyDrop}; +use crate::mem::ManuallyDrop; use crate::os::raw::c_int; use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; @@ -21,43 +21,25 @@ pub struct File { #[derive(Clone)] pub struct FileAttr { - meta: wasi::Filestat, + stat: libc::stat, } pub struct ReadDir { inner: Arc, - state: ReadDirState, -} - -enum ReadDirState { - /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`. - FillBuffer { - next_read_offset: wasi::Dircookie, - buf: Vec, - }, - ProcessEntry { - buf: Vec, - next_read_offset: Option, - offset: usize, - }, - /// There is no more data to get in [`Self::FillBuffer`]; keep returning - /// entries via ProcessEntry until `buf` is exhausted. - RunUntilExhaustion { - buf: Vec, - offset: usize, - }, - Done, + done: bool, } struct ReadDirInner { root: PathBuf, - dir: File, + dirp: c::Dirp, } pub struct DirEntry { - meta: wasi::Dirent, - name: Vec, inner: Arc, + #[cfg(target_env = "p1")] + d_ino: libc::ino_t, + d_type: libc::c_uchar, + name: CString, } #[derive(Clone, Debug, Default)] @@ -65,11 +47,21 @@ pub struct OpenOptions { read: bool, write: bool, append: bool, - dirflags: wasi::Lookupflags, - fdflags: wasi::Fdflags, - oflags: wasi::Oflags, - rights_base: Option, - rights_inheriting: Option, + truncate: bool, + create: bool, + create_new: bool, + custom_flags: libc::c_int, + + #[cfg(target_env = "p1")] + use_wasip1: bool, + #[cfg(target_env = "p1")] + wasip1_dirflags: Option, + #[cfg(target_env = "p1")] + wasip1_fdflags: wasi::Fdflags, + #[cfg(target_env = "p1")] + wasip1_rights_base: Option, + #[cfg(target_env = "p1")] + wasip1_rights_inheriting: Option, } #[derive(Clone, PartialEq, Eq, Debug)] @@ -85,7 +77,7 @@ pub struct FileTimes { #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] pub struct FileType { - bits: wasi::Filetype, + mode: libc::mode_t, } #[derive(Debug)] @@ -93,7 +85,7 @@ pub struct DirBuilder {} impl FileAttr { pub fn size(&self) -> u64 { - self.meta.size + self.stat.st_size as u64 } pub fn perm(&self) -> FilePermissions { @@ -102,23 +94,24 @@ impl FileAttr { } pub fn file_type(&self) -> FileType { - FileType { bits: self.meta.filetype } + FileType { mode: self.stat.st_mode } } pub fn modified(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.mtim)) + Ok(SystemTime::from_timespec(self.stat.st_mtim)) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.atim)) + Ok(SystemTime::from_timespec(self.stat.st_atim)) } pub fn created(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) + Ok(SystemTime::from_timespec(self.stat.st_ctim)) } - pub(crate) fn as_wasi(&self) -> &wasi::Filestat { - &self.meta + #[cfg(target_env = "p1")] + pub(crate) fn as_libc(&self) -> &libc::stat { + &self.stat } } @@ -144,28 +137,27 @@ impl FileTimes { impl FileType { pub fn is_dir(&self) -> bool { - self.bits == wasi::FILETYPE_DIRECTORY + self.mode == libc::S_IFDIR } pub fn is_file(&self) -> bool { - self.bits == wasi::FILETYPE_REGULAR_FILE + self.mode == libc::S_IFREG } pub fn is_symlink(&self) -> bool { - self.bits == wasi::FILETYPE_SYMBOLIC_LINK + self.mode == libc::S_IFLNK } - pub(crate) fn bits(&self) -> wasi::Filetype { - self.bits + #[cfg(target_env = "p1")] + pub(crate) fn mode(&self) -> libc::mode_t { + self.mode } } impl ReadDir { - fn new(dir: File, root: PathBuf) -> ReadDir { - ReadDir { - inner: Arc::new(ReadDirInner { dir, root }), - state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] }, - } + fn new(dir: File, root: PathBuf) -> io::Result { + let dirp = c::Dirp::new(dir)?; + Ok(ReadDir { inner: Arc::new(ReadDirInner { dirp, root }), done: false }) } } @@ -181,125 +173,83 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - match &mut self.state { - ReadDirState::FillBuffer { next_read_offset, buf } => { - let result = self.inner.dir.fd.readdir(buf, *next_read_offset); - match result { - Ok(read_bytes) => { - if read_bytes < buf.len() { - buf.truncate(read_bytes); - self.state = - ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 }; - } else { - debug_assert_eq!(read_bytes, buf.len()); - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: 0, - next_read_offset: Some(*next_read_offset), - }; - } - self.next() - } - Err(e) => { - self.state = ReadDirState::Done; - return Some(Err(e)); - } - } - } - ReadDirState::ProcessEntry { buf, next_read_offset, offset } => { - let contents = &buf[*offset..]; - const DIRENT_SIZE: usize = size_of::(); - if contents.len() >= DIRENT_SIZE { - let (dirent, data) = contents.split_at(DIRENT_SIZE); - let dirent = - unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; - // If the file name was truncated, then we need to reinvoke - // `readdir` so we truncate our buffer to start over and reread this - // descriptor. - if data.len() < dirent.d_namlen as usize { - if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE { - buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0); - } - if let Some(next_read_offset) = *next_read_offset { - self.state = - ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; - } - - return self.next(); - } - next_read_offset.as_mut().map(|cookie| { - *cookie = dirent.d_next; - }); - *offset = *offset + DIRENT_SIZE + dirent.d_namlen as usize; - - let name = &data[..(dirent.d_namlen as usize)]; - - // These names are skipped on all other platforms, so let's skip - // them here too - if name == b"." || name == b".." { - return self.next(); - } + if self.done { + return None; + } - return Some(Ok(DirEntry { - meta: dirent, - name: name.to_vec(), - inner: self.inner.clone(), - })); - } else if let Some(next_read_offset) = *next_read_offset { - self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; + loop { + let entry_ptr = match self.inner.dirp.readdir() { + Ok(Some(ptr)) => ptr, + Ok(None) => { + self.done = true; + return None; } - self.next() - } - ReadDirState::RunUntilExhaustion { buf, offset } => { - if *offset >= buf.len() { - self.state = ReadDirState::Done; - } else { - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: *offset, - next_read_offset: None, - }; + Err(e) => { + self.done = true; + return Some(Err(e)); + } + }; + + unsafe { + let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); + let name_bytes = name.to_bytes(); + if name_bytes == b"." || name_bytes == b".." { + continue; } - self.next() + return Some(Ok(DirEntry { + d_type: (*entry_ptr).d_type, + #[cfg(target_env = "p1")] + d_ino: (*entry_ptr).d_ino, + name: name.to_owned(), + inner: Arc::clone(&self.inner), + })); } - ReadDirState::Done => None, } } } impl DirEntry { pub fn path(&self) -> PathBuf { - let name = OsStr::from_bytes(&self.name); - self.inner.root.join(name) + self.inner.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - OsString::from_vec(self.name.clone()) + self.file_name_os_str().to_owned() + } + + fn file_name_os_str(&self) -> &OsStr { + OsStr::from_bytes(self.name.to_bytes()) } pub fn metadata(&self) -> io::Result { - metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) + c::fstatat(self.dir_fd(), &self.name, libc::AT_SYMLINK_NOFOLLOW) } pub fn file_type(&self) -> io::Result { - Ok(FileType { bits: self.meta.d_type }) + match self.d_type { + libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), + libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), + libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), + libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), + libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), + _ => self.metadata().map(|m| m.file_type()), + } } + #[cfg(target_env = "p1")] pub fn ino(&self) -> wasi::Inode { - self.meta.d_ino + self.d_ino + } + + fn dir_fd(&self) -> BorrowedFd<'_> { + self.inner.dirp.as_fd() } } impl OpenOptions { pub fn new() -> OpenOptions { - let mut base = OpenOptions::default(); - base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW; - base + OpenOptions::default() } pub fn read(&mut self, read: bool) { @@ -311,69 +261,68 @@ impl OpenOptions { } pub fn truncate(&mut self, truncate: bool) { - self.oflag(wasi::OFLAGS_TRUNC, truncate); + self.truncate = truncate; } pub fn create(&mut self, create: bool) { - self.oflag(wasi::OFLAGS_CREAT, create); + self.create = create; } pub fn create_new(&mut self, create_new: bool) { - self.oflag(wasi::OFLAGS_EXCL, create_new); - self.oflag(wasi::OFLAGS_CREAT, create_new); - } - - pub fn directory(&mut self, directory: bool) { - self.oflag(wasi::OFLAGS_DIRECTORY, directory); - } - - fn oflag(&mut self, bit: wasi::Oflags, set: bool) { - if set { - self.oflags |= bit; - } else { - self.oflags &= !bit; - } + self.create_new = create_new; } pub fn append(&mut self, append: bool) { self.append = append; - self.fdflag(wasi::FDFLAGS_APPEND, append); + #[cfg(target_env = "p1")] + self.wasip1_fdflag(wasi::FDFLAGS_APPEND, append); } - pub fn dsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_DSYNC, set); + #[cfg(target_env = "p1")] + pub fn wasip1_dsync(&mut self, set: bool) { + self.wasip1_fdflag(wasi::FDFLAGS_DSYNC, set); } - pub fn nonblock(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_NONBLOCK, set); + #[cfg(target_env = "p1")] + pub fn wasip1_nonblock(&mut self, set: bool) { + self.wasip1_fdflag(wasi::FDFLAGS_NONBLOCK, set); } - pub fn rsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_RSYNC, set); + #[cfg(target_env = "p1")] + pub fn wasip1_rsync(&mut self, set: bool) { + self.wasip1_fdflag(wasi::FDFLAGS_RSYNC, set); } - pub fn sync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_SYNC, set); + #[cfg(target_env = "p1")] + pub fn wasip1_sync(&mut self, set: bool) { + self.wasip1_fdflag(wasi::FDFLAGS_SYNC, set); } - fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) { + #[cfg(target_env = "p1")] + fn wasip1_fdflag(&mut self, bit: wasi::Fdflags, set: bool) { + self.use_wasip1 = true; if set { - self.fdflags |= bit; + self.wasip1_fdflags |= bit; } else { - self.fdflags &= !bit; + self.wasip1_fdflags &= !bit; } } - pub fn fs_rights_base(&mut self, rights: wasi::Rights) { - self.rights_base = Some(rights); + #[cfg(target_env = "p1")] + pub fn wasip1_fs_rights_base(&mut self, rights: wasi::Rights) { + self.use_wasip1 = true; + self.wasip1_rights_base = Some(rights); } - pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) { - self.rights_inheriting = Some(rights); + #[cfg(target_env = "p1")] + pub fn wasip1_fs_rights_inheriting(&mut self, rights: wasi::Rights) { + self.use_wasip1 = true; + self.wasip1_rights_inheriting = Some(rights); } - fn rights_base(&self) -> wasi::Rights { - if let Some(rights) = self.rights_base { + #[cfg(target_env = "p1")] + fn wasip1_rights_base(&self) -> wasi::Rights { + if let Some(rights) = self.wasip1_rights_base { return rights; } @@ -419,39 +368,150 @@ impl OpenOptions { base } - fn rights_inheriting(&self) -> wasi::Rights { - self.rights_inheriting.unwrap_or_else(|| self.rights_base()) + #[cfg(target_env = "p1")] + fn wasip1_rights_inheriting(&self) -> wasi::Rights { + self.wasip1_rights_inheriting.unwrap_or_else(|| self.wasip1_rights_base()) } - pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) { - self.dirflags = flags; + #[cfg(target_env = "p1")] + pub fn wasip1_lookup_flags(&mut self, flags: wasi::Lookupflags) { + self.use_wasip1 = true; + self.wasip1_dirflags = Some(flags); + } + + #[cfg(target_env = "p1")] + pub fn wasip1_directory(&mut self, enable: bool) { + if enable { + self.custom_flags |= libc::O_DIRECTORY; + } else { + self.custom_flags &= !libc::O_DIRECTORY; + } + self.use_wasip1 = true; + } + + pub fn custom_flags(&mut self, flags: libc::c_int) { + self.custom_flags = flags; + } + + fn open_flags(&self) -> io::Result { + Ok(self.get_access_mode()? | self.get_creation_mode()? | self.custom_flags) + } + + fn get_access_mode(&self) -> io::Result { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(libc::O_RDONLY), + (false, true, false) => Ok(libc::O_WRONLY), + (true, true, false) => Ok(libc::O_RDWR), + (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), + (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), + (false, false, false) => { + // If no access mode is set, check if any creation flags are set + // to provide a more descriptive error message + if self.create || self.create_new || self.truncate { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "must specify at least one of read, write, or append access", + )) + } + } + } + } + + fn get_creation_mode(&self) -> io::Result { + match (self.write, self.append) { + (true, false) => {} + (false, false) => { + if self.truncate || self.create || self.create_new { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); + } + } + (_, true) => { + if self.truncate && !self.create_new { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "creating or truncating a file requires write or append access", + )); + } + } + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => libc::O_CREAT, + (false, true, false) => libc::O_TRUNC, + (true, true, false) => libc::O_CREAT | libc::O_TRUNC, + (_, _, true) => libc::O_CREAT | libc::O_EXCL, + }) + } + + fn open_mode(&self) -> libc::c_int { + 0o666 + } + + #[cfg(target_env = "p1")] + pub fn wasip1_oflags(&self) -> wasi::Oflags { + let mut flags = 0; + if self.create { + flags |= wasi::OFLAGS_CREAT; + } + if self.create_new { + flags |= wasi::OFLAGS_CREAT | wasi::OFLAGS_EXCL; + } + if self.truncate { + flags |= wasi::OFLAGS_TRUNC; + } + if self.custom_flags == libc::O_DIRECTORY { + flags |= wasi::OFLAGS_DIRECTORY; + } + flags } } impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { let (dir, file) = open_parent(path)?; - open_at(&dir, &file, opts) - } - - pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { - open_at(&self.fd, path, opts) + run_path_with_cstr(&file, &|file| File::open_c(dir.as_fd(), file, opts)) + } + + pub fn open_c(fd: BorrowedFd<'_>, path: &CStr, opts: &OpenOptions) -> io::Result { + #[cfg(target_env = "p1")] + if opts.use_wasip1 { + let fd = unsafe { + let fd = wasi::path_open( + fd.as_raw_fd() as wasi::Fd, + opts.wasip1_dirflags.unwrap_or(wasi::LOOKUPFLAGS_SYMLINK_FOLLOW), + path.to_str().map_err(|_| io::ErrorKind::InvalidInput)?, + opts.wasip1_oflags(), + opts.wasip1_rights_base(), + opts.wasip1_rights_inheriting(), + opts.wasip1_fdflags, + ) + .map_err(crate::sys::err2io)?; + WasiFd::from_raw_fd(fd as libc::c_int) + }; + return Ok(File { fd }); + } + c::openat(fd, path, opts.open_flags()?, Some(opts.open_mode())) } pub fn file_attr(&self) -> io::Result { - self.fd.filestat_get().map(|meta| FileAttr { meta }) - } - - pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result { - metadata_at(&self.fd, flags, path) + c::fstat(self.as_fd()) } pub fn fsync(&self) -> io::Result<()> { - self.fd.sync() + c::fsync(self.as_fd()) } pub fn datasync(&self) -> io::Result<()> { - self.fd.datasync() + c::fdatasync(self.as_fd()) } pub fn lock(&self) -> io::Result<()> { @@ -475,7 +535,8 @@ impl File { } pub fn truncate(&self, size: u64) -> io::Result<()> { - self.fd.filestat_set_size(size) + let size = size.try_into().map_err(|_| io::ErrorKind::InvalidInput)?; + c::ftruncate(self.as_fd(), size) } pub fn read(&self, buf: &mut [u8]) -> io::Result { @@ -513,7 +574,14 @@ impl File { } pub fn seek(&self, pos: SeekFrom) -> io::Result { - self.fd.seek(pos) + let (whence, pos) = match pos { + // Casting to `i64` is fine, too large values will end up as + // negative which will cause an error in `lseek64`. + SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), + SeekFrom::End(off) => (libc::SEEK_END, off), + SeekFrom::Current(off) => (libc::SEEK_CUR, off), + }; + c::lseek(self.as_fd(), pos, whence) } pub fn size(&self) -> Option> { @@ -521,7 +589,7 @@ impl File { } pub fn tell(&self) -> io::Result { - self.fd.tell() + self.seek(SeekFrom::Current(0)) } pub fn duplicate(&self) -> io::Result { @@ -536,24 +604,27 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - let to_timestamp = |time: Option| match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - None => Ok(0), - }; - self.fd.filestat_set_times( - to_timestamp(times.accessed)?, - to_timestamp(times.modified)?, - times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) - | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), - ) + let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT }; + let times = [ + times.accessed.map(|t| t.to_timespec()).transpose()?.unwrap_or(omit), + times.modified.map(|t| t.to_timespec()).transpose()?.unwrap_or(omit), + ]; + c::futimens(self.as_fd(), ×) + } + + #[cfg(target_env = "p1")] + pub fn metadata_at(&self, flags: i32, path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| c::fstatat(self.as_fd(), path, flags)) } - pub fn read_link(&self, file: &Path) -> io::Result { - read_link(&self.fd, file) + #[cfg(target_env = "p1")] + pub fn readlink_at(&self, path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| read_link(self.as_fd(), path)) + } + + #[cfg(target_env = "p1")] + pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { + run_path_with_cstr(path, &|path| File::open_c(self.as_fd(), path, opts)) } } @@ -607,8 +678,8 @@ impl DirBuilder { } pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.create_directory(osstr2str(file.as_ref())?) + let (dir, path) = open_parent(p)?; + run_path_with_cstr(&path, &|path| c::mkdirat(dir.as_fd(), path)) } } @@ -620,21 +691,26 @@ impl fmt::Debug for File { pub fn readdir(p: &Path) -> io::Result { let mut opts = OpenOptions::new(); - opts.directory(true); + opts.custom_flags(libc::O_DIRECTORY); opts.read(true); let dir = File::open(p, &opts)?; - Ok(ReadDir::new(dir, p.to_path_buf())) + ReadDir::new(dir, p.to_path_buf()) } pub fn unlink(p: &Path) -> io::Result<()> { let (dir, file) = open_parent(p)?; - dir.unlink_file(osstr2str(file.as_ref())?) + run_path_with_cstr(&file, &|file| c::unlinkat(dir.as_fd(), file, 0)) } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { let (old, old_file) = open_parent(old)?; let (new, new_file) = open_parent(new)?; - old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?) + run_path_with_cstr(&old_file, &|old_file| { + run_path_with_cstr(&new_file, &|new_file| { + c::renameat(old.as_fd(), old_file, new.as_fd(), new_file) + }) + })?; + Ok(()) } pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { @@ -644,23 +720,23 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { } pub fn rmdir(p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.remove_directory(osstr2str(file.as_ref())?) + let (dir, path) = open_parent(p)?; + run_path_with_cstr(&path, &|path| c::unlinkat(dir.as_fd(), path, libc::AT_REMOVEDIR)) } pub fn readlink(p: &Path) -> io::Result { let (dir, file) = open_parent(p)?; - read_link(&dir, &file) + run_path_with_cstr(&file, &|file| read_link(dir.as_fd(), file)) } -fn read_link(fd: &WasiFd, file: &Path) -> io::Result { +fn read_link(fd: BorrowedFd<'_>, path: &CStr) -> io::Result { // Try to get a best effort initial capacity for the vector we're going to // fill. Note that if it's not a symlink we don't use a file to avoid // allocating gigabytes if you read_link a huge movie file by accident. // Additionally we add 1 to the initial size so if it doesn't change until // when we call `readlink` the returned length will be less than the // capacity, guaranteeing that we got all the data. - let meta = metadata_at(fd, 0, file)?; + let meta = c::fstatat(fd, path, libc::AT_SYMLINK_NOFOLLOW)?; let initial_size = if meta.file_type().is_symlink() { (meta.size() as usize).saturating_add(1) } else { @@ -670,10 +746,9 @@ fn read_link(fd: &WasiFd, file: &Path) -> io::Result { // Now that we have an initial guess of how big to make our buffer, call // `readlink` in a loop until it fails or reports it filled fewer bytes than // we asked for, indicating we got everything. - let file = osstr2str(file.as_ref())?; let mut destination = vec![0u8; initial_size]; loop { - let len = fd.readlink(file, &mut destination)?; + let len = c::readlinkat(fd, path, &mut destination)?; if len < destination.len() { destination.truncate(len); destination.shrink_to_fit(); @@ -686,29 +761,40 @@ fn read_link(fd: &WasiFd, file: &Path) -> io::Result { pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { let (link, link_file) = open_parent(link)?; - link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?) + run_path_with_cstr(&original, &|original| { + run_path_with_cstr(&link_file, &|link_file| c::symlinkat(original, link.as_fd(), link_file)) + })?; + Ok(()) } pub fn link(original: &Path, link: &Path) -> io::Result<()> { let (original, original_file) = open_parent(original)?; let (link, link_file) = open_parent(link)?; - // Pass 0 as the flags argument, meaning don't follow symlinks. - original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_file.as_ref())?) + + run_path_with_cstr(&original_file, &|original_file| { + run_path_with_cstr(&link_file, &|link_file| { + c::linkat( + original.as_fd(), + original_file, + link.as_fd(), + link_file, + // Pass 0 as the flags argument, meaning don't follow + // symlinks. + 0, + ) + }) + })?; + Ok(()) } pub fn stat(p: &Path) -> io::Result { let (dir, file) = open_parent(p)?; - metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file) + run_path_with_cstr(&file, &|file| c::fstatat(dir.as_fd(), file, libc::AT_SYMLINK_FOLLOW)) } pub fn lstat(p: &Path) -> io::Result { let (dir, file) = open_parent(p)?; - metadata_at(&dir, 0, &file) -} - -fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result { - let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?; - Ok(FileAttr { meta }) + run_path_with_cstr(&file, &|file| c::fstatat(dir.as_fd(), file, libc::AT_SYMLINK_NOFOLLOW)) } pub fn canonicalize(_p: &Path) -> io::Result { @@ -717,18 +803,6 @@ pub fn canonicalize(_p: &Path) -> io::Result { unsupported() } -fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result { - let fd = fd.open( - opts.dirflags, - osstr2str(path.as_ref())?, - opts.oflags, - opts.rights_base(), - opts.rights_inheriting(), - opts.fdflags, - )?; - Ok(File { fd }) -} - /// Attempts to open a bare path `p`. /// /// WASI has no fundamental capability to do this. All syscalls and operations @@ -761,9 +835,9 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { let mut buf = Vec::::with_capacity(512); loop { unsafe { - let mut relative_path = buf.as_ptr().cast(); + let mut relative_path = buf.as_mut_ptr().cast(); let mut abs_prefix = ptr::null(); - let fd = __wasilibc_find_relpath( + let fd = libc::__wasilibc_find_relpath( p.as_ptr(), &mut abs_prefix, &mut relative_path, @@ -792,22 +866,9 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { )); } } - - unsafe extern "C" { - pub fn __wasilibc_find_relpath( - path: *const libc::c_char, - abs_prefix: *mut *const libc::c_char, - relative_path: *mut *const libc::c_char, - relative_path_len: libc::size_t, - ) -> libc::c_int; - } }) } -pub fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) -} - pub fn copy(from: &Path, to: &Path) -> io::Result { use crate::fs::File; @@ -819,10 +880,11 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { pub fn remove_dir_all(path: &Path) -> io::Result<()> { let (parent, path) = open_parent(path)?; - remove_dir_all_recursive(&parent, &path) + + run_path_with_cstr(&path, &|path| remove_dir_all_recursive(parent.as_fd(), path)) } -fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { +fn remove_dir_all_recursive(parent: BorrowedFd<'_>, path: &CStr) -> io::Result<()> { // Open up a file descriptor for the directory itself. Note that we don't // follow symlinks here and we specifically open directories. // @@ -833,13 +895,9 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { // // If the opened file was actually a symlink then the symlink is deleted, // not the directory recursively. - let mut opts = OpenOptions::new(); - opts.lookup_flags(0); - opts.directory(true); - opts.read(true); - let fd = open_at(parent, path, &opts)?; + let fd = c::openat(parent, path, libc::O_RDONLY | libc::O_DIRECTORY | libc::O_NOFOLLOW, None)?; if fd.file_attr()?.file_type().is_symlink() { - return parent.unlink_file(osstr2str(path.as_ref())?); + return c::unlinkat(parent.as_fd(), path, 0); } // this "root" is only used by `DirEntry::path` which we don't use below so @@ -855,17 +913,13 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { // invocations of reading a directory. By reading all the entries at once // this ensures that, at least without concurrent modifications, it should // be possible to delete everything. - for entry in ReadDir::new(fd, dummy_root).collect::>() { + for entry in ReadDir::new(fd, dummy_root)?.collect::>() { let entry = entry?; - let path = crate::str::from_utf8(&entry.name).map_err(|_| { - io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") - })?; - let result: io::Result<()> = try { if entry.file_type()?.is_dir() { - remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?; + remove_dir_all_recursive(entry.dir_fd(), &entry.name)?; } else { - entry.inner.dir.fd.unlink_file(path)?; + c::unlinkat(entry.dir_fd(), &entry.name, 0)?; } }; // ignore internal NotFound errors @@ -878,5 +932,176 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { // Once all this directory's contents are deleted it should be safe to // delete the directory tiself. - ignore_notfound(parent.remove_directory(osstr2str(path.as_ref())?)) + ignore_notfound(c::unlinkat(parent, path, libc::AT_REMOVEDIR)) +} + +mod c { + use super::{File, FileAttr}; + use crate::ffi::CStr; + use crate::io; + use crate::mem::MaybeUninit; + use crate::os::fd::{FromRawFd, IntoRawFd}; + use crate::os::wasi::io::{AsRawFd, BorrowedFd}; + use crate::sys::os::{cvt, errno}; + + pub fn ftruncate(fd: BorrowedFd<'_>, size: libc::off_t) -> io::Result<()> { + unsafe { + cvt(libc::ftruncate(fd.as_raw_fd(), size))?; + } + Ok(()) + } + + pub fn linkat( + oldfd: BorrowedFd<'_>, + oldpath: &CStr, + newfd: BorrowedFd<'_>, + newpath: &CStr, + flags: libc::c_int, + ) -> io::Result<()> { + cvt(unsafe { + libc::linkat( + oldfd.as_raw_fd(), + oldpath.as_ptr(), + newfd.as_raw_fd(), + newpath.as_ptr(), + flags, + ) + })?; + Ok(()) + } + + pub fn symlinkat(original: &CStr, newfd: BorrowedFd<'_>, newpath: &CStr) -> io::Result<()> { + cvt(unsafe { libc::symlinkat(original.as_ptr(), newfd.as_raw_fd(), newpath.as_ptr()) })?; + Ok(()) + } + + pub fn renameat( + oldfd: BorrowedFd<'_>, + oldpath: &CStr, + newfd: BorrowedFd<'_>, + newpath: &CStr, + ) -> io::Result<()> { + cvt(unsafe { + libc::renameat(oldfd.as_raw_fd(), oldpath.as_ptr(), newfd.as_raw_fd(), newpath.as_ptr()) + })?; + Ok(()) + } + + pub fn mkdirat(fd: BorrowedFd<'_>, path: &CStr) -> io::Result<()> { + cvt(unsafe { libc::mkdirat(fd.as_raw_fd(), path.as_ptr(), 0o777) })?; + Ok(()) + } + + pub fn futimens(fd: BorrowedFd<'_>, times: &[libc::timespec; 2]) -> io::Result<()> { + unsafe { + cvt(libc::futimens(fd.as_raw_fd(), times.as_ptr()))?; + } + Ok(()) + } + + pub fn lseek(fd: BorrowedFd<'_>, pos: i64, whence: libc::c_int) -> io::Result { + let n = cvt(unsafe { libc::lseek(fd.as_raw_fd(), pos, whence) })?; + Ok(n as u64) + } + + pub fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + cvt(libc::fsync(fd.as_raw_fd()))?; + } + Ok(()) + } + + pub fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> { + unsafe { + cvt(libc::fdatasync(fd.as_raw_fd()))?; + } + Ok(()) + } + + pub fn fstat(fd: BorrowedFd<'_>) -> io::Result { + let mut stat = MaybeUninit::uninit(); + unsafe { + cvt(libc::fstat(fd.as_raw_fd(), stat.as_mut_ptr()))?; + Ok(FileAttr { stat: stat.assume_init() }) + } + } + + pub fn fstatat(fd: BorrowedFd<'_>, path: &CStr, flags: libc::c_int) -> io::Result { + let mut stat = MaybeUninit::uninit(); + unsafe { + cvt(libc::fstatat(fd.as_raw_fd(), path.as_ptr(), stat.as_mut_ptr(), flags))?; + Ok(FileAttr { stat: stat.assume_init() }) + } + } + + pub fn readlinkat(fd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result { + let len = cvt(unsafe { + libc::readlinkat(fd.as_raw_fd(), path.as_ptr(), buf.as_mut_ptr().cast(), buf.len()) + })?; + Ok(len as usize) + } + + pub fn unlinkat(fd: BorrowedFd<'_>, path: &CStr, flags: libc::c_int) -> io::Result<()> { + cvt(unsafe { libc::unlinkat(fd.as_raw_fd(), path.as_ptr(), flags) })?; + Ok(()) + } + + pub fn openat( + fd: BorrowedFd<'_>, + path: &CStr, + flags: libc::c_int, + mode: Option, + ) -> io::Result { + unsafe { + let fd = match mode { + Some(mode) => cvt(libc::openat(fd.as_raw_fd(), path.as_ptr(), flags, mode))?, + None => cvt(libc::openat(fd.as_raw_fd(), path.as_ptr(), flags))?, + }; + Ok(File::from_raw_fd(fd)) + } + } + + pub struct Dirp { + ptr: *mut libc::DIR, + } + + impl Dirp { + pub fn new(dir: File) -> io::Result { + unsafe { + let ptr = libc::fdopendir(dir.as_raw_fd()); + if ptr.is_null() { + return Err(io::Error::last_os_error()); + } + let _ = dir.into_raw_fd(); // `ptr` now owns the fd + Ok(Dirp { ptr }) + } + } + + pub fn readdir(&self) -> io::Result> { + unsafe { + let entry = libc::readdir(self.ptr); + if entry.is_null() { + let e = errno(); + if e == 0 { Ok(None) } else { Err(io::Error::from_raw_os_error(e)) } + } else { + Ok(Some(entry)) + } + } + } + + pub fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw(libc::dirfd(self.ptr)) } + } + } + + unsafe impl Send for Dirp {} + unsafe impl Sync for Dirp {} + + impl Drop for Dirp { + fn drop(&mut self) { + unsafe { + libc::closedir(self.ptr); + } + } + } } diff --git a/library/std/src/sys/io/io_slice/wasi.rs b/library/std/src/sys/io/io_slice/wasi.rs index 87acbbd924e56..0887682133b7a 100644 --- a/library/std/src/sys/io/io_slice/wasi.rs +++ b/library/std/src/sys/io/io_slice/wasi.rs @@ -1,40 +1,68 @@ use crate::marker::PhantomData; -use crate::slice; +use crate::{mem, slice}; #[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { - vec: wasi::Ciovec, + vec: libc::iovec, _p: PhantomData<&'a [u8]>, } impl<'a> IoSlice<'a> { #[inline] pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData } + IoSlice { + vec: libc::iovec { iov_base: buf.as_ptr().cast_mut().cast(), iov_len: buf.len() }, + _p: PhantomData, + } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { + if self.vec.iov_len < n { panic!("advancing IoSlice beyond its length"); } unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); } } #[inline] pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } + unsafe { slice::from_raw_parts(self.vec.iov_base as *const u8, self.vec.iov_len) } + } + + #[cfg(target_env = "p1")] + pub(crate) fn as_wasip1_slice<'b>(a: &'b [crate::io::IoSlice<'_>]) -> &'b [wasi::Ciovec] { + let a = Self::as_libc_slice(a); + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(mem::offset_of!(wasi::Ciovec, buf), mem::offset_of!(libc::iovec, iov_base)); + assert_eq!(mem::offset_of!(wasi::Ciovec, buf_len), mem::offset_of!(libc::iovec, iov_len)); + + // SAFETY: `wasi::Ciovec` and `libc::iovec` have different definitions + // but have the same layout by definition, so it should be safe to + // transmute between the two. + unsafe { mem::transmute(a) } + } + + pub(crate) fn as_libc_slice<'b>(a: &'b [crate::io::IoSlice<'_>]) -> &'b [libc::iovec] { + assert_eq!(size_of::>(), size_of::()); + assert_eq!(align_of::>(), align_of::()); + + // SAFETY: the `crate::io::IoSlice` type is a `repr(transparent)` + // wrapper around `Self`, and `Self` is a `repr(transparent)` wrapper + // aruond `libc::iovec`, thus an slice of one is a slice of the other. + unsafe { mem::transmute(a) } } } #[repr(transparent)] pub struct IoSliceMut<'a> { - vec: wasi::Iovec, + vec: libc::iovec, _p: PhantomData<&'a mut [u8]>, } @@ -42,35 +70,60 @@ impl<'a> IoSliceMut<'a> { #[inline] pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { IoSliceMut { - vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() }, + vec: libc::iovec { iov_base: buf.as_mut_ptr().cast(), iov_len: buf.len() }, _p: PhantomData, } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { + if self.vec.iov_len < n { panic!("advancing IoSlice beyond its length"); } unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); } } #[inline] pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } + unsafe { slice::from_raw_parts(self.vec.iov_base as *const u8, self.vec.iov_len) } } #[inline] pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[cfg(target_env = "p1")] + pub(crate) fn as_wasip1_slice<'b>(a: &'b mut [crate::io::IoSliceMut<'_>]) -> &'b [wasi::Iovec] { + let a = Self::as_libc_slice(a); + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(mem::offset_of!(wasi::Iovec, buf), mem::offset_of!(libc::iovec, iov_base)); + assert_eq!(mem::offset_of!(wasi::Iovec, buf_len), mem::offset_of!(libc::iovec, iov_len)); + + // SAFETY: `wasi::Iovec` and `libc::iovec` have different definitions + // but have the same layout by definition, so it should be safe to + // transmute between the two. + unsafe { mem::transmute(a) } + } + + pub(crate) fn as_libc_slice<'b>(a: &'b mut [crate::io::IoSliceMut<'_>]) -> &'b [libc::iovec] { + assert_eq!(size_of::>(), size_of::()); + assert_eq!(align_of::>(), align_of::()); + + // SAFETY: the `crate::io::IoSliceMut` type is a `repr(transparent)` + // wrapper around `Self`, and `Self` is a `repr(transparent)` wrapper + // aruond `libc::iovec`, thus an slice of one is a slice of the other. + unsafe { mem::transmute(a) } } } diff --git a/library/std/src/sys/pal/wasip1/time.rs b/library/std/src/sys/pal/wasip1/time.rs index 0d8d0b59ac14a..79b953fb2bddb 100644 --- a/library/std/src/sys/pal/wasip1/time.rs +++ b/library/std/src/sys/pal/wasip1/time.rs @@ -1,5 +1,6 @@ #![forbid(unsafe_op_in_unsafe_fn)] +use crate::io; use crate::time::Duration; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] @@ -43,12 +44,23 @@ impl SystemTime { SystemTime(current_time(wasi::CLOCKID_REALTIME)) } - pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { - SystemTime(Duration::from_nanos(ts)) + pub fn from_timespec(ts: libc::timespec) -> SystemTime { + SystemTime(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)) } - pub fn to_wasi_timestamp(&self) -> Option { - self.0.as_nanos().try_into().ok() + pub fn to_timespec(&self) -> io::Result { + Ok(libc::timespec { + tv_sec: self + .0 + .as_secs() + .try_into() + .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?, + tv_nsec: self + .0 + .subsec_nanos() + .try_into() + .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?, + }) } pub fn sub_time(&self, other: &SystemTime) -> Result { diff --git a/library/std/src/sys/pal/wasip2/helpers.rs b/library/std/src/sys/pal/wasip2/helpers.rs new file mode 100644 index 0000000000000..61f150a998af2 --- /dev/null +++ b/library/std/src/sys/pal/wasip2/helpers.rs @@ -0,0 +1,57 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::io as std_io; + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { + use std_io::ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => QuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + libc::EINPROGRESS => InProgress, + libc::EOPNOTSUPP => Unsupported, + libc::EACCES | libc::EPERM => PermissionDenied, + libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +pub fn abort_internal() -> ! { + unsafe { libc::abort() } +} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index c1d89da2677c9..b321806564eaa 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -23,13 +23,12 @@ mod common; pub use common::*; -#[path = "../wasip1/helpers.rs"] mod helpers; // The following exports are listed individually to work around Rust's glob // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, is_interrupted}; mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs index 434891839944d..cbe1be82d1204 100644 --- a/library/std/src/sys/pal/wasip2/time.rs +++ b/library/std/src/sys/pal/wasip2/time.rs @@ -1,3 +1,4 @@ +use crate::io; use crate::time::Duration; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] @@ -36,12 +37,23 @@ impl SystemTime { SystemTime(Duration::new(now.seconds, now.nanoseconds)) } - pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { - SystemTime(Duration::from_nanos(ts)) + pub fn from_timespec(ts: libc::timespec) -> SystemTime { + SystemTime(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)) } - pub fn to_wasi_timestamp(&self) -> Option { - self.0.as_nanos().try_into().ok() + pub fn to_timespec(&self) -> io::Result { + Ok(libc::timespec { + tv_sec: self + .0 + .as_secs() + .try_into() + .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?, + tv_nsec: self + .0 + .subsec_nanos() + .try_into() + .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?, + }) } pub fn sub_time(&self, other: &SystemTime) -> Result { diff --git a/tests/rustdoc-js-std/unbox-type-result.js b/tests/rustdoc-js-std/unbox-type-result.js index 1f5cba58adf6d..e4765d87637c3 100644 --- a/tests/rustdoc-js-std/unbox-type-result.js +++ b/tests/rustdoc-js-std/unbox-type-result.js @@ -8,7 +8,6 @@ const EXPECTED = [ query: "File -> Metadata", others: [ { path: "std::fs::File", name: "metadata" }, - { path: "std::fs::File", name: "metadata_at" }, ] }, {