From f90075862346c78b4c0c373e48fb8929168d277e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Sep 2025 14:47:21 -0700 Subject: std: Fix WASI implementation of `remove_dir_all` This commit is a change to the WASI-specific implementation of the `std::fs::remove_dir_all` function. Specifically it changes how directory entries are read of a directory-being-deleted to specifically buffer them all into a `Vec` before actually proceeding to delete anything. This is necessary to fix an interaction with how the WASIp1 `fd_readdir` API works to have everything work out in the face of mutations while reading a directory. The basic problem is that `fd_readdir`, the WASIp1 API for reading directories, is not a stateful read of a directory but instead a "seekable" read of a directory. Its `cookie` argument enables seeking anywhere within the directory at any time to read further entries. Native host implementations do not have this ability, however, which means that this seeking property must be achieved by re-reading the directory. The problem with this is that WASIp1 has under-specified semantics around what should happen if a directory is mutated between two calls to `fd_readdir`. In essence there's not really any possible implementation in hosts except to read the entire directory and support seeking through the already-read list. This implementation is not possible in the WASIp1-to-WASIp2 adapter that is primarily used to create components for the `wasm32-wasip2` target where it has constrained memory requirements and can't buffer up arbitrarily sized directories. The WASIp1 API definitions are effectively "dead" now at the standards level meaning that `fd_readdir` won't be changing nor will a replacement be coming. For the `wasm32-wasip2` target this will get fixed once filesystem APIs are updated to use WASIp2 directly instead of WASIp1, making this buffering unnecessary. In essence while this is a hack it's sort of the least invasive thing that works everywhere for now. I don't think this is viable to fix in hosts so guests compiled to wasm are going to have to work around it by not relying on any guarantees about what happens to a directory if it's mutated between reads. --- library/std/src/sys/fs/wasi.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'library/std/src') diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index b65d86de12a..0b65b9cb389 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -848,7 +848,14 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { // Iterate over all the entries in this directory, and travel recursively if // necessary - for entry in ReadDir::new(fd, dummy_root) { + // + // 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") -- cgit 1.4.1-3-g733a5 From 09d3120a99fee8d839e502bdb54f8117c0623c5d Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 14 Sep 2025 15:30:44 +0200 Subject: std: simplify host lookup --- library/std/src/net/socket_addr.rs | 41 ++++++++------- library/std/src/sys/net/connection/sgx.rs | 29 ++--------- library/std/src/sys/net/connection/socket/mod.rs | 58 ++++++---------------- library/std/src/sys/net/connection/socket/tests.rs | 2 +- library/std/src/sys/net/connection/uefi/mod.rs | 22 +------- library/std/src/sys/net/connection/unsupported.rs | 22 +------- library/std/src/sys/net/connection/wasip1.rs | 22 +------- library/std/src/sys/net/connection/xous/dns.rs | 45 +---------------- library/std/src/sys/net/connection/xous/mod.rs | 2 +- tests/ui/inference/issue-72616.rs | 1 - tests/ui/inference/issue-72616.stderr | 19 +------ 11 files changed, 51 insertions(+), 212 deletions(-) (limited to 'library/std/src') diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 41e623e79ce..5b56dd3f744 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,7 +6,6 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::LookupHost; use crate::{io, iter, option, slice, vec}; /// A trait for objects which can be converted or resolved to one or more @@ -188,15 +187,9 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { } } -fn resolve_socket_addr(lh: LookupHost) -> io::Result> { - let p = lh.port(); - let v: Vec<_> = lh - .map(|mut a| { - a.set_port(p); - a - }) - .collect(); - Ok(v.into_iter()) +fn lookup_host(host: &str, port: u16) -> io::Result> { + let addrs = crate::sys::net::lookup_host(host, port)?; + Ok(Vec::from_iter(addrs).into_iter()) } #[stable(feature = "rust1", since = "1.0.0")] @@ -205,17 +198,14 @@ impl ToSocketAddrs for (&str, u16) { fn to_socket_addrs(&self) -> io::Result> { let (host, port) = *self; - // try to parse the host as a regular IP address first - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV4::new(addr, port); - return Ok(vec![SocketAddr::V4(addr)].into_iter()); - } - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV6::new(addr, port, 0, 0); - return Ok(vec![SocketAddr::V6(addr)].into_iter()); + // Try to parse the host as a regular IP address first + if let Ok(addr) = host.parse::() { + let addr = SocketAddr::new(addr, port); + return Ok(vec![addr].into_iter()); } - resolve_socket_addr((host, port).try_into()?) + // Otherwise, make the system look it up. + lookup_host(host, port) } } @@ -232,12 +222,21 @@ impl ToSocketAddrs for (String, u16) { impl ToSocketAddrs for str { type Iter = vec::IntoIter; fn to_socket_addrs(&self) -> io::Result> { - // try to parse as a regular SocketAddr first + // Try to parse as a regular SocketAddr first if let Ok(addr) = self.parse() { return Ok(vec![addr].into_iter()); } - resolve_socket_addr(self.try_into()?) + // Otherwise, split the string by ':' and convert the second part to u16... + let Some((host, port_str)) = self.rsplit_once(':') else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); + }; + let Ok(port) = port_str.parse::() else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); + }; + + // ... and make the system look up the host. + lookup_host(host, port) } } diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 9b54571997d..8c9c17d3f17 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -499,16 +499,6 @@ impl fmt::Display for NonIpSockAddr { pub struct LookupHost(!); -impl LookupHost { - fn new(host: String) -> io::Result { - Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host })) - } - - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -516,18 +506,9 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(v: &str) -> io::Result { - LookupHost::new(v.to_owned()) - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - LookupHost::new(format!("{host}:{port}")) - } +pub fn lookup_host(host: &str, port: u16) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Uncategorized, + NonIpSockAddr { host: format!("{host}:{port}") }, + )) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 564f2e3a01f..1dd06e97bba 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -258,7 +258,7 @@ fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint { } //////////////////////////////////////////////////////////////////////////////// -// get_host_addresses +// lookup_host //////////////////////////////////////////////////////////////////////////////// pub struct LookupHost { @@ -267,12 +267,6 @@ pub struct LookupHost { port: u16, } -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -281,7 +275,10 @@ impl Iterator for LookupHost { let cur = self.cur.as_ref()?; self.cur = cur.ai_next; match socket_addr_from_c(cur.ai_addr.cast(), cur.ai_addrlen as usize) { - Ok(addr) => return Some(addr), + Ok(mut addr) => { + addr.set_port(self.port); + return Some(addr); + } Err(_) => continue, } } @@ -298,42 +295,17 @@ impl Drop for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), - } - }; +pub fn lookup_host(host: &str, port: u16) -> io::Result { + init(); + run_with_cstr(host.as_bytes(), &|c_host| { + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) + .map(|_| LookupHost { original: res, cur: res, port }) } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - init(); - - run_with_cstr(host.as_bytes(), &|c_host| { - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) - .map(|_| LookupHost { original: res, cur: res, port }) - } - }) - } + }) } //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/net/connection/socket/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs index fc236b8027b..049355afca7 100644 --- a/library/std/src/sys/net/connection/socket/tests.rs +++ b/library/std/src/sys/net/connection/socket/tests.rs @@ -4,7 +4,7 @@ use crate::collections::HashMap; #[test] fn no_lookup_host_duplicates() { let mut addrs = HashMap::new(); - let lh = match LookupHost::try_from(("localhost", 0)) { + let lh = match lookup_host("localhost", 0) { Ok(lh) => lh, Err(e) => panic!("couldn't resolve `localhost`: {e}"), }; diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 00368042873..004f6d413a1 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -333,12 +333,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -346,18 +340,6 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index fbc86343272..fb18e8dec55 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -304,12 +304,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -317,18 +311,6 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index cdfa25c8a44..048dafdcd7f 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -477,12 +477,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -490,18 +484,6 @@ impl Iterator for LookupHost { } } -impl<'a> TryFrom<&'a str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &'a str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/xous/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index bb29d211fad..b139376f597 100644 --- a/library/std/src/sys/net/connection/xous/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -1,15 +1,8 @@ -use core::convert::{TryFrom, TryInto}; - use crate::io; use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::os::xous::ffi::lend_mut; use crate::os::xous::services::{DnsLendMut, dns_server}; -pub struct DnsError { - #[allow(dead_code)] - pub code: u8, -} - #[repr(C, align(4096))] struct LookupHostQuery([u8; 4096]); @@ -20,12 +13,6 @@ pub struct LookupHost { count: usize, } -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -72,7 +59,7 @@ impl Iterator for LookupHost { } } -pub fn lookup(query: &str, port: u16) -> Result { +pub fn lookup_host(query: &str, port: u16) -> io::Result { let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port }; // Copy the query into the message that gets sent to the DNS server @@ -89,7 +76,7 @@ pub fn lookup(query: &str, port: u16) -> Result { ) .unwrap(); if result.data.0[0] != 0 { - return Err(DnsError { code: result.data.0[1] }); + return Err(io::const_error!(io::ErrorKind::InvalidInput, "DNS failure")); } assert_eq!(result.offset, 0); result.count = result.data.0[1] as usize; @@ -98,31 +85,3 @@ pub fn lookup(query: &str, port: u16) -> Result { result.offset = 2; Ok(result) } - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, &$msg)), - } - }; - } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl TryFrom<(&str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(v: (&str, u16)) -> io::Result { - lookup(v.0, v.1).map_err(|_e| io::const_error!(io::ErrorKind::InvalidInput, "DNS failure")) - } -} diff --git a/library/std/src/sys/net/connection/xous/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs index e44a375b9e3..0f77be5c3fa 100644 --- a/library/std/src/sys/net/connection/xous/mod.rs +++ b/library/std/src/sys/net/connection/xous/mod.rs @@ -45,4 +45,4 @@ pub struct GetAddress { raw: [u8; 4096], } -pub use dns::LookupHost; +pub use dns::lookup_host; diff --git a/tests/ui/inference/issue-72616.rs b/tests/ui/inference/issue-72616.rs index 71e095cc6fc..ba18575a571 100644 --- a/tests/ui/inference/issue-72616.rs +++ b/tests/ui/inference/issue-72616.rs @@ -21,7 +21,6 @@ pub fn main() { { if String::from("a") == "a".try_into().unwrap() {} //~^ ERROR type annotations needed - //~| ERROR type annotations needed } { let _: String = match "_".try_into() { diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index a271639996f..6b47d568811 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -16,23 +16,6 @@ LL - if String::from("a") == "a".try_into().unwrap() {} LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} | -error[E0283]: type annotations needed - --> $DIR/issue-72616.rs:22:37 - | -LL | if String::from("a") == "a".try_into().unwrap() {} - | ^^^^^^^^ - | - = note: multiple `impl`s satisfying `_: TryFrom<&str>` found in the following crates: `core`, `std`: - - impl TryFrom<&str> for std::sys::net::connection::socket::LookupHost; - - impl TryFrom for T - where U: Into; - = note: required for `&str` to implement `TryInto<_>` -help: try using a fully qualified path to specify the expected types - | -LL - if String::from("a") == "a".try_into().unwrap() {} -LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} - | - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0283`. -- cgit 1.4.1-3-g733a5