diff --git a/Cargo.toml b/Cargo.toml index 9411f906..c2400ba1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,15 +17,15 @@ exclude = ["/.*"] rust-version = "1.63" [dev-dependencies] -#async-std = { version = "1.10.0", features = ["attributes"] } +async-std = { version = "1.13.0", features = ["attributes", "io_safety"] } anyhow = "1.0.37" -#cap-async-std = { path = "cap-async-std", version = "^0.25.0" } -cap-fs-ext = { path = "cap-fs-ext", version = "^3.3.0" } -cap-net-ext = { path = "cap-net-ext", version = "^3.3.0" } -cap-directories = { path = "cap-directories", version = "^3.3.0" } -cap-std = { path = "cap-std", version = "^3.3.0" } -cap-tempfile = { path = "cap-tempfile", version = "^3.3.0" } -cap-rand = { path = "cap-rand", version = "^3.3.0" } +cap-async-std = { path = "cap-async-std", version = "3.3.0" } +cap-fs-ext = { path = "cap-fs-ext", version = "3.3.0" } +cap-net-ext = { path = "cap-net-ext", version = "3.3.0" } +cap-directories = { path = "cap-directories", version = "3.3.0" } +cap-std = { path = "cap-std", version = "3.3.0" } +cap-tempfile = { path = "cap-tempfile", version = "3.3.0" } +cap-rand = { path = "cap-rand", version = "3.3.0" } rand = "0.8.1" tempfile = "3.1.0" camino = "1.0.5" @@ -55,23 +55,23 @@ fs_utf8 = [ "cap-fs-ext/fs_utf8", "cap-tempfile/fs_utf8", ] -#async_std_fs_utf8 = [ -# "cap-async-std/fs_utf8", -# "cap-fs-ext/async_std_fs_utf8" -#] +async_std_fs_utf8 = [ + "cap-async-std/fs_utf8", + "cap-fs-ext/async_std_fs_utf8" +] arf_strings = [ "cap-std/arf_strings", "cap-fs-ext/arf_strings", "cap-tempfile/arf_strings", ] -#async_std_arf_strings = [ -# "cap-async-std/arf_strings", -# "cap-fs-ext/async_std_arf_strings" -#] +async_std_arf_strings = [ + "cap-async-std/arf_strings", + "cap-fs-ext/async_std_arf_strings" +] [workspace] members = [ - #"cap-async-std", + "cap-async-std", "cap-fs-ext", "cap-net-ext", "cap-directories", diff --git a/cap-async-std/Cargo.toml b/cap-async-std/Cargo.toml index 3fb03cc0..303ba57e 100644 --- a/cap-async-std/Cargo.toml +++ b/cap-async-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cap-async-std" -version = "0.25.2" +version = "3.3.0" description = "Capability-based version of async-std" authors = [ "Dan Gohman ", @@ -11,15 +11,13 @@ keywords = ["network", "file", "async", "future", "await"] categories = ["filesystem", "network-programming", "asynchronous", "concurrency"] repository = "https://github.com/bytecodealliance/cap-std" edition = "2021" -publish = false # temporary, until async-rs/async-std#1036 is available [dependencies] arf-strings = { version = "0.7.0", optional = true } -# Enable "unstable" for `spawn_blocking`. -async-std = { version = "1.10.0", features = ["attributes", "unstable"] } -cap-primitives = { path = "../cap-primitives", version = "^0.25.0" } +async-std = { version = "1.13.0", features = ["attributes", "io_safety"] } +cap-primitives = { path = "../cap-primitives", version = "^3.3.0" } io-lifetimes = { version = "2.0.0", default-features = false, features = ["async-std"] } -io-extras = { version = "0.17.0", features = ["use_async_std"] } +io-extras = { version = "0.18.3", features = ["use_async_std"] } camino = { version = "1.0.5", optional = true } [target.'cfg(not(windows))'.dependencies] diff --git a/cap-async-std/build.rs b/cap-async-std/build.rs new file mode 100644 index 00000000..3706ce13 --- /dev/null +++ b/cap-async-std/build.rs @@ -0,0 +1,84 @@ +use std::env::var; +use std::io::Write; + +fn main() { + use_feature_or_nothing("windows_file_type_ext"); + + // Cfgs that users may set. + println!("cargo:rustc-check-cfg=cfg(io_lifetimes_use_std)"); + + // Don't rerun this on changes other than build.rs, as we only depend on + // the rustc version. + println!("cargo:rerun-if-changed=build.rs"); +} + +fn use_feature_or_nothing(feature: &str) { + if has_feature(feature) { + use_feature(feature); + } + println!("cargo:rustc-check-cfg=cfg({})", feature); +} + +fn use_feature(feature: &str) { + println!("cargo:rustc-cfg={}", feature); +} + +/// Test whether the rustc at `var("RUSTC")` supports the given feature. +fn has_feature(feature: &str) -> bool { + can_compile(&format!( + "#![allow(stable_features)]\n#![feature({})]", + feature + )) +} + +/// Test whether the rustc at `var("RUSTC")` can compile the given code. +fn can_compile>(test: T) -> bool { + use std::process::Stdio; + + let out_dir = var("OUT_DIR").unwrap(); + let rustc = var("RUSTC").unwrap(); + let target = var("TARGET").unwrap(); + + // Use `RUSTC_WRAPPER` if it's set, unless it's set to an empty string, + // as documented [here]. + // [here]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads + let wrapper = var("RUSTC_WRAPPER") + .ok() + .and_then(|w| if w.is_empty() { None } else { Some(w) }); + + let mut cmd = if let Some(wrapper) = wrapper { + let mut cmd = std::process::Command::new(wrapper); + // The wrapper's first argument is supposed to be the path to rustc. + cmd.arg(rustc); + cmd + } else { + std::process::Command::new(rustc) + }; + + cmd.arg("--crate-type=rlib") // Don't require `main`. + .arg("--emit=metadata") // Do as little as possible but still parse. + .arg("--target") + .arg(target) + .arg("--out-dir") + .arg(out_dir); // Put the output somewhere inconsequential. + + // If Cargo wants to set RUSTFLAGS, use that. + if let Ok(rustflags) = var("CARGO_ENCODED_RUSTFLAGS") { + if !rustflags.is_empty() { + for arg in rustflags.split('\x1f') { + cmd.arg(arg); + } + } + } + + let mut child = cmd + .arg("-") // Read from stdin. + .stdin(Stdio::piped()) // Stdin is a pipe. + .stderr(Stdio::null()) // Errors from feature detection aren't interesting and can be confusing. + .spawn() + .unwrap(); + + writeln!(child.stdin.take().unwrap(), "{}", test.as_ref()).unwrap(); + + child.wait().unwrap().success() +} diff --git a/cap-async-std/src/fs/dir.rs b/cap-async-std/src/fs/dir.rs index f6cbb80d..692443f1 100644 --- a/cap-async-std/src/fs/dir.rs +++ b/cap-async-std/src/fs/dir.rs @@ -13,12 +13,14 @@ use cap_primitives::fs::{ remove_open_dir_all, rename, set_permissions, stat, DirOptions, FollowSymlinks, Permissions, }; use cap_primitives::AmbientAuthority; +use io_lifetimes::raw::{AsRawFilelike, FromRawFilelike}; #[cfg(not(windows))] use io_lifetimes::{AsFd, BorrowedFd, OwnedFd}; use io_lifetimes::{AsFilelike, FromFilelike}; #[cfg(windows)] use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle}; use std::fmt; +use std::mem::ManuallyDrop; #[cfg(unix)] use { crate::os::unix::net::{UnixDatagram, UnixListener, UnixStream}, @@ -883,15 +885,20 @@ impl Dir { /// This can be useful when interacting with other libraries and or C/C++ /// code which has invoked `openat(..., O_DIRECTORY)` external to this /// crate. - pub fn reopen_dir(dir: &Filelike) -> io::Result { - spawn_blocking(move || { - cap_primitives::fs::open_dir( - &dir.as_filelike_view::(), - std::path::Component::CurDir.as_ref(), - ) + pub async fn reopen_dir(dir: &Filelike) -> io::Result { + // Our public API has a `&Filelike` here, which prevents us from doing + // a `clone` as we usually do. So instead, we use the raw filelike, which + // we can clone and depend on it remaining open until we return. + let raw_filelike = dir.as_filelike_view::().as_raw_filelike(); + // SAFETY: `raw_filelike` remains open for the duration of the + // `reopen_dir` call. + let file = ManuallyDrop::new(unsafe { std::fs::File::from_raw_filelike(raw_filelike) }); + let dir = spawn_blocking(move || { + cap_primitives::fs::open_dir(&*file, std::path::Component::CurDir.as_ref()) }) - .await - .map(Self::from_std_file) + .await? + .into(); + Ok(Self::from_std_file(dir)) } } diff --git a/cap-async-std/src/fs/file.rs b/cap-async-std/src/fs/file.rs index c88297e8..adee6427 100644 --- a/cap-async-std/src/fs/file.rs +++ b/cap-async-std/src/fs/file.rs @@ -90,8 +90,7 @@ impl File { #[inline] pub async fn metadata(&self) -> io::Result { let clone = self.clone(); - let sync = clone.std.as_filelike_view::(); - spawn_blocking(move || metadata_from(&*sync)).await + spawn_blocking(move || metadata_from(&*clone.std.as_filelike_view::())).await } // async_std doesn't have `try_clone`. diff --git a/cap-async-std/src/fs/mod.rs b/cap-async-std/src/fs/mod.rs index 11e00162..7fccbaf0 100644 --- a/cap-async-std/src/fs/mod.rs +++ b/cap-async-std/src/fs/mod.rs @@ -40,9 +40,9 @@ pub use cap_primitives::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permis // Re-export conditional types from `cap_primitives`. #[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))] pub use cap_primitives::fs::FileTypeExt; -pub use cap_primitives::fs::{FileExt, OpenOptionsExt, MetadataExt}; #[cfg(unix)] pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt}; +pub use cap_primitives::fs::{FileExt, MetadataExt, OpenOptionsExt}; // Re-export things from `async_std` that we can use as-is. #[cfg(target_os = "wasi")] diff --git a/cap-async-std/src/fs_utf8/dir.rs b/cap-async-std/src/fs_utf8/dir.rs index 7cfa1f38..86da21a5 100644 --- a/cap-async-std/src/fs_utf8/dir.rs +++ b/cap-async-std/src/fs_utf8/dir.rs @@ -3,12 +3,14 @@ use crate::fs_utf8::{from_utf8, to_utf8, DirBuilder, File, Metadata, ReadDir}; use async_std::{fs, io}; use camino::{Utf8Path, Utf8PathBuf}; use cap_primitives::AmbientAuthority; +use io_lifetimes::raw::{AsRawFilelike, FromRawFilelike}; use io_lifetimes::AsFilelike; #[cfg(not(windows))] -use io_lifetimes::{AsFd, BorrowedFd, FromFd, IntoFd, OwnedFd}; +use io_lifetimes::{AsFd, BorrowedFd, OwnedFd}; #[cfg(windows)] use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle}; use std::fmt; +use std::mem::ManuallyDrop; #[cfg(unix)] use { crate::os::unix::net::{UnixDatagram, UnixListener, UnixStream}, @@ -170,8 +172,8 @@ impl Dir { to_dir: &Self, to: Q, ) -> io::Result { - let from = from_utf8(from)?; - let to = from_utf8(to)?; + let from = from_utf8(from.as_ref())?; + let to = from_utf8(to.as_ref())?; self.cap_std.copy(from, &to_dir.cap_std, to).await } @@ -186,8 +188,8 @@ impl Dir { dst_dir: &Self, dst: Q, ) -> io::Result<()> { - let src = from_utf8(src)?; - let dst = from_utf8(dst)?; + let src = from_utf8(src.as_ref())?; + let dst = from_utf8(dst.as_ref())?; self.cap_std.hard_link(src, &dst_dir.cap_std, dst).await } @@ -321,8 +323,8 @@ impl Dir { to_dir: &Self, to: Q, ) -> io::Result<()> { - let from = from_utf8(from)?; - let to = from_utf8(to)?; + let from = from_utf8(from.as_ref())?; + let to = from_utf8(to.as_ref())?; self.cap_std.rename(from, &to_dir.cap_std, to).await } @@ -388,8 +390,8 @@ impl Dir { original: P, link: Q, ) -> io::Result<()> { - let original = from_utf8(original)?; - let link = from_utf8(link)?; + let original = from_utf8(original.as_ref())?; + let link = from_utf8(link.as_ref())?; self.cap_std.symlink(original, link).await } @@ -416,8 +418,8 @@ impl Dir { original: P, link: Q, ) -> io::Result<()> { - let original = from_utf8(original)?; - let link = from_utf8(link)?; + let original = from_utf8(original.as_ref())?; + let link = from_utf8(link.as_ref())?; self.cap_std.symlink_file(original, link).await } @@ -444,8 +446,8 @@ impl Dir { original: P, link: Q, ) -> io::Result<()> { - let original = from_utf8(original)?; - let link = from_utf8(link)?; + let original = from_utf8(original.as_ref())?; + let link = from_utf8(link.as_ref())?; self.cap_std.symlink_dir(original, link).await } @@ -655,15 +657,17 @@ impl Dir { /// This can be useful when interacting with other libraries and or C/C++ /// code which has invoked `openat(..., O_DIRECTORY)` external to this /// crate. - pub fn reopen_dir(dir: &Filelike) -> io::Result { - spawn_blocking(move || { - cap_primitives::fs::open_dir( - &dir.as_filelike_view::(), - std::path::Component::CurDir.as_ref(), - ) - }) - .await - .map(Self::from_std_file) + pub async fn reopen_dir(dir: &Filelike) -> io::Result { + // Our public API has a `&Filelike` here, which prevents us from doing + // a `clone` as we usually do. So instead, we use the raw fd, which we + // can clone and depend on it remaining open until we return. + let raw_filelike = dir.as_filelike_view::().as_raw_filelike(); + // SAFETY: `raw_filelike` remains open for the duration of the `reopen_dir` + // call. + let file = ManuallyDrop::new(unsafe { std::fs::File::from_raw_filelike(raw_filelike) }); + crate::fs::Dir::reopen_dir(&*file) + .await + .map(Self::from_cap_std) } } diff --git a/cap-async-std/src/fs_utf8/file.rs b/cap-async-std/src/fs_utf8/file.rs index 3bb66cf8..5bbc5aa6 100644 --- a/cap-async-std/src/fs_utf8/file.rs +++ b/cap-async-std/src/fs_utf8/file.rs @@ -10,7 +10,7 @@ use async_std::task::{Context, Poll}; use camino::Utf8Path; use cap_primitives::AmbientAuthority; #[cfg(not(windows))] -use io_lifetimes::{AsFd, BorrowedFd, FromFd, IntoFd, OwnedFd}; +use io_lifetimes::{AsFd, BorrowedFd, OwnedFd}; #[cfg(windows)] use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle}; use std::fmt; @@ -93,8 +93,7 @@ impl File { /// This corresponds to [`async_std::fs::File::metadata`]. #[inline] pub async fn metadata(&self) -> io::Result { - let clone = self.clone(); - spawn_blocking(move || clone.metadata()).await + self.cap_std.metadata().await } // async_std doesn't have `try_clone`. @@ -119,7 +118,7 @@ impl File { path: P, ambient_authority: AmbientAuthority, ) -> io::Result { - let path = from_utf8(path)?; + let path = from_utf8(path.as_ref())?; crate::fs::File::open_ambient(path, ambient_authority) .await .map(Self::from_cap_std) @@ -134,11 +133,11 @@ impl File { /// This function is not sandboxed and may access any path that the host /// process has access to. #[inline] - pub async fn create_ambient>( + pub async fn create_ambient>( path: P, ambient_authority: AmbientAuthority, ) -> io::Result { - let path = from_utf8(path)?; + let path = from_utf8(path.as_ref())?; crate::fs::File::create_ambient(path, ambient_authority) .await .map(Self::from_cap_std) @@ -158,7 +157,7 @@ impl File { options: &OpenOptions, ambient_authority: AmbientAuthority, ) -> io::Result { - let path = from_utf8(path)?; + let path = from_utf8(path.as_ref())?; crate::fs::File::open_ambient_with(path, options, ambient_authority) .await .map(Self::from_cap_std) diff --git a/cap-async-std/src/fs_utf8/mod.rs b/cap-async-std/src/fs_utf8/mod.rs index 8503f6cd..3002a862 100644 --- a/cap-async-std/src/fs_utf8/mod.rs +++ b/cap-async-std/src/fs_utf8/mod.rs @@ -25,9 +25,9 @@ pub use crate::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permissions}; // Re-export conditional types from `cap_primitives`. #[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))] pub use cap_primitives::fs::FileTypeExt; -pub use cap_primitives::fs::{FileExt, OpenOptionsExt, MetadataExt}; #[cfg(unix)] pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt}; +pub use cap_primitives::fs::{FileExt, MetadataExt, OpenOptionsExt}; // Re-export `camino` to make it easy for users to depend on the same // version we do, because we use its types in our public API. @@ -37,7 +37,7 @@ use camino::{Utf8Path, Utf8PathBuf}; #[cfg(not(feature = "arf_strings"))] fn from_utf8<'a>(path: &'a Utf8Path) -> std::io::Result<&'a async_std::path::Path> { - Ok(path.as_std_path()) + Ok(path.as_std_path().into()) } #[cfg(feature = "arf_strings")] diff --git a/cap-async-std/src/lib.rs b/cap-async-std/src/lib.rs index 3e42b07c..2397ba31 100644 --- a/cap-async-std/src/lib.rs +++ b/cap-async-std/src/lib.rs @@ -49,5 +49,5 @@ pub use cap_primitives::{ambient_authority, AmbientAuthority}; // version we do, because we use its types in our public API. pub use async_std; // And these are also part of our public API -pub use io_lifetimes; pub use cap_primitives::ipnet; +pub use io_lifetimes; diff --git a/cap-async-std/src/net/pool.rs b/cap-async-std/src/net/pool.rs index 171dfe83..2da1cc5e 100644 --- a/cap-async-std/src/net/pool.rs +++ b/cap-async-std/src/net/pool.rs @@ -36,12 +36,15 @@ impl Pool { /// # Ambient Authority /// /// This function allows ambient access to any IP address. - pub fn insert( + pub async fn insert( &mut self, addrs: A, ambient_authority: AmbientAuthority, ) -> io::Result<()> { - self.cap.insert(addrs, ambient_authority) + for addr in addrs.to_socket_addrs().await? { + self.cap.insert(addr, ambient_authority)?; + } + Ok(()) } /// Add a specific [`net::SocketAddr`] to the pool. @@ -65,11 +68,16 @@ impl Pool { /// # Ambient Authority /// /// This function allows ambient access to any IP address. - pub fn insert_ip_net_port_any(&mut self, ip_net: ipnet::IpNet, ambient_authority: AmbientAuthority) { + pub fn insert_ip_net_port_any( + &mut self, + ip_net: ipnet::IpNet, + ambient_authority: AmbientAuthority, + ) { self.cap.insert_ip_net_port_any(ip_net, ambient_authority) } - /// Add a range of network addresses, accepting a range of ports, to the pool. + /// Add a range of network addresses, accepting a range of ports, to the + /// pool. /// /// This grants access to the port range starting at `ports_start` and, /// if `ports_end` is provided, ending before `ports_end`. @@ -84,7 +92,8 @@ impl Pool { ports_end: Option, ambient_authority: AmbientAuthority, ) { - self.cap.insert_ip_net_port_range(ip_net, ports_start, ports_end, ambient_authority) + self.cap + .insert_ip_net_port_range(ip_net, ports_start, ports_end, ambient_authority) } /// Add a range of network addresses with a specific port to the pool. diff --git a/cap-async-std/src/net/udp_socket.rs b/cap-async-std/src/net/udp_socket.rs index 815184b4..6cb418c3 100644 --- a/cap-async-std/src/net/udp_socket.rs +++ b/cap-async-std/src/net/udp_socket.rs @@ -337,7 +337,7 @@ impl IntoRawSocket for UdpSocket { impl From for OwnedSocket { #[inline] fn from(socket: UdpSocket) -> OwnedSocket { - self.std.into() + socket.std.into() } } diff --git a/cap-fs-ext/Cargo.toml b/cap-fs-ext/Cargo.toml index 3d1527a3..5c03dc6f 100644 --- a/cap-fs-ext/Cargo.toml +++ b/cap-fs-ext/Cargo.toml @@ -14,13 +14,12 @@ edition = "2021" [dependencies] arf-strings = { version = "0.7.0", optional = true } -#cap-async-std = { path = "../cap-async-std", optional = true, version = "^0.25.0" } -cap-std = { path = "../cap-std", optional = true, version = "^3.3.0" } -cap-primitives = { path = "../cap-primitives", version = "^3.3.0" } +cap-async-std = { path = "../cap-async-std", optional = true, version = "3.3.0" } +cap-std = { path = "../cap-std", optional = true, version = "3.3.0" } +cap-primitives = { path = "../cap-primitives", version = "3.3.0" } io-lifetimes = { version = "2.0.0", default-features = false } -# Enable "unstable" for `spawn_blocking`. -#async-std = { version = "1.10.0", features = ["attributes", "unstable"], optional = true } -#async-trait = { version = "0.1.42", optional = true } +async-std = { version = "1.13.0", features = ["io_safety", "attributes"], optional = true } +async-trait = { version = "0.1.42", optional = true } camino = { version = "1.0.5", optional = true } [features] @@ -28,10 +27,9 @@ default = ["std"] fs_utf8 = ["cap-std/fs_utf8", "camino"] arf_strings = ["cap-std/arf_strings", "fs_utf8", "arf-strings"] std = ["cap-std"] -# Temporarily disable cap-async-std. -#async_std = ["cap-async-std", "async-std", "io-lifetimes/async-std", "async-trait"] -#async_std_fs_utf8 = ["cap-async-std/fs_utf8", "camino"] -#async_std_arf_strings = ["cap-async-std/arf_strings", "async_std_fs_utf8", "arf-strings"] +async_std = ["cap-async-std", "async-std", "io-lifetimes/async-std", "async-trait"] +async_std_fs_utf8 = ["cap-async-std/fs_utf8", "camino"] +async_std_arf_strings = ["cap-async-std/arf_strings", "async_std_fs_utf8", "arf-strings"] [target.'cfg(windows)'.dependencies.windows-sys] version = "0.52.0" diff --git a/cap-fs-ext/src/dir_ext.rs b/cap-fs-ext/src/dir_ext.rs index a8f2a82f..71124a6c 100644 --- a/cap-fs-ext/src/dir_ext.rs +++ b/cap-fs-ext/src/dir_ext.rs @@ -233,11 +233,23 @@ pub trait AsyncDirExt { ) -> io::Result<()>; /// Test for accessibility or existence of a filesystem object. - async fn access>(&self, path: P, type_: AccessType) -> io::Result<()>; + async fn access + Send>( + &self, + path: P, + type_: AccessType, + ) -> io::Result<()>; + + /// Test for accessibility or existence of a filesystem object, without + /// following symbolic links. + async fn access_symlink + Send>( + &self, + path: P, + type_: AccessType, + ) -> io::Result<()>; /// Changes the permissions found on a file or a directory, without following /// symbolic links. - async fn set_symlink_permissions>( + async fn set_symlink_permissions + Send>( &self, path: P, perm: Permissions, @@ -453,11 +465,12 @@ pub trait AsyncDirExtUtf8 { async fn remove_file_or_symlink + Send>(&self, path: P) -> io::Result<()>; /// Test for accessibility or existence of a filesystem object. - async fn access>(&self, path: P, type_: AccessType) -> io::Result<()>; + async fn access + Send>(&self, path: P, type_: AccessType) + -> io::Result<()>; /// Test for accessibility or existence of a filesystem object, without /// following symbolic links. - async fn access_symlink>( + async fn access_symlink + Send>( &self, path: P, type_: AccessType, @@ -465,7 +478,7 @@ pub trait AsyncDirExtUtf8 { /// Changes the permissions found on a file or a directory, without following /// symbolic links. - async fn set_symlink_permissions>( + async fn set_symlink_permissions + Send>( &self, path: P, perm: Permissions, @@ -924,8 +937,7 @@ impl AsyncDirExt for cap_async_std::fs::Dir { ) -> io::Result<()> { use crate::OpenOptionsFollowExt; use cap_primitives::fs::_WindowsByHandle; - use cap_std::fs::OpenOptions; - use std::os::windows::fs::OpenOptionsExt; + use cap_std::fs::{OpenOptions, OpenOptionsExt}; use windows_sys::Win32::Storage::FileSystem::{ DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, @@ -938,7 +950,7 @@ impl AsyncDirExt for cap_async_std::fs::Dir { opts.follow(FollowSymlinks::No); let file = self.open_with(path, &opts).await?; - let meta = file.metadata()?; + let meta = file.metadata().await?; if meta.file_type().is_symlink() && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY { @@ -955,6 +967,64 @@ impl AsyncDirExt for cap_async_std::fs::Dir { Ok(()) } + + /// Test for accessibility or existence of a filesystem object. + async fn access + Send>( + &self, + path: P, + type_: AccessType, + ) -> io::Result<()> { + let path = path.as_ref().to_path_buf(); + let clone = self.clone(); + spawn_blocking(move || { + access( + &clone.as_filelike_view::(), + path.as_ref(), + type_, + FollowSymlinks::Yes, + ) + }) + .await + } + + /// Test for accessibility or existence of a filesystem object, without + /// following symbolic links. + async fn access_symlink + Send>( + &self, + path: P, + type_: AccessType, + ) -> io::Result<()> { + let path = path.as_ref().to_path_buf(); + let clone = self.clone(); + spawn_blocking(move || { + access( + &clone.as_filelike_view::(), + path.as_ref(), + type_, + FollowSymlinks::No, + ) + }) + .await + } + + /// Changes the permissions found on a file or a directory, without following + /// symbolic links. + async fn set_symlink_permissions + Send>( + &self, + path: P, + perm: Permissions, + ) -> io::Result<()> { + let path = path.as_ref().to_path_buf(); + let clone = self.clone(); + spawn_blocking(move || { + set_symlink_permissions( + &clone.as_filelike_view::(), + path.as_ref(), + perm, + ) + }) + .await + } } #[cfg(all(feature = "std", feature = "fs_utf8"))] @@ -1245,8 +1315,8 @@ impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir { src: P, dst: Q, ) -> io::Result<()> { - let src = from_utf8(src)?; - let dst = from_utf8(dst)?; + let src = from_utf8(src.as_ref())?; + let dst = from_utf8(dst.as_ref())?; let clone = self.clone(); spawn_blocking(move || symlink(&src, &clone.as_filelike_view::(), &dst)) .await @@ -1279,9 +1349,9 @@ impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir { src: P, dst: Q, ) -> io::Result<()> { - let src = from_utf8(src)?; + let src = from_utf8(src.as_ref())?; let src_ = src.clone(); - let dst = from_utf8(dst)?; + let dst = from_utf8(dst.as_ref())?; let clone = self.clone(); // Call `stat` directly to avoid `async_trait` capturing `self`. let metadata = spawn_blocking(move || { @@ -1313,8 +1383,8 @@ impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir { src: P, dst: Q, ) -> io::Result<()> { - let src = from_utf8(src)?; - let dst = from_utf8(dst)?; + let src = from_utf8(src.as_ref())?; + let dst = from_utf8(dst.as_ref())?; let clone = self.clone(); spawn_blocking(move || symlink_file(&src, &clone.as_filelike_view::(), &dst)) .await @@ -1327,8 +1397,8 @@ impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir { src: P, dst: Q, ) -> io::Result<()> { - let src = from_utf8(src)?; - let dst = from_utf8(dst)?; + let src = from_utf8(src.as_ref())?; + let dst = from_utf8(dst.as_ref())?; let clone = self.clone(); spawn_blocking(move || symlink_dir(&src, &clone.as_filelike_view::(), &dst)) .await @@ -1358,8 +1428,7 @@ impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir { async fn remove_file_or_symlink + Send>(&self, path: P) -> io::Result<()> { use crate::{FollowSymlinks, OpenOptionsFollowExt}; use cap_primitives::fs::_WindowsByHandle; - use cap_std::fs::OpenOptions; - use std::os::windows::fs::OpenOptionsExt; + use cap_std::fs::{OpenOptions, OpenOptionsExt}; use windows_sys::Win32::Storage::FileSystem::{ DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, @@ -1372,7 +1441,7 @@ impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir { opts.follow(FollowSymlinks::No); let file = self.open_with(path, &opts).await?; - let meta = file.metadata()?; + let meta = file.metadata().await?; if meta.file_type().is_symlink() && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY { @@ -1389,6 +1458,61 @@ impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir { Ok(()) } + + async fn access + Send>( + &self, + path: P, + type_: AccessType, + ) -> io::Result<()> { + let path = from_utf8(path.as_ref())?; + let clone = self.clone(); + spawn_blocking(move || { + access( + &clone.as_filelike_view::(), + path.as_ref(), + type_, + FollowSymlinks::Yes, + ) + }) + .await + } + + async fn access_symlink + Send>( + &self, + path: P, + type_: AccessType, + ) -> io::Result<()> { + let path = from_utf8(path.as_ref())?; + let clone = self.clone(); + spawn_blocking(move || { + access( + &clone.as_filelike_view::(), + path.as_ref(), + type_, + FollowSymlinks::No, + ) + }) + .await + } + + /// Changes the permissions found on a file or a directory, without following + /// symbolic links. + async fn set_symlink_permissions + Send>( + &self, + path: P, + perm: Permissions, + ) -> io::Result<()> { + let path = from_utf8(path.as_ref())?; + let clone = self.clone(); + spawn_blocking(move || { + set_symlink_permissions( + &clone.as_filelike_view::(), + path.as_ref(), + perm, + ) + }) + .await + } } #[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))] diff --git a/cap-fs-ext/src/lib.rs b/cap-fs-ext/src/lib.rs index cacef0e3..22ea2b1e 100644 --- a/cap-fs-ext/src/lib.rs +++ b/cap-fs-ext/src/lib.rs @@ -9,9 +9,6 @@ #![doc( html_favicon_url = "https://raw.githubusercontent.com/bytecodealliance/cap-std/main/media/cap-std.ico" )] -// Allow cfg(feature = "async_std") even though it isn't a feature. async_std -// support is temporarily disabled. -#![allow(unexpected_cfgs)] mod dir_entry_ext; mod dir_ext; @@ -24,6 +21,10 @@ mod open_options_sync_ext; mod reopen; pub use dir_entry_ext::DirEntryExt; +#[cfg(feature = "async_std")] +pub use dir_ext::AsyncDirExt; +#[cfg(all(feature = "async_std", feature = "fs_utf8"))] +pub use dir_ext::AsyncDirExtUtf8; #[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))] pub use dir_ext::DirExtUtf8; pub use dir_ext::{AccessType, DirExt, SystemTimeSpec}; diff --git a/cap-primitives/Cargo.toml b/cap-primitives/Cargo.toml index bcb30b88..06082d50 100644 --- a/cap-primitives/Cargo.toml +++ b/cap-primitives/Cargo.toml @@ -18,7 +18,7 @@ arbitrary = { version = "1.0.0", optional = true, features = ["derive"] } ipnet = "2.5.0" maybe-owned = "0.3.4" fs-set-times = "0.20.0" -io-extras = "0.18.0" +io-extras = "0.18.3" io-lifetimes = { version = "2.0.0", default-features = false } [dev-dependencies] diff --git a/cap-std/Cargo.toml b/cap-std/Cargo.toml index 9592cef7..402718df 100644 --- a/cap-std/Cargo.toml +++ b/cap-std/Cargo.toml @@ -19,7 +19,7 @@ rustdoc-args = ["--cfg=docsrs"] [dependencies] arf-strings = { version = "0.7.0", optional = true } cap-primitives = { path = "../cap-primitives", version = "^3.3.0" } -io-extras = "0.18.0" +io-extras = "0.18.3" io-lifetimes = { version = "2.0.0", default-features = false } camino = { version = "1.0.5", optional = true } diff --git a/examples/async_std_fs_misc.rs b/examples/async_std_fs_misc.rs index 4d50517b..cfd9a5c0 100644 --- a/examples/async_std_fs_misc.rs +++ b/examples/async_std_fs_misc.rs @@ -1,7 +1,6 @@ // Copied from https://doc.rust-lang.org/rust-by-example/std_misc/fs.html and // adapted to use this crate instead. -/* use async_std::io; use async_std::io::prelude::*; use cap_async_std::ambient_authority; @@ -112,7 +111,3 @@ async fn main() { println!("! {:?}", why.kind()); }); } -*/ -fn main() { - eprintln!("async-std doesn't have io_safety traits implemented yet"); -}