From 07a80aa789c8eb2431f81665c9d5d72f82a27e43 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 1 May 2024 08:27:15 -0700 Subject: [PATCH 01/54] Deprecate `rustix::net::SendFlags::EOT` and add `EOR`. (#1054) * Deprecate `rustix::net::SendFlags::EOT` and add `EOR`. In `rustix::net::SendFlags`, deprecate the mis-spelled `EOT` and add the correctly-spelled `EOT`. Fixes #1053. * Fix the libc backend too. --- src/backend/libc/net/send_recv.rs | 6 +++++- src/backend/linux_raw/net/send_recv.rs | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index 5dc60ddcd..c41122b08 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -27,9 +27,13 @@ bitflags! { /// `MSG_DONTWAIT` #[cfg(not(windows))] const DONTWAIT = bitcast!(c::MSG_DONTWAIT); - /// `MSG_EOR` + /// Deprecated alias for [`EOR`]. #[cfg(not(windows))] + #[deprecated(note = "`rustix::net::SendFlags::EOT` is renamed to `rustix::net::SendFlags::EOR`.")] const EOT = bitcast!(c::MSG_EOR); + /// `MSG_EOR` + #[cfg(not(windows))] + const EOR = bitcast!(c::MSG_EOR); /// `MSG_MORE` #[cfg(not(any( bsd, diff --git a/src/backend/linux_raw/net/send_recv.rs b/src/backend/linux_raw/net/send_recv.rs index d5cdd075e..de3c287c2 100644 --- a/src/backend/linux_raw/net/send_recv.rs +++ b/src/backend/linux_raw/net/send_recv.rs @@ -16,8 +16,11 @@ bitflags! { const DONTROUTE = c::MSG_DONTROUTE; /// `MSG_DONTWAIT` const DONTWAIT = c::MSG_DONTWAIT; - /// `MSG_EOT` + /// Deprecated alias for [`EOR`]. + #[deprecated(note = "`rustix::net::SendFlags::EOT` is renamed to `rustix::net::SendFlags::EOR`.")] const EOT = c::MSG_EOR; + /// `MSG_EOR` + const EOR = c::MSG_EOR; /// `MSG_MORE` const MORE = c::MSG_MORE; /// `MSG_NOSIGNAL` From b26eca9e8476f997137a371af1a5ede1b7c1edbe Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 20 May 2024 17:56:53 -0700 Subject: [PATCH 02/54] Fix `procfs`'s `open_and_check_file` to work in the presence of threads. (#1059) * Fix `procfs`'s `open_and_check_file` to work in the presence of threads. Have `open_and_check_file` create a new file descriptor before scanning for directory entries, so that it doesn't share a directory position with other threads. Fixes #1050. * Fix build errors on powerpc64-ibm-aix. --- src/backend/libc/conv.rs | 2 +- src/backend/libc/net/read_sockaddr.rs | 4 +++- src/procfs.rs | 15 +++++++++++++-- tests/procfs/basic.rs | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/backend/libc/conv.rs b/src/backend/libc/conv.rs index 171fc10d8..cc0b6d61a 100644 --- a/src/backend/libc/conv.rs +++ b/src/backend/libc/conv.rs @@ -13,7 +13,7 @@ use crate::io; #[cfg(not(windows))] #[inline] pub(super) fn c_str(c: &CStr) -> *const c::c_char { - c.as_ptr() + c.as_ptr().cast() } #[cfg(not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")))] diff --git a/src/backend/libc/net/read_sockaddr.rs b/src/backend/libc/net/read_sockaddr.rs index 08939d4f8..eebd46342 100644 --- a/src/backend/libc/net/read_sockaddr.rs +++ b/src/backend/libc/net/read_sockaddr.rs @@ -185,7 +185,9 @@ pub(crate) unsafe fn read_sockaddr( return Err(io::Errno::INVAL); } debug_assert_eq!( - CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(), + CStr::from_ptr(decode.sun_path.as_ptr().cast()) + .to_bytes() + .len(), provided_len ); &decode.sun_path[..provided_len] diff --git a/src/procfs.rs b/src/procfs.rs index 60a4b6c34..e275cdcb5 100644 --- a/src/procfs.rs +++ b/src/procfs.rs @@ -228,7 +228,8 @@ fn is_mountpoint(file: BorrowedFd<'_>) -> bool { fn proc_opendirat(dirfd: Fd, path: P) -> io::Result { // We don't add `PATH` here because that disables `DIRECTORY`. And we don't // add `NOATIME` for the same reason as the comment in `open_and_check_file`. - let oflags = OFlags::NOFOLLOW | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOCTTY; + let oflags = + OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOCTTY; openat(dirfd, path, oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP) } @@ -488,8 +489,18 @@ fn open_and_check_file( let mut found_file = false; let mut found_dot = false; + // Open a new fd, so that if we're called on multiple threads, they don't + // share a seek position. + let oflags = + OFlags::RDONLY | OFlags::CLOEXEC | OFlags::NOFOLLOW | OFlags::NOCTTY | OFlags::DIRECTORY; + let dir = openat(dir, cstr!("."), oflags, Mode::empty()).map_err(|_err| io::Errno::NOTSUP)?; + let check_dir_stat = fstat(&dir)?; + if check_dir_stat.st_dev != dir_stat.st_dev || check_dir_stat.st_ino != dir_stat.st_ino { + return Err(io::Errno::NOTSUP); + } + // Position the directory iteration at the start. - seek(dir, SeekFrom::Start(0))?; + seek(&dir, SeekFrom::Start(0))?; let mut buf = [MaybeUninit::uninit(); 2048]; let mut iter = RawDir::new(dir, &mut buf); diff --git a/tests/procfs/basic.rs b/tests/procfs/basic.rs index 6ceaa41a8..1512ffe96 100644 --- a/tests/procfs/basic.rs +++ b/tests/procfs/basic.rs @@ -14,3 +14,20 @@ fn test_status_twice() { let fd = rustix::procfs::proc_self_status().unwrap(); drop(fd); } + +#[test] +fn parallel_self_proc_status() { + const THREADS: usize = 3; + + fn self_proc_status() { + rustix::procfs::proc_self_status().expect("error getting proc/self/status pid"); + } + + let mut handles = Vec::with_capacity(THREADS); + for _ in 0..THREADS { + handles.push(std::thread::spawn(self_proc_status)); + } + for handle in handles.drain(..) { + handle.join().expect("thread crashed"); + } +} From 3346526711e885611c770ad09764b8ff8d2924ce Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 28 May 2024 18:20:53 +0200 Subject: [PATCH 03/54] Fix hurd build (#1064) * hurd: Fix build 6ec74e03c8ba ("Fix handling of negative timestamps in `Stat`. (#999)") broke the hurd build, where we have a st_[acm]tim timespec instead of st_[acm], like on aix and nto. * aix, nto, hurd: Implement impl StatExt --- src/fs/mod.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/fs/mod.rs b/src/fs/mod.rs index a4282088e..110cf79b8 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -150,7 +150,7 @@ pub use std::os::wasi::fs::{DirEntryExt, FileExt, FileTypeExt, MetadataExt, Open /// the Unix epoch. Until the next semver bump, these unsigned fields are /// deprecated, and this trait provides accessors which return their values /// as signed integers. -#[cfg(all(unix, not(any(target_os = "aix", target_os = "nto"))))] +#[cfg(all(unix))] pub trait StatExt { /// Return the value of the `st_atime` field, casted to the correct type. fn atime(&self) -> i64; @@ -160,7 +160,10 @@ pub trait StatExt { fn ctime(&self) -> i64; } -#[cfg(all(unix, not(any(target_os = "aix", target_os = "nto"))))] +#[cfg(all( + unix, + not(any(target_os = "aix", target_os = "hurd", target_os = "nto")) +))] #[allow(deprecated)] impl StatExt for Stat { #[inline] @@ -178,3 +181,22 @@ impl StatExt for Stat { self.st_ctime as i64 } } + +#[cfg(any(target_os = "aix", target_os = "hurd", target_os = "nto"))] +#[allow(deprecated)] +impl StatExt for Stat { + #[inline] + fn atime(&self) -> i64 { + self.st_atim.tv_sec as i64 + } + + #[inline] + fn mtime(&self) -> i64 { + self.st_mtim.tv_sec as i64 + } + + #[inline] + fn ctime(&self) -> i64 { + self.st_ctim.tv_sec as i64 + } +} From c2046050a56e05f91aa289f6d3984fe7f8d4bdca Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 11 Jun 2024 18:47:44 +0200 Subject: [PATCH 04/54] Add more hurd support (#1066) Now tested with all features. --- src/backend/libc/conv.rs | 2 ++ src/backend/libc/fs/syscalls.rs | 26 ++++++++++++------------- src/backend/libc/mm/types.rs | 19 +++++++++++++++++- src/backend/libc/net/addr.rs | 18 +++++++++++++++-- src/backend/libc/net/ext.rs | 2 ++ src/backend/libc/net/read_sockaddr.rs | 3 +++ src/backend/libc/net/send_recv.rs | 3 +++ src/backend/libc/net/sockopt.rs | 1 + src/backend/libc/net/write_sockaddr.rs | 3 +++ src/backend/libc/pipe/types.rs | 1 + src/fs/mod.rs | 4 ++-- src/net/sockopt.rs | 1 + src/net/types.rs | 27 ++++++++++++++++++++++++++ src/path/arg.rs | 4 ++-- src/signal.rs | 6 ++++-- 15 files changed, 98 insertions(+), 22 deletions(-) diff --git a/src/backend/libc/conv.rs b/src/backend/libc/conv.rs index cc0b6d61a..5052b010f 100644 --- a/src/backend/libc/conv.rs +++ b/src/backend/libc/conv.rs @@ -226,6 +226,7 @@ pub(crate) fn msg_iov_len(len: usize) -> c::c_int { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", ))] #[inline] @@ -244,6 +245,7 @@ pub(crate) fn msg_control_len(len: usize) -> c::socklen_t { target_os = "espidf", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", target_os = "vita", diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index b87f1ccde..af615ff74 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -80,7 +80,7 @@ use { crate::backend::conv::nonnegative_ret, crate::fs::{copyfile_state_t, CloneFlags, CopyfileFlags}, }; -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] use {crate::fs::XattrFlags, core::mem::size_of, core::ptr::null_mut}; #[cfg(linux_kernel)] use { @@ -2239,7 +2239,7 @@ struct Attrlist { forkattr: Attrgroup, } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { let value_ptr = value.as_mut_ptr(); @@ -2275,7 +2275,7 @@ pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result { let value_ptr = value.as_mut_ptr(); @@ -2312,7 +2312,7 @@ pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Resul } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result { let value_ptr = value.as_mut_ptr(); @@ -2348,7 +2348,7 @@ pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn setxattr( path: &CStr, name: &CStr, @@ -2379,7 +2379,7 @@ pub(crate) fn setxattr( } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn lsetxattr( path: &CStr, name: &CStr, @@ -2410,7 +2410,7 @@ pub(crate) fn lsetxattr( } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn fsetxattr( fd: BorrowedFd<'_>, name: &CStr, @@ -2441,7 +2441,7 @@ pub(crate) fn fsetxattr( } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result { #[cfg(not(apple))] unsafe { @@ -2459,7 +2459,7 @@ pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result io::Result { #[cfg(not(apple))] unsafe { @@ -2477,7 +2477,7 @@ pub(crate) fn llistxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result, list: &mut [c::c_char]) -> io::Result { let fd = borrowed_fd(fd); @@ -2492,7 +2492,7 @@ pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [c::c_char]) -> io::Resu } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { #[cfg(not(apple))] unsafe { @@ -2505,7 +2505,7 @@ pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { #[cfg(not(apple))] unsafe { @@ -2522,7 +2522,7 @@ pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { } } -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { let fd = borrowed_fd(fd); diff --git a/src/backend/libc/mm/types.rs b/src/backend/libc/mm/types.rs index a4aa3e232..f188c4665 100644 --- a/src/backend/libc/mm/types.rs +++ b/src/backend/libc/mm/types.rs @@ -85,6 +85,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -97,6 +98,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -112,6 +114,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -122,6 +125,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -132,6 +136,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -145,6 +150,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -158,6 +164,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -168,6 +175,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -179,6 +187,7 @@ bitflags! { #[cfg(not(any( freebsdlike, target_os = "aix", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -192,6 +201,7 @@ bitflags! { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", )))] @@ -203,6 +213,7 @@ bitflags! { target_os = "aix", target_os = "dragonfly", target_os = "haiku", + target_os = "hurd", target_os = "netbsd", target_os = "redox", )))] @@ -219,6 +230,7 @@ bitflags! { target_os = "emscripten", target_os = "fuchsia", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "redox", all( @@ -334,7 +346,12 @@ pub enum Advice { WillNeed = bitcast!(c::MADV_WILLNEED), /// `POSIX_MADV_DONTNEED` - #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "haiku")))] + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "haiku", + target_os = "hurd", + )))] DontNeed = bitcast!(c::POSIX_MADV_DONTNEED), /// `POSIX_MADV_DONTNEED` diff --git a/src/backend/libc/net/addr.rs b/src/backend/libc/net/addr.rs index 719a549b1..d07ee0fba 100644 --- a/src/backend/libc/net/addr.rs +++ b/src/backend/libc/net/addr.rs @@ -76,7 +76,13 @@ impl SocketAddrUnix { fn init() -> c::sockaddr_un { c::sockaddr_un { - #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))] + #[cfg(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "nto", + target_os = "hurd", + ))] sun_len: 0, #[cfg(target_os = "vita")] ss_len: 0, @@ -210,7 +216,13 @@ pub type SocketAddrStorage = c::sockaddr_storage; #[inline] pub(crate) fn offsetof_sun_path() -> usize { let z = c::sockaddr_un { - #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))] + #[cfg(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + ))] sun_len: 0_u8, #[cfg(target_os = "vita")] ss_len: 0, @@ -219,6 +231,7 @@ pub(crate) fn offsetof_sun_path() -> usize { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -228,6 +241,7 @@ pub(crate) fn offsetof_sun_path() -> usize { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" )))] diff --git a/src/backend/libc/net/ext.rs b/src/backend/libc/net/ext.rs index 2e11c051d..efd2b31c8 100644 --- a/src/backend/libc/net/ext.rs +++ b/src/backend/libc/net/ext.rs @@ -83,6 +83,7 @@ pub(crate) const fn sockaddr_in6_new( target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -99,6 +100,7 @@ pub(crate) const fn sockaddr_in6_new( target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] diff --git a/src/backend/libc/net/read_sockaddr.rs b/src/backend/libc/net/read_sockaddr.rs index eebd46342..d2c9b69e6 100644 --- a/src/backend/libc/net/read_sockaddr.rs +++ b/src/backend/libc/net/read_sockaddr.rs @@ -59,6 +59,7 @@ unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -68,6 +69,7 @@ unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -77,6 +79,7 @@ unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" )))] diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index c41122b08..51fbb84b7 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -19,6 +19,7 @@ bitflags! { target_os = "espidf", target_os = "nto", target_os = "haiku", + target_os = "hurd", target_os = "vita", )))] const CONFIRM = bitcast!(c::MSG_CONFIRM); @@ -41,6 +42,7 @@ bitflags! { windows, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -88,6 +90,7 @@ bitflags! { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] diff --git a/src/backend/libc/net/sockopt.rs b/src/backend/libc/net/sockopt.rs index 26c9df300..4a5499dce 100644 --- a/src/backend/libc/net/sockopt.rs +++ b/src/backend/libc/net/sockopt.rs @@ -369,6 +369,7 @@ pub(crate) fn get_socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result c::sockaddr_in { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", ))] @@ -62,6 +63,7 @@ pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" ))] @@ -80,6 +82,7 @@ pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita" )))] diff --git a/src/backend/libc/pipe/types.rs b/src/backend/libc/pipe/types.rs index 78fc2fcad..61e18d0fe 100644 --- a/src/backend/libc/pipe/types.rs +++ b/src/backend/libc/pipe/types.rs @@ -18,6 +18,7 @@ bitflags! { solarish, target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "openbsd", target_os = "redox", diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 110cf79b8..477a82ee9 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -63,7 +63,7 @@ mod statx; target_os = "wasi" )))] mod sync; -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] mod xattr; #[cfg(linux_kernel)] @@ -131,7 +131,7 @@ pub use statx::statx; target_os = "wasi" )))] pub use sync::sync; -#[cfg(any(apple, linux_kernel))] +#[cfg(any(apple, linux_kernel, target_os = "hurd"))] pub use xattr::*; /// Re-export types common to POSIX-ish platforms. diff --git a/src/net/sockopt.rs b/src/net/sockopt.rs index 47ab1a5be..f591643e3 100644 --- a/src/net/sockopt.rs +++ b/src/net/sockopt.rs @@ -442,6 +442,7 @@ pub fn get_socket_send_buffer_size(fd: Fd) -> io::Result { target_os = "emscripten", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "netbsd", target_os = "nto", target_os = "vita", diff --git a/src/net/types.rs b/src/net/types.rs index 14345a213..12e228e10 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -91,6 +91,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -106,6 +107,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -128,6 +130,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -140,6 +143,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -152,6 +156,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -163,6 +168,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -175,6 +181,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -190,6 +197,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -202,6 +210,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -213,6 +222,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -229,6 +239,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -241,6 +252,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -253,6 +265,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -265,6 +278,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -277,6 +291,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -291,6 +306,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -303,6 +319,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -315,6 +332,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -327,6 +345,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -339,6 +358,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -351,6 +371,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -362,6 +383,7 @@ impl AddressFamily { windows, target_os = "aix", target_os = "espidf", + target_os = "hurd", target_os = "vita", )))] pub const BLUETOOTH: Self = Self(c::AF_BLUETOOTH as _); @@ -373,6 +395,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -385,6 +408,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -396,6 +420,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "vita", )))] pub const ISDN: Self = Self(c::AF_ISDN as _); @@ -407,6 +432,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] @@ -419,6 +445,7 @@ impl AddressFamily { target_os = "aix", target_os = "espidf", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", )))] diff --git a/src/path/arg.rs b/src/path/arg.rs index fc2910291..d1e5326a8 100644 --- a/src/path/arg.rs +++ b/src/path/arg.rs @@ -1076,11 +1076,11 @@ where #[cfg(not(feature = "alloc"))] { - #[cfg(all(libc, not(target_os = "wasi")))] + #[cfg(all(libc, not(any(target_os = "hurd", target_os = "wasi"))))] const LARGE_PATH_BUFFER_SIZE: usize = libc::PATH_MAX as usize; #[cfg(linux_raw)] const LARGE_PATH_BUFFER_SIZE: usize = linux_raw_sys::general::PATH_MAX as usize; - #[cfg(target_os = "wasi")] + #[cfg(any(target_os = "hurd", target_os = "wasi"))] const LARGE_PATH_BUFFER_SIZE: usize = 4096 as usize; // TODO: upstream this // Taken from diff --git a/src/signal.rs b/src/signal.rs index e55126f37..91ab5c697 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -50,6 +50,7 @@ pub enum Signal { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", all( @@ -108,7 +109,7 @@ pub enum Signal { #[cfg(not(any(target_os = "haiku", target_os = "vita")))] Io = c::SIGIO, /// `SIGPWR` - #[cfg(not(any(bsd, target_os = "haiku", target_os = "vita")))] + #[cfg(not(any(bsd, target_os = "haiku", target_os = "hurd", target_os = "vita")))] #[doc(alias = "Pwr")] Power = c::SIGPWR, /// `SIGSYS`, aka `SIGUNUSED` @@ -171,6 +172,7 @@ impl Signal { solarish, target_os = "aix", target_os = "haiku", + target_os = "hurd", target_os = "nto", target_os = "vita", all( @@ -212,7 +214,7 @@ impl Signal { c::SIGWINCH => Some(Self::Winch), #[cfg(not(any(target_os = "haiku", target_os = "vita")))] c::SIGIO => Some(Self::Io), - #[cfg(not(any(bsd, target_os = "haiku", target_os = "vita")))] + #[cfg(not(any(bsd, target_os = "haiku", target_os = "hurd", target_os = "vita")))] c::SIGPWR => Some(Self::Power), c::SIGSYS => Some(Self::Sys), #[cfg(any( From cb2c70d9b32d31a3ecad92a7c8240c72d24207c4 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 11 Jun 2024 09:47:58 -0700 Subject: [PATCH 05/54] Fix `dup2_stdout` et al to handle fd aliasing. (#1069) * Fix `dup2_stdout` et al to handle fd aliasing. Fix `dup2_stdout` et al to work in the case where the file descriptor they are passed is the same as the file descriptor they implicitly operate on, including on targets that don't have a `dup2` syscall and implement it using `dup3`, such as aarch64. Fixes #1067. * rustfmt and fix test compilation. --- src/stdio.rs | 42 ++++++++++++++++++++++++--------------- tests/stdio/dup2_stdio.rs | 20 +++++++++++++++++++ tests/stdio/main.rs | 6 ++++++ 3 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 tests/stdio/dup2_stdio.rs diff --git a/src/stdio.rs b/src/stdio.rs index 99e118b35..54031a998 100644 --- a/src/stdio.rs +++ b/src/stdio.rs @@ -14,7 +14,11 @@ use backend::c; use backend::fd::{BorrowedFd, FromRawFd, RawFd}; #[cfg(not(any(windows, target_os = "wasi")))] -use {crate::io, backend::fd::AsFd, core::mem::forget}; +use { + crate::io, + backend::fd::{AsFd, AsRawFd}, + core::mem::forget, +}; /// `STDIN_FILENO`—Standard input, borrowed. /// @@ -476,11 +480,13 @@ pub const fn raw_stderr() -> RawFd { #[allow(clippy::mem_forget)] #[inline] pub fn dup2_stdin(fd: Fd) -> io::Result<()> { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stdin() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + if fd.as_fd().as_raw_fd() != c::STDIN_FILENO { + // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't + // dropped. + let mut target = unsafe { take_stdin() }; + backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; + forget(target); + } Ok(()) } @@ -489,11 +495,13 @@ pub fn dup2_stdin(fd: Fd) -> io::Result<()> { #[allow(clippy::mem_forget)] #[inline] pub fn dup2_stdout(fd: Fd) -> io::Result<()> { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stdout() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + if fd.as_fd().as_raw_fd() != c::STDOUT_FILENO { + // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't + // dropped. + let mut target = unsafe { take_stdout() }; + backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; + forget(target); + } Ok(()) } @@ -502,10 +510,12 @@ pub fn dup2_stdout(fd: Fd) -> io::Result<()> { #[allow(clippy::mem_forget)] #[inline] pub fn dup2_stderr(fd: Fd) -> io::Result<()> { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stderr() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + if fd.as_fd().as_raw_fd() != c::STDERR_FILENO { + // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't + // dropped. + let mut target = unsafe { take_stderr() }; + backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; + forget(target); + } Ok(()) } diff --git a/tests/stdio/dup2_stdio.rs b/tests/stdio/dup2_stdio.rs new file mode 100644 index 000000000..b004376ea --- /dev/null +++ b/tests/stdio/dup2_stdio.rs @@ -0,0 +1,20 @@ +use rustix::io::fcntl_getfd; +use rustix::stdio::{dup2_stdout, stdout}; + +#[test] +fn dup2_stdin_stdin() { + let _ = dup2_stdout(stdout()); + fcntl_getfd(stdout()).unwrap(); +} + +#[test] +fn dup2_stdout_stdout() { + let _ = dup2_stdout(stdout()); + fcntl_getfd(stdout()).unwrap(); +} + +#[test] +fn dup2_stderr_stderr() { + let _ = dup2_stdout(stdout()); + fcntl_getfd(stdout()).unwrap(); +} diff --git a/tests/stdio/main.rs b/tests/stdio/main.rs index af294d86e..7e5736099 100644 --- a/tests/stdio/main.rs +++ b/tests/stdio/main.rs @@ -1,5 +1,11 @@ //! Tests for [`rustix::stdio`]. +#![cfg(feature = "stdio")] + +#[cfg(not(feature = "rustc-dep-of-std"))] +#[cfg(not(windows))] +#[cfg(not(target_os = "wasi"))] +mod dup2_stdio; #[cfg(not(feature = "rustc-dep-of-std"))] #[cfg(not(windows))] #[cfg(not(target_os = "wasi"))] From f8f432d0c10c2da8650540751a7a99307b495cec Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 14 Jun 2024 06:22:38 -0700 Subject: [PATCH 06/54] Enable `MapFlags::STACK` on DragonFly BSD and NetBSD. (#1073) --- src/backend/libc/mm/types.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/backend/libc/mm/types.rs b/src/backend/libc/mm/types.rs index f188c4665..54bc63f39 100644 --- a/src/backend/libc/mm/types.rs +++ b/src/backend/libc/mm/types.rs @@ -211,10 +211,8 @@ bitflags! { apple, solarish, target_os = "aix", - target_os = "dragonfly", target_os = "haiku", target_os = "hurd", - target_os = "netbsd", target_os = "redox", )))] const STACK = bitcast!(c::MAP_STACK); From 1716fedee7385bcb978179b06f8be63b4bb75f44 Mon Sep 17 00:00:00 2001 From: Ryan Seipp <12203894+ryanseipp@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:21:28 -0400 Subject: [PATCH 07/54] Add io_uring setup flags `NO_MMAP` and `REGISTERED_FD_ONLY` (#1056) Adds the following setup flags: * `IORING_SETUP_NO_MMAP` - liburing 2.5, kernel 6.5 * `IORING_SETUP_REGISTERED_FD_ONLY` - liburing 2.5, kernel 6.5 Upgrades linux-raw-sys to 0.4.14 to provide the constants with the linux-raw backend. https://github.com/axboe/liburing/blob/master/src/include/liburing/io_uring.h#L188 --- Cargo.toml | 4 ++-- src/io_uring.rs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab413e710..f3cfacdfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ once_cell = { version = "1.5.2", optional = true } # libc backend can be selected via adding `--cfg=rustix_use_libc` to # `RUSTFLAGS` or enabling the `use-libc` cargo feature. [target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", 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 = "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.12", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] } +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 = { version = "0.2.153", default-features = false, optional = true } @@ -53,7 +53,7 @@ libc = { version = "0.2.153", default-features = false } # Some syscalls do not have libc wrappers, such as in `io_uring`. For these, # the libc backend uses the linux-raw-sys ABI and `libc::syscall`. [target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", 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 = "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.12", default-features = false, features = ["general", "ioctl", "no_std"] } +linux-raw-sys = { version = "0.4.14", default-features = false, features = ["general", "ioctl", "no_std"] } # For the libc backend on Windows, use the Winsock API in windows-sys. [target.'cfg(windows)'.dependencies.windows-sys] diff --git a/src/io_uring.rs b/src/io_uring.rs index 0f4d7d566..f6fe225e5 100644 --- a/src/io_uring.rs +++ b/src/io_uring.rs @@ -505,6 +505,12 @@ bitflags::bitflags! { /// `IORING_SETUP_DEFER_TASKRUN` const DEFER_TASKRUN = sys::IORING_SETUP_DEFER_TASKRUN; + /// `IORING_SETUP_NO_MMAP` + const NO_MMAP = sys::IORING_SETUP_NO_MMAP; + + /// `IORING_SETUP_REGISTERED_FD_ONLY` + const REGISTERED_FD_ONLY = sys::IORING_SETUP_REGISTERED_FD_ONLY; + /// const _ = !0; } From d24267b9cddb784d317892370d2415bfc6dfb0f1 Mon Sep 17 00:00:00 2001 From: wangcundashang Date: Tue, 25 Jun 2024 05:55:18 +0900 Subject: [PATCH 08/54] chore: fix some comments (#1078) Signed-off-by: wangcundashang --- src/pty.rs | 2 +- src/runtime.rs | 2 +- tests/io/dup.rs | 4 ++-- tests/net/unix.rs | 4 ++-- tests/net/unix_alloc.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pty.rs b/src/pty.rs index 926ebf789..a9e1a1c5b 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -141,7 +141,7 @@ pub fn unlockpt(fd: Fd) -> io::Result<()> { /// `grantpt(fd)`—Grant access to the user side of a pseudoterminal. /// /// On Linux, calling this function has no effect, as the kernel is expected to -/// grant the appropriate access. On all other platorms, this function has +/// grant the appropriate access. On all other platforms, this function has /// unspecified behavior if the calling process has a [`Signal::Child`] signal /// handler installed. /// diff --git a/src/runtime.rs b/src/runtime.rs index bd3eed2b3..50f5d9041 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -573,7 +573,7 @@ pub const SIGRTMAX: u32 = { linux_raw_sys::general::SIGRTMAX } - // On platfoms that don't, derive it from `_NSIG`. + // On platforms that don't, derive it from `_NSIG`. #[cfg(any(target_arch = "arm", target_arch = "x86", target_arch = "x86_64"))] { linux_raw_sys::general::_NSIG - 1 diff --git a/tests/io/dup.rs b/tests/io/dup.rs index 89d91b682..b132cbb70 100644 --- a/tests/io/dup.rs +++ b/tests/io/dup.rs @@ -17,7 +17,7 @@ fn test_dup() { let mut buf = [0_u8; 4]; assert_eq!(rustix::io::read(&file, &mut buf), Ok(4)); - // Both postitions updated. + // Both positions updated. assert_eq!( rustix::fs::seek(&file, rustix::fs::SeekFrom::Current(0)), Ok(4) @@ -29,7 +29,7 @@ fn test_dup() { assert_eq!(rustix::io::read(&alt, &mut buf), Ok(4)); - // Both postitions updated. + // Both positions updated. assert_eq!( rustix::fs::seek(&file, rustix::fs::SeekFrom::Current(0)), Ok(8) diff --git a/tests/net/unix.rs b/tests/net/unix.rs index 9f55feec8..8687d19e8 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -627,7 +627,7 @@ fn test_unix_peercred_explicit() { match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), - _ => panic!("Unexpected ancilliary message"), + _ => panic!("Unexpected ancillary message"), }; } @@ -683,7 +683,7 @@ fn test_unix_peercred_implicit() { match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), - _ => panic!("Unexpected ancilliary message"), + _ => panic!("Unexpected ancillary message"), }; } diff --git a/tests/net/unix_alloc.rs b/tests/net/unix_alloc.rs index b32af61ef..77d0f0a50 100644 --- a/tests/net/unix_alloc.rs +++ b/tests/net/unix_alloc.rs @@ -630,7 +630,7 @@ fn test_unix_peercred() { match cmsg_buffer.drain().next().unwrap() { RecvAncillaryMessage::ScmCredentials(ucred2) => assert_eq!(ucred2, ucred), - _ => panic!("Unexpected ancilliary message"), + _ => panic!("Unexpected ancillary message"), }; } From ccfc4d9b07558c41aae58e5f79135bc203ba6ca6 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 27 Jun 2024 18:35:20 +0300 Subject: [PATCH 09/54] Fix publish to Artifactory crates registry (#1072) * Remove aliases for crates rustc-std-workspace-core and rustc-std-workspace-alloc Those aliases causes problems in Artifactory crates registry and prevents rustix from being discovered (and as a result downloaded by cargo). This has been introduced in version 0.38.10 when the dep: syntax for Cargo.toml was first introduced. My guess is that the usage of the core and alloc keywords caused issues while uploading and indexing the crate. * Remove rustc-std-workspace-alloc dependency Trying to fix the breakage of the rustc-dep-of-std feature I have noticed that only the std feature uses the alloc feature. and the alloc feature was preventing from me to just remove the dep: syntax So I just deleted the rustc-std-alloc dep and it seems to work * Alias alloc in lib.rs instead of Cargo.toml The alias is only activated if the alloc feature is activated --- Cargo.toml | 10 +++++----- src/lib.rs | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f3cfacdfe..6a3b16083 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ bitflags = { version = "2.4.0", default-features = false } itoa = { version = "1.0.1", 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" } -alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" } +core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core"} +rustc-std-workspace-alloc = { version = "1.0.0", optional = true} # not aliased here but in lib.rs casuse of name collision with the alloc feature compiler_builtins = { version = '0.1.49', optional = true } # The procfs feature needs once_cell. @@ -238,9 +238,9 @@ alloc = [] # This is used in the port of std to rustix. This is experimental and not meant # for regular use. rustc-dep-of-std = [ - "dep:core", - "dep:alloc", - "dep:compiler_builtins", + "core", + "rustc-std-workspace-alloc", + "compiler_builtins", "linux-raw-sys/rustc-dep-of-std", "bitflags/rustc-dep-of-std", "compiler_builtins?/rustc-dep-of-std", diff --git a/src/lib.rs b/src/lib.rs index 51fbdbaf7..bd4465763 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,6 +127,9 @@ allow(unused_imports) )] +#[cfg(all(feature = "rustc-dep-of-std", feature = "alloc"))] +extern crate rustc_std_workspace_alloc as alloc; + #[cfg(all(feature = "alloc", not(feature = "rustc-dep-of-std")))] extern crate alloc; From a4826570ec45c2cc645c30918b2b3d6a2187c8cf Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 27 Jun 2024 10:49:16 -0700 Subject: [PATCH 10/54] Use manual inits in tests to avoid dev-dependeing on the ctor crate. (#1081) The `ctor` crate recently started to cause link errors: = note: symbols.o : error LNK2001: unresolved external symbol _ZN3net34windows_shutdown___rust_dtor___mod13__dtor_export17h23ee8b3200f4e26dE symbols.o : error LNK2001: unresolved external symbol _ZN3net34windows_startup___rust_ctor___ctor17hd7d3fdaa3ef2ca94E D:\a\rustix\rustix\target\release\deps\net-f008608c7b4f9589.exe : fatal error LNK1120: 2 unresolved externals so add manual inits to the net tests so that we don't need to depend on the ctor crate. --- Cargo.toml | 3 --- tests/net/connect_bind_send.rs | 34 +++++++++++++++++++++++++++++ tests/net/dgram.rs | 40 ++++++++++++++++++++++++++++++++++ tests/net/main.rs | 35 +++++++++++++++++++---------- tests/net/poll.rs | 2 ++ tests/net/sockopt.rs | 4 ++++ tests/net/unix.rs | 18 +++++++++++++++ tests/net/unix_alloc.rs | 16 ++++++++++++++ tests/net/v4.rs | 4 ++++ tests/net/v6.rs | 4 ++++ 10 files changed, 146 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a3b16083..9522c87d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,9 +84,6 @@ static_assertions = "1.1.0" [target.'cfg(all(criterion, not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies] criterion = "0.4" -[target.'cfg(windows)'.dev-dependencies] -ctor = "0.2.0" - # Add Criterion configuration, as described here: # [[bench]] diff --git a/tests/net/connect_bind_send.rs b/tests/net/connect_bind_send.rs index 95e68526e..05eae1990 100644 --- a/tests/net/connect_bind_send.rs +++ b/tests/net/connect_bind_send.rs @@ -9,6 +9,8 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; /// Test `connect_any`. #[test] fn net_v4_connect_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -40,6 +42,8 @@ fn net_v4_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_v4_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -70,6 +74,8 @@ fn net_v4_connect_any_accept_with() { /// Similar, but with V6. #[test] fn net_v6_connect_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -101,6 +107,8 @@ fn net_v6_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_v6_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -131,6 +139,8 @@ fn net_v6_connect_any_accept_with() { /// Test `connect` with a `SocketAddr`. #[test] fn net_v4_connect() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -164,6 +174,8 @@ fn net_v4_connect() { /// Similar, but use V6. #[test] fn net_v6_connect() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -197,6 +209,8 @@ fn net_v6_connect() { /// Test `connect_unspec`. #[test] fn net_v4_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, SOME_PORT); @@ -241,6 +255,8 @@ fn net_v4_connect_unspec() { /// Test `connect_unspec`. #[test] fn net_v6_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, SOME_PORT, 0, 0); @@ -285,6 +301,8 @@ fn net_v6_connect_unspec() { /// Test `bind_any`. #[test] fn net_v4_bind_any() { + crate::init(); + let localhost = Ipv4Addr::LOCALHOST; let addr = SocketAddrV4::new(localhost, 0).into(); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -314,6 +332,8 @@ fn net_v4_bind_any() { /// Similar, but use V6. #[test] fn net_v6_bind_any() { + crate::init(); + let localhost = Ipv6Addr::LOCALHOST; let addr = SocketAddrAny::V6(SocketAddrV6::new(localhost, 0, 0, 0)); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -343,6 +363,8 @@ fn net_v6_bind_any() { /// Test `sendto`. #[test] fn net_v4_sendto() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -378,6 +400,8 @@ fn net_v4_sendto() { /// Similar, but with V6. #[test] fn net_v6_sendto() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -413,6 +437,8 @@ fn net_v6_sendto() { /// Test `sendto_any`. #[test] fn net_v4_sendto_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -445,6 +471,8 @@ fn net_v4_sendto_any() { /// Similar, but with V6. #[test] fn net_v6_sendto_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -477,6 +505,8 @@ fn net_v6_sendto_any() { /// Test `acceptfrom`. #[test] fn net_v4_acceptfrom() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); @@ -531,6 +561,8 @@ fn net_v4_acceptfrom() { /// Similar, but with V6. #[test] fn net_v6_acceptfrom() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); @@ -585,6 +617,8 @@ fn net_v6_acceptfrom() { /// Test `shutdown`. #[test] fn net_shutdown() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); diff --git a/tests/net/dgram.rs b/tests/net/dgram.rs index d8cebb24c..906be8a4e 100644 --- a/tests/net/dgram.rs +++ b/tests/net/dgram.rs @@ -9,6 +9,8 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; /// Test `connect_any`. #[test] fn net_dgram_v4_connect_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -38,6 +40,8 @@ fn net_dgram_v4_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_dgram_v4_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -66,6 +70,8 @@ fn net_dgram_v4_connect_any_accept_with() { /// Similar, but with V6. #[test] fn net_dgram_v6_connect_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -95,6 +101,8 @@ fn net_dgram_v6_connect_any() { #[cfg(not(any(apple, windows, target_os = "haiku")))] #[test] fn net_dgram_v6_connect_any_accept_with() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -123,6 +131,8 @@ fn net_dgram_v6_connect_any_accept_with() { /// Test `connect` with a `SocketAddr`. #[test] fn net_dgram_v4_connect() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -154,6 +164,8 @@ fn net_dgram_v4_connect() { /// Similar, but use V6. #[test] fn net_dgram_v6_connect() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -185,6 +197,8 @@ fn net_dgram_v6_connect() { /// Test `connect_unspec`. #[test] fn net_dgram_v4_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, SOME_PORT); @@ -229,6 +243,8 @@ fn net_dgram_v4_connect_unspec() { /// Test `connect_unspec`. #[test] fn net_dgram_v6_connect_unspec() { + crate::init(); + const SOME_PORT: u16 = 47; let localhost_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, SOME_PORT, 0, 0); @@ -273,6 +289,8 @@ fn net_dgram_v6_connect_unspec() { /// Test `bind_any`. #[test] fn net_dgram_v4_bind_any() { + crate::init(); + let localhost = Ipv4Addr::LOCALHOST; let addr = SocketAddrV4::new(localhost, 0).into(); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -300,6 +318,8 @@ fn net_dgram_v4_bind_any() { /// Similar, but use V6. #[test] fn net_dgram_v6_bind_any() { + crate::init(); + let localhost = Ipv6Addr::LOCALHOST; let addr = SocketAddrAny::V6(SocketAddrV6::new(localhost, 0, 0, 0)); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -328,6 +348,8 @@ fn net_dgram_v6_bind_any() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v4_connect_sendto() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -373,6 +395,8 @@ fn net_dgram_v4_connect_sendto() { /// Test `sendto` without calling `connect`. #[test] fn net_dgram_v4_sendto() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -418,6 +442,8 @@ fn net_dgram_v4_sendto() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v6_connect_sendto() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -463,6 +489,8 @@ fn net_dgram_v6_connect_sendto() { /// Similar, but with V6. #[test] fn net_dgram_v6_sendto() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -508,6 +536,8 @@ fn net_dgram_v6_sendto() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v4_connect_sendto_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -550,6 +580,8 @@ fn net_dgram_v4_connect_sendto_any() { /// Test `sendto_any` without calling connect. #[test] fn net_dgram_v4_sendto_any() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -592,6 +624,8 @@ fn net_dgram_v4_sendto_any() { #[cfg(not(any(bsd, target_os = "illumos")))] #[test] fn net_dgram_v6_connect_sendto_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -634,6 +668,8 @@ fn net_dgram_v6_connect_sendto_any() { /// Similar, but with V6. #[test] fn net_dgram_v6_sendto_any() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); @@ -675,6 +711,8 @@ fn net_dgram_v6_sendto_any() { /// Test `acceptfrom`. #[test] fn net_dgram_v4_acceptfrom() { + crate::init(); + let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET, SocketType::DGRAM, None).unwrap(); @@ -702,6 +740,8 @@ fn net_dgram_v4_acceptfrom() { /// Similar, but with V6. #[test] fn net_dgram_v6_acceptfrom() { + crate::init(); + let localhost = IpAddr::V6(Ipv6Addr::LOCALHOST); let addr = SocketAddr::new(localhost, 0); let listener = rustix::net::socket(AddressFamily::INET6, SocketType::DGRAM, None).unwrap(); diff --git a/tests/net/main.rs b/tests/net/main.rs index 4af96f534..2c4b6a312 100644 --- a/tests/net/main.rs +++ b/tests/net/main.rs @@ -20,18 +20,31 @@ mod unix_alloc; mod v4; mod v6; -/// Windows requires us to call a setup function before using any of the -/// socket APIs. #[cfg(windows)] -#[ctor::ctor] -fn windows_startup() { - let _ = rustix::net::wsa_startup().unwrap(); +mod windows { + use std::sync::OnceLock; + + pub struct Thing; + + impl Thing { + pub fn new() -> Self { + let _ = rustix::net::wsa_startup().unwrap(); + Self + } + } + + impl Drop for Thing { + fn drop(&mut self) { + rustix::net::wsa_cleanup().unwrap(); + } + } + + pub static CLEANUP: OnceLock = OnceLock::new(); } -/// Windows requires us to call a cleanup function after using any of the -/// socket APIs. -#[cfg(windows)] -#[ctor::dtor] -fn windows_shutdown() { - rustix::net::wsa_cleanup().unwrap(); +/// Checks whether the Windows socket interface has been started already, and +/// if not, starts it. +pub fn init() { + #[cfg(windows)] + let _ = windows::CLEANUP.get_or_init(|| windows::Thing::new()); } diff --git a/tests/net/poll.rs b/tests/net/poll.rs index 053041e0b..a6d637fe6 100644 --- a/tests/net/poll.rs +++ b/tests/net/poll.rs @@ -95,6 +95,8 @@ fn client(ready: Arc<(Mutex, Condvar)>) { #[test] fn test_poll() { + crate::init(); + let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); let ready_clone = Arc::clone(&ready); diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index bb555d71b..9261f1918 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -292,6 +292,8 @@ fn test_sockopts_tcp(s: &OwnedFd) { #[test] fn test_sockopts_ipv4() { + crate::init(); + let s = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); test_sockopts_socket(&s); @@ -379,6 +381,8 @@ fn test_sockopts_ipv4() { #[test] fn test_sockopts_ipv6() { + crate::init(); + let s = rustix::net::socket(AddressFamily::INET6, SocketType::STREAM, None).unwrap(); test_sockopts_socket(&s); diff --git a/tests/net/unix.rs b/tests/net/unix.rs index 8687d19e8..a9652f1c1 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -93,6 +93,8 @@ fn client(ready: Arc<(Mutex, Condvar)>, path: &Path, runs: &[(&[&str], i32 #[test] fn test_unix() { + crate::init(); + let ready = Arc::new((Mutex::new(false), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -360,6 +362,8 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -373,6 +377,8 @@ fn test_unix_msg() { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_unconnected() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -385,6 +391,8 @@ fn test_unix_msg_unconnected() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -398,6 +406,8 @@ fn test_abstract_unix_msg() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg_unconnected() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -410,6 +420,8 @@ fn test_abstract_unix_msg_unconnected() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_scm_rights() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ @@ -583,6 +595,8 @@ fn test_unix_msg_with_scm_rights() { #[cfg(all(feature = "process", linux_kernel))] #[test] fn test_unix_peercred_explicit() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, @@ -637,6 +651,8 @@ fn test_unix_peercred_explicit() { #[cfg(all(feature = "process", linux_kernel))] #[test] fn test_unix_peercred_implicit() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, @@ -692,6 +708,8 @@ fn test_unix_peercred_implicit() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_combo() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ diff --git a/tests/net/unix_alloc.rs b/tests/net/unix_alloc.rs index 77d0f0a50..5bb241f79 100644 --- a/tests/net/unix_alloc.rs +++ b/tests/net/unix_alloc.rs @@ -91,6 +91,8 @@ fn client(ready: Arc<(Mutex, Condvar)>, path: &Path, runs: &[(&[&str], i32 #[test] fn test_unix() { + crate::init(); + let ready = Arc::new((Mutex::new(false), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -358,6 +360,8 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -371,6 +375,8 @@ fn test_unix_msg() { #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_unconnected() { + crate::init(); + let tmpdir = tempfile::tempdir().unwrap(); let path = tmpdir.path().join("scp_4804"); @@ -383,6 +389,8 @@ fn test_unix_msg_unconnected() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -396,6 +404,8 @@ fn test_abstract_unix_msg() { #[cfg(linux_kernel)] #[test] fn test_abstract_unix_msg_unconnected() { + crate::init(); + use std::os::unix::ffi::OsStrExt; let tmpdir = tempfile::tempdir().unwrap(); @@ -408,6 +418,8 @@ fn test_abstract_unix_msg_unconnected() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_scm_rights() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ @@ -581,6 +593,8 @@ fn test_unix_msg_with_scm_rights() { #[cfg(all(feature = "process", linux_kernel))] #[test] fn test_unix_peercred() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ recvmsg, sendmsg, sockopt, RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, @@ -639,6 +653,8 @@ fn test_unix_peercred() { #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[test] fn test_unix_msg_with_combo() { + crate::init(); + use rustix::fd::AsFd; use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{ diff --git a/tests/net/v4.rs b/tests/net/v4.rs index 99c70e135..d770b657b 100644 --- a/tests/net/v4.rs +++ b/tests/net/v4.rs @@ -65,6 +65,8 @@ fn client(ready: Arc<(Mutex, Condvar)>) { #[test] fn test_v4() { + crate::init(); + let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -87,6 +89,8 @@ fn test_v4() { #[cfg(not(any(windows, target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_v4_msg() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{recvmsg, sendmsg}; diff --git a/tests/net/v6.rs b/tests/net/v6.rs index c4946a63f..0d0a596c9 100644 --- a/tests/net/v6.rs +++ b/tests/net/v6.rs @@ -65,6 +65,8 @@ fn client(ready: Arc<(Mutex, Condvar)>) { #[test] fn test_v6() { + crate::init(); + let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); let ready_clone = Arc::clone(&ready); @@ -87,6 +89,8 @@ fn test_v6() { #[cfg(not(any(windows, target_os = "espidf", target_os = "redox", target_os = "wasi")))] #[test] fn test_v6_msg() { + crate::init(); + use rustix::io::{IoSlice, IoSliceMut}; use rustix::net::{recvmsg, sendmsg}; From 81f500ac5df42755a9608f1a1582653f54eee1b7 Mon Sep 17 00:00:00 2001 From: pekkarr Date: Tue, 2 Jul 2024 07:28:53 +0300 Subject: [PATCH 11/54] Fix `dup2_stdio` tests to test all three functions (#1082) These tests were added in #1069, but despite the names suggesting that they test all of stdin, stdout and stderr, the tests were identical and only tested stdout. --- tests/stdio/dup2_stdio.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/stdio/dup2_stdio.rs b/tests/stdio/dup2_stdio.rs index b004376ea..0a26c534c 100644 --- a/tests/stdio/dup2_stdio.rs +++ b/tests/stdio/dup2_stdio.rs @@ -1,10 +1,10 @@ use rustix::io::fcntl_getfd; -use rustix::stdio::{dup2_stdout, stdout}; +use rustix::stdio::{dup2_stderr, dup2_stdin, dup2_stdout, stderr, stdin, stdout}; #[test] fn dup2_stdin_stdin() { - let _ = dup2_stdout(stdout()); - fcntl_getfd(stdout()).unwrap(); + let _ = dup2_stdin(stdin()); + fcntl_getfd(stdin()).unwrap(); } #[test] @@ -15,6 +15,6 @@ fn dup2_stdout_stdout() { #[test] fn dup2_stderr_stderr() { - let _ = dup2_stdout(stdout()); - fcntl_getfd(stdout()).unwrap(); + let _ = dup2_stderr(stderr()); + fcntl_getfd(stderr()).unwrap(); } From 5662b1f431cba0a55c67d0aa45af7a6075f4e5d9 Mon Sep 17 00:00:00 2001 From: pekkarr Date: Tue, 2 Jul 2024 22:46:33 +0300 Subject: [PATCH 12/54] Fix soundness issues in `dup2_stdout` et al (#1080) * Fix `dup2_stdout` et al to handle incorrect `AsFd` implementations Without this change, an incorrect `AsFd` implementation could result to the stdio file descriptors being closed in safe code, violating I/O safety. The old `dup2_std*` implementations were thus unsound. Consider this old version of the code: ```rust if fd.as_fd().as_raw_fd() != c::STDOUT_FILENO { // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't // dropped. let mut target = unsafe { take_stdout() }; backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; forget(target); } ``` The safety of the `take_stdout()` call relies on two things: 1. In the call to `syscalls::dup2`, `fd` and `target` don't alias (#1069). 2. `forget(target)` is called to not drop `target`. If one of these conditions is violated, then stdout of the process can get closed. Violating both of them is possible in entirely safe code, because `AsFd` is not an unsafe trait. Consider a user defined type that (safely) implements `AsFd` and 1. Returns `stdin()` on the first `as_fd()` call, but `stdout()` on the second time `as_fd()` is called. This would bypass the aliasing check, because the `dup2_stdout` implementation calls `as_fd()` twice, and the first result is used in the check and second result in the actual `dup2` call. 2. Or returns `stdin()` on the first `as_fd()` call, but panics on the second call. The panic would happen on the same line as the `dup2` call, so `forget(target)` after it isn't called, which results in `target` getting dropped which closes stdout. The panic could be catched with `catch_unwind` that would let other code observe the missing stdout. This violates exception safety. This patch fixes both of these issues by calling `as_fd()` only once and storing the `BorrowedFd` into a variable. This ensures that the file descriptor in the alias check and the `dup2` call are the same, and a panic in `as_fd()` would happen before the unsafe `take_stdout` call, so there is no concern of `target` getting dropped because of a panic. * Fix `dup2_stdout` et al to not close the stdio file descriptor on error The `dup2_std*` functions use the `?` operator to return errors from the `dup2` system call. However, this means that the code after it (`forget(target)`) is not run on errors, so `target` gets dropped closing the file descriptor which violates I/O safety. Fix that by using `ManuallyDrop` instead of `forget`. --- src/stdio.rs | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/stdio.rs b/src/stdio.rs index 54031a998..40ecba583 100644 --- a/src/stdio.rs +++ b/src/stdio.rs @@ -17,7 +17,7 @@ use backend::fd::{BorrowedFd, FromRawFd, RawFd}; use { crate::io, backend::fd::{AsFd, AsRawFd}, - core::mem::forget, + core::mem::ManuallyDrop, }; /// `STDIN_FILENO`—Standard input, borrowed. @@ -477,45 +477,42 @@ pub const fn raw_stderr() -> RawFd { /// Utility function to safely `dup2` over stdin (fd 0). #[cfg(not(any(windows, target_os = "wasi")))] -#[allow(clippy::mem_forget)] #[inline] pub fn dup2_stdin(fd: Fd) -> io::Result<()> { - if fd.as_fd().as_raw_fd() != c::STDIN_FILENO { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stdin() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + let fd = fd.as_fd(); + if fd.as_raw_fd() != c::STDIN_FILENO { + // SAFETY: We wrap the returned `OwnedFd` to `ManuallyDrop` so that it + // isn't dropped. + let mut target = ManuallyDrop::new(unsafe { take_stdin() }); + backend::io::syscalls::dup2(fd, &mut target)?; } Ok(()) } /// Utility function to safely `dup2` over stdout (fd 1). #[cfg(not(any(windows, target_os = "wasi")))] -#[allow(clippy::mem_forget)] #[inline] pub fn dup2_stdout(fd: Fd) -> io::Result<()> { - if fd.as_fd().as_raw_fd() != c::STDOUT_FILENO { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stdout() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + let fd = fd.as_fd(); + if fd.as_raw_fd() != c::STDOUT_FILENO { + // SAFETY: We wrap the returned `OwnedFd` to `ManuallyDrop` so that it + // isn't dropped. + let mut target = ManuallyDrop::new(unsafe { take_stdout() }); + backend::io::syscalls::dup2(fd, &mut target)?; } Ok(()) } /// Utility function to safely `dup2` over stderr (fd 2). #[cfg(not(any(windows, target_os = "wasi")))] -#[allow(clippy::mem_forget)] #[inline] pub fn dup2_stderr(fd: Fd) -> io::Result<()> { - if fd.as_fd().as_raw_fd() != c::STDERR_FILENO { - // SAFETY: We pass the returned `OwnedFd` to `forget` so that it isn't - // dropped. - let mut target = unsafe { take_stderr() }; - backend::io::syscalls::dup2(fd.as_fd(), &mut target)?; - forget(target); + let fd = fd.as_fd(); + if fd.as_raw_fd() != c::STDERR_FILENO { + // SAFETY: We wrap the returned `OwnedFd` to `ManuallyDrop` so that it + // isn't dropped. + let mut target = ManuallyDrop::new(unsafe { take_stderr() }); + backend::io::syscalls::dup2(fd, &mut target)?; } Ok(()) } From f798960bcd2a6afc34beef5f3f53cee3e5a61ab3 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 8 Jul 2024 07:52:15 -0700 Subject: [PATCH 13/54] Add a `try_close` function. (#1085) The rustix developers do not intend the existence of this feature to imply that anyone should use it. --- Cargo.toml | 4 ++++ src/backend/libc/io/syscalls.rs | 5 +++++ src/backend/linux_raw/io/syscalls.rs | 6 ++++++ src/io/close.rs | 10 ++++++++++ src/io/mod.rs | 2 +- tests/io/close.rs | 10 ++++++++++ 6 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9522c87d1..edf2d2fe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -245,3 +245,7 @@ rustc-dep-of-std = [ # Obsolete and deprecated. cc = [] + +# Enable `rustix::io::try_close`. The rustix developers do not intend the +# existence of this feature to imply that anyone should use it. +try_close = [] diff --git a/src/backend/libc/io/syscalls.rs b/src/backend/libc/io/syscalls.rs index 9c8638f75..270aefd32 100644 --- a/src/backend/libc/io/syscalls.rs +++ b/src/backend/libc/io/syscalls.rs @@ -201,6 +201,11 @@ pub(crate) unsafe fn close(raw_fd: RawFd) { let _ = c::close(raw_fd as c::c_int); } +#[cfg(feature = "try_close")] +pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> { + ret(c::close(raw_fd as c::c_int)) +} + #[inline] pub(crate) unsafe fn ioctl( fd: BorrowedFd<'_>, diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index c38f28fce..484da991f 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -241,6 +241,12 @@ pub(crate) unsafe fn close(fd: RawFd) { syscall_readonly!(__NR_close, raw_fd(fd)).decode_void(); } +#[cfg(feature = "try_close")] +#[inline] +pub(crate) unsafe fn try_close(fd: RawFd) -> io::Result<()> { + ret(syscall_readonly!(__NR_close, raw_fd(fd))) +} + #[inline] pub(crate) unsafe fn ioctl( fd: BorrowedFd<'_>, diff --git a/src/io/close.rs b/src/io/close.rs index 474d252f4..b09267f30 100644 --- a/src/io/close.rs +++ b/src/io/close.rs @@ -53,3 +53,13 @@ use backend::fd::RawFd; pub unsafe fn close(raw_fd: RawFd) { backend::io::syscalls::close(raw_fd) } + +/// `close(raw_fd)`—Closes a `RawFd` directly, and report any errors +/// returned by the OS. +/// +/// The rustix developers do not intend the existence of this feature to imply +/// that anyone should use it. +#[cfg(feature = "try_close")] +pub unsafe fn try_close(raw_fd: RawFd) -> crate::io::Result<()> { + backend::io::syscalls::try_close(raw_fd) +} diff --git a/src/io/mod.rs b/src/io/mod.rs index bddd12acd..c47eb8d4e 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -18,7 +18,7 @@ mod is_read_write; #[cfg(not(windows))] mod read_write; -pub use close::close; +pub use close::*; #[cfg(not(windows))] pub use dup::*; pub use errno::{retry_on_intr, Errno, Result}; diff --git a/tests/io/close.rs b/tests/io/close.rs index a7a973192..920bd158d 100644 --- a/tests/io/close.rs +++ b/tests/io/close.rs @@ -18,3 +18,13 @@ fn test_close_socket() { rustix::io::close(raw); } } + +#[cfg(all(feature = "try_close", any(unix, target_os = "wasi")))] +#[test] +fn test_try_close() { + let file = std::fs::File::open("Cargo.toml").unwrap(); + let raw = file.into_raw_fd(); + unsafe { + rustix::io::try_close(raw).unwrap(); + } +} From 9e2af6100e3d4482f7ebc7cc49004e483f6b6bc6 Mon Sep 17 00:00:00 2001 From: Zang Leigang Date: Mon, 22 Jul 2024 23:35:21 +0800 Subject: [PATCH 14/54] Fix mising MREMAP_FIXED for mremap_fixed for linux_raw (#1089) --- src/backend/linux_raw/mm/syscalls.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/linux_raw/mm/syscalls.rs b/src/backend/linux_raw/mm/syscalls.rs index 361f11157..32e3a8e34 100644 --- a/src/backend/linux_raw/mm/syscalls.rs +++ b/src/backend/linux_raw/mm/syscalls.rs @@ -17,6 +17,7 @@ use crate::backend::conv::{c_uint, no_fd, pass_usize, ret, ret_owned_fd, ret_voi use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; use linux_raw_sys::general::MAP_ANONYMOUS; +use linux_raw_sys::general::MREMAP_FIXED; #[inline] pub(crate) fn madvise(addr: *mut c::c_void, len: usize, advice: Advice) -> io::Result<()> { @@ -170,7 +171,7 @@ pub(crate) unsafe fn mremap_fixed( old_address, pass_usize(old_size), pass_usize(new_size), - flags, + c_uint(flags.bits() | MREMAP_FIXED), new_address )) } From 29af8fc0de0ddc6a6357ab4d5bf898e2876fa995 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 22 Jul 2024 12:02:24 -0400 Subject: [PATCH 15/54] setns: Add a doc alias (#1088) At first I thought this wasn't bound in rustix until I went to search the source code to look at adding it. Signed-off-by: Colin Walters --- src/thread/setns.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thread/setns.rs b/src/thread/setns.rs index ef61d11e8..48279dcda 100644 --- a/src/thread/setns.rs +++ b/src/thread/setns.rs @@ -101,6 +101,7 @@ bitflags! { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html +#[doc(alias = "setns")] pub fn move_into_link_name_space( fd: BorrowedFd<'_>, allowed_type: Option, @@ -118,6 +119,7 @@ pub fn move_into_link_name_space( /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/setns.2.html +#[doc(alias = "setns")] pub fn move_into_thread_name_spaces( fd: BorrowedFd<'_>, allowed_types: ThreadNameSpaceType, From 8f891230c3a08134d75c434f49065d68b2a458a7 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 22 Jul 2024 20:21:10 -0400 Subject: [PATCH 16/54] rawdir: Use `ignore`, not `notrust` (#1087) - Fixes the problem that the prelude comments show up in the docs, including the confusing comment " # // The `notrust` above can be removed when we can depend on Rust 1.65." because the reader can't *see* the `notrust` - I parsed `notrust` as "no-trust" and thought there was some security thing The `ignore` attribute AFAICS is intended for these situations. --- src/fs/raw_dir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fs/raw_dir.rs b/src/fs/raw_dir.rs index 93686b19a..963df7d6f 100644 --- a/src/fs/raw_dir.rs +++ b/src/fs/raw_dir.rs @@ -89,8 +89,8 @@ impl<'buf, Fd: AsFd> RawDir<'buf, Fd> { /// Heap allocated growing buffer for supporting directory entries with /// arbitrarily large file names: /// - /// ```notrust - /// # // The `notrust` above can be removed when we can depend on Rust 1.65. + /// ```ignore + /// # // The `ignore` above can be removed when we can depend on Rust 1.65. /// # use std::mem::MaybeUninit; /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir}; /// # use rustix::io::Errno; From e8d9949b1add2a8036e4b8610ac5dbb147a63d92 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Thu, 25 Jul 2024 05:38:32 -0700 Subject: [PATCH 17/54] ci: Disable UWP windows target for now (#1093) It appears this target is broken in upstream Rust. cc rust-lang/rust#100400 Signed-off-by: John Nunley --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 257f89a66..9ba951b7b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -219,7 +219,8 @@ jobs: # Omit --all-targets on haiku because not all the tests build yet. # Temporarily disable this because the target appears to have breakage on nightly. #- run: cargo check -Z build-std --target x86_64-unknown-haiku --features=all-apis - - run: cargo check -Z build-std --target x86_64-uwp-windows-msvc --all-targets --features=all-apis + # Temporarily disable this because the target appears to have breakage on nightly. + #- run: cargo check -Z build-std --target x86_64-uwp-windows-msvc --all-targets --features=all-apis # Temporarily disable riscv32imc-esp-espidf due to std using SOMAXCONN. #- run: cargo check -Z build-std --target=riscv32imc-esp-espidf --features=all-apis - run: cargo check -Z build-std --target=aarch64-unknown-nto-qnx710 --features=all-apis From 51a88c00d09d460936c678843f68e217a0d4d73e Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Wed, 14 Aug 2024 10:54:12 -0700 Subject: [PATCH 18/54] Add cargo unexpected cfg directive (#1106) --- Cargo.toml | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index edf2d2fe1..9fa672ca7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ bitflags = { version = "2.4.0", default-features = false } itoa = { version = "1.0.1", 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"} -rustc-std-workspace-alloc = { version = "1.0.0", optional = true} # not aliased here but in lib.rs casuse of name collision with the alloc feature +core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } +rustc-std-workspace-alloc = { version = "1.0.0", optional = true } # not aliased here but in lib.rs casuse of name collision with the alloc feature compiler_builtins = { version = '0.1.49', optional = true } # The procfs feature needs once_cell. @@ -249,3 +249,32 @@ cc = [] # Enable `rustix::io::try_close`. The rustix developers do not intend the # existence of this feature to imply that anyone should use it. try_close = [] + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(alloc_c_string)', + 'cfg(alloc_ffi)', + 'cfg(apple)', + 'cfg(asm_experimental_arch)', + 'cfg(bsd)', + 'cfg(core_c_str)', + 'cfg(core_ffi_c)', + 'cfg(core_intrinsics)', + 'cfg(doc_cfg)', + 'cfg(document_experimental_runtime_api)', + 'cfg(fix_y2038)', + 'cfg(freebsdlike)', + 'cfg(libc)', + 'cfg(linux_kernel)', + 'cfg(linux_like)', + 'cfg(linux_raw)', + 'cfg(netbsdlike)', + 'cfg(rustc_attrs)', + 'cfg(solarish)', + 'cfg(staged_api)', + 'cfg(static_assertions)', + 'cfg(thumb_mode)', + 'cfg(wasi)', + 'cfg(wasi_ext)', +] From 6a8c228417e893804be5cd2014fe82f6ed15181b Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Thu, 15 Aug 2024 02:52:28 -0700 Subject: [PATCH 19/54] Fix a few more bad cfgs (#1109) * Fix a few more bad cfgs Signed-off-by: Alex Saveau * Fix borken test Signed-off-by: Alex Saveau --------- Signed-off-by: Alex Saveau --- Cargo.toml | 1 + tests/net/sockopt.rs | 2 +- tests/process/working_directory.rs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9fa672ca7..a4043103e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -261,6 +261,7 @@ check-cfg = [ 'cfg(core_c_str)', 'cfg(core_ffi_c)', 'cfg(core_intrinsics)', + 'cfg(criterion)', 'cfg(doc_cfg)', 'cfg(document_experimental_runtime_api)', 'cfg(fix_y2038)', diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index 9261f1918..60d776e02 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -186,7 +186,7 @@ fn test_sockopts_socket(s: &OwnedFd) { // Test `tcp` socket options. fn test_sockopts_tcp(s: &OwnedFd) { - #[cfg(any(linux_like, taraget_os = "fuchsia"))] + #[cfg(any(linux_like, target_os = "fuchsia"))] { assert_eq!(sockopt::get_tcp_user_timeout(s).unwrap(), 0); sockopt::set_tcp_user_timeout(s, 7).unwrap(); diff --git a/tests/process/working_directory.rs b/tests/process/working_directory.rs index e3ed56818..c80bb1134 100644 --- a/tests/process/working_directory.rs +++ b/tests/process/working_directory.rs @@ -39,8 +39,8 @@ fn test_changing_working_directory() { #[cfg(not(target_os = "fuchsia"))] rustix::process::fchdir(orig_fd_cwd).expect("changing dir to the original"); - #[cfg(target_os = "fushcia")] - rustix::process::chdir(orig_cwd).expect("changing dir to the original"); + #[cfg(target_os = "fuchsia")] + rustix::process::chdir(&orig_cwd).expect("changing dir to the original"); let ch2_cwd = rustix::process::getcwd(ch1_cwd).expect("get the cwd"); assert_eq!( From 2293963753632c90889203cdea410f0b0a4d81a2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 15 Aug 2024 07:13:51 -0700 Subject: [PATCH 20/54] Update CI to macos-12. (#1111) macos-11 no longer exists, so test with macos-12. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9ba951b7b..cbf79ab18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -247,7 +247,7 @@ jobs: RUSTFLAGS: --cfg rustix_use_experimental_features strategy: matrix: - build: [ubuntu, ubuntu-20.04, i686-linux, aarch64-linux, powerpc64le-linux, riscv64-linux, s390x-linux, arm-linux, ubuntu-stable, i686-linux-stable, aarch64-linux-stable, riscv64-linux-stable, s390x-linux-stable, powerpc64le-linux-stable, arm-linux-stable, ubuntu-1.63, i686-linux-1.63, aarch64-linux-1.63, riscv64-linux-1.63, s390x-linux-1.63, powerpc64le-linux-1.63, arm-linux-1.63, macos-latest, macos-11, windows, windows-2019, musl] + build: [ubuntu, ubuntu-20.04, i686-linux, aarch64-linux, powerpc64le-linux, riscv64-linux, s390x-linux, arm-linux, ubuntu-stable, i686-linux-stable, aarch64-linux-stable, riscv64-linux-stable, s390x-linux-stable, powerpc64le-linux-stable, arm-linux-stable, ubuntu-1.63, i686-linux-1.63, aarch64-linux-1.63, riscv64-linux-1.63, s390x-linux-1.63, powerpc64le-linux-1.63, arm-linux-1.63, macos-latest, macos-12, windows, windows-2019, musl] include: - build: ubuntu os: ubuntu-latest @@ -420,8 +420,8 @@ jobs: - build: macos-latest os: macos-latest rust: stable - - build: macos-11 - os: macos-11 + - build: macos-12 + os: macos-12 rust: stable - build: windows os: windows-latest From 0c7a143647ffe43f1427bb1d1f9ce25ba064c4a5 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Thu, 15 Aug 2024 12:39:50 -0700 Subject: [PATCH 21/54] Add missing epoll links (#1099) * Add missing epoll links * Remove incorrect comment * Tweak the epoll docs some more Signed-off-by: Alex Saveau --------- Signed-off-by: Alex Saveau --- src/backend/linux_raw/event/epoll.rs | 43 +++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 3d5787b8f..73e5da149 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -154,6 +154,12 @@ bitflags! { /// /// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file /// descriptor from being implicitly passed across `exec` boundaries. +/// +/// # References +/// +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html #[inline] #[doc(alias = "epoll_create1")] pub fn create(flags: CreateFlags) -> io::Result { @@ -166,12 +172,17 @@ pub fn create(flags: CreateFlags) -> io::Result { /// This registers interest in any of the events set in `events` occurring on /// the file descriptor associated with `data`. /// -/// If [`delete`] is not called on the I/O source passed into this function -/// before the I/O source is `close`d, then the `epoll` will act as if the I/O -/// source is still registered with it. This can lead to spurious events being -/// returned from [`wait`]. If a file descriptor is an -/// `Arc`, then `epoll` can be thought to maintain a -/// `Weak` to the file descriptor. +/// Note that `close`ing a file descriptor does not necessarily unregister interest +/// which can lead to spurious events being returned from [`wait`]. If a file descriptor +/// is an `Arc`, then `epoll` can be thought to maintain a +/// `Weak` to the file descriptor. Check the [faq] for details. +/// +/// # References +/// +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F #[doc(alias = "epoll_ctl")] #[inline] pub fn add( @@ -198,6 +209,12 @@ pub fn add( /// given epoll object. /// /// This sets the events of interest with `target` to `events`. +/// +/// # References +/// +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html #[doc(alias = "epoll_ctl")] #[inline] pub fn modify( @@ -223,6 +240,12 @@ pub fn modify( /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a /// given epoll object. +/// +/// # References +/// +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html #[doc(alias = "epoll_ctl")] #[inline] pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { @@ -239,8 +262,14 @@ pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { /// /// For each event of interest, an element is written to `events`. On /// success, this returns the number of written elements. +/// +/// # References +/// +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] #[inline] pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { // SAFETY: We're calling `epoll_wait` via FFI and we know how it From 9f57313d594adc5a832b55eb74d4eaab29818097 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Thu, 15 Aug 2024 14:24:57 -0700 Subject: [PATCH 22/54] Unify inotify public interface across backends and accept `impl AsFd` (#1105) * Unify inotify public interface across backends Signed-off-by: Alex Saveau * Fix bad inotify FD interface Signed-off-by: Alex Saveau --------- Signed-off-by: Alex Saveau --- src/backend/libc/fs/inotify.rs | 53 ----------------------------- src/backend/libc/fs/syscalls.rs | 36 ++++++++++++++++++++ src/backend/linux_raw/fs/inotify.rs | 40 ---------------------- src/fs/inotify.rs | 43 +++++++++++++++++++++++ src/fs/mod.rs | 4 +-- 5 files changed, 81 insertions(+), 95 deletions(-) create mode 100644 src/fs/inotify.rs diff --git a/src/backend/libc/fs/inotify.rs b/src/backend/libc/fs/inotify.rs index 2044bd945..3be68c7cf 100644 --- a/src/backend/libc/fs/inotify.rs +++ b/src/backend/libc/fs/inotify.rs @@ -1,9 +1,6 @@ //! inotify support for working with inotifies use crate::backend::c; -use crate::backend::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_owned_fd}; -use crate::fd::{BorrowedFd, OwnedFd}; -use crate::io; use bitflags::bitflags; bitflags! { @@ -79,53 +76,3 @@ bitflags! { const _ = !0; } } - -/// `inotify_init1(flags)`—Creates a new inotify object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -#[doc(alias = "inotify_init1")] -pub fn inotify_init(flags: CreateFlags) -> io::Result { - // SAFETY: `inotify_init1` has no safety preconditions. - unsafe { ret_owned_fd(c::inotify_init1(bitflags_bits!(flags))) } -} - -/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify. -/// -/// This registers or updates a watch for the filesystem path `path` and -/// returns a watch descriptor corresponding to this watch. -/// -/// Note: Due to the existence of hardlinks, providing two different paths to -/// this method may result in it returning the same watch descriptor. An -/// application should keep track of this externally to avoid logic errors. -pub fn inotify_add_watch( - inot: BorrowedFd<'_>, - path: P, - flags: WatchFlags, -) -> io::Result { - path.into_with_c_str(|path| { - // SAFETY: The fd and path we are passing is guaranteed valid by the - // type system. - unsafe { - ret_c_int(c::inotify_add_watch( - borrowed_fd(inot), - c_str(path), - flags.bits(), - )) - } - }) -} - -/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. -/// -/// The watch descriptor provided should have previously been returned by -/// [`inotify_add_watch`] and not previously have been removed. -#[doc(alias = "inotify_rm_watch")] -pub fn inotify_remove_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { - // Android's `inotify_rm_watch` takes `u32` despite that - // `inotify_add_watch` expects a `i32`. - #[cfg(target_os = "android")] - let wd = wd as u32; - // SAFETY: The fd is valid and closing an arbitrary wd is valid. - unsafe { ret(c::inotify_rm_watch(borrowed_fd(inot), wd)) } -} diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index af615ff74..a2204fe34 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -2549,3 +2549,39 @@ fn test_sizes() { #[cfg(fix_y2038)] assert!(core::mem::size_of::<[c::timespec; 2]>() < core::mem::size_of::()); } + +#[inline] +#[cfg(linux_kernel)] +pub(crate) fn inotify_init1(flags: super::inotify::CreateFlags) -> io::Result { + // SAFETY: `inotify_init1` has no safety preconditions. + unsafe { ret_owned_fd(c::inotify_init1(bitflags_bits!(flags))) } +} + +#[inline] +#[cfg(linux_kernel)] +pub(crate) fn inotify_add_watch( + inot: BorrowedFd<'_>, + path: &CStr, + flags: super::inotify::WatchFlags, +) -> io::Result { + // SAFETY: The fd and path we are passing is guaranteed valid by the + // type system. + unsafe { + ret_c_int(c::inotify_add_watch( + borrowed_fd(inot), + c_str(path), + flags.bits(), + )) + } +} + +#[inline] +#[cfg(linux_kernel)] +pub(crate) fn inotify_rm_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { + // Android's `inotify_rm_watch` takes `u32` despite that + // `inotify_add_watch` expects a `i32`. + #[cfg(target_os = "android")] + let wd = wd as u32; + // SAFETY: The fd is valid and closing an arbitrary wd is valid. + unsafe { ret(c::inotify_rm_watch(borrowed_fd(inot), wd)) } +} diff --git a/src/backend/linux_raw/fs/inotify.rs b/src/backend/linux_raw/fs/inotify.rs index 851335ba8..1876e8349 100644 --- a/src/backend/linux_raw/fs/inotify.rs +++ b/src/backend/linux_raw/fs/inotify.rs @@ -1,9 +1,6 @@ //! inotify support for working with inotifies use crate::backend::c; -use crate::backend::fs::syscalls; -use crate::fd::{BorrowedFd, OwnedFd}; -use crate::io; use bitflags::bitflags; bitflags! { @@ -79,40 +76,3 @@ bitflags! { const _ = !0; } } - -/// `inotify_init1(flags)`—Creates a new inotify object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -#[doc(alias = "inotify_init1")] -#[inline] -pub fn inotify_init(flags: CreateFlags) -> io::Result { - syscalls::inotify_init1(flags) -} - -/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify. -/// -/// This registers or updates a watch for the filesystem path `path` and -/// returns a watch descriptor corresponding to this watch. -/// -/// Note: Due to the existence of hardlinks, providing two different paths to -/// this method may result in it returning the same watch descriptor. An -/// application should keep track of this externally to avoid logic errors. -#[inline] -pub fn inotify_add_watch( - inot: BorrowedFd<'_>, - path: P, - flags: WatchFlags, -) -> io::Result { - path.into_with_c_str(|path| syscalls::inotify_add_watch(inot, path, flags)) -} - -/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. -/// -/// The watch descriptor provided should have previously been returned by -/// [`inotify_add_watch`] and not previously have been removed. -#[doc(alias = "inotify_rm_watch")] -#[inline] -pub fn inotify_remove_watch(inot: BorrowedFd<'_>, wd: i32) -> io::Result<()> { - syscalls::inotify_rm_watch(inot, wd) -} diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs new file mode 100644 index 000000000..addcfda57 --- /dev/null +++ b/src/fs/inotify.rs @@ -0,0 +1,43 @@ +//! inotify support for working with inotifies + +pub use crate::backend::fs::inotify::{CreateFlags, WatchFlags}; +use crate::backend::fs::syscalls; +use crate::fd::{AsFd, OwnedFd}; +use crate::io; + +/// `inotify_init1(flags)`—Creates a new inotify object. +/// +/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// descriptor from being implicitly passed across `exec` boundaries. +#[doc(alias = "inotify_init1")] +#[inline] +pub fn inotify_init(flags: CreateFlags) -> io::Result { + syscalls::inotify_init1(flags) +} + +/// `inotify_add_watch(self, path, flags)`—Adds a watch to inotify. +/// +/// This registers or updates a watch for the filesystem path `path` and +/// returns a watch descriptor corresponding to this watch. +/// +/// Note: Due to the existence of hardlinks, providing two different paths to +/// this method may result in it returning the same watch descriptor. An +/// application should keep track of this externally to avoid logic errors. +#[inline] +pub fn inotify_add_watch( + inot: impl AsFd, + path: P, + flags: WatchFlags, +) -> io::Result { + path.into_with_c_str(|path| syscalls::inotify_add_watch(inot.as_fd(), path, flags)) +} + +/// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. +/// +/// The watch descriptor provided should have previously been returned by +/// [`inotify_add_watch`] and not previously have been removed. +#[doc(alias = "inotify_rm_watch")] +#[inline] +pub fn inotify_remove_watch(inot: impl AsFd, wd: i32) -> io::Result<()> { + syscalls::inotify_rm_watch(inot.as_fd(), wd) +} diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 477a82ee9..13a6d29cd 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -33,6 +33,8 @@ mod getpath; #[cfg(not(target_os = "wasi"))] // WASI doesn't have get[gpu]id. mod id; #[cfg(linux_kernel)] +pub mod inotify; +#[cfg(linux_kernel)] mod ioctl; #[cfg(not(any( target_os = "espidf", @@ -66,8 +68,6 @@ mod sync; #[cfg(any(apple, linux_kernel, target_os = "hurd"))] mod xattr; -#[cfg(linux_kernel)] -pub use crate::backend::fs::inotify; pub use abs::*; #[cfg(not(target_os = "redox"))] pub use at::*; From 2338a0dbec96c95af21a4dbe6351e8cbc957bc84 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 16 Aug 2024 09:37:16 -0700 Subject: [PATCH 23/54] Add documentation links to `uninit` functions. (#1114) In non-`uninit` functions, add documentation links to their corresponding `uninit` functions. --- src/io/read_write.rs | 6 ++++++ src/net/send_recv/mod.rs | 6 ++++++ src/rand/getrandom.rs | 3 +++ 3 files changed, 15 insertions(+) diff --git a/src/io/read_write.rs b/src/io/read_write.rs index 9ebbd7a12..94a31ef2a 100644 --- a/src/io/read_write.rs +++ b/src/io/read_write.rs @@ -16,6 +16,9 @@ pub use backend::io::types::ReadWriteFlags; /// `read(fd, buf)`—Reads from a stream. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`read_uninit`]. +/// /// # References /// - [POSIX] /// - [Linux] @@ -88,6 +91,9 @@ pub fn write(fd: Fd, buf: &[u8]) -> io::Result { /// `pread(fd, buf, offset)`—Reads from a file at a given position. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`pread_uninit`]. +/// /// # References /// - [POSIX] /// - [Linux] diff --git a/src/net/send_recv/mod.rs b/src/net/send_recv/mod.rs index 1ae4fdb39..477089831 100644 --- a/src/net/send_recv/mod.rs +++ b/src/net/send_recv/mod.rs @@ -34,6 +34,9 @@ pub use msg::*; /// `recv(fd, buf, flags)`—Reads data from a socket. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`recv_uninit`]. +/// /// # References /// - [Beej's Guide to Network Programming] /// - [POSIX] @@ -115,6 +118,9 @@ pub fn send(fd: Fd, buf: &[u8], flags: SendFlags) -> io::Result /// `recvfrom(fd, buf, flags, addr, len)`—Reads data from a socket and /// returns the sender address. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`recvfrom_uninit`]. +/// /// # References /// - [Beej's Guide to Network Programming] /// - [POSIX] diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs index c7f117ad9..6958fa7ca 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -11,6 +11,9 @@ pub use backend::rand::types::GetRandomFlags; /// This is a very low-level API which may be difficult to use correctly. Most /// users should prefer to use [`getrandom`] or [`rand`] APIs instead. /// +/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. +/// To use an uninitialized buffer, use [`getrandom_uninit`]. +/// /// [`getrandom`]: https://crates.io/crates/getrandom /// [`rand`]: https://crates.io/crates/rand /// From 635b87b8870760964ac235992a48e7cbf0081e42 Mon Sep 17 00:00:00 2001 From: ur4t <46435411+ur4t@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:40:01 +0800 Subject: [PATCH 24/54] Update libc dependency to 0.2.156. (#1065) --- Cargo.toml | 6 +++--- src/backend/libc/param/auxv.rs | 4 +--- src/backend/linux_raw/param/libc_auxv.rs | 5 ++--- tests/param/auxv.rs | 4 +--- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a4043103e..ef810dfdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ once_cell = { version = "1.5.2", optional = true } [target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", 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 = "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 = { version = "0.2.153", default-features = false, optional = true } +libc = { version = "0.2.156", default-features = false, optional = true } # Dependencies for platforms where only libc is supported: # @@ -46,7 +46,7 @@ libc = { version = "0.2.153", default-features = false, optional = true } # backend, so enable its dependencies unconditionally. [target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", 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 = "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 = { version = "0.2.153", default-features = false } +libc = { version = "0.2.156", default-features = false } # Additional dependencies for Linux with the libc backend: # @@ -74,7 +74,7 @@ default-features = false [dev-dependencies] tempfile = "3.5.0" -libc = "0.2.153" +libc = "0.2.156" libc_errno = { package = "errno", version = "0.3.8", default-features = false } serial_test = "2.0.0" memoffset = "0.9.0" diff --git a/src/backend/libc/param/auxv.rs b/src/backend/libc/param/auxv.rs index e53767583..55890c24a 100644 --- a/src/backend/libc/param/auxv.rs +++ b/src/backend/libc/param/auxv.rs @@ -46,10 +46,8 @@ pub(crate) fn linux_hwcap() -> (usize, usize) { ))] #[inline] pub(crate) fn linux_minsigstksz() -> usize { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: c::c_ulong = 51; if let Some(libc_getauxval) = getauxval.get() { - unsafe { libc_getauxval(AT_MINSIGSTKSZ) as usize } + unsafe { libc_getauxval(c::AT_MINSIGSTKSZ) as usize } } else { 0 } diff --git a/src/backend/linux_raw/param/libc_auxv.rs b/src/backend/linux_raw/param/libc_auxv.rs index c92e62a28..9f6a06724 100644 --- a/src/backend/linux_raw/param/libc_auxv.rs +++ b/src/backend/linux_raw/param/libc_auxv.rs @@ -39,6 +39,7 @@ const AT_HWCAP2: c::c_ulong = 26; const AT_SECURE: c::c_ulong = 23; const AT_EXECFN: c::c_ulong = 31; const AT_SYSINFO_EHDR: c::c_ulong = 33; +const AT_MINSIGSTKSZ: c::c_ulong = 51; // Declare `sysconf` ourselves so that we don't depend on all of libc just for // this. @@ -64,6 +65,7 @@ fn test_abi() { const_assert_eq!(self::AT_EXECFN, ::libc::AT_EXECFN); const_assert_eq!(self::AT_SECURE, ::libc::AT_SECURE); const_assert_eq!(self::AT_SYSINFO_EHDR, ::libc::AT_SYSINFO_EHDR); + const_assert_eq!(self::AT_MINSIGSTKSZ, ::libc::AT_MINSIGSTKSZ); #[cfg(feature = "runtime")] const_assert_eq!(self::AT_PHDR, ::libc::AT_PHDR); #[cfg(feature = "runtime")] @@ -111,9 +113,6 @@ pub(crate) fn linux_hwcap() -> (usize, usize) { #[cfg(feature = "param")] #[inline] pub(crate) fn linux_minsigstksz() -> usize { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: c::c_ulong = 51; - #[cfg(not(feature = "runtime"))] if let Some(libc_getauxval) = getauxval.get() { unsafe { libc_getauxval(AT_MINSIGSTKSZ) as usize } diff --git a/tests/param/auxv.rs b/tests/param/auxv.rs index 7806fa357..f7755aad7 100644 --- a/tests/param/auxv.rs +++ b/tests/param/auxv.rs @@ -49,11 +49,9 @@ fn test_linux_minsigstksz() { weak!(fn getauxval(libc::c_ulong) -> libc::c_ulong); if let Some(libc_getauxval) = getauxval.get() { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: libc::c_ulong = 51; assert_eq!( linux_minsigstksz(), - unsafe { libc_getauxval(AT_MINSIGSTKSZ) } as usize + unsafe { libc_getauxval(libc::AT_MINSIGSTKSZ) } as usize ); } } From e1f4d486123f0b01f6d2ad89556a1f931d8e0480 Mon Sep 17 00:00:00 2001 From: "Ayose C." Date: Fri, 23 Aug 2024 13:03:48 +0000 Subject: [PATCH 25/54] Add pivot_root syscall. (#1116) It is enabled only in Linux, and with the feature `fs` (like `chroot`). --- src/backend/libc/process/syscalls.rs | 11 +++++++++++ src/backend/linux_raw/process/syscalls.rs | 6 ++++++ src/process/mod.rs | 4 ++++ src/process/pivot_root.rs | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 src/process/pivot_root.rs diff --git a/src/backend/libc/process/syscalls.rs b/src/backend/libc/process/syscalls.rs index fc00071de..cf0bc1852 100644 --- a/src/backend/libc/process/syscalls.rs +++ b/src/backend/libc/process/syscalls.rs @@ -725,6 +725,17 @@ pub(crate) fn pidfd_getfd( } } +#[cfg(target_os = "linux")] +pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> { + syscall! { + fn pivot_root( + new_root: *const c::c_char, + put_old: *const c::c_char + ) via SYS_pivot_root -> c::c_int + } + unsafe { ret(pivot_root(c_str(new_root), c_str(put_old))) } +} + #[cfg(all(feature = "alloc", not(target_os = "wasi")))] pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result { let len = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?; diff --git a/src/backend/linux_raw/process/syscalls.rs b/src/backend/linux_raw/process/syscalls.rs index fb900a4d3..de4307c7c 100644 --- a/src/backend/linux_raw/process/syscalls.rs +++ b/src/backend/linux_raw/process/syscalls.rs @@ -601,6 +601,12 @@ pub(crate) fn pidfd_send_signal(fd: BorrowedFd<'_>, sig: Signal) -> io::Result<( } } +#[cfg(feature = "fs")] +#[inline] +pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> { + unsafe { ret(syscall_readonly!(__NR_pivot_root, new_root, put_old)) } +} + #[cfg(feature = "alloc")] #[inline] pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result { diff --git a/src/process/mod.rs b/src/process/mod.rs index 5fbc1f3b7..5d69e023f 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -17,6 +17,8 @@ mod membarrier; mod pidfd; #[cfg(target_os = "linux")] mod pidfd_getfd; +#[cfg(target_os = "linux")] +mod pivot_root; #[cfg(linux_kernel)] mod prctl; #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] @@ -57,6 +59,8 @@ pub use membarrier::*; pub use pidfd::*; #[cfg(target_os = "linux")] pub use pidfd_getfd::*; +#[cfg(target_os = "linux")] +pub use pivot_root::*; #[cfg(linux_kernel)] pub use prctl::*; #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] diff --git a/src/process/pivot_root.rs b/src/process/pivot_root.rs new file mode 100644 index 000000000..f04b9b1d6 --- /dev/null +++ b/src/process/pivot_root.rs @@ -0,0 +1,18 @@ +#[cfg(feature = "fs")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +use crate::{backend, io, path}; + +/// `pivot_root(new_root, put_old)`—Change the root mount. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/pivot_root.2.html +#[cfg(feature = "fs")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[inline] +pub fn pivot_root(new_root: P, put_old: Q) -> io::Result<()> { + new_root.into_with_c_str(|new_root| { + put_old.into_with_c_str(|put_old| backend::process::syscalls::pivot_root(new_root, put_old)) + }) +} From d028c72189c705b4456c5818da367b666c73a430 Mon Sep 17 00:00:00 2001 From: Ryan Seipp <12203894+ryanseipp@users.noreply.github.com> Date: Fri, 23 Aug 2024 09:29:02 -0400 Subject: [PATCH 26/54] Add io_uring flags available in linux-raw-sys (#1115) * Add io_uring flags available in linux-raw-sys * Make IORING_REGISTER_USE_REGISTERED_RING const * Add io_uring_register_with and IoringRegisterFlags IORING_REGISTER_USE_REGISTERED_RING must be ored into `opcode` when calling `io_uring_register` with a registered ring fd. The value of this flag is 1u32 << 31, and does not fit into the `IoringRegisterOp` enum. To support this use case, add a new function `io_uring_register_with` that takes a bitflags flags argument. Also adds a minimal test that calls `io_uring_register` with a registered ring fd. This operation isn't supported on older kernels, so we ensure the feature is supported before completing the test. --- src/backend/libc/io_uring/syscalls.rs | 26 ++++- src/backend/linux_raw/io_uring/syscalls.rs | 19 +++- src/io_uring.rs | 48 ++++++++- tests/io_uring/main.rs | 4 + tests/io_uring/register.rs | 108 +++++++++++++++++++++ 5 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 tests/io_uring/main.rs create mode 100644 tests/io_uring/register.rs diff --git a/src/backend/libc/io_uring/syscalls.rs b/src/backend/libc/io_uring/syscalls.rs index 8e8182401..8bf1e987a 100644 --- a/src/backend/libc/io_uring/syscalls.rs +++ b/src/backend/libc/io_uring/syscalls.rs @@ -4,7 +4,7 @@ use crate::backend::c; use crate::backend::conv::{borrowed_fd, ret_owned_fd, ret_u32}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; -use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterOp}; +use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp}; #[inline] pub(crate) fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result { @@ -40,6 +40,30 @@ pub(crate) unsafe fn io_uring_register( )) } +#[inline] +pub(crate) unsafe fn io_uring_register_with( + fd: BorrowedFd<'_>, + opcode: IoringRegisterOp, + flags: IoringRegisterFlags, + arg: *const c::c_void, + nr_args: u32, +) -> io::Result { + syscall! { + fn io_uring_register( + fd: c::c_uint, + opcode: c::c_uint, + arg: *const c::c_void, + nr_args: c::c_uint + ) via SYS_io_uring_register -> c::c_int + } + ret_u32(io_uring_register( + borrowed_fd(fd) as _, + (opcode as u32) | bitflags_bits!(flags), + arg, + nr_args, + )) +} + #[inline] pub(crate) unsafe fn io_uring_enter( fd: BorrowedFd<'_>, diff --git a/src/backend/linux_raw/io_uring/syscalls.rs b/src/backend/linux_raw/io_uring/syscalls.rs index d10cd1395..3e61b7aa6 100644 --- a/src/backend/linux_raw/io_uring/syscalls.rs +++ b/src/backend/linux_raw/io_uring/syscalls.rs @@ -8,7 +8,7 @@ use crate::backend::conv::{by_mut, c_uint, pass_usize, ret_c_uint, ret_owned_fd}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; -use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterOp}; +use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterFlags, IoringRegisterOp}; use core::ffi::c_void; #[inline] @@ -38,6 +38,23 @@ pub(crate) unsafe fn io_uring_register( )) } +#[inline] +pub(crate) unsafe fn io_uring_register_with( + fd: BorrowedFd<'_>, + opcode: IoringRegisterOp, + flags: IoringRegisterFlags, + arg: *const c_void, + nr_args: u32, +) -> io::Result { + ret_c_uint(syscall_readonly!( + __NR_io_uring_register, + fd, + c_uint((opcode as u32) | bitflags_bits!(flags)), + arg, + c_uint(nr_args) + )) +} + #[inline] pub(crate) unsafe fn io_uring_enter( fd: BorrowedFd<'_>, diff --git a/src/io_uring.rs b/src/io_uring.rs index f6fe225e5..4f003f55a 100644 --- a/src/io_uring.rs +++ b/src/io_uring.rs @@ -35,7 +35,9 @@ use linux_raw_sys::net; pub use crate::event::epoll::{ Event as EpollEvent, EventData as EpollEventData, EventFlags as EpollEventFlags, }; -pub use crate::fs::{Advice, AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, Statx, StatxFlags}; +pub use crate::fs::{ + Advice, AtFlags, Mode, OFlags, RenameFlags, ResolveFlags, Statx, StatxFlags, XattrFlags, +}; pub use crate::io::ReadWriteFlags; pub use crate::net::{RecvFlags, SendFlags, SocketFlags}; pub use crate::timespec::Timespec; @@ -115,6 +117,30 @@ pub unsafe fn io_uring_register( backend::io_uring::syscalls::io_uring_register(fd.as_fd(), opcode, arg, nr_args) } +/// `io_uring_register_with(fd, opcode, flags, arg, nr_args)`—Register files or +/// user buffers for asynchronous I/O. +/// +/// # Safety +/// +/// io_uring operates on raw pointers and raw file descriptors. Users are +/// responsible for ensuring that memory and resources are only accessed in +/// valid ways. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man.archlinux.org/man/io_uring_register.2.en +#[inline] +pub unsafe fn io_uring_register_with( + fd: Fd, + opcode: IoringRegisterOp, + flags: IoringRegisterFlags, + arg: *const c_void, + nr_args: u32, +) -> io::Result { + backend::io_uring::syscalls::io_uring_register_with(fd.as_fd(), opcode, flags, arg, nr_args) +} + /// `io_uring_enter(fd, to_submit, min_complete, flags, arg, size)`—Initiate /// and/or complete asynchronous I/O. /// @@ -260,6 +286,19 @@ pub enum IoringRegisterOp { RegisterFileAllocRange = sys::IORING_REGISTER_FILE_ALLOC_RANGE as _, } +bitflags::bitflags! { + /// `IORING_REGISTER_*` flags for use with [`io_uring_register_with`]. + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct IoringRegisterFlags: u32 { + /// `IORING_REGISTER_USE_REGISTERED_RING` + const USE_REGISTERED_RING = sys::IORING_REGISTER_USE_REGISTERED_RING as u32; + + /// + const _ = !0; + } +} + /// `IORING_OP_*` constants for use with [`io_uring_sqe`]. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[repr(u8)] @@ -511,6 +550,9 @@ bitflags::bitflags! { /// `IORING_SETUP_REGISTERED_FD_ONLY` const REGISTERED_FD_ONLY = sys::IORING_SETUP_REGISTERED_FD_ONLY; + /// `IORING_SETUP_NO_SQARRAY` + const NO_SQARRAY = sys::IORING_SETUP_NO_SQARRAY; + /// const _ = !0; } @@ -712,6 +754,9 @@ bitflags::bitflags! { /// `IORING_FEAT_LINKED_FILE` const LINKED_FILE = sys::IORING_FEAT_LINKED_FILE; + /// `IORING_FEAT_REG_REG_RING` + const REG_REG_RING = sys::IORING_FEAT_REG_REG_RING; + /// const _ = !0; } @@ -1128,6 +1173,7 @@ pub union op_flags_union { pub rename_flags: RenameFlags, pub unlink_flags: AtFlags, pub hardlink_flags: AtFlags, + pub xattr_flags: XattrFlags, pub msg_ring_flags: IoringMsgringFlags, } diff --git a/tests/io_uring/main.rs b/tests/io_uring/main.rs new file mode 100644 index 000000000..bcafe4926 --- /dev/null +++ b/tests/io_uring/main.rs @@ -0,0 +1,4 @@ +#[cfg(linux_kernel)] +#[cfg(feature = "io_uring")] +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod register; diff --git a/tests/io_uring/register.rs b/tests/io_uring/register.rs new file mode 100644 index 000000000..c8d4c1a41 --- /dev/null +++ b/tests/io_uring/register.rs @@ -0,0 +1,108 @@ +use libc::c_void; +use rustix::{ + fd::{AsFd, AsRawFd, BorrowedFd}, + io::Result, + io_uring::{ + io_uring_params, io_uring_register_with, io_uring_rsrc_update, io_uring_setup, + IoringFeatureFlags, IoringRegisterFlags, IoringRegisterOp, + }, +}; + +fn do_register( + fd: FD, + registered_fd: bool, + opcode: IoringRegisterOp, + arg: *const c_void, + arg_nr: u32, +) -> Result<()> +where + FD: AsFd, +{ + let flags = if registered_fd { + IoringRegisterFlags::USE_REGISTERED_RING + } else { + IoringRegisterFlags::default() + }; + + unsafe { + io_uring_register_with(fd, opcode, flags, arg, arg_nr)?; + } + + Ok(()) +} + +fn register_ring<'a>(fd: BorrowedFd<'a>) -> Result> { + let update = io_uring_rsrc_update { + data: fd.as_raw_fd() as u64, + offset: u32::MAX, + resv: 0, + }; + + do_register( + fd, + false, + IoringRegisterOp::RegisterRingFds, + (&update) as *const io_uring_rsrc_update as *const c_void, + 1, + )?; + + let registered_fd = unsafe { BorrowedFd::borrow_raw(update.offset as i32) }; + Ok(registered_fd) +} + +fn unregister_ring(fd: FD) -> Result<()> +where + FD: AsRawFd + AsFd, +{ + let update = io_uring_rsrc_update { + offset: fd.as_raw_fd() as u32, + data: 0, + resv: 0, + }; + + do_register( + fd, + true, + IoringRegisterOp::UnregisterRingFds, + (&update) as *const io_uring_rsrc_update as *const c_void, + 1, + )?; + + Ok(()) +} + +/// Set bounded and unbounded async kernel worker counts to 0, to test registering with registered +/// ring fd. +fn register_iowq_max_workers(fd: FD) -> Result<()> +where + FD: AsFd, +{ + let iowq_max_workers = [0u32; 2]; + do_register( + fd, + true, + IoringRegisterOp::RegisterIowqMaxWorkers, + (&iowq_max_workers) as *const [u32; 2] as *const c_void, + 2, + )?; + + Ok(()) +} + +#[test] +fn test_io_uring_register_with() { + let mut params = io_uring_params::default(); + let ring_fd = io_uring_setup(4, &mut params).unwrap(); + assert_eq!(params.sq_entries, 4); + assert_eq!(params.cq_entries, 8); + + if !params.features.contains(IoringFeatureFlags::REG_REG_RING) { + // Kernel does not support `io_uring_register` with a registered ring fd + return; + } + + let ring_fd = register_ring(ring_fd.as_fd()).unwrap(); + let register_result = register_iowq_max_workers(ring_fd); + let _ = unregister_ring(ring_fd).unwrap(); + register_result.unwrap(); +} From b0d3681ca35a0aa3ab3c2f3fcf65dc5846d145da Mon Sep 17 00:00:00 2001 From: Daniel Schemmel Date: Fri, 23 Aug 2024 16:58:52 +0100 Subject: [PATCH 27/54] Futex Rework (#1098) * Add missing futex constants * split futex into individual functions * futex: split backend into val2 and timespec versions, reorganize new functions * Add a few futex tests * prevent futex timeouts from generating dangling pointers * futex: make naming in the backend more consistent * futex: format * futex: make lock_pi2 timeout parameter match the others * return boolean from trylock_pi * mark new futex api as safe rust * use libc variables in libc backend * Revert "use libc variables in libc backend" This reverts commit d20e14dc521da20dc1c82cd85b2c9bda64ae1445. * don't use libc variables: not set for android --- src/backend/libc/thread/futex.rs | 18 +- src/backend/libc/thread/syscalls.rs | 130 ++++- src/backend/linux_raw/conv.rs | 14 +- src/backend/linux_raw/thread/futex.rs | 18 +- src/backend/linux_raw/thread/syscalls.rs | 84 +++- src/thread/futex.rs | 591 ++++++++++++++++++++++- src/thread/mod.rs | 5 +- tests/thread/futex.rs | 108 +++++ tests/thread/main.rs | 2 + 9 files changed, 919 insertions(+), 51 deletions(-) create mode 100644 tests/thread/futex.rs diff --git a/src/backend/libc/thread/futex.rs b/src/backend/libc/thread/futex.rs index 44d96f0f6..5bce85735 100644 --- a/src/backend/libc/thread/futex.rs +++ b/src/backend/libc/thread/futex.rs @@ -3,7 +3,7 @@ use crate::backend::c; bitflags::bitflags! { /// `FUTEX_*` flags for use with [`futex`]. /// - /// [`futex`]: crate::thread::futex + /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct FutexFlags: u32 { @@ -16,7 +16,7 @@ bitflags::bitflags! { /// `FUTEX_*` operations for use with [`futex`]. /// -/// [`futex`]: crate::thread::futex +/// [`futex`]: mod@crate::thread::futex #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] pub enum FutexOperation { @@ -40,4 +40,18 @@ pub enum FutexOperation { TrylockPi = bitcast!(c::FUTEX_TRYLOCK_PI), /// `FUTEX_WAIT_BITSET` WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET), + /// `FUTEX_WAKE_BITSET` + WakeBitset = bitcast!(c::FUTEX_WAKE_BITSET), + /// `FUTEX_WAIT_REQUEUE_PI` + WaitRequeuePi = bitcast!(c::FUTEX_WAIT_REQUEUE_PI), + /// `FUTEX_CMP_REQUEUE_PI` + CmpRequeuePi = bitcast!(c::FUTEX_CMP_REQUEUE_PI), + /// `FUTEX_LOCK_PI2` + LockPi2 = bitcast!(c::FUTEX_LOCK_PI2), } + +/// `FUTEX_WAITERS` +pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; + +/// `FUTEX_OWNER_DIED` +pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index df2378690..fb5c71115 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -8,12 +8,14 @@ use crate::thread::{NanosleepRelativeResult, Timespec}; #[cfg(all(target_env = "gnu", fix_y2038))] use crate::timespec::LibcTimespec; use core::mem::MaybeUninit; +use core::sync::atomic::AtomicU32; #[cfg(linux_kernel)] use { + super::futex::FutexOperation, crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize}, crate::fd::BorrowedFd, crate::pid::Pid, - crate::thread::{FutexFlags, FutexOperation}, + crate::thread::FutexFlags, crate::utils::as_mut_ptr, }; #[cfg(not(any( @@ -415,15 +417,87 @@ pub(crate) fn setresgid_thread( unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) } } -// TODO: This could be de-multiplexed. #[cfg(linux_kernel)] -pub(crate) unsafe fn futex( - uaddr: *mut u32, +pub(crate) unsafe fn futex_val2( + uaddr: *const AtomicU32, op: FutexOperation, flags: FutexFlags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + val2: u32, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + // the least significant four bytes of the timeout pointer are used as `val2`. + // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), + // so we perform that exact conversion in reverse to create the pointer. + let timeout = val2 as usize as *const Timespec; + + #[cfg(all( + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) + ))] + { + // TODO: Upstream this to the libc crate. + #[allow(non_upper_case_globals)] + const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32; + + syscall! { + fn futex_time64( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex_time64 -> c::ssize_t + } + + ret_usize(futex_time64( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout, + uaddr2, + val3, + )) + } + + #[cfg(any( + target_pointer_width = "64", + target_arch = "aarch64", + target_arch = "x86_64" + ))] + { + syscall! { + fn futex( + uaddr: *const AtomicU32, + futex_op: c::c_int, + val: u32, + timeout: *const linux_raw_sys::general::__kernel_timespec, + uaddr2: *const AtomicU32, + val3: u32 + ) via SYS_futex -> c::c_long + } + + ret_usize(futex( + uaddr, + op as i32 | flags.bits() as i32, + val, + timeout.cast(), + uaddr2, + val3, + ) as isize) + } +} + +#[cfg(linux_kernel)] +pub(crate) unsafe fn futex_timeout( + uaddr: *const AtomicU32, + op: FutexOperation, + flags: FutexFlags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { #[cfg(all( @@ -437,11 +511,11 @@ pub(crate) unsafe fn futex( syscall! { fn futex_time64( - uaddr: *mut u32, + uaddr: *const AtomicU32, futex_op: c::c_int, val: u32, timeout: *const Timespec, - uaddr2: *mut u32, + uaddr2: *const AtomicU32, val3: u32 ) via SYS_futex_time64 -> c::ssize_t } @@ -450,7 +524,7 @@ pub(crate) unsafe fn futex( uaddr, op as i32 | flags.bits() as i32, val, - utime, + timeout, uaddr2, val3, )) @@ -458,7 +532,7 @@ pub(crate) unsafe fn futex( // See the comments in `rustix_clock_gettime_via_syscall` about // emulation. if err == io::Errno::NOSYS { - futex_old(uaddr, op, flags, val, utime, uaddr2, val3) + futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) } else { Err(err) } @@ -473,11 +547,11 @@ pub(crate) unsafe fn futex( { syscall! { fn futex( - uaddr: *mut u32, + uaddr: *const AtomicU32, futex_op: c::c_int, val: u32, timeout: *const linux_raw_sys::general::__kernel_timespec, - uaddr2: *mut u32, + uaddr2: *const AtomicU32, val3: u32 ) via SYS_futex -> c::c_long } @@ -486,7 +560,7 @@ pub(crate) unsafe fn futex( uaddr, op as i32 | flags.bits() as i32, val, - utime.cast(), + timeout.cast(), uaddr2, val3, ) as isize) @@ -498,35 +572,45 @@ pub(crate) unsafe fn futex( target_pointer_width = "32", not(any(target_arch = "aarch64", target_arch = "x86_64")) ))] -unsafe fn futex_old( - uaddr: *mut u32, +unsafe fn futex_old_timespec( + uaddr: *const AtomicU32, op: FutexOperation, flags: FutexFlags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { syscall! { fn futex( - uaddr: *mut u32, + uaddr: *const AtomicU32, futex_op: c::c_int, val: u32, timeout: *const linux_raw_sys::general::__kernel_old_timespec, - uaddr2: *mut u32, + uaddr2: *const AtomicU32, val3: u32 ) via SYS_futex -> c::c_long } - let old_utime = linux_raw_sys::general::__kernel_old_timespec { - tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + let old_timeout = if timeout.is_null() { + None + } else { + Some(linux_raw_sys::general::__kernel_old_timespec { + tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: (*timeout) + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }) }; ret_usize(futex( uaddr, op as i32 | flags.bits() as i32, val, - &old_utime, + old_timeout + .as_ref() + .map(|timeout| timeout as *const linux_raw_sys::general::__kernel_old_timespec) + .unwrap_or(core::ptr::null()), uaddr2, val3, ) as isize) diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index ab85c130f..9de51de3b 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -790,11 +790,19 @@ impl<'a, Num: ArgNumber> From<(crate::net::SocketType, crate::net::SocketFlags)> } #[cfg(feature = "thread")] -impl<'a, Num: ArgNumber> From<(crate::thread::FutexOperation, crate::thread::FutexFlags)> - for ArgReg<'a, Num> +impl<'a, Num: ArgNumber> + From<( + crate::backend::thread::futex::FutexOperation, + crate::thread::FutexFlags, + )> for ArgReg<'a, Num> { #[inline] - fn from(pair: (crate::thread::FutexOperation, crate::thread::FutexFlags)) -> Self { + fn from( + pair: ( + crate::backend::thread::futex::FutexOperation, + crate::thread::FutexFlags, + ), + ) -> Self { c_uint(pair.0 as u32 | pair.1.bits()) } } diff --git a/src/backend/linux_raw/thread/futex.rs b/src/backend/linux_raw/thread/futex.rs index 263e98070..ed1a34a7e 100644 --- a/src/backend/linux_raw/thread/futex.rs +++ b/src/backend/linux_raw/thread/futex.rs @@ -1,7 +1,7 @@ bitflags::bitflags! { /// `FUTEX_*` flags for use with [`futex`]. /// - /// [`futex`]: crate::thread::futex + /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct FutexFlags: u32 { @@ -18,7 +18,7 @@ bitflags::bitflags! { /// `FUTEX_*` operations for use with [`futex`]. /// -/// [`futex`]: crate::thread::futex +/// [`futex`]: mod@crate::thread::futex #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] pub enum FutexOperation { @@ -42,4 +42,18 @@ pub enum FutexOperation { TrylockPi = linux_raw_sys::general::FUTEX_TRYLOCK_PI, /// `FUTEX_WAIT_BITSET` WaitBitset = linux_raw_sys::general::FUTEX_WAIT_BITSET, + /// `FUTEX_WAKE_BITSET` + WakeBitset = linux_raw_sys::general::FUTEX_WAKE_BITSET, + /// `FUTEX_WAIT_REQUEUE_PI` + WaitRequeuePi = linux_raw_sys::general::FUTEX_WAIT_REQUEUE_PI, + /// `FUTEX_CMP_REQUEUE_PI` + CmpRequeuePi = linux_raw_sys::general::FUTEX_CMP_REQUEUE_PI, + /// `FUTEX_LOCK_PI2` + LockPi2 = linux_raw_sys::general::FUTEX_LOCK_PI2, } + +/// `FUTEX_WAITERS` +pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; + +/// `FUTEX_OWNER_DIED` +pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index f1a8f6e71..907bb6457 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -5,6 +5,7 @@ //! See the `rustix::backend` module documentation for details. #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] +use super::futex::FutexOperation; use crate::backend::c; use crate::backend::conv::{ by_mut, by_ref, c_int, c_uint, ret, ret_c_int, ret_c_int_infallible, ret_usize, slice, @@ -13,8 +14,9 @@ use crate::backend::conv::{ use crate::fd::BorrowedFd; use crate::io; use crate::pid::Pid; -use crate::thread::{ClockId, FutexFlags, FutexOperation, NanosleepRelativeResult, Timespec}; +use crate::thread::{ClockId, FutexFlags, NanosleepRelativeResult, Timespec}; use core::mem::MaybeUninit; +use core::sync::atomic::AtomicU32; #[cfg(target_pointer_width = "32")] use linux_raw_sys::general::timespec as __kernel_old_timespec; use linux_raw_sys::general::{__kernel_timespec, TIMER_ABSTIME}; @@ -203,15 +205,53 @@ pub(crate) fn gettid() -> Pid { } } -// TODO: This could be de-multiplexed. #[inline] -pub(crate) unsafe fn futex( - uaddr: *mut u32, +pub(crate) unsafe fn futex_val2( + uaddr: *const AtomicU32, op: FutexOperation, flags: FutexFlags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + val2: u32, + uaddr2: *const AtomicU32, + val3: u32, +) -> io::Result { + // the least significant four bytes of the timeout pointer are used as `val2`. + // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), + // so we perform that exact conversion in reverse to create the pointer. + let timeout = val2 as usize as *const Timespec; + + #[cfg(target_pointer_width = "32")] + { + ret_usize(syscall!( + __NR_futex_time64, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) + } + #[cfg(target_pointer_width = "64")] + ret_usize(syscall!( + __NR_futex, + uaddr, + (op, flags), + c_uint(val), + timeout, + uaddr2, + c_uint(val3) + )) +} + +#[inline] +pub(crate) unsafe fn futex_timeout( + uaddr: *const AtomicU32, + op: FutexOperation, + flags: FutexFlags, + val: u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -221,7 +261,7 @@ pub(crate) unsafe fn futex( uaddr, (op, flags), c_uint(val), - utime, + timeout, uaddr2, c_uint(val3) )) @@ -229,7 +269,7 @@ pub(crate) unsafe fn futex( // See the comments in `rustix_clock_gettime_via_syscall` about // emulation. if err == io::Errno::NOSYS { - futex_old(uaddr, op, flags, val, utime, uaddr2, val3) + futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3) } else { Err(err) } @@ -241,32 +281,42 @@ pub(crate) unsafe fn futex( uaddr, (op, flags), c_uint(val), - utime, + timeout, uaddr2, c_uint(val3) )) } #[cfg(target_pointer_width = "32")] -unsafe fn futex_old( - uaddr: *mut u32, +unsafe fn futex_old_timespec( + uaddr: *const AtomicU32, op: FutexOperation, flags: FutexFlags, val: u32, - utime: *const Timespec, - uaddr2: *mut u32, + timeout: *const Timespec, + uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - let old_utime = __kernel_old_timespec { - tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, - tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?, + let old_timeout = if timeout.is_null() { + None + } else { + Some(__kernel_old_timespec { + tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?, + tv_nsec: (*timeout) + .tv_nsec + .try_into() + .map_err(|_| io::Errno::INVAL)?, + }) }; ret_usize(syscall!( __NR_futex, uaddr, (op, flags), c_uint(val), - by_ref(&old_utime), + old_timeout + .as_ref() + .map(|timeout| timeout as *const __kernel_old_timespec) + .unwrap_or(core::ptr::null()), uaddr2, c_uint(val3) )) diff --git a/src/thread/futex.rs b/src/thread/futex.rs index 47947c8b5..beecc431f 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -6,11 +6,25 @@ //! primitives. #![allow(unsafe_code)] +use core::num::NonZeroU32; +use core::ptr; +use core::sync::atomic::AtomicU32; + +use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; +use crate::fd::{FromRawFd, OwnedFd}; use crate::thread::Timespec; use crate::{backend, io}; -pub use backend::thread::futex::{FutexFlags, FutexOperation}; +pub use backend::thread::futex::FutexFlags; +pub use backend::thread::futex::FutexOperation; + +/// `FUTEX_WAITERS` +pub const FUTEX_WAITERS: u32 = backend::thread::futex::FUTEX_WAITERS; +/// `FUTEX_OWNER_DIED` +pub const FUTEX_OWNER_DIED: u32 = backend::thread::futex::FUTEX_OWNER_DIED; +/// DEPRECATED: There are now individual functions available to perform futex operations with improved type safety. See the [futex module](`self`). +/// /// `futex(uaddr, op, val, utime, uaddr2, val3)` /// /// # References @@ -24,6 +38,10 @@ pub use backend::thread::futex::{FutexFlags, FutexOperation}; /// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[deprecated( + since = "0.38.35", + note = "There are now individual functions available to perform futex operations with improved type safety. See the futex module." +)] #[inline] pub unsafe fn futex( uaddr: *mut u32, @@ -34,5 +52,574 @@ pub unsafe fn futex( uaddr2: *mut u32, val3: u32, ) -> io::Result { - backend::thread::syscalls::futex(uaddr, op, flags, val, utime, uaddr2, val3) + use FutexOperation::*; + + match op { + Wait | LockPi | WaitBitset | WaitRequeuePi | LockPi2 => futex_timeout( + uaddr as *const AtomicU32, + op, + flags, + val, + utime, + uaddr2 as *const AtomicU32, + val3, + ), + Wake | Fd | Requeue | CmpRequeue | WakeOp | UnlockPi | TrylockPi | WakeBitset + | CmpRequeuePi => futex_val2( + uaddr as *const AtomicU32, + op, + flags, + val, + utime as usize as u32, + uaddr2 as *const AtomicU32, + val3, + ), + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wait( + uaddr: &AtomicU32, + flags: FutexFlags, + val: u32, + timeout: Option, +) -> io::Result<()> { + unsafe { + backend::thread::syscalls::futex_timeout( + uaddr, + FutexOperation::Wait, + flags, + val, + timeout + .as_ref() + .map(|timeout| timeout as *const Timespec) + .unwrap_or(ptr::null()), + ptr::null(), + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wake(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::Wake, + flags, + val, + 0, + ptr::null(), + 0, + ) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn fd(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::Fd, + flags, + val, + 0, + ptr::null(), + 0, + ) + .map(|fd| OwnedFd::from_raw_fd(fd.try_into().expect("return value should be a valid fd"))) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_REQUEUE, val, val2, uaddr2, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn requeue( + uaddr: &AtomicU32, + flags: FutexFlags, + val: u32, + val2: u32, + uaddr2: &AtomicU32, +) -> io::Result { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::Requeue, + flags, + val, + val2, + uaddr2, + 0, + ) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn cmp_requeue( + uaddr: &AtomicU32, + flags: FutexFlags, + val: u32, + val2: u32, + uaddr2: &AtomicU32, + val3: u32, +) -> io::Result { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::CmpRequeue, + flags, + val, + val2, + uaddr2, + val3, + ) + } +} + +/// `FUTEX_OP_*` operations for use with [`wake_op`]. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum WakeOp { + /// `FUTEX_OP_SET`: `uaddr2 = oparg;` + Set = 0, + /// `FUTEX_OP_ADD`: `uaddr2 += oparg;` + Add = 1, + /// `FUTEX_OP_OR`: `uaddr2 |= oparg;` + Or = 2, + /// `FUTEX_OP_ANDN`: `uaddr2 &= ~oparg;` + AndN = 3, + /// `FUTEX_OP_XOR`: `uaddr2 ^= oparg;` + XOr = 4, + /// `FUTEX_OP_SET | FUTEX_OP_ARG_SHIFT`: `uaddr2 = (oparg << 1);` + SetShift = 0 | 8, + /// `FUTEX_OP_ADD | FUTEX_OP_ARG_SHIFT`: `uaddr2 += (oparg << 1);` + AddShift = 1 | 8, + /// `FUTEX_OP_OR | FUTEX_OP_ARG_SHIFT`: `uaddr2 |= (oparg << 1);` + OrShift = 2 | 8, + /// `FUTEX_OP_ANDN | FUTEX_OP_ARG_SHIFT`: `uaddr2 &= !(oparg << 1);` + AndNShift = 3 | 8, + /// `FUTEX_OP_XOR | FUTEX_OP_ARG_SHIFT`: `uaddr2 ^= (oparg << 1);` + XOrShift = 4 | 8, +} + +/// `FUTEX_OP_CMP_*` operations for use with [`wake_op`]. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum WakeOpCmp { + /// `FUTEX_OP_CMP_EQ`: `if oldval == cmparg { wake(); }` + Eq = 0, + /// `FUTEX_OP_CMP_EQ`: `if oldval != cmparg { wake(); }` + Ne = 1, + /// `FUTEX_OP_CMP_EQ`: `if oldval < cmparg { wake(); }` + Lt = 2, + /// `FUTEX_OP_CMP_EQ`: `if oldval <= cmparg { wake(); }` + Le = 3, + /// `FUTEX_OP_CMP_EQ`: `if oldval > cmparg { wake(); }` + Gt = 4, + /// `FUTEX_OP_CMP_EQ`: `if oldval >= cmparg { wake(); }` + Ge = 5, +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, uaddr2, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wake_op( + uaddr: &AtomicU32, + flags: FutexFlags, + val: u32, + val2: u32, + uaddr2: &AtomicU32, + op: WakeOp, + cmp: WakeOpCmp, + oparg: u16, + cmparg: u16, +) -> io::Result { + if oparg >= 1 << 12 || cmparg >= 1 << 12 { + return Err(io::Errno::INVAL); + } + + let val3 = + ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32); + + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::WakeOp, + flags, + val, + val2, + uaddr2, + val3, + ) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn lock_pi(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) -> io::Result<()> { + unsafe { + backend::thread::syscalls::futex_timeout( + uaddr, + FutexOperation::LockPi, + flags, + 0, + timeout + .as_ref() + .map(|timeout| timeout as *const Timespec) + .unwrap_or(ptr::null()), + ptr::null(), + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn unlock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result<()> { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::UnlockPi, + flags, + 0, + 0, + ptr::null(), + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_TRYLOCK_PI, 0, NULL, NULL, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn trylock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::TrylockPi, + flags, + 0, + 0, + ptr::null(), + 0, + ) + .map(|ret| ret == 0) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout/val2, NULL, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wait_bitset( + uaddr: &AtomicU32, + flags: FutexFlags, + val: u32, + timeout: Option, + val3: NonZeroU32, +) -> io::Result<()> { + unsafe { + backend::thread::syscalls::futex_timeout( + uaddr, + FutexOperation::WaitBitset, + flags, + val, + timeout + .as_ref() + .map(|timeout| timeout as *const Timespec) + .unwrap_or(ptr::null()), + ptr::null(), + val3.get(), + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wake_bitset( + uaddr: &AtomicU32, + flags: FutexFlags, + val: u32, + val3: NonZeroU32, +) -> io::Result { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::WakeBitset, + flags, + val, + 0, + ptr::null(), + val3.get(), + ) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn wait_requeue_pi( + uaddr: &AtomicU32, + flags: FutexFlags, + val: u32, + timeout: Option, + uaddr2: &AtomicU32, +) -> io::Result<()> { + unsafe { + backend::thread::syscalls::futex_timeout( + uaddr, + FutexOperation::WaitRequeuePi, + flags, + val, + timeout + .as_ref() + .map(|timeout| timeout as *const Timespec) + .unwrap_or(ptr::null()), + uaddr2, + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, val2, uaddr2, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn cmp_requeue_pi( + uaddr: &AtomicU32, + flags: FutexFlags, + val2: u32, + uaddr2: &AtomicU32, + val3: u32, +) -> io::Result { + unsafe { + backend::thread::syscalls::futex_val2( + uaddr, + FutexOperation::CmpRequeuePi, + flags, + 1, + val2, + uaddr2, + val3, + ) + } +} + +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +#[inline] +pub fn lock_pi2(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) -> io::Result<()> { + unsafe { + backend::thread::syscalls::futex_timeout( + uaddr, + FutexOperation::LockPi2, + flags, + 0, + timeout + .as_ref() + .map(|timeout| timeout as *const Timespec) + .unwrap_or(ptr::null()), + ptr::null(), + 0, + ) + .map(|val| { + debug_assert_eq!( + val, 0, + "The return value should always equal zero, if the call is successful" + ); + }) + } } diff --git a/src/thread/mod.rs b/src/thread/mod.rs index f3b8b3ebd..cb5d566f3 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -3,7 +3,7 @@ #[cfg(not(target_os = "redox"))] mod clock; #[cfg(linux_kernel)] -mod futex; +pub mod futex; #[cfg(linux_kernel)] mod id; #[cfg(linux_kernel)] @@ -16,7 +16,8 @@ mod setns; #[cfg(not(target_os = "redox"))] pub use clock::*; #[cfg(linux_kernel)] -pub use futex::{futex, FutexFlags, FutexOperation}; +#[allow(deprecated)] +pub use futex::{futex, FutexFlags, FutexOperation, FUTEX_OWNER_DIED, FUTEX_WAITERS}; #[cfg(linux_kernel)] pub use id::{ gettid, set_thread_gid, set_thread_groups, set_thread_res_gid, set_thread_res_uid, diff --git a/tests/thread/futex.rs b/tests/thread/futex.rs new file mode 100644 index 000000000..ab5b937f9 --- /dev/null +++ b/tests/thread/futex.rs @@ -0,0 +1,108 @@ +use core::sync::atomic::{AtomicU32, Ordering}; +use rustix::{ + io::Errno, + thread::{futex, FutexFlags}, +}; + +#[test] +fn test_lock_unlock_pi() { + let lock = AtomicU32::new(0); + futex::lock_pi(&lock, FutexFlags::empty(), None).unwrap(); + assert_ne!(lock.load(Ordering::SeqCst), 0); + + let err = unsafe { futex::lock_pi(&lock, FutexFlags::empty(), None).unwrap_err() }; + assert_eq!(err, Errno::DEADLK); + + futex::unlock_pi(&lock, FutexFlags::empty()).unwrap(); + assert_eq!(lock.load(Ordering::SeqCst), 0); + + let err = futex::unlock_pi(&lock, FutexFlags::empty()).unwrap_err(); + assert_eq!(err, Errno::PERM); +} + +#[cfg(feature = "std")] +#[test] +fn test_wait_wake() { + let lock = std::sync::Arc::new(AtomicU32::new(0)); + + match futex::wait(&lock, FutexFlags::empty(), 1, None) { + Ok(()) => panic!("Nobody should be waking us!"), + Err(Errno::AGAIN) => { + assert_eq!(lock.load(Ordering::SeqCst), 0, "the lock should still be 0") + } + Err(err) => panic!("{err}"), + } + + let other = std::thread::spawn({ + let lock = lock.clone(); + move || { + std::thread::sleep(std::time::Duration::from_millis(1)); + lock.store(1, Ordering::SeqCst); + futex::wake(&lock, FutexFlags::empty(), 1); + + std::thread::sleep(std::time::Duration::from_millis(50)); + match futex::wait(&lock, FutexFlags::empty(), 1, None) { + Ok(()) => (), + Err(Errno::AGAIN) => { + assert_eq!(lock.load(Ordering::SeqCst), 2, "the lock should now be 2") + } + Err(err) => panic!("{err}"), + } + } + }); + + match futex::wait(&lock, FutexFlags::empty(), 0, None) { + Ok(()) => (), + Err(Errno::AGAIN) => assert_eq!(lock.load(Ordering::SeqCst), 1, "the lock should now be 1"), + Err(err) => panic!("{err}"), + } + + lock.store(2, Ordering::SeqCst); + futex::wake(&lock, FutexFlags::empty(), 1); + + other.join().unwrap(); +} + +#[cfg(feature = "std")] +#[test] +fn test_timeout() { + use rustix::fs::Timespec; + + let lock = AtomicU32::new(0); + + let err = futex::wait( + &lock, + FutexFlags::empty(), + 0, + Some(Timespec { + tv_sec: 1, + tv_nsec: 13, + }), + ) + .unwrap_err(); + assert_eq!(err, Errno::TIMEDOUT); + + let err = futex::wait( + &lock, + FutexFlags::empty(), + 0, + Some(Timespec { + tv_sec: 0, + tv_nsec: 1_000_000_000, + }), + ) + .unwrap_err(); + assert_eq!(err, Errno::INVAL); + + let err = futex::wait( + &lock, + FutexFlags::empty(), + 0, + Some(Timespec { + tv_sec: -1, + tv_nsec: 0, + }), + ) + .unwrap_err(); + assert_eq!(err, Errno::INVAL); +} diff --git a/tests/thread/main.rs b/tests/thread/main.rs index fccf2682c..bdd02dc6c 100644 --- a/tests/thread/main.rs +++ b/tests/thread/main.rs @@ -6,6 +6,8 @@ #[cfg(not(target_os = "redox"))] mod clocks; #[cfg(linux_kernel)] +mod futex; +#[cfg(linux_kernel)] mod id; #[cfg(linux_kernel)] mod libcap; From 444088174e541f8e1cefb822a864d14b5951963b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 08:59:25 -0700 Subject: [PATCH 28/54] Add a simple example program that uses `pivot_root`. (#1117) Add an example program that uses `pivot_root`, similar to the common shell utility. --- examples/pivot_root.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/pivot_root.rs diff --git a/examples/pivot_root.rs b/examples/pivot_root.rs new file mode 100644 index 000000000..bf62fa735 --- /dev/null +++ b/examples/pivot_root.rs @@ -0,0 +1,27 @@ +//! A wrapper around `rustix::fs::pivot_root`. + +#[cfg(all(target_os = "linux", feature = "fs", feature = "process"))] +fn main() -> rustix::io::Result<()> { + let mut args = std::env::args(); + if args.len() != 3 { + eprintln!("Usage: {} new_root put_old", args.next().unwrap()); + std::process::exit(1); + } + + let _argv0 = args.next().unwrap(); + let new_root = args.next().unwrap(); + let put_old = args.next().unwrap(); + + rustix::process::pivot_root(new_root, put_old)?; + + Ok(()) +} + +#[cfg(any( + not(target_os = "linux"), + not(feature = "fs"), + not(feature = "process") +))] +fn main() -> Result<(), &'static str> { + Err("This example requires --features=fs,process and is only supported on Linux.") +} From c636c2b5dc2585cad9f0c4612f91adc406428e82 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 19:03:41 -0700 Subject: [PATCH 29/54] Fix compilation on various `cfg` configurations (#1119) Fix some clippy lints, and fix compilation on various targets with various feature flags enabled. --- src/backend/libc/c.rs | 8 ++++---- src/backend/libc/event/syscalls.rs | 2 +- src/backend/libc/io/windows_syscalls.rs | 7 +++++++ src/backend/libc/process/syscalls.rs | 4 ++-- src/backend/linux_raw/event/epoll.rs | 9 +++++---- src/backend/linux_raw/event/syscalls.rs | 7 ++++++- src/backend/linux_raw/mm/syscalls.rs | 3 +-- src/fs/mod.rs | 2 +- src/net/netdevice.rs | 9 ++++----- src/process/procctl.rs | 1 + tests/fs/file.rs | 4 ++-- tests/io_uring/register.rs | 16 +++++++--------- 12 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index 02958698c..2657dde2c 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -86,18 +86,18 @@ pub(crate) const XCASE: tcflag_t = linux_raw_sys::general::XCASE as _; #[cfg(target_os = "aix")] pub(crate) const MSG_DONTWAIT: c_int = libc::MSG_NONBLOCK; -// TODO: Remove once https://github.com/rust-lang/libc/pull/3377 is merged and released. +// TODO: Remove once is merged and released. #[cfg(target_os = "netbsd")] #[cfg(feature = "net")] pub(crate) const SO_NOSIGPIPE: c_int = 0x0800; -// It is defined as 0 in libc under 64-bit platforms, but is automatically set by kernel. -// https://github.com/torvalds/linux/blob/v6.7/fs/open.c#L1458-L1459 +// It is defined as 0 in libc under 64-bit platforms, but is automatically set +// by kernel. #[cfg(linux_kernel)] pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _; // Gated under `_LARGEFILE_SOURCE` but automatically set by the kernel. -// https://github.com/illumos/illumos-gate/blob/fb2cb638e5604b214d8ea8d4f01ad2e77b437c17/usr/src/ucbhead/sys/fcntl.h#L64 +// #[cfg(target_os = "illumos")] pub(crate) const O_LARGEFILE: c_int = 0x2000; diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 725ec8250..602979256 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -8,7 +8,7 @@ use crate::event::PollFd; #[cfg(any(linux_kernel, bsd, solarish, target_os = "espidf"))] use crate::fd::OwnedFd; use crate::io; -#[cfg(any(bsd, solarish))] +#[cfg(any(all(feature = "alloc", bsd), solarish))] use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit}; #[cfg(solarish)] use { diff --git a/src/backend/libc/io/windows_syscalls.rs b/src/backend/libc/io/windows_syscalls.rs index 049221d2f..6e451a9f1 100644 --- a/src/backend/libc/io/windows_syscalls.rs +++ b/src/backend/libc/io/windows_syscalls.rs @@ -1,6 +1,8 @@ //! Windows system calls in the `io` module. use crate::backend::c; +#[cfg(feature = "try_close")] +use crate::backend::conv::ret; use crate::backend::conv::{borrowed_fd, ret_c_int}; use crate::backend::fd::LibcFd; use crate::fd::{BorrowedFd, RawFd}; @@ -11,6 +13,11 @@ pub(crate) unsafe fn close(raw_fd: RawFd) { let _ = c::close(raw_fd as LibcFd); } +#[cfg(feature = "try_close")] +pub(crate) unsafe fn try_close(raw_fd: RawFd) -> io::Result<()> { + ret(c::close(raw_fd as LibcFd)) +} + #[inline] pub(crate) unsafe fn ioctl( fd: BorrowedFd<'_>, diff --git a/src/backend/libc/process/syscalls.rs b/src/backend/libc/process/syscalls.rs index cf0bc1852..3f2b1d950 100644 --- a/src/backend/libc/process/syscalls.rs +++ b/src/backend/libc/process/syscalls.rs @@ -5,7 +5,7 @@ use super::types::RawCpuSet; use crate::backend::c; #[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))] use crate::backend::conv::borrowed_fd; -#[cfg(feature = "fs")] +#[cfg(any(target_os = "linux", feature = "fs"))] use crate::backend::conv::c_str; #[cfg(all(feature = "alloc", feature = "fs", not(target_os = "wasi")))] use crate::backend::conv::ret_discarded_char_ptr; @@ -28,7 +28,7 @@ use crate::backend::conv::{ret, ret_c_int}; use crate::fd::BorrowedFd; #[cfg(target_os = "linux")] use crate::fd::{AsRawFd, OwnedFd, RawFd}; -#[cfg(feature = "fs")] +#[cfg(any(target_os = "linux", feature = "fs"))] use crate::ffi::CStr; #[cfg(feature = "fs")] use crate::fs::Mode; diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 73e5da149..5b2ccce96 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -172,10 +172,11 @@ pub fn create(flags: CreateFlags) -> io::Result { /// This registers interest in any of the events set in `events` occurring on /// the file descriptor associated with `data`. /// -/// Note that `close`ing a file descriptor does not necessarily unregister interest -/// which can lead to spurious events being returned from [`wait`]. If a file descriptor -/// is an `Arc`, then `epoll` can be thought to maintain a -/// `Weak` to the file descriptor. Check the [faq] for details. +/// Note that `close`ing a file descriptor does not necessarily unregister +/// interest which can lead to spurious events being returned from [`wait`]. If +/// a file descriptor is an `Arc`, then `epoll` can be +/// thought to maintain a `Weak` to the file descriptor. +/// Check the [faq] for details. /// /// # References /// diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index 0ae775339..323e09d59 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -6,10 +6,15 @@ #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use crate::backend::c; +#[cfg(any( + feature = "alloc", + not(any(target_arch = "aarch64", target_arch = "riscv64")) +))] +use crate::backend::conv::c_int; #[cfg(feature = "alloc")] use crate::backend::conv::pass_usize; use crate::backend::conv::{ - by_ref, c_int, c_uint, raw_fd, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, + by_ref, c_uint, raw_fd, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, }; use crate::event::{epoll, EventfdFlags, PollFd}; use crate::fd::{BorrowedFd, OwnedFd}; diff --git a/src/backend/linux_raw/mm/syscalls.rs b/src/backend/linux_raw/mm/syscalls.rs index 32e3a8e34..0aff9ab7e 100644 --- a/src/backend/linux_raw/mm/syscalls.rs +++ b/src/backend/linux_raw/mm/syscalls.rs @@ -16,8 +16,7 @@ use crate::backend::conv::loff_t_from_u64; use crate::backend::conv::{c_uint, no_fd, pass_usize, ret, ret_owned_fd, ret_void_star}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; -use linux_raw_sys::general::MAP_ANONYMOUS; -use linux_raw_sys::general::MREMAP_FIXED; +use linux_raw_sys::general::{MAP_ANONYMOUS, MREMAP_FIXED}; #[inline] pub(crate) fn madvise(addr: *mut c::c_void, len: usize, advice: Advice) -> io::Result<()> { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 13a6d29cd..abed4c71a 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -150,7 +150,7 @@ pub use std::os::wasi::fs::{DirEntryExt, FileExt, FileTypeExt, MetadataExt, Open /// the Unix epoch. Until the next semver bump, these unsigned fields are /// deprecated, and this trait provides accessors which return their values /// as signed integers. -#[cfg(all(unix))] +#[cfg(unix)] pub trait StatExt { /// Return the value of the `st_atime` field, casted to the correct type. fn atime(&self) -> i64; diff --git a/src/net/netdevice.rs b/src/net/netdevice.rs index b0a20433b..409d9caf3 100644 --- a/src/net/netdevice.rs +++ b/src/net/netdevice.rs @@ -2,14 +2,13 @@ //! //! The methods in this module take a socket's file descriptor to communicate //! with the kernel in their ioctl call: -//! - glibc uses an `AF_UNIX`, `AF_INET`, or `AF_INET6` socket. -//! The address family itself does not matter and glibc tries the next address -//! family if socket creation with one fails. +//! - glibc uses an `AF_UNIX`, `AF_INET`, or `AF_INET6` socket. The address +//! family itself does not matter and glibc tries the next address family if +//! socket creation with one fails. //! - Android (bionic) uses an `AF_INET` socket. //! - Both create the socket with `SOCK_DGRAM|SOCK_CLOEXEC` type/flag. //! - The [man-pages] specify, that the ioctl calls "can be used on any -//! socket's file descriptor regardless of the -//! family or type". +//! socket's file descriptor regardless of the family or type". //! //! # References //! - [Linux] diff --git a/src/process/procctl.rs b/src/process/procctl.rs index bb6372299..249fcccfb 100644 --- a/src/process/procctl.rs +++ b/src/process/procctl.rs @@ -284,6 +284,7 @@ pub fn get_reaper_status(process: ProcSelector) -> io::Result { }) } +#[cfg(feature = "alloc")] const PROC_REAP_GETPIDS: c_int = 5; bitflags! { diff --git a/tests/fs/file.rs b/tests/fs/file.rs index d80015d36..451a8132b 100644 --- a/tests/fs/file.rs +++ b/tests/fs/file.rs @@ -112,8 +112,8 @@ fn test_file() { // Test `fcntl_getfl`. let fl = rustix::fs::fcntl_getfl(&file).unwrap(); - // Clear O_LARGEFILE, which may be set by rustix on 32-bit Linux or automatically by some - // kernel on 64-bit (Linux and illumos). + // 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"))] let fl = fl - rustix::fs::OFlags::LARGEFILE; diff --git a/tests/io_uring/register.rs b/tests/io_uring/register.rs index c8d4c1a41..0f709b0ad 100644 --- a/tests/io_uring/register.rs +++ b/tests/io_uring/register.rs @@ -1,11 +1,9 @@ use libc::c_void; -use rustix::{ - fd::{AsFd, AsRawFd, BorrowedFd}, - io::Result, - io_uring::{ - io_uring_params, io_uring_register_with, io_uring_rsrc_update, io_uring_setup, - IoringFeatureFlags, IoringRegisterFlags, IoringRegisterOp, - }, +use rustix::fd::{AsFd, AsRawFd, BorrowedFd}; +use rustix::io::Result; +use rustix::io_uring::{ + io_uring_params, io_uring_register_with, io_uring_rsrc_update, io_uring_setup, + IoringFeatureFlags, IoringRegisterFlags, IoringRegisterOp, }; fn do_register( @@ -71,8 +69,8 @@ where Ok(()) } -/// Set bounded and unbounded async kernel worker counts to 0, to test registering with registered -/// ring fd. +/// Set bounded and unbounded async kernel worker counts to 0, to test +/// registering with registered ring fd. fn register_iowq_max_workers(fd: FD) -> Result<()> where FD: AsFd, From 080e0d26f755348e5e625607281cb78527469d2d Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 19:19:28 -0700 Subject: [PATCH 30/54] Misc documentation cleanups. (#1120) --- src/backend/libc/fs/syscalls.rs | 12 ++++++------ src/backend/libc/pipe/types.rs | 4 ++++ src/backend/libc/process/syscalls.rs | 4 ++-- src/backend/linux_raw/param/auxv.rs | 2 +- src/backend/linux_raw/pipe/types.rs | 4 ++++ src/backend/linux_raw/process/syscalls.rs | 4 ++-- src/fs/mod.rs | 6 ++++-- src/process/sched.rs | 2 +- src/runtime.rs | 10 +++++----- src/termios/tc.rs | 2 +- 10 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index a2204fe34..5afb2d294 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -2255,8 +2255,8 @@ pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass null - // instead. + // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass + // null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { @@ -2291,8 +2291,8 @@ pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Resul #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass null - // instead. + // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass + // null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { @@ -2328,8 +2328,8 @@ pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass null - // instead. + // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass + // null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { diff --git a/src/backend/libc/pipe/types.rs b/src/backend/libc/pipe/types.rs index 61e18d0fe..19c3e982c 100644 --- a/src/backend/libc/pipe/types.rs +++ b/src/backend/libc/pipe/types.rs @@ -37,6 +37,10 @@ bitflags! { bitflags! { /// `SPLICE_F_*` constants for use with [`splice`], [`vmsplice`], and /// [`tee`]. + /// + /// [`splice`]: crate::pipe::splice + /// [`vmsplice`]: crate::pipe::splice + /// [`tee`]: crate::pipe::tee #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct SpliceFlags: c::c_uint { diff --git a/src/backend/libc/process/syscalls.rs b/src/backend/libc/process/syscalls.rs index 3f2b1d950..b15e3117e 100644 --- a/src/backend/libc/process/syscalls.rs +++ b/src/backend/libc/process/syscalls.rs @@ -380,7 +380,7 @@ pub(crate) fn prlimit(pid: Option, limit: Resource, new: Rlimit) -> io::Res } } -/// Convert a Rust [`Rlimit`] to a C `c::rlimit`. +/// Convert a C `c::rlimit` to a Rust `Rlimit`. #[cfg(not(any( target_os = "espidf", target_os = "fuchsia", @@ -402,7 +402,7 @@ fn rlimit_from_libc(lim: c::rlimit) -> Rlimit { Rlimit { current, maximum } } -/// Convert a C `c::rlimit` to a Rust `Rlimit`. +/// Convert a Rust [`Rlimit`] to a C `c::rlimit`. #[cfg(not(any( target_os = "espidf", target_os = "fuchsia", diff --git a/src/backend/linux_raw/param/auxv.rs b/src/backend/linux_raw/param/auxv.rs index 59c7a17ad..6de662d1a 100644 --- a/src/backend/linux_raw/param/auxv.rs +++ b/src/backend/linux_raw/param/auxv.rs @@ -145,7 +145,7 @@ pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr { let mut ehdr = SYSINFO_EHDR.load(Relaxed); if ehdr.is_null() { - // Use `maybe_init_auxv` to to read the aux vectors if it can, but do + // Use `maybe_init_auxv` to read the aux vectors if it can, but do // nothing if it can't. If it can't, then we'll get a null pointer // here, which our callers are prepared to deal with. maybe_init_auxv(); diff --git a/src/backend/linux_raw/pipe/types.rs b/src/backend/linux_raw/pipe/types.rs index 2d1ed9ab9..a2a9e1cb1 100644 --- a/src/backend/linux_raw/pipe/types.rs +++ b/src/backend/linux_raw/pipe/types.rs @@ -24,6 +24,10 @@ bitflags! { bitflags! { /// `SPLICE_F_*` constants for use with [`splice`], [`vmsplice`], and /// [`tee`]. + /// + /// [`splice`]: crate::pipe::splice + /// [`vmsplice`]: crate::pipe::splice + /// [`tee`]: crate::pipe::tee #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct SpliceFlags: c::c_uint { diff --git a/src/backend/linux_raw/process/syscalls.rs b/src/backend/linux_raw/process/syscalls.rs index de4307c7c..94c4d1145 100644 --- a/src/backend/linux_raw/process/syscalls.rs +++ b/src/backend/linux_raw/process/syscalls.rs @@ -345,7 +345,7 @@ pub(crate) fn prlimit(pid: Option, limit: Resource, new: Rlimit) -> io::Res } } -/// Convert a Rust [`Rlimit`] to a C `rlimit64`. +/// Convert a C `rlimit64` to a Rust `Rlimit`. #[inline] fn rlimit_from_linux(lim: rlimit64) -> Rlimit { let current = if lim.rlim_cur == RLIM64_INFINITY as _ { @@ -361,7 +361,7 @@ fn rlimit_from_linux(lim: rlimit64) -> Rlimit { Rlimit { current, maximum } } -/// Convert a C `rlimit64` to a Rust `Rlimit`. +/// Convert a Rust [`Rlimit`] to a C `rlimit64`. #[inline] fn rlimit_to_linux(lim: Rlimit) -> rlimit64 { let rlim_cur = match lim.current { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index abed4c71a..ed32b0162 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -7,7 +7,8 @@ mod constants; #[cfg(linux_kernel)] mod copy_file_range; #[cfg(not(any(target_os = "espidf", target_os = "redox")))] -#[cfg(not(target_os = "haiku"))] // Haiku needs +#[cfg(not(target_os = "haiku"))] +// Haiku needs mod cwd; #[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] mod dir; @@ -75,7 +76,8 @@ pub use constants::*; #[cfg(linux_kernel)] pub use copy_file_range::copy_file_range; #[cfg(not(any(target_os = "espidf", target_os = "redox")))] -#[cfg(not(target_os = "haiku"))] // Haiku needs +#[cfg(not(target_os = "haiku"))] +// Haiku needs pub use cwd::*; #[cfg(all(feature = "alloc", not(any(target_os = "espidf", target_os = "redox"))))] pub use dir::{Dir, DirEntry}; diff --git a/src/process/sched.rs b/src/process/sched.rs index 211d25d1f..5551d8499 100644 --- a/src/process/sched.rs +++ b/src/process/sched.rs @@ -151,7 +151,7 @@ pub fn sched_getaffinity(pid: Option) -> io::Result { /// - [Linux] /// - [DragonFly BSD] /// -/// [Linux]: https://man7.org/linux/man-pages/man2/sched_getcpu.2.html +/// [Linux]: https://man7.org/linux/man-pages/man3/sched_getcpu.3.html /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=sched_getcpu§ion=2 // FreeBSD added `sched_getcpu` in 13.0. #[cfg(any(linux_kernel, target_os = "dragonfly"))] diff --git a/src/runtime.rs b/src/runtime.rs index 50f5d9041..72b832ded 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -408,8 +408,8 @@ pub unsafe fn sigaltstack(new: Option) -> io::Result { /// # Safety /// /// You're on your own. And on top of all the troubles with signal handlers, -/// this implementation is highly experimental. The warning about the hazard -/// of recycled thread ID's applies. +/// this implementation is highly experimental. Also, this is not `tgkill`, so +/// the warning about the hazard of recycled thread ID's applies. /// /// # References /// - [Linux] @@ -420,7 +420,7 @@ pub unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { backend::runtime::syscalls::tkill(tid, sig) } -/// `sigprocmask(how, set, oldset)`—Adjust the process signal mask. +/// `rt_sigprocmask(how, set, oldset)`—Adjust the process signal mask. /// /// # Safety /// @@ -429,10 +429,10 @@ pub unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { /// the libc `sigprocmask` in several non-obvious and unsafe ways. /// /// # References -/// - [Linux `sigprocmask`] +/// - [Linux `rt_sigprocmask`] /// - [Linux `pthread_sigmask`] /// -/// [Linux `sigprocmask`]: https://man7.org/linux/man-pages/man2/sigprocmask.2.html +/// [Linux `rt_sigprocmask`]: https://man7.org/linux/man-pages/man2/rt_sigprocmask.2.html /// [Linux `pthread_sigmask`]: https://man7.org/linux/man-pages/man3/pthread_sigmask.3.html #[inline] #[doc(alias = "pthread_sigmask")] diff --git a/src/termios/tc.rs b/src/termios/tc.rs index 4522cb82a..0f828448d 100644 --- a/src/termios/tc.rs +++ b/src/termios/tc.rs @@ -82,7 +82,7 @@ pub fn tcsetpgrp(fd: Fd, pid: Pid) -> io::Result<()> { /// `tcsetattr(fd)`—Set terminal attributes. /// -/// Also known as the `TCSETS` (or `TCSETS2 on Linux) operation with `ioctl`. +/// Also known as the `TCSETS` (or `TCSETS2` on Linux) operation with `ioctl`. /// /// # References /// - [POSIX `tcsetattr`] From a0d6f3a22aa4e205df34449e7bc79f3e958f5334 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 19:28:26 -0700 Subject: [PATCH 31/54] Remove obsolete code. (#1121) This NetBSD constant is now upstream. --- src/backend/libc/c.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index 2657dde2c..80126d27a 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -86,11 +86,6 @@ pub(crate) const XCASE: tcflag_t = linux_raw_sys::general::XCASE as _; #[cfg(target_os = "aix")] pub(crate) const MSG_DONTWAIT: c_int = libc::MSG_NONBLOCK; -// TODO: Remove once is merged and released. -#[cfg(target_os = "netbsd")] -#[cfg(feature = "net")] -pub(crate) const SO_NOSIGPIPE: c_int = 0x0800; - // It is defined as 0 in libc under 64-bit platforms, but is automatically set // by kernel. #[cfg(linux_kernel)] From 60366ea4e4957b6ace42b3eafc1d1fff838c3668 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 19:34:18 -0700 Subject: [PATCH 32/54] Add a `DROPPABLE` flag to `MapFlags`. (#1123) Fixes #1096. --- src/backend/libc/c.rs | 4 ++++ src/backend/libc/mm/types.rs | 3 +++ src/backend/linux_raw/c.rs | 3 +++ src/backend/linux_raw/mm/types.rs | 2 ++ 4 files changed, 12 insertions(+) diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index 80126d27a..216e4b7f2 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -96,6 +96,10 @@ pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _; #[cfg(target_os = "illumos")] pub(crate) const O_LARGEFILE: c_int = 0x2000; +// TODO: This is new in Linux 6.11; remove when linux-raw-sys is updated. +#[cfg(linux_kernel)] +pub(crate) const MAP_DROPPABLE: u32 = 0x8; + // On PowerPC, the regular `termios` has the `termios2` fields and there is no // `termios2`. linux-raw-sys has aliases `termios2` to `termios` to cover this // difference, but we still need to manually import it since `libc` doesn't diff --git a/src/backend/libc/mm/types.rs b/src/backend/libc/mm/types.rs index 54bc63f39..46150982e 100644 --- a/src/backend/libc/mm/types.rs +++ b/src/backend/libc/mm/types.rs @@ -240,6 +240,9 @@ bitflags! { /// `MAP_UNINITIALIZED` #[cfg(any())] const UNINITIALIZED = bitcast!(c::MAP_UNINITIALIZED); + /// `MAP_DROPPABLE` + #[cfg(linux_kernel)] + const DROPPABLE = bitcast!(c::MAP_DROPPABLE); /// const _ = !0; diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index b2cd5bdcb..6e168299c 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -317,3 +317,6 @@ mod reboot_symbols { } #[cfg(feature = "system")] pub(crate) use reboot_symbols::*; + +// TODO: This is new in Linux 6.11; remove when linux-raw-sys is updated. +pub(crate) const MAP_DROPPABLE: u32 = 0x8; diff --git a/src/backend/linux_raw/mm/types.rs b/src/backend/linux_raw/mm/types.rs index 68898f58b..6960cf7f3 100644 --- a/src/backend/linux_raw/mm/types.rs +++ b/src/backend/linux_raw/mm/types.rs @@ -105,6 +105,8 @@ bitflags! { /// `MAP_UNINITIALIZED` #[cfg(not(any(target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6")))] const UNINITIALIZED = linux_raw_sys::general::MAP_UNINITIALIZED; + /// `MAP_DROPPABLE` + const DROPPABLE = c::MAP_DROPPABLE; /// const _ = !0; From ad83f40c0f660f89e5046064f72766da064bb3ad Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 19:34:44 -0700 Subject: [PATCH 33/54] Clippy fixes. (#1122) Fix some miscellaneous warnings. --- Cargo.toml | 1 + src/backend/linux_raw/conv.rs | 4 ++-- src/backend/linux_raw/param/auxv.rs | 4 +--- src/backend/linux_raw/process/syscalls.rs | 2 +- src/process/sched.rs | 4 ++-- src/thread/clock.rs | 6 +++--- tests/thread/futex.rs | 4 ++-- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ef810dfdd..71298e899 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -278,4 +278,5 @@ check-cfg = [ 'cfg(thumb_mode)', 'cfg(wasi)', 'cfg(wasi_ext)', + 'cfg(target_arch, values("xtensa"))', ] diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index 9de51de3b..5c0fd45c2 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -107,14 +107,14 @@ pub(super) fn pass_usize<'a, Num: ArgNumber>(t: usize) -> ArgReg<'a, Num> { impl<'a, Num: ArgNumber, T> From<*mut T> for ArgReg<'a, Num> { #[inline] - fn from(c: *mut T) -> ArgReg<'a, Num> { + fn from(c: *mut T) -> Self { raw_arg(c.cast()) } } impl<'a, Num: ArgNumber, T> From<*const T> for ArgReg<'a, Num> { #[inline] - fn from(c: *const T) -> ArgReg<'a, Num> { + fn from(c: *const T) -> Self { let mut_ptr = c as *mut T; raw_arg(mut_ptr.cast()) } diff --git a/src/backend/linux_raw/param/auxv.rs b/src/backend/linux_raw/param/auxv.rs index 6de662d1a..d34384dab 100644 --- a/src/backend/linux_raw/param/auxv.rs +++ b/src/backend/linux_raw/param/auxv.rs @@ -270,9 +270,7 @@ fn init_auxv() { /// must be prepared for initialization to be skipped. #[cold] fn maybe_init_auxv() { - if let Ok(()) = init_auxv_impl() { - return; - } + let _ = init_auxv_impl(); } /// If we don't have "use-explicitly-provided-auxv" or "use-libc-auxv", we diff --git a/src/backend/linux_raw/process/syscalls.rs b/src/backend/linux_raw/process/syscalls.rs index 94c4d1145..d562aab33 100644 --- a/src/backend/linux_raw/process/syscalls.rs +++ b/src/backend/linux_raw/process/syscalls.rs @@ -313,7 +313,7 @@ pub(crate) fn getrlimit(limit: Resource) -> Rlimit { #[inline] pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> { unsafe { - let lim = rlimit_to_linux(new.clone()); + let lim = rlimit_to_linux(new); match ret(syscall_readonly!( __NR_prlimit64, c_uint(0), diff --git a/src/process/sched.rs b/src/process/sched.rs index 5551d8499..7bf4407e8 100644 --- a/src/process/sched.rs +++ b/src/process/sched.rs @@ -80,7 +80,7 @@ impl fmt::Debug for CpuSet { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "CpuSet {{")?; let mut first = true; - for i in 0..CpuSet::MAX_CPU { + for i in 0..Self::MAX_CPU { if self.is_set(i) { if first { write!(fmt, " ")?; @@ -97,7 +97,7 @@ impl fmt::Debug for CpuSet { impl hash::Hash for CpuSet { fn hash(&self, state: &mut H) { - for i in 0..CpuSet::MAX_CPU { + for i in 0..Self::MAX_CPU { self.is_set(i).hash(state); } } diff --git a/src/thread/clock.rs b/src/thread/clock.rs index eebefbdab..6b401b2a6 100644 --- a/src/thread/clock.rs +++ b/src/thread/clock.rs @@ -103,13 +103,13 @@ pub enum NanosleepRelativeResult { impl fmt::Debug for NanosleepRelativeResult { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - NanosleepRelativeResult::Ok => fmt.write_str("Ok"), - NanosleepRelativeResult::Interrupted(remaining) => write!( + Self::Ok => fmt.write_str("Ok"), + Self::Interrupted(remaining) => write!( fmt, "Interrupted(Timespec {{ tv_sec: {:?}, tv_nsec: {:?} }})", remaining.tv_sec, remaining.tv_nsec ), - NanosleepRelativeResult::Err(err) => write!(fmt, "Err({:?})", err), + Self::Err(err) => write!(fmt, "Err({:?})", err), } } } diff --git a/tests/thread/futex.rs b/tests/thread/futex.rs index ab5b937f9..918b7dac1 100644 --- a/tests/thread/futex.rs +++ b/tests/thread/futex.rs @@ -38,7 +38,7 @@ fn test_wait_wake() { move || { std::thread::sleep(std::time::Duration::from_millis(1)); lock.store(1, Ordering::SeqCst); - futex::wake(&lock, FutexFlags::empty(), 1); + futex::wake(&lock, FutexFlags::empty(), 1).unwrap(); std::thread::sleep(std::time::Duration::from_millis(50)); match futex::wait(&lock, FutexFlags::empty(), 1, None) { @@ -58,7 +58,7 @@ fn test_wait_wake() { } lock.store(2, Ordering::SeqCst); - futex::wake(&lock, FutexFlags::empty(), 1); + futex::wake(&lock, FutexFlags::empty(), 1).unwrap(); other.join().unwrap(); } From adf24e712ba5b558f9e785c52df54259196c9577 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 19:35:43 -0700 Subject: [PATCH 34/54] Switch to the well-known feature name for `docsrs`. (#1126) Switch from using a custom `doc_cfg` feature to using the existing `docsrs` feature. --- Cargo.toml | 2 -- src/backend/libc/event/epoll.rs | 2 +- src/backend/libc/fs/dir.rs | 2 +- src/backend/linux_raw/event/epoll.rs | 2 +- src/backend/linux_raw/fs/dir.rs | 2 +- src/io/is_read_write.rs | 2 +- src/lib.rs | 44 ++++++++++++++-------------- src/path/mod.rs | 2 +- src/process/chdir.rs | 4 +-- src/process/chroot.rs | 4 +-- src/process/pivot_root.rs | 4 +-- src/process/umask.rs | 2 +- src/procfs.rs | 10 +++---- src/runtime.rs | 2 +- src/termios/tty.rs | 2 +- 15 files changed, 42 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71298e899..c585a535a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,6 @@ harness = false [package.metadata.docs.rs] features = ["all-apis"] -rustdoc-args = ["--cfg", "doc_cfg"] targets = [ "x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu", @@ -262,7 +261,6 @@ check-cfg = [ 'cfg(core_ffi_c)', 'cfg(core_intrinsics)', 'cfg(criterion)', - 'cfg(doc_cfg)', 'cfg(document_experimental_runtime_api)', 'cfg(fix_y2038)', 'cfg(freebsdlike)', diff --git a/src/backend/libc/event/epoll.rs b/src/backend/libc/event/epoll.rs index 6e2ba3c12..41588df89 100644 --- a/src/backend/libc/event/epoll.rs +++ b/src/backend/libc/event/epoll.rs @@ -261,7 +261,7 @@ pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { /// For each event of interest, an element is written to `events`. On /// success, this returns the number of written elements. #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { // SAFETY: We're calling `epoll_wait` via FFI and we know how it // behaves. diff --git a/src/backend/libc/fs/dir.rs b/src/backend/libc/fs/dir.rs index 6c8274323..4b4676872 100644 --- a/src/backend/libc/fs/dir.rs +++ b/src/backend/libc/fs/dir.rs @@ -220,7 +220,7 @@ impl Dir { /// `fchdir(self)` #[cfg(feature = "process")] #[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] - #[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] + #[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[inline] pub fn chdir(&self) -> io::Result<()> { fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) }) diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 5b2ccce96..57e478e77 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -270,7 +270,7 @@ pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { /// /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html #[cfg(feature = "alloc")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] #[inline] pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { // SAFETY: We're calling `epoll_wait` via FFI and we know how it diff --git a/src/backend/linux_raw/fs/dir.rs b/src/backend/linux_raw/fs/dir.rs index a8c9a5576..429dd023d 100644 --- a/src/backend/linux_raw/fs/dir.rs +++ b/src/backend/linux_raw/fs/dir.rs @@ -234,7 +234,7 @@ impl Dir { /// `fchdir(self)` #[cfg(feature = "process")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] + #[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[inline] pub fn chdir(&self) -> io::Result<()> { fchdir(&self.fd) diff --git a/src/io/is_read_write.rs b/src/io/is_read_write.rs index af33806cb..392ec9ba2 100644 --- a/src/io/is_read_write.rs +++ b/src/io/is_read_write.rs @@ -13,7 +13,7 @@ use backend::fd::AsFd; /// /// [`is_file_read_write`]: crate::fs::is_file_read_write #[inline] -#[cfg_attr(doc_cfg, doc(cfg(all(feature = "fs", feature = "net"))))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "fs", feature = "net"))))] pub fn is_read_write(fd: Fd) -> io::Result<(bool, bool)> { backend::io::syscalls::is_read_write(fd.as_fd()) } diff --git a/src/lib.rs b/src/lib.rs index bd4465763..28b77a96f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,7 +100,7 @@ #![allow(stable_features)] #![cfg_attr(linux_raw, deny(unsafe_code))] #![cfg_attr(rustc_attrs, feature(rustc_attrs))] -#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(all(wasi_ext, target_os = "wasi", feature = "std"), feature(wasi_ext))] #![cfg_attr(core_ffi_c, feature(core_ffi_c))] #![cfg_attr(core_c_str, feature(core_c_str))] @@ -196,63 +196,63 @@ pub mod fd { // The public API modules. #[cfg(feature = "event")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "event")))] +#[cfg_attr(docsrs, doc(cfg(feature = "event")))] pub mod event; #[cfg(not(windows))] pub mod ffi; #[cfg(not(windows))] #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub mod fs; pub mod io; #[cfg(linux_kernel)] #[cfg(feature = "io_uring")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "io_uring")))] +#[cfg_attr(docsrs, doc(cfg(feature = "io_uring")))] pub mod io_uring; pub mod ioctl; #[cfg(not(any(windows, target_os = "espidf", target_os = "vita", target_os = "wasi")))] #[cfg(feature = "mm")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "mm")))] +#[cfg_attr(docsrs, doc(cfg(feature = "mm")))] pub mod mm; #[cfg(linux_kernel)] #[cfg(feature = "mount")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "mount")))] +#[cfg_attr(docsrs, doc(cfg(feature = "mount")))] pub mod mount; #[cfg(not(any(target_os = "redox", target_os = "wasi")))] #[cfg(feature = "net")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "net")))] +#[cfg_attr(docsrs, doc(cfg(feature = "net")))] pub mod net; #[cfg(not(any(windows, target_os = "espidf")))] #[cfg(feature = "param")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "param")))] +#[cfg_attr(docsrs, doc(cfg(feature = "param")))] pub mod param; #[cfg(not(windows))] #[cfg(any(feature = "fs", feature = "mount", feature = "net"))] #[cfg_attr( - doc_cfg, + docsrs, doc(cfg(any(feature = "fs", feature = "mount", feature = "net"))) )] pub mod path; #[cfg(feature = "pipe")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "pipe")))] +#[cfg_attr(docsrs, doc(cfg(feature = "pipe")))] #[cfg(not(any(windows, target_os = "wasi")))] pub mod pipe; #[cfg(not(windows))] #[cfg(feature = "process")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "process")))] +#[cfg_attr(docsrs, doc(cfg(feature = "process")))] pub mod process; #[cfg(feature = "procfs")] #[cfg(linux_kernel)] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub mod procfs; #[cfg(not(windows))] #[cfg(not(target_os = "wasi"))] #[cfg(feature = "pty")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "pty")))] +#[cfg_attr(docsrs, doc(cfg(feature = "pty")))] pub mod pty; #[cfg(not(windows))] #[cfg(feature = "rand")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] +#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] pub mod rand; #[cfg(not(any( windows, @@ -262,27 +262,27 @@ pub mod rand; target_os = "wasi" )))] #[cfg(feature = "shm")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "shm")))] +#[cfg_attr(docsrs, doc(cfg(feature = "shm")))] pub mod shm; #[cfg(not(windows))] #[cfg(feature = "stdio")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "stdio")))] +#[cfg_attr(docsrs, doc(cfg(feature = "stdio")))] pub mod stdio; #[cfg(feature = "system")] #[cfg(not(any(windows, target_os = "wasi")))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "system")))] +#[cfg_attr(docsrs, doc(cfg(feature = "system")))] pub mod system; #[cfg(not(any(windows, target_os = "vita")))] #[cfg(feature = "termios")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "termios")))] +#[cfg_attr(docsrs, doc(cfg(feature = "termios")))] pub mod termios; #[cfg(not(windows))] #[cfg(feature = "thread")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "thread")))] +#[cfg_attr(docsrs, doc(cfg(feature = "thread")))] pub mod thread; #[cfg(not(any(windows, target_os = "espidf")))] #[cfg(feature = "time")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "time")))] +#[cfg_attr(docsrs, doc(cfg(feature = "time")))] pub mod time; // "runtime" is also a public API module, but it's only for libc-like users. @@ -290,7 +290,7 @@ pub mod time; #[cfg(feature = "runtime")] #[cfg(linux_raw)] #[cfg_attr(not(document_experimental_runtime_api), doc(hidden))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "runtime")))] +#[cfg_attr(docsrs, doc(cfg(feature = "runtime")))] pub mod runtime; // Temporarily provide some mount functions for use in the fs module for @@ -315,7 +315,7 @@ pub(crate) mod mount; target_arch = "x86", ) ))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub(crate) mod fs; // Similarly, declare `path` as a non-public module if needed. diff --git a/src/path/mod.rs b/src/path/mod.rs index ae1cbc14b..54ca1886b 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -6,7 +6,7 @@ mod dec_int; pub use arg::{option_into_with_c_str, Arg}; #[cfg(feature = "itoa")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "itoa")))] +#[cfg_attr(docsrs, doc(cfg(feature = "itoa")))] pub use dec_int::DecInt; pub(crate) const SMALL_PATH_BUFFER_SIZE: usize = 256; diff --git a/src/process/chdir.rs b/src/process/chdir.rs index a68352f0e..16785bea8 100644 --- a/src/process/chdir.rs +++ b/src/process/chdir.rs @@ -21,7 +21,7 @@ use { /// [Linux]: https://man7.org/linux/man-pages/man2/chdir.2.html #[inline] #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub fn chdir(path: P) -> io::Result<()> { path.into_with_c_str(backend::process::syscalls::chdir) } @@ -52,7 +52,7 @@ pub fn fchdir(fd: Fd) -> io::Result<()> { /// [Linux]: https://man7.org/linux/man-pages/man3/getcwd.3.html #[cfg(all(feature = "alloc", feature = "fs"))] #[cfg(not(target_os = "wasi"))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[inline] pub fn getcwd>>(reuse: B) -> io::Result { _getcwd(reuse.into()) diff --git a/src/process/chroot.rs b/src/process/chroot.rs index a4fd8d852..7c2476db3 100644 --- a/src/process/chroot.rs +++ b/src/process/chroot.rs @@ -1,5 +1,5 @@ #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] use crate::{backend, io, path}; /// `chroot(path)`—Change the process root directory. @@ -9,7 +9,7 @@ use crate::{backend, io, path}; /// /// [Linux]: https://man7.org/linux/man-pages/man2/chroot.2.html #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[inline] pub fn chroot(path: P) -> io::Result<()> { path.into_with_c_str(backend::process::syscalls::chroot) diff --git a/src/process/pivot_root.rs b/src/process/pivot_root.rs index f04b9b1d6..91672774d 100644 --- a/src/process/pivot_root.rs +++ b/src/process/pivot_root.rs @@ -1,5 +1,5 @@ #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] use crate::{backend, io, path}; /// `pivot_root(new_root, put_old)`—Change the root mount. @@ -9,7 +9,7 @@ use crate::{backend, io, path}; /// /// [Linux]: https://man7.org/linux/man-pages/man2/pivot_root.2.html #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[inline] pub fn pivot_root(new_root: P, put_old: Q) -> io::Result<()> { new_root.into_with_c_str(|new_root| { diff --git a/src/process/umask.rs b/src/process/umask.rs index 01779d7ed..c8655faef 100644 --- a/src/process/umask.rs +++ b/src/process/umask.rs @@ -14,7 +14,7 @@ use crate::fs::Mode; /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html /// [Linux]: https://man7.org/linux/man-pages/man2/umask.2.html #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[inline] pub fn umask(mask: Mode) -> Mode { backend::process::syscalls::umask(mask) diff --git a/src/procfs.rs b/src/procfs.rs index e275cdcb5..59794758c 100644 --- a/src/procfs.rs +++ b/src/procfs.rs @@ -309,7 +309,7 @@ fn proc_self() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_fd() -> io::Result> { static PROC_SELF_FD: StaticFd = StaticFd::new(); @@ -378,7 +378,7 @@ fn proc_self_fdinfo() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_fdinfo_fd(fd: Fd) -> io::Result { _proc_self_fdinfo(fd.as_fd()) } @@ -406,7 +406,7 @@ fn _proc_self_fdinfo(fd: BorrowedFd<'_>) -> io::Result { /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html /// [Linux pagemap]: https://www.kernel.org/doc/Documentation/vm/pagemap.txt #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_pagemap() -> io::Result { proc_self_file(cstr!("pagemap")) } @@ -421,7 +421,7 @@ pub fn proc_self_pagemap() -> io::Result { /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_maps() -> io::Result { proc_self_file(cstr!("maps")) } @@ -436,7 +436,7 @@ pub fn proc_self_maps() -> io::Result { /// /// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] pub fn proc_self_status() -> io::Result { proc_self_file(cstr!("status")) } diff --git a/src/runtime.rs b/src/runtime.rs index 72b832ded..f965ba714 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -338,7 +338,7 @@ pub enum Fork { /// [Linux]: https://man7.org/linux/man-pages/man2/execveat.2.html #[inline] #[cfg(feature = "fs")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] pub unsafe fn execveat( dirfd: Fd, path: &CStr, diff --git a/src/termios/tty.rs b/src/termios/tty.rs index b14e602cd..1a40cf4a9 100644 --- a/src/termios/tty.rs +++ b/src/termios/tty.rs @@ -34,7 +34,7 @@ pub fn isatty(fd: Fd) -> bool { /// [Linux]: https://man7.org/linux/man-pages/man3/ttyname.3.html #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] #[cfg(all(feature = "alloc", feature = "procfs"))] -#[cfg_attr(doc_cfg, doc(cfg(feature = "procfs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "procfs")))] #[doc(alias = "ttyname_r")] #[inline] pub fn ttyname>>(dirfd: Fd, reuse: B) -> io::Result { From 3b8ffebe05bd63320fc38a63b75faac7cf3f68bf Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 19:37:38 -0700 Subject: [PATCH 35/54] Avoid using `.expect` in release mode. (#1125) This reduces code size. --- src/net/send_recv/msg.rs | 2 +- src/thread/futex.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/net/send_recv/msg.rs b/src/net/send_recv/msg.rs index 8b81c8d8f..bda8f9b9c 100644 --- a/src/net/send_recv/msg.rs +++ b/src/net/send_recv/msg.rs @@ -942,7 +942,7 @@ mod messages { let msghdr = { let mut h = msghdr::zero_msghdr(); h.msg_control = buf.as_mut_ptr().cast(); - h.msg_controllen = buf.len().try_into().expect("buffer too large for msghdr"); + h.msg_controllen = buf.len().try_into().unwrap(); h }; diff --git a/src/thread/futex.rs b/src/thread/futex.rs index beecc431f..1c89b96bb 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -11,7 +11,7 @@ use core::ptr; use core::sync::atomic::AtomicU32; use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; -use crate::fd::{FromRawFd, OwnedFd}; +use crate::fd::{FromRawFd, OwnedFd, RawFd}; use crate::thread::Timespec; use crate::{backend, io}; @@ -172,7 +172,11 @@ pub fn fd(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result ptr::null(), 0, ) - .map(|fd| OwnedFd::from_raw_fd(fd.try_into().expect("return value should be a valid fd"))) + .map(|val| { + let fd = val as RawFd; + debug_assert_eq!(fd as usize, val, "return value should be a valid fd"); + OwnedFd::from_raw_fd(fd) + }) } } From 4971c51ae055b58862c17d5c47b1e5f9a15ce1ec Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 23 Aug 2024 21:09:30 -0700 Subject: [PATCH 36/54] Fix `Mode::from_raw_mode` to mask out the `S_IFMT` bits. (#1124) The `S_IFMT` bits in a `st_mode` field value hold the file type; mask them out when converting the value to a `Mode`. `Mode` can tolerate bits it doesn't recognize, however this does prevent it from being compared for equality in the obvious way. Fixes #1104. --- src/backend/libc/fs/types.rs | 2 +- src/backend/linux_raw/fs/types.rs | 2 +- tests/fs/file.rs | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/backend/libc/fs/types.rs b/src/backend/libc/fs/types.rs index 0eb122fb0..922d20e3f 100644 --- a/src/backend/libc/fs/types.rs +++ b/src/backend/libc/fs/types.rs @@ -162,7 +162,7 @@ impl Mode { /// `Mode`. #[inline] pub const fn from_raw_mode(st_mode: RawMode) -> Self { - Self::from_bits_truncate(st_mode) + Self::from_bits_truncate(st_mode & !c::S_IFMT as RawMode) } /// Construct an `st_mode` value from a `Mode`. diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index 201ed9169..9b3c058ac 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -130,7 +130,7 @@ impl Mode { /// `Mode`. #[inline] pub const fn from_raw_mode(st_mode: RawMode) -> Self { - Self::from_bits_truncate(st_mode) + Self::from_bits_truncate(st_mode & !linux_raw_sys::general::S_IFMT) } /// Construct an `st_mode` value from a `Mode`. diff --git a/tests/fs/file.rs b/tests/fs/file.rs index 451a8132b..ff503d5fe 100644 --- a/tests/fs/file.rs +++ b/tests/fs/file.rs @@ -185,3 +185,16 @@ fn test_setfl_append() { assert_eq!(rustix::io::read(&file, &mut buf), Ok(19)); assert_eq!(&buf, b"uvwdefghijklmnopxyz\0\0\0\0\0\0\0\0\0\0\0\0\0"); } + +#[test] +fn test_mode() { + use rustix::fs::{Mode, RawMode}; + + let mode = Mode::from_raw_mode((libc::S_IFSOCK | libc::S_IRUSR) as RawMode); + assert_eq!(mode, Mode::RUSR); + assert_eq!(mode.bits(), libc::S_IRUSR as RawMode); + + let mode = Mode::from_raw_mode((libc::S_IFSOCK | libc::S_IRWXU) as RawMode); + assert_eq!(mode, Mode::RWXU); + assert_eq!(mode.bits(), libc::S_IRWXU as RawMode); +} From 25c54dc766b57eb3f1db5090ff1290b041280780 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 26 Aug 2024 06:21:57 -0700 Subject: [PATCH 37/54] Miscellaneous clippy fixes. (#1128) --- src/backend/libc/event/epoll.rs | 5 +++++ src/backend/libc/event/poll_fd.rs | 4 ++-- src/backend/libc/net/addr.rs | 8 ++++---- src/backend/libc/net/sockopt.rs | 3 ++- src/backend/libc/pipe/syscalls.rs | 6 +++--- src/backend/linux_raw/event/epoll.rs | 5 +++++ src/backend/linux_raw/io/syscalls.rs | 2 +- src/backend/linux_raw/net/addr.rs | 8 ++++---- src/backend/linux_raw/net/netdevice.rs | 5 +++-- src/backend/linux_raw/net/sockopt.rs | 7 ++++--- src/backend/linux_raw/pipe/syscalls.rs | 6 +++--- src/backend/linux_raw/vdso_wrappers.rs | 1 + src/bitcast.rs | 7 ++++++- src/buffer.rs | 2 +- src/fs/fd.rs | 4 ++-- src/io/errno.rs | 12 ++++++------ src/io/read_write.rs | 7 ++++--- src/io_uring.rs | 4 ++-- src/ioctl/mod.rs | 7 ++++--- src/net/send_recv/mod.rs | 9 +++++++-- src/net/socket_addr_any.rs | 10 +++++----- src/path/arg.rs | 4 ++-- src/path/dec_int.rs | 4 ++-- src/process/ioctl.rs | 2 +- src/process/sched.rs | 12 ++++++------ src/rand/getrandom.rs | 2 +- src/system.rs | 6 +++--- src/thread/clock.rs | 8 ++++---- src/thread/prctl.rs | 2 +- tests/backends.rs | 2 +- tests/fs/readdir.rs | 12 ++++++------ tests/fs/readlinkat.rs | 6 +++--- tests/io_uring/register.rs | 4 ++-- tests/termios/isatty.rs | 4 ++-- 34 files changed, 108 insertions(+), 82 deletions(-) diff --git a/src/backend/libc/event/epoll.rs b/src/backend/libc/event/epoll.rs index 41588df89..267f8e7ec 100644 --- a/src/backend/libc/event/epoll.rs +++ b/src/backend/libc/event/epoll.rs @@ -294,6 +294,11 @@ impl<'a> Iterator for Iter<'a> { fn next(&mut self) -> Option { self.iter.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } } /// A record of an event that occurred. diff --git a/src/backend/libc/event/poll_fd.rs b/src/backend/libc/event/poll_fd.rs index 26df15b2c..ba1bab0b5 100644 --- a/src/backend/libc/event/poll_fd.rs +++ b/src/backend/libc/event/poll_fd.rs @@ -63,8 +63,8 @@ pub struct PollFd<'fd> { } impl<'fd> fmt::Debug for PollFd<'fd> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("PollFd") + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PollFd") .field("fd", &self.pollfd.fd) .field("events", &self.pollfd.events) .field("revents", &self.pollfd.revents) diff --git a/src/backend/libc/net/addr.rs b/src/backend/libc/net/addr.rs index d07ee0fba..bef45e44e 100644 --- a/src/backend/libc/net/addr.rs +++ b/src/backend/libc/net/addr.rs @@ -194,16 +194,16 @@ impl Hash for SocketAddrUnix { #[cfg(unix)] impl fmt::Debug for SocketAddrUnix { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(path) = self.path() { - path.fmt(fmt) + path.fmt(f) } else { #[cfg(linux_kernel)] if let Some(name) = self.abstract_name() { - return name.fmt(fmt); + return name.fmt(f); } - "(unnamed)".fmt(fmt) + "(unnamed)".fmt(f) } } } diff --git a/src/backend/libc/net/sockopt.rs b/src/backend/libc/net/sockopt.rs index 4a5499dce..7824e69a1 100644 --- a/src/backend/libc/net/sockopt.rs +++ b/src/backend/libc/net/sockopt.rs @@ -915,9 +915,10 @@ pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result< ))] #[inline] pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result { + const OPTLEN: c::socklen_t = 16; + let level = c::IPPROTO_TCP; let optname = c::TCP_CONGESTION; - const OPTLEN: c::socklen_t = 16; let mut value = MaybeUninit::<[MaybeUninit; OPTLEN as usize]>::uninit(); let mut optlen = OPTLEN; getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; diff --git a/src/backend/libc/pipe/syscalls.rs b/src/backend/libc/pipe/syscalls.rs index cff932d55..b36a18141 100644 --- a/src/backend/libc/pipe/syscalls.rs +++ b/src/backend/libc/pipe/syscalls.rs @@ -54,7 +54,7 @@ pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> { #[cfg(linux_kernel)] #[inline] -pub fn splice( +pub(crate) fn splice( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, @@ -79,7 +79,7 @@ pub fn splice( #[cfg(linux_kernel)] #[inline] -pub unsafe fn vmsplice( +pub(crate) unsafe fn vmsplice( fd: BorrowedFd<'_>, bufs: &[IoSliceRaw<'_>], flags: SpliceFlags, @@ -94,7 +94,7 @@ pub unsafe fn vmsplice( #[cfg(linux_kernel)] #[inline] -pub fn tee( +pub(crate) fn tee( fd_in: BorrowedFd<'_>, fd_out: BorrowedFd<'_>, len: usize, diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 57e478e77..7639a6c2c 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -304,6 +304,11 @@ impl<'a> Iterator for Iter<'a> { fn next(&mut self) -> Option { self.iter.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } } /// A record of an event that occurred. diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index 484da991f..059ad7abd 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -277,7 +277,7 @@ pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> { match unsafe { crate::backend::net::syscalls::recv( fd, - buf.as_mut_ptr() as *mut u8, + buf.as_mut_ptr().cast::(), 1, RecvFlags::PEEK | RecvFlags::DONTWAIT, ) diff --git a/src/backend/linux_raw/net/addr.rs b/src/backend/linux_raw/net/addr.rs index 3c6af46b7..5bc84ed69 100644 --- a/src/backend/linux_raw/net/addr.rs +++ b/src/backend/linux_raw/net/addr.rs @@ -152,13 +152,13 @@ impl Hash for SocketAddrUnix { } impl fmt::Debug for SocketAddrUnix { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(path) = self.path() { - path.fmt(fmt) + path.fmt(f) } else if let Some(name) = self.abstract_name() { - name.fmt(fmt) + name.fmt(f) } else { - "(unnamed)".fmt(fmt) + "(unnamed)".fmt(f) } } } diff --git a/src/backend/linux_raw/net/netdevice.rs b/src/backend/linux_raw/net/netdevice.rs index c312dfb5c..6747e894b 100644 --- a/src/backend/linux_raw/net/netdevice.rs +++ b/src/backend/linux_raw/net/netdevice.rs @@ -3,6 +3,7 @@ use crate::backend::io::syscalls::ioctl; use crate::fd::AsFd; use crate::io; +use core::ptr::addr_of_mut; use core::{slice, str}; use linux_raw_sys::ctypes::c_char; use linux_raw_sys::ioctl::SIOCGIFINDEX; @@ -32,7 +33,7 @@ pub(crate) fn name_to_index(fd: impl AsFd, if_name: &str) -> io::Result { }; unsafe { ifreq.ifr_ifrn.ifrn_name[..if_name_bytes.len()].copy_from_slice(if_name_bytes) }; - unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX, &mut ifreq as *mut ifreq as _) }?; + unsafe { ioctl(fd.as_fd(), SIOCGIFINDEX, addr_of_mut!(ifreq).cast()) }?; let index = unsafe { ifreq.ifr_ifru.ifru_ivalue }; Ok(index as u32) } @@ -46,7 +47,7 @@ pub(crate) fn index_to_name(fd: impl AsFd, index: u32) -> io::Result { }, }; - unsafe { ioctl(fd.as_fd(), SIOCGIFNAME, &mut ifreq as *mut ifreq as _) }?; + unsafe { ioctl(fd.as_fd(), SIOCGIFNAME, addr_of_mut!(ifreq).cast()) }?; if let Some(nul_byte) = unsafe { ifreq.ifr_ifrn.ifrn_name } .iter() diff --git a/src/backend/linux_raw/net/sockopt.rs b/src/backend/linux_raw/net/sockopt.rs index 1cc3b744c..642f9f3eb 100644 --- a/src/backend/linux_raw/net/sockopt.rs +++ b/src/backend/linux_raw/net/sockopt.rs @@ -709,7 +709,7 @@ pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Re #[inline] pub(crate) fn get_tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result { let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPIDLE)?; - Ok(Duration::from_secs(secs as u64)) + Ok(Duration::from_secs(secs.into())) } #[inline] @@ -721,7 +721,7 @@ pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::R #[inline] pub(crate) fn get_tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result { let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?; - Ok(Duration::from_secs(secs as u64)) + Ok(Duration::from_secs(secs.into())) } #[inline] @@ -755,9 +755,10 @@ pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result< #[cfg(feature = "alloc")] #[inline] pub(crate) fn get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result { + const OPTLEN: c::socklen_t = 16; + let level = c::IPPROTO_TCP; let optname = c::TCP_CONGESTION; - const OPTLEN: c::socklen_t = 16; let mut value = MaybeUninit::<[MaybeUninit; OPTLEN as usize]>::uninit(); let mut optlen = OPTLEN; getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; diff --git a/src/backend/linux_raw/pipe/syscalls.rs b/src/backend/linux_raw/pipe/syscalls.rs index ec3e459be..19039b42f 100644 --- a/src/backend/linux_raw/pipe/syscalls.rs +++ b/src/backend/linux_raw/pipe/syscalls.rs @@ -57,7 +57,7 @@ pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> { } #[inline] -pub fn splice( +pub(crate) fn splice( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, @@ -79,7 +79,7 @@ pub fn splice( } #[inline] -pub unsafe fn vmsplice( +pub(crate) unsafe fn vmsplice( fd: BorrowedFd<'_>, bufs: &[IoSliceRaw<'_>], flags: SpliceFlags, @@ -89,7 +89,7 @@ pub unsafe fn vmsplice( } #[inline] -pub fn tee( +pub(crate) fn tee( fd_in: BorrowedFd<'_>, fd_out: BorrowedFd<'_>, len: usize, diff --git a/src/backend/linux_raw/vdso_wrappers.rs b/src/backend/linux_raw/vdso_wrappers.rs index 441738f8d..69309a101 100644 --- a/src/backend/linux_raw/vdso_wrappers.rs +++ b/src/backend/linux_raw/vdso_wrappers.rs @@ -8,6 +8,7 @@ //! passes them uninitialized memory buffers. This file also calls vDSO //! functions. #![allow(unsafe_code)] +#![allow(clippy::missing_transmute_annotations)] #[cfg(target_arch = "x86")] use super::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0}; diff --git a/src/bitcast.rs b/src/bitcast.rs index 77e0e6338..11ae24513 100644 --- a/src/bitcast.rs +++ b/src/bitcast.rs @@ -14,7 +14,12 @@ macro_rules! bitcast { } else if false { // Ensure that the source and destinations are the same size. // SAFETY: This code is under an `if false`. - #[allow(unsafe_code, unused_unsafe, clippy::useless_transmute)] + #[allow( + unsafe_code, + unused_unsafe, + clippy::useless_transmute, + clippy::missing_transmute_annotations + )] unsafe { ::core::mem::transmute($x) } diff --git a/src/buffer.rs b/src/buffer.rs index e4b40c739..d7c0924ce 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -16,6 +16,6 @@ pub(super) unsafe fn split_init( init: usize, ) -> (&mut [u8], &mut [MaybeUninit]) { let (init, uninit) = buf.split_at_mut(init); - let init = slice::from_raw_parts_mut(init.as_mut_ptr() as *mut u8, init.len()); + let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::(), init.len()); (init, uninit) } diff --git a/src/fs/fd.rs b/src/fs/fd.rs index 1aa0e6352..8dc35d1a5 100644 --- a/src/fs/fd.rs +++ b/src/fs/fd.rs @@ -59,8 +59,8 @@ pub struct Timestamps { } impl fmt::Debug for Timestamps { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("Timestamps") + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Timestamps") .field("last_access.tv_sec", &self.last_access.tv_sec) .field("last_access.tv_nsec", &self.last_access.tv_nsec) .field("last_modification.tv_sec", &self.last_modification.tv_sec) diff --git a/src/io/errno.rs b/src/io/errno.rs index 2b72de005..2cfaef70d 100644 --- a/src/io/errno.rs +++ b/src/io/errno.rs @@ -24,27 +24,27 @@ impl Errno { } impl fmt::Display for Errno { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "std")] { - std::io::Error::from(*self).fmt(fmt) + std::io::Error::from(*self).fmt(f) } #[cfg(not(feature = "std"))] { - write!(fmt, "os error {}", self.raw_os_error()) + write!(f, "os error {}", self.raw_os_error()) } } } impl fmt::Debug for Errno { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "std")] { - std::io::Error::from(*self).fmt(fmt) + std::io::Error::from(*self).fmt(f) } #[cfg(not(feature = "std"))] { - write!(fmt, "os error {}", self.raw_os_error()) + write!(f, "os error {}", self.raw_os_error()) } } } diff --git a/src/io/read_write.rs b/src/io/read_write.rs index 94a31ef2a..c2cc1122d 100644 --- a/src/io/read_write.rs +++ b/src/io/read_write.rs @@ -55,8 +55,9 @@ pub fn read_uninit( buf: &mut [MaybeUninit], ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { // Get number of initialized bytes. - let length = - unsafe { backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len()) }; + let length = unsafe { + backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len()) + }; // Split into the initialized and uninitialized portions. Ok(unsafe { split_init(buf, length?) }) @@ -129,7 +130,7 @@ pub fn pread_uninit( offset: u64, ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { let length = unsafe { - backend::io::syscalls::pread(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), offset) + backend::io::syscalls::pread(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len(), offset) }; Ok(unsafe { split_init(buf, length?) }) } diff --git a/src/io_uring.rs b/src/io_uring.rs index 4f003f55a..e1b13d3c6 100644 --- a/src/io_uring.rs +++ b/src/io_uring.rs @@ -1059,10 +1059,10 @@ impl Default for io_uring_user_data { } impl core::fmt::Debug for io_uring_user_data { - fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { // SAFETY: Just format as a `u64`, since formatting doesn't preserve // provenance, and we don't have a discriminant. - unsafe { self.u64_.fmt(fmt) } + unsafe { self.u64_.fmt(f) } } } diff --git a/src/ioctl/mod.rs b/src/ioctl/mod.rs index 30646efe9..2f0c95188 100644 --- a/src/ioctl/mod.rs +++ b/src/ioctl/mod.rs @@ -217,9 +217,10 @@ impl Opcode { number: u8, data_size: usize, ) -> Self { - if data_size > RawOpcode::MAX as usize { - panic!("data size is too large"); - } + assert!( + data_size <= RawOpcode::MAX as usize, + "data size is too large" + ); Self::old(platform::compose_opcode( direction, diff --git a/src/net/send_recv/mod.rs b/src/net/send_recv/mod.rs index 477089831..2bbf0cf7d 100644 --- a/src/net/send_recv/mod.rs +++ b/src/net/send_recv/mod.rs @@ -78,7 +78,7 @@ pub fn recv_uninit( flags: RecvFlags, ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { let length = unsafe { - backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), flags) + backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len(), flags) }; Ok(unsafe { split_init(buf, length?) }) @@ -168,7 +168,12 @@ pub fn recvfrom_uninit( flags: RecvFlags, ) -> io::Result<(&mut [u8], &mut [MaybeUninit], Option)> { let (length, addr) = unsafe { - backend::net::syscalls::recvfrom(fd.as_fd(), buf.as_mut_ptr() as *mut u8, buf.len(), flags)? + backend::net::syscalls::recvfrom( + fd.as_fd(), + buf.as_mut_ptr().cast::(), + buf.len(), + flags, + )? }; let (init, uninit) = unsafe { split_init(buf, length) }; Ok((init, uninit, addr)) diff --git a/src/net/socket_addr_any.rs b/src/net/socket_addr_any.rs index 3be80a3ad..b43d09667 100644 --- a/src/net/socket_addr_any.rs +++ b/src/net/socket_addr_any.rs @@ -109,14 +109,14 @@ impl SocketAddrAny { #[cfg(feature = "std")] impl fmt::Debug for SocketAddrAny { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::V4(v4) => v4.fmt(fmt), - Self::V6(v6) => v6.fmt(fmt), + Self::V4(v4) => v4.fmt(f), + Self::V6(v6) => v6.fmt(f), #[cfg(unix)] - Self::Unix(unix) => unix.fmt(fmt), + Self::Unix(unix) => unix.fmt(f), #[cfg(target_os = "linux")] - Self::Xdp(xdp) => xdp.fmt(fmt), + Self::Xdp(xdp) => xdp.fmt(f), } } } diff --git a/src/path/arg.rs b/src/path/arg.rs index d1e5326a8..5eb4709bf 100644 --- a/src/path/arg.rs +++ b/src/path/arg.rs @@ -90,9 +90,9 @@ pub trait Arg { } /// Runs a closure on `arg` where `A` is mapped to a `&CStr` -pub fn option_into_with_c_str(arg: Option, f: F) -> io::Result +pub fn option_into_with_c_str(arg: Option, f: F) -> io::Result where - A: Sized, + A: Arg + Sized, F: FnOnce(Option<&CStr>) -> io::Result, { if let Some(arg) = arg { diff --git a/src/path/dec_int.rs b/src/path/dec_int.rs index e9c46f49a..985d504ce 100644 --- a/src/path/dec_int.rs +++ b/src/path/dec_int.rs @@ -114,7 +114,7 @@ impl AsRef for DecInt { #[cfg(feature = "std")] impl fmt::Debug for DecInt { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_str().fmt(fmt) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) } } diff --git a/src/process/ioctl.rs b/src/process/ioctl.rs index 5afc76609..1b617035f 100644 --- a/src/process/ioctl.rs +++ b/src/process/ioctl.rs @@ -40,7 +40,7 @@ unsafe impl ioctl::Ioctl for Tiocsctty { const OPCODE: ioctl::Opcode = ioctl::Opcode::old(c::TIOCSCTTY as ioctl::RawOpcode); fn as_ptr(&mut self) -> *mut c::c_void { - (&0u32) as *const u32 as *mut c::c_void + (&0_u32) as *const u32 as *mut c::c_void } unsafe fn output_from_ptr( diff --git a/src/process/sched.rs b/src/process/sched.rs index 7bf4407e8..f9614e8a0 100644 --- a/src/process/sched.rs +++ b/src/process/sched.rs @@ -77,21 +77,21 @@ impl Default for CpuSet { } impl fmt::Debug for CpuSet { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "CpuSet {{")?; + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CpuSet {{")?; let mut first = true; for i in 0..Self::MAX_CPU { if self.is_set(i) { if first { - write!(fmt, " ")?; + write!(f, " ")?; first = false; } else { - write!(fmt, ", ")?; + write!(f, ", ")?; } - write!(fmt, "cpu{}", i)?; + write!(f, "cpu{}", i)?; } } - write!(fmt, " }}") + write!(f, " }}") } } diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs index 6958fa7ca..760e04f62 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -38,7 +38,7 @@ pub fn getrandom_uninit( ) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { // Get number of initialized bytes. let length = unsafe { - backend::rand::syscalls::getrandom(buf.as_mut_ptr() as *mut u8, buf.len(), flags) + backend::rand::syscalls::getrandom(buf.as_mut_ptr().cast::(), buf.len(), flags) }; // Split into the initialized and uninitialized portions. diff --git a/src/system.rs b/src/system.rs index c1b9eaffe..9fad16df2 100644 --- a/src/system.rs +++ b/src/system.rs @@ -109,11 +109,11 @@ impl Uname { } impl fmt::Debug for Uname { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(linux_kernel))] { write!( - fmt, + f, "{:?} {:?} {:?} {:?} {:?}", self.sysname(), self.nodename(), @@ -125,7 +125,7 @@ impl fmt::Debug for Uname { #[cfg(linux_kernel)] { write!( - fmt, + f, "{:?} {:?} {:?} {:?} {:?} {:?}", self.sysname(), self.nodename(), diff --git a/src/thread/clock.rs b/src/thread/clock.rs index 6b401b2a6..2db7d3ab1 100644 --- a/src/thread/clock.rs +++ b/src/thread/clock.rs @@ -101,15 +101,15 @@ pub enum NanosleepRelativeResult { } impl fmt::Debug for NanosleepRelativeResult { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Ok => fmt.write_str("Ok"), + Self::Ok => f.write_str("Ok"), Self::Interrupted(remaining) => write!( - fmt, + f, "Interrupted(Timespec {{ tv_sec: {:?}, tv_nsec: {:?} }})", remaining.tv_sec, remaining.tv_nsec ), - Self::Err(err) => write!(fmt, "Err({:?})", err), + Self::Err(err) => write!(f, "Err({:?})", err), } } } diff --git a/src/thread/prctl.rs b/src/thread/prctl.rs index 9d9e6b2d5..c6d5198ae 100644 --- a/src/thread/prctl.rs +++ b/src/thread/prctl.rs @@ -367,7 +367,7 @@ pub enum Capability { /// - `bpf_probe_read` to read arbitrary kernel memory is allowed /// - `bpf_trace_printk` to print kernel memory is allowed /// - /// [`Capability::SystemAdmin`] is required to use bpf_probe_write_user. + /// [`Capability::SystemAdmin`] is required to use `bpf_probe_write_user`. /// /// [`Capability::SystemAdmin`] is required to iterate system-wide loaded /// programs, maps, links, and BTFs, and convert their IDs to file diff --git a/tests/backends.rs b/tests/backends.rs index 085d8e90a..230f175b7 100644 --- a/tests/backends.rs +++ b/tests/backends.rs @@ -111,7 +111,7 @@ fn has_dependency( .arg("tree") .arg("--quiet") .arg("--edges=normal") - .arg(&format!("--invert={}", dependency)) + .arg(format!("--invert={}", dependency)) .current_dir(dir); command.args(args); diff --git a/tests/fs/readdir.rs b/tests/fs/readdir.rs index 8b310f992..92db4ea84 100644 --- a/tests/fs/readdir.rs +++ b/tests/fs/readdir.rs @@ -18,7 +18,7 @@ fn dir_entries() { let entries = read_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains_key("file1"), "directory contains `file1`: {:?}", entries ); @@ -27,12 +27,12 @@ fn dir_entries() { let _f2 = File::create(tmpdir.path().join("file2")).expect("create file1"); let entries = read_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains_key("file1"), "directory contains `file1`: {:?}", entries ); assert!( - entries.get("file2").is_some(), + entries.contains_key("file2"), "directory contains `file2`: {:?}", entries ); @@ -88,7 +88,7 @@ fn test_raw_dir(buf: &mut [MaybeUninit]) { dirfd.seek(SeekFrom::Start(0)).unwrap(); let entries = read_raw_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains("file1"), "directory contains `file1`: {:?}", entries ); @@ -98,12 +98,12 @@ fn test_raw_dir(buf: &mut [MaybeUninit]) { dirfd.seek(SeekFrom::Start(0)).unwrap(); let entries = read_raw_entries(&mut dir); assert!( - entries.get("file1").is_some(), + entries.contains("file1"), "directory contains `file1`: {:?}", entries ); assert!( - entries.get("file2").is_some(), + entries.contains("file2"), "directory contains `file2`: {:?}", entries ); diff --git a/tests/fs/readlinkat.rs b/tests/fs/readlinkat.rs index 9345cd0e4..80a786c4d 100644 --- a/tests/fs/readlinkat.rs +++ b/tests/fs/readlinkat.rs @@ -75,7 +75,7 @@ fn test_readlinkat_raw() { assert!(!no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "link", &mut short).unwrap(); - assert_eq!(yes, &[b'f', b'i']); + assert_eq!(yes, b"fi"); assert!(no.is_empty()); symlinkat("link", &dir, "another").unwrap(); @@ -85,7 +85,7 @@ fn test_readlinkat_raw() { assert!(!no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "link", &mut short).unwrap(); - assert_eq!(yes, &[b'f', b'i']); + assert_eq!(yes, b"fi"); assert!(no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "another", &mut some).unwrap(); @@ -93,6 +93,6 @@ fn test_readlinkat_raw() { assert!(!no.is_empty()); let (yes, no) = readlinkat_raw(&dir, "another", &mut short).unwrap(); - assert_eq!(yes, &[b'l', b'i']); + assert_eq!(yes, b"li"); assert!(no.is_empty()); } diff --git a/tests/io_uring/register.rs b/tests/io_uring/register.rs index 0f709b0ad..929241e76 100644 --- a/tests/io_uring/register.rs +++ b/tests/io_uring/register.rs @@ -29,7 +29,7 @@ where Ok(()) } -fn register_ring<'a>(fd: BorrowedFd<'a>) -> Result> { +fn register_ring(fd: BorrowedFd<'_>) -> Result> { let update = io_uring_rsrc_update { data: fd.as_raw_fd() as u64, offset: u32::MAX, @@ -101,6 +101,6 @@ fn test_io_uring_register_with() { let ring_fd = register_ring(ring_fd.as_fd()).unwrap(); let register_result = register_iowq_max_workers(ring_fd); - let _ = unregister_ring(ring_fd).unwrap(); + unregister_ring(ring_fd).unwrap(); register_result.unwrap(); } diff --git a/tests/termios/isatty.rs b/tests/termios/isatty.rs index 605bf123c..cb9982dfe 100644 --- a/tests/termios/isatty.rs +++ b/tests/termios/isatty.rs @@ -11,10 +11,10 @@ fn tmpdir() -> TempDir { fn std_file_is_not_terminal() { let tmpdir = tempfile::tempdir().unwrap(); assert!(!isatty( - &std::fs::File::create(tmpdir.path().join("file")).unwrap() + std::fs::File::create(tmpdir.path().join("file")).unwrap() )); assert!(!isatty( - &std::fs::File::open(tmpdir.path().join("file")).unwrap() + std::fs::File::open(tmpdir.path().join("file")).unwrap() )); } From 2c926a200466a3b9dd825645194f4cf76ee8cdff Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 26 Aug 2024 06:22:19 -0700 Subject: [PATCH 38/54] Miscellaneous documentation fixes. (#1129) --- Cargo.toml | 4 +- src/backend/libc/c.rs | 5 +- src/backend/libc/event/epoll.rs | 39 +++++- src/backend/libc/fs/syscalls.rs | 20 +-- src/backend/libc/io/errno.rs | 1 - src/backend/libc/net/netdevice.rs | 2 + src/backend/libc/net/send_recv.rs | 4 +- src/backend/libc/net/syscalls.rs | 8 +- src/backend/libc/process/syscalls.rs | 4 +- src/backend/libc/termios/syscalls.rs | 2 +- src/backend/linux_raw/arch/x86.rs | 1 + src/backend/linux_raw/event/epoll.rs | 7 +- src/backend/linux_raw/fs/syscalls.rs | 2 +- src/backend/linux_raw/fs/types.rs | 2 +- src/backend/linux_raw/io/errno.rs | 1 - src/backend/linux_raw/net/netdevice.rs | 2 + src/backend/linux_raw/net/send_recv.rs | 4 +- src/backend/linux_raw/param/auxv.rs | 8 +- src/backend/linux_raw/runtime/syscalls.rs | 4 +- src/backend/linux_raw/termios/syscalls.rs | 2 +- src/clockid.rs | 4 +- src/event/kqueue.rs | 4 +- src/fs/ioctl.rs | 12 +- src/io/dup.rs | 2 +- src/io/fcntl.rs | 2 + src/io/ioctl.rs | 6 +- src/ioctl/bsd.rs | 4 +- src/ioctl/linux.rs | 4 +- src/mount/mount_unmount.rs | 6 +- src/net/netdevice.rs | 6 +- src/net/send_recv/msg.rs | 2 + src/net/socket.rs | 4 +- src/net/sockopt.rs | 7 +- src/net/types.rs | 2 +- src/path/arg.rs | 5 +- src/path/dec_int.rs | 2 + src/process/prctl.rs | 156 +++++++++++----------- src/process/procctl.rs | 72 +++++----- src/process/wait.rs | 6 +- src/procfs.rs | 2 +- src/pty.rs | 2 +- src/rand/getrandom.rs | 2 + src/runtime.rs | 33 +++-- src/stdio.rs | 4 +- src/system.rs | 8 +- src/termios/ioctl.rs | 4 +- src/thread/prctl.rs | 139 ++++++++++--------- tests/event/poll.rs | 2 +- tests/fs/seek.rs | 2 +- tests/net/sockopt.rs | 35 ++--- tests/net/unix.rs | 3 +- tests/net/unix_alloc.rs | 3 +- tests/param/weak.rs | 2 +- 53 files changed, 369 insertions(+), 300 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c585a535a..06de21b7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -182,8 +182,8 @@ stdio = [] # Enable `rustix::system::*`. system = ["linux-raw-sys/system"] -# Enable `rustix::runtime::*`. This API is undocumented and unstable and -# experimental and not intended for general-purpose use. +# Enable `rustix::runtime::*`. ⚠ This API is undocumented and unstable and +# experimental and not intended for general-purpose use. ⚠ runtime = ["linux-raw-sys/prctl"] # Enable all API features. diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index 216e4b7f2..1a5dbf79d 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -86,8 +86,9 @@ pub(crate) const XCASE: tcflag_t = linux_raw_sys::general::XCASE as _; #[cfg(target_os = "aix")] pub(crate) const MSG_DONTWAIT: c_int = libc::MSG_NONBLOCK; -// It is defined as 0 in libc under 64-bit platforms, but is automatically set -// by kernel. +// `O_LARGEFILE` can be automatically set by the kernel on Linux: +// +// so libc implementations may leave it undefined or defined to zero. #[cfg(linux_kernel)] pub(crate) const O_LARGEFILE: c_int = linux_raw_sys::general::O_LARGEFILE as _; diff --git a/src/backend/libc/event/epoll.rs b/src/backend/libc/event/epoll.rs index 267f8e7ec..ac0b4ca70 100644 --- a/src/backend/libc/event/epoll.rs +++ b/src/backend/libc/event/epoll.rs @@ -85,7 +85,7 @@ use core::ptr::null_mut; use core::slice; bitflags! { - /// `EPOLL_*` for use with [`new`]. + /// `EPOLL_*` for use with [`create`]. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: u32 { @@ -157,6 +157,11 @@ bitflags! { /// /// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file /// descriptor from being implicitly passed across `exec` boundaries. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html #[inline] #[doc(alias = "epoll_create1")] pub fn create(flags: CreateFlags) -> io::Result { @@ -171,12 +176,17 @@ pub fn create(flags: CreateFlags) -> io::Result { /// This registers interest in any of the events set in `events` occurring on /// the file descriptor associated with `data`. /// -/// If [`delete`] is not called on the I/O source passed into this function -/// before the I/O source is `close`d, then the `epoll` will act as if the I/O -/// source is still registered with it. This can lead to spurious events being -/// returned from [`wait`]. If a file descriptor is an -/// `Arc`, then `epoll` can be thought to maintain a -/// `Weak` to the file descriptor. +/// Note that `close`ing a file descriptor does not necessarily unregister +/// interest which can lead to spurious events being returned from [`wait`]. If +/// a file descriptor is an `Arc`, then `epoll` can be +/// thought to maintain a `Weak` to the file descriptor. +/// Check the [faq] for details. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F #[doc(alias = "epoll_ctl")] pub fn add( epoll: impl AsFd, @@ -209,6 +219,11 @@ pub fn add( /// given epoll object. /// /// This sets the events of interest with `target` to `events`. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html #[doc(alias = "epoll_ctl")] pub fn modify( epoll: impl AsFd, @@ -240,6 +255,11 @@ pub fn modify( /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a /// given epoll object. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html #[doc(alias = "epoll_ctl")] pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { // SAFETY: We're calling `epoll_ctl` via FFI and we know how it @@ -260,6 +280,11 @@ pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { /// /// For each event of interest, an element is written to `events`. On /// success, this returns the number of written elements. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 5afb2d294..205dc0094 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -348,7 +348,7 @@ pub(crate) fn linkat( new_path: &CStr, flags: AtFlags, ) -> io::Result<()> { - // macOS <= 10.9 lacks `linkat`. + // macOS ≤ 10.9 lacks `linkat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -405,7 +405,7 @@ pub(crate) fn unlink(path: &CStr) -> io::Result<()> { #[cfg(not(any(target_os = "espidf", target_os = "redox")))] pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> { - // macOS <= 10.9 lacks `unlinkat`. + // macOS ≤ 10.9 lacks `unlinkat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -458,7 +458,7 @@ pub(crate) fn renameat( new_dirfd: BorrowedFd<'_>, new_path: &CStr, ) -> io::Result<()> { - // macOS <= 10.9 lacks `renameat`. + // macOS ≤ 10.9 lacks `renameat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -744,7 +744,7 @@ pub(crate) fn accessat( access: Access, flags: AtFlags, ) -> io::Result<()> { - // macOS <= 10.9 lacks `faccessat`. + // macOS ≤ 10.9 lacks `faccessat`. #[cfg(target_os = "macos")] unsafe { weak! { @@ -2255,8 +2255,8 @@ pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass - // null instead. + // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. + // Pass null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { @@ -2291,8 +2291,8 @@ pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Resul #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass - // null instead. + // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. + // Pass null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { @@ -2328,8 +2328,8 @@ pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io #[cfg(apple)] { - // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass - // null instead. + // Passing an empty to slice to `getxattr` leads to `ERANGE` on macOS. + // Pass null instead. let ptr = if value.is_empty() { core::ptr::null_mut() } else { diff --git a/src/backend/libc/io/errno.rs b/src/backend/libc/io/errno.rs index fb604a882..70613c41d 100644 --- a/src/backend/libc/io/errno.rs +++ b/src/backend/libc/io/errno.rs @@ -31,7 +31,6 @@ use libc_errno::errno; /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=errno§ion=2 /// [illumos]: https://illumos.org/man/3C/errno /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html -/// [`std::io::Error`]: Result #[repr(transparent)] #[doc(alias = "errno")] #[derive(Eq, PartialEq, Hash, Copy, Clone)] diff --git a/src/backend/libc/net/netdevice.rs b/src/backend/libc/net/netdevice.rs index 2fbb55b81..6ba80eca7 100644 --- a/src/backend/libc/net/netdevice.rs +++ b/src/backend/libc/net/netdevice.rs @@ -1,3 +1,5 @@ +//! Wrappers for netdevice ioctls. + #![allow(unsafe_code)] #[cfg(feature = "alloc")] diff --git a/src/backend/libc/net/send_recv.rs b/src/backend/libc/net/send_recv.rs index 51fbb84b7..3dbe92598 100644 --- a/src/backend/libc/net/send_recv.rs +++ b/src/backend/libc/net/send_recv.rs @@ -2,7 +2,7 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `MSG_*` flags for use with [`send`], [`send_to`], and related + /// `MSG_*` flags for use with [`send`], [`sendto`], and related /// functions. /// /// [`send`]: crate::net::send @@ -29,6 +29,8 @@ bitflags! { #[cfg(not(windows))] const DONTWAIT = bitcast!(c::MSG_DONTWAIT); /// Deprecated alias for [`EOR`]. + /// + /// [`EOR`]: Self::EOR #[cfg(not(windows))] #[deprecated(note = "`rustix::net::SendFlags::EOT` is renamed to `rustix::net::SendFlags::EOR`.")] const EOT = bitcast!(c::MSG_EOR); diff --git a/src/backend/libc/net/syscalls.rs b/src/backend/libc/net/syscalls.rs index 3fdb7766b..3013f9922 100644 --- a/src/backend/libc/net/syscalls.rs +++ b/src/backend/libc/net/syscalls.rs @@ -526,8 +526,8 @@ pub(crate) fn acceptfrom_with( } } -/// Darwin lacks `accept4`, but does have `accept`. We define -/// `SocketFlags` to have no flags, so we can discard it here. +/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to +/// have no flags, so we can discard it here. #[cfg(any( apple, windows, @@ -541,8 +541,8 @@ pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: SocketFlags) -> io::Re accept(sockfd) } -/// Darwin lacks `accept4`, but does have `accept`. We define -/// `SocketFlags` to have no flags, so we can discard it here. +/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to +/// have no flags, so we can discard it here. #[cfg(any( apple, windows, diff --git a/src/backend/libc/process/syscalls.rs b/src/backend/libc/process/syscalls.rs index b15e3117e..efb5a77f0 100644 --- a/src/backend/libc/process/syscalls.rs +++ b/src/backend/libc/process/syscalls.rs @@ -585,8 +585,8 @@ fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitidOptions) -> io::Result) -> Option { let status = status.assume_init(); // `si_pid` is supposedly the better way to check that the struct has been - // filled, e.g. the Linux manpage says about the `WNOHANG` case “zero out - // the si_pid field before the call and check for a nonzero value”. + // filled, e.g. the Linux manual page says about the `WNOHANG` case “zero + // out the si_pid field before the call and check for a nonzero value”. // But e.g. NetBSD/OpenBSD don't have it exposed in the libc crate for now, // and some platforms don't have it at all. For simplicity, always check // `si_signo`. We have zero-initialized the whole struct, and all kernels diff --git a/src/backend/libc/termios/syscalls.rs b/src/backend/libc/termios/syscalls.rs index a833aea8d..9eba5637f 100644 --- a/src/backend/libc/termios/syscalls.rs +++ b/src/backend/libc/termios/syscalls.rs @@ -117,7 +117,7 @@ pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result { let pid = ret_pid_t(c::tcgetpgrp(borrowed_fd(fd)))?; // This doesn't appear to be documented, but on Linux, it appears - // `tcsetpgrp` can succceed and set the pid to 0 if we pass it a + // `tcsetpgrp` can succeed and set the pid to 0 if we pass it a // pseudo-terminal device fd. For now, translate it into `OPNOTSUPP`. #[cfg(linux_kernel)] if pid == 0 { diff --git a/src/backend/linux_raw/arch/x86.rs b/src/backend/linux_raw/arch/x86.rs index e789181cc..a5e651567 100644 --- a/src/backend/linux_raw/arch/x86.rs +++ b/src/backend/linux_raw/arch/x86.rs @@ -357,6 +357,7 @@ pub(in crate::backend) unsafe fn syscall4_readonly( a3: ArgReg<'_, A3>, ) -> RetReg { let r0; + // See the comments in `syscall4`. asm!( "xchg esi, {a3}", "int $$0x80", diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 7639a6c2c..4b7f041a3 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -83,7 +83,7 @@ use core::hash::{Hash, Hasher}; use core::slice; bitflags! { - /// `EPOLL_*` for use with [`new`]. + /// `EPOLL_*` for use with [`create`]. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_uint { @@ -156,7 +156,6 @@ bitflags! { /// descriptor from being implicitly passed across `exec` boundaries. /// /// # References -/// /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html @@ -179,7 +178,6 @@ pub fn create(flags: CreateFlags) -> io::Result { /// Check the [faq] for details. /// /// # References -/// /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html @@ -212,7 +210,6 @@ pub fn add( /// This sets the events of interest with `target` to `events`. /// /// # References -/// /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html @@ -243,7 +240,6 @@ pub fn modify( /// given epoll object. /// /// # References -/// /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html @@ -265,7 +261,6 @@ pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { /// success, this returns the number of written elements. /// /// # References -/// /// - [Linux] /// /// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs index b6cf6b7b4..0c4b03ba1 100644 --- a/src/backend/linux_raw/fs/syscalls.rs +++ b/src/backend/linux_raw/fs/syscalls.rs @@ -358,7 +358,7 @@ pub(crate) fn fallocate( #[inline] pub(crate) fn fadvise(fd: BorrowedFd<'_>, pos: u64, len: u64, advice: Advice) -> io::Result<()> { - // On ARM, the arguments are reordered so that the len and pos argument + // On ARM, the arguments are reordered so that the `len` and `pos` argument // pairs are aligned. And ARM has a custom syscall code for this. #[cfg(target_arch = "arm")] unsafe { diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index 9b3c058ac..ecadde06d 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -742,7 +742,7 @@ pub type RawMode = linux_raw_sys::general::__kernel_mode_t; pub type RawMode = c::c_uint; /// `dev_t` -// Within the kernel the dev_t is 32-bit, but userspace uses a 64-bit field. +// Within the kernel the `dev_t` is 32-bit, but userspace uses a 64-bit field. pub type Dev = u64; /// `__fsword_t` diff --git a/src/backend/linux_raw/io/errno.rs b/src/backend/linux_raw/io/errno.rs index 4b13ae1b0..1af1936c1 100644 --- a/src/backend/linux_raw/io/errno.rs +++ b/src/backend/linux_raw/io/errno.rs @@ -41,7 +41,6 @@ use linux_raw_sys::errno; /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=errno§ion=2 /// [illumos]: https://illumos.org/man/3C/errno /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Error-Codes.html -/// [`std::io::Error`]: Result #[repr(transparent)] #[doc(alias = "errno")] #[derive(Eq, PartialEq, Hash, Copy, Clone)] diff --git a/src/backend/linux_raw/net/netdevice.rs b/src/backend/linux_raw/net/netdevice.rs index 6747e894b..2f7e5c1ef 100644 --- a/src/backend/linux_raw/net/netdevice.rs +++ b/src/backend/linux_raw/net/netdevice.rs @@ -1,3 +1,5 @@ +//! Wrappers for netdevice ioctls. + #![allow(unsafe_code)] use crate::backend::io::syscalls::ioctl; diff --git a/src/backend/linux_raw/net/send_recv.rs b/src/backend/linux_raw/net/send_recv.rs index de3c287c2..ef4609cbf 100644 --- a/src/backend/linux_raw/net/send_recv.rs +++ b/src/backend/linux_raw/net/send_recv.rs @@ -2,7 +2,7 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `MSG_*` flags for use with [`send`], [`send_to`], and related + /// `MSG_*` flags for use with [`send`], [`sendto`], and related /// functions. /// /// [`send`]: crate::net::send @@ -17,6 +17,8 @@ bitflags! { /// `MSG_DONTWAIT` const DONTWAIT = c::MSG_DONTWAIT; /// Deprecated alias for [`EOR`]. + /// + /// [`EOR`]: Self::EOR #[deprecated(note = "`rustix::net::SendFlags::EOT` is renamed to `rustix::net::SendFlags::EOR`.")] const EOT = c::MSG_EOR; /// `MSG_EOR` diff --git a/src/backend/linux_raw/param/auxv.rs b/src/backend/linux_raw/param/auxv.rs index d34384dab..676fac687 100644 --- a/src/backend/linux_raw/param/auxv.rs +++ b/src/backend/linux_raw/param/auxv.rs @@ -204,9 +204,9 @@ static RANDOM: AtomicPtr<[u8; 16]> = AtomicPtr::new(null_mut()); const PR_GET_AUXV: c::c_int = 0x4155_5856; -/// Use Linux >= 6.4's `PR_GET_AUXV` to read the aux records, into a provided +/// Use Linux ≥ 6.4's `PR_GET_AUXV` to read the aux records, into a provided /// statically-sized buffer. Return: -/// - `Ok(...)` if the buffer is big enough. +/// - `Ok(…)` if the buffer is big enough. /// - `Err(Ok(len))` if we need a buffer of length `len`. /// - `Err(Err(err))` if we failed with `err`. #[cold] @@ -228,10 +228,10 @@ fn pr_get_auxv_static(buffer: &mut [u8; 512]) -> Result<&mut [u8], crate::io::Re Err(Ok(len)) } -/// Use Linux >= 6.4's `PR_GET_AUXV` to read the aux records, using a provided +/// Use Linux ≥ 6.4's `PR_GET_AUXV` to read the aux records, using a provided /// statically-sized buffer if possible, or a dynamically allocated buffer /// otherwise. Return: -/// - Ok(...) on success. +/// - Ok(…) on success. /// - Err(err) on failure. #[cfg(feature = "alloc")] #[cold] diff --git a/src/backend/linux_raw/runtime/syscalls.rs b/src/backend/linux_raw/runtime/syscalls.rs index e00acc6fc..4b16ff5ba 100644 --- a/src/backend/linux_raw/runtime/syscalls.rs +++ b/src/backend/linux_raw/runtime/syscalls.rs @@ -38,8 +38,8 @@ pub(crate) unsafe fn fork() -> io::Result { let mut child_pid = MaybeUninit::::uninit(); // Unix `fork` only returns the child PID in the parent; we'd like it in - // the child too, so set `CLONE_CHILD_SETTID` and pass in the address of - // a memory location to store it to in the child. + // the child too, so set `CLONE_CHILD_SETTID` and pass in the address of a + // memory location to store it to in the child. // // Architectures differ on the order of the parameters. #[cfg(target_arch = "x86_64")] diff --git a/src/backend/linux_raw/termios/syscalls.rs b/src/backend/linux_raw/termios/syscalls.rs index 5a24c0a74..75eeb5edf 100644 --- a/src/backend/linux_raw/termios/syscalls.rs +++ b/src/backend/linux_raw/termios/syscalls.rs @@ -88,7 +88,7 @@ pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result { let pid = result.assume_init(); // This doesn't appear to be documented, but it appears `tcsetpgrp` can - // succceed and set the pid to 0 if we pass it a pseudo-terminal device + // succeed and set the pid to 0 if we pass it a pseudo-terminal device // fd. For now, fail with `OPNOTSUPP`. if pid == 0 { return Err(io::Errno::OPNOTSUPP); diff --git a/src/clockid.rs b/src/clockid.rs index 1c7deeb9e..7f986ad1d 100644 --- a/src/clockid.rs +++ b/src/clockid.rs @@ -69,7 +69,7 @@ pub enum ClockId { #[doc(alias = "CLOCK_REALTIME_ALARM")] RealtimeAlarm = bitcast!(c::CLOCK_REALTIME_ALARM), - /// `CLOCK_TAI`, available on Linux >= 3.10 + /// `CLOCK_TAI`, available on Linux ≥ 3.10 #[cfg(all(linux_kernel, feature = "linux_4_11"))] #[doc(alias = "CLOCK_TAI")] Tai = bitcast!(c::CLOCK_TAI), @@ -142,7 +142,7 @@ pub enum DynamicClockId<'a> { #[doc(alias = "CLOCK_REALTIME_ALARM")] RealtimeAlarm, - /// `CLOCK_TAI`, available on Linux >= 3.10 + /// `CLOCK_TAI`, available on Linux ≥ 3.10 #[cfg(linux_kernel)] #[doc(alias = "CLOCK_TAI")] Tai, diff --git a/src/event/kqueue.rs b/src/event/kqueue.rs index d6b7cdecf..56064999c 100644 --- a/src/event/kqueue.rs +++ b/src/event/kqueue.rs @@ -196,8 +196,8 @@ pub enum EventFilter { /// The signal number we waited on. signal: Signal, - /// The number of times the signal has been - /// received since the last call to kevent. + /// The number of times the signal has been received since the last + /// call to kevent. times: usize, }, diff --git a/src/fs/ioctl.rs b/src/fs/ioctl.rs index 490f183ff..b44bd174e 100644 --- a/src/fs/ioctl.rs +++ b/src/fs/ioctl.rs @@ -23,7 +23,7 @@ use crate::fd::{AsRawFd, BorrowedFd}; #[inline] #[doc(alias = "BLKSSZGET")] pub fn ioctl_blksszget(fd: Fd) -> io::Result { - // SAFETY: BLZSSZGET is a getter opcode that gets a u32. + // SAFETY: `BLZSSZGET` is a getter opcode that gets a u32. unsafe { let ctl = ioctl::Getter::, c::c_uint>::new(); ioctl::ioctl(fd, ctl) @@ -35,7 +35,7 @@ pub fn ioctl_blksszget(fd: Fd) -> io::Result { #[inline] #[doc(alias = "BLKPBSZGET")] pub fn ioctl_blkpbszget(fd: Fd) -> io::Result { - // SAFETY: BLKPBSZGET is a getter opcode that gets a u32. + // SAFETY: `BLKPBSZGET` is a getter opcode that gets a u32. unsafe { let ctl = ioctl::Getter::, c::c_uint>::new(); ioctl::ioctl(fd, ctl) @@ -44,7 +44,7 @@ pub fn ioctl_blkpbszget(fd: Fd) -> io::Result { /// `ioctl(fd, FICLONE, src_fd)`—Share data between open files. /// -/// This ioctl is not available on Sparc platforms +/// This ioctl is not available on Sparc platforms. /// /// # References /// - [Linux] @@ -62,7 +62,7 @@ pub fn ioctl_ficlone(fd: Fd, src_fd: SrcFd) -> io::Result #[inline] #[doc(alias = "EXT4_IOC_RESIZE_FS")] pub fn ext4_ioc_resize_fs(fd: Fd, blocks: u64) -> io::Result<()> { - // SAFETY: EXT4_IOC_RESIZE_FS is a pointer setter opcode. + // SAFETY: `EXT4_IOC_RESIZE_FS` is a pointer setter opcode. unsafe { let ctl = ioctl::Setter::, u64>::new( blocks, @@ -95,7 +95,9 @@ unsafe impl ioctl::Ioctl for Ficlone<'_> { #[cfg(linux_kernel)] bitflags! { - /// `FS_*` constants for use with [`ioctl_getflags`][crate::io::ioctl::ioctl_getflags]. + /// `FS_*` constants for use with [`ioctl_getflags`]. + /// + /// [`ioctl_getflags`]: crate::fs::ioctl::ioctl_getflags pub struct IFlags: c::c_uint { /// `FS_APPEND_FL` const APPEND = linux_raw_sys::general::FS_APPEND_FL; diff --git a/src/io/dup.rs b/src/io/dup.rs index 5d959c125..fab5357db 100644 --- a/src/io/dup.rs +++ b/src/io/dup.rs @@ -54,7 +54,7 @@ pub fn dup(fd: Fd) -> io::Result { /// /// This function does not set the `O_CLOEXEC` flag. To do a `dup2` that does /// set `O_CLOEXEC`, use [`dup3`] with [`DupFlags::CLOEXEC`] on platforms which -/// support it, or [`fcntl_dupfd_cloexec`] +/// support it, or [`fcntl_dupfd_cloexec`]. /// /// For `dup2` to stdin, stdout, and stderr, see [`stdio::dup2_stdin`], /// [`stdio::dup2_stdout`], and [`stdio::dup2_stderr`]. diff --git a/src/io/fcntl.rs b/src/io/fcntl.rs index 31eb84cef..f06b30ffa 100644 --- a/src/io/fcntl.rs +++ b/src/io/fcntl.rs @@ -98,6 +98,7 @@ pub fn fcntl_setfd(fd: Fd, flags: FdFlags) -> io::Result<()> { /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=fcntl§ion=2 /// [illumos]: https://illumos.org/man/2/fcntl /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Control-Operations.html#index-fcntl-function +/// [file description]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_258 #[cfg(not(any(target_os = "espidf", target_os = "wasi")))] #[inline] #[doc(alias = "F_DUPFD_CLOEXEC")] @@ -133,6 +134,7 @@ pub fn fcntl_dupfd_cloexec(fd: Fd, min: RawFd) -> io::Result /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=fcntl§ion=2 /// [illumos]: https://illumos.org/man/2/fcntl /// [glibc]: https://www.gnu.org/software/libc/manual/html_node/Control-Operations.html#index-fcntl-function +/// [file description]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_258 #[cfg(target_os = "espidf")] #[inline] #[doc(alias = "F_DUPFD")] diff --git a/src/io/ioctl.rs b/src/io/ioctl.rs index e83e692a3..596877f7b 100644 --- a/src/io/ioctl.rs +++ b/src/io/ioctl.rs @@ -21,7 +21,7 @@ use backend::fd::AsFd; #[doc(alias = "FIOCLEX")] #[doc(alias = "FD_CLOEXEC")] pub fn ioctl_fioclex(fd: Fd) -> io::Result<()> { - // SAFETY: FIOCLEX is a no-argument setter opcode. + // SAFETY: `FIOCLEX` is a no-argument setter opcode. unsafe { let ctl = ioctl::NoArg::>::new(); ioctl::ioctl(fd, ctl) @@ -41,7 +41,7 @@ pub fn ioctl_fioclex(fd: Fd) -> io::Result<()> { #[inline] #[doc(alias = "FIONBIO")] pub fn ioctl_fionbio(fd: Fd, value: bool) -> io::Result<()> { - // SAFETY: FIONBIO is a pointer setter opcode. + // SAFETY: `FIONBIO` is a pointer setter opcode. unsafe { let ctl = ioctl::Setter::, c::c_int>::new(value.into()); ioctl::ioctl(fd, ctl) @@ -69,7 +69,7 @@ pub fn ioctl_fionbio(fd: Fd, value: bool) -> io::Result<()> { #[inline] #[doc(alias = "FIONREAD")] pub fn ioctl_fionread(fd: Fd) -> io::Result { - // SAFETY: FIONREAD is a getter opcode that gets a c_int. + // SAFETY: `FIONREAD` is a getter opcode that gets a `c_int`. unsafe { let ctl = ioctl::Getter::, c::c_int>::new(); ioctl::ioctl(fd, ctl).map(|n| n as u64) diff --git a/src/ioctl/bsd.rs b/src/ioctl/bsd.rs index 2639d81fc..814d93d66 100644 --- a/src/ioctl/bsd.rs +++ b/src/ioctl/bsd.rs @@ -20,8 +20,8 @@ pub(super) const fn compose_opcode( // `IOC_VOID` pub const NONE: RawOpcode = 0x2000_0000; -// `IOC_OUT` ("out" is from the perspective of the kernel) +// `IOC_OUT` (“out” is from the perspective of the kernel) pub const READ: RawOpcode = 0x4000_0000; -// `IOC_IN` +// `IOC_IN` (“in” is from the perspective of the kernel) pub const WRITE: RawOpcode = 0x8000_0000; pub const IOCPARAM_MASK: RawOpcode = 0x1FFF; diff --git a/src/ioctl/linux.rs b/src/ioctl/linux.rs index 2f3599fc2..552625f2d 100644 --- a/src/ioctl/linux.rs +++ b/src/ioctl/linux.rs @@ -84,8 +84,8 @@ mod consts { } #[cfg(not(any( - // These have no ioctl opcodes defined in linux_raw_sys - // so can't use that as a known-good value for this test. + // These have no ioctl opcodes defined in linux_raw_sys so we can't use + // that as a known-good value for this test. target_arch = "sparc", target_arch = "sparc64" )))] diff --git a/src/mount/mount_unmount.rs b/src/mount/mount_unmount.rs index 651a18338..feda8eec1 100644 --- a/src/mount/mount_unmount.rs +++ b/src/mount/mount_unmount.rs @@ -40,9 +40,9 @@ pub fn mount usize { // Add `align_of::()` so that we can align the user-provided @@ -106,6 +107,7 @@ pub const fn __cmsg_space(len: usize) -> usize { __cmsg_aligned_space(len) } +/// Helper function for [`cmsg_aligned_space`]. #[doc(hidden)] pub const fn __cmsg_aligned_space(len: usize) -> usize { // Convert `len` to `u32` for `CMSG_SPACE`. This would be `try_into()` if diff --git a/src/net/socket.rs b/src/net/socket.rs index 1a2157776..ac40b2672 100644 --- a/src/net/socket.rs +++ b/src/net/socket.rs @@ -473,8 +473,8 @@ pub fn connect_unix(sockfd: Fd, addr: &SocketAddrUnix) -> io::Result<( backend::net::syscalls::connect_unix(sockfd.as_fd(), addr) } -/// `connect(sockfd, {.sa_family = AF_UNSPEC}, sizeof(struct sockaddr))` -/// — Dissolve the socket's association. +/// `connect(sockfd, {.sa_family = AF_UNSPEC}, sizeof(struct sockaddr))`— +/// Dissolve the socket's association. /// /// On UDP sockets, BSD platforms report [`Errno::AFNOSUPPORT`] or /// [`Errno::INVAL`] even if the disconnect was successful. diff --git a/src/net/sockopt.rs b/src/net/sockopt.rs index f591643e3..1ed3ce594 100644 --- a/src/net/sockopt.rs +++ b/src/net/sockopt.rs @@ -1070,7 +1070,7 @@ pub fn get_ipv6_freebind(fd: Fd) -> io::Result { /// `getsockopt(fd, IPPROTO_IP, SO_ORIGINAL_DST)` /// -/// Even though this corresponnds to a `SO_*` constant, it is an `IPPROTO_IP` +/// Even though this corresponds to a `SO_*` constant, it is an `IPPROTO_IP` /// option. /// /// See the [module-level documentation] for more. @@ -1085,7 +1085,7 @@ pub fn get_ip_original_dst(fd: Fd) -> io::Result { /// `getsockopt(fd, IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST)` /// -/// Even though this corresponnds to a `IP6T_*` constant, it is an +/// Even though this corresponds to a `IP6T_*` constant, it is an /// `IPPROTO_IPV6` option. /// /// See the [module-level documentation] for more. @@ -1363,7 +1363,8 @@ pub fn get_tcp_cork(fd: Fd) -> io::Result { backend::net::sockopt::get_tcp_cork(fd.as_fd()) } -/// Get credentials of Unix domain socket peer process +/// `getsockopt(fd, SOL_SOCKET, SO_PEERCRED)`—Get credentials of Unix domain +/// socket peer process. /// /// # References /// - [Linux `unix`] diff --git a/src/net/types.rs b/src/net/types.rs index 12e228e10..aabffbc6c 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -1495,7 +1495,7 @@ pub mod xdp { // Constant needs to be cast because bindgen does generate a u32 but the struct // expects a u16. https://github.com/torvalds/linux/blob/v6.6/include/uapi/linux/if_xdp.h#L15-L44 bitflags! { - /// `XDP_*` constants for use in [`SockaddrXdp`]. + /// `XDP_*` constants for use in [`SocketAddrXdp`]. #[repr(transparent)] #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)] pub struct SockaddrXdpFlags: u16 { diff --git a/src/path/arg.rs b/src/path/arg.rs index 5eb4709bf..435d727a1 100644 --- a/src/path/arg.rs +++ b/src/path/arg.rs @@ -3,7 +3,8 @@ //! This module defines the `Arg` trait and implements it for several common //! string types. This allows users to pass any of these string types directly //! to rustix APIs with string arguments, and it allows rustix to implement -//! NUL-termination without the need for copying where possible. +//! NUL-termination without the need for copying or dynamic allocation where +//! possible. use crate::ffi::CStr; use crate::io; @@ -1032,7 +1033,7 @@ where // `openat` to open the files under it, which will avoid this, and is often // faster in the OS as well. - // Test with >= so that we have room for the trailing NUL. + // Test with `>=` so that we have room for the trailing NUL. if bytes.len() >= SMALL_PATH_BUFFER_SIZE { return with_c_str_slow_path(bytes, f); } diff --git a/src/path/dec_int.rs b/src/path/dec_int.rs index 985d504ce..73907c5b2 100644 --- a/src/path/dec_int.rs +++ b/src/path/dec_int.rs @@ -87,6 +87,8 @@ impl DecInt { } } +/// 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 { diff --git a/src/process/prctl.rs b/src/process/prctl.rs index 830abc13d..ed64adb6e 100644 --- a/src/process/prctl.rs +++ b/src/process/prctl.rs @@ -29,11 +29,11 @@ const PR_GET_PDEATHSIG: c_int = 2; /// Get the current value of the parent process death signal. /// /// # References -/// - [Linux: `prctl(PR_GET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`] +/// - [Linux: `prctl(PR_GET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`] /// -/// [Linux: `prctl(PR_GET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_GET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] #[doc(alias = "PR_GET_PDEATHSIG")] pub fn parent_process_death_signal() -> io::Result> { @@ -45,11 +45,11 @@ const PR_SET_PDEATHSIG: c_int = 1; /// Set the parent-death signal of the calling process. /// /// # References -/// - [Linux: `prctl(PR_SET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`] +/// - [Linux: `prctl(PR_SET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`] /// -/// [Linux: `prctl(PR_SET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_SET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] #[doc(alias = "PR_SET_PDEATHSIG")] pub fn set_parent_process_death_signal(signal: Option) -> io::Result<()> { @@ -99,9 +99,9 @@ impl TryFrom for DumpableBehavior { /// Get the current state of the calling process' `dumpable` attribute. /// /// # References -/// - [`prctl(PR_GET_DUMPABLE,...)`] +/// - [`prctl(PR_GET_DUMPABLE,…)`] /// -/// [`prctl(PR_GET_DUMPABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_DUMPABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_DUMPABLE")] pub fn dumpable_behavior() -> io::Result { @@ -120,9 +120,9 @@ const PR_SET_DUMPABLE: c_int = 4; /// select a process other then the current process. /// /// # References -/// - [`prctl(PR_SET_DUMPABLE,...)`] +/// - [`prctl(PR_SET_DUMPABLE,…)`] /// -/// [`prctl(PR_SET_DUMPABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_DUMPABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_DUMPABLE")] pub fn set_dumpable_behavior(config: DumpableBehavior) -> io::Result<()> { @@ -157,9 +157,9 @@ bitflags! { /// Get unaligned access control bits. /// /// # References -/// - [`prctl(PR_GET_UNALIGN,...)`] +/// - [`prctl(PR_GET_UNALIGN,…)`] /// -/// [`prctl(PR_GET_UNALIGN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_UNALIGN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_UNALIGN")] pub fn unaligned_access_control() -> io::Result { @@ -172,9 +172,9 @@ const PR_SET_UNALIGN: c_int = 6; /// Set unaligned access control bits. /// /// # References -/// - [`prctl(PR_SET_UNALIGN,...)`] +/// - [`prctl(PR_SET_UNALIGN,…)`] /// -/// [`prctl(PR_SET_UNALIGN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_UNALIGN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_UNALIGN")] pub fn set_unaligned_access_control(config: UnalignedAccessControl) -> io::Result<()> { @@ -206,9 +206,9 @@ bitflags! { /// Get floating point emulation control bits. /// /// # References -/// - [`prctl(PR_GET_FPEMU,...)`] +/// - [`prctl(PR_GET_FPEMU,…)`] /// -/// [`prctl(PR_GET_FPEMU,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_FPEMU,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_FPEMU")] pub fn floating_point_emulation_control() -> io::Result { @@ -221,9 +221,9 @@ const PR_SET_FPEMU: c_int = 10; /// Set floating point emulation control bits. /// /// # References -/// - [`prctl(PR_SET_FPEMU,...)`] +/// - [`prctl(PR_SET_FPEMU,…)`] /// -/// [`prctl(PR_SET_FPEMU,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_FPEMU,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_FPEMU")] pub fn set_floating_point_emulation_control( @@ -268,9 +268,9 @@ bitflags! { /// Get floating point exception mode. /// /// # References -/// - [`prctl(PR_GET_FPEXC,...)`] +/// - [`prctl(PR_GET_FPEXC,…)`] /// -/// [`prctl(PR_GET_FPEXC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_FPEXC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_FPEXEC")] pub fn floating_point_exception_mode() -> io::Result> { @@ -283,9 +283,9 @@ const PR_SET_FPEXC: c_int = 12; /// Set floating point exception mode. /// /// # References -/// - [`prctl(PR_SET_FPEXC,...)`] +/// - [`prctl(PR_SET_FPEXC,…)`] /// -/// [`prctl(PR_SET_FPEXC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_FPEXC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_FPEXEC")] pub fn set_floating_point_exception_mode( @@ -330,9 +330,9 @@ impl TryFrom for TimingMethod { /// Get which process timing method is currently in use. /// /// # References -/// - [`prctl(PR_GET_TIMING,...)`] +/// - [`prctl(PR_GET_TIMING,…)`] /// -/// [`prctl(PR_GET_TIMING,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TIMING,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_TIMING")] pub fn timing_method() -> io::Result { @@ -345,9 +345,9 @@ const PR_SET_TIMING: c_int = 14; /// accurate timestamp-based process timing. /// /// # References -/// - [`prctl(PR_SET_TIMING,...)`] +/// - [`prctl(PR_SET_TIMING,…)`] /// -/// [`prctl(PR_SET_TIMING,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TIMING,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_TIMING")] pub fn set_timing_method(method: TimingMethod) -> io::Result<()> { @@ -392,9 +392,9 @@ impl TryFrom for EndianMode { /// Get the endianness of the calling process. /// /// # References -/// - [`prctl(PR_GET_ENDIAN,...)`] +/// - [`prctl(PR_GET_ENDIAN,…)`] /// -/// [`prctl(PR_GET_ENDIAN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_ENDIAN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_ENDIAN")] pub fn endian_mode() -> io::Result { @@ -406,14 +406,14 @@ const PR_SET_ENDIAN: c_int = 20; /// Set the endianness of the calling process. /// /// # References -/// - [`prctl(PR_SET_ENDIAN,...)`] +/// - [`prctl(PR_SET_ENDIAN,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_ENDIAN,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_ENDIAN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_ENDIAN")] pub unsafe fn set_endian_mode(mode: EndianMode) -> io::Result<()> { @@ -455,9 +455,9 @@ impl TryFrom for TimeStampCounterReadability { /// Get the state of the flag determining if the timestamp counter can be read. /// /// # References -/// - [`prctl(PR_GET_TSC,...)`] +/// - [`prctl(PR_GET_TSC,…)`] /// -/// [`prctl(PR_GET_TSC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TSC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_TSC")] pub fn time_stamp_counter_readability() -> io::Result { @@ -470,9 +470,9 @@ const PR_SET_TSC: c_int = 26; /// by the process. /// /// # References -/// - [`prctl(PR_SET_TSC,...)`] +/// - [`prctl(PR_SET_TSC,…)`] /// -/// [`prctl(PR_SET_TSC,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TSC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_TSC")] pub fn set_time_stamp_counter_readability( @@ -491,11 +491,11 @@ const PR_TASK_PERF_EVENTS_ENABLE: c_int = 32; /// Enable or disable all performance counters attached to the calling process. /// /// # References -/// - [`prctl(PR_TASK_PERF_EVENTS_ENABLE,...)`] -/// - [`prctl(PR_TASK_PERF_EVENTS_DISABLE,...)`] +/// - [`prctl(PR_TASK_PERF_EVENTS_ENABLE,…)`] +/// - [`prctl(PR_TASK_PERF_EVENTS_DISABLE,…)`] /// -/// [`prctl(PR_TASK_PERF_EVENTS_ENABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [`prctl(PR_TASK_PERF_EVENTS_DISABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_TASK_PERF_EVENTS_ENABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_TASK_PERF_EVENTS_DISABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_TASK_PERF_EVENTS_ENABLE")] #[doc(alias = "PR_TASK_PERF_EVENTS_DISABLE")] @@ -552,9 +552,9 @@ impl TryFrom for MachineCheckMemoryCorruptionKillPolicy { /// Get the current per-process machine check kill policy. /// /// # References -/// - [`prctl(PR_MCE_KILL_GET,...)`] +/// - [`prctl(PR_MCE_KILL_GET,…)`] /// -/// [`prctl(PR_MCE_KILL_GET,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_MCE_KILL_GET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_MCE_KILL_GET")] pub fn machine_check_memory_corruption_kill_policy( @@ -571,9 +571,9 @@ const PR_MCE_KILL_SET: usize = 1; /// Set the machine check memory corruption kill policy for the calling thread. /// /// # References -/// - [`prctl(PR_MCE_KILL,...)`] +/// - [`prctl(PR_MCE_KILL,…)`] /// -/// [`prctl(PR_MCE_KILL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_MCE_KILL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_MCE_KILL")] pub fn set_machine_check_memory_corruption_kill_policy( @@ -645,14 +645,14 @@ pub enum VirtualMemoryMapAddress { /// process. /// /// # References -/// - [`prctl(PR_SET_MM,...)`] +/// - [`prctl(PR_SET_MM,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_MM,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] pub unsafe fn set_virtual_memory_map_address( @@ -667,9 +667,9 @@ pub unsafe fn set_virtual_memory_map_address( /// new executable file. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,…)`] /// -/// [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_EXE_FILE")] @@ -681,14 +681,14 @@ pub fn set_executable_file(fd: BorrowedFd<'_>) -> io::Result<()> { /// Set a new auxiliary vector. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_AUXV,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_AUXV,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_MM,PR_SET_MM_AUXV,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_AUXV,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_AUXV")] @@ -706,9 +706,9 @@ pub unsafe fn set_auxiliary_vector(auxv: &[*const c_void]) -> io::Result<()> { /// Get the size of the [`PrctlMmMap`] the kernel expects. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,…)`] /// -/// [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_MAP_SIZE")] @@ -760,14 +760,14 @@ pub struct PrctlMmMap { /// [`PrctlMmMap`]. /// /// # References -/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP,...)`] +/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_MM,PR_SET_MM_MAP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_MM,PR_SET_MM_MAP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_MM")] #[doc(alias = "PR_SET_MM_MAP")] @@ -805,9 +805,9 @@ pub enum PTracer { /// were a direct process ancestor. /// /// # References -/// - [`prctl(PR_SET_PTRACER,...)`] +/// - [`prctl(PR_SET_PTRACER,…)`] /// -/// [`prctl(PR_SET_PTRACER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_PTRACER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_PTRACER")] pub fn set_ptracer(tracer: PTracer) -> io::Result<()> { @@ -829,9 +829,9 @@ const PR_GET_CHILD_SUBREAPER: c_int = 37; /// Get the `child subreaper` setting of the calling process. /// /// # References -/// - [`prctl(PR_GET_CHILD_SUBREAPER,...)`] +/// - [`prctl(PR_GET_CHILD_SUBREAPER,…)`] /// -/// [`prctl(PR_GET_CHILD_SUBREAPER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_CHILD_SUBREAPER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_CHILD_SUBREAPER")] pub fn child_subreaper() -> io::Result> { @@ -846,9 +846,9 @@ const PR_SET_CHILD_SUBREAPER: c_int = 36; /// Set the `child subreaper` attribute of the calling process. /// /// # References -/// - [`prctl(PR_SET_CHILD_SUBREAPER,...)`] +/// - [`prctl(PR_SET_CHILD_SUBREAPER,…)`] /// -/// [`prctl(PR_SET_CHILD_SUBREAPER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_CHILD_SUBREAPER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_CHILD_SUBREAPER")] pub fn set_child_subreaper(pid: Option) -> io::Result<()> { @@ -891,9 +891,9 @@ impl TryFrom for FloatingPointMode { /// Get the current floating point mode. /// /// # References -/// - [`prctl(PR_GET_FP_MODE,...)`] +/// - [`prctl(PR_GET_FP_MODE,…)`] /// -/// [`prctl(PR_GET_FP_MODE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_FP_MODE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_FP_MODE")] pub fn floating_point_mode() -> io::Result { @@ -906,9 +906,9 @@ const PR_SET_FP_MODE: c_int = 45; /// Allow control of the floating point mode from user space. /// /// # References -/// - [`prctl(PR_SET_FP_MODE,...)`] +/// - [`prctl(PR_SET_FP_MODE,…)`] /// -/// [`prctl(PR_SET_FP_MODE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_FP_MODE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_FP_MODE")] pub fn set_floating_point_mode(mode: FloatingPointMode) -> io::Result<()> { @@ -993,9 +993,9 @@ bitflags! { /// Get the state of the speculation misfeature. /// /// # References -/// - [`prctl(PR_GET_SPECULATION_CTRL,...)`] +/// - [`prctl(PR_GET_SPECULATION_CTRL,…)`] /// -/// [`prctl(PR_GET_SPECULATION_CTRL,...)`]: https://www.kernel.org/doc/html/v5.18/userspace-api/spec_ctrl.html +/// [`prctl(PR_GET_SPECULATION_CTRL,…)`]: https://www.kernel.org/doc/html/v6.10/userspace-api/spec_ctrl.html #[inline] #[doc(alias = "PR_GET_SPECULATION_CTRL")] pub fn speculative_feature_state( @@ -1010,9 +1010,9 @@ const PR_SET_SPECULATION_CTRL: c_int = 53; /// Sets the state of the speculation misfeature. /// /// # References -/// - [`prctl(PR_SET_SPECULATION_CTRL,...)`] +/// - [`prctl(PR_SET_SPECULATION_CTRL,…)`] /// -/// [`prctl(PR_SET_SPECULATION_CTRL,...)`]: https://www.kernel.org/doc/html/v5.18/userspace-api/spec_ctrl.html +/// [`prctl(PR_SET_SPECULATION_CTRL,…)`]: https://www.kernel.org/doc/html/v6.10/userspace-api/spec_ctrl.html #[inline] #[doc(alias = "PR_SET_SPECULATION_CTRL")] pub fn control_speculative_feature( @@ -1033,9 +1033,9 @@ const PR_GET_IO_FLUSHER: c_int = 58; /// Get the `IO_FLUSHER` state of the caller. /// /// # References -/// - [`prctl(PR_GET_IO_FLUSHER,...)`] +/// - [`prctl(PR_GET_IO_FLUSHER,…)`] /// -/// [`prctl(PR_GET_IO_FLUSHER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_IO_FLUSHER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_GET_IO_FLUSHER")] pub fn is_io_flusher() -> io::Result { @@ -1048,9 +1048,9 @@ const PR_SET_IO_FLUSHER: c_int = 57; /// when allocating memory. /// /// # References -/// - [`prctl(PR_SET_IO_FLUSHER,...)`] +/// - [`prctl(PR_SET_IO_FLUSHER,…)`] /// -/// [`prctl(PR_SET_IO_FLUSHER,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_IO_FLUSHER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[doc(alias = "PR_SET_IO_FLUSHER")] pub fn configure_io_flusher_behavior(enable: bool) -> io::Result<()> { @@ -1066,9 +1066,9 @@ const PR_PAC_GET_ENABLED_KEYS: c_int = 61; /// Get enabled pointer authentication keys. /// /// # References -/// - [`prctl(PR_PAC_GET_ENABLED_KEYS,...)`] +/// - [`prctl(PR_PAC_GET_ENABLED_KEYS,…)`] /// -/// [`prctl(PR_PAC_GET_ENABLED_KEYS,...)`]: https://www.kernel.org/doc/html/v5.18/arm64/pointer-authentication.html +/// [`prctl(PR_PAC_GET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.10/arch/arm64/pointer-authentication.html #[inline] #[doc(alias = "PR_PAC_GET_ENABLED_KEYS")] pub fn enabled_pointer_authentication_keys() -> io::Result { @@ -1081,14 +1081,14 @@ const PR_PAC_SET_ENABLED_KEYS: c_int = 60; /// Set enabled pointer authentication keys. /// /// # References -/// - [`prctl(PR_PAC_SET_ENABLED_KEYS,...)`] +/// - [`prctl(PR_PAC_SET_ENABLED_KEYS,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_PAC_SET_ENABLED_KEYS,...)`]: https://www.kernel.org/doc/html/v5.18/arm64/pointer-authentication.html +/// [`prctl(PR_PAC_SET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.10/arch/arm64/pointer-authentication.html #[inline] #[doc(alias = "PR_PAC_SET_ENABLED_KEYS")] pub unsafe fn configure_pointer_authentication_keys( @@ -1131,9 +1131,9 @@ const PR_SET_VMA_ANON_NAME: usize = 0; /// Set the name for a virtual memory region. /// /// # References -/// - [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,...)`] +/// - [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,…)`] /// -/// [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,...)`]: https://lwn.net/Articles/867818/ +/// [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,…)`]: https://lwn.net/Articles/867818/ #[inline] #[doc(alias = "PR_SET_VMA")] #[doc(alias = "PR_SET_VMA_ANON_NAME")] diff --git a/src/process/procctl.rs b/src/process/procctl.rs index 249fcccfb..e198d03b0 100644 --- a/src/process/procctl.rs +++ b/src/process/procctl.rs @@ -85,11 +85,11 @@ const PROC_PDEATHSIG_STATUS: c_int = 12; /// Get the current value of the parent process death signal. /// /// # References -/// - [Linux: `prctl(PR_GET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`] +/// - [Linux: `prctl(PR_GET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`] /// -/// [Linux: `prctl(PR_GET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_GET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn parent_process_death_signal() -> io::Result> { unsafe { procctl_get_optional::(PROC_PDEATHSIG_STATUS, None) }.map(Signal::from_raw) @@ -100,11 +100,11 @@ const PROC_PDEATHSIG_CTL: c_int = 11; /// Set the parent-death signal of the calling process. /// /// # References -/// - [Linux: `prctl(PR_SET_PDEATHSIG,...)`] -/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`] +/// - [Linux: `prctl(PR_SET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`] /// -/// [Linux: `prctl(PR_SET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_SET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_parent_process_death_signal(signal: Option) -> io::Result<()> { let signal = signal.map_or(0, |signal| signal as c_int); @@ -144,9 +144,9 @@ pub enum DumpableBehavior { /// Linux. /// /// # References -/// - [FreeBSD `procctl(PROC_TRACE_CTL,...)`] +/// - [FreeBSD `procctl(PROC_TRACE_CTL,…)`] /// -/// [FreeBSD `procctl(PROC_TRACE_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD `procctl(PROC_TRACE_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_dumpable_behavior(process: ProcSelector, config: DumpableBehavior) -> io::Result<()> { unsafe { procctl(PROC_TRACE_CTL, process, config as usize as *mut _) } @@ -174,9 +174,9 @@ pub enum TracingStatus { /// Get the tracing status of the process indicated by `idtype` and `id`. /// /// # References -/// - [FreeBSD `procctl(PROC_TRACE_STATUS,...)`] +/// - [FreeBSD `procctl(PROC_TRACE_STATUS,…)`] /// -/// [FreeBSD `procctl(PROC_TRACE_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD `procctl(PROC_TRACE_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn trace_status(process: ProcSelector) -> io::Result { let val = unsafe { procctl_get_optional::(PROC_TRACE_STATUS, process) }?; @@ -200,9 +200,9 @@ const PROC_REAP_RELEASE: c_int = 3; /// Acquire or release the reaper status of the calling process. /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_reaper_status(reaper: bool) -> io::Result<()> { unsafe { @@ -265,9 +265,9 @@ pub struct ReaperStatus { /// itself if it is a reaper). /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_STATUS,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_STATUS,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn get_reaper_status(process: ProcSelector) -> io::Result { let raw = unsafe { procctl_get_optional::(PROC_REAP_STATUS, process) }?; @@ -332,7 +332,7 @@ pub struct PidInfo { pub flags: PidInfoFlags, /// The pid of the process. pub pid: Pid, - /// The pid of the child of the reaper which is the (grand-..)parent of the + /// The pid of the child of the reaper which is the (grand-…)parent of the /// process. pub subtree: Pid, } @@ -340,13 +340,13 @@ pub struct PidInfo { /// Get the list of descendants of the specified reaper process. /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_GETPIDS,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_GETPIDS,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_GETPIDS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_GETPIDS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[cfg(feature = "alloc")] pub fn get_reaper_pids(process: ProcSelector) -> io::Result> { // Sadly no better way to guarantee that we get all the results than to - // allocate ~8MB of memory.. + // allocate ≈8MB of memory… const PID_MAX: usize = 99999; let mut pids: Vec = vec![Default::default(); PID_MAX]; let mut pinfo = procctl_reaper_pids { @@ -401,12 +401,12 @@ pub struct KillResult { pub first_failed: Option, } -/// Deliver a signal to some subset of +/// Deliver a signal to some subset of the descendants of the reaper. /// /// # References -/// - [FreeBSD: `procctl(PROC_REAP_KILL,...)`] +/// - [FreeBSD: `procctl(PROC_REAP_KILL,…)`] /// -/// [FreeBSD: `procctl(PROC_REAP_KILL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_REAP_KILL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 pub fn reaper_kill( process: ProcSelector, signal: Signal, @@ -455,15 +455,15 @@ pub enum TrapCapBehavior { /// Set the current value of the capability mode violation trapping behavior. /// If this behavior is enabled, the kernel would deliver a [`Signal::Trap`] /// signal on any return from a system call that would result in a -/// [`io::Errno::NOTCAPABLE`]` or [`io::Errno::CAPMODE`] error. +/// [`io::Errno::NOTCAPABLE`] or [`io::Errno::CAPMODE`] error. /// /// This behavior is inherited by the children of the process and is kept /// across `execve` calls. /// /// # References -/// - [FreeBSD: `procctl(PROC_TRAPCAP_CTL,...)`] +/// - [FreeBSD: `procctl(PROC_TRAPCAP_CTL,…)`] /// -/// [FreeBSD: `procctl(PROC_TRAPCAP_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_TRAPCAP_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_trap_cap_behavior(process: ProcSelector, config: TrapCapBehavior) -> io::Result<()> { let config = config as c_int; @@ -475,9 +475,9 @@ const PROC_TRAPCAP_STATUS: c_int = 10; /// Get the current value of the capability mode violation trapping behavior. /// /// # References -/// - [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,...)`] +/// - [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,…)`] /// -/// [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn trap_cap_behavior(process: ProcSelector) -> io::Result { let val = unsafe { procctl_get_optional::(PROC_TRAPCAP_STATUS, process) }?; @@ -504,11 +504,11 @@ const PROC_NO_NEW_PRIVS_ENABLE: c_int = 1; /// to enable this mode and there's no going back. /// /// # References -/// - [Linux: `prctl(PR_SET_NO_NEW_PRIVS,...)`] -/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,...)`] +/// - [Linux: `prctl(PR_SET_NO_NEW_PRIVS,…)`] +/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,…)`] /// -/// [Linux: `prctl(PR_SET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_SET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn set_no_new_privs(process: ProcSelector) -> io::Result<()> { unsafe { procctl_set::(PROC_NO_NEW_PRIVS_CTL, process, &PROC_NO_NEW_PRIVS_ENABLE) } @@ -519,11 +519,11 @@ const PROC_NO_NEW_PRIVS_STATUS: c_int = 20; /// Check the `no_new_privs` mode of the specified process. /// /// # References -/// - [Linux: `prctl(PR_GET_NO_NEW_PRIVS,...)`] -/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,...)`] +/// - [Linux: `prctl(PR_GET_NO_NEW_PRIVS,…)`] +/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,…)`] /// -/// [Linux: `prctl(PR_GET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html -/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +/// [Linux: `prctl(PR_GET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 #[inline] pub fn no_new_privs(process: ProcSelector) -> io::Result { unsafe { procctl_get_optional::(PROC_NO_NEW_PRIVS_STATUS, process) } diff --git a/src/process/wait.rs b/src/process/wait.rs index 4d0e6e25d..d6edab162 100644 --- a/src/process/wait.rs +++ b/src/process/wait.rs @@ -15,12 +15,14 @@ bitflags! { pub struct WaitOptions: u32 { /// Return immediately if no child has exited. const NOHANG = bitcast!(backend::process::wait::WNOHANG); - /// Return if a child has stopped (but not traced via [`ptrace`]) + /// Return if a child has stopped (but not traced via [`ptrace`]). /// /// [`ptrace`]: https://man7.org/linux/man-pages/man2/ptrace.2.html const UNTRACED = bitcast!(backend::process::wait::WUNTRACED); /// Return if a stopped child has been resumed by delivery of /// [`Signal::Cont`]. + /// + /// [`Signal::Cont`]: crate::process::Signal::Cont const CONTINUED = bitcast!(backend::process::wait::WCONTINUED); /// @@ -38,6 +40,8 @@ bitflags! { const NOHANG = bitcast!(backend::process::wait::WNOHANG); /// Return if a stopped child has been resumed by delivery of /// [`Signal::Cont`]. + /// + /// [`Signal::Cont`]: crate::process::Signal::Cont const CONTINUED = bitcast!(backend::process::wait::WCONTINUED); /// Wait for processed that have exited. const EXITED = bitcast!(backend::process::wait::WEXITED); diff --git a/src/procfs.rs b/src/procfs.rs index 59794758c..384f0243b 100644 --- a/src/procfs.rs +++ b/src/procfs.rs @@ -245,7 +245,7 @@ fn proc_opendirat(dirfd: Fd, path: P) -> io::Resu fn proc() -> io::Result<(BorrowedFd<'static>, &'static Stat)> { static PROC: StaticFd = StaticFd::new(); - // `OnceBox` is "racey" in that the initialization function may run + // `OnceBox` is “racy” in that the initialization function may run // multiple times. We're ok with that, since the initialization function // has no side effects. PROC.get_or_try_init(|| { diff --git a/src/pty.rs b/src/pty.rs index a9e1a1c5b..4a9ca7812 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -170,7 +170,7 @@ pub fn grantpt(fd: Fd) -> io::Result<()> { } } -/// `ioctl(fd, TIOCGPTPEER)`—Open the user side of a pseduoterminal. +/// `ioctl(fd, TIOCGPTPEER)`—Open the user side of a pseudoterminal. /// /// This function is currently only implemented on Linux. /// diff --git a/src/rand/getrandom.rs b/src/rand/getrandom.rs index 760e04f62..ab838c40c 100644 --- a/src/rand/getrandom.rs +++ b/src/rand/getrandom.rs @@ -1,3 +1,5 @@ +//! Wrappers for `getrandom`. + #![allow(unsafe_code)] use crate::buffer::split_init; diff --git a/src/runtime.rs b/src/runtime.rs index f965ba714..43ac220ec 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,12 +1,24 @@ //! Experimental low-level implementation details for libc-like runtime //! libraries such as [Origin]. //! -//! Do not use the functions in this module unless you've read all of their -//! code. They don't always behave the same way as functions with similar names -//! in `libc`. Sometimes information about the differences is included in the -//! Linux documentation under “C library/kernel differences” sections. And, if -//! there is a libc in the process, these functions may have surprising -//! interactions with it. +//! ⚠ These are not normal functions. ⚠ +//! +//! - Some of the functions in this module cannot be used in a process which +//! also has a libc present. This can be true even for functions that have +//! the same name as a libc function that Rust code can use. +//! +//! - Some of the functions in this module don't behave exactly the same way +//! as functions in libc with similar names. Sometimes information about the +//! differences is included in the Linux documentation under “C +//! library/kernel differences” sections. But not always. +//! +//! - The safety requirements of the functions in this module are not fully +//! documented. +//! +//! - The API for these functions is not considered stable, and this module is +//! `doc(hidden)`. +//! +//! ⚠ Caution is indicated. ⚠ //! //! These functions are for implementing thread-local storage (TLS), managing //! threads, loaded libraries, and other process-wide resources. Most of @@ -14,9 +26,6 @@ //! program or what they're doing, but the features in this module generally //! can only be used by one entity within a process. //! -//! The API for these functions is not stable, and this module is -//! `doc(hidden)`. -//! //! [Origin]: https://github.com/sunfishcode/origin#readme //! //! # Safety @@ -320,7 +329,12 @@ pub unsafe fn fork() -> io::Result { /// the child can just do `getpid`. That's true, but it's more fun if it /// doesn't have to. pub enum Fork { + /// This is returned in the child process after a `fork`. It holds the PID + /// of the child. Child(Pid), + + /// This is returned in the parent process after a `fork`. It holds the PID + /// of the child. Parent(Pid), } @@ -436,6 +450,7 @@ pub unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { /// [Linux `pthread_sigmask`]: https://man7.org/linux/man-pages/man3/pthread_sigmask.3.html #[inline] #[doc(alias = "pthread_sigmask")] +#[doc(alias = "rt_sigprocmask")] pub unsafe fn sigprocmask(how: How, set: Option<&Sigset>) -> io::Result { backend::runtime::syscalls::sigprocmask(how, set) } diff --git a/src/stdio.rs b/src/stdio.rs index 40ecba583..528c6cf0f 100644 --- a/src/stdio.rs +++ b/src/stdio.rs @@ -4,8 +4,8 @@ //! //! These access the file descriptors by absolute index value, and nothing //! prevents them from being closed and reused. They should only be used in -//! `main` or other situations where one is in control of the process' -//! stdio streams. +//! `main` or other situations where one is in control of the process' stdio +//! streams. #![allow(unsafe_code)] use crate::backend; diff --git a/src/system.rs b/src/system.rs index 9fad16df2..891ff6090 100644 --- a/src/system.rs +++ b/src/system.rs @@ -201,7 +201,7 @@ pub enum RebootCommand { SwSuspend = c::LINUX_REBOOT_CMD_SW_SUSPEND, } -/// `reboot`—Reboot the system or enable/disable Ctrl-Alt-Del +/// `reboot`—Reboot the system or enable/disable Ctrl-Alt-Del. /// /// The reboot syscall, despite the name, can actually do much more than /// reboot. @@ -224,7 +224,7 @@ pub fn reboot(cmd: RebootCommand) -> io::Result<()> { backend::system::syscalls::reboot(cmd) } -/// `init_module`—Load a kernel module +/// `init_module`—Load a kernel module. /// /// # References /// - [Linux] @@ -236,7 +236,7 @@ pub fn init_module(image: &[u8], param_values: &CStr) -> io::Result<()> { backend::system::syscalls::init_module(image, param_values) } -/// `finit_module`—Load a kernel module from a file descriptor +/// `finit_module`—Load a kernel module from a file descriptor. /// /// # References /// - [Linux] @@ -248,7 +248,7 @@ pub fn finit_module(fd: Fd, param_values: &CStr, flags: c_int) -> io:: backend::system::syscalls::finit_module(fd.as_fd(), param_values, flags) } -/// `delete_module`—Unload a kernel module +/// `delete_module`—Unload a kernel module. /// /// # References /// - [Linux] diff --git a/src/termios/ioctl.rs b/src/termios/ioctl.rs index 620ae4c71..8fcf1b110 100644 --- a/src/termios/ioctl.rs +++ b/src/termios/ioctl.rs @@ -22,7 +22,7 @@ use backend::c; #[inline] #[doc(alias = "TIOCEXCL")] pub fn ioctl_tiocexcl(fd: Fd) -> io::Result<()> { - // SAFETY: TIOCEXCL is a no-argument setter opcode. + // SAFETY: `TIOCEXCL` is a no-argument setter opcode. unsafe { let ctl = ioctl::NoArg::>::new(); ioctl::ioctl(fd, ctl) @@ -45,7 +45,7 @@ pub fn ioctl_tiocexcl(fd: Fd) -> io::Result<()> { #[inline] #[doc(alias = "TIOCNXCL")] pub fn ioctl_tiocnxcl(fd: Fd) -> io::Result<()> { - // SAFETY: TIOCNXCL is a no-argument setter opcode. + // SAFETY: `TIOCNXCL` is a no-argument setter opcode. unsafe { let ctl = ioctl::NoArg::>::new(); ioctl::ioctl(fd, ctl) diff --git a/src/thread/prctl.rs b/src/thread/prctl.rs index c6d5198ae..08ba0a2d6 100644 --- a/src/thread/prctl.rs +++ b/src/thread/prctl.rs @@ -1,12 +1,11 @@ //! Linux `prctl` wrappers. //! -//! Rustix wraps variadic/dynamic-dispatch functions like `prctl` in -//! type-safe wrappers. +//! Rustix wraps variadic/dynamic-dispatch functions like `prctl` in type-safe +//! wrappers. //! //! # Safety //! -//! The inner `prctl` calls are dynamically typed and must be called -//! correctly. +//! The inner `prctl` calls are dynamically typed and must be called correctly. #![allow(unsafe_code)] use core::mem::MaybeUninit; @@ -38,9 +37,9 @@ const PR_GET_KEEPCAPS: c_int = 7; /// Get the current state of the calling thread's `keep capabilities` flag. /// /// # References -/// - [`prctl(PR_GET_KEEPCAPS,...)`] +/// - [`prctl(PR_GET_KEEPCAPS,…)`] /// -/// [`prctl(PR_GET_KEEPCAPS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_KEEPCAPS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn get_keep_capabilities() -> io::Result { unsafe { prctl_1arg(PR_GET_KEEPCAPS) }.map(|r| r != 0) @@ -51,9 +50,9 @@ const PR_SET_KEEPCAPS: c_int = 8; /// Set the state of the calling thread's `keep capabilities` flag. /// /// # References -/// - [`prctl(PR_SET_KEEPCAPS,...)`] +/// - [`prctl(PR_SET_KEEPCAPS,…)`] /// -/// [`prctl(PR_SET_KEEPCAPS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_KEEPCAPS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_keep_capabilities(enable: bool) -> io::Result<()> { unsafe { prctl_2args(PR_SET_KEEPCAPS, usize::from(enable) as *mut _) }.map(|_r| ()) @@ -69,9 +68,9 @@ const PR_GET_NAME: c_int = 16; /// Get the name of the calling thread. /// /// # References -/// - [`prctl(PR_GET_NAME,...)`] +/// - [`prctl(PR_GET_NAME,…)`] /// -/// [`prctl(PR_GET_NAME,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_NAME,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] #[cfg(feature = "alloc")] pub fn name() -> io::Result { @@ -90,9 +89,9 @@ const PR_SET_NAME: c_int = 15; /// 16 bytes, as the Linux syscall does. /// /// # References -/// - [`prctl(PR_SET_NAME,...)`] +/// - [`prctl(PR_SET_NAME,…)`] /// -/// [`prctl(PR_SET_NAME,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_NAME,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_name(name: &CStr) -> io::Result<()> { unsafe { prctl_2args(PR_SET_NAME, name.as_ptr() as *mut _) }.map(|_r| ()) @@ -149,9 +148,9 @@ impl TryFrom for SecureComputingMode { /// the process is killed; see [the `proc` manual page]. /// /// # References -/// - [`prctl(PR_GET_SECCOMP,...)`] +/// - [`prctl(PR_GET_SECCOMP,…)`] /// -/// [`prctl(PR_GET_SECCOMP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_SECCOMP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html /// [the `proc` manual page]: https://man7.org/linux/man-pages/man5/proc.5.html #[inline] pub fn secure_computing_mode() -> io::Result { @@ -165,9 +164,9 @@ const PR_SET_SECCOMP: c_int = 22; /// available system calls. /// /// # References -/// - [`prctl(PR_SET_SECCOMP,...)`] +/// - [`prctl(PR_SET_SECCOMP,…)`] /// -/// [`prctl(PR_SET_SECCOMP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SECCOMP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_secure_computing_mode(mode: SecureComputingMode) -> io::Result<()> { unsafe { prctl_2args(PR_SET_SECCOMP, mode as usize as *mut _) }.map(|_r| ()) @@ -388,9 +387,9 @@ pub enum Capability { /// bounding set. /// /// # References -/// - [`prctl(PR_CAPBSET_READ,...)`] +/// - [`prctl(PR_CAPBSET_READ,…)`] /// -/// [`prctl(PR_CAPBSET_READ,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAPBSET_READ,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn capability_is_in_bounding_set(capability: Capability) -> io::Result { unsafe { prctl_2args(PR_CAPBSET_READ, capability as usize as *mut _) }.map(|r| r != 0) @@ -403,9 +402,9 @@ const PR_CAPBSET_DROP: c_int = 24; /// from the thread's capability bounding set. /// /// # References -/// - [`prctl(PR_CAPBSET_DROP,...)`] +/// - [`prctl(PR_CAPBSET_DROP,…)`] /// -/// [`prctl(PR_CAPBSET_DROP,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAPBSET_DROP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn remove_capability_from_bounding_set(capability: Capability) -> io::Result<()> { unsafe { prctl_2args(PR_CAPBSET_DROP, capability as usize as *mut _) }.map(|_r| ()) @@ -427,6 +426,8 @@ bitflags! { /// with an effective or real UID of 0 calls `execve`. const NO_ROOT = 1_u32 << 0; /// Set [`NO_ROOT`] irreversibly. + /// + /// [`NO_ROOT`]: Self::NO_ROOT const NO_ROOT_LOCKED = 1_u32 << 1; /// Setting this flag stops the kernel from adjusting the process' /// permitted, effective, and ambient capability sets when the thread's @@ -434,17 +435,23 @@ bitflags! { /// values. const NO_SETUID_FIXUP = 1_u32 << 2; /// Set [`NO_SETUID_FIXUP`] irreversibly. + /// + /// [`NO_SETUID_FIXUP`]: Self::NO_SETUID_FIXUP const NO_SETUID_FIXUP_LOCKED = 1_u32 << 3; /// Setting this flag allows a thread that has one or more 0 UIDs to /// retain capabilities in its permitted set when it switches all of /// its UIDs to nonzero values. const KEEP_CAPS = 1_u32 << 4; /// Set [`KEEP_CAPS`] irreversibly. + /// + /// [`KEEP_CAPS`]: Self::KEEP_CAPS const KEEP_CAPS_LOCKED = 1_u32 << 5; /// Setting this flag disallows raising ambient capabilities via the /// `prctl`'s `PR_CAP_AMBIENT_RAISE` operation. const NO_CAP_AMBIENT_RAISE = 1_u32 << 6; /// Set [`NO_CAP_AMBIENT_RAISE`] irreversibly. + /// + /// [`NO_CAP_AMBIENT_RAISE`]: Self::NO_CAP_AMBIENT_RAISE const NO_CAP_AMBIENT_RAISE_LOCKED = 1_u32 << 7; /// @@ -455,9 +462,9 @@ bitflags! { /// Get the `securebits` flags of the calling thread. /// /// # References -/// - [`prctl(PR_GET_SECUREBITS,...)`] +/// - [`prctl(PR_GET_SECUREBITS,…)`] /// -/// [`prctl(PR_GET_SECUREBITS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_SECUREBITS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn capabilities_secure_bits() -> io::Result { let r = unsafe { prctl_1arg(PR_GET_SECUREBITS)? } as c_uint; @@ -469,9 +476,9 @@ const PR_SET_SECUREBITS: c_int = 28; /// Set the `securebits` flags of the calling thread. /// /// # References -/// - [`prctl(PR_SET_SECUREBITS,...)`] +/// - [`prctl(PR_SET_SECUREBITS,…)`] /// -/// [`prctl(PR_SET_SECUREBITS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SECUREBITS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_capabilities_secure_bits(bits: CapabilitiesSecureBits) -> io::Result<()> { unsafe { prctl_2args(PR_SET_SECUREBITS, bits.bits() as usize as *mut _) }.map(|_r| ()) @@ -486,9 +493,9 @@ const PR_GET_TIMERSLACK: c_int = 30; /// Get the `current` timer slack value of the calling thread. /// /// # References -/// - [`prctl(PR_GET_TIMERSLACK,...)`] +/// - [`prctl(PR_GET_TIMERSLACK,…)`] /// -/// [`prctl(PR_GET_TIMERSLACK,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TIMERSLACK,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn current_timer_slack() -> io::Result { unsafe { prctl_1arg(PR_GET_TIMERSLACK) }.map(|r| r as u64) @@ -499,9 +506,9 @@ const PR_SET_TIMERSLACK: c_int = 29; /// Sets the `current` timer slack value for the calling thread. /// /// # References -/// - [`prctl(PR_SET_TIMERSLACK,...)`] +/// - [`prctl(PR_SET_TIMERSLACK,…)`] /// -/// [`prctl(PR_SET_TIMERSLACK,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TIMERSLACK,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_current_timer_slack(value: Option) -> io::Result<()> { let value = usize::try_from(value.map_or(0, NonZeroU64::get)).map_err(|_r| io::Errno::RANGE)?; @@ -517,9 +524,9 @@ const PR_GET_NO_NEW_PRIVS: c_int = 39; /// Get the value of the `no_new_privs` attribute for the calling thread. /// /// # References -/// - [`prctl(PR_GET_NO_NEW_PRIVS,...)`] +/// - [`prctl(PR_GET_NO_NEW_PRIVS,…)`] /// -/// [`prctl(PR_GET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn no_new_privs() -> io::Result { unsafe { prctl_1arg(PR_GET_NO_NEW_PRIVS) }.map(|r| r != 0) @@ -530,9 +537,9 @@ const PR_SET_NO_NEW_PRIVS: c_int = 38; /// Set the calling thread's `no_new_privs` attribute. /// /// # References -/// - [`prctl(PR_SET_NO_NEW_PRIVS,...)`] +/// - [`prctl(PR_SET_NO_NEW_PRIVS,…)`] /// -/// [`prctl(PR_SET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn set_no_new_privs(no_new_privs: bool) -> io::Result<()> { unsafe { prctl_2args(PR_SET_NO_NEW_PRIVS, usize::from(no_new_privs) as *mut _) }.map(|_r| ()) @@ -548,9 +555,9 @@ const PR_GET_TID_ADDRESS: c_int = 40; /// and `clone`'s `CLONE_CHILD_CLEARTID` flag. /// /// # References -/// - [`prctl(PR_GET_TID_ADDRESS,...)`] +/// - [`prctl(PR_GET_TID_ADDRESS,…)`] /// -/// [`prctl(PR_GET_TID_ADDRESS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TID_ADDRESS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn get_clear_child_tid_address() -> io::Result>> { unsafe { prctl_get_at_arg2_optional::<*mut c_void>(PR_GET_TID_ADDRESS) }.map(NonNull::new) @@ -565,9 +572,9 @@ const PR_GET_THP_DISABLE: c_int = 42; /// Get the current setting of the `THP disable` flag for the calling thread. /// /// # References -/// - [`prctl(PR_GET_THP_DISABLE,...)`] +/// - [`prctl(PR_GET_THP_DISABLE,…)`] /// -/// [`prctl(PR_GET_THP_DISABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_THP_DISABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn transparent_huge_pages_are_disabled() -> io::Result { unsafe { prctl_1arg(PR_GET_THP_DISABLE) }.map(|r| r != 0) @@ -578,9 +585,9 @@ const PR_SET_THP_DISABLE: c_int = 41; /// Set the state of the `THP disable` flag for the calling thread. /// /// # References -/// - [`prctl(PR_SET_THP_DISABLE,...)`] +/// - [`prctl(PR_SET_THP_DISABLE,…)`] /// -/// [`prctl(PR_SET_THP_DISABLE,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_THP_DISABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn disable_transparent_huge_pages(thp_disable: bool) -> io::Result<()> { unsafe { prctl_2args(PR_SET_THP_DISABLE, usize::from(thp_disable) as *mut _) }.map(|_r| ()) @@ -597,9 +604,9 @@ const PR_CAP_AMBIENT_IS_SET: usize = 1; /// Check if the specified capability is in the ambient set. /// /// # References -/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,...)`] +/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,…)`] /// -/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_IS_SET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn capability_is_in_ambient_set(capability: Capability) -> io::Result { let cap = capability as usize as *mut _; @@ -611,9 +618,9 @@ const PR_CAP_AMBIENT_CLEAR_ALL: usize = 4; /// Remove all capabilities from the ambient set. /// /// # References -/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,...)`] +/// - [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,…)`] /// -/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn clear_ambient_capability_set() -> io::Result<()> { unsafe { prctl_2args(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL as *mut _) }.map(|_r| ()) @@ -625,9 +632,9 @@ const PR_CAP_AMBIENT_LOWER: usize = 3; /// Add or remove the specified capability to the ambient set. /// /// # References -/// - [`prctl(PR_CAP_AMBIENT,...)`] +/// - [`prctl(PR_CAP_AMBIENT,…)`] /// -/// [`prctl(PR_CAP_AMBIENT,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_CAP_AMBIENT,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn configure_capability_in_ambient_set(capability: Capability, enable: bool) -> io::Result<()> { let sub_operation = if enable { @@ -661,9 +668,9 @@ pub struct SVEVectorLengthConfig { /// Get the thread's current SVE vector length configuration. /// /// # References -/// - [`prctl(PR_SVE_GET_VL,...)`] +/// - [`prctl(PR_SVE_GET_VL,…)`] /// -/// [`prctl(PR_SVE_GET_VL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SVE_GET_VL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn sve_vector_length_configuration() -> io::Result { let bits = unsafe { prctl_1arg(PR_SVE_GET_VL)? } as c_uint; @@ -680,14 +687,14 @@ const PR_SVE_SET_VL_ONEXEC: u32 = 1_u32 << 18; /// Configure the thread's vector length of Scalable Vector Extension. /// /// # References -/// - [`prctl(PR_SVE_SET_VL,...)`] +/// - [`prctl(PR_SVE_SET_VL,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// -/// [`prctl(PR_SVE_SET_VL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SVE_SET_VL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn set_sve_vector_length_configuration( vector_length_in_bytes: usize, @@ -720,14 +727,14 @@ const PR_PAC_RESET_KEYS: c_int = 54; /// values generated by the kernel. /// /// # References -/// - [`prctl(PR_PAC_RESET_KEYS,...)`] +/// - [`prctl(PR_PAC_RESET_KEYS,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, /// as detailed in the references above. /// -/// [`prctl(PR_PAC_RESET_KEYS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_PAC_RESET_KEYS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn reset_pointer_authentication_keys( keys: Option, @@ -767,9 +774,9 @@ bitflags! { /// Get the current tagged address mode for the calling thread. /// /// # References -/// - [`prctl(PR_GET_TAGGED_ADDR_CTRL,...)`] +/// - [`prctl(PR_GET_TAGGED_ADDR_CTRL,…)`] /// -/// [`prctl(PR_GET_TAGGED_ADDR_CTRL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_GET_TAGGED_ADDR_CTRL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub fn current_tagged_address_mode() -> io::Result<(Option, u32)> { let r = unsafe { prctl_1arg(PR_GET_TAGGED_ADDR_CTRL)? } as c_uint; @@ -783,14 +790,14 @@ const PR_SET_TAGGED_ADDR_CTRL: c_int = 55; /// Controls support for passing tagged user-space addresses to the kernel. /// /// # References -/// - [`prctl(PR_SET_TAGGED_ADDR_CTRL,...)`] +/// - [`prctl(PR_SET_TAGGED_ADDR_CTRL,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_TAGGED_ADDR_CTRL,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_TAGGED_ADDR_CTRL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn set_current_tagged_address_mode( mode: Option, @@ -812,14 +819,14 @@ const PR_SYS_DISPATCH_OFF: usize = 0; /// Disable Syscall User Dispatch mechanism. /// /// # References -/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,...)`] +/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_OFF,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn disable_syscall_user_dispatch() -> io::Result<()> { prctl_2args(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF as *mut _).map(|_r| ()) @@ -858,14 +865,14 @@ impl TryFrom for SysCallUserDispatchFastSwitch { /// Enable Syscall User Dispatch mechanism. /// /// # References -/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,...)`] +/// - [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,…)`] /// /// # Safety /// /// Please ensure the conditions necessary to safely call this function, as /// detailed in the references above. /// -/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_SET_SYSCALL_USER_DISPATCH,PR_SYS_DISPATCH_ON,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html #[inline] pub unsafe fn enable_syscall_user_dispatch( always_allowed_region: &[u8], @@ -922,9 +929,9 @@ impl TryFrom for CoreSchedulingScope { /// Get core scheduling cookie of a process. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_GET,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result { let mut value: MaybeUninit = MaybeUninit::uninit(); @@ -945,9 +952,9 @@ const PR_SCHED_CORE_CREATE: usize = 1; /// Create unique core scheduling cookie. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_CREATE,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn create_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> { unsafe { @@ -967,9 +974,9 @@ const PR_SCHED_CORE_SHARE_TO: usize = 2; /// Push core scheduling cookie to a process. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_TO,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn push_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> { unsafe { @@ -989,9 +996,9 @@ const PR_SCHED_CORE_SHARE_FROM: usize = 3; /// Pull core scheduling cookie from a process. /// /// # References -/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,...)`] +/// - [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,…)`] /// -/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,...)`]: https://www.kernel.org/doc/html/v5.18/admin-guide/hw-vuln/core-scheduling.html +/// [`prctl(PR_SCHED_CORE,PR_SCHED_CORE_SHARE_FROM,…)`]: https://www.kernel.org/doc/html/v6.10/admin-guide/hw-vuln/core-scheduling.html #[inline] pub fn pull_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> { unsafe { diff --git a/tests/event/poll.rs b/tests/event/poll.rs index 27ad34e05..f5c595800 100644 --- a/tests/event/poll.rs +++ b/tests/event/poll.rs @@ -51,7 +51,7 @@ fn test_poll() { #[test] fn test_poll_fd_set_fd() { - // Make up some file descriptors so that we can test that set_fd works. + // Make up some file descriptors so that we can test that `set_fd` works. let a = unsafe { OwnedFd::from_raw_fd(777) }; let mut poll_fd = PollFd::new(&a, PollFlags::empty()); assert_eq!(poll_fd.as_fd().as_raw_fd(), 777); diff --git a/tests/fs/seek.rs b/tests/fs/seek.rs index fe4f4409c..d279ad705 100644 --- a/tests/fs/seek.rs +++ b/tests/fs/seek.rs @@ -1,4 +1,4 @@ -/// Test seek positions related to file "holes". +/// Test seek positions related to file “holes”. #[cfg(any(apple, freebsdlike, linux_kernel, solarish))] #[test] fn test_seek_holes() { diff --git a/tests/net/sockopt.rs b/tests/net/sockopt.rs index 60d776e02..b98a6b4cd 100644 --- a/tests/net/sockopt.rs +++ b/tests/net/sockopt.rs @@ -59,7 +59,8 @@ fn test_sockopts_socket(s: &OwnedFd) { >= Duration::new(1, 1) ); } else { - // On FreeBSD <= 12 and NetBSD, it appears the system rounds the timeout down. + // On FreeBSD ≤ 12 and NetBSD, it appears the system rounds the timeout + // down. assert!( sockopt::get_socket_timeout(s, sockopt::Timeout::Recv) .unwrap() @@ -141,7 +142,7 @@ fn test_sockopts_socket(s: &OwnedFd) { // Check that the oobinline flag is set. assert!(sockopt::get_socket_oobinline(s).unwrap()); - // Check the initial value of SO_REUSEPORT, set it, and check it. + // Check the initial value of `SO_REUSEPORT`, set it, and check it. #[cfg(not(any(solarish, windows)))] { assert!(!sockopt::get_socket_reuseport(s).unwrap()); @@ -149,7 +150,7 @@ fn test_sockopts_socket(s: &OwnedFd) { assert!(sockopt::get_socket_reuseport(s).unwrap()); } - // Check the initial value of SO_REUSEPORT_LB, set it, and check it. + // Check the initial value of `SO_REUSEPORT_LB`, set it, and check it. #[cfg(target_os = "freebsd")] { assert!(!sockopt::get_socket_reuseport_lb(s).unwrap()); @@ -167,7 +168,7 @@ fn test_sockopts_socket(s: &OwnedFd) { ); } - // Check the initial value of SO_INCOMING_CPU, set it, and check it. + // Check the initial value of `SO_INCOMING_CPU`, set it, and check it. #[cfg(target_os = "linux")] { assert_eq!(sockopt::get_socket_incoming_cpu(s).unwrap(), u32::MAX); @@ -175,7 +176,7 @@ fn test_sockopts_socket(s: &OwnedFd) { assert_eq!(sockopt::get_socket_incoming_cpu(s).unwrap(), 3); } - // Check the initial value of SO_NOSIGPIPE, set it, and check it. + // Check the initial value of `SO_NOSIGPIPE`, set it, and check it. #[cfg(any(apple, freebsdlike, target_os = "netbsd"))] { assert_eq!(sockopt::get_socket_nosigpipe(s).unwrap(), false); @@ -242,7 +243,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { } } - // Check the initial value of TCP_QUICKACK, set it, and check it. + // Check the initial value of `TCP_QUICKACK`, set it, and check it. #[cfg(any(linux_like, target_os = "fuchsia"))] { assert!(sockopt::get_tcp_quickack(s).unwrap()); @@ -250,7 +251,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { assert!(!sockopt::get_tcp_quickack(s).unwrap()); } - // Check the initial value of TCP_CONGESTION, set it, and check it. + // Check the initial value of `TCP_CONGESTION`, set it, and check it. // // Temporarily disable this test on non-x86 as qemu isn't yet aware of // TCP_CONGESTION. @@ -272,7 +273,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { } } - // Check the initial value of TCP_THIN_LINEAR_TIMEOUTS, set it, and check + // Check the initial value of `TCP_THIN_LINEAR_TIMEOUTS`, set it, and check // it. #[cfg(any(linux_like, target_os = "fuchsia"))] { @@ -281,7 +282,7 @@ fn test_sockopts_tcp(s: &OwnedFd) { assert!(sockopt::get_tcp_thin_linear_timeouts(s).unwrap()); } - // Check the initial value of TCP_CORK, set it, and check it. + // Check the initial value of `TCP_CORK`, set it, and check it. #[cfg(any(linux_like, solarish, target_os = "fuchsia"))] { assert!(!sockopt::get_tcp_cork(s).unwrap()); @@ -331,7 +332,7 @@ fn test_sockopts_ipv4() { assert!(!sockopt::get_ip_multicast_loop(&s).unwrap()); } - // Check the initial value of IP TOS, set it, and check it. + // Check the initial value of `IP_TOS`, set it, and check it. #[cfg(any( bsd, linux_like, @@ -351,7 +352,7 @@ fn test_sockopts_ipv4() { } } - // Check the initial value of IP RECVTOS, set it, and check it. + // Check the initial value of `IP_RECVTOS`, set it, and check it. #[cfg(any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia"))] { assert!(!sockopt::get_ip_recvtos(&s).unwrap()); @@ -359,7 +360,7 @@ fn test_sockopts_ipv4() { assert!(sockopt::get_ip_recvtos(&s).unwrap()); } - // Check the initial value of IP_FREEBIND, set it, and check it. + // Check the initial value of `IP_FREEBIND`, set it, and check it. #[cfg(any(linux_kernel, target_os = "fuchsia"))] { assert!(!sockopt::get_ip_freebind(&s).unwrap()); @@ -367,7 +368,7 @@ fn test_sockopts_ipv4() { assert!(sockopt::get_ip_freebind(&s).unwrap()); } - // Check that we can query SO_ORIGINAL_DST. + // Check that we can query `SO_ORIGINAL_DST`. #[cfg(any(linux_kernel, target_os = "fuchsia"))] { assert!(matches!( @@ -456,7 +457,7 @@ fn test_sockopts_ipv6() { // Check that the IPV6 unicast hops value is set. assert_eq!(sockopt::get_ipv6_unicast_hops(&s).unwrap(), 8); - // Check the initial value of IPV6 RECVTCLASS, set it, and check it. + // Check the initial value of `IPV6_RECVTCLASS`, set it, and check it. #[cfg(any( bsd, linux_like, @@ -470,7 +471,7 @@ fn test_sockopts_ipv6() { assert!(sockopt::get_ipv6_recvtclass(&s).unwrap()); } - // Check the initial value of IPV6_FREEBIND, set it, and check it. + // Check the initial value of `IPV6_FREEBIND`, set it, and check it. #[cfg(linux_kernel)] { assert!(!sockopt::get_ipv6_freebind(&s).unwrap()); @@ -478,7 +479,7 @@ fn test_sockopts_ipv6() { assert!(sockopt::get_ipv6_freebind(&s).unwrap()); } - // Check the initial value of IPV6_TCLASS, set it, and check it. + // Check the initial value of `IPV6_TCLASS`, set it, and check it. #[cfg(not(any(solarish, windows, target_os = "espidf", target_os = "haiku")))] { assert_eq!(sockopt::get_ipv6_tclass(&s).unwrap(), 0); @@ -486,7 +487,7 @@ fn test_sockopts_ipv6() { assert_eq!(sockopt::get_ipv6_tclass(&s).unwrap(), 12); } - // Check that we can query IP6T_SO_ORIGINAL_DST. + // Check that we can query `IP6T_SO_ORIGINAL_DST`. #[cfg(linux_kernel)] { assert!(matches!( diff --git a/tests/net/unix.rs b/tests/net/unix.rs index a9652f1c1..acfacdc0d 100644 --- a/tests/net/unix.rs +++ b/tests/net/unix.rs @@ -2,7 +2,8 @@ //! //! The client sends lists of integers and the server sends back sums. -// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on macOS. +// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on +// macOS. #![cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))] // This test uses `DecInt`. #![cfg(feature = "itoa")] diff --git a/tests/net/unix_alloc.rs b/tests/net/unix_alloc.rs index 5bb241f79..5ed2b4b28 100644 --- a/tests/net/unix_alloc.rs +++ b/tests/net/unix_alloc.rs @@ -1,6 +1,7 @@ //! Like unix.rs, but uses `Vec`s for the buffers. -// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on macOS. +// This test uses `AF_UNIX` with `SOCK_SEQPACKET` which is unsupported on +// macOS. #![cfg(not(any(apple, target_os = "espidf", target_os = "redox", target_os = "wasi")))] // This test uses `DecInt`. #![cfg(feature = "itoa")] diff --git a/tests/param/weak.rs b/tests/param/weak.rs index 4859a5b37..8fdd946d5 100644 --- a/tests/param/weak.rs +++ b/tests/param/weak.rs @@ -77,7 +77,7 @@ impl Weak { // need to have loaded it with at least C11's consume // ordering in order to be guaranteed that the data we read // from the pointer isn't from before the pointer was - // stored. Rust has no equivalent to memory_order_consume, + // stored. Rust has no equivalent to `memory_order_consume`, // so we use an acquire fence (sorry, ARM). // // Now, in practice this likely isn't needed even on CPUs From 7bc53a7399f0e952e044a2591b9822beb0a72b34 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 26 Aug 2024 06:22:37 -0700 Subject: [PATCH 39/54] Fix test compilation on Redox. (#1130) --- tests/fs/seek.rs | 4 ++-- tests/termios/pgrp.rs | 4 ++-- tests/termios/sid.rs | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/fs/seek.rs b/tests/fs/seek.rs index d279ad705..c483817fd 100644 --- a/tests/fs/seek.rs +++ b/tests/fs/seek.rs @@ -61,9 +61,9 @@ fn test_seek_holes() { #[test] fn test_seek_offsets() { - use rustix::fs::{openat, seek, Mode, OFlags, SeekFrom, CWD}; + use rustix::fs::{open, seek, Mode, OFlags, SeekFrom}; - let f = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + let f = open("Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); match seek(&f, SeekFrom::Start(0)) { Ok(_) => {} diff --git a/tests/termios/pgrp.rs b/tests/termios/pgrp.rs index 6b9e2a798..d43198f62 100644 --- a/tests/termios/pgrp.rs +++ b/tests/termios/pgrp.rs @@ -19,8 +19,8 @@ fn pgrp_notty() { } // Disable on illumos where `tcgetattr` doesn't appear to support -// pseudoterminals. -#[cfg(not(target_os = "illumos"))] +// pseudoterminals. And on Redox which lacks `NOCTTY`. +#[cfg(not(any(target_os = "illumos", target_os = "redox")))] #[cfg(feature = "pty")] #[test] fn pgrp_pseudoterminal() { diff --git a/tests/termios/sid.rs b/tests/termios/sid.rs index 68de2e2c4..28bc94eb0 100644 --- a/tests/termios/sid.rs +++ b/tests/termios/sid.rs @@ -17,6 +17,8 @@ fn sid_notty() { assert_eq!(tcgetsid(&fd), Err(Errno::NOTTY)); } +// Disable on Redox which lacks `getsid`. +#[cfg(not(target_os = "redox"))] #[cfg(all(feature = "stdio", feature = "process"))] #[test] fn sid_match() { From a01a716fbe6d85dcb2954c71f101e7989ed7ec54 Mon Sep 17 00:00:00 2001 From: primoly <168267431+primoly@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:51:35 +0200 Subject: [PATCH 40/54] Do some todos (#1092) * Do some todos * Add a `MOVE_MOUNT_BENEATH` constant to linux_raw as a temporary workaround. * Document that `MOVE_MOUNT_BENEATH` is since Linux 6.5. --------- Co-authored-by: Dan Gohman --- src/backend/libc/mount/types.rs | 5 ++--- src/backend/linux_raw/c.rs | 3 +++ src/backend/linux_raw/mount/types.rs | 5 ++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/backend/libc/mount/types.rs b/src/backend/libc/mount/types.rs index 10236866c..4df8fbc35 100644 --- a/src/backend/libc/mount/types.rs +++ b/src/backend/libc/mount/types.rs @@ -227,9 +227,8 @@ bitflags! { /// `MOVE_MOUNT__MASK` const MOVE_MOUNT_SET_GROUP = 0x0000_0100; - // TODO: add when Linux 6.5 is released - // /// `MOVE_MOUNT_BENEATH` - // const MOVE_MOUNT_BENEATH = 0x0000_0200; + /// `MOVE_MOUNT_BENEATH` (since Linux 6.5) + const MOVE_MOUNT_BENEATH = 0x0000_0200; /// `MOVE_MOUNT__MASK` const MOVE_MOUNT__MASK = 0x0000_0377; diff --git a/src/backend/linux_raw/c.rs b/src/backend/linux_raw/c.rs index 6e168299c..70bc46a8c 100644 --- a/src/backend/linux_raw/c.rs +++ b/src/backend/linux_raw/c.rs @@ -320,3 +320,6 @@ pub(crate) use reboot_symbols::*; // TODO: This is new in Linux 6.11; remove when linux-raw-sys is updated. pub(crate) const MAP_DROPPABLE: u32 = 0x8; + +// TODO: This is new in Linux 6.5; remove when linux-raw-sys is updated. +pub(crate) const MOVE_MOUNT_BENEATH: u32 = 0x200; diff --git a/src/backend/linux_raw/mount/types.rs b/src/backend/linux_raw/mount/types.rs index 43cb8c0d5..4ca38c0ee 100644 --- a/src/backend/linux_raw/mount/types.rs +++ b/src/backend/linux_raw/mount/types.rs @@ -223,9 +223,8 @@ bitflags! { /// `MOVE_MOUNT__MASK` const MOVE_MOUNT_SET_GROUP = linux_raw_sys::general::MOVE_MOUNT_SET_GROUP; - // TODO: add when Linux 6.5 is released - // /// `MOVE_MOUNT_BENEATH` - // const MOVE_MOUNT_BENEATH = linux_raw_sys::general::MOVE_MOUNT_BENEATH; + /// `MOVE_MOUNT_BENEATH` (since Linux 6.5) + const MOVE_MOUNT_BENEATH = c::MOVE_MOUNT_BENEATH; /// `MOVE_MOUNT__MASK` const MOVE_MOUNT__MASK = linux_raw_sys::general::MOVE_MOUNT__MASK; From 777fbc684adaa6649c54439e193c171ee45f2d70 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Mon, 26 Aug 2024 07:03:56 -0700 Subject: [PATCH 41/54] Implement InotifyReader for raw inotify event iteration (#1113) * Implement InotifyReader for raw inotify event iteration Signed-off-by: Alex Saveau * Add inotify test Signed-off-by: Alex Saveau --------- Signed-off-by: Alex Saveau --- src/backend/libc/fs/inotify.rs | 46 +++++++++++ src/backend/linux_raw/fs/inotify.rs | 46 +++++++++++ src/fs/inotify.rs | 121 +++++++++++++++++++++++++++- tests/fs/inotify.rs | 109 +++++++++++++++++++++++++ tests/fs/main.rs | 2 + 5 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 tests/fs/inotify.rs diff --git a/src/backend/libc/fs/inotify.rs b/src/backend/libc/fs/inotify.rs index 3be68c7cf..36911689a 100644 --- a/src/backend/libc/fs/inotify.rs +++ b/src/backend/libc/fs/inotify.rs @@ -76,3 +76,49 @@ bitflags! { const _ = !0; } } + +bitflags! { + /// `IN*` for use with [`InotifyReader`]. + /// + /// [`InotifyReader`]: crate::fs::inotify::InotifyReader + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReadFlags: u32 { + /// `IN_ACCESS` + const ACCESS = c::IN_ACCESS; + /// `IN_ATTRIB` + const ATTRIB = c::IN_ATTRIB; + /// `IN_CLOSE_NOWRITE` + const CLOSE_NOWRITE = c::IN_CLOSE_NOWRITE; + /// `IN_CLOSE_WRITE` + const CLOSE_WRITE = c::IN_CLOSE_WRITE; + /// `IN_CREATE` + const CREATE = c::IN_CREATE; + /// `IN_DELETE` + const DELETE = c::IN_DELETE; + /// `IN_DELETE_SELF` + const DELETE_SELF = c::IN_DELETE_SELF; + /// `IN_MODIFY` + const MODIFY = c::IN_MODIFY; + /// `IN_MOVE_SELF` + const MOVE_SELF = c::IN_MOVE_SELF; + /// `IN_MOVED_FROM` + const MOVED_FROM = c::IN_MOVED_FROM; + /// `IN_MOVED_TO` + const MOVED_TO = c::IN_MOVED_TO; + /// `IN_OPEN` + const OPEN = c::IN_OPEN; + + /// `IN_IGNORED` + const IGNORED = c::IN_IGNORED; + /// `IN_ISDIR` + const ISDIR = c::IN_ISDIR; + /// `IN_Q_OVERFLOW` + const QUEUE_OVERFLOW = c::IN_Q_OVERFLOW; + /// `IN_UNMOUNT` + const UNMOUNT = c::IN_UNMOUNT; + + /// + const _ = !0; + } +} diff --git a/src/backend/linux_raw/fs/inotify.rs b/src/backend/linux_raw/fs/inotify.rs index 1876e8349..d517151e7 100644 --- a/src/backend/linux_raw/fs/inotify.rs +++ b/src/backend/linux_raw/fs/inotify.rs @@ -76,3 +76,49 @@ bitflags! { const _ = !0; } } + +bitflags! { + /// `IN*` for use with [`InotifyReader`]. + /// + /// [`InotifyReader`]: crate::fs::inotify::InotifyReader + #[repr(transparent)] + #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReadFlags: c::c_uint { + /// `IN_ACCESS` + const ACCESS = linux_raw_sys::general::IN_ACCESS; + /// `IN_ATTRIB` + const ATTRIB = linux_raw_sys::general::IN_ATTRIB; + /// `IN_CLOSE_NOWRITE` + const CLOSE_NOWRITE = linux_raw_sys::general::IN_CLOSE_NOWRITE; + /// `IN_CLOSE_WRITE` + const CLOSE_WRITE = linux_raw_sys::general::IN_CLOSE_WRITE; + /// `IN_CREATE` + const CREATE = linux_raw_sys::general::IN_CREATE; + /// `IN_DELETE` + const DELETE = linux_raw_sys::general::IN_DELETE; + /// `IN_DELETE_SELF` + const DELETE_SELF = linux_raw_sys::general::IN_DELETE_SELF; + /// `IN_MODIFY` + const MODIFY = linux_raw_sys::general::IN_MODIFY; + /// `IN_MOVE_SELF` + const MOVE_SELF = linux_raw_sys::general::IN_MOVE_SELF; + /// `IN_MOVED_FROM` + const MOVED_FROM = linux_raw_sys::general::IN_MOVED_FROM; + /// `IN_MOVED_TO` + const MOVED_TO = linux_raw_sys::general::IN_MOVED_TO; + /// `IN_OPEN` + const OPEN = linux_raw_sys::general::IN_OPEN; + + /// `IN_IGNORED` + const IGNORED = linux_raw_sys::general::IN_IGNORED; + /// `IN_ISDIR` + const ISDIR = linux_raw_sys::general::IN_ISDIR; + /// `IN_Q_OVERFLOW` + const QUEUE_OVERFLOW = linux_raw_sys::general::IN_Q_OVERFLOW; + /// `IN_UNMOUNT` + const UNMOUNT = linux_raw_sys::general::IN_UNMOUNT; + + /// + const _ = !0; + } +} diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index addcfda57..21b85aca0 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -1,9 +1,13 @@ //! inotify support for working with inotifies -pub use crate::backend::fs::inotify::{CreateFlags, WatchFlags}; +pub use crate::backend::fs::inotify::{CreateFlags, ReadFlags, WatchFlags}; use crate::backend::fs::syscalls; use crate::fd::{AsFd, OwnedFd}; +use crate::ffi::CStr; use crate::io; +use crate::io::{read_uninit, Errno}; +use core::mem::{align_of, size_of, MaybeUninit}; +use linux_raw_sys::general::inotify_event; /// `inotify_init1(flags)`—Creates a new inotify object. /// @@ -41,3 +45,118 @@ pub fn inotify_add_watch( pub fn inotify_remove_watch(inot: impl AsFd, wd: i32) -> io::Result<()> { syscalls::inotify_rm_watch(inot.as_fd(), wd) } + +/// An inotify event iterator implemented with the read syscall. +/// +/// See the [`RawDir`] API for more details and usage examples as this API is +/// based on it. +/// +/// [`RawDir`]: crate::fs::raw_dir::RawDir +pub struct InotifyReader<'buf, Fd: AsFd> { + fd: Fd, + buf: &'buf mut [MaybeUninit], + initialized: usize, + offset: usize, +} + +impl<'buf, Fd: AsFd> InotifyReader<'buf, Fd> { + /// Create a new iterator from the given file descriptor and buffer. + pub fn new(fd: Fd, buf: &'buf mut [MaybeUninit]) -> Self { + Self { + fd, + buf: { + let offset = buf.as_ptr().align_offset(align_of::()); + if offset < buf.len() { + &mut buf[offset..] + } else { + &mut [] + } + }, + initialized: 0, + offset: 0, + } + } +} + +/// An inotify event. +#[derive(Debug)] +pub struct InotifyEvent<'a> { + wd: i32, + events: ReadFlags, + cookie: u32, + file_name: Option<&'a CStr>, +} + +impl<'a> InotifyEvent<'a> { + /// Returns the watch for which this event occurs. + #[inline] + pub fn wd(&self) -> i32 { + self.wd + } + + /// Returns a description of the events. + #[inline] + #[doc(alias = "mask")] + pub fn events(&self) -> ReadFlags { + self.events + } + + /// Returns the unique cookie associating related events. + #[inline] + pub fn cookie(&self) -> u32 { + self.cookie + } + + /// Returns the file name of this event, if any. + #[inline] + pub fn file_name(&self) -> Option<&CStr> { + self.file_name + } +} + +impl<'buf, Fd: AsFd> InotifyReader<'buf, Fd> { + /// Read the next inotify event. + #[allow(unsafe_code)] + pub fn next(&mut self) -> io::Result { + if self.is_buffer_empty() { + match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { + Ok(0) => return Err(Errno::INVAL), + Ok(bytes_read) => { + self.initialized = bytes_read; + self.offset = 0; + } + Err(e) => return Err(e), + } + } + + let ptr = self.buf[self.offset..].as_ptr(); + // SAFETY: + // - This data is initialized by the check above. + // - Assumption: the kernel will not give us partial structs. + // - Assumption: the kernel uses proper alignment between structs. + // - The starting pointer is aligned (performed in RawDir::new) + let event = unsafe { &*ptr.cast::() }; + + self.offset += size_of::() + usize::try_from(event.len).unwrap(); + + Ok(InotifyEvent { + wd: event.wd, + events: ReadFlags::from_bits_retain(event.mask), + cookie: event.cookie, + file_name: if event.len > 0 { + // SAFETY: The kernel guarantees a NUL-terminated string. + Some(unsafe { CStr::from_ptr(event.name.as_ptr().cast()) }) + } else { + None + }, + }) + } + + /// Returns true if the internal buffer is empty and will be refilled when + /// calling [`next`]. This is useful to avoid further blocking reads. + /// + /// [`next`]: Self::next + pub fn is_buffer_empty(&self) -> bool { + self.offset >= self.initialized + } +} diff --git a/tests/fs/inotify.rs b/tests/fs/inotify.rs new file mode 100644 index 000000000..0e5f7c1ee --- /dev/null +++ b/tests/fs/inotify.rs @@ -0,0 +1,109 @@ +use rustix::fs::inotify::{ + inotify_add_watch, inotify_init, CreateFlags, InotifyReader, WatchFlags, +}; +use rustix::io::Errno; +use std::fmt::Write; +use std::fs::{create_dir_all, remove_file, rename, File}; +use std::mem::MaybeUninit; + +#[test] +fn test_inotify_iter() { + let inotify = inotify_init(CreateFlags::NONBLOCK).unwrap(); + create_dir_all("/tmp/.rustix-inotify-test").unwrap(); + inotify_add_watch( + &inotify, + "/tmp/.rustix-inotify-test", + WatchFlags::ALL_EVENTS, + ) + .unwrap(); + + File::create("/tmp/.rustix-inotify-test/foo").unwrap(); + rename( + "/tmp/.rustix-inotify-test/foo", + "/tmp/.rustix-inotify-test/bar", + ) + .unwrap(); + remove_file("/tmp/.rustix-inotify-test/bar").unwrap(); + + let mut output = String::new(); + let mut cookie = 0; + + let mut buf = [MaybeUninit::uninit(); 512]; + let mut iter = InotifyReader::new(inotify, &mut buf); + loop { + let e = match iter.next() { + Err(Errno::WOULDBLOCK) => break, + r => r.unwrap(), + }; + + writeln!(output, "{e:#?}").unwrap(); + if e.cookie() != 0 { + cookie = e.cookie(); + } + } + + let expected = format!( + r#"InotifyEvent {{ + wd: 1, + events: ReadFlags( + CREATE, + ), + cookie: 0, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + OPEN, + ), + cookie: 0, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + CLOSE_WRITE, + ), + cookie: 0, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + MOVED_FROM, + ), + cookie: {cookie}, + file_name: Some( + "foo", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + MOVED_TO, + ), + cookie: {cookie}, + file_name: Some( + "bar", + ), +}} +InotifyEvent {{ + wd: 1, + events: ReadFlags( + DELETE, + ), + cookie: 0, + file_name: Some( + "bar", + ), +}} +"# + ); + assert_eq!(expected, output); +} diff --git a/tests/fs/main.rs b/tests/fs/main.rs index 4ab608fc7..db074fedb 100644 --- a/tests/fs/main.rs +++ b/tests/fs/main.rs @@ -20,6 +20,8 @@ mod file; #[cfg(not(target_os = "wasi"))] mod flock; mod futimens; +#[cfg(linux_kernel)] +mod inotify; mod invalid_offset; #[cfg(not(target_os = "redox"))] mod ioctl; From c700ad7543b8e8982c3b1434cddb3cd69e33e980 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Mon, 26 Aug 2024 11:18:40 -0700 Subject: [PATCH 42/54] Unify epoll backend implementations (#1131) * Unify epoll backend implementations Signed-off-by: Alex Saveau * Use new module prefix style Signed-off-by: Alex Saveau --------- Signed-off-by: Alex Saveau --- src/backend/libc/event/epoll.rs | 473 +----------------------- src/backend/libc/event/syscalls.rs | 99 ++++- src/backend/linux_raw/event/epoll.rs | 438 +--------------------- src/backend/linux_raw/event/syscalls.rs | 81 ++-- src/event/epoll.rs | 444 ++++++++++++++++++++++ src/event/mod.rs | 4 +- 6 files changed, 583 insertions(+), 956 deletions(-) create mode 100644 src/event/epoll.rs diff --git a/src/backend/libc/event/epoll.rs b/src/backend/libc/event/epoll.rs index ac0b4ca70..7473f6857 100644 --- a/src/backend/libc/event/epoll.rs +++ b/src/backend/libc/event/epoll.rs @@ -1,91 +1,10 @@ -//! Linux `epoll` support. -//! -//! # Examples -//! -//! ```no_run -//! # #[cfg(feature = "net")] -//! # fn main() -> std::io::Result<()> { -//! use rustix::event::epoll; -//! use rustix::fd::AsFd; -//! use rustix::io::{ioctl_fionbio, read, write}; -//! use rustix::net::{ -//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, -//! }; -//! use std::collections::HashMap; -//! use std::os::unix::io::AsRawFd; -//! -//! // Create a socket and listen on it. -//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; -//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; -//! listen(&listen_sock, 1)?; -//! -//! // Create an epoll object. Using `Owning` here means the epoll object will -//! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; -//! -//! // Register the socket with the epoll object. -//! epoll::add( -//! &epoll, -//! &listen_sock, -//! epoll::EventData::new_u64(1), -//! epoll::EventFlags::IN, -//! )?; -//! -//! // Keep track of the sockets we've opened. -//! let mut next_id = epoll::EventData::new_u64(2); -//! let mut sockets = HashMap::new(); -//! -//! // Process events. -//! let mut event_list = epoll::EventVec::with_capacity(4); -//! loop { -//! epoll::wait(&epoll, &mut event_list, -1)?; -//! for event in &event_list { -//! let target = event.data; -//! if target.u64() == 1 { -//! // Accept a new connection, set it to non-blocking, and -//! // register to be notified when it's ready to write to. -//! let conn_sock = accept(&listen_sock)?; -//! ioctl_fionbio(&conn_sock, true)?; -//! epoll::add( -//! &epoll, -//! &conn_sock, -//! next_id, -//! epoll::EventFlags::OUT | epoll::EventFlags::ET, -//! )?; -//! -//! // Keep track of the socket. -//! sockets.insert(next_id, conn_sock); -//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); -//! } else { -//! // Write a message to the stream and then unregister it. -//! let target = sockets.remove(&target).unwrap(); -//! write(&target, b"hello\n")?; -//! let _ = epoll::delete(&epoll, &target)?; -//! } -//! } -//! } -//! # } -//! # #[cfg(not(feature = "net"))] -//! # fn main() {} -//! ``` - use crate::backend::c; -#[cfg(feature = "alloc")] -use crate::backend::conv::ret_u32; -use crate::backend::conv::{ret, ret_owned_fd}; -use crate::fd::{AsFd, AsRawFd, OwnedFd}; -use crate::io; -use crate::utils::as_mut_ptr; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use bitflags::bitflags; -use core::ffi::c_void; -use core::hash::{Hash, Hasher}; -use core::ptr::null_mut; -use core::slice; bitflags! { - /// `EPOLL_*` for use with [`create`]. + /// `EPOLL_*` for use with [`epoll::create`]. + /// + /// [`epoll::create`]: crate::event::epoll::create #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: u32 { @@ -98,7 +17,9 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`add`]. + /// `EPOLL*` for use with [`epoll::add`]. + /// + /// [`epoll::add`]: crate::event::epoll::add #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { @@ -152,385 +73,3 @@ bitflags! { const _ = !0; } } - -/// `epoll_create1(flags)`—Creates a new epoll object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html -#[inline] -#[doc(alias = "epoll_create1")] -pub fn create(flags: CreateFlags) -> io::Result { - // SAFETY: We're calling `epoll_create1` via FFI and we know how it - // behaves. - unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) } -} - -/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll -/// object. -/// -/// This registers interest in any of the events set in `events` occurring on -/// the file descriptor associated with `data`. -/// -/// Note that `close`ing a file descriptor does not necessarily unregister -/// interest which can lead to spurious events being returned from [`wait`]. If -/// a file descriptor is an `Arc`, then `epoll` can be -/// thought to maintain a `Weak` to the file descriptor. -/// Check the [faq] for details. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F -#[doc(alias = "epoll_ctl")] -pub fn add( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. We use our own `Event` struct instead of libc's because - // ours preserves pointer provenance instead of just using a `u64`, - // and we have tests elsewhere for layout equivalence. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_ADD, - raw_fd, - as_mut_ptr(&mut Event { - flags: event_flags, - data, - #[cfg(target_os = "redox")] - _pad: 0, - }) - .cast(), - )) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a -/// given epoll object. -/// -/// This sets the events of interest with `target` to `events`. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -pub fn modify( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - let raw_fd = source.as_fd().as_raw_fd(); - - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. We use our own `Event` struct instead of libc's because - // ours preserves pointer provenance instead of just using a `u64`, - // and we have tests elsewhere for layout equivalence. - unsafe { - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_MOD, - raw_fd, - as_mut_ptr(&mut Event { - flags: event_flags, - data, - #[cfg(target_os = "redox")] - _pad: 0, - }) - .cast(), - )) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a -/// given epoll object. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - ret(c::epoll_ctl( - epoll.as_fd().as_raw_fd(), - c::EPOLL_CTL_DEL, - raw_fd, - null_mut(), - )) - } -} - -/// `epoll_wait(self, events, timeout)`—Waits for registered events of -/// interest. -/// -/// For each event of interest, an element is written to `events`. On -/// success, this returns the number of written elements. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] -pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { - // SAFETY: We're calling `epoll_wait` via FFI and we know how it - // behaves. - unsafe { - event_list.events.set_len(0); - let nfds = ret_u32(c::epoll_wait( - epoll.as_fd().as_raw_fd(), - event_list.events.as_mut_ptr().cast::(), - event_list.events.capacity().try_into().unwrap_or(i32::MAX), - timeout, - ))?; - event_list.events.set_len(nfds as usize); - } - - Ok(()) -} - -/// An iterator over the `Event`s in an `EventVec`. -pub struct Iter<'a> { - /// Use `Copied` to copy the struct, since `Event` is `packed` on some - /// platforms, and it's common for users to directly destructure it, which - /// would lead to errors about forming references to packed fields. - iter: core::iter::Copied>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = Event; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -/// A record of an event that occurred. -#[repr(C)] -#[cfg_attr( - all( - linux_kernel, - any( - all( - target_arch = "x86", - not(target_env = "musl"), - not(target_os = "android"), - ), - target_arch = "x86_64", - ) - ), - repr(packed) -)] -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Event { - /// Which specific event(s) occurred. - pub flags: EventFlags, - /// User data. - pub data: EventData, - - #[cfg(target_os = "redox")] - _pad: u64, -} - -/// Data associated with an [`Event`]. This can either be a 64-bit integer -/// value or a pointer which preserves pointer provenance. -#[repr(C)] -#[derive(Copy, Clone)] -pub union EventData { - /// A 64-bit integer value. - as_u64: u64, - - /// A `*mut c_void` which preserves pointer provenance, extended to be - /// 64-bit so that if we read the value as a `u64` union field, we don't - /// get uninitialized memory. - sixty_four_bit_pointer: SixtyFourBitPointer, -} - -impl EventData { - /// Construct a new value containing a `u64`. - #[inline] - pub const fn new_u64(value: u64) -> Self { - Self { as_u64: value } - } - - /// Construct a new value containing a `*mut c_void`. - #[inline] - pub const fn new_ptr(value: *mut c_void) -> Self { - Self { - sixty_four_bit_pointer: SixtyFourBitPointer { - pointer: value, - #[cfg(target_pointer_width = "32")] - _padding: 0, - }, - } - } - - /// Return the value as a `u64`. - /// - /// If the stored value was a pointer, the pointer is zero-extended to a - /// `u64`. - #[inline] - pub fn u64(self) -> u64 { - unsafe { self.as_u64 } - } - - /// Return the value as a `*mut c_void`. - /// - /// If the stored value was a `u64`, the least-significant bits of the - /// `u64` are returned as a pointer value. - #[inline] - pub fn ptr(self) -> *mut c_void { - unsafe { self.sixty_four_bit_pointer.pointer } - } -} - -impl PartialEq for EventData { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.u64() == other.u64() - } -} - -impl Eq for EventData {} - -impl Hash for EventData { - #[inline] - fn hash(&self, state: &mut H) { - self.u64().hash(state) - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SixtyFourBitPointer { - #[cfg(target_endian = "big")] - #[cfg(target_pointer_width = "32")] - _padding: u32, - - pointer: *mut c_void, - - #[cfg(target_endian = "little")] - #[cfg(target_pointer_width = "32")] - _padding: u32, -} - -/// A vector of `Event`s, plus context for interpreting them. -#[cfg(feature = "alloc")] -pub struct EventVec { - events: Vec, -} - -#[cfg(feature = "alloc")] -impl EventVec { - /// Constructs an `EventVec` from raw pointer, length, and capacity. - /// - /// # Safety - /// - /// This function calls [`Vec::from_raw_parts`] with its arguments. - /// - /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts - #[inline] - pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { - Self { - events: Vec::from_raw_parts(ptr, len, capacity), - } - } - - /// Constructs an `EventVec` with memory for `capacity` `Event`s. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { - events: Vec::with_capacity(capacity), - } - } - - /// Returns the current `Event` capacity of this `EventVec`. - #[inline] - pub fn capacity(&self) -> usize { - self.events.capacity() - } - - /// Reserves enough memory for at least `additional` more `Event`s. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.events.reserve(additional); - } - - /// Reserves enough memory for exactly `additional` more `Event`s. - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.events.reserve_exact(additional); - } - - /// Clears all the `Events` out of this `EventVec`. - #[inline] - pub fn clear(&mut self) { - self.events.clear(); - } - - /// Shrinks the capacity of this `EventVec` as much as possible. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.events.shrink_to_fit(); - } - - /// Returns an iterator over the `Event`s in this `EventVec`. - #[inline] - pub fn iter(&self) -> Iter<'_> { - Iter { - iter: self.events.iter().copied(), - } - } - - /// Returns the number of `Event`s logically contained in this `EventVec`. - #[inline] - pub fn len(&mut self) -> usize { - self.events.len() - } - - /// Tests whether this `EventVec` is logically empty. - #[inline] - pub fn is_empty(&mut self) -> bool { - self.events.is_empty() - } -} - -#[cfg(feature = "alloc")] -impl<'a> IntoIterator for &'a EventVec { - type IntoIter = Iter<'a>; - type Item = Event; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -#[test] -fn test_epoll_layouts() { - check_renamed_type!(Event, epoll_event); - check_renamed_type!(Event, epoll_event); - check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); - check_renamed_struct_renamed_field!(Event, epoll_event, data, u64); -} diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 602979256..23cb93085 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -1,27 +1,24 @@ //! libc syscalls supporting `rustix::event`. use crate::backend::c; -use crate::backend::conv::ret_c_int; -#[cfg(any(apple, netbsdlike, target_os = "dragonfly", target_os = "solaris"))] -use crate::backend::conv::ret_owned_fd; -use crate::event::PollFd; -#[cfg(any(linux_kernel, bsd, solarish, target_os = "espidf"))] -use crate::fd::OwnedFd; -use crate::io; -#[cfg(any(all(feature = "alloc", bsd), solarish))] -use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit}; +#[cfg(any(linux_kernel, target_os = "redox"))] +use crate::backend::conv::ret_u32; +use crate::backend::conv::{ret, ret_c_int, ret_owned_fd}; #[cfg(solarish)] -use { - crate::backend::conv::ret, crate::event::port::Event, crate::utils::as_mut_ptr, - core::ptr::null_mut, -}; +use crate::event::port::Event; #[cfg(any( linux_kernel, target_os = "freebsd", target_os = "illumos", target_os = "espidf" ))] -use {crate::backend::conv::ret_owned_fd, crate::event::EventfdFlags}; +use crate::event::EventfdFlags; +use crate::event::PollFd; +use crate::fd::OwnedFd; +use crate::io; +use crate::utils::as_mut_ptr; +use core::ptr::null_mut; +use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit}; #[cfg(all(feature = "alloc", bsd))] use {crate::event::kqueue::Event, crate::utils::as_ptr, core::ptr::null}; @@ -189,3 +186,77 @@ pub(crate) fn pause() { debug_assert_eq!(r, -1); debug_assert_eq!(errno, libc::EINTR); } + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result { + unsafe { ret_owned_fd(c::epoll_create1(bitflags_bits!(flags))) } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_add( + epoll: BorrowedFd<'_>, + source: BorrowedFd, + event: &mut crate::event::epoll::Event, +) -> io::Result<()> { + // We use our own `Event` struct instead of libc's because + // ours preserves pointer provenance instead of just using a `u64`, + // and we have tests elsewhere for layout equivalence. + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_ADD, + borrowed_fd(source), + as_mut_ptr(event).cast(), + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_mod( + epoll: BorrowedFd<'_>, + source: BorrowedFd, + event: &mut crate::event::epoll::Event, +) -> io::Result<()> { + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_MOD, + borrowed_fd(source), + as_mut_ptr(event).cast(), + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd) -> io::Result<()> { + unsafe { + ret(c::epoll_ctl( + borrowed_fd(epoll), + c::EPOLL_CTL_DEL, + borrowed_fd(source), + null_mut(), + )) + } +} + +#[inline] +#[cfg(any(linux_kernel, target_os = "redox"))] +pub(crate) fn epoll_wait( + epoll: BorrowedFd<'_>, + events: &mut [MaybeUninit], + timeout: c::c_int, +) -> io::Result { + unsafe { + ret_u32(c::epoll_wait( + borrowed_fd(epoll), + events.as_mut_ptr().cast::(), + events.len().try_into().unwrap_or(i32::MAX), + timeout, + )) + .map(|i| i.try_into().unwrap_or(usize::MAX)) + } +} diff --git a/src/backend/linux_raw/event/epoll.rs b/src/backend/linux_raw/event/epoll.rs index 4b7f041a3..fa3a46660 100644 --- a/src/backend/linux_raw/event/epoll.rs +++ b/src/backend/linux_raw/event/epoll.rs @@ -1,89 +1,10 @@ -//! Linux `epoll` support. -//! -//! # Examples -//! -//! ```no_run -//! # #[cfg(feature = "net")] -//! # fn main() -> std::io::Result<()> { -//! use rustix::event::epoll; -//! use rustix::fd::AsFd; -//! use rustix::io::{ioctl_fionbio, read, write}; -//! use rustix::net::{ -//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, -//! }; -//! use std::collections::HashMap; -//! use std::os::unix::io::AsRawFd; -//! -//! // Create a socket and listen on it. -//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; -//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; -//! listen(&listen_sock, 1)?; -//! -//! // Create an epoll object. Using `Owning` here means the epoll object will -//! // take ownership of the file descriptors registered with it. -//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; -//! -//! // Register the socket with the epoll object. -//! epoll::add( -//! &epoll, -//! &listen_sock, -//! epoll::EventData::new_u64(1), -//! epoll::EventFlags::IN, -//! )?; -//! -//! // Keep track of the sockets we've opened. -//! let mut next_id = epoll::EventData::new_u64(2); -//! let mut sockets = HashMap::new(); -//! -//! // Process events. -//! let mut event_list = epoll::EventVec::with_capacity(4); -//! loop { -//! epoll::wait(&epoll, &mut event_list, -1)?; -//! for event in &event_list { -//! let target = event.data; -//! if target.u64() == 1 { -//! // Accept a new connection, set it to non-blocking, and -//! // register to be notified when it's ready to write to. -//! let conn_sock = accept(&listen_sock)?; -//! ioctl_fionbio(&conn_sock, true)?; -//! epoll::add( -//! &epoll, -//! &conn_sock, -//! next_id, -//! epoll::EventFlags::OUT | epoll::EventFlags::ET, -//! )?; -//! -//! // Keep track of the socket. -//! sockets.insert(next_id, conn_sock); -//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); -//! } else { -//! // Write a message to the stream and then unregister it. -//! let target = sockets.remove(&target).unwrap(); -//! write(&target, b"hello\n")?; -//! let _ = epoll::delete(&epoll, &target)?; -//! } -//! } -//! } -//! # } -//! # #[cfg(not(feature = "net"))] -//! # fn main() {} -//! ``` - -#![allow(unsafe_code)] - use crate::backend::c; -use crate::backend::event::syscalls; -use crate::fd::{AsFd, AsRawFd, OwnedFd}; -use crate::io; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use bitflags::bitflags; -use core::ffi::c_void; -use core::hash::{Hash, Hasher}; -use core::slice; bitflags! { - /// `EPOLL_*` for use with [`create`]. + /// `EPOLL_*` for use with [`epoll::create`]. + /// + /// [`epoll::create`]: crate::event::epoll::create #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_uint { @@ -96,7 +17,9 @@ bitflags! { } bitflags! { - /// `EPOLL*` for use with [`add`]. + /// `EPOLL*` for use with [`epoll::add`]. + /// + /// [`epoll::add`]: crate::event::epoll::add #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct EventFlags: u32 { @@ -149,352 +72,3 @@ bitflags! { const _ = !0; } } - -/// `epoll_create1(flags)`—Creates a new epoll object. -/// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file -/// descriptor from being implicitly passed across `exec` boundaries. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html -#[inline] -#[doc(alias = "epoll_create1")] -pub fn create(flags: CreateFlags) -> io::Result { - syscalls::epoll_create(flags) -} - -/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll -/// object. -/// -/// This registers interest in any of the events set in `events` occurring on -/// the file descriptor associated with `data`. -/// -/// Note that `close`ing a file descriptor does not necessarily unregister -/// interest which can lead to spurious events being returned from [`wait`]. If -/// a file descriptor is an `Arc`, then `epoll` can be -/// thought to maintain a `Weak` to the file descriptor. -/// Check the [faq] for details. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn add( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - syscalls::epoll_add( - epoll.as_fd(), - source.as_fd().as_raw_fd(), - &Event { - flags: event_flags, - data, - }, - ) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a -/// given epoll object. -/// -/// This sets the events of interest with `target` to `events`. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn modify( - epoll: impl AsFd, - source: impl AsFd, - data: EventData, - event_flags: EventFlags, -) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - syscalls::epoll_mod( - epoll.as_fd(), - raw_fd, - &Event { - flags: event_flags, - data, - }, - ) - } -} - -/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a -/// given epoll object. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html -#[doc(alias = "epoll_ctl")] -#[inline] -pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { - // SAFETY: We're calling `epoll_ctl` via FFI and we know how it - // behaves. - unsafe { - let raw_fd = source.as_fd().as_raw_fd(); - syscalls::epoll_del(epoll.as_fd(), raw_fd) - } -} - -/// `epoll_wait(self, events, timeout)`—Waits for registered events of -/// interest. -/// -/// For each event of interest, an element is written to `events`. On -/// success, this returns the number of written elements. -/// -/// # References -/// - [Linux] -/// -/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html -#[cfg(feature = "alloc")] -#[cfg_attr(docsrs, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] -#[inline] -pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { - // SAFETY: We're calling `epoll_wait` via FFI and we know how it - // behaves. - unsafe { - event_list.events.set_len(0); - let nfds = syscalls::epoll_wait( - epoll.as_fd(), - event_list.events[..].as_mut_ptr().cast(), - event_list.events.capacity(), - timeout, - )?; - event_list.events.set_len(nfds); - } - - Ok(()) -} - -/// An iterator over the `Event`s in an `EventVec`. -pub struct Iter<'a> { - /// Use `Copied` to copy the struct, since `Event` is `packed` on some - /// platforms, and it's common for users to directly destructure it, which - /// would lead to errors about forming references to packed fields. - iter: core::iter::Copied>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = Event; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -/// A record of an event that occurred. -#[repr(C)] -#[cfg_attr(target_arch = "x86_64", repr(packed))] -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Event { - /// Which specific event(s) occurred. - pub flags: EventFlags, - /// User data. - pub data: EventData, -} - -/// Data associated with an [`Event`]. This can either be a 64-bit integer -/// value or a pointer which preserves pointer provenance. -#[repr(C)] -#[derive(Copy, Clone)] -pub union EventData { - /// A 64-bit integer value. - as_u64: u64, - - /// A `*mut c_void` which preserves pointer provenance, extended to be - /// 64-bit so that if we read the value as a `u64` union field, we don't - /// get uninitialized memory. - sixty_four_bit_pointer: SixtyFourBitPointer, -} - -impl EventData { - /// Construct a new value containing a `u64`. - #[inline] - pub const fn new_u64(value: u64) -> Self { - Self { as_u64: value } - } - - /// Construct a new value containing a `*mut c_void`. - #[inline] - pub const fn new_ptr(value: *mut c_void) -> Self { - Self { - sixty_four_bit_pointer: SixtyFourBitPointer { - pointer: value, - #[cfg(target_pointer_width = "32")] - _padding: 0, - }, - } - } - - /// Return the value as a `u64`. - /// - /// If the stored value was a pointer, the pointer is zero-extended to a - /// `u64`. - #[inline] - pub fn u64(self) -> u64 { - unsafe { self.as_u64 } - } - - /// Return the value as a `*mut c_void`. - /// - /// If the stored value was a `u64`, the least-significant bits of the - /// `u64` are returned as a pointer value. - #[inline] - pub fn ptr(self) -> *mut c_void { - unsafe { self.sixty_four_bit_pointer.pointer } - } -} - -impl PartialEq for EventData { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.u64() == other.u64() - } -} - -impl Eq for EventData {} - -impl Hash for EventData { - #[inline] - fn hash(&self, state: &mut H) { - self.u64().hash(state) - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SixtyFourBitPointer { - #[cfg(target_endian = "big")] - #[cfg(target_pointer_width = "32")] - _padding: u32, - - pointer: *mut c_void, - - #[cfg(target_endian = "little")] - #[cfg(target_pointer_width = "32")] - _padding: u32, -} - -/// A vector of `Event`s, plus context for interpreting them. -#[cfg(feature = "alloc")] -pub struct EventVec { - events: Vec, -} - -#[cfg(feature = "alloc")] -impl EventVec { - /// Constructs an `EventVec` from raw pointer, length, and capacity. - /// - /// # Safety - /// - /// This function calls [`Vec::from_raw_parts`] with its arguments. - /// - /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts - #[inline] - pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { - Self { - events: Vec::from_raw_parts(ptr, len, capacity), - } - } - - /// Constructs an `EventVec` with memory for `capacity` `Event`s. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { - events: Vec::with_capacity(capacity), - } - } - - /// Returns the current `Event` capacity of this `EventVec`. - #[inline] - pub fn capacity(&self) -> usize { - self.events.capacity() - } - - /// Reserves enough memory for at least `additional` more `Event`s. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.events.reserve(additional); - } - - /// Reserves enough memory for exactly `additional` more `Event`s. - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.events.reserve_exact(additional); - } - - /// Clears all the `Events` out of this `EventVec`. - #[inline] - pub fn clear(&mut self) { - self.events.clear(); - } - - /// Shrinks the capacity of this `EventVec` as much as possible. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.events.shrink_to_fit(); - } - - /// Returns an iterator over the `Event`s in this `EventVec`. - #[inline] - pub fn iter(&self) -> Iter<'_> { - Iter { - iter: self.events.iter().copied(), - } - } - - /// Returns the number of `Event`s logically contained in this `EventVec`. - #[inline] - pub fn len(&mut self) -> usize { - self.events.len() - } - - /// Tests whether this `EventVec` is logically empty. - #[inline] - pub fn is_empty(&mut self) -> bool { - self.events.is_empty() - } -} - -#[cfg(feature = "alloc")] -impl<'a> IntoIterator for &'a EventVec { - type IntoIter = Iter<'a>; - type Item = Event; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -#[test] -fn test_epoll_layouts() { - check_renamed_type!(Event, epoll_event); - check_renamed_type!(Event, epoll_event); - check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); - check_renamed_struct_renamed_field!(Event, epoll_event, data, data); -} diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index 323e09d59..abae5c62e 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -6,19 +6,13 @@ #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use crate::backend::c; -#[cfg(any( - feature = "alloc", - not(any(target_arch = "aarch64", target_arch = "riscv64")) -))] -use crate::backend::conv::c_int; -#[cfg(feature = "alloc")] -use crate::backend::conv::pass_usize; use crate::backend::conv::{ - by_ref, c_uint, raw_fd, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, + by_ref, c_int, c_uint, ret, ret_error, ret_owned_fd, ret_usize, slice_mut, zero, }; use crate::event::{epoll, EventfdFlags, PollFd}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; +use core::mem::MaybeUninit; use linux_raw_sys::general::{EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD}; #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use { @@ -61,61 +55,66 @@ pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result { } #[inline] -pub(crate) unsafe fn epoll_add( +pub(crate) fn epoll_add( epfd: BorrowedFd<'_>, - fd: c::c_int, + fd: BorrowedFd, event: &epoll::Event, ) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_ADD), - raw_fd(fd), - by_ref(event) - )) + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_ADD), + fd, + by_ref(event) + )) + } } #[inline] -pub(crate) unsafe fn epoll_mod( +pub(crate) fn epoll_mod( epfd: BorrowedFd<'_>, - fd: c::c_int, + fd: BorrowedFd, event: &epoll::Event, ) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_MOD), - raw_fd(fd), - by_ref(event) - )) + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_MOD), + fd, + by_ref(event) + )) + } } #[inline] -pub(crate) unsafe fn epoll_del(epfd: BorrowedFd<'_>, fd: c::c_int) -> io::Result<()> { - ret(syscall_readonly!( - __NR_epoll_ctl, - epfd, - c_uint(EPOLL_CTL_DEL), - raw_fd(fd), - zero() - )) +pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_epoll_ctl, + epfd, + c_uint(EPOLL_CTL_DEL), + fd, + zero() + )) + } } -#[cfg(feature = "alloc")] #[inline] pub(crate) fn epoll_wait( epfd: BorrowedFd<'_>, - events: *mut epoll::Event, - num_events: usize, + events: &mut [MaybeUninit], timeout: c::c_int, ) -> io::Result { + let (buf_addr_mut, buf_len) = slice_mut(events); #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] unsafe { ret_usize(syscall!( __NR_epoll_wait, epfd, - events, - pass_usize(num_events), + buf_addr_mut, + buf_len, c_int(timeout) )) } @@ -124,8 +123,8 @@ pub(crate) fn epoll_wait( ret_usize(syscall!( __NR_epoll_pwait, epfd, - events, - pass_usize(num_events), + buf_addr_mut, + buf_len, c_int(timeout), zero() )) diff --git a/src/event/epoll.rs b/src/event/epoll.rs new file mode 100644 index 000000000..1afd74910 --- /dev/null +++ b/src/event/epoll.rs @@ -0,0 +1,444 @@ +//! Linux `epoll` support. +//! +//! # Examples +//! +//! ```no_run +//! # #[cfg(feature = "net")] +//! # fn main() -> std::io::Result<()> { +//! use rustix::event::epoll; +//! use rustix::fd::AsFd; +//! use rustix::io::{ioctl_fionbio, read, write}; +//! use rustix::net::{ +//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, SocketAddrV4, SocketType, +//! }; +//! use std::collections::HashMap; +//! use std::os::unix::io::AsRawFd; +//! +//! // Create a socket and listen on it. +//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, None)?; +//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; +//! listen(&listen_sock, 1)?; +//! +//! // Create an epoll object. Using `Owning` here means the epoll object will +//! // take ownership of the file descriptors registered with it. +//! let epoll = epoll::create(epoll::CreateFlags::CLOEXEC)?; +//! +//! // Register the socket with the epoll object. +//! epoll::add( +//! &epoll, +//! &listen_sock, +//! epoll::EventData::new_u64(1), +//! epoll::EventFlags::IN, +//! )?; +//! +//! // Keep track of the sockets we've opened. +//! let mut next_id = epoll::EventData::new_u64(2); +//! let mut sockets = HashMap::new(); +//! +//! // Process events. +//! let mut event_list = epoll::EventVec::with_capacity(4); +//! loop { +//! epoll::wait(&epoll, &mut event_list, -1)?; +//! for event in &event_list { +//! let target = event.data; +//! if target.u64() == 1 { +//! // Accept a new connection, set it to non-blocking, and +//! // register to be notified when it's ready to write to. +//! let conn_sock = accept(&listen_sock)?; +//! ioctl_fionbio(&conn_sock, true)?; +//! epoll::add( +//! &epoll, +//! &conn_sock, +//! next_id, +//! epoll::EventFlags::OUT | epoll::EventFlags::ET, +//! )?; +//! +//! // Keep track of the socket. +//! sockets.insert(next_id, conn_sock); +//! next_id = epoll::EventData::new_u64(next_id.u64() + 1); +//! } else { +//! // Write a message to the stream and then unregister it. +//! let target = sockets.remove(&target).unwrap(); +//! write(&target, b"hello\n")?; +//! let _ = epoll::delete(&epoll, &target)?; +//! } +//! } +//! } +//! # } +//! # #[cfg(not(feature = "net"))] +//! # fn main() {} +//! ``` + +#![allow(unsafe_code)] +#![allow(unused_qualifications)] + +use super::epoll; +use crate::backend::c; +pub use crate::backend::event::epoll::*; +use crate::backend::event::syscalls; +use crate::fd::{AsFd, OwnedFd}; +use crate::io; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use core::ffi::c_void; +use core::hash::{Hash, Hasher}; +use core::slice; + +/// `epoll_create1(flags)`—Creates a new epoll object. +/// +/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// descriptor from being implicitly passed across `exec` boundaries. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_create.2.html +#[inline] +#[doc(alias = "epoll_create1")] +pub fn create(flags: epoll::CreateFlags) -> io::Result { + syscalls::epoll_create(flags) +} + +/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll +/// object. +/// +/// This registers interest in any of the events set in `event_flags` occurring on +/// the file descriptor associated with `data`. +/// +/// Note that `close`ing a file descriptor does not necessarily unregister +/// interest which can lead to spurious events being returned from [`epoll::wait`]. If +/// a file descriptor is an `Arc`, then `epoll` can be +/// thought to maintain a `Weak` to the file descriptor. +/// Check the [faq] for details. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +/// [faq]: https://man7.org/linux/man-pages/man7/epoll.7.html#:~:text=Will%20closing%20a%20file%20descriptor%20cause%20it%20to%20be%20removed%20from%20all%0A%20%20%20%20%20%20%20%20%20%20epoll%20interest%20lists%3F +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn add( + epoll: impl AsFd, + source: impl AsFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_add( + epoll.as_fd(), + source.as_fd(), + &mut Event { + flags: event_flags, + data, + #[cfg(all(libc, target_os = "redox"))] + _pad: 0, + }, + ) +} + +/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in a +/// given epoll object. +/// +/// This sets the events of interest with `target` to `events`. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn modify( + epoll: impl AsFd, + source: impl AsFd, + data: epoll::EventData, + event_flags: epoll::EventFlags, +) -> io::Result<()> { + syscalls::epoll_mod( + epoll.as_fd(), + source.as_fd(), + &mut Event { + flags: event_flags, + data, + #[cfg(all(libc, target_os = "redox"))] + _pad: 0, + }, + ) +} + +/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in a +/// given epoll object. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html +#[doc(alias = "epoll_ctl")] +#[inline] +pub fn delete(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { + syscalls::epoll_del(epoll.as_fd(), source.as_fd()) +} + +/// `epoll_wait(self, events, timeout)`—Waits for registered events of +/// interest. +/// +/// For each event of interest, an element is written to `events`. On +/// success, this returns the number of written elements. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/epoll_wait.2.html +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc"), alias = "epoll_wait"))] +#[inline] +pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> io::Result<()> { + // SAFETY: We're calling `epoll_wait` via FFI and we know how it + // behaves. + unsafe { + event_list.events.clear(); + let nfds = syscalls::epoll_wait( + epoll.as_fd(), + event_list.events.spare_capacity_mut(), + timeout, + )?; + event_list.events.set_len(nfds); + } + + Ok(()) +} + +/// An iterator over the `Event`s in an `EventVec`. +pub struct Iter<'a> { + /// Use `Copied` to copy the struct, since `Event` is `packed` on some + /// platforms, and it's common for users to directly destructure it, which + /// would lead to errors about forming references to packed fields. + iter: core::iter::Copied>, +} + +impl<'a> Iterator for Iter<'a> { + type Item = Event; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +/// A record of an event that occurred. +#[repr(C)] +#[cfg_attr(all(not(libc), target_arch = "x86_64"), repr(packed))] +#[cfg_attr( + all( + libc, + linux_kernel, + any( + all( + target_arch = "x86", + not(target_env = "musl"), + not(target_os = "android"), + ), + target_arch = "x86_64", + ) + ), + repr(packed) +)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct Event { + /// Which specific event(s) occurred. + pub flags: EventFlags, + /// User data. + pub data: EventData, + + #[cfg(all(libc, target_os = "redox"))] + _pad: u64, +} + +/// Data associated with an [`Event`]. This can either be a 64-bit integer +/// value or a pointer which preserves pointer provenance. +#[repr(C)] +#[derive(Copy, Clone)] +pub union EventData { + /// A 64-bit integer value. + as_u64: u64, + + /// A `*mut c_void` which preserves pointer provenance, extended to be + /// 64-bit so that if we read the value as a `u64` union field, we don't + /// get uninitialized memory. + sixty_four_bit_pointer: SixtyFourBitPointer, +} + +impl EventData { + /// Construct a new value containing a `u64`. + #[inline] + pub const fn new_u64(value: u64) -> Self { + Self { as_u64: value } + } + + /// Construct a new value containing a `*mut c_void`. + #[inline] + pub const fn new_ptr(value: *mut c_void) -> Self { + Self { + sixty_four_bit_pointer: SixtyFourBitPointer { + pointer: value, + #[cfg(target_pointer_width = "32")] + _padding: 0, + }, + } + } + + /// Return the value as a `u64`. + /// + /// If the stored value was a pointer, the pointer is zero-extended to a + /// `u64`. + #[inline] + pub fn u64(self) -> u64 { + unsafe { self.as_u64 } + } + + /// Return the value as a `*mut c_void`. + /// + /// If the stored value was a `u64`, the least-significant bits of the + /// `u64` are returned as a pointer value. + #[inline] + pub fn ptr(self) -> *mut c_void { + unsafe { self.sixty_four_bit_pointer.pointer } + } +} + +impl PartialEq for EventData { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.u64() == other.u64() + } +} + +impl Eq for EventData {} + +impl Hash for EventData { + #[inline] + fn hash(&self, state: &mut H) { + self.u64().hash(state) + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct SixtyFourBitPointer { + #[cfg(target_endian = "big")] + #[cfg(target_pointer_width = "32")] + _padding: u32, + + pointer: *mut c_void, + + #[cfg(target_endian = "little")] + #[cfg(target_pointer_width = "32")] + _padding: u32, +} + +/// A vector of `Event`s, plus context for interpreting them. +#[cfg(feature = "alloc")] +pub struct EventVec { + events: Vec, +} + +#[cfg(feature = "alloc")] +impl EventVec { + /// Constructs an `EventVec` from raw pointer, length, and capacity. + /// + /// # Safety + /// + /// This function calls [`Vec::from_raw_parts`] with its arguments. + /// + /// [`Vec::from_raw_parts`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts + #[inline] + pub unsafe fn from_raw_parts(ptr: *mut Event, len: usize, capacity: usize) -> Self { + Self { + events: Vec::from_raw_parts(ptr, len, capacity), + } + } + + /// Constructs an `EventVec` with memory for `capacity` `Event`s. + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self { + events: Vec::with_capacity(capacity), + } + } + + /// Returns the current `Event` capacity of this `EventVec`. + #[inline] + pub fn capacity(&self) -> usize { + self.events.capacity() + } + + /// Reserves enough memory for at least `additional` more `Event`s. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.events.reserve(additional); + } + + /// Reserves enough memory for exactly `additional` more `Event`s. + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.events.reserve_exact(additional); + } + + /// Clears all the `Events` out of this `EventVec`. + #[inline] + pub fn clear(&mut self) { + self.events.clear(); + } + + /// Shrinks the capacity of this `EventVec` as much as possible. + #[inline] + pub fn shrink_to_fit(&mut self) { + self.events.shrink_to_fit(); + } + + /// Returns an iterator over the `Event`s in this `EventVec`. + #[inline] + pub fn iter(&self) -> Iter<'_> { + Iter { + iter: self.events.iter().copied(), + } + } + + /// Returns the number of `Event`s logically contained in this `EventVec`. + #[inline] + pub fn len(&mut self) -> usize { + self.events.len() + } + + /// Tests whether this `EventVec` is logically empty. + #[inline] + pub fn is_empty(&mut self) -> bool { + self.events.is_empty() + } +} + +#[cfg(feature = "alloc")] +impl<'a> IntoIterator for &'a EventVec { + type IntoIter = Iter<'a>; + type Item = Event; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[test] +fn test_epoll_layouts() { + check_renamed_type!(Event, epoll_event); + check_renamed_type!(Event, epoll_event); + check_renamed_struct_renamed_field!(Event, epoll_event, flags, events); + #[cfg(libc)] + check_renamed_struct_renamed_field!(Event, epoll_event, data, u64); + #[cfg(not(libc))] + check_renamed_struct_renamed_field!(Event, epoll_event, data, data); +} diff --git a/src/event/mod.rs b/src/event/mod.rs index c497febe0..dab9c6932 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -1,5 +1,7 @@ //! Event operations. +#[cfg(any(linux_kernel, target_os = "redox"))] +pub mod epoll; #[cfg(any( linux_kernel, target_os = "freebsd", @@ -15,8 +17,6 @@ mod poll; #[cfg(solarish)] pub mod port; -#[cfg(any(linux_kernel, target_os = "redox"))] -pub use crate::backend::event::epoll; #[cfg(any( linux_kernel, target_os = "freebsd", From 4bd811f196806d0ef9d4298cf14ded72186c4a4f Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Mon, 26 Aug 2024 21:30:55 -0700 Subject: [PATCH 43/54] Implement the new module structure for shm and inotify (#1127) * Implement the new module structure for shm and inotify Signed-off-by: Alex Saveau * Use module prefixes in the docs for shm and inotify Signed-off-by: Alex Saveau * Fix inotify names for new module structure Signed-off-by: Alex Saveau --------- Signed-off-by: Alex Saveau --- src/backend/libc/fs/inotify.rs | 12 ++++++------ src/backend/libc/shm/types.rs | 4 ++-- src/backend/linux_raw/fs/inotify.rs | 12 ++++++------ src/backend/linux_raw/shm/types.rs | 4 ++-- src/fs/inotify.rs | 30 +++++++++++++++++++++-------- src/shm.rs | 20 ++++++++++++++++--- tests/fs/inotify.rs | 10 ++++------ 7 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/backend/libc/fs/inotify.rs b/src/backend/libc/fs/inotify.rs index 36911689a..c89702564 100644 --- a/src/backend/libc/fs/inotify.rs +++ b/src/backend/libc/fs/inotify.rs @@ -4,9 +4,9 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `IN_*` for use with [`inotify_init`]. + /// `IN_*` for use with [`inotify::init`]. /// - /// [`inotify_init`]: crate::fs::inotify::inotify_init + /// [`inotify::init`]: crate::fs::inotify::init #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: u32 { @@ -21,9 +21,9 @@ bitflags! { } bitflags! { - /// `IN*` for use with [`inotify_add_watch`]. + /// `IN*` for use with [`inotify::add_watch`]. /// - /// [`inotify_add_watch`]: crate::fs::inotify::inotify_add_watch + /// [`inotify::add_watch`]: crate::fs::inotify::add_watch #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct WatchFlags: u32 { @@ -78,9 +78,9 @@ bitflags! { } bitflags! { - /// `IN*` for use with [`InotifyReader`]. + /// `IN*` for use with [`inotify::Reader`]. /// - /// [`InotifyReader`]: crate::fs::inotify::InotifyReader + /// [`inotify::Reader`]: crate::fs::inotify::Reader #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ReadFlags: u32 { diff --git a/src/backend/libc/shm/types.rs b/src/backend/libc/shm/types.rs index 6575ef523..59f19b38f 100644 --- a/src/backend/libc/shm/types.rs +++ b/src/backend/libc/shm/types.rs @@ -2,9 +2,9 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `O_*` constants for use with [`shm_open`]. + /// `O_*` constants for use with [`shm::open`]. /// - /// [`shm_open`]: crate:shm::shm_open + /// [`shm::open`]: crate:shm::open #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ShmOFlags: u32 { diff --git a/src/backend/linux_raw/fs/inotify.rs b/src/backend/linux_raw/fs/inotify.rs index d517151e7..fbcfddb18 100644 --- a/src/backend/linux_raw/fs/inotify.rs +++ b/src/backend/linux_raw/fs/inotify.rs @@ -4,9 +4,9 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `IN_*` for use with [`inotify_init`]. + /// `IN_*` for use with [`inotify::init`]. /// - /// [`inotify_init`]: crate::fs::inotify::inotify_init + /// [`inotify::init`]: crate::fs::inotify::init #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct CreateFlags: c::c_uint { @@ -21,9 +21,9 @@ bitflags! { } bitflags! { - /// `IN*` for use with [`inotify_add_watch`]. + /// `IN*` for use with [`inotify::add_watch`]. /// - /// [`inotify_add_watch`]: crate::fs::inotify::inotify_add_watch + /// [`inotify::add_watch`]: crate::fs::inotify::add_watch #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct WatchFlags: c::c_uint { @@ -78,9 +78,9 @@ bitflags! { } bitflags! { - /// `IN*` for use with [`InotifyReader`]. + /// `IN*` for use with [`inotify::Reader`]. /// - /// [`InotifyReader`]: crate::fs::inotify::InotifyReader + /// [`inotify::Reader`]: crate::fs::inotify::InotifyReader #[repr(transparent)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ReadFlags: c::c_uint { diff --git a/src/backend/linux_raw/shm/types.rs b/src/backend/linux_raw/shm/types.rs index 3343d4424..866e8b73b 100644 --- a/src/backend/linux_raw/shm/types.rs +++ b/src/backend/linux_raw/shm/types.rs @@ -2,9 +2,9 @@ use crate::backend::c; use bitflags::bitflags; bitflags! { - /// `O_*` constants for use with [`shm_open`]. + /// `O_*` constants for use with [`shm::open`]. /// - /// [`shm_open`]: crate:shm::shm_open + /// [`shm::open`]: crate:shm::open #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ShmOFlags: c::c_uint { diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index 21b85aca0..53ddb5c41 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -1,5 +1,8 @@ //! inotify support for working with inotifies +#![allow(unused_qualifications)] + +use super::inotify; pub use crate::backend::fs::inotify::{CreateFlags, ReadFlags, WatchFlags}; use crate::backend::fs::syscalls; use crate::fd::{AsFd, OwnedFd}; @@ -9,13 +12,23 @@ use crate::io::{read_uninit, Errno}; use core::mem::{align_of, size_of, MaybeUninit}; use linux_raw_sys::general::inotify_event; +#[deprecated(note = "Use add_watch.")] +#[doc(hidden)] +pub use add_watch as inotify_add_watch; +#[deprecated(note = "Use init.")] +#[doc(hidden)] +pub use init as inotify_init; +#[deprecated(note = "Use remove_watch.")] +#[doc(hidden)] +pub use remove_watch as inotify_remove_watch; + /// `inotify_init1(flags)`—Creates a new inotify object. /// /// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file /// descriptor from being implicitly passed across `exec` boundaries. #[doc(alias = "inotify_init1")] #[inline] -pub fn inotify_init(flags: CreateFlags) -> io::Result { +pub fn init(flags: inotify::CreateFlags) -> io::Result { syscalls::inotify_init1(flags) } @@ -27,11 +40,12 @@ pub fn inotify_init(flags: CreateFlags) -> io::Result { /// Note: Due to the existence of hardlinks, providing two different paths to /// this method may result in it returning the same watch descriptor. An /// application should keep track of this externally to avoid logic errors. +#[doc(alias = "inotify_add_watch")] #[inline] -pub fn inotify_add_watch( +pub fn add_watch( inot: impl AsFd, path: P, - flags: WatchFlags, + flags: inotify::WatchFlags, ) -> io::Result { path.into_with_c_str(|path| syscalls::inotify_add_watch(inot.as_fd(), path, flags)) } @@ -39,10 +53,10 @@ pub fn inotify_add_watch( /// `inotify_rm_watch(self, wd)`—Removes a watch from this inotify. /// /// The watch descriptor provided should have previously been returned by -/// [`inotify_add_watch`] and not previously have been removed. +/// [`inotify::add_watch`] and not previously have been removed. #[doc(alias = "inotify_rm_watch")] #[inline] -pub fn inotify_remove_watch(inot: impl AsFd, wd: i32) -> io::Result<()> { +pub fn remove_watch(inot: impl AsFd, wd: i32) -> io::Result<()> { syscalls::inotify_rm_watch(inot.as_fd(), wd) } @@ -52,14 +66,14 @@ pub fn inotify_remove_watch(inot: impl AsFd, wd: i32) -> io::Result<()> { /// based on it. /// /// [`RawDir`]: crate::fs::raw_dir::RawDir -pub struct InotifyReader<'buf, Fd: AsFd> { +pub struct Reader<'buf, Fd: AsFd> { fd: Fd, buf: &'buf mut [MaybeUninit], initialized: usize, offset: usize, } -impl<'buf, Fd: AsFd> InotifyReader<'buf, Fd> { +impl<'buf, Fd: AsFd> Reader<'buf, Fd> { /// Create a new iterator from the given file descriptor and buffer. pub fn new(fd: Fd, buf: &'buf mut [MaybeUninit]) -> Self { Self { @@ -114,7 +128,7 @@ impl<'a> InotifyEvent<'a> { } } -impl<'buf, Fd: AsFd> InotifyReader<'buf, Fd> { +impl<'buf, Fd: AsFd> Reader<'buf, Fd> { /// Read the next inotify event. #[allow(unsafe_code)] pub fn next(&mut self) -> io::Result { diff --git a/src/shm.rs b/src/shm.rs index 450b6fcc6..19103ca3a 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -1,10 +1,22 @@ //! POSIX shared memory +#![allow(unused_qualifications)] + use crate::fd::OwnedFd; use crate::{backend, io, path}; +use super::shm; pub use crate::backend::fs::types::Mode; +pub use crate::backend::shm::types::ShmOFlags as OFlags; +#[deprecated(note = "Use OFlags.")] +#[doc(hidden)] pub use crate::backend::shm::types::ShmOFlags; +#[deprecated(note = "Use open.")] +#[doc(hidden)] +pub use open as shm_open; +#[deprecated(note = "Use unlink.")] +#[doc(hidden)] +pub use unlink as shm_unlink; /// `shm_open(name, oflags, mode)`—Opens a shared memory object. /// @@ -12,7 +24,7 @@ pub use crate::backend::shm::types::ShmOFlags; /// slashes, and be no longer than an implementation-defined limit (255 on /// Linux). /// -/// Exactly one of [`ShmOFlags::RDONLY`] and [`ShmOFlags::RDWR`] should be +/// Exactly one of [`shm::OFlags::RDONLY`] and [`shm::OFlags::RDWR`] should be /// passed. The file descriptor will be opened with `FD_CLOEXEC` set. /// /// # References @@ -21,8 +33,9 @@ pub use crate::backend::shm::types::ShmOFlags; /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html /// [Linux]: https://man7.org/linux/man-pages/man3/shm_open.3.html +#[doc(alias = "shm_open")] #[inline] -pub fn shm_open(name: P, flags: ShmOFlags, mode: Mode) -> io::Result { +pub fn open(name: P, flags: shm::OFlags, mode: Mode) -> io::Result { name.into_with_c_str(|name| backend::shm::syscalls::shm_open(name, flags, mode)) } @@ -34,7 +47,8 @@ pub fn shm_open(name: P, flags: ShmOFlags, mode: Mode) -> io::Resu /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_unlink.html /// [Linux]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html +#[doc(alias = "shm_unlink")] #[inline] -pub fn shm_unlink(name: P) -> io::Result<()> { +pub fn unlink(name: P) -> io::Result<()> { name.into_with_c_str(backend::shm::syscalls::shm_unlink) } diff --git a/tests/fs/inotify.rs b/tests/fs/inotify.rs index 0e5f7c1ee..72fc7bde4 100644 --- a/tests/fs/inotify.rs +++ b/tests/fs/inotify.rs @@ -1,6 +1,4 @@ -use rustix::fs::inotify::{ - inotify_add_watch, inotify_init, CreateFlags, InotifyReader, WatchFlags, -}; +use rustix::fs::inotify::{self, CreateFlags, WatchFlags}; use rustix::io::Errno; use std::fmt::Write; use std::fs::{create_dir_all, remove_file, rename, File}; @@ -8,9 +6,9 @@ use std::mem::MaybeUninit; #[test] fn test_inotify_iter() { - let inotify = inotify_init(CreateFlags::NONBLOCK).unwrap(); + let inotify = inotify::init(CreateFlags::NONBLOCK).unwrap(); create_dir_all("/tmp/.rustix-inotify-test").unwrap(); - inotify_add_watch( + inotify::add_watch( &inotify, "/tmp/.rustix-inotify-test", WatchFlags::ALL_EVENTS, @@ -29,7 +27,7 @@ fn test_inotify_iter() { let mut cookie = 0; let mut buf = [MaybeUninit::uninit(); 512]; - let mut iter = InotifyReader::new(inotify, &mut buf); + let mut iter = inotify::Reader::new(inotify, &mut buf); loop { let e = match iter.next() { Err(Errno::WOULDBLOCK) => break, From 5ab7762efc970d29834528a81285de4701cf3f30 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 26 Aug 2024 23:24:57 -0700 Subject: [PATCH 44/54] Deprecate `FutexOperation` instead of `futex` (#1118) With `rustix::thread::futex` being both a deprecated function and a public module, I'm seeing warnings like this when I use it in some simple testcases: ```console warning: use of deprecated function `rustix::thread::futex`: There are now individual functions available to perform futex operations with improved type safety. See the futex module. --> test.rs:2:21 | 2 | use rustix::thread::futex; | ^^^^^ | = note: `#[warn(deprecated)]` on by default ``` To fix this, move the deprecation from the `futex` function to the `FutexOperation` enum. This has the same effect, but doesn't produce the warning then the `futex` module is used. Also while using the API, I found `futex::FutexFlags` to be a little redundant, so I think it makes sense to rename it to `futex::Flags`, and similarly rename `futex::FUTEX_WAITERS` to `futex::WAITERS` and so on. This also splits `FutexOperation` into two enums internally, so that the public `FutexOperation` which is `#[non_exhausitive]` can continue to have its original contents, and the private enum can add new opcodes. --- src/backend/libc/thread/futex.rs | 49 ++++- src/backend/libc/thread/syscalls.rs | 17 +- src/backend/linux_raw/conv.rs | 8 +- src/backend/linux_raw/thread/futex.rs | 49 ++++- src/backend/linux_raw/thread/syscalls.rs | 18 +- src/thread/futex.rs | 219 +++++------------------ src/thread/mod.rs | 66 ++++++- tests/thread/futex.rs | 32 ++-- 8 files changed, 228 insertions(+), 230 deletions(-) diff --git a/src/backend/libc/thread/futex.rs b/src/backend/libc/thread/futex.rs index 5bce85735..7b26d865b 100644 --- a/src/backend/libc/thread/futex.rs +++ b/src/backend/libc/thread/futex.rs @@ -1,12 +1,12 @@ use crate::backend::c; bitflags::bitflags! { - /// `FUTEX_*` flags for use with [`futex`]. + /// `FUTEX_*` flags for use with the functions in [`futex`]. /// /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct FutexFlags: u32 { + pub struct Flags: u32 { /// `FUTEX_PRIVATE_FLAG` const PRIVATE = bitcast!(c::FUTEX_PRIVATE_FLAG); /// `FUTEX_CLOCK_REALTIME` @@ -14,12 +14,10 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. -/// -/// [`futex`]: mod@crate::thread::futex +/// `FUTEX_*` operations for use with the futex syscall wrappers. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] -pub enum FutexOperation { +pub(crate) enum Operation { /// `FUTEX_WAIT` Wait = bitcast!(c::FUTEX_WAIT), /// `FUTEX_WAKE` @@ -50,8 +48,43 @@ pub enum FutexOperation { LockPi2 = bitcast!(c::FUTEX_LOCK_PI2), } +/// `FUTEX_*` operations for use with the [`futex`] function. +/// +/// [`futex`]: fn@crate::thread::futex +#[deprecated( + since = "0.38.35", + note = " + The `futex` function and `FutexOperation` enum are deprecated. There are + individual functions available to perform futex operations with improved + type safety. See the `rustix::thread::futex` module." +)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum FutexOperation { + /// `FUTEX_WAIT` + Wait = bitcast!(c::FUTEX_WAIT), + /// `FUTEX_WAKE` + Wake = bitcast!(c::FUTEX_WAKE), + /// `FUTEX_FD` + Fd = bitcast!(c::FUTEX_FD), + /// `FUTEX_REQUEUE` + Requeue = bitcast!(c::FUTEX_REQUEUE), + /// `FUTEX_CMP_REQUEUE` + CmpRequeue = bitcast!(c::FUTEX_CMP_REQUEUE), + /// `FUTEX_WAKE_OP` + WakeOp = bitcast!(c::FUTEX_WAKE_OP), + /// `FUTEX_LOCK_PI` + LockPi = bitcast!(c::FUTEX_LOCK_PI), + /// `FUTEX_UNLOCK_PI` + UnlockPi = bitcast!(c::FUTEX_UNLOCK_PI), + /// `FUTEX_TRYLOCK_PI` + TrylockPi = bitcast!(c::FUTEX_TRYLOCK_PI), + /// `FUTEX_WAIT_BITSET` + WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET), +} + /// `FUTEX_WAITERS` -pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; +pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; /// `FUTEX_OWNER_DIED` -pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; +pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index fb5c71115..036b60d48 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -11,11 +11,10 @@ use core::mem::MaybeUninit; use core::sync::atomic::AtomicU32; #[cfg(linux_kernel)] use { - super::futex::FutexOperation, crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize}, crate::fd::BorrowedFd, crate::pid::Pid, - crate::thread::FutexFlags, + crate::thread::futex, crate::utils::as_mut_ptr, }; #[cfg(not(any( @@ -420,14 +419,14 @@ pub(crate) fn setresgid_thread( #[cfg(linux_kernel)] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, val2: u32, uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // the least significant four bytes of the timeout pointer are used as `val2`. + // The least-significant four bytes of the timeout pointer are used as `val2`. // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), // so we perform that exact conversion in reverse to create the pointer. let timeout = val2 as usize as *const Timespec; @@ -493,8 +492,8 @@ pub(crate) unsafe fn futex_val2( #[cfg(linux_kernel)] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, @@ -574,8 +573,8 @@ pub(crate) unsafe fn futex_timeout( ))] unsafe fn futex_old_timespec( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, diff --git a/src/backend/linux_raw/conv.rs b/src/backend/linux_raw/conv.rs index 5c0fd45c2..62f6868e0 100644 --- a/src/backend/linux_raw/conv.rs +++ b/src/backend/linux_raw/conv.rs @@ -792,15 +792,15 @@ impl<'a, Num: ArgNumber> From<(crate::net::SocketType, crate::net::SocketFlags)> #[cfg(feature = "thread")] impl<'a, Num: ArgNumber> From<( - crate::backend::thread::futex::FutexOperation, - crate::thread::FutexFlags, + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, )> for ArgReg<'a, Num> { #[inline] fn from( pair: ( - crate::backend::thread::futex::FutexOperation, - crate::thread::FutexFlags, + crate::backend::thread::futex::Operation, + crate::thread::futex::Flags, ), ) -> Self { c_uint(pair.0 as u32 | pair.1.bits()) diff --git a/src/backend/linux_raw/thread/futex.rs b/src/backend/linux_raw/thread/futex.rs index ed1a34a7e..a0a63abd4 100644 --- a/src/backend/linux_raw/thread/futex.rs +++ b/src/backend/linux_raw/thread/futex.rs @@ -1,10 +1,10 @@ bitflags::bitflags! { - /// `FUTEX_*` flags for use with [`futex`]. + /// `FUTEX_*` flags for use with the functions in [`futex`]. /// /// [`futex`]: mod@crate::thread::futex #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] - pub struct FutexFlags: u32 { + pub struct Flags: u32 { /// `FUTEX_PRIVATE_FLAG` const PRIVATE = linux_raw_sys::general::FUTEX_PRIVATE_FLAG; /// `FUTEX_CLOCK_REALTIME` @@ -16,12 +16,10 @@ bitflags::bitflags! { } } -/// `FUTEX_*` operations for use with [`futex`]. -/// -/// [`futex`]: mod@crate::thread::futex +/// `FUTEX_*` operations for use with the futex syscall wrappers. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] -pub enum FutexOperation { +pub(crate) enum Operation { /// `FUTEX_WAIT` Wait = linux_raw_sys::general::FUTEX_WAIT, /// `FUTEX_WAKE` @@ -52,8 +50,43 @@ pub enum FutexOperation { LockPi2 = linux_raw_sys::general::FUTEX_LOCK_PI2, } +/// `FUTEX_*` operations for use with the [`futex`] function. +/// +/// [`futex`]: fn@crate::thread::futex +#[deprecated( + since = "0.38.35", + note = " + The `futex` function and `FutexOperation` enum are deprecated. There are + individual functions available to perform futex operations with improved + type safety. See the `rustix::thread::futex` module." +)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum FutexOperation { + /// `FUTEX_WAIT` + Wait = linux_raw_sys::general::FUTEX_WAIT, + /// `FUTEX_WAKE` + Wake = linux_raw_sys::general::FUTEX_WAKE, + /// `FUTEX_FD` + Fd = linux_raw_sys::general::FUTEX_FD, + /// `FUTEX_REQUEUE` + Requeue = linux_raw_sys::general::FUTEX_REQUEUE, + /// `FUTEX_CMP_REQUEUE` + CmpRequeue = linux_raw_sys::general::FUTEX_CMP_REQUEUE, + /// `FUTEX_WAKE_OP` + WakeOp = linux_raw_sys::general::FUTEX_WAKE_OP, + /// `FUTEX_LOCK_PI` + LockPi = linux_raw_sys::general::FUTEX_LOCK_PI, + /// `FUTEX_UNLOCK_PI` + UnlockPi = linux_raw_sys::general::FUTEX_UNLOCK_PI, + /// `FUTEX_TRYLOCK_PI` + TrylockPi = linux_raw_sys::general::FUTEX_TRYLOCK_PI, + /// `FUTEX_WAIT_BITSET` + WaitBitset = linux_raw_sys::general::FUTEX_WAIT_BITSET, +} + /// `FUTEX_WAITERS` -pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; +pub const WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS; /// `FUTEX_OWNER_DIED` -pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; +pub const OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED; diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index 907bb6457..336453131 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -5,7 +5,6 @@ //! See the `rustix::backend` module documentation for details. #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] -use super::futex::FutexOperation; use crate::backend::c; use crate::backend::conv::{ by_mut, by_ref, c_int, c_uint, ret, ret_c_int, ret_c_int_infallible, ret_usize, slice, @@ -14,7 +13,7 @@ use crate::backend::conv::{ use crate::fd::BorrowedFd; use crate::io; use crate::pid::Pid; -use crate::thread::{ClockId, FutexFlags, NanosleepRelativeResult, Timespec}; +use crate::thread::{futex, ClockId, NanosleepRelativeResult, Timespec}; use core::mem::MaybeUninit; use core::sync::atomic::AtomicU32; #[cfg(target_pointer_width = "32")] @@ -208,14 +207,14 @@ pub(crate) fn gettid() -> Pid { #[inline] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, val2: u32, uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // the least significant four bytes of the timeout pointer are used as `val2`. + // The least-significant four bytes of the timeout pointer are used as `val2`. // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), // so we perform that exact conversion in reverse to create the pointer. let timeout = val2 as usize as *const Timespec; @@ -247,8 +246,8 @@ pub(crate) unsafe fn futex_val2( #[inline] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, @@ -290,8 +289,8 @@ pub(crate) unsafe fn futex_timeout( #[cfg(target_pointer_width = "32")] unsafe fn futex_old_timespec( uaddr: *const AtomicU32, - op: FutexOperation, - flags: FutexFlags, + op: super::futex::Operation, + flags: futex::Flags, val: u32, timeout: *const Timespec, uaddr2: *const AtomicU32, @@ -321,7 +320,6 @@ unsafe fn futex_old_timespec( c_uint(val3) )) } - #[inline] pub(crate) fn setns(fd: BorrowedFd<'_>, nstype: c::c_int) -> io::Result { unsafe { ret_c_int(syscall_readonly!(__NR_setns, fd, c_int(nstype))) } diff --git a/src/thread/futex.rs b/src/thread/futex.rs index 1c89b96bb..0cb6687b9 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -1,81 +1,35 @@ //! Linux `futex`. //! +//! # References +//! - [Linux `futex` system call] +//! - [Linux `futex` feature] +//! //! # Safety //! //! Futex is a very low-level mechanism for implementing concurrency //! primitives. +//! +//! [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +//! [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #![allow(unsafe_code)] use core::num::NonZeroU32; use core::ptr; use core::sync::atomic::AtomicU32; +use crate::backend::thread::futex::Operation; use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; use crate::fd::{FromRawFd, OwnedFd, RawFd}; -use crate::thread::Timespec; use crate::{backend, io}; -pub use backend::thread::futex::FutexFlags; -pub use backend::thread::futex::FutexOperation; +pub use crate::timespec::Timespec; + +pub use backend::thread::futex::Flags; /// `FUTEX_WAITERS` -pub const FUTEX_WAITERS: u32 = backend::thread::futex::FUTEX_WAITERS; +pub const WAITERS: u32 = backend::thread::futex::WAITERS; /// `FUTEX_OWNER_DIED` -pub const FUTEX_OWNER_DIED: u32 = backend::thread::futex::FUTEX_OWNER_DIED; - -/// DEPRECATED: There are now individual functions available to perform futex operations with improved type safety. See the [futex module](`self`). -/// -/// `futex(uaddr, op, val, utime, uaddr2, val3)` -/// -/// # References -/// - [Linux `futex` system call] -/// - [Linux `futex` feature] -/// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// -/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html -/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html -#[deprecated( - since = "0.38.35", - note = "There are now individual functions available to perform futex operations with improved type safety. See the futex module." -)] -#[inline] -pub unsafe fn futex( - uaddr: *mut u32, - op: FutexOperation, - flags: FutexFlags, - val: u32, - utime: *const Timespec, - uaddr2: *mut u32, - val3: u32, -) -> io::Result { - use FutexOperation::*; - - match op { - Wait | LockPi | WaitBitset | WaitRequeuePi | LockPi2 => futex_timeout( - uaddr as *const AtomicU32, - op, - flags, - val, - utime, - uaddr2 as *const AtomicU32, - val3, - ), - Wake | Fd | Requeue | CmpRequeue | WakeOp | UnlockPi | TrylockPi | WakeBitset - | CmpRequeuePi => futex_val2( - uaddr as *const AtomicU32, - op, - flags, - val, - utime as usize as u32, - uaddr2 as *const AtomicU32, - val3, - ), - } -} +pub const OWNER_DIED: u32 = backend::thread::futex::OWNER_DIED; /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` /// @@ -93,14 +47,14 @@ pub unsafe fn futex( #[inline] pub fn wait( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, timeout: Option, ) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::Wait, + Operation::Wait, flags, val, timeout @@ -133,18 +87,8 @@ pub fn wait( /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] -pub fn wake(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::Wake, - flags, - val, - 0, - ptr::null(), - 0, - ) - } +pub fn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)` @@ -161,18 +105,9 @@ pub fn wake(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] -pub fn fd(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result { +pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::Fd, - flags, - val, - 0, - ptr::null(), - 0, - ) - .map(|val| { + futex_val2(uaddr, Operation::Fd, flags, val, 0, ptr::null(), 0).map(|val| { let fd = val as RawFd; debug_assert_eq!(fd as usize, val, "return value should be a valid fd"); OwnedFd::from_raw_fd(fd) @@ -196,22 +131,12 @@ pub fn fd(uaddr: &AtomicU32, flags: FutexFlags, val: u32) -> io::Result #[inline] pub fn requeue( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val2: u32, uaddr2: &AtomicU32, ) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::Requeue, - flags, - val, - val2, - uaddr2, - 0, - ) - } + unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)` @@ -230,23 +155,13 @@ pub fn requeue( #[inline] pub fn cmp_requeue( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val2: u32, uaddr2: &AtomicU32, val3: u32, ) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::CmpRequeue, - flags, - val, - val2, - uaddr2, - val3, - ) - } + unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) } } /// `FUTEX_OP_*` operations for use with [`wake_op`]. @@ -309,7 +224,7 @@ pub enum WakeOpCmp { #[inline] pub fn wake_op( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val2: u32, uaddr2: &AtomicU32, @@ -325,17 +240,7 @@ pub fn wake_op( let val3 = ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32); - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::WakeOp, - flags, - val, - val2, - uaddr2, - val3, - ) - } + unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)` @@ -352,11 +257,11 @@ pub fn wake_op( /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] -pub fn lock_pi(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) -> io::Result<()> { +pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::LockPi, + Operation::LockPi, flags, 0, timeout @@ -389,18 +294,9 @@ pub fn lock_pi(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] -pub fn unlock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result<()> { +pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::UnlockPi, - flags, - 0, - 0, - ptr::null(), - 0, - ) - .map(|val| { + futex_val2(uaddr, Operation::UnlockPi, flags, 0, 0, ptr::null(), 0).map(|val| { debug_assert_eq!( val, 0, "The return value should always equal zero, if the call is successful" @@ -423,22 +319,13 @@ pub fn unlock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result<()> { /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] -pub fn trylock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result { +pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result { unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::TrylockPi, - flags, - 0, - 0, - ptr::null(), - 0, - ) - .map(|ret| ret == 0) + futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0) } } -/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout/val2, NULL, val3)` +/// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)` /// /// # References /// - [Linux `futex` system call] @@ -454,15 +341,15 @@ pub fn trylock_pi(uaddr: &AtomicU32, flags: FutexFlags) -> io::Result { #[inline] pub fn wait_bitset( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, timeout: Option, val3: NonZeroU32, ) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::WaitBitset, + Operation::WaitBitset, flags, val, timeout @@ -497,14 +384,14 @@ pub fn wait_bitset( #[inline] pub fn wake_bitset( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, val3: NonZeroU32, ) -> io::Result { unsafe { - backend::thread::syscalls::futex_val2( + futex_val2( uaddr, - FutexOperation::WakeBitset, + Operation::WakeBitset, flags, val, 0, @@ -530,15 +417,15 @@ pub fn wake_bitset( #[inline] pub fn wait_requeue_pi( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val: u32, timeout: Option, uaddr2: &AtomicU32, ) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::WaitRequeuePi, + Operation::WaitRequeuePi, flags, val, timeout @@ -573,22 +460,12 @@ pub fn wait_requeue_pi( #[inline] pub fn cmp_requeue_pi( uaddr: &AtomicU32, - flags: FutexFlags, + flags: Flags, val2: u32, uaddr2: &AtomicU32, val3: u32, ) -> io::Result { - unsafe { - backend::thread::syscalls::futex_val2( - uaddr, - FutexOperation::CmpRequeuePi, - flags, - 1, - val2, - uaddr2, - val3, - ) - } + unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)` @@ -605,11 +482,11 @@ pub fn cmp_requeue_pi( /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] -pub fn lock_pi2(uaddr: &AtomicU32, flags: FutexFlags, timeout: Option) -> io::Result<()> { +pub fn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { unsafe { - backend::thread::syscalls::futex_timeout( + futex_timeout( uaddr, - FutexOperation::LockPi2, + Operation::LockPi2, flags, 0, timeout diff --git a/src/thread/mod.rs b/src/thread/mod.rs index cb5d566f3..ce62d8f68 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -13,12 +13,16 @@ mod prctl; #[cfg(linux_kernel)] mod setns; +#[allow(deprecated)] +#[cfg(linux_kernel)] +pub use crate::backend::thread::futex::FutexOperation; +#[cfg(linux_kernel)] +pub use crate::thread::futex::{ + Flags as FutexFlags, OWNER_DIED as FUTEX_OWNER_DIED, WAITERS as FUTEX_WAITERS, +}; #[cfg(not(target_os = "redox"))] pub use clock::*; #[cfg(linux_kernel)] -#[allow(deprecated)] -pub use futex::{futex, FutexFlags, FutexOperation, FUTEX_OWNER_DIED, FUTEX_WAITERS}; -#[cfg(linux_kernel)] pub use id::{ gettid, set_thread_gid, set_thread_groups, set_thread_res_gid, set_thread_res_uid, set_thread_uid, Gid, Pid, RawGid, RawPid, RawUid, Uid, @@ -29,3 +33,59 @@ pub use libcap::{capabilities, set_capabilities, CapabilityFlags, CapabilitySets pub use prctl::*; #[cfg(linux_kernel)] pub use setns::*; + +/// DEPRECATED: There are now individual functions available to perform futex operations with improved type safety. See the [futex module]. +/// +/// `futex(uaddr, op, val, utime, uaddr2, val3)` +/// +/// # References +/// - [Linux `futex` system call] +/// - [Linux `futex` feature] +/// +/// # Safety +/// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links above. +/// +/// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html +/// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html +/// [futex module]: mod@crate::thread::futex +#[cfg(linux_kernel)] +#[allow(unsafe_code, deprecated)] +#[inline] +pub unsafe fn futex( + uaddr: *mut u32, + op: FutexOperation, + flags: FutexFlags, + val: u32, + utime: *const Timespec, + uaddr2: *mut u32, + val3: u32, +) -> crate::io::Result { + use crate::backend::thread::futex::Operation; + use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; + use core::mem::transmute; + use core::sync::atomic::AtomicU32; + use FutexOperation::*; + + match op { + Wait | LockPi | WaitBitset => futex_timeout( + uaddr as *const AtomicU32, + transmute::(op), + flags, + val, + utime, + uaddr2 as *const AtomicU32, + val3, + ), + Wake | Fd | Requeue | CmpRequeue | WakeOp | UnlockPi | TrylockPi => futex_val2( + uaddr as *const AtomicU32, + transmute::(op), + flags, + val, + utime as usize as u32, + uaddr2 as *const AtomicU32, + val3, + ), + } +} diff --git a/tests/thread/futex.rs b/tests/thread/futex.rs index 918b7dac1..cd1df57e5 100644 --- a/tests/thread/futex.rs +++ b/tests/thread/futex.rs @@ -1,22 +1,20 @@ use core::sync::atomic::{AtomicU32, Ordering}; -use rustix::{ - io::Errno, - thread::{futex, FutexFlags}, -}; +use rustix::io::Errno; +use rustix::thread::futex; #[test] fn test_lock_unlock_pi() { let lock = AtomicU32::new(0); - futex::lock_pi(&lock, FutexFlags::empty(), None).unwrap(); + futex::lock_pi(&lock, futex::Flags::empty(), None).unwrap(); assert_ne!(lock.load(Ordering::SeqCst), 0); - let err = unsafe { futex::lock_pi(&lock, FutexFlags::empty(), None).unwrap_err() }; + let err = futex::lock_pi(&lock, futex::Flags::empty(), None).unwrap_err(); assert_eq!(err, Errno::DEADLK); - futex::unlock_pi(&lock, FutexFlags::empty()).unwrap(); + futex::unlock_pi(&lock, futex::Flags::empty()).unwrap(); assert_eq!(lock.load(Ordering::SeqCst), 0); - let err = futex::unlock_pi(&lock, FutexFlags::empty()).unwrap_err(); + let err = futex::unlock_pi(&lock, futex::Flags::empty()).unwrap_err(); assert_eq!(err, Errno::PERM); } @@ -25,7 +23,7 @@ fn test_lock_unlock_pi() { fn test_wait_wake() { let lock = std::sync::Arc::new(AtomicU32::new(0)); - match futex::wait(&lock, FutexFlags::empty(), 1, None) { + match futex::wait(&lock, futex::Flags::empty(), 1, None) { Ok(()) => panic!("Nobody should be waking us!"), Err(Errno::AGAIN) => { assert_eq!(lock.load(Ordering::SeqCst), 0, "the lock should still be 0") @@ -38,10 +36,10 @@ fn test_wait_wake() { move || { std::thread::sleep(std::time::Duration::from_millis(1)); lock.store(1, Ordering::SeqCst); - futex::wake(&lock, FutexFlags::empty(), 1).unwrap(); + futex::wake(&lock, futex::Flags::empty(), 1).unwrap(); std::thread::sleep(std::time::Duration::from_millis(50)); - match futex::wait(&lock, FutexFlags::empty(), 1, None) { + match futex::wait(&lock, futex::Flags::empty(), 1, None) { Ok(()) => (), Err(Errno::AGAIN) => { assert_eq!(lock.load(Ordering::SeqCst), 2, "the lock should now be 2") @@ -51,14 +49,14 @@ fn test_wait_wake() { } }); - match futex::wait(&lock, FutexFlags::empty(), 0, None) { + match futex::wait(&lock, futex::Flags::empty(), 0, None) { Ok(()) => (), Err(Errno::AGAIN) => assert_eq!(lock.load(Ordering::SeqCst), 1, "the lock should now be 1"), Err(err) => panic!("{err}"), } lock.store(2, Ordering::SeqCst); - futex::wake(&lock, FutexFlags::empty(), 1).unwrap(); + futex::wake(&lock, futex::Flags::empty(), 1).unwrap(); other.join().unwrap(); } @@ -66,13 +64,13 @@ fn test_wait_wake() { #[cfg(feature = "std")] #[test] fn test_timeout() { - use rustix::fs::Timespec; + use rustix::thread::futex::Timespec; let lock = AtomicU32::new(0); let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: 1, @@ -84,7 +82,7 @@ fn test_timeout() { let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: 0, @@ -96,7 +94,7 @@ fn test_timeout() { let err = futex::wait( &lock, - FutexFlags::empty(), + futex::Flags::empty(), 0, Some(Timespec { tv_sec: -1, From d0c4b8f374d5f5228201bdb7eccb90db45d04f73 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 26 Aug 2024 23:25:28 -0700 Subject: [PATCH 45/54] Add more `epoll::` qualifiers to epoll's public API. (#1132) Qualify more types and functions in epoll's public API with `epoll::` qualifiers. --- src/event/epoll.rs | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/event/epoll.rs b/src/event/epoll.rs index 1afd74910..07cb9da93 100644 --- a/src/event/epoll.rs +++ b/src/event/epoll.rs @@ -86,7 +86,7 @@ use core::slice; /// `epoll_create1(flags)`—Creates a new epoll object. /// -/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file +/// Use the [`epoll::CreateFlags::CLOEXEC`] flag to prevent the resulting file /// descriptor from being implicitly passed across `exec` boundaries. /// /// # References @@ -207,7 +207,7 @@ pub fn wait(epoll: impl AsFd, event_list: &mut EventVec, timeout: c::c_int) -> i Ok(()) } -/// An iterator over the `Event`s in an `EventVec`. +/// An iterator over the [`epoll::Event`]s in an [`epoll::EventVec`]. pub struct Iter<'a> { /// Use `Copied` to copy the struct, since `Event` is `packed` on some /// platforms, and it's common for users to directly destructure it, which @@ -216,7 +216,7 @@ pub struct Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = Event; + type Item = epoll::Event; #[inline] fn next(&mut self) -> Option { @@ -258,8 +258,8 @@ pub struct Event { _pad: u64, } -/// Data associated with an [`Event`]. This can either be a 64-bit integer -/// value or a pointer which preserves pointer provenance. +/// Data associated with an [`epoll::Event`]. This can either be a 64-bit +/// integer value or a pointer which preserves pointer provenance. #[repr(C)] #[derive(Copy, Clone)] pub union EventData { @@ -340,7 +340,7 @@ struct SixtyFourBitPointer { _padding: u32, } -/// A vector of `Event`s, plus context for interpreting them. +/// A vector of `epoll::Event`s, plus context for interpreting them. #[cfg(feature = "alloc")] pub struct EventVec { events: Vec, @@ -348,7 +348,7 @@ pub struct EventVec { #[cfg(feature = "alloc")] impl EventVec { - /// Constructs an `EventVec` from raw pointer, length, and capacity. + /// Constructs an `epoll::EventVec` from raw pointer, length, and capacity. /// /// # Safety /// @@ -362,7 +362,8 @@ impl EventVec { } } - /// Constructs an `EventVec` with memory for `capacity` `Event`s. + /// Constructs an `epoll::EventVec` with memory for `capacity` + /// `epoll::Event`s. #[inline] pub fn with_capacity(capacity: usize) -> Self { Self { @@ -370,37 +371,37 @@ impl EventVec { } } - /// Returns the current `Event` capacity of this `EventVec`. + /// Returns the current `epoll::Event` capacity of this `epoll::EventVec`. #[inline] pub fn capacity(&self) -> usize { self.events.capacity() } - /// Reserves enough memory for at least `additional` more `Event`s. + /// Reserves enough memory for at least `additional` more `epoll::Event`s. #[inline] pub fn reserve(&mut self, additional: usize) { self.events.reserve(additional); } - /// Reserves enough memory for exactly `additional` more `Event`s. + /// Reserves enough memory for exactly `additional` more `epoll::Event`s. #[inline] pub fn reserve_exact(&mut self, additional: usize) { self.events.reserve_exact(additional); } - /// Clears all the `Events` out of this `EventVec`. + /// Clears all the `epoll::Events` out of this `epoll::EventVec`. #[inline] pub fn clear(&mut self) { self.events.clear(); } - /// Shrinks the capacity of this `EventVec` as much as possible. + /// Shrinks the capacity of this `epoll::EventVec` as much as possible. #[inline] pub fn shrink_to_fit(&mut self) { self.events.shrink_to_fit(); } - /// Returns an iterator over the `Event`s in this `EventVec`. + /// Returns an iterator over the `epoll::Event`s in this `epoll::EventVec`. #[inline] pub fn iter(&self) -> Iter<'_> { Iter { @@ -408,13 +409,14 @@ impl EventVec { } } - /// Returns the number of `Event`s logically contained in this `EventVec`. + /// Returns the number of `epoll::Event`s logically contained in this + /// `epoll::EventVec`. #[inline] pub fn len(&mut self) -> usize { self.events.len() } - /// Tests whether this `EventVec` is logically empty. + /// Tests whether this `epoll::EventVec` is logically empty. #[inline] pub fn is_empty(&mut self) -> bool { self.events.is_empty() @@ -424,7 +426,7 @@ impl EventVec { #[cfg(feature = "alloc")] impl<'a> IntoIterator for &'a EventVec { type IntoIter = Iter<'a>; - type Item = Event; + type Item = epoll::Event; #[inline] fn into_iter(self) -> Self::IntoIter { From e2014c52d4834d1f1c41392228a3eabc8b74ab61 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 08:05:55 -0700 Subject: [PATCH 46/54] Miscellaneous documentation and clippy fixes for futex. (#1134) --- src/backend/libc/thread/syscalls.rs | 30 +++- src/backend/linux_raw/thread/syscalls.rs | 27 ++-- src/thread/futex.rs | 182 ++++++++++------------- src/thread/mod.rs | 3 +- 4 files changed, 124 insertions(+), 118 deletions(-) diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index 036b60d48..27836ebe9 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -7,7 +7,14 @@ use crate::io; use crate::thread::{NanosleepRelativeResult, Timespec}; #[cfg(all(target_env = "gnu", fix_y2038))] use crate::timespec::LibcTimespec; +#[cfg(all( + linux_kernel, + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) +))] +use crate::utils::option_as_ptr; use core::mem::MaybeUninit; +#[cfg(linux_kernel)] use core::sync::atomic::AtomicU32; #[cfg(linux_kernel)] use { @@ -416,6 +423,9 @@ pub(crate) fn setresgid_thread( unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, @@ -426,9 +436,12 @@ pub(crate) unsafe fn futex_val2( uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // The least-significant four bytes of the timeout pointer are used as `val2`. - // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), - // so we perform that exact conversion in reverse to create the pointer. + // Pass `val2` in the least-significant bytes of the `timeout` argument. + // [“the kernel casts the timeout value first to unsigned long, then to + // uint32_t”], so we perform that exact conversion in reverse to create + // the pointer. + // + // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html let timeout = val2 as usize as *const Timespec; #[cfg(all( @@ -489,6 +502,9 @@ pub(crate) unsafe fn futex_val2( } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, @@ -566,6 +582,9 @@ pub(crate) unsafe fn futex_timeout( } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] #[cfg(all( target_pointer_width = "32", @@ -606,10 +625,7 @@ unsafe fn futex_old_timespec( uaddr, op as i32 | flags.bits() as i32, val, - old_timeout - .as_ref() - .map(|timeout| timeout as *const linux_raw_sys::general::__kernel_old_timespec) - .unwrap_or(core::ptr::null()), + option_as_ptr(old_timeout.as_ref()), uaddr2, val3, ) as isize) diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index 336453131..a88a8e51e 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -16,9 +16,9 @@ use crate::pid::Pid; use crate::thread::{futex, ClockId, NanosleepRelativeResult, Timespec}; use core::mem::MaybeUninit; use core::sync::atomic::AtomicU32; -#[cfg(target_pointer_width = "32")] -use linux_raw_sys::general::timespec as __kernel_old_timespec; use linux_raw_sys::general::{__kernel_timespec, TIMER_ABSTIME}; +#[cfg(target_pointer_width = "32")] +use {crate::utils::option_as_ptr, linux_raw_sys::general::timespec as __kernel_old_timespec}; #[inline] pub(crate) fn clock_nanosleep_relative( @@ -204,6 +204,9 @@ pub(crate) fn gettid() -> Pid { } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[inline] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, @@ -214,9 +217,12 @@ pub(crate) unsafe fn futex_val2( uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // The least-significant four bytes of the timeout pointer are used as `val2`. - // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), - // so we perform that exact conversion in reverse to create the pointer. + // Pass `val2` in the least-significant bytes of the `timeout` argument. + // [“the kernel casts the timeout value first to unsigned long, then to + // uint32_t”], so we perform that exact conversion in reverse to create + // the pointer. + // + // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html let timeout = val2 as usize as *const Timespec; #[cfg(target_pointer_width = "32")] @@ -243,6 +249,9 @@ pub(crate) unsafe fn futex_val2( )) } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[inline] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, @@ -286,6 +295,9 @@ pub(crate) unsafe fn futex_timeout( )) } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(target_pointer_width = "32")] unsafe fn futex_old_timespec( uaddr: *const AtomicU32, @@ -312,10 +324,7 @@ unsafe fn futex_old_timespec( uaddr, (op, flags), c_uint(val), - old_timeout - .as_ref() - .map(|timeout| timeout as *const __kernel_old_timespec) - .unwrap_or(core::ptr::null()), + option_as_ptr(old_timeout.as_ref()), uaddr2, c_uint(val3) )) diff --git a/src/thread/futex.rs b/src/thread/futex.rs index 0cb6687b9..97aa084fd 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -1,14 +1,25 @@ //! Linux `futex`. //! +//! Futex is a very low-level mechanism for implementing concurrency primitives +//! such as mutexes, rwlocks, and condvars. +//! +//! # Examples +//! +//! ``` +//! use rustix::thread::futex; +//! use std::sync::atomic::AtomicU32; +//! +//! # fn test(futex: &AtomicU32) -> rustix::io::Result<()> { +//! // Wake up one waiter. +//! futex::wake(futex, futex::Flags::PRIVATE, 1)?; +//! # Ok(()) +//! # } +//! ``` + //! # References //! - [Linux `futex` system call] //! - [Linux `futex` feature] //! -//! # Safety -//! -//! Futex is a very low-level mechanism for implementing concurrency -//! primitives. -//! //! [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html //! [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #![allow(unsafe_code)] @@ -20,28 +31,22 @@ use core::sync::atomic::AtomicU32; use crate::backend::thread::futex::Operation; use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; use crate::fd::{FromRawFd, OwnedFd, RawFd}; +use crate::utils::option_as_ptr; use crate::{backend, io}; pub use crate::timespec::Timespec; -pub use backend::thread::futex::Flags; - -/// `FUTEX_WAITERS` -pub const WAITERS: u32 = backend::thread::futex::WAITERS; -/// `FUTEX_OWNER_DIED` -pub const OWNER_DIED: u32 = backend::thread::futex::OWNER_DIED; +pub use backend::thread::futex::{Flags, OWNER_DIED, WAITERS}; /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -51,16 +56,14 @@ pub fn wait( val: u32, timeout: Option, ) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::Wait, flags, val, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), 0, ) @@ -75,37 +78,35 @@ pub fn wait( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Fd, flags, val, 0, ptr::null(), 0).map(|val| { let fd = val as RawFd; @@ -117,15 +118,13 @@ pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_REQUEUE, val, val2, uaddr2, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -136,20 +135,19 @@ pub fn requeue( val2: u32, uaddr2: &AtomicU32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -161,12 +159,14 @@ pub fn cmp_requeue( uaddr2: &AtomicU32, val3: u32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) } } /// `FUTEX_OP_*` operations for use with [`wake_op`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] +#[allow(clippy::identity_op)] pub enum WakeOp { /// `FUTEX_OP_SET`: `uaddr2 = oparg;` Set = 0, @@ -210,18 +210,17 @@ pub enum WakeOpCmp { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, uaddr2, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] +#[allow(clippy::too_many_arguments)] pub fn wake_op( uaddr: &AtomicU32, flags: Flags, @@ -240,34 +239,31 @@ pub fn wake_op( let val3 = ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32); + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::LockPi, flags, 0, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), 0, ) @@ -282,19 +278,18 @@ pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::UnlockPi, flags, 0, 0, ptr::null(), 0).map(|val| { debug_assert_eq!( @@ -307,19 +302,18 @@ pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_TRYLOCK_PI, 0, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0) } @@ -327,15 +321,13 @@ pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -346,16 +338,14 @@ pub fn wait_bitset( timeout: Option, val3: NonZeroU32, ) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::WaitBitset, flags, val, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), val3.get(), ) @@ -370,15 +360,13 @@ pub fn wait_bitset( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -388,6 +376,7 @@ pub fn wake_bitset( val: u32, val3: NonZeroU32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2( uaddr, @@ -403,15 +392,13 @@ pub fn wake_bitset( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -422,16 +409,14 @@ pub fn wait_requeue_pi( timeout: Option, uaddr2: &AtomicU32, ) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::WaitRequeuePi, flags, val, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), uaddr2, 0, ) @@ -446,15 +431,13 @@ pub fn wait_requeue_pi( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, val2, uaddr2, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -465,34 +448,31 @@ pub fn cmp_requeue_pi( uaddr2: &AtomicU32, val3: u32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::LockPi2, flags, 0, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), 0, ) diff --git a/src/thread/mod.rs b/src/thread/mod.rs index ce62d8f68..cb22dd271 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -34,7 +34,8 @@ pub use prctl::*; #[cfg(linux_kernel)] pub use setns::*; -/// DEPRECATED: There are now individual functions available to perform futex operations with improved type safety. See the [futex module]. +/// DEPRECATED: There are now individual functions available to perform futex +/// operations with improved type safety. See the [futex module]. /// /// `futex(uaddr, op, val, utime, uaddr2, val3)` /// From 8da70cbd8297574e3c9bced63e40e040bf8e93a0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 08:08:20 -0700 Subject: [PATCH 47/54] Miscellaneous documentation and clippy fixes for epoll. (#1135) --- src/backend/libc/event/syscalls.rs | 23 ++++++++++++++--------- src/backend/linux_raw/event/syscalls.rs | 13 +++++++++++++ src/event/epoll.rs | 17 +++++++++-------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 23cb93085..8f3ac0644 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -3,7 +3,7 @@ use crate::backend::c; #[cfg(any(linux_kernel, target_os = "redox"))] use crate::backend::conv::ret_u32; -use crate::backend::conv::{ret, ret_c_int, ret_owned_fd}; +use crate::backend::conv::{borrowed_fd, ret, ret_c_int, ret_owned_fd}; #[cfg(solarish)] use crate::event::port::Event; #[cfg(any( @@ -14,11 +14,14 @@ use crate::event::port::Event; ))] use crate::event::EventfdFlags; use crate::event::PollFd; -use crate::fd::OwnedFd; +use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; +#[cfg(solarish)] use crate::utils::as_mut_ptr; +#[cfg(any(linux_kernel, target_os = "redox"))] +use crate::utils::as_ptr; +use core::mem::MaybeUninit; use core::ptr::null_mut; -use {crate::backend::conv::borrowed_fd, crate::fd::BorrowedFd, core::mem::MaybeUninit}; #[cfg(all(feature = "alloc", bsd))] use {crate::event::kqueue::Event, crate::utils::as_ptr, core::ptr::null}; @@ -181,10 +184,10 @@ pub(crate) fn port_send( #[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] pub(crate) fn pause() { - let r = unsafe { libc::pause() }; + let r = unsafe { c::pause() }; let errno = libc_errno::errno().0; debug_assert_eq!(r, -1); - debug_assert_eq!(errno, libc::EINTR); + debug_assert_eq!(errno, c::EINTR); } #[inline] @@ -198,7 +201,7 @@ pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result, source: BorrowedFd, - event: &mut crate::event::epoll::Event, + event: &crate::event::epoll::Event, ) -> io::Result<()> { // We use our own `Event` struct instead of libc's because // ours preserves pointer provenance instead of just using a `u64`, @@ -208,7 +211,8 @@ pub(crate) fn epoll_add( borrowed_fd(epoll), c::EPOLL_CTL_ADD, borrowed_fd(source), - as_mut_ptr(event).cast(), + // The event is read-only even though libc has a non-const pointer. + as_ptr(event) as *mut c::epoll_event, )) } } @@ -218,14 +222,15 @@ pub(crate) fn epoll_add( pub(crate) fn epoll_mod( epoll: BorrowedFd<'_>, source: BorrowedFd, - event: &mut crate::event::epoll::Event, + event: &crate::event::epoll::Event, ) -> io::Result<()> { unsafe { ret(c::epoll_ctl( borrowed_fd(epoll), c::EPOLL_CTL_MOD, borrowed_fd(source), - as_mut_ptr(event).cast(), + // The event is read-only even though libc has a non-const pointer. + as_ptr(event) as *mut c::epoll_event, )) } } diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index abae5c62e..b5ad45db3 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -12,6 +12,7 @@ use crate::backend::conv::{ use crate::event::{epoll, EventfdFlags, PollFd}; use crate::fd::{BorrowedFd, OwnedFd}; use crate::io; +#[cfg(feature = "alloc")] use core::mem::MaybeUninit; use linux_raw_sys::general::{EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD}; #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] @@ -51,6 +52,7 @@ pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result io::Result { + // SAFETY: `__NR_epoll_create1` doesn't access any user memory. unsafe { ret_owned_fd(syscall_readonly!(__NR_epoll_create1, flags)) } } @@ -60,6 +62,8 @@ pub(crate) fn epoll_add( fd: BorrowedFd, event: &epoll::Event, ) -> io::Result<()> { + // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_ADD` doesn't modify any user + // memory, and it only reads from `event`. unsafe { ret(syscall_readonly!( __NR_epoll_ctl, @@ -77,6 +81,8 @@ pub(crate) fn epoll_mod( fd: BorrowedFd, event: &epoll::Event, ) -> io::Result<()> { + // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_MOD` doesn't modify any user + // memory, and it only reads from `event`. unsafe { ret(syscall_readonly!( __NR_epoll_ctl, @@ -90,6 +96,8 @@ pub(crate) fn epoll_mod( #[inline] pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd) -> io::Result<()> { + // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_DEL` doesn't access any user + // memory. unsafe { ret(syscall_readonly!( __NR_epoll_ctl, @@ -101,6 +109,7 @@ pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd) -> io::Result<()> } } +#[cfg(feature = "alloc")] #[inline] pub(crate) fn epoll_wait( epfd: BorrowedFd<'_>, @@ -108,6 +117,8 @@ pub(crate) fn epoll_wait( timeout: c::c_int, ) -> io::Result { let (buf_addr_mut, buf_len) = slice_mut(events); + // SAFETY: `__NR_epoll_wait` doesn't access any user memory outside of + // the `events` array. #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] unsafe { ret_usize(syscall!( @@ -118,6 +129,8 @@ pub(crate) fn epoll_wait( c_int(timeout) )) } + // SAFETY: `__NR_epoll_pwait` doesn't access any user memory outside of + // the `events` array, as we don't pass it a `sigmask`. #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] unsafe { ret_usize(syscall!( diff --git a/src/event/epoll.rs b/src/event/epoll.rs index 07cb9da93..0990407b9 100644 --- a/src/event/epoll.rs +++ b/src/event/epoll.rs @@ -73,6 +73,7 @@ #![allow(unused_qualifications)] use super::epoll; +#[cfg(feature = "alloc")] use crate::backend::c; pub use crate::backend::event::epoll::*; use crate::backend::event::syscalls; @@ -102,14 +103,14 @@ pub fn create(flags: epoll::CreateFlags) -> io::Result { /// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an epoll /// object. /// -/// This registers interest in any of the events set in `event_flags` occurring on -/// the file descriptor associated with `data`. +/// This registers interest in any of the events set in `event_flags` occurring +/// on the file descriptor associated with `data`. /// /// Note that `close`ing a file descriptor does not necessarily unregister -/// interest which can lead to spurious events being returned from [`epoll::wait`]. If -/// a file descriptor is an `Arc`, then `epoll` can be -/// thought to maintain a `Weak` to the file descriptor. -/// Check the [faq] for details. +/// interest which can lead to spurious events being returned from +/// [`epoll::wait`]. If a file descriptor is an `Arc`, then +/// `epoll` can be thought to maintain a `Weak` to the file +/// descriptor. Check the [faq] for details. /// /// # References /// - [Linux] @@ -127,7 +128,7 @@ pub fn add( syscalls::epoll_add( epoll.as_fd(), source.as_fd(), - &mut Event { + &Event { flags: event_flags, data, #[cfg(all(libc, target_os = "redox"))] @@ -156,7 +157,7 @@ pub fn modify( syscalls::epoll_mod( epoll.as_fd(), source.as_fd(), - &mut Event { + &Event { flags: event_flags, data, #[cfg(all(libc, target_os = "redox"))] From 547fbe776eab0f2bb83a92c2c539647c468bdb03 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 08:09:06 -0700 Subject: [PATCH 48/54] Add a module-level example comment to the shm module. (#1137) --- src/shm.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/shm.rs b/src/shm.rs index 19103ca3a..b404e9134 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -1,4 +1,55 @@ //! POSIX shared memory +//! +//! # Example +//! +//! ``` +//! use rustix::fs::{ftruncate, Mode}; +//! use rustix::mm::{mmap, MapFlags, ProtFlags}; +//! use rustix::{io, shm}; +//! use std::mem::size_of; +//! use std::ptr::null_mut; +//! +//! # fn example() -> io::Result<()> { +//! // A type describing the data to be shared. +//! #[repr(C)] +//! struct MyBufferType { +//! // ... +//! } +//! +//! // Create the shared memory object. +//! let shm_path = "/rustix-shm-example"; +//! let fd = shm::open( +//! shm_path, +//! shm::OFlags::CREATE | shm::OFlags::EXCL | shm::OFlags::RDWR, +//! Mode::RUSR | Mode::WUSR, +//! )?; +//! +//! // Resize the shared memory object to the size of our data. +//! ftruncate(&fd, size_of::() as u64)?; +//! +//! // Map the shared memory object into our address space. +//! // +//! // SAFETY: We're creating a new mapping that's independent of any existing +//! // memory allocations. There are interesting things to say about *using* +//! // `ptr`, but that's for another safety comment. +//! let ptr = unsafe { +//! mmap( +//! null_mut(), +//! size_of::(), +//! ProtFlags::READ | ProtFlags::WRITE, +//! MapFlags::SHARED, +//! &fd, +//! 0, +//! )? +//! }; +//! +//! // Use `ptr`... +//! +//! // Remove the shared memory object name. +//! shm::unlink(shm_path)?; +//! # Ok(()) +//! # } +//! ``` #![allow(unused_qualifications)] @@ -8,13 +59,13 @@ use crate::{backend, io, path}; use super::shm; pub use crate::backend::fs::types::Mode; pub use crate::backend::shm::types::ShmOFlags as OFlags; -#[deprecated(note = "Use OFlags.")] +#[deprecated(note = "Use `shm::OFlags`.")] #[doc(hidden)] pub use crate::backend::shm::types::ShmOFlags; -#[deprecated(note = "Use open.")] +#[deprecated(note = "Use `shm::open`.")] #[doc(hidden)] pub use open as shm_open; -#[deprecated(note = "Use unlink.")] +#[deprecated(note = "Use `shm::unlink`.")] #[doc(hidden)] pub use unlink as shm_unlink; From 3bf3410886b9a954b23354cc32c2472d6636ba4b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 08:09:35 -0700 Subject: [PATCH 49/54] Add `inotify::` qualifiers to the inotify deprecation messages. (#1136) --- src/fs/inotify.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index 53ddb5c41..6a21f65aa 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -12,13 +12,13 @@ use crate::io::{read_uninit, Errno}; use core::mem::{align_of, size_of, MaybeUninit}; use linux_raw_sys::general::inotify_event; -#[deprecated(note = "Use add_watch.")] +#[deprecated(note = "Use `inotify::add_watch`.")] #[doc(hidden)] pub use add_watch as inotify_add_watch; -#[deprecated(note = "Use init.")] +#[deprecated(note = "Use `inotify::init`.")] #[doc(hidden)] pub use init as inotify_init; -#[deprecated(note = "Use remove_watch.")] +#[deprecated(note = "Use `inotify::remove_watch`.")] #[doc(hidden)] pub use remove_watch as inotify_remove_watch; From e8bcc80268b0f1b84660493864341637b248514d Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 08:10:11 -0700 Subject: [PATCH 50/54] Add more documentation for inotify. (#1140) Add a simple module-level example for inotify, to show how to get started, and to demonstrate the `use rustix::fs::inotify;` pattern. And, suppress a clippy lint about how `inotify::Reader::next` looks like `Iterator::next`. Add a comment explaining how it differs. And add a comment about what it does if there are no events waiting. --- src/fs/inotify.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index 6a21f65aa..a248f25c7 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -1,4 +1,43 @@ //! inotify support for working with inotifies +//! +//! # Examples +//! +//! ``` +//! use rustix::fs::inotify; +//! use rustix::io; +//! use std::mem::MaybeUninit; +//! +//! # fn test() -> io::Result<()> { +//! // Creeate an inotify object. In this example, we use `NONBLOCK` so that +//! // the reader fails with `WOULDBLOCk` when no events are ready. Otherwise +//! // it will block until at least one event is ready. +//! let inotify = inotify::init(inotify::CreateFlags::NONBLOCK)?; +//! +//! // Add a directory to watch. +//! inotify::add_watch( +//! &inotify, +//! "/path/to/some/directory/to/watch", +//! inotify::WatchFlags::ALL_EVENTS, +//! )?; +//! +//! // Generate some events in the watched directory... +//! +//! // Loop over pending events. +//! let mut buf = [MaybeUninit::uninit(); 512]; +//! let mut iter = inotify::Reader::new(inotify, &mut buf); +//! loop { +//! let entry = match iter.next() { +//! // Stop iterating if there are no more events for now. +//! Err(io::Errno::WOULDBLOCK) => break, +//! Err(e) => return Err(e), +//! Ok(entry) => entry, +//! }; +//! +//! // Use `entry`... +//! } +//! +//! # Ok(()) +//! # } #![allow(unused_qualifications)] @@ -130,7 +169,19 @@ impl<'a> InotifyEvent<'a> { impl<'buf, Fd: AsFd> Reader<'buf, Fd> { /// Read the next inotify event. + /// + /// This is similar to `[Iterator::next`] except that it doesn't return an + /// `Option`, because the stream doesn't have an ending. It always returns + /// events or errors. + /// + /// If there are no events in the buffer and none ready to be read: + /// - If the file descriptor was opened with + /// [`inotify::CreateFlags::NONBLOCK`], this will fail with + /// [`Errno::AGAIN`]. + /// - Otherwise this will block until at least one event is ready or an + /// error occurs. #[allow(unsafe_code)] + #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> io::Result { if self.is_buffer_empty() { match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { @@ -144,6 +195,7 @@ impl<'buf, Fd: AsFd> Reader<'buf, Fd> { } let ptr = self.buf[self.offset..].as_ptr(); + // SAFETY: // - This data is initialized by the check above. // - Assumption: the kernel will not give us partial structs. From 5ee37f58bc3f9eadbc2bd4703e8e9da1005dd9fa Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 08:10:37 -0700 Subject: [PATCH 51/54] Disable the `futex` deprecation for now. (#1138) There are significant numbers of users using `futex`, so don't deprecate the old API for now. --- src/backend/libc/thread/futex.rs | 3 +++ src/backend/linux_raw/thread/futex.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/backend/libc/thread/futex.rs b/src/backend/libc/thread/futex.rs index 7b26d865b..cc8465fad 100644 --- a/src/backend/libc/thread/futex.rs +++ b/src/backend/libc/thread/futex.rs @@ -51,6 +51,8 @@ pub(crate) enum Operation { /// `FUTEX_*` operations for use with the [`futex`] function. /// /// [`futex`]: fn@crate::thread::futex +// TODO: Deprecate this now that we have a new typed API. +/* #[deprecated( since = "0.38.35", note = " @@ -58,6 +60,7 @@ pub(crate) enum Operation { individual functions available to perform futex operations with improved type safety. See the `rustix::thread::futex` module." )] +*/ #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] pub enum FutexOperation { diff --git a/src/backend/linux_raw/thread/futex.rs b/src/backend/linux_raw/thread/futex.rs index a0a63abd4..232096e75 100644 --- a/src/backend/linux_raw/thread/futex.rs +++ b/src/backend/linux_raw/thread/futex.rs @@ -53,6 +53,8 @@ pub(crate) enum Operation { /// `FUTEX_*` operations for use with the [`futex`] function. /// /// [`futex`]: fn@crate::thread::futex +// TODO: Deprecate this now that we have a new typed API. +/* #[deprecated( since = "0.38.35", note = " @@ -60,6 +62,7 @@ pub(crate) enum Operation { individual functions available to perform futex operations with improved type safety. See the `rustix::thread::futex` module." )] +*/ #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] pub enum FutexOperation { From b9788513bab80138deccddebc2ef72a0daa94a71 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 08:59:52 -0700 Subject: [PATCH 52/54] Fix `elided_lifetimes_in_paths` warnings. (#1141) Fix `elided_lifetimes_in_paths` warnings that are enabled in the libstd build. --- src/backend/libc/event/syscalls.rs | 6 +++--- src/backend/linux_raw/event/syscalls.rs | 6 +++--- src/fs/inotify.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 8f3ac0644..626ddb524 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -200,7 +200,7 @@ pub(crate) fn epoll_create(flags: super::epoll::CreateFlags) -> io::Result, - source: BorrowedFd, + source: BorrowedFd<'_>, event: &crate::event::epoll::Event, ) -> io::Result<()> { // We use our own `Event` struct instead of libc's because @@ -221,7 +221,7 @@ pub(crate) fn epoll_add( #[cfg(any(linux_kernel, target_os = "redox"))] pub(crate) fn epoll_mod( epoll: BorrowedFd<'_>, - source: BorrowedFd, + source: BorrowedFd<'_>, event: &crate::event::epoll::Event, ) -> io::Result<()> { unsafe { @@ -237,7 +237,7 @@ pub(crate) fn epoll_mod( #[inline] #[cfg(any(linux_kernel, target_os = "redox"))] -pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd) -> io::Result<()> { +pub(crate) fn epoll_del(epoll: BorrowedFd<'_>, source: BorrowedFd<'_>) -> io::Result<()> { unsafe { ret(c::epoll_ctl( borrowed_fd(epoll), diff --git a/src/backend/linux_raw/event/syscalls.rs b/src/backend/linux_raw/event/syscalls.rs index b5ad45db3..ac199adfa 100644 --- a/src/backend/linux_raw/event/syscalls.rs +++ b/src/backend/linux_raw/event/syscalls.rs @@ -59,7 +59,7 @@ pub(crate) fn epoll_create(flags: epoll::CreateFlags) -> io::Result { #[inline] pub(crate) fn epoll_add( epfd: BorrowedFd<'_>, - fd: BorrowedFd, + fd: BorrowedFd<'_>, event: &epoll::Event, ) -> io::Result<()> { // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_ADD` doesn't modify any user @@ -78,7 +78,7 @@ pub(crate) fn epoll_add( #[inline] pub(crate) fn epoll_mod( epfd: BorrowedFd<'_>, - fd: BorrowedFd, + fd: BorrowedFd<'_>, event: &epoll::Event, ) -> io::Result<()> { // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_MOD` doesn't modify any user @@ -95,7 +95,7 @@ pub(crate) fn epoll_mod( } #[inline] -pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd) -> io::Result<()> { +pub(crate) fn epoll_del(epfd: BorrowedFd<'_>, fd: BorrowedFd<'_>) -> io::Result<()> { // SAFETY: `__NR_epoll_ctl` with `EPOLL_CTL_DEL` doesn't access any user // memory. unsafe { diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index a248f25c7..dd7fcad9c 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -182,7 +182,7 @@ impl<'buf, Fd: AsFd> Reader<'buf, Fd> { /// error occurs. #[allow(unsafe_code)] #[allow(clippy::should_implement_trait)] - pub fn next(&mut self) -> io::Result { + pub fn next(&mut self) -> io::Result> { if self.is_buffer_empty() { match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { Ok(0) => return Err(Errno::INVAL), From c4c4511b3145306dd9740957533bdca932be6938 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 11:47:30 -0700 Subject: [PATCH 53/54] Miscellaneous documentation fixes. (#1142) Fix a typo, fix some warnings from the new clippy::too_long_first_doc_paragraph, and tidy up. --- src/fs/inotify.rs | 10 +++++----- src/net/types.rs | 6 ++++-- src/process/prctl.rs | 9 +++++---- src/shm.rs | 4 ++-- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index dd7fcad9c..f169e0470 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -8,9 +8,9 @@ //! use std::mem::MaybeUninit; //! //! # fn test() -> io::Result<()> { -//! // Creeate an inotify object. In this example, we use `NONBLOCK` so that -//! // the reader fails with `WOULDBLOCk` when no events are ready. Otherwise -//! // it will block until at least one event is ready. +//! // Create an inotify object. In this example, we use `NONBLOCK` so that the +//! // reader fails with `WOULDBLOCk` when no events are ready. Otherwise it +//! // will block until at least one event is ready. //! let inotify = inotify::init(inotify::CreateFlags::NONBLOCK)?; //! //! // Add a directory to watch. @@ -20,7 +20,7 @@ //! inotify::WatchFlags::ALL_EVENTS, //! )?; //! -//! // Generate some events in the watched directory... +//! // Generate some events in the watched directory… //! //! // Loop over pending events. //! let mut buf = [MaybeUninit::uninit(); 512]; @@ -33,7 +33,7 @@ //! Ok(entry) => entry, //! }; //! -//! // Use `entry`... +//! // Use `entry`… //! } //! //! # Ok(()) diff --git a/src/net/types.rs b/src/net/types.rs index aabffbc6c..0d4c40ea8 100644 --- a/src/net/types.rs +++ b/src/net/types.rs @@ -643,8 +643,10 @@ const fn new_raw_protocol(u: u32) -> RawProtocol { } /// `IPPROTO_*` and other constants for use with [`socket`], [`socket_with`], -/// and [`socketpair`] when a nondefault value is desired. See the [`ipproto`], -/// [`sysproto`], and [`netlink`] modules for possible values. +/// and [`socketpair`] when a nondefault value is desired. +/// +/// See the [`ipproto`], [`sysproto`], and [`netlink`] modules for possible +/// values. /// /// For the default values, such as `IPPROTO_IP` or `NETLINK_ROUTE`, pass /// `None` as the `protocol` argument in these functions. diff --git a/src/process/prctl.rs b/src/process/prctl.rs index ed64adb6e..6a2465b6b 100644 --- a/src/process/prctl.rs +++ b/src/process/prctl.rs @@ -110,10 +110,11 @@ pub fn dumpable_behavior() -> io::Result { const PR_SET_DUMPABLE: c_int = 4; -/// Set the state of the `dumpable` attribute, which determines whether the -/// process can be traced and whether core dumps are produced for the calling -/// process upon delivery of a signal whose default behavior is to produce a -/// core dump. +/// Set the state of the `dumpable` attribute. +/// +/// This attribute determines whether the process can be traced and whether +/// core dumps are produced for the calling process upon delivery of a signal +/// whose default behavior is to produce a core dump. /// /// A similar function with the same name is available on FreeBSD (as part of /// the `procctl` interface), but it has an extra argument which allows to diff --git a/src/shm.rs b/src/shm.rs index b404e9134..1904dc4f9 100644 --- a/src/shm.rs +++ b/src/shm.rs @@ -13,7 +13,7 @@ //! // A type describing the data to be shared. //! #[repr(C)] //! struct MyBufferType { -//! // ... +//! // … //! } //! //! // Create the shared memory object. @@ -43,7 +43,7 @@ //! )? //! }; //! -//! // Use `ptr`... +//! // Use `ptr`… //! //! // Remove the shared memory object name. //! shm::unlink(shm_path)?; From 629de02459f756f6dbfc0a609148ed69b4f04a68 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Aug 2024 11:48:43 -0700 Subject: [PATCH 54/54] chore: Release rustix version 0.38.35 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 06de21b7f..85cc2f130 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustix" -version = "0.38.34" +version = "0.38.35" authors = [ "Dan Gohman ", "Jakub Konka ",