From ffb3ff1442374dba3895bc263641d058153ab150 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 10 Oct 2025 12:59:41 -0700 Subject: [PATCH] std: Use more `unix.rs` code on WASI targets This commit is a large change to the implementation of filesystem and other system-related operations on WASI targets. Previously the standard library explicitly used the `wasi` crate at the 0.11.x version track which means that it used WASIp1 APIs directly. This meant that `std` was hard-coded to use WASIp1 syscalls and there was no separate implementation for the WASIp{2,3} targets, for example. The high-level goal of this commit is to decouple this interaction and avoid the use of the `wasi` crate on the WASIp2 target. Historically when WASIp1 was originally added to Rust the wasi-libc library was in a much different position than it is today. Nowadays Rust already depends on wasi-libc on WASI targets for things like memory allocation and environment variable management. As a libc library it also has all the functions necessary to implement all filesystem operations Rust wants. Recently wasi-libc additionally was updated to use WASIp2 APIs directly on the `wasm32-wasip2` target instead of using `wasm32-wasip1` APIs. This commit is leveraging this work by enabling Rust to completely sever the dependence on WASIp1 APIs when compiling for `wasm32-wasip2`. This is also intended to make it easier to migrate to `wasm32-wasip3` internally in the future where now only libc need be updated and Rust doesn't need to explicitly change as well. The overall premise of this commit is that there's no need for WASI-specific implementation modules throughout the standard library. Instead the libc-style bindings already implemented for Unix-like targets are sufficient. This means that Rust will now be using libc-style interfaces to interact with the filesystem, for example, and wasi-libc is the one responsible for translating these POSIX-ish functions into WASIp{1,2} calls. Concrete changes here are: * `std` for `wasm32-wasip2` no longer depends on `wasi 0.11.x` * The implementation of `std::os::wasi::fs`, which was previously unstable and still is, now has portions gated to only work on the WASIp1 target which use the `wasi` crate directly. Traits have been trimmed down in some cases, updated in others, or now present a different API on WASIp1 and WASIp2. It's expected this'll get further cleanup in the future. * The `std::sys::fd::wasi` module is deleted and `unix` is used instead. * The `std::sys::fs::wasi` module is deleted and `unix` is used instead. * The `std::sys::io::io_slice::wasi` module is deleted and `unix` is used instead. * The `std::sys::pal::{wasip1,wasip2}` modules are now merged together as their difference is much smaller than before. * The `std::sys::pal::wasi::time` is deleted and the `unix` variant is used directly instead. * The `std::sys::stdio::wasip{1,2}` modules are deleted and the `unix` variant is used instead. * The `std::sys::thread::wasip{1,2}` modules are deleted and the `unix` variant is used instead. Overall Rust's libstd is effectively more tightly bound to libc when compiled to WASI targets. This is intended to mirror how it's expected all other languages will also bind to WASI. This additionally has the nice goal of drastically reducing the WASI-specific maintenance burden in libstd (in theory) and the only real changes required here are extra definitions being added to `libc` (done in separate PRs). This might be required for more symbols in the future but for now everything should be mostly complete. --- library/Cargo.lock | 2 - library/Cargo.toml | 2 + library/std/Cargo.toml | 2 +- library/std/src/os/wasi/fs.rs | 310 +++--- library/std/src/os/wasi/net/mod.rs | 5 +- library/std/src/sys/fd/mod.rs | 6 +- library/std/src/sys/fd/unix.rs | 10 +- library/std/src/sys/fd/wasi.rs | 331 ------- library/std/src/sys/fs/mod.rs | 12 +- library/std/src/sys/fs/unix.rs | 55 +- library/std/src/sys/fs/wasi.rs | 913 ------------------ library/std/src/sys/io/io_slice/iovec.rs | 2 +- library/std/src/sys/io/io_slice/wasi.rs | 76 -- library/std/src/sys/io/mod.rs | 6 +- .../std/src/sys/net/connection/socket/mod.rs | 6 +- .../std/src/sys/net/connection/socket/unix.rs | 6 +- .../src/sys/net/connection/socket/wasip2.rs | 408 -------- library/std/src/sys/net/connection/wasip1.rs | 20 +- library/std/src/sys/pal/mod.rs | 10 +- library/std/src/sys/pal/unix/time.rs | 7 + .../sys/pal/{wasip2 => wasi}/cabi_realloc.rs | 0 library/std/src/sys/pal/wasi/helpers.rs | 63 ++ library/std/src/sys/pal/wasi/mod.rs | 39 + .../std/src/sys/pal/{wasip1 => wasi}/os.rs | 30 +- .../std/src/sys/pal/wasi/stack_overflow.rs | 7 + library/std/src/sys/pal/wasip1/helpers.rs | 114 --- library/std/src/sys/pal/wasip1/mod.rs | 38 - library/std/src/sys/pal/wasip1/time.rs | 65 -- library/std/src/sys/pal/wasip2/mod.rs | 35 - library/std/src/sys/pal/wasip2/time.rs | 58 -- library/std/src/sys/stdio/mod.rs | 10 +- library/std/src/sys/stdio/unix.rs | 7 +- library/std/src/sys/stdio/wasip1.rs | 117 --- library/std/src/sys/stdio/wasip2.rs | 120 --- library/std/src/sys/thread/mod.rs | 28 +- library/std/src/sys/thread/unix.rs | 3 + library/std/src/sys/thread/wasip1.rs | 185 ---- library/std/src/sys/thread/wasip2.rs | 32 - tests/rustdoc-js-std/unbox-type-result.js | 1 - 39 files changed, 341 insertions(+), 2800 deletions(-) delete mode 100644 library/std/src/sys/fd/wasi.rs delete mode 100644 library/std/src/sys/fs/wasi.rs delete mode 100644 library/std/src/sys/io/io_slice/wasi.rs delete mode 100644 library/std/src/sys/net/connection/socket/wasip2.rs rename library/std/src/sys/pal/{wasip2 => wasi}/cabi_realloc.rs (100%) create mode 100644 library/std/src/sys/pal/wasi/helpers.rs create mode 100644 library/std/src/sys/pal/wasi/mod.rs rename library/std/src/sys/pal/{wasip1 => wasi}/os.rs (88%) create mode 100644 library/std/src/sys/pal/wasi/stack_overflow.rs delete mode 100644 library/std/src/sys/pal/wasip1/helpers.rs delete mode 100644 library/std/src/sys/pal/wasip1/mod.rs delete mode 100644 library/std/src/sys/pal/wasip1/time.rs delete mode 100644 library/std/src/sys/pal/wasip2/mod.rs delete mode 100644 library/std/src/sys/pal/wasip2/time.rs delete mode 100644 library/std/src/sys/stdio/wasip1.rs delete mode 100644 library/std/src/sys/stdio/wasip2.rs delete mode 100644 library/std/src/sys/thread/wasip1.rs delete mode 100644 library/std/src/sys/thread/wasip2.rs diff --git a/library/Cargo.lock b/library/Cargo.lock index f06f57799c2fc..bb06d259cf42d 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -140,8 +140,6 @@ dependencies = [ [[package]] name = "libc" version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" dependencies = [ "rustc-std-workspace-core", ] diff --git a/library/Cargo.toml b/library/Cargo.toml index e30e624094285..ef616968971af 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -59,3 +59,5 @@ rustflags = ["-Cpanic=abort"] rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } rustc-std-workspace-std = { path = 'rustc-std-workspace-std' } + +libc = { path = '../../libc' } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 685c2cf162abd..d7eb92bd06210 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -78,7 +78,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..d48db85605c75 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 @@ -8,11 +8,18 @@ #[allow(unused_imports)] use io::{Read, Write}; +#[cfg(target_env = "p1")] use crate::ffi::OsStr; -use crate::fs::{self, File, Metadata, OpenOptions}; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::path::{Path, PathBuf}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; +#[cfg(target_env = "p1")] +use crate::fs::File; +use crate::fs::{self, OpenOptions}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +#[cfg(target_env = "p1")] +use crate::os::fd::AsRawFd; +use crate::path::Path; +#[cfg(target_env = "p1")] +use crate::sys::err2io; +use crate::sys_common::{AsInner, AsInnerMut}; /// WASI-specific extensions to [`File`]. pub trait FileExt { @@ -27,10 +34,7 @@ pub trait FileExt { /// /// Note that similar to [`File::read`], it is not an error to return with a /// short read. - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - let bufs = &mut [IoSliceMut::new(buf)]; - self.read_vectored_at(bufs, offset) - } + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; /// Reads a number of bytes starting from a given offset. /// @@ -45,6 +49,13 @@ pub trait FileExt { /// return with a short read. fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result; + /// Reads some bytes starting from a given offset into the buffer. + /// + /// This equivalent to the [`read_at`](FileExt::read_at) method, except that it is passed a + /// [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The new + /// data will be appended to any existing contents of `buf`. + fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()>; + /// Reads the exact number of byte required to fill `buf` from the given offset. /// /// The offset is relative to the start of the file and thus independent @@ -102,10 +113,7 @@ pub trait FileExt { /// /// Note that similar to [`File::write`], it is not an error to return a /// short write. - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - let bufs = &[IoSlice::new(buf)]; - self.write_vectored_at(bufs, offset) - } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; /// Writes a number of bytes starting from a given offset. /// @@ -164,54 +172,49 @@ pub trait FileExt { /// /// This corresponds to the `fd_fdstat_set_flags` syscall. #[doc(alias = "fd_fdstat_set_flags")] + #[cfg(target_env = "p1")] fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; /// Adjusts the rights associated with this file. /// /// This corresponds to the `fd_fdstat_set_rights` syscall. #[doc(alias = "fd_fdstat_set_rights")] + #[cfg(target_env = "p1")] fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; /// Provides file advisory information on a file descriptor. /// /// This corresponds to the `fd_advise` syscall. #[doc(alias = "fd_advise")] + #[cfg(target_env = "p1")] fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; /// Forces the allocation of space in a file. /// /// This corresponds to the `fd_allocate` syscall. #[doc(alias = "fd_allocate")] + #[cfg(target_env = "p1")] fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; /// Creates a directory. /// /// This corresponds to the `path_create_directory` syscall. #[doc(alias = "path_create_directory")] + #[cfg(target_env = "p1")] fn create_directory>(&self, dir: P) -> io::Result<()>; - /// Reads the contents of a symbolic link. - /// - /// This corresponds to the `path_readlink` syscall. - #[doc(alias = "path_readlink")] - fn read_link>(&self, path: P) -> io::Result; - - /// Returns the attributes of a file or directory. - /// - /// This corresponds to the `path_filestat_get` syscall. - #[doc(alias = "path_filestat_get")] - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; - /// Unlinks a file. /// /// This corresponds to the `path_unlink_file` syscall. #[doc(alias = "path_unlink_file")] + #[cfg(target_env = "p1")] fn remove_file>(&self, path: P) -> io::Result<()>; /// Removes a directory. /// /// This corresponds to the `path_remove_directory` syscall. #[doc(alias = "path_remove_directory")] + #[cfg(target_env = "p1")] fn remove_directory>(&self, path: P) -> io::Result<()>; } @@ -223,22 +226,40 @@ pub trait FileExt { // FIXME: bind random_get maybe? - on crates.io for unix impl FileExt for fs::File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.as_inner().read_at(buf, offset) + } + + fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + self.as_inner().read_buf_at(buf, offset) + } + fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - self.as_inner().as_inner().pread(bufs, offset) + self.as_inner().read_vectored_at(bufs, offset) + } + + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.as_inner().write_at(buf, offset) } fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - self.as_inner().as_inner().pwrite(bufs, offset) + self.as_inner().write_vectored_at(bufs, offset) } + #[cfg(target_env = "p1")] 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) } } + #[cfg(target_env = "p1")] 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) + } } + #[cfg(target_env = "p1")] fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { let advice = match advice { a if a == wasi::ADVICE_NORMAL.raw() => wasi::ADVICE_NORMAL, @@ -255,149 +276,46 @@ 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) + } } + #[cfg(target_env = "p1")] 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) } } + #[cfg(target_env = "p1")] fn create_directory>(&self, dir: P) -> io::Result<()> { - self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?) - } - - fn read_link>(&self, path: P) -> io::Result { - self.as_inner().read_link(path.as_ref()) - } - - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result { - let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?; - Ok(FromInner::from_inner(m)) + let path = osstr2str(dir.as_ref().as_ref())?; + unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } + #[cfg(target_env = "p1")] 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) } } + #[cfg(target_env = "p1")] 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) } } } -/// WASI-specific extensions to [`fs::OpenOptions`]. +/// WASI-specific extensions to [`OpenOptions`]. pub trait OpenOptionsExt { - /// Pass custom `dirflags` argument to `path_open`. - /// - /// This option configures the `dirflags` argument to the - /// `path_open` syscall which `OpenOptions` will eventually call. The - /// `dirflags` argument configures how the file is looked up, currently - /// primarily affecting whether symlinks are followed or not. - /// - /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are - /// followed. You can call this method with 0 to disable following symlinks - fn lookup_flags(&mut self, flags: u32) -> &mut Self; - - /// Indicates whether `OpenOptions` must open a directory or not. - /// - /// This method will configure whether the `__WASI_O_DIRECTORY` flag is - /// passed when opening a file. When passed it will require that the opened - /// path is a directory. - /// - /// This option is by default `false` - fn directory(&mut self, dir: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn dsync(&mut self, dsync: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn nonblock(&mut self, nonblock: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn rsync(&mut self, rsync: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn sync(&mut self, sync: bool) -> &mut Self; - - /// Indicates the value that should be passed in for the `fs_rights_base` - /// parameter of `path_open`. - /// - /// This option defaults based on the `read` and `write` configuration of - /// this `OpenOptions` builder. If this method is called, however, the - /// exact mask passed in will be used instead. - fn fs_rights_base(&mut self, rights: u64) -> &mut Self; - - /// Indicates the value that should be passed in for the - /// `fs_rights_inheriting` parameter of `path_open`. - /// - /// The default for this option is the same value as what will be passed - /// for the `fs_rights_base` parameter but if this method is called then - /// the specified value will be used instead. - fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self; - - /// Open a file or directory. - /// - /// This corresponds to the `path_open` syscall. - #[doc(alias = "path_open")] - fn open_at>(&self, file: &File, path: P) -> io::Result; + /// Pass custom flags to the `flags` argument of `open`. + fn custom_flags(&mut self, flags: i32) -> &mut Self; } impl OpenOptionsExt for OpenOptions { - fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions { - self.as_inner_mut().lookup_flags(flags); - self - } - - fn directory(&mut self, dir: bool) -> &mut OpenOptions { - self.as_inner_mut().directory(dir); - self - } - - fn dsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().dsync(enabled); - self - } - - fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().nonblock(enabled); - self - } - - fn rsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().rsync(enabled); - self - } - - fn sync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().sync(enabled); + fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); self } - - fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_base(rights); - self - } - - fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_inheriting(rights); - self - } - - fn open_at>(&self, file: &File, path: P) -> io::Result { - let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?; - Ok(File::from_inner(inner)) - } } /// WASI-specific extensions to [`fs::Metadata`]. @@ -408,37 +326,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_inner().st_dev } fn ino(&self) -> u64 { - self.as_inner().as_wasi().ino + self.as_inner().as_inner().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_inner().st_nlink } } @@ -451,28 +349,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().is(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().is(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().is(libc::S_IFSOCK) } } @@ -492,6 +381,7 @@ impl DirEntryExt for fs::DirEntry { /// /// This corresponds to the `path_link` syscall. #[doc(alias = "path_link")] +#[cfg(target_env = "p1")] pub fn link, U: AsRef>( old_fd: &File, old_flags: u32, @@ -499,43 +389,58 @@ 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. /// /// This corresponds to the `path_rename` syscall. #[doc(alias = "path_rename")] +#[cfg(target_env = "p1")] pub fn rename, U: AsRef>( old_fd: &File, old_path: P, 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. /// /// This corresponds to the `path_symlink` syscall. #[doc(alias = "path_symlink")] +#[cfg(target_env = "p1")] pub fn symlink, U: AsRef>( old_path: P, 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. @@ -546,6 +451,7 @@ pub fn symlink_path, U: AsRef>(old_path: P, new_path: U) -> crate::sys::fs::symlink(old_path.as_ref(), new_path.as_ref()) } +#[cfg(target_env = "p1")] fn osstr2str(f: &OsStr) -> io::Result<&str> { f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } 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/mod.rs b/library/std/src/sys/fd/mod.rs index 330499ecc18f6..02d61a62f4e6b 100644 --- a/library/std/src/sys/fd/mod.rs +++ b/library/std/src/sys/fd/mod.rs @@ -3,7 +3,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] cfg_select! { - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi") => { mod unix; pub use unix::*; } @@ -19,9 +19,5 @@ cfg_select! { mod sgx; pub use sgx::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; - } _ => {} } diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index 2b2dfe48e89e2..6dff6eaf8993f 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -35,7 +35,7 @@ cfg_select! { use crate::cmp; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::sys::cvt; #[cfg(all(target_os = "android", target_pointer_width = "64"))] use crate::sys::pal::weak::syscall; @@ -152,7 +152,8 @@ impl FileDesc { target_os = "espidf", target_os = "horizon", target_os = "vita", - target_os = "nuttx" + target_os = "nuttx", + target_os = "wasi", ))) } @@ -385,7 +386,8 @@ impl FileDesc { target_os = "espidf", target_os = "horizon", target_os = "vita", - target_os = "nuttx" + target_os = "nuttx", + target_os = "wasi", ))) } @@ -560,6 +562,7 @@ impl FileDesc { target_os = "redox", target_os = "vxworks", target_os = "nto", + target_os = "wasi", )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -583,6 +586,7 @@ impl FileDesc { target_os = "redox", target_os = "vxworks", target_os = "nto", + target_os = "wasi", ))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { diff --git a/library/std/src/sys/fd/wasi.rs b/library/std/src/sys/fd/wasi.rs deleted file mode 100644 index 80a5143ff0b00..0000000000000 --- a/library/std/src/sys/fd/wasi.rs +++ /dev/null @@ -1,331 +0,0 @@ -#![expect(dead_code)] - -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::net::Shutdown; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::pal::err2io; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -#[derive(Debug)] -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 { - unsafe { - wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io) - } - } - - 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)), - } - } - } - - 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 { - unsafe { - wasi::fd_readdir(self.as_raw_fd() as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie) - .map_err(err2io) - } - } - - 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 { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.fd - } -} - -impl AsInnerMut for WasiFd { - #[inline] - fn as_inner_mut(&mut self) -> &mut OwnedFd { - &mut self.fd - } -} - -impl IntoInner for WasiFd { - fn into_inner(self) -> OwnedFd { - self.fd - } -} - -impl FromInner for WasiFd { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self { fd: owned_fd } - } -} - -impl AsFd for WasiFd { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for WasiFd { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for WasiFd { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} - -impl FromRawFd for WasiFd { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } - } -} diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index bc1052b6f8c55..9143f28e9051a 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -6,12 +6,14 @@ use crate::path::{Path, PathBuf}; pub mod common; cfg_select! { - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi") => { mod unix; use unix as imp; + #[cfg(not(target_os = "wasi"))] pub use unix::{chown, fchown, lchown, mkfifo}; - #[cfg(not(target_os = "fuchsia"))] + #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] pub use unix::chroot; + #[cfg(not(target_os = "wasi"))] pub(crate) use unix::debug_assert_fd_is_open; #[cfg(any(target_os = "linux", target_os = "android"))] pub(crate) use unix::CachedFileMetadata; @@ -43,10 +45,6 @@ cfg_select! { mod vexos; use vexos as imp; } - target_os = "wasi" => { - mod wasi; - use wasi as imp; - } _ => { mod unsupported; use unsupported as imp; @@ -54,7 +52,7 @@ cfg_select! { } // FIXME: Replace this with platform-specific path conversion functions. -#[cfg(not(any(target_family = "unix", target_os = "windows")))] +#[cfg(not(any(target_family = "unix", target_os = "windows", target_os = "wasi")))] #[inline] pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> io::Result { f(path) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index cadcfddb0f7f8..e424a8733243e 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -31,6 +31,7 @@ use libc::fstatat64; target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", all(target_os = "linux", target_env = "musl"), ))] use libc::readdir as readdir64; @@ -47,6 +48,7 @@ use libc::readdir as readdir64; target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", )))] use libc::readdir_r as readdir64_r; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] @@ -80,8 +82,11 @@ use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt::{self, Write as _}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +#[cfg(target_family = "unix")] use crate::os::unix::prelude::*; +#[cfg(target_os = "wasi")] +use crate::os::wasi::prelude::*; use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; @@ -285,6 +290,7 @@ unsafe impl Sync for Dir {} target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", ))] pub struct DirEntry { dir: Arc, @@ -310,6 +316,7 @@ pub struct DirEntry { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", ))] struct dirent64_min { d_ino: u64, @@ -335,6 +342,7 @@ struct dirent64_min { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", )))] pub struct DirEntry { dir: Arc, @@ -480,7 +488,7 @@ impl FileAttr { } } -#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))] +#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix", target_os = "wasi")))] impl FileAttr { #[cfg(not(any( target_os = "vxworks", @@ -595,18 +603,18 @@ impl FileAttr { } } -#[cfg(target_os = "nto")] +#[cfg(any(target_os = "nto", target_os = "wasi"))] impl FileAttr { pub fn modified(&self) -> io::Result { - SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec) + SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec.into()) } pub fn accessed(&self) -> io::Result { - SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec) + SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec.into()) } pub fn created(&self) -> io::Result { - SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec) + SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec.into()) } } @@ -632,6 +640,7 @@ impl FilePermissions { self.mode |= 0o222; } } + #[cfg(not(target_os = "wasi"))] pub fn mode(&self) -> u32 { self.mode as u32 } @@ -715,6 +724,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", ))] fn next(&mut self) -> Option> { use crate::sys::os::{errno, set_errno}; @@ -812,6 +822,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -1002,6 +1013,7 @@ impl DirEntry { target_os = "solaris", target_os = "vita", target_os = "vxworks", + target_os = "wasi", target_vendor = "apple", ))] pub fn ino(&self) -> u64 { @@ -1057,6 +1069,7 @@ impl DirEntry { target_os = "nto", target_os = "vita", target_os = "hurd", + target_os = "wasi", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -1073,6 +1086,7 @@ impl DirEntry { target_os = "nto", target_os = "vita", target_os = "hurd", + target_os = "wasi", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1121,6 +1135,7 @@ impl OpenOptions { pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; } + #[cfg(not(target_os = "wasi"))] pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; } @@ -1769,6 +1784,7 @@ impl DirBuilder { run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) } + #[cfg(not(target_os = "wasi"))] pub fn set_mode(&mut self, mode: u32) { self.mode = mode as mode_t; } @@ -2217,7 +2233,7 @@ pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { set_times_impl(p, times, false) } -#[cfg(target_os = "espidf")] +#[cfg(any(target_os = "espidf", target_os = "wasi"))] fn open_to_and_set_permissions( to: &Path, _reader_metadata: &crate::fs::Metadata, @@ -2228,7 +2244,7 @@ fn open_to_and_set_permissions( Ok((writer, writer_metadata)) } -#[cfg(not(target_os = "espidf"))] +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] fn open_to_and_set_permissions( to: &Path, reader_metadata: &crate::fs::Metadata, @@ -2376,6 +2392,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { Ok(bytes_copied as u64) } +#[cfg(not(target_os = "wasi"))] pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) @@ -2383,12 +2400,13 @@ pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { }) } +#[cfg(not(target_os = "wasi"))] pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?; Ok(()) } -#[cfg(not(target_os = "vxworks"))] +#[cfg(not(any(target_os = "vxworks", target_os = "wasi")))] pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) @@ -2402,7 +2420,7 @@ pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) } -#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] +#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks", target_os = "wasi")))] pub fn chroot(dir: &Path) -> io::Result<()> { run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) } @@ -2413,6 +2431,7 @@ pub fn chroot(dir: &Path) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) } +#[cfg(not(target_os = "wasi"))] pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ()) @@ -2451,11 +2470,11 @@ mod remove_dir_impl { #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::{fdopendir, openat64 as openat, unlinkat}; - use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat}; + use super::{ + AsRawFd, Dir, DirEntry, FromRawFd, InnerReadDir, IntoRawFd, OwnedFd, RawFd, ReadDir, lstat, + }; use crate::ffi::CStr; use crate::io; - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; - use crate::os::unix::prelude::{OwnedFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::{cvt, cvt_r}; @@ -2543,6 +2562,16 @@ mod remove_dir_impl { // open the directory passing ownership of the fd let (dir, fd) = fdreaddir(fd)?; + + // For WASI all directory entries for this directory are read first + // before any removal is done. This works around the fact that the + // WASIp1 API for reading directories is not well-designed for handling + // mutations between 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. + #[cfg(target_os = "wasi")] + let dir = dir.collect::>(); + for child in dir { let child = child?; let child_name = child.name_cstr(); diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs deleted file mode 100644 index 92eb35317415f..0000000000000 --- a/library/std/src/sys/fs/wasi.rs +++ /dev/null @@ -1,913 +0,0 @@ -use crate::ffi::{CStr, OsStr, OsString}; -use crate::fs::TryLockError; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, 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}; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::fd::WasiFd; -pub use crate::sys::fs::common::exists; -use crate::sys::time::SystemTime; -use crate::sys::{unsupported, unsupported_err}; -use crate::sys_common::{AsInner, FromInner, IntoInner, ignore_notfound}; -use crate::{fmt, iter, ptr}; - -pub struct File { - fd: WasiFd, -} - -#[derive(Clone)] -pub struct FileAttr { - meta: wasi::Filestat, -} - -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, -} - -struct ReadDirInner { - root: PathBuf, - dir: File, -} - -pub struct DirEntry { - meta: wasi::Dirent, - name: Vec, - inner: Arc, -} - -#[derive(Clone, Debug, Default)] -pub struct OpenOptions { - read: bool, - write: bool, - append: bool, - dirflags: wasi::Lookupflags, - fdflags: wasi::Fdflags, - oflags: wasi::Oflags, - rights_base: Option, - rights_inheriting: Option, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - readonly: bool, -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes { - accessed: Option, - modified: Option, -} - -#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] -pub struct FileType { - bits: wasi::Filetype, -} - -#[derive(Debug)] -pub struct DirBuilder {} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.meta.size - } - - pub fn perm(&self) -> FilePermissions { - // not currently implemented in wasi yet - FilePermissions { readonly: false } - } - - pub fn file_type(&self) -> FileType { - FileType { bits: self.meta.filetype } - } - - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.mtim)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.atim)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) - } - - pub(crate) fn as_wasi(&self) -> &wasi::Filestat { - &self.meta - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.readonly - } - - pub fn set_readonly(&mut self, readonly: bool) { - self.readonly = readonly; - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, t: SystemTime) { - self.accessed = Some(t); - } - - pub fn set_modified(&mut self, t: SystemTime) { - self.modified = Some(t); - } -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.bits == wasi::FILETYPE_DIRECTORY - } - - pub fn is_file(&self) -> bool { - self.bits == wasi::FILETYPE_REGULAR_FILE - } - - pub fn is_symlink(&self) -> bool { - self.bits == wasi::FILETYPE_SYMBOLIC_LINK - } - - pub(crate) fn bits(&self) -> wasi::Filetype { - self.bits - } -} - -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] }, - } - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ReadDir").finish_non_exhaustive() - } -} - -impl core::iter::FusedIterator for ReadDir {} - -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(); - } - - 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; - } - 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, - }; - } - - self.next() - } - ReadDirState::Done => None, - } - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - let name = OsStr::from_bytes(&self.name); - self.inner.root.join(name) - } - - pub fn file_name(&self) -> OsString { - OsString::from_vec(self.name.clone()) - } - - pub fn metadata(&self) -> io::Result { - metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) - } - - pub fn file_type(&self) -> io::Result { - Ok(FileType { bits: self.meta.d_type }) - } - - pub fn ino(&self) -> wasi::Inode { - self.meta.d_ino - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - let mut base = OpenOptions::default(); - base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW; - base - } - - pub fn read(&mut self, read: bool) { - self.read = read; - } - - pub fn write(&mut self, write: bool) { - self.write = write; - } - - pub fn truncate(&mut self, truncate: bool) { - self.oflag(wasi::OFLAGS_TRUNC, truncate); - } - - pub fn create(&mut self, create: bool) { - self.oflag(wasi::OFLAGS_CREAT, 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; - } - } - - pub fn append(&mut self, append: bool) { - self.append = append; - self.fdflag(wasi::FDFLAGS_APPEND, append); - } - - pub fn dsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_DSYNC, set); - } - - pub fn nonblock(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_NONBLOCK, set); - } - - pub fn rsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_RSYNC, set); - } - - pub fn sync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_SYNC, set); - } - - fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) { - if set { - self.fdflags |= bit; - } else { - self.fdflags &= !bit; - } - } - - pub fn fs_rights_base(&mut self, rights: wasi::Rights) { - self.rights_base = Some(rights); - } - - pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) { - self.rights_inheriting = Some(rights); - } - - fn rights_base(&self) -> wasi::Rights { - if let Some(rights) = self.rights_base { - return rights; - } - - // If rights haven't otherwise been specified try to pick a reasonable - // set. This can always be overridden by users via extension traits, and - // implementations may give us fewer rights silently than we ask for. So - // given that, just look at `read` and `write` and bucket permissions - // based on that. - let mut base = 0; - if self.read { - base |= wasi::RIGHTS_FD_READ; - base |= wasi::RIGHTS_FD_READDIR; - } - if self.write || self.append { - base |= wasi::RIGHTS_FD_WRITE; - base |= wasi::RIGHTS_FD_DATASYNC; - base |= wasi::RIGHTS_FD_ALLOCATE; - base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE; - } - - // FIXME: some of these should probably be read-only or write-only... - base |= wasi::RIGHTS_FD_ADVISE; - base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS; - base |= wasi::RIGHTS_FD_FILESTAT_GET; - base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES; - base |= wasi::RIGHTS_FD_SEEK; - base |= wasi::RIGHTS_FD_SYNC; - base |= wasi::RIGHTS_FD_TELL; - base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY; - base |= wasi::RIGHTS_PATH_CREATE_FILE; - base |= wasi::RIGHTS_PATH_FILESTAT_GET; - base |= wasi::RIGHTS_PATH_LINK_SOURCE; - base |= wasi::RIGHTS_PATH_LINK_TARGET; - base |= wasi::RIGHTS_PATH_OPEN; - base |= wasi::RIGHTS_PATH_READLINK; - base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY; - base |= wasi::RIGHTS_PATH_RENAME_SOURCE; - base |= wasi::RIGHTS_PATH_RENAME_TARGET; - base |= wasi::RIGHTS_PATH_SYMLINK; - base |= wasi::RIGHTS_PATH_UNLINK_FILE; - base |= wasi::RIGHTS_POLL_FD_READWRITE; - - base - } - - fn rights_inheriting(&self) -> wasi::Rights { - self.rights_inheriting.unwrap_or_else(|| self.rights_base()) - } - - pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) { - self.dirflags = 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) - } - - 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) - } - - pub fn fsync(&self) -> io::Result<()> { - self.fd.sync() - } - - pub fn datasync(&self) -> io::Result<()> { - self.fd.datasync() - } - - pub fn lock(&self) -> io::Result<()> { - unsupported() - } - - pub fn lock_shared(&self) -> io::Result<()> { - unsupported() - } - - pub fn try_lock(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(unsupported_err())) - } - - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(unsupported_err())) - } - - pub fn unlock(&self) -> io::Result<()> { - unsupported() - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - self.fd.filestat_set_size(size) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(buf)]) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.fd.read(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.fd.read_buf(cursor) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.fd.write(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - self.fd.seek(pos) - } - - pub fn size(&self) -> Option> { - None - } - - pub fn tell(&self) -> io::Result { - self.fd.tell() - } - - pub fn duplicate(&self) -> io::Result { - // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup - unsupported() - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - // Permissions haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() - } - - pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - self.fd.filestat_set_times( - to_wasi_timestamp_or_now(times.accessed)?, - to_wasi_timestamp_or_now(times.modified)?, - times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) - | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), - ) - } - - pub fn read_link(&self, file: &Path) -> io::Result { - read_link(&self.fd, file) - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &WasiFd { - &self.fd - } -} - -impl IntoInner for File { - fn into_inner(self) -> WasiFd { - self.fd - } -} - -impl FromInner for File { - fn from_inner(fd: WasiFd) -> File { - File { fd } - } -} - -impl AsFd for File { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for File { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for File { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} - -impl FromRawFd for File { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder {} - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.create_directory(osstr2str(file.as_ref())?) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("File").field("fd", &self.as_raw_fd()).finish() - } -} - -pub fn readdir(p: &Path) -> io::Result { - let mut opts = OpenOptions::new(); - opts.directory(true); - opts.read(true); - let dir = File::open(p, &opts)?; - Ok(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())?) -} - -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())?) -} - -pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { - // Permissions haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() -} - -#[inline(always)] -pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - set_times_impl(&dir, &file, times, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW) -} - -#[inline(always)] -pub fn set_times_nofollow(p: &Path, times: FileTimes) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - set_times_impl(&dir, &file, times, 0) -} - -fn to_wasi_timestamp_or_now(time: Option) -> io::Result { - 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), - } -} - -fn set_times_impl( - fd: &WasiFd, - path: &Path, - times: FileTimes, - flags: wasi::Lookupflags, -) -> io::Result<()> { - fd.path_filestat_set_times( - flags, - osstr2str(path.as_ref())?, - to_wasi_timestamp_or_now(times.accessed)?, - to_wasi_timestamp_or_now(times.modified)?, - times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) - | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), - ) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.remove_directory(osstr2str(file.as_ref())?) -} - -pub fn readlink(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - read_link(&dir, &file) -} - -fn read_link(fd: &WasiFd, file: &Path) -> 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 initial_size = if meta.file_type().is_symlink() { - (meta.size() as usize).saturating_add(1) - } else { - 1 // this'll fail in just a moment - }; - - // 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)?; - if len < destination.len() { - destination.truncate(len); - destination.shrink_to_fit(); - return Ok(PathBuf::from(OsString::from_vec(destination))); - } - let amt_to_add = destination.len(); - destination.extend(iter::repeat(0).take(amt_to_add)); - } -} - -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())?) -} - -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())?) -} - -pub fn stat(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file) -} - -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 }) -} - -pub fn canonicalize(_p: &Path) -> io::Result { - // This seems to not be in wasi's API yet, and we may need to end up - // emulating it ourselves. For now just return an error. - 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 -/// are relative to already-open file descriptors. The C library, however, -/// manages a map of pre-opened file descriptors to their path, and then the C -/// library provides an API to look at this. In other words, when you want to -/// open a path `p`, you have to find a previously opened file descriptor in a -/// global table and then see if `p` is relative to that file descriptor. -/// -/// This function, if successful, will return two items: -/// -/// * The first is a `ManuallyDrop`. This represents a pre-opened file -/// descriptor which we don't have ownership of, but we can use. You shouldn't -/// actually drop the `fd`. -/// -/// * The second is a path that should be a part of `p` and represents a -/// relative traversal from the file descriptor specified to the desired -/// location `p`. -/// -/// If successful you can use the returned file descriptor to perform -/// file-descriptor-relative operations on the path returned as well. The -/// `rights` argument indicates what operations are desired on the returned file -/// descriptor, and if successful the returned file descriptor should have the -/// appropriate rights for performing `rights` actions. -/// -/// Note that this can fail if `p` doesn't look like it can be opened relative -/// to any pre-opened file descriptor. -fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { - run_path_with_cstr(p, &|p| { - let mut buf = Vec::::with_capacity(512); - loop { - unsafe { - let mut relative_path = buf.as_ptr().cast(); - let mut abs_prefix = ptr::null(); - let fd = __wasilibc_find_relpath( - p.as_ptr(), - &mut abs_prefix, - &mut relative_path, - buf.capacity(), - ); - if fd == -1 { - if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) { - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. - let cap = buf.capacity(); - buf.set_len(cap); - buf.reserve(1); - continue; - } - let msg = format!( - "failed to find a pre-opened file descriptor \ - through which {p:?} could be opened", - ); - return Err(io::Error::new(io::ErrorKind::Uncategorized, msg)); - } - let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); - - return Ok(( - ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), - PathBuf::from(OsString::from_vec(relative)), - )); - } - } - - 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; - - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - - io::copy(&mut reader, &mut writer) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let (parent, path) = open_parent(path)?; - remove_dir_all_recursive(&parent, &path) -} - -fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { - // Open up a file descriptor for the directory itself. Note that we don't - // follow symlinks here and we specifically open directories. - // - // At the root invocation of this function this will correctly handle - // symlinks passed to the top-level `remove_dir_all`. At the recursive - // level this will double-check that after the `readdir` call deduced this - // was a directory it's still a directory by the time we open it up. - // - // 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)?; - if fd.file_attr()?.file_type().is_symlink() { - return parent.unlink_file(osstr2str(path.as_ref())?); - } - - // this "root" is only used by `DirEntry::path` which we don't use below so - // it's ok for this to be a bogus value - let dummy_root = PathBuf::new(); - - // Iterate over all the entries in this directory, and travel recursively if - // necessary - // - // Note that all directory entries for this directory are read first before - // any removal is done. This works around the fact that the WASIp1 API for - // reading directories is not well-designed for handling mutations between - // 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::>() { - 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())?; - } else { - entry.inner.dir.fd.unlink_file(path)?; - } - }; - // ignore internal NotFound errors - if let Err(err) = &result - && err.kind() != io::ErrorKind::NotFound - { - return 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())?)) -} diff --git a/library/std/src/sys/io/io_slice/iovec.rs b/library/std/src/sys/io/io_slice/iovec.rs index df56358969a39..d549aca250d5f 100644 --- a/library/std/src/sys/io/io_slice/iovec.rs +++ b/library/std/src/sys/io/io_slice/iovec.rs @@ -1,6 +1,6 @@ #[cfg(target_os = "hermit")] use hermit_abi::iovec; -#[cfg(any(target_family = "unix", target_os = "trusty"))] +#[cfg(any(target_family = "unix", target_os = "trusty", target_os = "wasi"))] use libc::iovec; use crate::ffi::c_void; diff --git a/library/std/src/sys/io/io_slice/wasi.rs b/library/std/src/sys/io/io_slice/wasi.rs deleted file mode 100644 index 87acbbd924e56..0000000000000 --- a/library/std/src/sys/io/io_slice/wasi.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::marker::PhantomData; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: wasi::Ciovec, - _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 } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.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) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: wasi::Iovec, - _p: PhantomData<&'a mut [u8]>, -} - -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() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_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) } - } - - #[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) } - } -} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 0916eda1c06a5..54294ec2dad03 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -2,7 +2,7 @@ mod io_slice { cfg_select! { - any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty") => { + any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { mod iovec; pub use iovec::*; } @@ -10,10 +10,6 @@ mod io_slice { mod windows; pub use windows::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; - } target_os = "uefi" => { mod uefi; pub use uefi::*; diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 1d941dec1b792..63d5c1d23124e 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -22,14 +22,10 @@ cfg_select! { mod solid; pub use solid::*; } - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi") => { mod unix; pub use unix::*; } - all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { - mod wasip2; - pub use wasip2::*; - } target_os = "windows" => { mod windows; pub use windows::*; diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 559e27604a9d3..352ab0091e534 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -4,7 +4,7 @@ use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::net::{getsockopt, setsockopt}; use crate::sys::pal::IsMinusOne; @@ -107,7 +107,7 @@ impl Socket { } } - #[cfg(not(target_os = "vxworks"))] + #[cfg(not(any(target_os = "vxworks", target_os = "wasi")))] pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { unsafe { let mut fds = [0, 0]; @@ -275,6 +275,7 @@ impl Socket { self.0.duplicate().map(Socket) } + #[cfg(not(target_os = "wasi"))] pub fn send_with_flags(&self, buf: &[u8], flags: c_int) -> io::Result { let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; let ret = cvt(unsafe { @@ -361,6 +362,7 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } + #[cfg(not(target_os = "wasi"))] pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs deleted file mode 100644 index a1b08609eb024..0000000000000 --- a/library/std/src/sys/net/connection/socket/wasip2.rs +++ /dev/null @@ -1,408 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -pub(super) use libc as netc; -use libc::{c_int, c_void, size_t}; - -use super::{getsockopt, setsockopt, socket_addr_from_c, socket_addr_to_c}; -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::unsupported; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{cmp, mem, str}; - -#[allow(non_camel_case_types)] -pub type wrlen_t = size_t; - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_interrupted() => {} - other => return other, - } - } -} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - if err == netc::EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - let detail = unsafe { - str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned() - }; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -pub fn init() {} - -pub struct WasiSocket(OwnedFd); - -pub struct Socket(WasiSocket); - -impl Socket { - pub fn new(family: c_int, ty: c_int) -> io::Result { - let fd = cvt(unsafe { netc::socket(family, ty, 0) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = socket_addr_to_c(addr); - cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = self.connect(addr); - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS - Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { netc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - // WASI poll does not return POLLHUP or POLLERR in revents. Check if the - // connection actually succeeded and return ok only when the socket is - // ready and no errors were found. - if let Some(e) = self.take_error()? { - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept( - &self, - storage: *mut netc::sockaddr, - len: *mut netc::socklen_t, - ) -> io::Result { - let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv( - self.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut c_void, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - io::default_read_vectored(|b| self.read(b), bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - false - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - core::ptr::addr_of_mut!(storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, netc::MSG_PEEK) - } - - fn write(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - io::default_write_vectored(|b| self.write(b), bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX); - let mut timeout = netc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as netc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, _linger: Option) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result> { - unsupported() - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_int; - cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)? }; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } -} - -impl AsInner for WasiSocket { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.0 - } -} - -impl IntoInner for WasiSocket { - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl FromInner for WasiSocket { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(owned_fd) - } -} - -impl AsFd for WasiSocket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for WasiSocket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for WasiSocket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for WasiSocket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &WasiSocket { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> WasiSocket { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(sock: WasiSocket) -> Socket { - Socket(sock) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index 048dafdcd7f7c..3a0ac5552a64b 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -4,32 +4,32 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::fd::WasiFd; +use crate::sys::fd::FileDesc; use crate::sys::{err2io, unsupported}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; -pub struct Socket(WasiFd); +pub struct Socket(FileDesc); pub struct TcpStream { inner: Socket, } -impl AsInner for Socket { +impl AsInner for Socket { #[inline] - fn as_inner(&self) -> &WasiFd { + fn as_inner(&self) -> &FileDesc { &self.0 } } -impl IntoInner for Socket { - fn into_inner(self) -> WasiFd { +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { self.0 } } -impl FromInner for Socket { - fn from_inner(inner: WasiFd) -> Socket { +impl FromInner for Socket { + fn from_inner(inner: FileDesc) -> Socket { Socket(inner) } } @@ -97,7 +97,7 @@ impl TcpStream { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.socket().as_inner().read(bufs) + self.socket().as_inner().read_vectored(bufs) } pub fn is_read_vectored(&self) -> bool { @@ -109,7 +109,7 @@ impl TcpStream { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.socket().as_inner().write(bufs) + self.socket().as_inner().write_vectored(bufs) } pub fn is_write_vectored(&self) -> bool { diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index e11df38a8ee68..2bd3ec8188a7f 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -53,13 +53,9 @@ cfg_select! { mod vexos; pub use self::vexos::*; } - all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { - mod wasip2; - pub use self::wasip2::*; - } - all(target_os = "wasi", target_env = "p1") => { - mod wasip1; - pub use self::wasip1::*; + target_os = "wasi" => { + mod wasi; + pub use self::wasi::*; } target_family = "wasm" => { mod wasm; diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index c207f41cad4b5..24f13853b96b3 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,5 +1,6 @@ use core::num::niche_types::Nanoseconds; +use crate::sys_common::AsInner; use crate::time::Duration; use crate::{fmt, io}; @@ -298,6 +299,12 @@ impl Instant { } } +impl AsInner for Instant { + fn as_inner(&self) -> &Timespec { + &self.t + } +} + impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") diff --git a/library/std/src/sys/pal/wasip2/cabi_realloc.rs b/library/std/src/sys/pal/wasi/cabi_realloc.rs similarity index 100% rename from library/std/src/sys/pal/wasip2/cabi_realloc.rs rename to library/std/src/sys/pal/wasi/cabi_realloc.rs diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs new file mode 100644 index 0000000000000..6bc41d469584d --- /dev/null +++ b/library/std/src/sys/pal/wasi/helpers.rs @@ -0,0 +1,63 @@ +#![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() } +} + +#[inline] +#[cfg(target_env = "p1")] +pub(crate) fn err2io(err: wasi::Errno) -> std_io::Error { + std_io::Error::from_raw_os_error(err.raw().into()) +} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs new file mode 100644 index 0000000000000..42629a91e1afe --- /dev/null +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -0,0 +1,39 @@ +//! System bindings for the WASI platforms. +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for WASI. Currently this includes both WASIp1 and +//! WASIp2. + +#[allow(unused)] +#[path = "../wasm/atomics/futex.rs"] +pub mod futex; + +pub mod os; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +pub mod stack_overflow; +#[path = "../unix/time.rs"] +pub mod time; + +#[path = "../unsupported/common.rs"] +#[deny(unsafe_op_in_unsafe_fn)] +#[allow(unused)] +mod common; + +pub use common::*; + +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. + +#[cfg(target_env = "p1")] +pub(crate) use helpers::err2io; +pub(crate) use helpers::{abort_internal, decode_error_kind, is_interrupted}; +#[cfg(not(target_env = "p1"))] +pub use os::IsMinusOne; +pub use os::{cvt, cvt_r}; + +#[cfg(not(target_env = "p1"))] +mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasip1/os.rs b/library/std/src/sys/pal/wasi/os.rs similarity index 88% rename from library/std/src/sys/pal/wasip1/os.rs rename to library/std/src/sys/pal/wasi/os.rs index 151ba254ec4cf..367c63dfc59e7 100644 --- a/library/std/src/sys/pal/wasip1/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -19,13 +19,20 @@ pub mod libc { } } +unsafe extern "C" { + #[thread_local] + #[link_name = "errno"] + static mut libc_errno: libc::c_int; +} + pub fn errno() -> i32 { - unsafe extern "C" { - #[thread_local] - static errno: libc::c_int; - } + unsafe { libc_errno as i32 } +} - unsafe { errno as i32 } +pub fn set_errno(val: i32) { + unsafe { + libc_errno = val; + } } pub fn error_string(errno: i32) -> String { @@ -149,3 +156,16 @@ impl_is_minus_one! { i8 i16 i32 i64 isize } pub fn cvt(t: T) -> io::Result { if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } } + +pub fn cvt_r(mut f: F) -> crate::io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_interrupted() => {} + other => return other, + } + } +} diff --git a/library/std/src/sys/pal/wasi/stack_overflow.rs b/library/std/src/sys/pal/wasi/stack_overflow.rs new file mode 100644 index 0000000000000..6fe209d5f6430 --- /dev/null +++ b/library/std/src/sys/pal/wasi/stack_overflow.rs @@ -0,0 +1,7 @@ +pub struct Handler; + +impl Handler { + pub unsafe fn new(_thread_name: Option>) -> Handler { + Handler + } +} diff --git a/library/std/src/sys/pal/wasip1/helpers.rs b/library/std/src/sys/pal/wasip1/helpers.rs deleted file mode 100644 index 404747f0dc756..0000000000000 --- a/library/std/src/sys/pal/wasip1/helpers.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::io as std_io; - -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == wasi::ERRNO_INTR.raw().into() -} - -pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { - use std_io::ErrorKind; - - let Ok(errno) = u16::try_from(errno) else { - return ErrorKind::Uncategorized; - }; - - macro_rules! match_errno { - ($($($errno:ident)|+ => $errkind:ident),*, _ => $wildcard:ident $(,)?) => { - match errno { - $(e if $(e == ::wasi::$errno.raw())||+ => ErrorKind::$errkind),*, - _ => ErrorKind::$wildcard, - } - }; - } - - match_errno! { - ERRNO_2BIG => ArgumentListTooLong, - ERRNO_ACCES => PermissionDenied, - ERRNO_ADDRINUSE => AddrInUse, - ERRNO_ADDRNOTAVAIL => AddrNotAvailable, - ERRNO_AFNOSUPPORT => Unsupported, - ERRNO_AGAIN => WouldBlock, - // ALREADY => "connection already in progress", - // BADF => "bad file descriptor", - // BADMSG => "bad message", - ERRNO_BUSY => ResourceBusy, - // CANCELED => "operation canceled", - // CHILD => "no child processes", - ERRNO_CONNABORTED => ConnectionAborted, - ERRNO_CONNREFUSED => ConnectionRefused, - ERRNO_CONNRESET => ConnectionReset, - ERRNO_DEADLK => Deadlock, - // DESTADDRREQ => "destination address required", - ERRNO_DOM => InvalidInput, - // DQUOT => /* reserved */, - ERRNO_EXIST => AlreadyExists, - // FAULT => "bad address", - ERRNO_FBIG => FileTooLarge, - ERRNO_HOSTUNREACH => HostUnreachable, - // IDRM => "identifier removed", - // ILSEQ => "illegal byte sequence", - // INPROGRESS => "operation in progress", - ERRNO_INTR => Interrupted, - ERRNO_INVAL => InvalidInput, - ERRNO_IO => Uncategorized, - // ISCONN => "socket is connected", - ERRNO_ISDIR => IsADirectory, - ERRNO_LOOP => FilesystemLoop, - // MFILE => "file descriptor value too large", - ERRNO_MLINK => TooManyLinks, - // MSGSIZE => "message too large", - // MULTIHOP => /* reserved */, - ERRNO_NAMETOOLONG => InvalidFilename, - ERRNO_NETDOWN => NetworkDown, - // NETRESET => "connection aborted by network", - ERRNO_NETUNREACH => NetworkUnreachable, - // NFILE => "too many files open in system", - // NOBUFS => "no buffer space available", - ERRNO_NODEV => NotFound, - ERRNO_NOENT => NotFound, - // NOEXEC => "executable file format error", - // NOLCK => "no locks available", - // NOLINK => /* reserved */, - ERRNO_NOMEM => OutOfMemory, - // NOMSG => "no message of the desired type", - // NOPROTOOPT => "protocol not available", - ERRNO_NOSPC => StorageFull, - ERRNO_NOSYS => Unsupported, - ERRNO_NOTCONN => NotConnected, - ERRNO_NOTDIR => NotADirectory, - ERRNO_NOTEMPTY => DirectoryNotEmpty, - // NOTRECOVERABLE => "state not recoverable", - // NOTSOCK => "not a socket", - ERRNO_NOTSUP => Unsupported, - // NOTTY => "inappropriate I/O control operation", - ERRNO_NXIO => NotFound, - // OVERFLOW => "value too large to be stored in data type", - // OWNERDEAD => "previous owner died", - ERRNO_PERM => PermissionDenied, - ERRNO_PIPE => BrokenPipe, - // PROTO => "protocol error", - ERRNO_PROTONOSUPPORT => Unsupported, - // PROTOTYPE => "protocol wrong type for socket", - // RANGE => "result too large", - ERRNO_ROFS => ReadOnlyFilesystem, - ERRNO_SPIPE => NotSeekable, - ERRNO_SRCH => NotFound, - // STALE => /* reserved */, - ERRNO_TIMEDOUT => TimedOut, - ERRNO_TXTBSY => ResourceBusy, - ERRNO_XDEV => CrossesDevices, - ERRNO_NOTCAPABLE => PermissionDenied, - _ => Uncategorized, - } -} - -pub fn abort_internal() -> ! { - unsafe { libc::abort() } -} - -#[inline] -pub(crate) fn err2io(err: wasi::Errno) -> std_io::Error { - std_io::Error::from_raw_os_error(err.raw().into()) -} diff --git a/library/std/src/sys/pal/wasip1/mod.rs b/library/std/src/sys/pal/wasip1/mod.rs deleted file mode 100644 index ae5da3c1f77be..0000000000000 --- a/library/std/src/sys/pal/wasip1/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! System bindings for the wasm/web platform -//! -//! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for wasm. -//! -//! This is all super highly experimental and not actually intended for -//! wide/production use yet, it's still all in the experimental category. This -//! will likely change over time. -//! -//! Currently all functions here are basically stubs that immediately return -//! errors. The hope is that with a portability lint we can turn actually just -//! remove all this and just omit parts of the standard library if we're -//! compiling for wasm. That way it's a compile time error for something that's -//! guaranteed to be a runtime error! - -#[allow(unused)] -#[path = "../wasm/atomics/futex.rs"] -pub mod futex; - -pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; -pub mod time; - -#[path = "../unsupported/common.rs"] -#[deny(unsafe_op_in_unsafe_fn)] -#[allow(unused)] -mod common; - -pub use common::*; - -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}; diff --git a/library/std/src/sys/pal/wasip1/time.rs b/library/std/src/sys/pal/wasip1/time.rs deleted file mode 100644 index 0d8d0b59ac14a..0000000000000 --- a/library/std/src/sys/pal/wasip1/time.rs +++ /dev/null @@ -1,65 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::time::Duration; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant(Duration); - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct SystemTime(Duration); - -pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); - -fn current_time(clock: wasi::Clockid) -> Duration { - let ts = unsafe { - wasi::clock_time_get( - clock, 1, // precision... seems ignored though? - ) - .unwrap() - }; - Duration::new((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32) -} - -impl Instant { - pub fn now() -> Instant { - Instant(current_time(wasi::CLOCKID_MONOTONIC)) - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.checked_sub(other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_sub(*other)?)) - } -} - -impl SystemTime { - pub fn now() -> SystemTime { - SystemTime(current_time(wasi::CLOCKID_REALTIME)) - } - - pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { - SystemTime(Duration::from_nanos(ts)) - } - - pub fn to_wasi_timestamp(&self) -> Option { - self.0.as_nanos().try_into().ok() - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub(*other)?)) - } -} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs deleted file mode 100644 index c1d89da2677c9..0000000000000 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! System bindings for the wasi preview 2 target. -//! -//! This is the next evolution of the original wasi target, and is intended to -//! replace that target over time. -//! -//! To begin with, this target mirrors the wasi target 1 to 1, but over -//! time this will change significantly. - -#[allow(unused)] -#[path = "../wasm/atomics/futex.rs"] -pub mod futex; - -#[path = "../wasip1/os.rs"] -pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; -pub mod time; - -#[path = "../unsupported/common.rs"] -#[deny(unsafe_op_in_unsafe_fn)] -#[allow(unused)] -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}; - -mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs deleted file mode 100644 index 434891839944d..0000000000000 --- a/library/std/src/sys/pal/wasip2/time.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::time::Duration; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant(Duration); - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct SystemTime(Duration); - -pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); - -impl Instant { - pub fn now() -> Instant { - Instant(Duration::from_nanos(wasip2::clocks::monotonic_clock::now())) - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.checked_sub(other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_sub(*other)?)) - } - - pub(crate) fn as_duration(&self) -> &Duration { - &self.0 - } -} - -impl SystemTime { - pub fn now() -> SystemTime { - let now = wasip2::clocks::wall_clock::now(); - SystemTime(Duration::new(now.seconds, now.nanoseconds)) - } - - pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { - SystemTime(Duration::from_nanos(ts)) - } - - pub fn to_wasi_timestamp(&self) -> Option { - self.0.as_nanos().try_into().ok() - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add(*other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub(*other)?)) - } -} diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index d51ea9ad726b5..86d0f3fe49cb3 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -1,7 +1,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] cfg_select! { - any(target_family = "unix", target_os = "hermit") => { + any(target_family = "unix", target_os = "hermit", target_os = "wasi") => { mod unix; pub use unix::*; } @@ -37,14 +37,6 @@ cfg_select! { mod vexos; pub use vexos::*; } - all(target_os = "wasi", target_env = "p1") => { - mod wasip1; - pub use wasip1::*; - } - all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { - mod wasip2; - pub use wasip2::*; - } target_os = "xous" => { mod xous; pub use xous::*; diff --git a/library/std/src/sys/stdio/unix.rs b/library/std/src/sys/stdio/unix.rs index 8535e3539e9fd..ffed376a8e96e 100644 --- a/library/std/src/sys/stdio/unix.rs +++ b/library/std/src/sys/stdio/unix.rs @@ -1,14 +1,11 @@ #[cfg(target_os = "hermit")] use hermit_abi::{EBADF, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; -#[cfg(target_family = "unix")] +#[cfg(any(target_family = "unix", target_os = "wasi"))] use libc::{EBADF, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; -#[cfg(target_os = "hermit")] -use crate::os::hermit::io::FromRawFd; -#[cfg(target_family = "unix")] -use crate::os::unix::io::FromRawFd; +use crate::os::fd::FromRawFd; use crate::sys::fd::FileDesc; pub struct Stdin; diff --git a/library/std/src/sys/stdio/wasip1.rs b/library/std/src/sys/stdio/wasip1.rs deleted file mode 100644 index b70efd026f945..0000000000000 --- a/library/std/src/sys/stdio/wasip1.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem::ManuallyDrop; -use crate::os::raw; -use crate::os::wasi::io::{AsRawFd, FromRawFd}; -use crate::sys::fd::WasiFd; - -pub struct Stdin; -pub struct Stdout; -pub struct Stderr; - -impl Stdin { - pub const fn new() -> Stdin { - Stdin - } -} - -impl AsRawFd for Stdin { - #[inline] - fn as_raw_fd(&self) -> raw::c_int { - 0 - } -} - -impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(data)]) - } - - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { - ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read_buf(buf) - } - - fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data) - } - - #[inline] - fn is_read_vectored(&self) -> bool { - true - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout - } -} - -impl AsRawFd for Stdout { - #[inline] - fn as_raw_fd(&self) -> raw::c_int { - 1 - } -} - -impl io::Write for Stdout { - fn write(&mut self, data: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(data)]) - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub const fn new() -> Stderr { - Stderr - } -} - -impl AsRawFd for Stderr { - #[inline] - fn as_raw_fd(&self) -> raw::c_int { - 2 - } -} - -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(data)]) - } - - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) -} - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/stdio/wasip2.rs b/library/std/src/sys/stdio/wasip2.rs deleted file mode 100644 index 1fcb49a083dd0..0000000000000 --- a/library/std/src/sys/stdio/wasip2.rs +++ /dev/null @@ -1,120 +0,0 @@ -use wasip2::cli; -use wasip2::io::streams::{Error, InputStream, OutputStream, StreamError}; - -use crate::io::{self, BorrowedBuf, BorrowedCursor}; - -pub struct Stdin(Option); -pub struct Stdout(Option); -pub struct Stderr(Option); - -fn error_to_io(err: Error) -> io::Error { - // There exists a function in `wasi:filesystem` to optionally acquire an - // error code from an error, but the streams in use in this module are - // exclusively used with stdio meaning that a filesystem error is not - // possible here. - // - // In lieu of an error code, which WASIp2 does not specify, this instead - // carries along the `to_debug_string` implementation that the host - // supplies. If this becomes too expensive in the future this could also - // become `io::Error::from_raw_os_error(libc::EIO)` or similar. - io::Error::new(io::ErrorKind::Other, err.to_debug_string()) -} - -impl Stdin { - pub const fn new() -> Stdin { - Stdin(None) - } - - fn stream(&mut self) -> &InputStream { - self.0.get_or_insert_with(cli::stdin::get_stdin) - } -} - -impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(data); - self.read_buf(buf.unfilled())?; - Ok(buf.len()) - } - - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - match self.stream().blocking_read(u64::try_from(buf.capacity()).unwrap()) { - Ok(result) => { - buf.append(&result); - Ok(()) - } - Err(StreamError::Closed) => Ok(()), - Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)), - } - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout(None) - } - - fn stream(&mut self) -> &OutputStream { - self.0.get_or_insert_with(cli::stdout::get_stdout) - } -} - -fn write(stream: &OutputStream, buf: &[u8]) -> io::Result { - // WASIp2's `blocking_write_and_flush` function is defined as accepting no - // more than 4096 bytes. Larger writes can be issued by manually using - // `check_write`, `write`, and `blocking_flush`, but for now just go ahead - // and use `blocking_write_and_flush` and report a short write and let a - // higher level loop over the result. - const MAX: usize = 4096; - let buf = &buf[..buf.len().min(MAX)]; - match stream.blocking_write_and_flush(buf) { - Ok(()) => Ok(buf.len()), - Err(StreamError::Closed) => Ok(0), - Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)), - } -} - -impl io::Write for Stdout { - fn write(&mut self, data: &[u8]) -> io::Result { - write(self.stream(), data) - } - - fn flush(&mut self) -> io::Result<()> { - // Note that `OutputStream` has a `flush` function but for stdio all - // writes are accompanied with a flush which means that this flush - // doesn't need to do anything. - Ok(()) - } -} - -impl Stderr { - pub const fn new() -> Stderr { - Stderr(None) - } - - fn stream(&mut self) -> &OutputStream { - self.0.get_or_insert_with(cli::stderr::get_stderr) - } -} - -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - write(self.stream(), data) - } - - fn flush(&mut self) -> io::Result<()> { - // See `Stdout::flush` for why this is a noop. - Ok(()) - } -} - -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; - -pub fn is_ebadf(_err: &io::Error) -> bool { - // WASIp2 stdio streams are always available so ebadf never shows up. - false -} - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index b98be62be0ad2..cb6bf6518f81e 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -48,7 +48,7 @@ cfg_select! { mod unsupported; pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; } - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi") => { mod unix; pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; #[cfg(not(any( @@ -58,6 +58,7 @@ cfg_select! { target_os = "redox", target_os = "hurd", target_os = "aix", + target_os = "wasi", )))] pub use unix::set_name; #[cfg(any( @@ -71,6 +72,7 @@ cfg_select! { target_os = "hurd", target_os = "fuchsia", target_os = "vxworks", + target_os = "wasi", ))] pub use unix::sleep_until; #[expect(dead_code)] @@ -82,6 +84,7 @@ cfg_select! { target_os = "redox", target_os = "hurd", target_os = "aix", + target_os = "wasi", ))] pub use unsupported::set_name; } @@ -92,27 +95,6 @@ cfg_select! { mod unsupported; pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, DEFAULT_MIN_STACK_SIZE}; } - all(target_os = "wasi", target_env = "p1") => { - mod wasip1; - pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now}; - #[cfg(target_feature = "atomics")] - pub use wasip1::{Thread, available_parallelism}; - #[expect(dead_code)] - mod unsupported; - pub use unsupported::{current_os_id, set_name}; - #[cfg(not(target_feature = "atomics"))] - pub use unsupported::{Thread, available_parallelism}; - } - all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { - mod wasip2; - pub use wasip2::{sleep, sleep_until}; - #[expect(dead_code)] - mod unsupported; - // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled - // there is no support for threads, not even experimentally, not even in - // wasi-libc. Thus this is unconditionally unsupported. - pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; - } all(target_family = "wasm", target_feature = "atomics") => { mod wasm; pub use wasm::sleep; @@ -150,7 +132,7 @@ cfg_select! { target_os = "hurd", target_os = "fuchsia", target_os = "vxworks", - all(target_os = "wasi", not(target_env = "p1")), + target_os = "wasi", )))] pub fn sleep_until(deadline: crate::time::Instant) { use crate::time::Instant; diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 9b26262bc80dc..4a0f726498399 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -5,6 +5,7 @@ target_os = "redox", target_os = "hurd", target_os = "aix", + target_os = "wasi", )))] use crate::ffi::CStr; use crate::mem::{self, DropGuard, ManuallyDrop}; @@ -133,6 +134,7 @@ impl Thread { assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } + #[cfg(not(target_os = "wasi"))] pub fn id(&self) -> libc::pthread_t { self.id } @@ -594,6 +596,7 @@ pub fn sleep(dur: Duration) { target_os = "hurd", target_os = "fuchsia", target_os = "vxworks", + target_os = "wasi", ))] pub fn sleep_until(deadline: crate::time::Instant) { use crate::time::Instant; diff --git a/library/std/src/sys/thread/wasip1.rs b/library/std/src/sys/thread/wasip1.rs deleted file mode 100644 index 83001fad49c81..0000000000000 --- a/library/std/src/sys/thread/wasip1.rs +++ /dev/null @@ -1,185 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -#[cfg(target_feature = "atomics")] -use crate::io; -use crate::mem; -#[cfg(target_feature = "atomics")] -use crate::num::NonZero; -#[cfg(target_feature = "atomics")] -use crate::sys::os; -use crate::time::Duration; -#[cfg(target_feature = "atomics")] -use crate::{cmp, ptr}; - -// Add a few symbols not in upstream `libc` just yet. -#[cfg(target_feature = "atomics")] -mod libc { - pub use libc::*; - - pub use crate::ffi; - - // defined in wasi-libc - // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 - #[repr(C)] - union pthread_attr_union { - __i: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], - __vi: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], - __s: [ffi::c_ulong; if size_of::() == 8 { 7 } else { 9 }], - } - - #[repr(C)] - pub struct pthread_attr_t { - __u: pthread_attr_union, - } - - #[allow(non_camel_case_types)] - pub type pthread_t = *mut ffi::c_void; - - pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; - - unsafe extern "C" { - pub fn pthread_create( - native: *mut pthread_t, - attr: *const pthread_attr_t, - f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, - value: *mut ffi::c_void, - ) -> ffi::c_int; - pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; - pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_attr_setstacksize( - attr: *mut pthread_attr_t, - stack_size: libc::size_t, - ) -> ffi::c_int; - pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; - } -} - -#[cfg(target_feature = "atomics")] -pub struct Thread { - id: libc::pthread_t, -} - -#[cfg(target_feature = "atomics")] -impl Drop for Thread { - fn drop(&mut self) { - let ret = unsafe { libc::pthread_detach(self.id) }; - debug_assert_eq!(ret, 0); - } -} - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -#[cfg(target_feature = "atomics")] -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { - let p = Box::into_raw(Box::new(p)); - let mut native: libc::pthread_t = unsafe { mem::zeroed() }; - let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; - assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); - - let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); - - match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { - 0 => {} - n => { - assert_eq!(n, libc::EINVAL); - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - let page_size = os::page_size(); - let stack_size = - (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); - } - }; - - let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; - // Note: if the thread creation fails and this assert fails, then p will - // be leaked. However, an alternative design could cause double-free - // which is clearly worse. - assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0); - - return if ret != 0 { - // The thread failed to start and as a result p was not consumed. Therefore, it is - // safe to reconstruct the box so that it gets deallocated. - unsafe { - drop(Box::from_raw(p)); - } - Err(io::Error::from_raw_os_error(ret)) - } else { - Ok(Thread { id: native }) - }; - - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - ptr::null_mut() - } - } - - pub fn join(self) { - let id = mem::ManuallyDrop::new(self).id; - let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - if ret != 0 { - rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } - } -} - -#[cfg(target_feature = "atomics")] -pub fn available_parallelism() -> io::Result> { - match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { - -1 => Err(io::Error::last_os_error()), - cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), - } -} - -pub fn yield_now() { - let ret = unsafe { wasi::sched_yield() }; - debug_assert_eq!(ret, Ok(())); -} - -pub fn sleep(dur: Duration) { - let mut nanos = dur.as_nanos(); - while nanos > 0 { - const USERDATA: wasi::Userdata = 0x0123_45678; - - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: u64::try_from(nanos).unwrap_or(u64::MAX), - precision: 0, - flags: 0, - }; - nanos -= u128::from(clock.timeout); - - let in_ = wasi::Subscription { - userdata: USERDATA, - u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, - }; - unsafe { - let mut event: wasi::Event = mem::zeroed(); - let res = wasi::poll_oneoff(&in_, &mut event, 1); - match (res, event) { - ( - Ok(1), - wasi::Event { - userdata: USERDATA, - error: wasi::ERRNO_SUCCESS, - type_: wasi::EVENTTYPE_CLOCK, - .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), - } - } - } -} diff --git a/library/std/src/sys/thread/wasip2.rs b/library/std/src/sys/thread/wasip2.rs deleted file mode 100644 index 420cad2a5e4ab..0000000000000 --- a/library/std/src/sys/thread/wasip2.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::time::{Duration, Instant}; - -pub fn sleep(dur: Duration) { - // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is - // entirely drained. - let mut remaining = dur.as_nanos(); - while remaining > 0 { - let amt = u64::try_from(remaining).unwrap_or(u64::MAX); - wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); - remaining -= u128::from(amt); - } -} - -pub fn sleep_until(deadline: Instant) { - match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { - // If the point in time we're sleeping to fits within a 64-bit - // number of nanoseconds then directly use `subscribe_instant`. - Ok(deadline) => { - wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); - } - // ... otherwise we're sleeping for 500+ years relative to the - // "start" of what the system is using as a clock so speed/accuracy - // is not so much of a concern. Use `sleep` instead. - Err(_) => { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - sleep(delay); - } - } - } -} 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" }, ] }, {