diff --git a/Cargo.toml b/Cargo.toml index fd230dfda..3ae5d614f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.63" [dependencies] bitflags = { version = "2.4.0", default-features = false } -itoa = { version = "1.0.1", default-features = false, optional = true } +itoa = { version = "1.0.13", default-features = false, optional = true } # Special dependencies used in rustc-dep-of-std mode. core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } @@ -37,7 +37,7 @@ once_cell = { version = "1.5.2", optional = true } # `RUSTFLAGS` or enabling the `use-libc` cargo feature. [target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies] linux-raw-sys = { version = "0.4.14", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] } -libc_errno = { package = "errno", version = "0.3.8", default-features = false, optional = true } +libc_errno = { package = "errno", version = "0.3.10", default-features = false, optional = true } libc = { version = "0.2.161", default-features = false, optional = true } # Dependencies for platforms where only libc is supported: @@ -45,7 +45,7 @@ libc = { version = "0.2.161", default-features = false, optional = true } # On all other Unix-family platforms, and under Miri, we always use the libc # backend, so enable its dependencies unconditionally. [target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] -libc_errno = { package = "errno", version = "0.3.8", default-features = false } +libc_errno = { package = "errno", version = "0.3.10", default-features = false } libc = { version = "0.2.161", default-features = false } # Additional dependencies for Linux with the libc backend: @@ -57,7 +57,7 @@ linux-raw-sys = { version = "0.4.14", default-features = false, features = ["gen # For the libc backend on Windows, use the Winsock API in windows-sys. [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.52.0" +version = ">=0.52, <=0.59" features = [ "Win32_Foundation", "Win32_Networking_WinSock", @@ -68,14 +68,14 @@ features = [ # For the libc backend on Windows, also use the errno crate, which has Windows # support. [target.'cfg(windows)'.dependencies.libc_errno] -version = "0.3.8" +version = "0.3.10" package = "errno" default-features = false [dev-dependencies] tempfile = "3.5.0" libc = "0.2.161" -libc_errno = { package = "errno", version = "0.3.8", default-features = false } +libc_errno = { package = "errno", version = "0.3.10", default-features = false } serial_test = "2.0.0" memoffset = "0.9.0" flate2 = "1.0" diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index ec140a327..10d44e6d3 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -94,7 +94,7 @@ pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _; // Gated under `_LARGEFILE_SOURCE` but automatically set by the kernel. // -#[cfg(target_os = "illumos")] +#[cfg(solarish)] pub(crate) const O_LARGEFILE: c_int = 0x2000; // TODO: This is new in Linux 6.11; remove when linux-raw-sys is updated. diff --git a/src/backend/libc/fs/dir.rs b/src/backend/libc/fs/dir.rs index 4b4676872..9865e13bb 100644 --- a/src/backend/libc/fs/dir.rs +++ b/src/backend/libc/fs/dir.rs @@ -227,11 +227,12 @@ impl Dir { } } -/// `Dir` implements `Send` but not `Sync`, because we use `readdir` which is -/// not guaranteed to be thread-safe. Users can wrap this in a `Mutex` if they -/// need `Sync`, which is effectively what'd need to do to implement `Sync` -/// ourselves. +/// `Dir` is `Send` and `Sync`, because even though it contains internal +/// state, all methods that modify the state require a `mut &self` and +/// can therefore not be called concurrently. Calling them from different +/// threads sequentially is fine. unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} impl Drop for Dir { #[inline] diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 2b09c5829..2d2f4c674 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -36,12 +36,7 @@ use crate::fs::AtFlags; target_os = "vita", )))] use crate::fs::FallocateFlags; -#[cfg(not(any( - target_os = "espidf", - target_os = "solaris", - target_os = "vita", - target_os = "wasi" -)))] +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] use crate::fs::FlockOperation; #[cfg(any(linux_kernel, target_os = "freebsd"))] use crate::fs::MemfdFlags; @@ -1257,7 +1252,6 @@ pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Resul target_os = "espidf", target_os = "fuchsia", target_os = "redox", - target_os = "solaris", target_os = "vita", target_os = "wasi" )))] diff --git a/src/backend/libc/fs/types.rs b/src/backend/libc/fs/types.rs index cac86994a..8d70615cc 100644 --- a/src/backend/libc/fs/types.rs +++ b/src/backend/libc/fs/types.rs @@ -334,7 +334,7 @@ bitflags! { /// Note that rustix and/or libc will automatically set this flag when appropriate on /// `open(2)` and friends, thus typical users do not need to care about it. /// It will may be reported in return of `fcntl_getfl`, though. - #[cfg(any(linux_kernel, target_os = "illumos"))] + #[cfg(any(linux_kernel, solarish))] const LARGEFILE = bitcast!(c::O_LARGEFILE); /// @@ -949,27 +949,49 @@ bitflags! { /// /// [`flock`]: crate::fs::flock /// [`fcntl_lock`]: crate::fs::fcntl_lock -#[cfg(not(any( - target_os = "espidf", - target_os = "solaris", - target_os = "vita", - target_os = "wasi" -)))] +// Solaris doesn't support `flock` and doesn't define `LOCK_SH` etc., but we +// reuse this `FlockOperation` enum for `fcntl_lock`, so on Solaris we use +// our own made-up integer values. +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u32)] pub enum FlockOperation { /// `LOCK_SH` + #[cfg(not(target_os = "solaris"))] LockShared = bitcast!(c::LOCK_SH), + /// `LOCK_SH` + #[cfg(target_os = "solaris")] + LockShared = bitcast!(1), /// `LOCK_EX` + #[cfg(not(target_os = "solaris"))] LockExclusive = bitcast!(c::LOCK_EX), + /// `LOCK_EX` + #[cfg(target_os = "solaris")] + LockExclusive = bitcast!(2), /// `LOCK_UN` + #[cfg(not(target_os = "solaris"))] Unlock = bitcast!(c::LOCK_UN), + /// `LOCK_UN` + #[cfg(target_os = "solaris")] + Unlock = bitcast!(8), /// `LOCK_SH | LOCK_NB` + #[cfg(not(target_os = "solaris"))] NonBlockingLockShared = bitcast!(c::LOCK_SH | c::LOCK_NB), + /// `LOCK_SH | LOCK_NB` + #[cfg(target_os = "solaris")] + NonBlockingLockShared = bitcast!(1 | 4), /// `LOCK_EX | LOCK_NB` + #[cfg(not(target_os = "solaris"))] NonBlockingLockExclusive = bitcast!(c::LOCK_EX | c::LOCK_NB), + /// `LOCK_EX | LOCK_NB` + #[cfg(target_os = "solaris")] + NonBlockingLockExclusive = bitcast!(2 | 4), /// `LOCK_UN | LOCK_NB` + #[cfg(not(target_os = "solaris"))] NonBlockingUnlock = bitcast!(c::LOCK_UN | c::LOCK_NB), + /// `LOCK_UN | LOCK_NB` + #[cfg(target_os = "solaris")] + NonBlockingUnlock = bitcast!(8 | 4), } /// `struct stat` for use with [`statat`] and [`fstat`]. diff --git a/src/fs/fcntl.rs b/src/fs/fcntl.rs index c93f9d32f..f5cd47ddf 100644 --- a/src/fs/fcntl.rs +++ b/src/fs/fcntl.rs @@ -8,7 +8,6 @@ target_os = "espidf", target_os = "fuchsia", target_os = "redox", - target_os = "solaris", target_os = "vita", target_os = "wasi" )))] @@ -102,7 +101,6 @@ pub fn fcntl_add_seals(fd: Fd, seals: SealFlags) -> io::Result<()> { target_os = "espidf", target_os = "fuchsia", target_os = "redox", - target_os = "solaris", target_os = "vita", target_os = "wasi" )))] diff --git a/src/net/types.rs b/src/net/types.rs index 853d6fbc9..bcda0f987 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -1809,8 +1809,11 @@ fn test_sizes() { #[cfg(linux_kernel)] assert_eq_size!(UCred, libc::ucred); + // Linux added fields to `xdp_umem_reg` so it's bigger now. + /* #[cfg(target_os = "linux")] assert_eq_size!(super::xdp::XdpUmemReg, c::xdp_umem_reg); + */ #[cfg(target_os = "linux")] assert_eq_size!(super::xdp::XdpOptions, c::xdp_options); #[cfg(target_os = "linux")] diff --git a/src/path/dec_int.rs b/src/path/dec_int.rs index 73907c5b2..944c5212e 100644 --- a/src/path/dec_int.rs +++ b/src/path/dec_int.rs @@ -8,7 +8,7 @@ use crate::backend::fd::{AsFd, AsRawFd}; use crate::ffi::CStr; -use core::fmt::Write; +use core::mem::{self, MaybeUninit}; use itoa::{Buffer, Integer}; #[cfg(all(feature = "std", unix))] use std::os::unix::ffi::OsStrExt; @@ -36,23 +36,36 @@ use {core::fmt, std::ffi::OsStr, std::path::Path}; /// ``` #[derive(Clone)] pub struct DecInt { - // 20 `u8`s is enough to hold the decimal ASCII representation of any - // `u64`, and we add one for a NUL terminator for `as_c_str`. - buf: [u8; 20 + 1], + // Enough to hold an {u,i}64 and NUL terminator. + buf: [MaybeUninit; u64::MAX_STR_LEN + 1], len: usize, } +const _: () = assert!(u64::MAX_STR_LEN == i64::MAX_STR_LEN); impl DecInt { /// Construct a new path component from an integer. #[inline] pub fn new(i: Int) -> Self { - let mut me = DecIntWriter(Self { - buf: [0; 20 + 1], - len: 0, + let mut buf = [MaybeUninit::uninit(); 21]; + + let mut str_buf = Buffer::new(); + let str_buf = str_buf.format(i); + assert!( + str_buf.len() < buf.len(), + "{str_buf}{} unsupported.", + core::any::type_name::() + ); + + buf[..str_buf.len()].copy_from_slice(unsafe { + // SAFETY: you can always go from init to uninit + mem::transmute::<&[u8], &[MaybeUninit]>(str_buf.as_bytes()) }); - let mut buf = Buffer::new(); - me.write_str(buf.format(i)).unwrap(); - me.0 + buf[str_buf.len()] = MaybeUninit::new(0); + + Self { + buf, + len: str_buf.len(), + } } /// Construct a new path component from a file descriptor. @@ -72,7 +85,7 @@ impl DecInt { /// Return the raw byte buffer as a `&CStr`. #[inline] pub fn as_c_str(&self) -> &CStr { - let bytes_with_nul = &self.buf[..=self.len]; + let bytes_with_nul = self.as_bytes_with_nul(); debug_assert!(CStr::from_bytes_with_nul(bytes_with_nul).is_ok()); // SAFETY: `self.buf` holds a single decimal ASCII representation and @@ -80,28 +93,19 @@ impl DecInt { unsafe { CStr::from_bytes_with_nul_unchecked(bytes_with_nul) } } - /// Return the raw byte buffer. + /// Return the raw byte buffer including the NUL byte. #[inline] - pub fn as_bytes(&self) -> &[u8] { - &self.buf[..self.len] + pub fn as_bytes_with_nul(&self) -> &[u8] { + let init = &self.buf[..=self.len]; + // SAFETY: we're guaranteed to have initialized len+1 bytes. + unsafe { mem::transmute::<&[MaybeUninit], &[u8]>(init) } } -} - -/// A wrapper around `DecInt` that implements `Write` without exposing this -/// implementation to `DecInt`'s public API. -struct DecIntWriter(DecInt); -impl core::fmt::Write for DecIntWriter { + /// Return the raw byte buffer. #[inline] - fn write_str(&mut self, s: &str) -> core::fmt::Result { - match self.0.buf.get_mut(self.0.len..self.0.len + s.len()) { - Some(slice) => { - slice.copy_from_slice(s.as_bytes()); - self.0.len += s.len(); - Ok(()) - } - None => Err(core::fmt::Error), - } + pub fn as_bytes(&self) -> &[u8] { + let bytes = self.as_bytes_with_nul(); + &bytes[..bytes.len() - 1] } } @@ -109,7 +113,7 @@ impl core::fmt::Write for DecIntWriter { impl AsRef for DecInt { #[inline] fn as_ref(&self) -> &Path { - let as_os_str: &OsStr = OsStrExt::from_bytes(&self.buf[..self.len]); + let as_os_str: &OsStr = OsStrExt::from_bytes(self.as_bytes()); Path::new(as_os_str) } } diff --git a/tests/fs/file.rs b/tests/fs/file.rs index 451443c1b..db080ecaf 100644 --- a/tests/fs/file.rs +++ b/tests/fs/file.rs @@ -114,7 +114,7 @@ fn test_file() { // Clear `O_LARGEFILE`, which may be set by rustix on 32-bit Linux or // automatically by some kernel on 64-bit (Linux and illumos). - #[cfg(any(linux_kernel, target_os = "illumos"))] + #[cfg(any(linux_kernel, solarish))] let fl = fl - rustix::fs::OFlags::LARGEFILE; assert_eq!(fl, rustix::fs::OFlags::empty()); diff --git a/tests/net/dgram.rs b/tests/net/dgram.rs index 906be8a4e..19021c5a2 100644 --- a/tests/net/dgram.rs +++ b/tests/net/dgram.rs @@ -345,7 +345,7 @@ fn net_dgram_v6_bind_any() { } /// Test `sendto` with calling `connect`, on platforms which support that. -#[cfg(not(any(bsd, target_os = "illumos")))] +#[cfg(not(any(bsd, solarish)))] #[test] fn net_dgram_v4_connect_sendto() { crate::init(); @@ -439,7 +439,7 @@ fn net_dgram_v4_sendto() { } /// Similar, but with V6. -#[cfg(not(any(bsd, target_os = "illumos")))] +#[cfg(not(any(bsd, solarish)))] #[test] fn net_dgram_v6_connect_sendto() { crate::init(); @@ -533,7 +533,7 @@ fn net_dgram_v6_sendto() { } /// Test `sendto_any` with calling connect, on platforms which support that. -#[cfg(not(any(bsd, target_os = "illumos")))] +#[cfg(not(any(bsd, solarish)))] #[test] fn net_dgram_v4_connect_sendto_any() { crate::init(); @@ -621,7 +621,7 @@ fn net_dgram_v4_sendto_any() { } /// Similar, but with V6. -#[cfg(not(any(bsd, target_os = "illumos")))] +#[cfg(not(any(bsd, solarish)))] #[test] fn net_dgram_v6_connect_sendto_any() { crate::init(); diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index b98a6b4cd..5690634b5 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -312,9 +312,9 @@ fn test_sockopts_ipv4() { assert_eq!(sockopt::get_socket_domain(&s).unwrap(), AddressFamily::INET); assert_ne!(sockopt::get_ip_ttl(&s).unwrap(), 0); assert_ne!(sockopt::get_ip_ttl(&s).unwrap(), 77); - #[cfg(not(any(bsd, windows, target_os = "illumos")))] + #[cfg(not(any(bsd, windows, solarish)))] assert!(sockopt::get_ip_multicast_loop(&s).unwrap()); - #[cfg(not(any(bsd, windows, target_os = "illumos")))] + #[cfg(not(any(bsd, windows, solarish)))] assert_eq!(sockopt::get_ip_multicast_ttl(&s).unwrap(), 1); // Set the ip ttl. @@ -323,7 +323,7 @@ fn test_sockopts_ipv4() { // Check the ip ttl. assert_eq!(sockopt::get_ip_ttl(&s).unwrap(), 77); - #[cfg(not(any(bsd, windows, target_os = "illumos")))] + #[cfg(not(any(bsd, windows, solarish)))] { // Set the multicast loop flag; sockopt::set_ip_multicast_loop(&s, false).unwrap(); diff --git a/tests/net/unix.rs b/tests/net/unix.rs index acfacdc0d..27d0ff1d0 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -220,7 +220,7 @@ fn do_test_unix_msg(addr: SocketAddrUnix) { // Don't ask me why, but this was seen to fail on FreeBSD. // `SocketAddrUnix::path()` returned `None` for some reason. // illumos and NetBSD too. - #[cfg(not(any(target_os = "freebsd", target_os = "illumos", target_os = "netbsd")))] + #[cfg(not(any(solarish, target_os = "freebsd", target_os = "netbsd")))] assert_eq!( Some(rustix::net::SocketAddrAny::Unix(addr.clone())), result.address diff --git a/tests/net/unix_alloc.rs b/tests/net/unix_alloc.rs index 5ed2b4b28..aedc9b736 100644 --- a/tests/net/unix_alloc.rs +++ b/tests/net/unix_alloc.rs @@ -218,7 +218,7 @@ fn do_test_unix_msg(addr: SocketAddrUnix) { // Don't ask me why, but this was seen to fail on FreeBSD. // `SocketAddrUnix::path()` returned `None` for some reason. // illumos and NetBSD too. - #[cfg(not(any(target_os = "freebsd", target_os = "illumos", target_os = "netbsd")))] + #[cfg(not(any(solarish, target_os = "freebsd", target_os = "netbsd")))] assert_eq!( Some(rustix::net::SocketAddrAny::Unix(addr.clone())), result.address diff --git a/tests/path/dec_int.rs b/tests/path/dec_int.rs index 6ce1a5123..80f95126f 100644 --- a/tests/path/dec_int.rs +++ b/tests/path/dec_int.rs @@ -1,20 +1,45 @@ use rustix::path::DecInt; +macro_rules! check { + ($i:expr) => { + let i = $i; + assert_eq!(DecInt::new(i).as_ref().to_str().unwrap(), i.to_string()); + }; +} + #[test] fn test_dec_int() { - assert_eq!(DecInt::new(0).as_ref().to_str().unwrap(), "0"); - assert_eq!(DecInt::new(-1).as_ref().to_str().unwrap(), "-1"); - assert_eq!(DecInt::new(789).as_ref().to_str().unwrap(), "789"); - assert_eq!( - DecInt::new(i64::MIN).as_ref().to_str().unwrap(), - i64::MIN.to_string() - ); - assert_eq!( - DecInt::new(i64::MAX).as_ref().to_str().unwrap(), - i64::MAX.to_string() - ); - assert_eq!( - DecInt::new(u64::MAX).as_ref().to_str().unwrap(), - u64::MAX.to_string() - ); + check!(0); + check!(-1); + check!(789); + + check!(u8::MAX); + check!(i8::MIN); + check!(u16::MAX); + check!(i16::MIN); + check!(u32::MAX); + check!(i32::MIN); + check!(u64::MAX); + check!(i64::MIN); + #[cfg(any( + target_pointer_width = "16", + target_pointer_width = "32", + target_pointer_width = "64" + ))] + { + check!(usize::MAX); + check!(isize::MIN); + } +} + +#[test] +#[should_panic] +fn test_unsupported_max_u128_dec_int() { + check!(u128::MAX); +} + +#[test] +#[should_panic] +fn test_unsupported_min_u128_dec_int() { + check!(i128::MIN); }