Skip to content

Commit ff5664d

Browse files
committed
Auto merge of rust-lang#116683 - ChrisDenton:exists, r=Mark-Simulacrum
Make `try_exists` return `Ok(true)` for Windows Unix Sockets This is a follow up to rust-lang#109106 but for[ `fs::try_exists`](https://doc.rust-lang.org/std/fs/fn.try_exists.html), which doesn't need to get the metadata of a file (which can fail even if a file exists). `fs::try_exists` currently fails on Windows if encountering a Unix Domain Socket (UDS). This PR fixes it by checking for an error code that's returned when there's a failure to use a reparse point. ## Reparse points A reparse point is a way to invoke a filesystem filter on a file instead of the file being opened normally. This is used to implement symbolic links (by redirecting to a different path) but also to implement other types of special files such as Unix domain sockets. If the reparse point is not a link type then opening it with `CreateFileW` may fail with `ERROR_CANT_ACCESS_FILE` because the filesystem filter does not implement that operation. This differs from resolving links which may fail with errors such as `ERROR_FILE_NOT_FOUND` or `ERROR_CANT_RESOLVE_FILENAME`. So `ERROR_CANT_ACCESS_FILE` means that the file exists but that we can't open it normally. Still, the file does exist on the filesystem so `try_exists` should report that as `Ok(true)`. r? libs
2 parents dda7d4c + 2f5dea0 commit ff5664d

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

Diff for: library/std/src/fs/tests.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1707,3 +1707,38 @@ fn test_file_times() {
17071707
assert_eq!(metadata.created().unwrap(), created);
17081708
}
17091709
}
1710+
1711+
#[test]
1712+
#[cfg(windows)]
1713+
fn windows_unix_socket_exists() {
1714+
use crate::sys::{c, net};
1715+
use crate::{mem, ptr};
1716+
1717+
let tmp = tmpdir();
1718+
let socket_path = tmp.join("socket");
1719+
1720+
// std doesn't current support Unix sockets on Windows so manually create one here.
1721+
net::init();
1722+
unsafe {
1723+
let socket = c::WSASocketW(
1724+
c::AF_UNIX as i32,
1725+
c::SOCK_STREAM,
1726+
0,
1727+
ptr::null_mut(),
1728+
0,
1729+
c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT,
1730+
);
1731+
assert_ne!(socket, c::INVALID_SOCKET);
1732+
let mut addr = c::SOCKADDR_UN { sun_family: c::AF_UNIX, sun_path: mem::zeroed() };
1733+
let bytes = socket_path.as_os_str().as_encoded_bytes();
1734+
addr.sun_path[..bytes.len()].copy_from_slice(bytes);
1735+
let len = mem::size_of_val(&addr) as i32;
1736+
let result = c::bind(socket, ptr::addr_of!(addr).cast::<c::SOCKADDR>(), len);
1737+
c::closesocket(socket);
1738+
assert_eq!(result, 0);
1739+
}
1740+
// Make sure all ways of testing a file exist work for a Unix socket.
1741+
assert_eq!(socket_path.exists(), true);
1742+
assert_eq!(socket_path.try_exists().unwrap(), true);
1743+
assert_eq!(socket_path.metadata().is_ok(), true);
1744+
}

Diff for: library/std/src/sys/windows/c/windows_sys.lst

+2
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,7 @@ Windows.Win32.Networking.WinSock.ADDRESS_FAMILY
19641964
Windows.Win32.Networking.WinSock.ADDRINFOA
19651965
Windows.Win32.Networking.WinSock.AF_INET
19661966
Windows.Win32.Networking.WinSock.AF_INET6
1967+
Windows.Win32.Networking.WinSock.AF_UNIX
19671968
Windows.Win32.Networking.WinSock.AF_UNSPEC
19681969
Windows.Win32.Networking.WinSock.bind
19691970
Windows.Win32.Networking.WinSock.closesocket
@@ -2058,6 +2059,7 @@ Windows.Win32.Networking.WinSock.SOCK_RDM
20582059
Windows.Win32.Networking.WinSock.SOCK_SEQPACKET
20592060
Windows.Win32.Networking.WinSock.SOCK_STREAM
20602061
Windows.Win32.Networking.WinSock.SOCKADDR
2062+
Windows.Win32.Networking.WinSock.SOCKADDR_UN
20612063
Windows.Win32.Networking.WinSock.SOCKET
20622064
Windows.Win32.Networking.WinSock.SOCKET_ERROR
20632065
Windows.Win32.Networking.WinSock.SOL_SOCKET

Diff for: library/std/src/sys/windows/c/windows_sys.rs

+12
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@ impl ::core::clone::Clone for ADDRINFOA {
847847
}
848848
pub const AF_INET: ADDRESS_FAMILY = 2u16;
849849
pub const AF_INET6: ADDRESS_FAMILY = 23u16;
850+
pub const AF_UNIX: u16 = 1u16;
850851
pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16;
851852
pub const ALL_PROCESSOR_GROUPS: u32 = 65535u32;
852853
#[repr(C)]
@@ -3813,6 +3814,17 @@ impl ::core::clone::Clone for SOCKADDR {
38133814
*self
38143815
}
38153816
}
3817+
#[repr(C)]
3818+
pub struct SOCKADDR_UN {
3819+
pub sun_family: ADDRESS_FAMILY,
3820+
pub sun_path: [u8; 108],
3821+
}
3822+
impl ::core::marker::Copy for SOCKADDR_UN {}
3823+
impl ::core::clone::Clone for SOCKADDR_UN {
3824+
fn clone(&self) -> Self {
3825+
*self
3826+
}
3827+
}
38163828
pub type SOCKET = usize;
38173829
pub const SOCKET_ERROR: i32 = -1i32;
38183830
pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32;

Diff for: library/std/src/sys/windows/fs.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,13 @@ pub fn try_exists(path: &Path) -> io::Result<bool> {
15151515
// as the file existing.
15161516
_ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),
15171517

1518+
// `ERROR_CANT_ACCESS_FILE` means that a file exists but that the
1519+
// reparse point could not be handled by `CreateFile`.
1520+
// This can happen for special files such as:
1521+
// * Unix domain sockets which you need to `connect` to
1522+
// * App exec links which require using `CreateProcess`
1523+
_ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true),
1524+
15181525
// Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
15191526
// file exists. However, these types of errors are usually more
15201527
// permanent so we report them here.

0 commit comments

Comments
 (0)