From 507731c7100c06dbf6c7da9f6c77721bc5b75701 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 24 Feb 2024 12:39:59 +0800 Subject: [PATCH 01/64] docs: update release doc[skip ci] (#2321) * docs: update release doc[skip ci] * docs: document that it should be done against master[skip ci] --- RELEASE_PROCEDURE.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/RELEASE_PROCEDURE.md b/RELEASE_PROCEDURE.md index b7d1fa3972..3f2041cec7 100644 --- a/RELEASE_PROCEDURE.md +++ b/RELEASE_PROCEDURE.md @@ -11,6 +11,9 @@ major bump. The release is prepared as follows: +> NOTE: the following procedure should be done directly against the master +> branch of the repo. + - Ask for a new libc version if, necessary. It usually is. Then update the dependency in `Cargo.toml` to rely on a release from crates.io. @@ -23,7 +26,12 @@ The release is prepared as follows: - Update the version number in `Cargo.toml` - Generate `CHANGELOG.md` for this release by `towncrier build --version= --yes` + +- Ensure you have a crates.io token + 1. With the `publich-update` scope + 2. Can be used for crate `nix` + 3. It is set via `cargo login` + - Confirm that everything's ready for a release by running `cargo release ` - Create the release with `cargo release -x ` -- Push the created tag to GitHub. From 1def0c05c3d7ac460c3428bb2227cccd01008ccb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 24 Feb 2024 18:41:15 -0800 Subject: [PATCH 02/64] Update cfg_aliases dependency to 0.2 (#2322) --- Cargo.toml | 2 +- changelog/2322.changed.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/2322.changed.md diff --git a/Cargo.toml b/Cargo.toml index d8176a7500..adaf31f0fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,7 @@ caps = "0.5.3" sysctl = "0.4" [build-dependencies] -cfg_aliases = "0.1.1" +cfg_aliases = "0.2" [[test]] name = "test" diff --git a/changelog/2322.changed.md b/changelog/2322.changed.md new file mode 100644 index 0000000000..e84e912885 --- /dev/null +++ b/changelog/2322.changed.md @@ -0,0 +1 @@ +Updated `cfg_aliases` dependency from version 0.1 to 0.2 From b171eebebe937feceb2a6f7bb05abb8bffe283e7 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 26 Feb 2024 07:34:37 +0800 Subject: [PATCH 03/64] feat: sockopt Ipv6Ttl for apple targets (#2287) * feat: sockopt Ipv6Ttl for apple targets * changelog * temporarily enable test for Ipv6Ttl on apple targets * revert last commit --- changelog/2287.added.md | 1 + src/sys/socket/sockopt.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/2287.added.md diff --git a/changelog/2287.added.md b/changelog/2287.added.md new file mode 100644 index 0000000000..a42a65bcf4 --- /dev/null +++ b/changelog/2287.added.md @@ -0,0 +1 @@ +Add socket option Ipv6Ttl for apple targets. diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 4357695f56..ab18035dbb 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -1026,7 +1026,7 @@ sockopt_impl!( libc::IP_TTL, libc::c_int ); -#[cfg(any(linux_android, target_os = "freebsd"))] +#[cfg(any(apple_targets, linux_android, target_os = "freebsd"))] sockopt_impl!( /// Set the unicast hop limit for the socket. Ipv6Ttl, From a97cc878da917267d0288e7ce36a758d51e7c968 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 26 Feb 2024 07:34:54 +0800 Subject: [PATCH 04/64] docs: correct a doc comment (#2317) --- src/dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dir.rs b/src/dir.rs index ab70f064cc..20c5593702 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -59,7 +59,7 @@ impl Dir { Dir::from_fd(fd.into_raw_fd()) } - /// Converts from a file descriptor, closing it on success or failure. + /// Converts from a file descriptor, closing it on failure. #[doc(alias("fdopendir"))] pub fn from_fd(fd: RawFd) -> Result { let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else( From 358fe9f502fc7eaa21ac56a63ae3b1b195e5bcbc Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 26 Feb 2024 07:35:03 +0800 Subject: [PATCH 05/64] refactor: redefine ForkptyResult to avoid uninited master field (#2315) * refactor: redefine ForkptyResult to avoid uninited master field * docs: safety --- changelog/2315.changed.md | 12 +++++++++ src/pty.rs | 56 ++++++++++++++++++++++++--------------- test/test_pty.rs | 15 ++++++----- 3 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 changelog/2315.changed.md diff --git a/changelog/2315.changed.md b/changelog/2315.changed.md new file mode 100644 index 0000000000..bf437876d6 --- /dev/null +++ b/changelog/2315.changed.md @@ -0,0 +1,12 @@ +Change the `ForkptyResult` type to the following repr so that the uninitialized +`master` field won't be accessed in the child process: + +```rs +pub enum ForkptyResult { + Parent { + child: Pid, + master: OwnedFd, + }, + Child, +} +``` diff --git a/src/pty.rs b/src/pty.rs index 74f8ecf0df..818b25b1b1 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -12,8 +12,6 @@ use std::os::unix::prelude::*; use crate::errno::Errno; #[cfg(not(target_os = "aix"))] use crate::sys::termios::Termios; -#[cfg(feature = "process")] -use crate::unistd::ForkResult; #[cfg(all(feature = "process", not(target_os = "aix")))] use crate::unistd::Pid; use crate::{fcntl, unistd, Result}; @@ -31,15 +29,19 @@ pub struct OpenptyResult { feature! { #![feature = "process"] -/// Representation of a master with a forked pty -/// -/// This is returned by [`forkpty`]. +/// A successful result of [`forkpty()`]. #[derive(Debug)] -pub struct ForkptyResult { - /// The master port in a virtual pty pair - pub master: OwnedFd, - /// Metadata about forked process - pub fork_result: ForkResult, +pub enum ForkptyResult { + /// This is the parent process of the underlying fork. + Parent { + /// The PID of the fork's child process + child: Pid, + /// A file descriptor referring to master side of the pseudoterminal of + /// the child process. + master: OwnedFd, + }, + /// This is the child process of the underlying fork. + Child, } } @@ -300,9 +302,7 @@ pub fn openpty< feature! { #![feature = "process"] -/// Create a new pseudoterminal, returning the master file descriptor and forked pid. -/// in `ForkptyResult` -/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.3.html)). +/// Create a new process operating in a pseudoterminal. /// /// If `winsize` is not `None`, the window size of the slave will be set to /// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's @@ -319,6 +319,11 @@ feature! { /// special care must be taken to only invoke code you can control and audit. /// /// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html +/// +/// # Reference +/// +/// * [FreeBSD](https://man.freebsd.org/cgi/man.cgi?query=forkpty) +/// * [Linux](https://man7.org/linux/man-pages/man3/forkpty.3.html) #[cfg(not(target_os = "aix"))] pub unsafe fn forkpty<'a, 'b, T: Into>, U: Into>>( winsize: T, @@ -343,14 +348,23 @@ pub unsafe fn forkpty<'a, 'b, T: Into>, U: Into ForkResult::Child, - res => ForkResult::Parent { child: Pid::from_raw(res) }, - })?; + let success_ret = Errno::result(res)?; + let forkpty_result = match success_ret { + // In the child process + 0 => ForkptyResult::Child, + // In the parent process + child_pid => { + // SAFETY: + // 1. The master buffer is guaranteed to be initialized in the parent process + // 2. OwnedFd::from_raw_fd won't panic as the fd is a valid file descriptor + let master = unsafe { OwnedFd::from_raw_fd( master.assume_init() ) }; + ForkptyResult::Parent { + master, + child: Pid::from_raw(child_pid), + } + } + }; - Ok(ForkptyResult { - master: unsafe { OwnedFd::from_raw_fd( master.assume_init() ) }, - fork_result, - }) + Ok(forkpty_result) } } diff --git a/test/test_pty.rs b/test/test_pty.rs index 368ec129b0..dda8b3f6ce 100644 --- a/test/test_pty.rs +++ b/test/test_pty.rs @@ -8,6 +8,7 @@ use nix::fcntl::{open, OFlag}; use nix::pty::*; use nix::sys::stat; use nix::sys::termios::*; +use nix::sys::wait::WaitStatus; use nix::unistd::{pause, write}; /// Test equivalence of `ptsname` and `ptsname_r` @@ -247,7 +248,6 @@ fn test_openpty_with_termios() { fn test_forkpty() { use nix::sys::signal::*; use nix::sys::wait::wait; - use nix::unistd::ForkResult::*; // forkpty calls openpty which uses ptname(3) internally. let _m0 = crate::PTSNAME_MTX.lock(); // forkpty spawns a child process @@ -255,21 +255,22 @@ fn test_forkpty() { let string = "naninani\n"; let echoed_string = "naninani\r\n"; - let pty = unsafe { forkpty(None, None).unwrap() }; - match pty.fork_result { - Child => { + let res = unsafe { forkpty(None, None).unwrap() }; + match res { + ForkptyResult::Child => { write(stdout(), string.as_bytes()).unwrap(); pause(); // we need the child to stay alive until the parent calls read unsafe { _exit(0); } } - Parent { child } => { + ForkptyResult::Parent { child, master } => { let mut buf = [0u8; 10]; assert!(child.as_raw() > 0); - crate::read_exact(&pty.master, &mut buf); + crate::read_exact(&master, &mut buf); kill(child, SIGTERM).unwrap(); - wait().unwrap(); // keep other tests using generic wait from getting our child + let status = wait().unwrap(); // keep other tests using generic wait from getting our child + assert_eq!(status, WaitStatus::Signaled(child, SIGTERM, false)); assert_eq!(&buf, echoed_string.as_bytes()); } } From e0f19657601795b5e32f846e4b717596eba87968 Mon Sep 17 00:00:00 2001 From: Song Gao <158983297@qq.com> Date: Fri, 1 Mar 2024 07:46:53 +0800 Subject: [PATCH 06/64] Change the type of data of ptrace::write to make it safe (#2324) * Change the type of data of ptrace::write to make it safe * add change log * add safe commnet --------- Co-authored-by: ShuiRuTian --- changelog/2324.changed.md | 1 + src/sys/ptrace/linux.rs | 38 ++++++++++++++++---------------------- 2 files changed, 17 insertions(+), 22 deletions(-) create mode 100644 changelog/2324.changed.md diff --git a/changelog/2324.changed.md b/changelog/2324.changed.md new file mode 100644 index 0000000000..aca724850c --- /dev/null +++ b/changelog/2324.changed.md @@ -0,0 +1 @@ +Change the signature of `ptrace::write` and `ptrace::write_user` to make them safe \ No newline at end of file diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index 26544e134b..de3124de95 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -543,17 +543,15 @@ pub fn read(pid: Pid, addr: AddressType) -> Result { /// Writes a word into the processes memory at the given address, as with /// ptrace(PTRACE_POKEDATA, ...) -/// -/// # Safety -/// -/// The `data` argument is passed directly to `ptrace(2)`. Read that man page -/// for guidance. -pub unsafe fn write( - pid: Pid, - addr: AddressType, - data: *mut c_void, -) -> Result<()> { - unsafe { ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) } +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn write(pid: Pid, addr: AddressType, data: c_long) -> Result<()> { + unsafe { + // Safety(not_unsafe_ptr_arg_deref): + // `ptrace_other` is a common abstract + // but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten + ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data as *mut c_void) + .map(drop) + } } /// Reads a word from a user area at `offset`, as with ptrace(PTRACE_PEEKUSER, ...). @@ -564,17 +562,13 @@ pub fn read_user(pid: Pid, offset: AddressType) -> Result { /// Writes a word to a user area at `offset`, as with ptrace(PTRACE_POKEUSER, ...). /// The user struct definition can be found in `/usr/include/sys/user.h`. -/// -/// # Safety -/// -/// The `data` argument is passed directly to `ptrace(2)`. Read that man page -/// for guidance. -pub unsafe fn write_user( - pid: Pid, - offset: AddressType, - data: *mut c_void, -) -> Result<()> { +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn write_user(pid: Pid, offset: AddressType, data: c_long) -> Result<()> { unsafe { - ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop) + // Safety(not_unsafe_ptr_arg_deref): + // `ptrace_other` is a common abstract + // but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten + ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data as *mut c_void) + .map(drop) } } From 7badbee1e388618457ed0d725c1091359f253012 Mon Sep 17 00:00:00 2001 From: friedrich Date: Sun, 3 Mar 2024 08:02:44 +0100 Subject: [PATCH 07/64] Add the UtunIfname sockopt (UTUN_OPT_IFNAME) (#2325) --- changelog/2325.added.md | 1 + src/sys/socket/sockopt.rs | 11 +++++++++++ test/sys/test_sockopt.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 changelog/2325.added.md diff --git a/changelog/2325.added.md b/changelog/2325.added.md new file mode 100644 index 0000000000..94dd68b00a --- /dev/null +++ b/changelog/2325.added.md @@ -0,0 +1 @@ +Add socket option UtunIfname. diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index ab18035dbb..708775c11b 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -1065,6 +1065,17 @@ sockopt_impl!( libc::IPV6_DONTFRAG, bool ); +#[cfg(apple_targets)] +#[cfg(feature = "net")] +sockopt_impl!( + /// Get the utun interface name. + UtunIfname, + GetOnly, + libc::SYSPROTO_CONTROL, + libc::UTUN_OPT_IFNAME, + OsString, + GetOsString<[u8; libc::IFNAMSIZ]> +); #[allow(missing_docs)] // Not documented by Linux! diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index a99d4e39ed..61108f2603 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -828,3 +828,34 @@ fn test_ktls() { Err(err) => panic!("{err:?}"), } } + +#[test] +#[cfg(apple_targets)] +fn test_utun_ifname() { + use nix::sys::socket::connect; + use nix::sys::socket::SysControlAddr; + + let fd = socket( + AddressFamily::System, + SockType::Datagram, + SockFlag::empty(), + SockProtocol::KextControl, + ) + .unwrap(); + + let unit = 123; + let addr = SysControlAddr::from_name( + fd.as_raw_fd(), + "com.apple.net.utun_control", + unit, + ) + .unwrap(); + + connect(fd.as_raw_fd(), &addr).unwrap(); + + let name = getsockopt(&fd, sockopt::UtunIfname) + .expect("getting UTUN_OPT_IFNAME on a utun interface should succeed"); + + let expected_name = format!("utun{}\0", unit - 1); + assert_eq!(name.into_string(), Ok(expected_name)); +} From 99f2dc2329af7bd154edb237995f414100b787dc Mon Sep 17 00:00:00 2001 From: Andrej Mihajlov Date: Thu, 7 Mar 2024 12:59:37 +0100 Subject: [PATCH 08/64] Improve ergonomics between SockaddrIn and underlying libc type (#2328) --- changelog/2328.added.md | 1 + src/sys/socket/addr.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 changelog/2328.added.md diff --git a/changelog/2328.added.md b/changelog/2328.added.md new file mode 100644 index 0000000000..2e3f440f50 --- /dev/null +++ b/changelog/2328.added.md @@ -0,0 +1 @@ +Add `From` trait implementation for conversions between `sockaddr_in` and `SockaddrIn`, `sockaddr_in6` and `SockaddrIn6` diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index f6800aa5d0..f3c3d980b6 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -919,6 +919,19 @@ impl From for net::SocketAddrV4 { } } +#[cfg(feature = "net")] +impl From for libc::sockaddr_in { + fn from(sin: SockaddrIn) -> libc::sockaddr_in { + sin.0 + } +} +#[cfg(feature = "net")] +impl From for SockaddrIn { + fn from(sin: libc::sockaddr_in) -> SockaddrIn { + SockaddrIn(sin) + } +} + #[cfg(feature = "net")] impl std::str::FromStr for SockaddrIn { type Err = net::AddrParseError; @@ -969,6 +982,20 @@ impl SockaddrIn6 { } } +#[cfg(feature = "net")] +impl From for libc::sockaddr_in6 { + fn from(sin6: SockaddrIn6) -> libc::sockaddr_in6 { + sin6.0 + } +} + +#[cfg(feature = "net")] +impl From for SockaddrIn6 { + fn from(sin6: libc::sockaddr_in6) -> SockaddrIn6 { + SockaddrIn6(sin6) + } +} + #[cfg(feature = "net")] impl private::SockaddrLikePriv for SockaddrIn6 {} #[cfg(feature = "net")] From bb9ffeb8d4f0a21d5bfdfe2f74cc5e71a48d2cc5 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 9 Mar 2024 11:22:33 +0800 Subject: [PATCH 09/64] style: fix clippy lint clippy::mixed_attributes_style on nightly tier3 (#2330) --- src/sys/socket/addr.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index f3c3d980b6..aa89ba9723 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -2177,9 +2177,8 @@ mod tests { } #[cfg(not(any(target_os = "hurd", target_os = "redox")))] + #[allow(clippy::cast_ptr_alignment)] mod link { - #![allow(clippy::cast_ptr_alignment)] - #[cfg(any(apple_targets, solarish))] use super::super::super::socklen_t; use super::*; From ce0f5b1af63a7879d37f3e738031aeaf36d99339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedrich=20Sch=C3=B6ller?= Date: Sat, 9 Mar 2024 08:39:35 +0100 Subject: [PATCH 10/64] Change UtunIfname sockopt type to CString (#2329) * Implement GetCString for use with sockopt * Change UtunIfname sockopt type to CString --- src/sys/socket/sockopt.rs | 35 ++++++++++++++++++++++++++++++++--- test/sys/test_sockopt.rs | 2 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 708775c11b..09e5fd3b86 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -5,7 +5,7 @@ use crate::sys::time::TimeVal; use crate::Result; use cfg_if::cfg_if; use libc::{self, c_int, c_void, socklen_t}; -use std::ffi::{OsStr, OsString}; +use std::ffi::{CStr, CString, OsStr, OsString}; use std::mem::{self, MaybeUninit}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::{AsFd, AsRawFd}; @@ -1073,8 +1073,8 @@ sockopt_impl!( GetOnly, libc::SYSPROTO_CONTROL, libc::UTUN_OPT_IFNAME, - OsString, - GetOsString<[u8; libc::IFNAMSIZ]> + CString, + GetCString<[u8; libc::IFNAMSIZ]> ); #[allow(missing_docs)] @@ -1579,3 +1579,32 @@ impl<'a> Set<'a, OsString> for SetOsString<'a> { } } +/// Getter for a `CString` value. +struct GetCString> { + len: socklen_t, + val: MaybeUninit, +} + +impl> Get for GetCString { + fn uninit() -> Self { + GetCString { + len: mem::size_of::() as socklen_t, + val: MaybeUninit::uninit(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + self.val.as_mut_ptr().cast() + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn assume_init(self) -> CString { + let mut v = unsafe { self.val.assume_init() }; + CStr::from_bytes_until_nul(v.as_mut()) + .expect("string should be null-terminated") + .to_owned() + } +} diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index 61108f2603..22812acf22 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -856,6 +856,6 @@ fn test_utun_ifname() { let name = getsockopt(&fd, sockopt::UtunIfname) .expect("getting UTUN_OPT_IFNAME on a utun interface should succeed"); - let expected_name = format!("utun{}\0", unit - 1); + let expected_name = format!("utun{}", unit - 1); assert_eq!(name.into_string(), Ok(expected_name)); } From 346c1c39b39e369f59755b546691b506acc12d37 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 16 Mar 2024 16:04:12 +0800 Subject: [PATCH 11/64] ci: disable the DragonFlyBSD CI (#2335) --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a3a02c707..110208f080 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -292,7 +292,9 @@ jobs: fail-fast: false matrix: include: - - target: x86_64-unknown-dragonfly + # Temporarily disable DragonFlyBSD + # https://github.com/nix-rust/nix/issues/2337 + # - target: x86_64-unknown-dragonfly - target: x86_64-unknown-openbsd # Temporarily disable armv7-unknown-linux-uclibceabihf # https://github.com/nix-rust/nix/issues/2200 From 36e0248b820c46772aabce3177ea966d0d3b09c3 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 16 Mar 2024 08:39:06 +0000 Subject: [PATCH 12/64] sys::resource update max_rss accessor doc to reflect the unit per platform (#2333) --- src/sys/resource.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sys/resource.rs b/src/sys/resource.rs index 71315072d4..73d8a05e0f 100644 --- a/src/sys/resource.rs +++ b/src/sys/resource.rs @@ -293,7 +293,9 @@ impl Usage { TimeVal::from(self.0.ru_stime) } - /// The resident set size at its peak, in kilobytes. + /// The resident set size at its peak, + #[cfg_attr(apple_targets, doc = " in bytes.")] + #[cfg_attr(not(apple_targets), doc = " in kilobytes.")] pub fn max_rss(&self) -> c_long { self.0.ru_maxrss } From a0078c16402c90741fe536bcd39445b303de85e1 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 16 Mar 2024 08:39:24 +0000 Subject: [PATCH 13/64] sys::socket: adding freebsd's ReusePortLb constant. (#2332) Allows to use `SO_REUSEPORT_LB` so incoming connections can be distributed to bound (up to 256) sockets in a Load-Balanced fashion. --- changelog/2332.added.md | 1 + src/sys/socket/sockopt.rs | 10 ++++++++++ test/sys/test_sockopt.rs | 16 ++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 changelog/2332.added.md diff --git a/changelog/2332.added.md b/changelog/2332.added.md new file mode 100644 index 0000000000..3080e89cc0 --- /dev/null +++ b/changelog/2332.added.md @@ -0,0 +1 @@ +Add socket option ReusePortLb for FreeBSD. diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 09e5fd3b86..f66b54e1fa 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -270,6 +270,16 @@ sockopt_impl!( libc::SO_REUSEPORT, bool ); +#[cfg(target_os = "freebsd")] +sockopt_impl!( + /// Enables incoming connections to be distributed among N sockets (up to 256) + /// via a Load-Balancing hash based algorithm. + ReusePortLb, + Both, + libc::SOL_SOCKET, + libc::SO_REUSEPORT_LB, + bool +); #[cfg(feature = "net")] sockopt_impl!( #[cfg_attr(docsrs, doc(cfg(feature = "net")))] diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index 22812acf22..427edf24e0 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -859,3 +859,19 @@ fn test_utun_ifname() { let expected_name = format!("utun{}", unit - 1); assert_eq!(name.into_string(), Ok(expected_name)); } + +#[test] +#[cfg(target_os = "freebsd")] +fn test_reuseport_lb() { + let fd = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + setsockopt(&fd, sockopt::ReusePortLb, &false).unwrap(); + assert!(!getsockopt(&fd, sockopt::ReusePortLb).unwrap()); + setsockopt(&fd, sockopt::ReusePortLb, &true).unwrap(); + assert!(getsockopt(&fd, sockopt::ReusePortLb).unwrap()); +} From 08e784960af955a032c5995da7880b36cbe1e4b6 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 16 Mar 2024 16:39:34 +0800 Subject: [PATCH 14/64] feat: make SigAction repr(transparent)&From&Into (#2326) * feat: make SigAction repr(transparent)&From&Into * style: fix clippy --- changelog/2326.added.md | 1 + src/sys/signal.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 changelog/2326.added.md diff --git a/changelog/2326.added.md b/changelog/2326.added.md new file mode 100644 index 0000000000..bcc29cb3db --- /dev/null +++ b/changelog/2326.added.md @@ -0,0 +1 @@ +make SigAction repr(transparent) & can be converted to/from the libc raw type diff --git a/src/sys/signal.rs b/src/sys/signal.rs index c9b593d0db..2a9c0027a9 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -753,11 +753,25 @@ pub enum SigHandler { } /// Action to take on receipt of a signal. Corresponds to `sigaction`. +#[repr(transparent)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SigAction { sigaction: libc::sigaction } +impl From for SigAction { + fn from(value: libc::sigaction) -> Self { + Self { + sigaction: value + } + } +} +impl From for libc::sigaction { + fn from(value: SigAction) -> libc::sigaction { + value.sigaction + } +} + impl SigAction { /// Creates a new action. /// From c6a7d402d9eabf21f2edea28aa4839617e9d5478 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 18 Mar 2024 00:02:16 +0000 Subject: [PATCH 15/64] sys::sysinfo: fix clippy. (#2338) --- src/sys/sysinfo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/sysinfo.rs b/src/sys/sysinfo.rs index e8aa00b00d..a2bc093643 100644 --- a/src/sys/sysinfo.rs +++ b/src/sys/sysinfo.rs @@ -1,4 +1,4 @@ -use libc::{self, SI_LOAD_SHIFT}; +use libc::SI_LOAD_SHIFT; use std::time::Duration; use std::{cmp, mem}; From 01cd697b82087730b00bafa60e25fd53b025cfdd Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Fri, 22 Mar 2024 08:33:12 +0800 Subject: [PATCH 16/64] style: try fixing clippy warning (#2341) --- test/sys/test_socket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 90b8a6f528..ee60e62b45 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -2407,7 +2407,7 @@ fn test_recvmmsg_timestampns() { // Receive the message let mut buffer = vec![0u8; message.len()]; let cmsgspace = nix::cmsg_space!(TimeSpec); - let mut iov = vec![[IoSliceMut::new(&mut buffer)]]; + let mut iov = [[IoSliceMut::new(&mut buffer)]]; let mut data = MultiHeaders::preallocate(1, Some(cmsgspace)); let r: Vec> = recvmmsg( in_socket.as_raw_fd(), From 19f3c43939bfad6a9c9f4829ffab18c2fa1f025f Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 24 Mar 2024 08:23:45 +0800 Subject: [PATCH 17/64] style: ignore false-positive lint clippy::duplicated_attributes (#2344) --- src/sys/statfs.rs | 2 ++ src/sys/time.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs index b2315f4ceb..1150a0f9dc 100644 --- a/src/sys/statfs.rs +++ b/src/sys/statfs.rs @@ -71,6 +71,8 @@ type fs_type_t = libc::c_int; type fs_type_t = libc::__fsword_t; /// Describes the file system type as known by the operating system. +// false positive, see: https://github.com/rust-lang/rust-clippy/issues/12537 +#[allow(clippy::duplicated_attributes)] #[cfg(any( target_os = "freebsd", target_os = "android", diff --git a/src/sys/time.rs b/src/sys/time.rs index af436cabd5..0472fced34 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -12,6 +12,8 @@ const fn zero_init_timespec() -> timespec { unsafe { std::mem::transmute([0u8; std::mem::size_of::()]) } } +// false positive, see: https://github.com/rust-lang/rust-clippy/issues/12537 +#[allow(clippy::duplicated_attributes)] #[cfg(any( all(feature = "time", any(target_os = "android", target_os = "linux")), all( From 6c11f022a589631da586315a658b1465bf327607 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 24 Mar 2024 08:58:09 +0800 Subject: [PATCH 18/64] docs: update code block in CONVENTIONS.md to use cfg alias[skip ci] (#2345) --- CONVENTIONS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONVENTIONS.md b/CONVENTIONS.md index d71a3d17dd..51afae1ce4 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -70,9 +70,9 @@ libc_bitflags!{ PROT_READ; PROT_WRITE; PROT_EXEC; - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(linux_android)] PROT_GROWSDOWN; - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(linux_android)] PROT_GROWSUP; } } From 945910d319f19bd99c74d121f48842225af0cadc Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Mon, 25 Mar 2024 00:45:13 +0100 Subject: [PATCH 19/64] Added if_indextoname function. (#2340) * Added if_indextoname function. * Added changelog. * Fixed pointer cast. * Applied suggestions. --- changelog/2340.added.md | 1 + src/net/if_.rs | 21 +++++++++++++++++---- test/test_net.rs | 11 +++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 changelog/2340.added.md diff --git a/changelog/2340.added.md b/changelog/2340.added.md new file mode 100644 index 0000000000..9d120d4879 --- /dev/null +++ b/changelog/2340.added.md @@ -0,0 +1 @@ +Add if_indextoname function. diff --git a/src/net/if_.rs b/src/net/if_.rs index c66b5dc0b3..5b6c2e77a5 100644 --- a/src/net/if_.rs +++ b/src/net/if_.rs @@ -3,9 +3,9 @@ //! Uses Linux and/or POSIX functions to resolve interface names like "eth0" //! or "socan1" into device numbers. -use std::fmt; -use crate::{Error, NixPath, Result}; -use libc::c_uint; +use std::{ffi::{CStr, CString}, fmt}; +use crate::{errno::Errno, Error, NixPath, Result}; +use libc::{c_uint, IF_NAMESIZE}; #[cfg(not(solarish))] /// type alias for InterfaceFlags @@ -14,7 +14,7 @@ pub type IflagsType = libc::c_int; /// type alias for InterfaceFlags pub type IflagsType = libc::c_longlong; -/// Resolve an interface into a interface number. +/// Resolve an interface into an interface number. pub fn if_nametoindex(name: &P) -> Result { let if_index = name .with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?; @@ -26,6 +26,19 @@ pub fn if_nametoindex(name: &P) -> Result { } } +/// Resolve an interface number into an interface. +pub fn if_indextoname(index: c_uint) -> Result { + // We need to allocate this anyway, so doing it directly is faster. + let mut buf = vec![0u8; IF_NAMESIZE]; + + let return_buf = unsafe { + libc::if_indextoname(index, buf.as_mut_ptr().cast()) + }; + + Errno::result(return_buf.cast())?; + Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned()) +} + libc_bitflags!( /// Standard interface flags, used by `getifaddrs` pub struct InterfaceFlags: IflagsType { diff --git a/test/test_net.rs b/test/test_net.rs index faba8503fe..46a3efd501 100644 --- a/test/test_net.rs +++ b/test/test_net.rs @@ -13,3 +13,14 @@ const LOOPBACK: &[u8] = b"loop"; fn test_if_nametoindex() { if_nametoindex(LOOPBACK).expect("assertion failed"); } + +#[test] +fn test_if_indextoname() { + let loopback_index = if_nametoindex(LOOPBACK).expect("assertion failed"); + assert_eq!( + if_indextoname(loopback_index) + .expect("assertion failed") + .as_bytes(), + LOOPBACK + ); +} From 5b1b6951a0b204ef8cef7d32fe436a2b6e424b6d Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 25 Mar 2024 17:59:52 +0800 Subject: [PATCH 20/64] docs: document we don't alias for ONLY 1 OS[skip ci] (#2346) --- CONVENTIONS.md | 6 +++++- build.rs | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 51afae1ce4..5615da4b33 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -47,9 +47,13 @@ When creating newtypes, we use Rust's `CamelCase` type naming convention. ## cfg gates When creating operating-system-specific functionality, we gate it by -`#[cfg(target_os = ...)]`. If more than one operating system is affected, we +`#[cfg(target_os = ...)]`. If **MORE THAN ONE operating system** is affected, we prefer to use the cfg aliases defined in build.rs, like `#[cfg(bsd)]`. +Please **DO NOT** use cfg aliases for **ONLY ONE** system as [they are bad][mismatched_target_os]. + +[mismatched_target_os]: https://rust-lang.github.io/rust-clippy/master/index.html#/mismatched_target_os + ## Bitflags Many C functions have flags parameters that are combined from constants using diff --git a/build.rs b/build.rs index 4535af1f04..ecadc04f5d 100644 --- a/build.rs +++ b/build.rs @@ -15,6 +15,8 @@ fn main() { watchos: { target_os = "watchos" }, tvos: { target_os = "tvos" }, + + // cfg aliases we would like to use apple_targets: { any(ios, macos, watchos, tvos) }, bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) }, linux_android: { any(android, linux) }, From 4ab23c327889ffb4f57dfec883a9c3c596953949 Mon Sep 17 00:00:00 2001 From: Ben Linsay Date: Mon, 25 Mar 2024 19:14:05 -0400 Subject: [PATCH 21/64] Add support for openat2 (#2339) Adds an openat2 function with an OpenHow and ResolveFlag options for configuring path resolution. Fixes #1400. --- changelog/2339.added.md | 1 + src/fcntl.rs | 113 ++++++++++++++++++++++++++++++++++++++++ test/test_fcntl.rs | 62 ++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 changelog/2339.added.md diff --git a/changelog/2339.added.md b/changelog/2339.added.md new file mode 100644 index 0000000000..0f80767d84 --- /dev/null +++ b/changelog/2339.added.md @@ -0,0 +1 @@ +Added support for openat2 on linux. diff --git a/src/fcntl.rs b/src/fcntl.rs index ccefe955de..16af6e6d86 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -242,6 +242,119 @@ pub fn openat( Errno::result(fd) } +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + libc_bitflags! { + /// Path resolution flags. + /// + /// See [path resolution(7)](https://man7.org/linux/man-pages/man7/path_resolution.7.html) + /// for details of the resolution process. + pub struct ResolveFlag: libc::c_ulonglong { + /// Do not permit the path resolution to succeed if any component of + /// the resolution is not a descendant of the directory indicated by + /// dirfd. This causes absolute symbolic links (and absolute values of + /// pathname) to be rejected. + RESOLVE_BENEATH; + + /// Treat the directory referred to by dirfd as the root directory + /// while resolving pathname. + RESOLVE_IN_ROOT; + + /// Disallow all magic-link resolution during path resolution. Magic + /// links are symbolic link-like objects that are most notably found + /// in proc(5); examples include `/proc/[pid]/exe` and `/proc/[pid]/fd/*`. + /// + /// See symlink(7) for more details. + RESOLVE_NO_MAGICLINKS; + + /// Disallow resolution of symbolic links during path resolution. This + /// option implies RESOLVE_NO_MAGICLINKS. + RESOLVE_NO_SYMLINKS; + + /// Disallow traversal of mount points during path resolution (including + /// all bind mounts). + RESOLVE_NO_XDEV; + } + } + + /// Specifies how [openat2] should open a pathname. + /// + /// See + #[repr(transparent)] + #[derive(Clone, Copy, Debug)] + pub struct OpenHow(libc::open_how); + + impl OpenHow { + /// Create a new zero-filled `open_how`. + pub fn new() -> Self { + // safety: according to the man page, open_how MUST be zero-initialized + // on init so that unknown fields are also zeroed. + Self(unsafe { + std::mem::MaybeUninit::zeroed().assume_init() + }) + } + + /// Set the open flags used to open a file, completely overwriting any + /// existing flags. + pub fn flags(mut self, flags: OFlag) -> Self { + let flags = flags.bits() as libc::c_ulonglong; + self.0.flags = flags; + self + } + + /// Set the file mode new files will be created with, overwriting any + /// existing flags. + pub fn mode(mut self, mode: Mode) -> Self { + let mode = mode.bits() as libc::c_ulonglong; + self.0.mode = mode; + self + } + + /// Set resolve flags, completely overwriting any existing flags. + /// + /// See [ResolveFlag] for more detail. + pub fn resolve(mut self, resolve: ResolveFlag) -> Self { + let resolve = resolve.bits(); + self.0.resolve = resolve; + self + } + } + + // safety: default isn't derivable because libc::open_how must be zeroed + impl Default for OpenHow { + fn default() -> Self { + Self::new() + } + } + + /// Open or create a file for reading, writing or executing. + /// + /// `openat2` is an extension of the [`openat`] function that allows the caller + /// to control how path resolution happens. + /// + /// # See also + /// + /// [openat2](https://man7.org/linux/man-pages/man2/openat2.2.html) + pub fn openat2( + dirfd: RawFd, + path: &P, + mut how: OpenHow, + ) -> Result { + let fd = path.with_nix_path(|cstr| unsafe { + libc::syscall( + libc::SYS_openat2, + dirfd, + cstr.as_ptr(), + &mut how as *mut OpenHow, + std::mem::size_of::(), + ) + })?; + + Errno::result(fd as RawFd) + } + } +} + /// Change the name of a file. /// /// The `renameat` function is equivalent to `rename` except in the case where either `old_path` diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index 6572e8af8d..e4ace020f7 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -4,6 +4,10 @@ use nix::errno::*; use nix::fcntl::{open, readlink, OFlag}; #[cfg(not(target_os = "redox"))] use nix::fcntl::{openat, readlinkat, renameat}; + +#[cfg(target_os = "linux")] +use nix::fcntl::{openat2, OpenHow, ResolveFlag}; + #[cfg(all( target_os = "linux", target_env = "gnu", @@ -57,6 +61,64 @@ fn test_openat() { close(dirfd).unwrap(); } +#[test] +#[cfg(target_os = "linux")] +// QEMU does not handle openat well enough to satisfy this test +// https://gitlab.com/qemu-project/qemu/-/issues/829 +#[cfg_attr(qemu, ignore)] +fn test_openat2() { + const CONTENTS: &[u8] = b"abcd"; + let mut tmp = NamedTempFile::new().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let dirfd = + open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty()) + .unwrap(); + + let fd = openat2( + dirfd, + tmp.path().file_name().unwrap(), + OpenHow::new() + .flags(OFlag::O_RDONLY) + .mode(Mode::empty()) + .resolve(ResolveFlag::RESOLVE_BENEATH), + ) + .unwrap(); + + let mut buf = [0u8; 1024]; + assert_eq!(4, read(fd, &mut buf).unwrap()); + assert_eq!(CONTENTS, &buf[0..4]); + + close(fd).unwrap(); + close(dirfd).unwrap(); +} + +#[test] +#[cfg(target_os = "linux")] +// QEMU does not handle openat well enough to satisfy this test +// https://gitlab.com/qemu-project/qemu/-/issues/829 +#[cfg_attr(qemu, ignore)] +fn test_openat2_forbidden() { + let mut tmp = NamedTempFile::new().unwrap(); + tmp.write_all(b"let me out").unwrap(); + + let dirfd = + open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty()) + .unwrap(); + + let escape_attempt = + tmp.path().parent().unwrap().join("../../../hello.txt"); + + let res = openat2( + dirfd, + &escape_attempt, + OpenHow::new() + .flags(OFlag::O_RDONLY) + .resolve(ResolveFlag::RESOLVE_BENEATH), + ); + assert_eq!(Err(Errno::EXDEV), res); +} + #[test] #[cfg(not(target_os = "redox"))] fn test_renameat() { From 5d47e1593577a81844e97954df2db5213947b93d Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 1 Apr 2024 17:02:44 -0600 Subject: [PATCH 22/64] Temporarily disable CI on Haiku (#2351) Issue #2350 --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 110208f080..24714602e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -299,7 +299,9 @@ jobs: # Temporarily disable armv7-unknown-linux-uclibceabihf # https://github.com/nix-rust/nix/issues/2200 #- target: armv7-unknown-linux-uclibceabihf - - target: x86_64-unknown-haiku + # Temporarily disable x86_64-unknown-haiku + # https://github.com/nix-rust/nix/issues/2350 + #- target: x86_64-unknown-haiku # Disable hurd due to # https://github.com/nix-rust/nix/issues/2306 # - target: i686-unknown-hurd-gnu From 1ed24e680af326c669a3c0e5da84d3ebfc2b2229 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Mon, 1 Apr 2024 18:13:08 -0600 Subject: [PATCH 23/64] Add _PC_MIN_HOLE_SIZE for use with pathconf (#2349) --- changelog/2349.added.md | 1 + src/unistd.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 changelog/2349.added.md diff --git a/changelog/2349.added.md b/changelog/2349.added.md new file mode 100644 index 0000000000..277ded2c98 --- /dev/null +++ b/changelog/2349.added.md @@ -0,0 +1 @@ +Added `_PC_MIN_HOLE_SIZE` for `pathconf` and `fpathconf`. diff --git a/src/unistd.rs b/src/unistd.rs index 4502766c5d..c73786a6f5 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2031,6 +2031,18 @@ pub enum PathconfVar { /// queue; therefore, the maximum number of bytes a conforming application /// may require to be typed as input before reading them. MAX_INPUT = libc::_PC_MAX_INPUT, + #[cfg(any( + solarish, + freebsdlike, + target_os = "netbsd", + ))] + /// If a file system supports the reporting of holes (see lseek(2)), + /// pathconf() and fpathconf() return a positive number that represents the + /// minimum hole size returned in bytes. The offsets of holes returned will + /// be aligned to this same value. A special value of 1 is returned if the + /// file system does not specify the minimum hole size but still reports + /// holes. + MIN_HOLE_SIZE = libc::_PC_MIN_HOLE_SIZE, /// Maximum number of bytes in a filename (not including the terminating /// null of a filename string). NAME_MAX = libc::_PC_NAME_MAX, From bc374af3b978fa485dad04bcbae3c27b40ca6359 Mon Sep 17 00:00:00 2001 From: Jun Ouyang Date: Tue, 2 Apr 2024 15:05:55 +0800 Subject: [PATCH 24/64] feat: introduce macos mount API support (#2347) * feat: introduce macos mount API support * feat: add doc comment * feat: fix code * feat: fix code * feat: fix code * feat: fix code * feat: fix code * feat: fix code * feat: fix code * feat: fix code * feat: fix code * feat: fix code --- build.rs | 1 + changelog/2347.added.md | 1 + src/mount/apple.rs | 122 +++++++++++++++++++++ src/mount/{bsd.rs => bsd_without_apple.rs} | 5 +- src/mount/mod.rs | 14 ++- test/mount/mod.rs | 6 + test/{ => mount}/test_mount.rs | 0 test/mount/test_mount_apple.rs | 8 ++ test/{ => mount}/test_nmount.rs | 0 test/test.rs | 5 +- 10 files changed, 151 insertions(+), 11 deletions(-) create mode 100644 changelog/2347.added.md create mode 100644 src/mount/apple.rs rename src/mount/{bsd.rs => bsd_without_apple.rs} (98%) create mode 100644 test/mount/mod.rs rename test/{ => mount}/test_mount.rs (100%) create mode 100644 test/mount/test_mount_apple.rs rename test/{ => mount}/test_nmount.rs (100%) diff --git a/build.rs b/build.rs index ecadc04f5d..fd19de0fe9 100644 --- a/build.rs +++ b/build.rs @@ -19,6 +19,7 @@ fn main() { // cfg aliases we would like to use apple_targets: { any(ios, macos, watchos, tvos) }, bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) }, + bsd_without_apple: { any(freebsd, dragonfly, netbsd, openbsd) }, linux_android: { any(android, linux) }, freebsdlike: { any(dragonfly, freebsd) }, netbsdlike: { any(netbsd, openbsd) }, diff --git a/changelog/2347.added.md b/changelog/2347.added.md new file mode 100644 index 0000000000..9000d61deb --- /dev/null +++ b/changelog/2347.added.md @@ -0,0 +1 @@ +Add `mount` and `unmount` API for apple targets. diff --git a/src/mount/apple.rs b/src/mount/apple.rs new file mode 100644 index 0000000000..6759ed20bb --- /dev/null +++ b/src/mount/apple.rs @@ -0,0 +1,122 @@ +use crate::{Errno, NixPath, Result}; +use libc::c_int; + +libc_bitflags!( + /// Used with [`mount()`] and [`unmount()`]. + pub struct MntFlags: c_int { + /// Do not interpret special files on the filesystem. + MNT_NODEV; + /// Enable data protection on the filesystem if the filesystem is configured for it. + MNT_CPROTECT; + /// file system is quarantined + MNT_QUARANTINE; + /// filesystem is stored locally + MNT_LOCAL; + /// quotas are enabled on filesystem + MNT_QUOTA; + /// identifies the root filesystem + MNT_ROOTFS; + /// file system is not appropriate path to user data + MNT_DONTBROWSE; + /// VFS will ignore ownership information on filesystem objects + MNT_IGNORE_OWNERSHIP; + /// filesystem was mounted by automounter + MNT_AUTOMOUNTED; + /// filesystem is journaled + MNT_JOURNALED; + /// Don't allow user extended attributes + MNT_NOUSERXATTR; + /// filesystem should defer writes + MNT_DEFWRITE; + /// don't block unmount if not responding + MNT_NOBLOCK; + /// file system is exported + MNT_EXPORTED; + /// file system written asynchronously + MNT_ASYNC; + /// Force a read-write mount even if the file system appears to be + /// unclean. + MNT_FORCE; + /// MAC support for objects. + MNT_MULTILABEL; + /// Do not update access times. + MNT_NOATIME; + /// Disallow program execution. + MNT_NOEXEC; + /// Do not honor setuid or setgid bits on files when executing them. + MNT_NOSUID; + /// Mount read-only. + MNT_RDONLY; + /// Causes the vfs subsystem to update its data structures pertaining to + /// the specified already mounted file system. + MNT_RELOAD; + /// Create a snapshot of the file system. + MNT_SNAPSHOT; + /// All I/O to the file system should be done synchronously. + MNT_SYNCHRONOUS; + /// Union with underlying fs. + MNT_UNION; + /// Indicates that the mount command is being applied to an already + /// mounted file system. + MNT_UPDATE; + } +); + +/// Mount a file system. +/// +/// # Arguments +/// - `source` - Specifies the file system. e.g. `/dev/sd0`. +/// - `target` - Specifies the destination. e.g. `/mnt`. +/// - `flags` - Optional flags controlling the mount. +/// - `data` - Optional file system specific data. +/// +/// # see also +/// [`mount`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mount.2.html) +pub fn mount< + P1: ?Sized + NixPath, + P2: ?Sized + NixPath, + P3: ?Sized + NixPath, +>( + source: &P1, + target: &P2, + flags: MntFlags, + data: Option<&P3>, +) -> Result<()> { + fn with_opt_nix_path(p: Option<&P>, f: F) -> Result + where + P: ?Sized + NixPath, + F: FnOnce(*const libc::c_char) -> T, + { + match p { + Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())), + None => Ok(f(std::ptr::null())), + } + } + + let res = source.with_nix_path(|s| { + target.with_nix_path(|t| { + with_opt_nix_path(data, |d| unsafe { + libc::mount( + s.as_ptr(), + t.as_ptr(), + flags.bits(), + d.cast_mut().cast(), + ) + }) + }) + })???; + + Errno::result(res).map(drop) +} + +/// Umount the file system mounted at `target`. +pub fn unmount

(target: &P, flags: MntFlags) -> Result<()> +where + P: ?Sized + NixPath, +{ + let res = target.with_nix_path(|cstr| unsafe { + libc::unmount(cstr.as_ptr(), flags.bits()) + })?; + + Errno::result(res).map(drop) +} diff --git a/src/mount/bsd.rs b/src/mount/bsd_without_apple.rs similarity index 98% rename from src/mount/bsd.rs rename to src/mount/bsd_without_apple.rs index 248e0ab1d2..ae9eed7c0e 100644 --- a/src/mount/bsd.rs +++ b/src/mount/bsd_without_apple.rs @@ -30,7 +30,7 @@ libc_bitflags!( #[cfg(target_os = "freebsd")] MNT_GJOURNAL; /// MAC support for objects. - #[cfg(any(apple_targets, target_os = "freebsd"))] + #[cfg(target_os = "freebsd")] MNT_MULTILABEL; /// Disable read clustering. #[cfg(freebsdlike)] @@ -58,7 +58,7 @@ libc_bitflags!( /// Create a snapshot of the file system. /// /// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs) - #[cfg(any(apple_targets, target_os = "freebsd"))] + #[cfg(target_os = "freebsd")] MNT_SNAPSHOT; /// Using soft updates. #[cfg(any(freebsdlike, netbsdlike))] @@ -71,7 +71,6 @@ libc_bitflags!( MNT_SYNCHRONOUS; /// Union with underlying fs. #[cfg(any( - apple_targets, target_os = "freebsd", target_os = "netbsd" ))] diff --git a/src/mount/mod.rs b/src/mount/mod.rs index 8caf27f7df..41e7b3ec6d 100644 --- a/src/mount/mod.rs +++ b/src/mount/mod.rs @@ -5,8 +5,14 @@ mod linux; #[cfg(linux_android)] pub use self::linux::*; -#[cfg(bsd)] -mod bsd; +#[cfg(bsd_without_apple)] +mod bsd_without_apple; -#[cfg(bsd)] -pub use self::bsd::*; +#[cfg(bsd_without_apple)] +pub use self::bsd_without_apple::*; + +#[cfg(apple_targets)] +mod apple; + +#[cfg(apple_targets)] +pub use self::apple::*; diff --git a/test/mount/mod.rs b/test/mount/mod.rs new file mode 100644 index 0000000000..2764b83f71 --- /dev/null +++ b/test/mount/mod.rs @@ -0,0 +1,6 @@ +#[cfg(target_os = "linux")] +mod test_mount; +#[cfg(apple_targets)] +mod test_mount_apple; +#[cfg(target_os = "freebsd")] +mod test_nmount; diff --git a/test/test_mount.rs b/test/mount/test_mount.rs similarity index 100% rename from test/test_mount.rs rename to test/mount/test_mount.rs diff --git a/test/mount/test_mount_apple.rs b/test/mount/test_mount_apple.rs new file mode 100644 index 0000000000..f2868500d0 --- /dev/null +++ b/test/mount/test_mount_apple.rs @@ -0,0 +1,8 @@ +use nix::errno::Errno; +use nix::mount::{mount, MntFlags}; + +#[test] +fn test_mount() { + let res = mount::("", "", MntFlags::empty(), None); + assert_eq!(res, Err(Errno::ENOENT)); +} diff --git a/test/test_nmount.rs b/test/mount/test_nmount.rs similarity index 100% rename from test/test_nmount.rs rename to test/mount/test_nmount.rs diff --git a/test/test.rs b/test/test.rs index c7231426c2..f32a85a3b7 100644 --- a/test/test.rs +++ b/test/test.rs @@ -4,6 +4,7 @@ extern crate cfg_if; extern crate nix; mod common; +mod mount; mod sys; #[cfg(not(target_os = "redox"))] mod test_dir; @@ -11,8 +12,6 @@ mod test_errno; mod test_fcntl; #[cfg(linux_android)] mod test_kmod; -#[cfg(target_os = "linux")] -mod test_mount; #[cfg(any( freebsdlike, target_os = "fushsia", @@ -23,8 +22,6 @@ mod test_mq; #[cfg(not(target_os = "redox"))] mod test_net; mod test_nix_path; -#[cfg(target_os = "freebsd")] -mod test_nmount; mod test_poll; #[cfg(not(any( target_os = "redox", From 05178937b0570962ff999441c50685817b5e8273 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Wed, 3 Apr 2024 10:24:04 +0800 Subject: [PATCH 25/64] refactor: make with_opt_nix_path() a util (#2353) * refactor: make with_opt_nix_path() a util * refactor: feature/target gate it * refactor: add Android --- src/lib.rs | 18 ++++++++++++++++++ src/mount/apple.rs | 13 +------------ src/mount/linux.rs | 17 +++-------------- src/sys/fanotify.rs | 13 +------------ 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dffac29b54..c4c0fa53cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -362,3 +362,21 @@ impl NixPath for PathBuf { self.as_os_str().with_nix_path(f) } } + +/// Like `NixPath::with_nix_path()`, but allow the `path` argument to be optional. +/// +/// A NULL pointer will be provided if `path.is_none()`. +#[cfg(any( + all(apple_targets, feature = "mount"), + all(linux_android, any(feature = "mount", feature = "fanotify")) +))] +pub(crate) fn with_opt_nix_path(path: Option<&P>, f: F) -> Result +where + P: ?Sized + NixPath, + F: FnOnce(*const libc::c_char) -> T, +{ + match path { + Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())), + None => Ok(f(ptr::null())), + } +} diff --git a/src/mount/apple.rs b/src/mount/apple.rs index 6759ed20bb..ce0ab1e9ca 100644 --- a/src/mount/apple.rs +++ b/src/mount/apple.rs @@ -82,20 +82,9 @@ pub fn mount< flags: MntFlags, data: Option<&P3>, ) -> Result<()> { - fn with_opt_nix_path(p: Option<&P>, f: F) -> Result - where - P: ?Sized + NixPath, - F: FnOnce(*const libc::c_char) -> T, - { - match p { - Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())), - None => Ok(f(std::ptr::null())), - } - } - let res = source.with_nix_path(|s| { target.with_nix_path(|t| { - with_opt_nix_path(data, |d| unsafe { + crate::with_opt_nix_path(data, |d| unsafe { libc::mount( s.as_ptr(), t.as_ptr(), diff --git a/src/mount/linux.rs b/src/mount/linux.rs index aa166bc9d3..3c27150761 100644 --- a/src/mount/linux.rs +++ b/src/mount/linux.rs @@ -113,21 +113,10 @@ pub fn mount< flags: MsFlags, data: Option<&P4>, ) -> Result<()> { - fn with_opt_nix_path(p: Option<&P>, f: F) -> Result - where - P: ?Sized + NixPath, - F: FnOnce(*const libc::c_char) -> T, - { - match p { - Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())), - None => Ok(f(std::ptr::null())), - } - } - - let res = with_opt_nix_path(source, |s| { + let res = crate::with_opt_nix_path(source, |s| { target.with_nix_path(|t| { - with_opt_nix_path(fstype, |ty| { - with_opt_nix_path(data, |d| unsafe { + crate::with_opt_nix_path(fstype, |ty| { + crate::with_opt_nix_path(data, |d| unsafe { libc::mount( s, t.as_ptr(), diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index e217406e02..9ff306017d 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -313,18 +313,7 @@ impl Fanotify { dirfd: Option, path: Option<&P>, ) -> Result<()> { - fn with_opt_nix_path(p: Option<&P>, f: F) -> Result - where - P: ?Sized + NixPath, - F: FnOnce(*const libc::c_char) -> T, - { - match p { - Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())), - None => Ok(f(std::ptr::null())), - } - } - - let res = with_opt_nix_path(path, |p| unsafe { + let res = crate::with_opt_nix_path(path, |p| unsafe { libc::fanotify_mark( self.fd.as_raw_fd(), flags.bits(), From 391a3647926e5aa9a199cc0af591ca1cf8d5544f Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 9 Apr 2024 06:01:44 +0000 Subject: [PATCH 26/64] feat: impl AsFd for PtyMaster (#2355) Fixes #2354 --- changelog/2355.added.md | 1 + src/pty.rs | 6 ++++++ test/test_pty.rs | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog/2355.added.md diff --git a/changelog/2355.added.md b/changelog/2355.added.md new file mode 100644 index 0000000000..663590262c --- /dev/null +++ b/changelog/2355.added.md @@ -0,0 +1 @@ +Added `impl AsFd for pty::PtyMaster` diff --git a/src/pty.rs b/src/pty.rs index 818b25b1b1..171bbfa138 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -58,6 +58,12 @@ impl AsRawFd for PtyMaster { } } +impl AsFd for PtyMaster { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + impl IntoRawFd for PtyMaster { fn into_raw_fd(self) -> RawFd { let fd = self.0; diff --git a/test/test_pty.rs b/test/test_pty.rs index dda8b3f6ce..bc618e0bd8 100644 --- a/test/test_pty.rs +++ b/test/test_pty.rs @@ -17,9 +17,10 @@ use nix::unistd::{pause, write}; fn test_ptsname_equivalence() { let _m = crate::PTSNAME_MTX.lock(); - // Open a new PTTY master + // Open a new PTY master let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); assert!(master_fd.as_raw_fd() > 0); + assert!(master_fd.as_fd().as_raw_fd() == master_fd.as_raw_fd()); // Get the name of the slave let slave_name = unsafe { ptsname(&master_fd) }.unwrap(); From 2b6c3deb8a93899b38120d24818b38b9af194e68 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Wed, 10 Apr 2024 20:18:21 +0800 Subject: [PATCH 27/64] docs: document Errno::result() in CONVENTIONS.md (#2358) --- CONVENTIONS.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 5615da4b33..036762e4ee 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -135,3 +135,20 @@ If you want to add a test for a feature that is in `xxx.rs`, then the test shoul be put in the corresponding `test_xxx.rs` file unless you cannot do this, e.g., the test involves private stuff and thus cannot be added outside of Nix, then it is allowed to leave the test in `xxx.rs`. + +## Syscall/libc function error handling + +Most syscall and libc functions return an [`ErrnoSentinel`][trait] value on error, +we has a nice utility function [`Errno::result()`][util] to convert it to the +Rusty `Result` type, e.g., here is how `dup(2)` uses it: + +```rs +pub fn dup(oldfd: RawFd) -> Result { + let res = unsafe { libc::dup(oldfd) }; + + Errno::result(res) +} +``` + +[trait]: https://docs.rs/nix/latest/nix/errno/trait.ErrnoSentinel.html +[util]: https://docs.rs/nix/latest/nix/errno/enum.Errno.html#method.result From a7db481a2f5e43cd5c8f2c5a3d35118ebcfa77da Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Wed, 10 Apr 2024 21:19:42 +0800 Subject: [PATCH 28/64] ci: bring DragonFlyBSD and Haiku CI back (#2359) --- .github/workflows/ci.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24714602e2..5a3a02c707 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -292,16 +292,12 @@ jobs: fail-fast: false matrix: include: - # Temporarily disable DragonFlyBSD - # https://github.com/nix-rust/nix/issues/2337 - # - target: x86_64-unknown-dragonfly + - target: x86_64-unknown-dragonfly - target: x86_64-unknown-openbsd # Temporarily disable armv7-unknown-linux-uclibceabihf # https://github.com/nix-rust/nix/issues/2200 #- target: armv7-unknown-linux-uclibceabihf - # Temporarily disable x86_64-unknown-haiku - # https://github.com/nix-rust/nix/issues/2350 - #- target: x86_64-unknown-haiku + - target: x86_64-unknown-haiku # Disable hurd due to # https://github.com/nix-rust/nix/issues/2306 # - target: i686-unknown-hurd-gnu From 2bc9717de37ce06ee690f2f7b06c6aaf689689b6 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 13 Apr 2024 13:11:53 +0800 Subject: [PATCH 29/64] ci: check newly-added CHANGELOGs in CI (#2364) --- .../actions/check_new_changelog/action.yml | 42 +++++++++++++++++++ .github/workflows/ci.yml | 8 ++++ 2 files changed, 50 insertions(+) create mode 100644 .github/actions/check_new_changelog/action.yml diff --git a/.github/actions/check_new_changelog/action.yml b/.github/actions/check_new_changelog/action.yml new file mode 100644 index 0000000000..a145021468 --- /dev/null +++ b/.github/actions/check_new_changelog/action.yml @@ -0,0 +1,42 @@ +name: 'Check new CHANGELOG' +description: 'Check new CHANGELOG kind and PR number' + +runs: + using: "composite" + steps: + - name: Get newly added CHANGELOGs + id: new-changelogs + uses: tj-actions/changed-files@v44 + with: + # Only checek the files under the `changelog` directory + files: changelog/** + + - name: Check them + shell: bash + if: steps.new-changelogs.outputs.added_files_count != 0 + env: + NEW_CHANGELOGS: ${{ steps.new-changelogs.outputs.added_files }} + PR_NUMBER: ${{ github.event.number }} + run: | + for cl in ${NEW_CHANGELOGS}; do + # parse it + IFS='.' read id kind file_extension <<< "${cl}" + + # Check the kind field + if [ "$kind" != "added" ] && [ "$kind" != "changed" ] && [ "$kind" != "fixed" ] && [ "$kind" != "removed" ]; then + echo "Invalid CHANGELOG kind [${kind}] from [${cl}], available options are [added, changed, fixed, removed]"; + exit 1; + fi + + # Check the file extension + if [ "$file_extension" != "md" ]; then + echo "Invalid file extension [${file_extension}] from [${cl}], it should be [md]"; + exit 1; + fi + + # Check PR number + if [ "$id" != "$PR_NUMBER" ]; then + echo "Mismatched PR number [${id}] from [${cl}], it should be ${PR_NUMBER}"; + exit 1; + fi + done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a3a02c707..3200fecf22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,14 @@ env: MSRV: 1.69.0 jobs: + check_new_changelog: + runs-on: ubuntu-20.04 + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: check new CHANGELOG + uses: ./.github/actions/check_new_changelog macos: runs-on: macos-13 From c179d7b475df04f9f3f9b4e09e4f43ec3827a9b5 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 13 Apr 2024 17:23:36 +0800 Subject: [PATCH 30/64] style: revert #2344 as the issue has been fixed (#2365) --- src/sys/statfs.rs | 2 -- src/sys/time.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs index 1150a0f9dc..b2315f4ceb 100644 --- a/src/sys/statfs.rs +++ b/src/sys/statfs.rs @@ -71,8 +71,6 @@ type fs_type_t = libc::c_int; type fs_type_t = libc::__fsword_t; /// Describes the file system type as known by the operating system. -// false positive, see: https://github.com/rust-lang/rust-clippy/issues/12537 -#[allow(clippy::duplicated_attributes)] #[cfg(any( target_os = "freebsd", target_os = "android", diff --git a/src/sys/time.rs b/src/sys/time.rs index 0472fced34..af436cabd5 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -12,8 +12,6 @@ const fn zero_init_timespec() -> timespec { unsafe { std::mem::transmute([0u8; std::mem::size_of::()]) } } -// false positive, see: https://github.com/rust-lang/rust-clippy/issues/12537 -#[allow(clippy::duplicated_attributes)] #[cfg(any( all(feature = "time", any(target_os = "android", target_os = "linux")), all( From 95d1285a091f57e6bcd6a3ca94c712934d12094c Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 13 Apr 2024 19:22:46 +0800 Subject: [PATCH 31/64] ci: fix changelog checker by trimming the 'changelog/' prefix (#2368) --- .github/actions/check_new_changelog/action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/check_new_changelog/action.yml b/.github/actions/check_new_changelog/action.yml index a145021468..c366654aae 100644 --- a/.github/actions/check_new_changelog/action.yml +++ b/.github/actions/check_new_changelog/action.yml @@ -18,7 +18,11 @@ runs: NEW_CHANGELOGS: ${{ steps.new-changelogs.outputs.added_files }} PR_NUMBER: ${{ github.event.number }} run: | + # `cl` will be something like "changelog/1.added.md" for cl in ${NEW_CHANGELOGS}; do + # Trim the directory name + prefix="changelog/"; trimmed_cl=${cl/#$prefix}; cl="${trimmed_cl}"; + # parse it IFS='.' read id kind file_extension <<< "${cl}" From bae5fdb5865ae3a413ed5fb9af1d8e9a63df8c1f Mon Sep 17 00:00:00 2001 From: chloekek <50083900+chloekek@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:41:19 +0200 Subject: [PATCH 32/64] Allow use of `SignalFd` through shared reference (#2367) Like with many other file descriptors, concurrent use of signalfds is safe. Changing the signal mask of and reading signals from a signalfd can now be done with the `SignalFd` API even if other references to it exist. Fixes #2366. --- changelog/2367.changed.md | 5 +++++ src/sys/signalfd.rs | 4 ++-- test/sys/test_signalfd.rs | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 changelog/2367.changed.md diff --git a/changelog/2367.changed.md b/changelog/2367.changed.md new file mode 100644 index 0000000000..b467681600 --- /dev/null +++ b/changelog/2367.changed.md @@ -0,0 +1,5 @@ +Allow use of `SignalFd` through shared reference + +Like with many other file descriptors, concurrent use of signalfds is safe. +Changing the signal mask of and reading signals from a signalfd can now be done +with the `SignalFd` API even if other references to it exist. diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs index ccba774d1a..4594f4deaa 100644 --- a/src/sys/signalfd.rs +++ b/src/sys/signalfd.rs @@ -105,11 +105,11 @@ impl SignalFd { Ok(SignalFd(fd)) } - pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { + pub fn set_mask(&self, mask: &SigSet) -> Result<()> { self.update(mask, SfdFlags::empty()) } - pub fn read_signal(&mut self) -> Result> { + pub fn read_signal(&self) -> Result> { let mut buffer = mem::MaybeUninit::::uninit(); let size = mem::size_of_val(&buffer); diff --git a/test/sys/test_signalfd.rs b/test/sys/test_signalfd.rs index 4e0971aba7..d315848453 100644 --- a/test/sys/test_signalfd.rs +++ b/test/sys/test_signalfd.rs @@ -28,7 +28,7 @@ fn read_empty_signalfd() { }; let mask = SigSet::empty(); - let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); + let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); let res = fd.read_signal(); assert!(res.unwrap().is_none()); @@ -47,7 +47,7 @@ fn test_signalfd() { mask.add(signal::SIGUSR1); mask.thread_block().unwrap(); - let mut fd = SignalFd::new(&mask).unwrap(); + let fd = SignalFd::new(&mask).unwrap(); // Send a SIGUSR1 signal to the current process. Note that this uses `raise` instead of `kill` // because `kill` with `getpid` isn't correct during multi-threaded execution like during a @@ -72,7 +72,7 @@ fn test_signalfd_setmask() { // Block the SIGUSR1 signal from automatic processing for this thread let mut mask = SigSet::empty(); - let mut fd = SignalFd::new(&mask).unwrap(); + let fd = SignalFd::new(&mask).unwrap(); mask.add(signal::SIGUSR1); mask.thread_block().unwrap(); From b8033c2e62eed13a14af1b6a4572957ec7bead60 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 13 Apr 2024 19:54:30 +0800 Subject: [PATCH 33/64] ci: only run changelog checker in PR (#2370) --- .github/workflows/check_new_changelog.yml | 18 ++++++++++++++++++ .github/workflows/ci.yml | 9 --------- 2 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/check_new_changelog.yml diff --git a/.github/workflows/check_new_changelog.yml b/.github/workflows/check_new_changelog.yml new file mode 100644 index 0000000000..b6ef26ab1c --- /dev/null +++ b/.github/workflows/check_new_changelog.yml @@ -0,0 +1,18 @@ +name: Check new CHANGELOGs + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + +jobs: + check_new_changelog: + runs-on: ubuntu-20.04 + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: check new CHANGELOG + uses: ./.github/actions/check_new_changelog diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3200fecf22..8a56e7d2b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,15 +16,6 @@ env: MSRV: 1.69.0 jobs: - check_new_changelog: - runs-on: ubuntu-20.04 - steps: - - name: checkout - uses: actions/checkout@v4 - - - name: check new CHANGELOG - uses: ./.github/actions/check_new_changelog - macos: runs-on: macos-13 env: From b6c55ada062898224aa1a7d942388788d27295fc Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 13 Apr 2024 20:55:40 +0800 Subject: [PATCH 34/64] test: skip test_utun_ifname if unpriviledged (#2371) --- test/sys/test_sockopt.rs | 2 ++ test/test.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index 427edf24e0..1da3f6af38 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -832,6 +832,8 @@ fn test_ktls() { #[test] #[cfg(apple_targets)] fn test_utun_ifname() { + skip_if_not_root!("test_utun_ifname"); + use nix::sys::socket::connect; use nix::sys::socket::SysControlAddr; diff --git a/test/test.rs b/test/test.rs index f32a85a3b7..7153d82aed 100644 --- a/test/test.rs +++ b/test/test.rs @@ -3,6 +3,7 @@ extern crate cfg_if; #[cfg_attr(not(any(target_os = "redox", target_os = "haiku")), macro_use)] extern crate nix; +#[macro_use] mod common; mod mount; mod sys; From ba66e137f90f98e96e25fed21b386b84b7fbed3a Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 14 Apr 2024 08:40:19 +0800 Subject: [PATCH 35/64] test: use parking_lot::Mutex for FORK_MTX (#2372) --- test/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.rs b/test/test.rs index 7153d82aed..53a6af4b6c 100644 --- a/test/test.rs +++ b/test/test.rs @@ -59,7 +59,7 @@ fn read_exact(f: Fd, buf: &mut [u8]) { /// Any test that creates child processes must grab this mutex, regardless /// of what it does with those children. -pub static FORK_MTX: std::sync::Mutex<()> = std::sync::Mutex::new(()); +pub static FORK_MTX: Mutex<()> = Mutex::new(()); /// Any test that changes the process's current working directory must grab /// the RwLock exclusively. Any process that cares about the current /// working directory must grab it shared. From 1c2cad8bdff511133e87c2feb4a193bc351a46f5 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Sun, 14 Apr 2024 08:46:35 +0800 Subject: [PATCH 36/64] Add implementation of `PTRACE_{GET,SET}REGSET` (#2044) * Add implementation of `PTRACE_{GET,SET}REGSET` Also added `PTRACE_{GET,SET}REGS` for most platforms other than x86 using aforementioned implementation. * test: remove unused import on aarch64 and riscv64 * chore: changelog --------- Co-authored-by: Steve Lau --- changelog/2044.added.md | 2 + src/sys/ptrace/linux.rs | 135 +++++++++++++++++++++++++++++++++++++++- test/sys/test_ptrace.rs | 97 +++++++++++++++++++++++++++-- 3 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 changelog/2044.added.md diff --git a/changelog/2044.added.md b/changelog/2044.added.md new file mode 100644 index 0000000000..95f79a755d --- /dev/null +++ b/changelog/2044.added.md @@ -0,0 +1,2 @@ +Add `getregset()/setregset()` for Linux/glibc/x86/x86_64/aarch64/riscv64 and +`getregs()/setregs()` for Linux/glibc/aarch64/riscv64 diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index de3124de95..c36bf05197 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -17,8 +17,10 @@ pub type AddressType = *mut ::libc::c_void; target_arch = "x86_64", any(target_env = "gnu", target_env = "musl") ), - all(target_arch = "x86", target_env = "gnu") - ) + all(target_arch = "x86", target_env = "gnu"), + all(target_arch = "aarch64", target_env = "gnu"), + all(target_arch = "riscv64", target_env = "gnu"), + ), ))] use libc::user_regs_struct; @@ -170,6 +172,29 @@ libc_enum! { } } +libc_enum! { + #[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) + ))] + #[repr(i32)] + /// Defining a specific register set, as used in [`getregset`] and [`setregset`]. + #[non_exhaustive] + pub enum RegisterSet { + NT_PRSTATUS, + NT_PRFPREG, + NT_PRPSINFO, + NT_TASKSTRUCT, + NT_AUXV, + } +} + libc_bitflags! { /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request. /// See `man ptrace` for more details. @@ -217,6 +242,12 @@ fn ptrace_peek( } /// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)` +/// +/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]), +/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect +/// on aarch64 and riscv64. +/// +/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html #[cfg(all( target_os = "linux", any( @@ -231,7 +262,58 @@ pub fn getregs(pid: Pid) -> Result { ptrace_get_data::(Request::PTRACE_GETREGS, pid) } +/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)` +/// +/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]), +/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect +/// on aarch64 and riscv64. +/// +/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any(target_arch = "aarch64", target_arch = "riscv64") +))] +pub fn getregs(pid: Pid) -> Result { + getregset(pid, RegisterSet::NT_PRSTATUS) +} + +/// Get a particular set of user registers, as with `ptrace(PTRACE_GETREGSET, ...)` +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) +))] +pub fn getregset(pid: Pid, set: RegisterSet) -> Result { + let request = Request::PTRACE_GETREGSET; + let mut data = mem::MaybeUninit::::uninit(); + let mut iov = libc::iovec { + iov_base: data.as_mut_ptr().cast(), + iov_len: mem::size_of::(), + }; + unsafe { + ptrace_other( + request, + pid, + set as i32 as AddressType, + (&mut iov as *mut libc::iovec).cast(), + )?; + }; + Ok(unsafe { data.assume_init() }) +} + /// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)` +/// +/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]), +/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect +/// on aarch64 and riscv64. +/// +/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html #[cfg(all( target_os = "linux", any( @@ -248,12 +330,59 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { Request::PTRACE_SETREGS as RequestType, libc::pid_t::from(pid), ptr::null_mut::(), - ®s as *const _ as *const c_void, + ®s as *const user_regs_struct as *const c_void, ) }; Errno::result(res).map(drop) } +/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)` +/// +/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]), +/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect +/// on aarch64 and riscv64. +/// +/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any(target_arch = "aarch64", target_arch = "riscv64") +))] +pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { + setregset(pid, RegisterSet::NT_PRSTATUS, regs) +} + +/// Set a particular set of user registers, as with `ptrace(PTRACE_SETREGSET, ...)` +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) +))] +pub fn setregset( + pid: Pid, + set: RegisterSet, + mut regs: user_regs_struct, +) -> Result<()> { + let mut iov = libc::iovec { + iov_base: (&mut regs as *mut user_regs_struct).cast(), + iov_len: mem::size_of::(), + }; + unsafe { + ptrace_other( + Request::PTRACE_SETREGSET, + pid, + set as i32 as AddressType, + (&mut iov as *mut libc::iovec).cast(), + )?; + } + Ok(()) +} + /// Function for ptrace requests that return values from the data field. /// Some ptrace get requests populate structs or larger elements than `c_long` /// and therefore use the data field to return values. This function handles these diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 246b35445d..5eb7e249f3 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -1,7 +1,7 @@ #[cfg(all( target_os = "linux", - any(target_arch = "x86_64", target_arch = "x86"), - target_env = "gnu" + target_env = "gnu", + any(target_arch = "x86_64", target_arch = "x86") ))] use memoffset::offset_of; use nix::errno::Errno; @@ -179,8 +179,13 @@ fn test_ptrace_interrupt() { // ptrace::{setoptions, getregs} are only available in these platforms #[cfg(all( target_os = "linux", - any(target_arch = "x86_64", target_arch = "x86"), - target_env = "gnu" + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) ))] #[test] fn test_ptrace_syscall() { @@ -226,12 +231,21 @@ fn test_ptrace_syscall() { let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as libc::c_long; + #[cfg(target_arch = "aarch64")] + let get_syscall_id = + || ptrace::getregs(child).unwrap().regs[8] as libc::c_long; + + #[cfg(target_arch = "riscv64")] + let get_syscall_id = + || ptrace::getregs(child).unwrap().a7 as libc::c_long; + // this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`. #[cfg(target_arch = "x86_64")] let rax_offset = offset_of!(libc::user_regs_struct, orig_rax); #[cfg(target_arch = "x86")] let rax_offset = offset_of!(libc::user_regs_struct, orig_eax); + #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] let get_syscall_from_user_area = || { // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86) let rax_offset = offset_of!(libc::user, regs) + rax_offset; @@ -246,6 +260,7 @@ fn test_ptrace_syscall() { Ok(WaitStatus::PtraceSyscall(child)) ); assert_eq!(get_syscall_id(), ::libc::SYS_kill); + #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill); // kill exit @@ -255,6 +270,7 @@ fn test_ptrace_syscall() { Ok(WaitStatus::PtraceSyscall(child)) ); assert_eq!(get_syscall_id(), ::libc::SYS_kill); + #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill); // receive signal @@ -273,3 +289,76 @@ fn test_ptrace_syscall() { } } } + +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) +))] +#[test] +fn test_ptrace_regsets() { + use nix::sys::ptrace::{self, getregset, setregset, RegisterSet}; + use nix::sys::signal::*; + use nix::sys::wait::{waitpid, WaitStatus}; + use nix::unistd::fork; + use nix::unistd::ForkResult::*; + + require_capability!("test_ptrace_regsets", CAP_SYS_PTRACE); + + let _m = crate::FORK_MTX.lock(); + + match unsafe { fork() }.expect("Error: Fork Failed") { + Child => { + ptrace::traceme().unwrap(); + // As recommended by ptrace(2), raise SIGTRAP to pause the child + // until the parent is ready to continue + loop { + raise(Signal::SIGTRAP).unwrap(); + } + } + + Parent { child } => { + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)) + ); + let mut regstruct = + getregset(child, RegisterSet::NT_PRSTATUS).unwrap(); + + #[cfg(target_arch = "x86_64")] + let reg = &mut regstruct.r15; + #[cfg(target_arch = "x86")] + let reg = &mut regstruct.edx; + #[cfg(target_arch = "aarch64")] + let reg = &mut regstruct.regs[16]; + #[cfg(target_arch = "riscv64")] + let reg = &mut regstruct.regs[16]; + + *reg = 0xdeadbeefu32 as _; + let _ = setregset(child, RegisterSet::NT_PRSTATUS, regstruct); + regstruct = getregset(child, RegisterSet::NT_PRSTATUS).unwrap(); + + #[cfg(target_arch = "x86_64")] + let reg = regstruct.r15; + #[cfg(target_arch = "x86")] + let reg = regstruct.edx; + #[cfg(target_arch = "aarch64")] + let reg = regstruct.regs[16]; + #[cfg(target_arch = "riscv64")] + let reg = regstruct.regs[16]; + assert_eq!(reg, 0xdeadbeefu32 as _); + + ptrace::cont(child, Some(Signal::SIGKILL)).unwrap(); + match waitpid(child, None) { + Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) + if pid == child => {} + _ => panic!("The process should have been killed"), + } + } + } +} From 0a566eb8ef4341cf5714a9c233784c314ef81098 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 21 Apr 2024 10:06:39 +0800 Subject: [PATCH 37/64] feat: sync APIs for more platforms (#2379) --- changelog/2379.added.md | 1 + src/unistd.rs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 changelog/2379.added.md diff --git a/changelog/2379.added.md b/changelog/2379.added.md new file mode 100644 index 0000000000..325abdc8ff --- /dev/null +++ b/changelog/2379.added.md @@ -0,0 +1 @@ +Add `sync(2)` for `apple_targets/solarish/haiku/aix/hurd`, `syncfs(2)` for `hurd` and `fdatasync(2)` for `aix/hurd` diff --git a/src/unistd.rs b/src/unistd.rs index c73786a6f5..8330245143 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -1377,7 +1377,7 @@ pub fn chroot(path: &P) -> Result<()> { /// Commit filesystem caches to disk /// /// See also [sync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html) -#[cfg(any(freebsdlike, linux_android, netbsdlike))] +#[cfg(any(bsd, linux_android, solarish, target_os = "haiku", target_os = "aix", target_os = "hurd"))] pub fn sync() { unsafe { libc::sync() }; } @@ -1386,7 +1386,7 @@ pub fn sync() { /// descriptor `fd` to disk /// /// See also [syncfs(2)](https://man7.org/linux/man-pages/man2/sync.2.html) -#[cfg(linux_android)] +#[cfg(any(linux_android, target_os = "hurd"))] pub fn syncfs(fd: RawFd) -> Result<()> { let res = unsafe { libc::syncfs(fd) }; @@ -1414,6 +1414,8 @@ pub fn fsync(fd: RawFd) -> Result<()> { target_os = "freebsd", target_os = "emscripten", target_os = "fuchsia", + target_os = "aix", + target_os = "hurd", ))] #[inline] pub fn fdatasync(fd: RawFd) -> Result<()> { From 15dcd6b80109c15509772c6da3ebc401fd8543bb Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 21 Apr 2024 08:12:43 +0100 Subject: [PATCH 38/64] sys::prctl: Adding set_vma_anon_name. (#2378) to set a name for an `anonymous` region for Linux/Android. --- changelog/2378.added.md | 1 + src/sys/prctl.rs | 15 ++++++++++++++- test/sys/test_prctl.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 changelog/2378.added.md diff --git a/changelog/2378.added.md b/changelog/2378.added.md new file mode 100644 index 0000000000..21ad0b90f6 --- /dev/null +++ b/changelog/2378.added.md @@ -0,0 +1 @@ +Add prctl function `prctl_set_vma_anon_name` for Linux/Android. diff --git a/src/sys/prctl.rs b/src/sys/prctl.rs index 42324beab2..35b1ce170f 100644 --- a/src/sys/prctl.rs +++ b/src/sys/prctl.rs @@ -9,9 +9,11 @@ use crate::errno::Errno; use crate::sys::signal::Signal; use crate::Result; -use libc::{c_int, c_ulong}; +use libc::{c_int, c_ulong, c_void}; use std::convert::TryFrom; use std::ffi::{CStr, CString}; +use std::num::NonZeroUsize; +use std::ptr::NonNull; libc_enum! { /// The type of hardware memory corruption kill policy for the thread. @@ -213,3 +215,14 @@ pub fn set_thp_disable(flag: bool) -> Result<()> { pub fn get_thp_disable() -> Result { prctl_get_bool(libc::PR_GET_THP_DISABLE) } + +/// Set an identifier (or reset it) to the address memory range. +pub fn set_vma_anon_name(addr: NonNull, length: NonZeroUsize, name: Option<&CStr>) -> Result<()> { + let nameref = match name { + Some(n) => n.as_ptr(), + _ => std::ptr::null() + }; + let res = unsafe { libc::prctl(libc::PR_SET_VMA, libc::PR_SET_VMA_ANON_NAME, addr.as_ptr(), length, nameref) }; + + Errno::result(res).map(drop) +} diff --git a/test/sys/test_prctl.rs b/test/sys/test_prctl.rs index 351213b7ef..b409735af6 100644 --- a/test/sys/test_prctl.rs +++ b/test/sys/test_prctl.rs @@ -122,4 +122,38 @@ mod test_prctl { prctl::set_thp_disable(original).unwrap(); } + + #[test] + fn test_set_vma_anon_name() { + use nix::errno::Errno; + use nix::sys::mman; + use std::num::NonZeroUsize; + + const ONE_K: libc::size_t = 1024; + let sz = NonZeroUsize::new(ONE_K).unwrap(); + let ptr = unsafe { + mman::mmap_anonymous( + None, + sz, + mman::ProtFlags::PROT_READ, + mman::MapFlags::MAP_SHARED, + ) + .unwrap() + }; + let err = prctl::set_vma_anon_name( + ptr, + sz, + Some(CStr::from_bytes_with_nul(b"[,$\0").unwrap()), + ) + .unwrap_err(); + assert_eq!(err, Errno::EINVAL); + // `CONFIG_ANON_VMA_NAME` kernel config might not be set + prctl::set_vma_anon_name( + ptr, + sz, + Some(CStr::from_bytes_with_nul(b"Nix\0").unwrap()), + ) + .unwrap_or_default(); + prctl::set_vma_anon_name(ptr, sz, None).unwrap_or_default(); + } } From 6555ab4b353aad390632f902a3e7e1f0569c9a27 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 22 Apr 2024 00:02:19 +0100 Subject: [PATCH 39/64] unistd: fdatasync support for apple. (#2380) is not present in system headers, however the syscall is actually implemented. --- changelog/2380.added.md | 1 + src/unistd.rs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 changelog/2380.added.md diff --git a/changelog/2380.added.md b/changelog/2380.added.md new file mode 100644 index 0000000000..e9445c8500 --- /dev/null +++ b/changelog/2380.added.md @@ -0,0 +1 @@ +Add fdatasync support for Apple targets. diff --git a/src/unistd.rs b/src/unistd.rs index 8330245143..9e98ba7897 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -1411,6 +1411,7 @@ pub fn fsync(fd: RawFd) -> Result<()> { linux_android, solarish, netbsdlike, + apple_targets, target_os = "freebsd", target_os = "emscripten", target_os = "fuchsia", @@ -1419,7 +1420,18 @@ pub fn fsync(fd: RawFd) -> Result<()> { ))] #[inline] pub fn fdatasync(fd: RawFd) -> Result<()> { - let res = unsafe { libc::fdatasync(fd) }; + cfg_if! { + // apple libc supports fdatasync too, albeit not being present in its headers + // [fdatasync](https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/vfs/vfs_syscalls.c#L7728) + if #[cfg(apple_targets)] { + extern "C" { + fn fdatasync(fd: libc::c_int) -> libc::c_int; + } + } else { + use libc::fdatasync as fdatasync; + } + } + let res = unsafe { fdatasync(fd) }; Errno::result(res).map(drop) } From 7ffbd3cd6be52c835751f33c3011355d0b8abede Mon Sep 17 00:00:00 2001 From: TheJonny Date: Mon, 22 Apr 2024 01:03:30 +0200 Subject: [PATCH 40/64] Add O_SEARCH to all platforms supported by libc crate (#2374) * add solarish and freebsd to O_SEARCH, as it exists in in the libc crate * fcntl/O_SEARCH: add description similiar to the posix manpage * fcntl O_SEARCH oops - fix syntax * fcntl O_SEARCH: add fuchsia * O_SEARCH: changelog * fcntl/OPath: no alias for single platform Co-authored-by: SteveLauC * O_SEARCH: also add aux, wasi, emscripten * O_SEARCH: updated added platforms in changelog --------- Co-authored-by: SteveLauC --- changelog/2374.added.md | 1 + src/fcntl.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelog/2374.added.md diff --git a/changelog/2374.added.md b/changelog/2374.added.md new file mode 100644 index 0000000000..ed0ed25675 --- /dev/null +++ b/changelog/2374.added.md @@ -0,0 +1 @@ +Add `open` flag `O_SEARCH` to AIX, Empscripten, FreeBSD, Fuchsia, solarish, WASI diff --git a/src/fcntl.rs b/src/fcntl.rs index 16af6e6d86..a33b2642d3 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -164,8 +164,9 @@ libc_bitflags!( /// Similar to `O_DSYNC` but applies to `read`s instead. #[cfg(any(target_os = "linux", netbsdlike))] O_RSYNC; - /// Skip search permission checks. - #[cfg(target_os = "netbsd")] + /// Open directory for search only. Skip search permission checks on + /// later `openat()` calls using the obtained file descriptor. + #[cfg(any(target_os = "netbsd", target_os = "freebsd", solarish, target_os = "fuchsia", target_os = "emscripten", target_os = "aix", target_os = "wasi"))] O_SEARCH; /// Open with a shared file lock. #[cfg(any(bsd, target_os = "redox"))] From 313dac0437424dc76e744dacfb83ff57a1e7c843 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 22 Apr 2024 08:44:13 +0800 Subject: [PATCH 41/64] test: grab the SIGNAL_MTX lock in test_sigaction (#2381) --- test/sys/test_signal.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs index bf607497be..7494b71234 100644 --- a/test/sys/test_signal.rs +++ b/test/sys/test_signal.rs @@ -283,6 +283,7 @@ fn test_from_and_into_iterator() { #[test] #[cfg(not(target_os = "redox"))] fn test_sigaction() { + let _m = crate::SIGNAL_MTX.lock(); thread::spawn(|| { extern "C" fn test_sigaction_handler(_: libc::c_int) {} extern "C" fn test_sigaction_action( From f129095ac6e76505cfa6711cc78e2b5f4430070e Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 22 Apr 2024 09:45:18 +0800 Subject: [PATCH 42/64] feat: OFlag::O_PATH for FreeBSD and Fuchsia (#2382) --- changelog/2382.added.md | 1 + src/fcntl.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/2382.added.md diff --git a/changelog/2382.added.md b/changelog/2382.added.md new file mode 100644 index 0000000000..d9b2274157 --- /dev/null +++ b/changelog/2382.added.md @@ -0,0 +1 @@ +Add `fcntl::OFlag::O_PATH` for FreeBSD and Fuchsia diff --git a/src/fcntl.rs b/src/fcntl.rs index a33b2642d3..67204a7272 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -151,7 +151,7 @@ libc_bitflags!( /// Obtain a file descriptor for low-level access. /// /// The file itself is not opened and other file operations will fail. - #[cfg(any(linux_android, target_os = "redox"))] + #[cfg(any(linux_android, target_os = "redox", target_os = "freebsd", target_os = "fuchsia"))] O_PATH; /// Only allow reading. /// From e04689745050a6e8e7381dc3fae09f5015af3024 Mon Sep 17 00:00:00 2001 From: TheJonny Date: Tue, 23 Apr 2024 15:12:58 +0200 Subject: [PATCH 43/64] test: initgroups: compare result group lists without order (#2385) Fixes issue #2384 --- test/test_unistd.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/test_unistd.rs b/test/test_unistd.rs index aa2e5e56d7..6ccf59fb05 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -313,12 +313,15 @@ fn test_initgroups() { // groups that the user belongs to are also set. let user = CString::new("root").unwrap(); let group = Gid::from_raw(123); - let group_list = getgrouplist(&user, group).unwrap(); + let mut group_list = getgrouplist(&user, group).unwrap(); assert!(group_list.contains(&group)); initgroups(&user, group).unwrap(); - let new_groups = getgroups().unwrap(); + let mut new_groups = getgroups().unwrap(); + + new_groups.sort_by_key(|gid| gid.as_raw()); + group_list.sort_by_key(|gid| gid.as_raw()); assert_eq!(new_groups, group_list); // Revert back to the old groups From 395906eb2a1faefa5e82c7be23cc4c78465be473 Mon Sep 17 00:00:00 2001 From: TheJonny Date: Wed, 24 Apr 2024 03:34:16 +0200 Subject: [PATCH 44/64] test: Grab FORK_MTX when unmounting or forking in the mount tests (#2386) * mount tests: take FORK_MTX when forking or unmounting unmounting can fail if a child process inherited a file a file descriptor we opened in temporary mount point Should fix #2369 * tests: clarify the meaning of the FORK_MTX * tests: clarify the meaning of FORK_MTX Co-authored-by: SteveLauC * here no one accesses the file system to be unmounted, so no fork lock needed --------- Co-authored-by: SteveLauC --- test/mount/test_mount.rs | 6 ++++++ test/test.rs | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/mount/test_mount.rs b/test/mount/test_mount.rs index a4f0903dba..9cb7741796 100644 --- a/test/mount/test_mount.rs +++ b/test/mount/test_mount.rs @@ -53,6 +53,8 @@ fn test_mount_tmpfs_without_flags_allows_rwx() { .unwrap_or_else(|e| panic!("read failed: {e}")); assert_eq!(buf, SCRIPT_CONTENTS); + // while forking and unmounting prevent other child processes + let _m = FORK_MTX.lock(); // Verify execute. assert_eq!( EXPECTED_STATUS, @@ -129,6 +131,8 @@ fn test_mount_noexec_disallows_exec() { &test_path ); + // while forking and unmounting prevent other child processes + let _m = FORK_MTX.lock(); // EACCES: Permission denied assert_eq!( EACCES, @@ -168,6 +172,8 @@ fn test_mount_bind() { .and_then(|mut f| f.write(SCRIPT_CONTENTS)) .unwrap_or_else(|e| panic!("write failed: {e}")); + // wait for child processes to prevent EBUSY + let _m = FORK_MTX.lock(); umount(mount_point.path()) .unwrap_or_else(|e| panic!("umount failed: {e}")); } diff --git a/test/test.rs b/test/test.rs index 53a6af4b6c..4e0ffc8f9a 100644 --- a/test/test.rs +++ b/test/test.rs @@ -57,8 +57,9 @@ fn read_exact(f: Fd, buf: &mut [u8]) { } } -/// Any test that creates child processes must grab this mutex, regardless -/// of what it does with those children. +/// Any test that creates child processes or can be affected by child processes must grab this mutex, regardless +/// of what it does with those children. It must hold the mutex until the +/// child processes are waited upon. pub static FORK_MTX: Mutex<()> = Mutex::new(()); /// Any test that changes the process's current working directory must grab /// the RwLock exclusively. Any process that cares about the current From 213127b5177c576b9ff7a7da2af2bf3c2684ad47 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Thu, 25 Apr 2024 09:01:47 +0800 Subject: [PATCH 45/64] feat: Use type parameters to allow `{get,set}regset` to use different register set structs (#2373) --- src/sys/ptrace/linux.rs | 111 ++++++++++++++++++++++++++++++---------- test/sys/test_ptrace.rs | 33 +++++++----- 2 files changed, 106 insertions(+), 38 deletions(-) diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index c36bf05197..ddd0a71fd0 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -172,21 +172,21 @@ libc_enum! { } } +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) +))] libc_enum! { - #[cfg(all( - target_os = "linux", - target_env = "gnu", - any( - target_arch = "x86_64", - target_arch = "x86", - target_arch = "aarch64", - target_arch = "riscv64", - ) - ))] #[repr(i32)] - /// Defining a specific register set, as used in [`getregset`] and [`setregset`]. + /// Defines a specific register set, as used in `PTRACE_GETREGSET` and `PTRACE_SETREGSET`. #[non_exhaustive] - pub enum RegisterSet { + pub enum RegisterSetValue { NT_PRSTATUS, NT_PRFPREG, NT_PRPSINFO, @@ -195,6 +195,69 @@ libc_enum! { } } +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) +))] +/// Represents register set areas, such as general-purpose registers or +/// floating-point registers. +/// +/// # Safety +/// +/// This trait is marked unsafe, since implementation of the trait must match +/// ptrace's request `VALUE` and return data type `Regs`. +pub unsafe trait RegisterSet { + /// Corresponding type of registers in the kernel. + const VALUE: RegisterSetValue; + + /// Struct representing the register space. + type Regs; +} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", + any( + target_arch = "x86_64", + target_arch = "x86", + target_arch = "aarch64", + target_arch = "riscv64", + ) +))] +/// Register sets used in [`getregset`] and [`setregset`] +pub mod regset { + use super::*; + + #[derive(Debug, Clone, Copy)] + /// General-purpose registers. + pub struct NT_PRSTATUS; + + unsafe impl RegisterSet for NT_PRSTATUS { + const VALUE: RegisterSetValue = RegisterSetValue::NT_PRSTATUS; + type Regs = user_regs_struct; + } + + #[derive(Debug, Clone, Copy)] + /// Floating-point registers. + pub struct NT_PRFPREG; + + unsafe impl RegisterSet for NT_PRFPREG { + const VALUE: RegisterSetValue = RegisterSetValue::NT_PRFPREG; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + type Regs = libc::user_fpregs_struct; + #[cfg(target_arch = "aarch64")] + type Regs = libc::user_fpsimd_struct; + #[cfg(target_arch = "riscv64")] + type Regs = libc::__riscv_mc_d_ext_state; + } +} + libc_bitflags! { /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request. /// See `man ptrace` for more details. @@ -275,7 +338,7 @@ pub fn getregs(pid: Pid) -> Result { any(target_arch = "aarch64", target_arch = "riscv64") ))] pub fn getregs(pid: Pid) -> Result { - getregset(pid, RegisterSet::NT_PRSTATUS) + getregset::(pid) } /// Get a particular set of user registers, as with `ptrace(PTRACE_GETREGSET, ...)` @@ -289,18 +352,18 @@ pub fn getregs(pid: Pid) -> Result { target_arch = "riscv64", ) ))] -pub fn getregset(pid: Pid, set: RegisterSet) -> Result { +pub fn getregset(pid: Pid) -> Result { let request = Request::PTRACE_GETREGSET; - let mut data = mem::MaybeUninit::::uninit(); + let mut data = mem::MaybeUninit::::uninit(); let mut iov = libc::iovec { iov_base: data.as_mut_ptr().cast(), - iov_len: mem::size_of::(), + iov_len: mem::size_of::(), }; unsafe { ptrace_other( request, pid, - set as i32 as AddressType, + S::VALUE as i32 as AddressType, (&mut iov as *mut libc::iovec).cast(), )?; }; @@ -349,7 +412,7 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { any(target_arch = "aarch64", target_arch = "riscv64") ))] pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { - setregset(pid, RegisterSet::NT_PRSTATUS, regs) + setregset::(pid, regs) } /// Set a particular set of user registers, as with `ptrace(PTRACE_SETREGSET, ...)` @@ -363,20 +426,16 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { target_arch = "riscv64", ) ))] -pub fn setregset( - pid: Pid, - set: RegisterSet, - mut regs: user_regs_struct, -) -> Result<()> { +pub fn setregset(pid: Pid, mut regs: S::Regs) -> Result<()> { let mut iov = libc::iovec { - iov_base: (&mut regs as *mut user_regs_struct).cast(), - iov_len: mem::size_of::(), + iov_base: (&mut regs as *mut S::Regs).cast(), + iov_len: mem::size_of::(), }; unsafe { ptrace_other( Request::PTRACE_SETREGSET, pid, - set as i32 as AddressType, + S::VALUE as i32 as AddressType, (&mut iov as *mut libc::iovec).cast(), )?; } diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 5eb7e249f3..c99c6762c3 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -302,7 +302,7 @@ fn test_ptrace_syscall() { ))] #[test] fn test_ptrace_regsets() { - use nix::sys::ptrace::{self, getregset, setregset, RegisterSet}; + use nix::sys::ptrace::{self, getregset, regset, setregset}; use nix::sys::signal::*; use nix::sys::wait::{waitpid, WaitStatus}; use nix::unistd::fork; @@ -328,30 +328,39 @@ fn test_ptrace_regsets() { Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)) ); let mut regstruct = - getregset(child, RegisterSet::NT_PRSTATUS).unwrap(); + getregset::(child).unwrap(); + let mut fpregstruct = + getregset::(child).unwrap(); #[cfg(target_arch = "x86_64")] - let reg = &mut regstruct.r15; + let (reg, fpreg) = + (&mut regstruct.r15, &mut fpregstruct.st_space[5]); #[cfg(target_arch = "x86")] - let reg = &mut regstruct.edx; + let (reg, fpreg) = + (&mut regstruct.edx, &mut fpregstruct.st_space[5]); #[cfg(target_arch = "aarch64")] - let reg = &mut regstruct.regs[16]; + let (reg, fpreg) = + (&mut regstruct.regs[16], &mut fpregstruct.vregs[5]); #[cfg(target_arch = "riscv64")] - let reg = &mut regstruct.regs[16]; + let (reg, fpreg) = (&mut regstruct.t1, &mut fpregstruct.__f[5]); *reg = 0xdeadbeefu32 as _; - let _ = setregset(child, RegisterSet::NT_PRSTATUS, regstruct); - regstruct = getregset(child, RegisterSet::NT_PRSTATUS).unwrap(); + *fpreg = 0xfeedfaceu32 as _; + let _ = setregset::(child, regstruct); + regstruct = getregset::(child).unwrap(); + let _ = setregset::(child, fpregstruct); + fpregstruct = getregset::(child).unwrap(); #[cfg(target_arch = "x86_64")] - let reg = regstruct.r15; + let (reg, fpreg) = (regstruct.r15, fpregstruct.st_space[5]); #[cfg(target_arch = "x86")] - let reg = regstruct.edx; + let (reg, fpreg) = (regstruct.edx, fpregstruct.st_space[5]); #[cfg(target_arch = "aarch64")] - let reg = regstruct.regs[16]; + let (reg, fpreg) = (regstruct.regs[16], fpregstruct.vregs[5]); #[cfg(target_arch = "riscv64")] - let reg = regstruct.regs[16]; + let (reg, fpreg) = (regstruct.t1, fpregstruct.__f[5]); assert_eq!(reg, 0xdeadbeefu32 as _); + assert_eq!(fpreg, 0xfeedfaceu32 as _); ptrace::cont(child, Some(Signal::SIGKILL)).unwrap(); match waitpid(child, None) { From e99e74cf3c44389f71abada397e1988c4c37dc17 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 27 Apr 2024 12:26:12 +0100 Subject: [PATCH 46/64] fcntl: change (vm)splice, tee using AsFd instead. (#2387) --- changelog/2387.changed.md | 1 + src/fcntl.rs | 22 +++++++++++----------- test/test_fcntl.rs | 20 ++++++-------------- 3 files changed, 18 insertions(+), 25 deletions(-) create mode 100644 changelog/2387.changed.md diff --git a/changelog/2387.changed.md b/changelog/2387.changed.md new file mode 100644 index 0000000000..28ade908c2 --- /dev/null +++ b/changelog/2387.changed.md @@ -0,0 +1 @@ +Changed tee, splice and vmsplice RawFd arguments to AsFd. diff --git a/src/fcntl.rs b/src/fcntl.rs index 67204a7272..b27a570bac 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -1054,10 +1054,10 @@ pub fn copy_file_range( /// # See Also /// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html) #[cfg(linux_android)] -pub fn splice( - fd_in: RawFd, +pub fn splice( + fd_in: Fd1, off_in: Option<&mut libc::loff_t>, - fd_out: RawFd, + fd_out: Fd2, off_out: Option<&mut libc::loff_t>, len: usize, flags: SpliceFFlags, @@ -1070,7 +1070,7 @@ pub fn splice( .unwrap_or(ptr::null_mut()); let ret = unsafe { - libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) + libc::splice(fd_in.as_fd().as_raw_fd(), off_in, fd_out.as_fd().as_raw_fd(), off_out, len, flags.bits()) }; Errno::result(ret).map(|r| r as usize) } @@ -1080,13 +1080,13 @@ pub fn splice( /// # See Also /// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html) #[cfg(linux_android)] -pub fn tee( - fd_in: RawFd, - fd_out: RawFd, +pub fn tee( + fd_in: Fd1, + fd_out: Fd2, len: usize, flags: SpliceFFlags, ) -> Result { - let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) }; + let ret = unsafe { libc::tee(fd_in.as_fd().as_raw_fd(), fd_out.as_fd().as_raw_fd(), len, flags.bits()) }; Errno::result(ret).map(|r| r as usize) } @@ -1095,14 +1095,14 @@ pub fn tee( /// # See Also /// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html) #[cfg(linux_android)] -pub fn vmsplice( - fd: RawFd, +pub fn vmsplice( + fd: F, iov: &[std::io::IoSlice<'_>], flags: SpliceFFlags, ) -> Result { let ret = unsafe { libc::vmsplice( - fd, + fd.as_fd().as_raw_fd(), iov.as_ptr().cast(), iov.len(), flags.bits(), diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index e4ace020f7..1d05b1e250 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -357,15 +357,9 @@ mod linux_android { let (rd, wr) = pipe().unwrap(); let mut offset: loff_t = 5; - let res = splice( - tmp.as_raw_fd(), - Some(&mut offset), - wr.as_raw_fd(), - None, - 2, - SpliceFFlags::empty(), - ) - .unwrap(); + let res = + splice(tmp, Some(&mut offset), wr, None, 2, SpliceFFlags::empty()) + .unwrap(); assert_eq!(2, res); @@ -381,9 +375,8 @@ mod linux_android { let (rd2, wr2) = pipe().unwrap(); write(wr1, b"abc").unwrap(); - let res = - tee(rd1.as_raw_fd(), wr2.as_raw_fd(), 2, SpliceFFlags::empty()) - .unwrap(); + let res = tee(rd1.try_clone().unwrap(), wr2, 2, SpliceFFlags::empty()) + .unwrap(); assert_eq!(2, res); @@ -406,8 +399,7 @@ mod linux_android { let buf2 = b"defghi"; let iovecs = [IoSlice::new(&buf1[0..3]), IoSlice::new(&buf2[0..3])]; - let res = vmsplice(wr.as_raw_fd(), &iovecs[..], SpliceFFlags::empty()) - .unwrap(); + let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap(); assert_eq!(6, res); From e524c400bc0eff5fc22425c6043a86a0066353c2 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 28 Apr 2024 07:42:02 +0800 Subject: [PATCH 47/64] feat: MIN_HOLE_SIZE for apple targets (#2388) --- Cargo.toml | 2 +- changelog/2388.added.md | 1 + src/unistd.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelog/2388.added.md diff --git a/Cargo.toml b/Cargo.toml index adaf31f0fd..6707429d53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ targets = [ ] [dependencies] -libc = { version = "0.2.153", features = ["extra_traits"] } +libc = { git = "https://github.com/rust-lang/libc", branch = "libc-0.2", features = ["extra_traits"] } bitflags = "2.3.1" cfg-if = "1.0" pin-utils = { version = "0.1.0", optional = true } diff --git a/changelog/2388.added.md b/changelog/2388.added.md new file mode 100644 index 0000000000..b70769d4af --- /dev/null +++ b/changelog/2388.added.md @@ -0,0 +1 @@ +Added `PathconfVar::MIN_HOLE_SIZE` for apple_targets. \ No newline at end of file diff --git a/src/unistd.rs b/src/unistd.rs index 9e98ba7897..58ede6eb9b 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2046,6 +2046,7 @@ pub enum PathconfVar { /// may require to be typed as input before reading them. MAX_INPUT = libc::_PC_MAX_INPUT, #[cfg(any( + apple_targets, solarish, freebsdlike, target_os = "netbsd", From e59c9709998ebbcc275ed06f5b49ee2db9c42c15 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 29 Apr 2024 09:28:40 +0800 Subject: [PATCH 48/64] feat: Add VisionOS to apple_targets (#2390) --- build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index fd19de0fe9..70adacbd1f 100644 --- a/build.rs +++ b/build.rs @@ -14,10 +14,11 @@ fn main() { solaris: { target_os = "solaris" }, watchos: { target_os = "watchos" }, tvos: { target_os = "tvos" }, + visionos: { target_os = "visionos" }, // cfg aliases we would like to use - apple_targets: { any(ios, macos, watchos, tvos) }, + apple_targets: { any(ios, macos, watchos, tvos, visionos) }, bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) }, bsd_without_apple: { any(freebsd, dragonfly, netbsd, openbsd) }, linux_android: { any(android, linux) }, From 590ab4d5d0b6228f0529b036e4f8a42265ba6d31 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 29 Apr 2024 20:00:10 +0800 Subject: [PATCH 49/64] feat: OFlag::O_SEARCH for apple targets (#2391) --- changelog/2391.added.md | 1 + src/fcntl.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 changelog/2391.added.md diff --git a/changelog/2391.added.md b/changelog/2391.added.md new file mode 100644 index 0000000000..9989be85fc --- /dev/null +++ b/changelog/2391.added.md @@ -0,0 +1 @@ +Add `open` flag `O_SEARCH` to apple_targets diff --git a/src/fcntl.rs b/src/fcntl.rs index b27a570bac..043cdde473 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -166,7 +166,16 @@ libc_bitflags!( O_RSYNC; /// Open directory for search only. Skip search permission checks on /// later `openat()` calls using the obtained file descriptor. - #[cfg(any(target_os = "netbsd", target_os = "freebsd", solarish, target_os = "fuchsia", target_os = "emscripten", target_os = "aix", target_os = "wasi"))] + #[cfg(any( + apple_targets, + solarish, + target_os = "netbsd", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "emscripten", + target_os = "aix", + target_os = "wasi" + ))] O_SEARCH; /// Open with a shared file lock. #[cfg(any(bsd, target_os = "redox"))] From 047d6626b01ccbe17e5ba5cf68bbde54ed19d345 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 5 May 2024 21:03:34 +0800 Subject: [PATCH 50/64] ci: fix CI by correcting wrong cfg value & removing cache with sudo (#2394) * refactor: correct wrong cfg value && remove ones that do not exist * debug ci * debug ci * debug ci * debug ci * test: disable test_mq module for Fuchsia * style: fmt --- .github/workflows/ci.yml | 8 ++++---- build.rs | 11 +++++++++++ src/sys/signal.rs | 2 +- test/common/mod.rs | 4 ++-- test/sys/test_signal.rs | 2 +- test/test.rs | 7 +------ test/test_fcntl.rs | 4 ---- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a56e7d2b7..ed51548a7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: TARGET: '${{ env.TARGET }}' - name: before_cache_script - run: rm -rf $CARGO_HOME/registry/index + run: sudo rm -rf $CARGO_HOME/registry/index macos-aarch64: runs-on: macos-14 @@ -68,7 +68,7 @@ jobs: TARGET: "${{ env.TARGET }}" - name: before_cache_script - run: rm -rf $CARGO_HOME/registry/index + run: sudo rm -rf $CARGO_HOME/registry/index # Use cross for QEMU-based testing # cross needs to execute Docker, GitHub Action already has it installed @@ -162,7 +162,7 @@ jobs: TARGET: '${{ matrix.TARGET }}' - name: before_cache_script - run: rm -rf $CARGO_HOME/registry/index + run: sudo rm -rf $CARGO_HOME/registry/index; rust_stable: runs-on: ubuntu-20.04 @@ -188,7 +188,7 @@ jobs: TARGET: '${{ env.TARGET }}' - name: before_cache_script - run: rm -rf $CARGO_HOME/registry/index + run: sudo rm -rf $CARGO_HOME/registry/index diff --git a/build.rs b/build.rs index 70adacbd1f..226a32cccb 100644 --- a/build.rs +++ b/build.rs @@ -26,4 +26,15 @@ fn main() { netbsdlike: { any(netbsd, openbsd) }, solarish: { any(illumos, solaris) }, } + + // Below are Nix's custom cfg values that we need to let the compiler know + println!("cargo:rustc-check-cfg=cfg(apple_targets)"); + println!("cargo:rustc-check-cfg=cfg(bsd)"); + println!("cargo:rustc-check-cfg=cfg(bsd_without_apple)"); + println!("cargo:rustc-check-cfg=cfg(linux_android)"); + println!("cargo:rustc-check-cfg=cfg(freebsdlike)"); + println!("cargo:rustc-check-cfg=cfg(netbsdlike)"); + println!("cargo:rustc-check-cfg=cfg(solarish)"); + println!("cargo:rustc-check-cfg=cfg(fbsd14)"); + println!("cargo:rustc-check-cfg=cfg(qemu)"); } diff --git a/src/sys/signal.rs b/src/sys/signal.rs index 2a9c0027a9..8f41127e27 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -597,7 +597,7 @@ impl SigSet { target_os = "haiku", target_os = "hurd", target_os = "aix", - target_os = "fushsia" + target_os = "fuchsia" ))] #[doc(alias("sigsuspend"))] pub fn suspend(&self) -> Result<()> { diff --git a/test/common/mod.rs b/test/common/mod.rs index db4aed2598..ab0e746367 100644 --- a/test/common/mod.rs +++ b/test/common/mod.rs @@ -37,8 +37,8 @@ cfg_if! { #[macro_export] macro_rules! require_mount { ($name:expr) => { - use ::sysctl::{CtlValue, Sysctl}; use nix::unistd::Uid; + use sysctl::{CtlValue, Sysctl}; let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap(); if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap() @@ -65,7 +65,7 @@ macro_rules! skip_if_cirrus { #[macro_export] macro_rules! skip_if_jailed { ($name:expr) => { - use ::sysctl::{CtlValue, Sysctl}; + use sysctl::{CtlValue, Sysctl}; let ctl = ::sysctl::Ctl::new("security.jail.jailed").unwrap(); if let CtlValue::Int(1) = ctl.value().unwrap() { diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs index 7494b71234..cd4bc3d9b9 100644 --- a/test/sys/test_signal.rs +++ b/test/sys/test_signal.rs @@ -350,7 +350,7 @@ fn test_sigwait() { target_os = "haiku", target_os = "hurd", target_os = "aix", - target_os = "fushsia" + target_os = "fuchsia" ))] #[test] fn test_sigsuspend() { diff --git a/test/test.rs b/test/test.rs index 4e0ffc8f9a..7401c95611 100644 --- a/test/test.rs +++ b/test/test.rs @@ -13,12 +13,7 @@ mod test_errno; mod test_fcntl; #[cfg(linux_android)] mod test_kmod; -#[cfg(any( - freebsdlike, - target_os = "fushsia", - target_os = "linux", - target_os = "netbsd" -))] +#[cfg(any(freebsdlike, target_os = "linux", target_os = "netbsd"))] mod test_mq; #[cfg(not(target_os = "redox"))] mod test_net; diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index 1d05b1e250..2131f46c5c 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -13,7 +13,6 @@ use nix::fcntl::{openat2, OpenHow, ResolveFlag}; target_env = "gnu", any( target_arch = "x86_64", - target_arch = "x32", target_arch = "powerpc", target_arch = "s390x" ) @@ -146,7 +145,6 @@ fn test_renameat() { target_env = "gnu", any( target_arch = "x86_64", - target_arch = "x32", target_arch = "powerpc", target_arch = "s390x" ) @@ -190,7 +188,6 @@ fn test_renameat2_behaves_like_renameat_with_no_flags() { target_env = "gnu", any( target_arch = "x86_64", - target_arch = "x32", target_arch = "powerpc", target_arch = "s390x" ) @@ -238,7 +235,6 @@ fn test_renameat2_exchange() { target_env = "gnu", any( target_arch = "x86_64", - target_arch = "x32", target_arch = "powerpc", target_arch = "s390x" ) From ac3373b1dbf380ee65a21a847eb672976581584f Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Mon, 6 May 2024 20:34:23 +0800 Subject: [PATCH 51/64] refactor: use zero-variants enum for types that are RegisterSet (#2395) --- src/sys/ptrace/linux.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index ddd0a71fd0..8abaf4d71b 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -236,7 +236,7 @@ pub mod regset { #[derive(Debug, Clone, Copy)] /// General-purpose registers. - pub struct NT_PRSTATUS; + pub enum NT_PRSTATUS {} unsafe impl RegisterSet for NT_PRSTATUS { const VALUE: RegisterSetValue = RegisterSetValue::NT_PRSTATUS; @@ -245,7 +245,7 @@ pub mod regset { #[derive(Debug, Clone, Copy)] /// Floating-point registers. - pub struct NT_PRFPREG; + pub enum NT_PRFPREG {} unsafe impl RegisterSet for NT_PRFPREG { const VALUE: RegisterSetValue = RegisterSetValue::NT_PRFPREG; From 99a890a748ff0ef96057cde4a7c025ab144cf706 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Wed, 8 May 2024 07:35:06 +0800 Subject: [PATCH 52/64] chore: bump deps libc to 0.2.154 (#2398) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6707429d53..c6a69b4654 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ targets = [ ] [dependencies] -libc = { git = "https://github.com/rust-lang/libc", branch = "libc-0.2", features = ["extra_traits"] } +libc = { version = "0.2.154", features = ["extra_traits"] } bitflags = "2.3.1" cfg-if = "1.0" pin-utils = { version = "0.1.0", optional = true } From 0c08f39bb102e4594b09780c43705569dbb3f87e Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Tue, 14 May 2024 20:38:39 -0600 Subject: [PATCH 53/64] Added I/O safety to sys::aio (#2401) --- changelog/2401.changed.md | 2 + src/sys/aio.rs | 124 +++++++++++++------------ test/sys/test_aio.rs | 189 ++++++++++++++++++++------------------ test/sys/test_aio_drop.rs | 4 +- 4 files changed, 172 insertions(+), 147 deletions(-) create mode 100644 changelog/2401.changed.md diff --git a/changelog/2401.changed.md b/changelog/2401.changed.md new file mode 100644 index 0000000000..7eb53e677a --- /dev/null +++ b/changelog/2401.changed.md @@ -0,0 +1,2 @@ +Added I/O safety to the sys/aio module. Most functions that previously +accepted a `AsRawFd` argument now accept an `AsFd` instead. diff --git a/src/sys/aio.rs b/src/sys/aio.rs index e9213c6434..71952aa05f 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -30,7 +30,7 @@ use std::{ fmt::{self, Debug}, marker::{PhantomData, PhantomPinned}, mem, - os::unix::io::RawFd, + os::unix::io::{AsFd, AsRawFd, BorrowedFd}, pin::Pin, ptr, thread, }; @@ -102,7 +102,7 @@ unsafe impl Sync for LibcAiocb {} // provide polymorphism at the wrong level. Instead, the best place for // polymorphism is at the level of `Futures`. #[repr(C)] -struct AioCb { +struct AioCb<'a> { aiocb: LibcAiocb, /// Could this `AioCb` potentially have any in-kernel state? // It would be really nice to perform the in-progress check entirely at @@ -112,9 +112,10 @@ struct AioCb { // that there's no way to write an AioCb constructor that neither boxes // the object itself, nor moves it during return. in_progress: bool, + _fd: PhantomData>, } -impl AioCb { +impl<'a> AioCb<'a> { pin_utils::unsafe_unpinned!(aiocb: LibcAiocb); fn aio_return(mut self: Pin<&mut Self>) -> Result { @@ -139,18 +140,23 @@ impl AioCb { } } - fn common_init(fd: RawFd, prio: i32, sigev_notify: SigevNotify) -> Self { + fn common_init( + fd: BorrowedFd<'a>, + prio: i32, + sigev_notify: SigevNotify, + ) -> Self { // Use mem::zeroed instead of explicitly zeroing each field, because the // number and name of reserved fields is OS-dependent. On some OSes, // some reserved fields are used the kernel for state, and must be // explicitly zeroed when allocated. let mut a = unsafe { mem::zeroed::() }; - a.aio_fildes = fd; + a.aio_fildes = fd.as_raw_fd(); a.aio_reqprio = prio; a.aio_sigevent = SigEvent::new(sigev_notify).sigevent(); AioCb { aiocb: LibcAiocb(a), in_progress: false, + _fd: PhantomData, } } @@ -186,7 +192,7 @@ impl AioCb { } } -impl Debug for AioCb { +impl<'a> Debug for AioCb<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("AioCb") .field("aiocb", &self.aiocb.0) @@ -195,7 +201,7 @@ impl Debug for AioCb { } } -impl Drop for AioCb { +impl<'a> Drop for AioCb<'a> { /// If the `AioCb` has no remaining state in the kernel, just drop it. /// Otherwise, dropping constitutes a resource leak, which is an error fn drop(&mut self) { @@ -243,11 +249,11 @@ pub trait Aio { /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::io::Write; - /// # use std::os::unix::io::AsRawFd; + /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// let wbuf = b"CDEF"; /// let mut f = tempfile().unwrap(); - /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(), + /// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// &wbuf[..], /// 0, //priority @@ -284,11 +290,11 @@ pub trait Aio { /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; - /// # use std::os::unix::io::AsRawFd; + /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); - /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(), + /// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// WBUF, /// 0, //priority @@ -306,7 +312,7 @@ pub trait Aio { fn error(self: Pin<&mut Self>) -> Result<()>; /// Returns the underlying file descriptor associated with the operation. - fn fd(&self) -> RawFd; + fn fd(&self) -> BorrowedFd; /// Does this operation currently have any in-kernel state? /// @@ -321,10 +327,10 @@ pub trait Aio { /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify::SigevNone; /// # use std::{thread, time}; - /// # use std::os::unix::io::AsRawFd; + /// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// let f = tempfile().unwrap(); - /// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC, + /// let mut aiof = Box::pin(AioFsync::new(f.as_fd(), AioFsyncMode::O_SYNC, /// 0, SigevNone)); /// assert!(!aiof.as_mut().in_progress()); /// aiof.as_mut().submit().expect("aio_fsync failed early"); @@ -364,8 +370,10 @@ macro_rules! aio_methods { self.aiocb().error() } - fn fd(&self) -> RawFd { - self.aiocb.aiocb.0.aio_fildes + fn fd(&self) -> BorrowedFd<'a> { + // safe because self's lifetime is the same as the original file + // descriptor. + unsafe { BorrowedFd::borrow_raw(self.aiocb.aiocb.0.aio_fildes) } } fn in_progress(&self) -> bool { @@ -413,10 +421,10 @@ macro_rules! aio_methods { /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify::SigevNone; /// # use std::{thread, time}; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// let f = tempfile().unwrap(); -/// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC, +/// let mut aiof = Box::pin(AioFsync::new(f.as_fd(), AioFsyncMode::O_SYNC, /// 0, SigevNone)); /// aiof.as_mut().submit().expect("aio_fsync failed early"); /// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) { @@ -426,13 +434,13 @@ macro_rules! aio_methods { /// ``` #[derive(Debug)] #[repr(transparent)] -pub struct AioFsync { - aiocb: AioCb, +pub struct AioFsync<'a> { + aiocb: AioCb<'a>, _pin: PhantomPinned, } -impl AioFsync { - unsafe_pinned!(aiocb: AioCb); +impl<'a> AioFsync<'a> { + unsafe_pinned!(aiocb: AioCb<'a>); /// Returns the operation's fsync mode: data and metadata or data only? pub fn mode(&self) -> AioFsyncMode { @@ -451,7 +459,7 @@ impl AioFsync { /// * `sigev_notify`: Determines how you will be notified of event /// completion. pub fn new( - fd: RawFd, + fd: BorrowedFd<'a>, mode: AioFsyncMode, prio: i32, sigev_notify: SigevNotify, @@ -469,7 +477,7 @@ impl AioFsync { } } -impl Aio for AioFsync { +impl<'a> Aio for AioFsync<'a> { type Output = (); aio_methods!(); @@ -490,7 +498,7 @@ impl Aio for AioFsync { // AioFsync does not need AsMut, since it can't be used with lio_listio -impl AsRef for AioFsync { +impl<'a> AsRef for AioFsync<'a> { fn as_ref(&self) -> &libc::aiocb { &self.aiocb.aiocb.0 } @@ -512,7 +520,7 @@ impl AsRef for AioFsync { /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::io::Write; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const INITIAL: &[u8] = b"abcdef123456"; /// const LEN: usize = 4; @@ -522,7 +530,7 @@ impl AsRef for AioFsync { /// { /// let mut aior = Box::pin( /// AioRead::new( -/// f.as_raw_fd(), +/// f.as_fd(), /// 2, //offset /// &mut rbuf, /// 0, //priority @@ -540,13 +548,13 @@ impl AsRef for AioFsync { #[derive(Debug)] #[repr(transparent)] pub struct AioRead<'a> { - aiocb: AioCb, + aiocb: AioCb<'a>, _data: PhantomData<&'a [u8]>, _pin: PhantomPinned, } impl<'a> AioRead<'a> { - unsafe_pinned!(aiocb: AioCb); + unsafe_pinned!(aiocb: AioCb<'a>); /// Returns the requested length of the aio operation in bytes /// @@ -570,7 +578,7 @@ impl<'a> AioRead<'a> { /// * `sigev_notify`: Determines how you will be notified of event /// completion. pub fn new( - fd: RawFd, + fd: BorrowedFd<'a>, offs: off_t, buf: &'a mut [u8], prio: i32, @@ -629,7 +637,7 @@ impl<'a> AsRef for AioRead<'a> { /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::io::{IoSliceMut, Write}; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const INITIAL: &[u8] = b"abcdef123456"; /// let mut rbuf0 = vec![0; 4]; @@ -641,7 +649,7 @@ impl<'a> AsRef for AioRead<'a> { /// { /// let mut aior = Box::pin( /// AioReadv::new( -/// f.as_raw_fd(), +/// f.as_fd(), /// 2, //offset /// &mut rbufs, /// 0, //priority @@ -661,14 +669,14 @@ impl<'a> AsRef for AioRead<'a> { #[derive(Debug)] #[repr(transparent)] pub struct AioReadv<'a> { - aiocb: AioCb, + aiocb: AioCb<'a>, _data: PhantomData<&'a [&'a [u8]]>, _pin: PhantomPinned, } #[cfg(target_os = "freebsd")] impl<'a> AioReadv<'a> { - unsafe_pinned!(aiocb: AioCb); + unsafe_pinned!(aiocb: AioCb<'a>); /// Returns the number of buffers the operation will read into. pub fn iovlen(&self) -> usize { @@ -689,7 +697,7 @@ impl<'a> AioReadv<'a> { /// * `sigev_notify`: Determines how you will be notified of event /// completion. pub fn new( - fd: RawFd, + fd: BorrowedFd<'a>, offs: off_t, bufs: &mut [IoSliceMut<'a>], prio: i32, @@ -750,13 +758,13 @@ impl<'a> AsRef for AioReadv<'a> { /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin( /// AioWrite::new( -/// f.as_raw_fd(), +/// f.as_fd(), /// 2, //offset /// WBUF, /// 0, //priority @@ -772,13 +780,13 @@ impl<'a> AsRef for AioReadv<'a> { #[derive(Debug)] #[repr(transparent)] pub struct AioWrite<'a> { - aiocb: AioCb, + aiocb: AioCb<'a>, _data: PhantomData<&'a [u8]>, _pin: PhantomPinned, } impl<'a> AioWrite<'a> { - unsafe_pinned!(aiocb: AioCb); + unsafe_pinned!(aiocb: AioCb<'a>); /// Returns the requested length of the aio operation in bytes /// @@ -802,7 +810,7 @@ impl<'a> AioWrite<'a> { /// * `sigev_notify`: Determines how you will be notified of event /// completion. pub fn new( - fd: RawFd, + fd: BorrowedFd<'a>, offs: off_t, buf: &'a [u8], prio: i32, @@ -864,7 +872,7 @@ impl<'a> AsRef for AioWrite<'a> { /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::io::IoSlice; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const wbuf0: &[u8] = b"abcdef"; /// const wbuf1: &[u8] = b"123456"; @@ -873,7 +881,7 @@ impl<'a> AsRef for AioWrite<'a> { /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin( /// AioWritev::new( -/// f.as_raw_fd(), +/// f.as_fd(), /// 2, //offset /// &wbufs, /// 0, //priority @@ -890,14 +898,14 @@ impl<'a> AsRef for AioWrite<'a> { #[derive(Debug)] #[repr(transparent)] pub struct AioWritev<'a> { - aiocb: AioCb, + aiocb: AioCb<'a>, _data: PhantomData<&'a [&'a [u8]]>, _pin: PhantomPinned, } #[cfg(target_os = "freebsd")] impl<'a> AioWritev<'a> { - unsafe_pinned!(aiocb: AioCb); + unsafe_pinned!(aiocb: AioCb<'a>); /// Returns the number of buffers the operation will read into. pub fn iovlen(&self) -> usize { @@ -918,7 +926,7 @@ impl<'a> AioWritev<'a> { /// * `sigev_notify`: Determines how you will be notified of event /// completion. pub fn new( - fd: RawFd, + fd: BorrowedFd<'a>, offs: off_t, bufs: &[IoSlice<'a>], prio: i32, @@ -983,17 +991,17 @@ impl<'a> AsRef for AioWritev<'a> { /// # use nix::sys::signal::SigevNotify; /// # use std::{thread, time}; /// # use std::io::Write; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// let wbuf = b"CDEF"; /// let mut f = tempfile().unwrap(); -/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(), +/// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// &wbuf[..], /// 0, //priority /// SigevNotify::SigevNone)); /// aiocb.as_mut().submit().unwrap(); -/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap(); +/// let cs = aio_cancel_all(f.as_fd()).unwrap(); /// if cs == AioCancelStat::AioNotCanceled { /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) { /// thread::sleep(time::Duration::from_millis(10)); @@ -1006,8 +1014,8 @@ impl<'a> AsRef for AioWritev<'a> { /// # References /// /// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) -pub fn aio_cancel_all(fd: RawFd) -> Result { - match unsafe { libc::aio_cancel(fd, ptr::null_mut()) } { +pub fn aio_cancel_all(fd: F) -> Result { + match unsafe { libc::aio_cancel(fd.as_fd().as_raw_fd(), ptr::null_mut()) } { libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled), libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone), @@ -1028,11 +1036,11 @@ pub fn aio_cancel_all(fd: RawFd) -> Result { /// ``` /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); -/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(), +/// let mut aiocb = Box::pin(AioWrite::new(f.as_fd(), /// 2, //offset /// WBUF, /// 0, //priority @@ -1078,14 +1086,14 @@ pub fn aio_suspend( /// This mode is useful for otherwise-synchronous programs that want to execute /// a handful of I/O operations in parallel. /// ``` -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use nix::sys::aio::*; /// # use nix::sys::signal::SigevNotify; /// # use tempfile::tempfile; /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin(AioWrite::new( -/// f.as_raw_fd(), +/// f.as_fd(), /// 2, // offset /// WBUF, /// 0, // priority @@ -1102,7 +1110,7 @@ pub fn aio_suspend( /// technique for reducing overall context-switch overhead, especially when /// combined with kqueue. /// ``` -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use std::thread; /// # use std::time; /// # use nix::errno::Errno; @@ -1112,7 +1120,7 @@ pub fn aio_suspend( /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin(AioWrite::new( -/// f.as_raw_fd(), +/// f.as_fd(), /// 2, // offset /// WBUF, /// 0, // priority @@ -1136,7 +1144,7 @@ pub fn aio_suspend( /// possibly resubmit some. /// ``` /// # use libc::c_int; -/// # use std::os::unix::io::AsRawFd; +/// # use std::os::unix::io::AsFd; /// # use std::sync::atomic::{AtomicBool, Ordering}; /// # use std::thread; /// # use std::time; @@ -1158,7 +1166,7 @@ pub fn aio_suspend( /// const WBUF: &[u8] = b"abcdef123456"; /// let mut f = tempfile().unwrap(); /// let mut aiow = Box::pin(AioWrite::new( -/// f.as_raw_fd(), +/// f.as_fd(), /// 2, // offset /// WBUF, /// 0, // priority diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs index ba5ad02ec3..2f4494facf 100644 --- a/test/sys/test_aio.rs +++ b/test/sys/test_aio.rs @@ -1,7 +1,7 @@ use std::{ io::{Read, Seek, Write}, ops::Deref, - os::unix::io::AsRawFd, + os::unix::io::{AsFd, AsRawFd, BorrowedFd}, pin::Pin, sync::atomic::{AtomicBool, Ordering}, thread, time, @@ -45,8 +45,9 @@ mod aio_fsync { #[test] fn test_accessors() { + let f = tempfile().unwrap(); let aiocb = AioFsync::new( - 1001, + f.as_fd(), AioFsyncMode::O_SYNC, 42, SigevNotify::SigevSignal { @@ -54,7 +55,7 @@ mod aio_fsync { si_value: 99, }, ); - assert_eq!(1001, aiocb.fd()); + assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd()); assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode()); assert_eq!(42, aiocb.priority()); let sev = aiocb.sigevent().sigevent(); @@ -67,21 +68,17 @@ mod aio_fsync { // Skip on Linux, because Linux's AIO implementation can't detect errors // synchronously #[test] - #[cfg(any(target_os = "freebsd", apple_targets))] + #[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)] fn error() { use std::mem; const INITIAL: &[u8] = b"abcdef123456"; // Create an invalid AioFsyncMode - let mode = unsafe { mem::transmute(666) }; + let mode = unsafe { mem::transmute::(666) }; let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); - let mut aiof = Box::pin(AioFsync::new( - f.as_raw_fd(), - mode, - 0, - SigevNotify::SigevNone, - )); + let mut aiof = + Box::pin(AioFsync::new(f.as_fd(), mode, 0, SigevNotify::SigevNone)); let err = aiof.as_mut().submit(); err.expect_err("assertion failed"); } @@ -92,9 +89,8 @@ mod aio_fsync { const INITIAL: &[u8] = b"abcdef123456"; let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); - let fd = f.as_raw_fd(); let mut aiof = Box::pin(AioFsync::new( - fd, + f.as_fd(), AioFsyncMode::O_SYNC, 0, SigevNotify::SigevNone, @@ -110,9 +106,10 @@ mod aio_read { #[test] fn test_accessors() { + let f = tempfile().unwrap(); let mut rbuf = vec![0; 4]; let aiocb = AioRead::new( - 1001, + f.as_fd(), 2, //offset &mut rbuf, 42, //priority @@ -121,7 +118,7 @@ mod aio_read { si_value: 99, }, ); - assert_eq!(1001, aiocb.fd()); + assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd()); assert_eq!(4, aiocb.nbytes()); assert_eq!(2, aiocb.offset()); assert_eq!(42, aiocb.priority()); @@ -140,7 +137,7 @@ mod aio_read { let mut rbuf = vec![0; 4]; let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); - let fd = f.as_raw_fd(); + let fd = f.as_fd(); let mut aior = Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone)); aior.as_mut().submit().unwrap(); @@ -164,7 +161,7 @@ mod aio_read { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); let mut aior = Box::pin(AioRead::new( - f.as_raw_fd(), + f.as_fd(), -1, //an invalid offset &mut rbuf, 0, //priority @@ -184,7 +181,7 @@ mod aio_read { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); { - let fd = f.as_raw_fd(); + let fd = f.as_fd(); let mut aior = Box::pin(AioRead::new( fd, 2, @@ -211,7 +208,7 @@ mod aio_read { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); { - let fd = f.as_raw_fd(); + let fd = f.as_fd(); let mut aior = AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone); let mut aior = unsafe { Pin::new_unchecked(&mut aior) }; @@ -234,12 +231,13 @@ mod aio_readv { #[test] fn test_accessors() { + let f = tempfile().unwrap(); let mut rbuf0 = vec![0; 4]; let mut rbuf1 = vec![0; 8]; let mut rbufs = [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)]; let aiocb = AioReadv::new( - 1001, + f.as_fd(), 2, //offset &mut rbufs, 42, //priority @@ -248,7 +246,7 @@ mod aio_readv { si_value: 99, }, ); - assert_eq!(1001, aiocb.fd()); + assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd()); assert_eq!(2, aiocb.iovlen()); assert_eq!(2, aiocb.offset()); assert_eq!(42, aiocb.priority()); @@ -270,7 +268,7 @@ mod aio_readv { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); { - let fd = f.as_raw_fd(); + let fd = f.as_fd(); let mut aior = Box::pin(AioReadv::new( fd, 2, @@ -297,9 +295,10 @@ mod aio_write { #[test] fn test_accessors() { + let f = tempfile().unwrap(); let wbuf = vec![0; 4]; let aiocb = AioWrite::new( - 1001, + f.as_fd(), 2, //offset &wbuf, 42, //priority @@ -308,7 +307,7 @@ mod aio_write { si_value: 99, }, ); - assert_eq!(1001, aiocb.fd()); + assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd()); assert_eq!(4, aiocb.nbytes()); assert_eq!(2, aiocb.offset()); assert_eq!(42, aiocb.priority()); @@ -327,7 +326,7 @@ mod aio_write { let f = tempfile().unwrap(); let mut aiow = Box::pin(AioWrite::new( - f.as_raw_fd(), + f.as_fd(), 0, wbuf, 0, @@ -356,18 +355,20 @@ mod aio_write { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); - let mut aiow = Box::pin(AioWrite::new( - f.as_raw_fd(), - 2, - &wbuf, - 0, - SigevNotify::SigevNone, - )); - aiow.as_mut().submit().unwrap(); + { + let mut aiow = Box::pin(AioWrite::new( + f.as_fd(), + 2, + &wbuf, + 0, + SigevNotify::SigevNone, + )); + aiow.as_mut().submit().unwrap(); - let err = poll_aio!(&mut aiow); - assert_eq!(err, Ok(())); - assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len()); + let err = poll_aio!(&mut aiow); + assert_eq!(err, Ok(())); + assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len()); + } f.rewind().unwrap(); let len = f.read_to_end(&mut rbuf).unwrap(); @@ -386,19 +387,21 @@ mod aio_write { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); - let mut aiow = AioWrite::new( - f.as_raw_fd(), - 2, //offset - &wbuf, - 0, //priority - SigevNotify::SigevNone, - ); - let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) }; - aiow.as_mut().submit().unwrap(); + { + let mut aiow = AioWrite::new( + f.as_fd(), + 2, //offset + &wbuf, + 0, //priority + SigevNotify::SigevNone, + ); + let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) }; + aiow.as_mut().submit().unwrap(); - let err = poll_aio!(&mut aiow); - assert_eq!(err, Ok(())); - assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len()); + let err = poll_aio!(&mut aiow); + assert_eq!(err, Ok(())); + assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len()); + } f.rewind().unwrap(); let len = f.read_to_end(&mut rbuf).unwrap(); @@ -411,12 +414,14 @@ mod aio_write { // Skip on Linux, because Linux's AIO implementation can't detect errors // synchronously #[test] - #[cfg(any(target_os = "freebsd", apple_targets))] + #[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)] fn error() { + // Not I/O safe! Deliberately create an invalid fd. + let fd = unsafe { BorrowedFd::borrow_raw(666) }; let wbuf = "CDEF".to_string().into_bytes(); let mut aiow = Box::pin(AioWrite::new( - 666, // An invalid file descriptor - 0, //offset + fd, + 0, //offset &wbuf, 0, //priority SigevNotify::SigevNone, @@ -435,11 +440,12 @@ mod aio_writev { #[test] fn test_accessors() { + let f = tempfile().unwrap(); let wbuf0 = vec![0; 4]; let wbuf1 = vec![0; 8]; let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)]; let aiocb = AioWritev::new( - 1001, + f.as_fd(), 2, //offset &wbufs, 42, //priority @@ -448,7 +454,7 @@ mod aio_writev { si_value: 99, }, ); - assert_eq!(1001, aiocb.fd()); + assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd()); assert_eq!(2, aiocb.iovlen()); assert_eq!(2, aiocb.offset()); assert_eq!(42, aiocb.priority()); @@ -472,18 +478,20 @@ mod aio_writev { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); - let mut aiow = Box::pin(AioWritev::new( - f.as_raw_fd(), - 1, - &wbufs, - 0, - SigevNotify::SigevNone, - )); - aiow.as_mut().submit().unwrap(); + { + let mut aiow = Box::pin(AioWritev::new( + f.as_fd(), + 1, + &wbufs, + 0, + SigevNotify::SigevNone, + )); + aiow.as_mut().submit().unwrap(); - let err = poll_aio!(&mut aiow); - assert_eq!(err, Ok(())); - assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen); + let err = poll_aio!(&mut aiow); + assert_eq!(err, Ok(())); + assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen); + } f.rewind().unwrap(); let len = f.read_to_end(&mut rbuf).unwrap(); @@ -521,22 +529,25 @@ fn sigev_signal() { let mut f = tempfile().unwrap(); f.write_all(INITIAL).unwrap(); - let mut aiow = Box::pin(AioWrite::new( - f.as_raw_fd(), - 2, //offset - WBUF, - 0, //priority - SigevNotify::SigevSignal { - signal: Signal::SIGUSR2, - si_value: 0, //TODO: validate in sigfunc - }, - )); - aiow.as_mut().submit().unwrap(); - while !SIGNALED.load(Ordering::Relaxed) { - thread::sleep(time::Duration::from_millis(10)); + { + let mut aiow = Box::pin(AioWrite::new( + f.as_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevSignal { + signal: Signal::SIGUSR2, + si_value: 0, //TODO: validate in sigfunc + }, + )); + aiow.as_mut().submit().unwrap(); + while !SIGNALED.load(Ordering::Relaxed) { + thread::sleep(time::Duration::from_millis(10)); + } + + assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len()); } - assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len()); f.rewind().unwrap(); let len = f.read_to_end(&mut rbuf).unwrap(); assert_eq!(len, EXPECT.len()); @@ -551,7 +562,7 @@ fn test_aio_cancel_all() { let f = tempfile().unwrap(); let mut aiocb = Box::pin(AioWrite::new( - f.as_raw_fd(), + f.as_fd(), 0, //offset wbuf, 0, //priority @@ -561,7 +572,7 @@ fn test_aio_cancel_all() { let err = aiocb.as_mut().error(); assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS)); - aio_cancel_all(f.as_raw_fd()).unwrap(); + aio_cancel_all(f.as_fd()).unwrap(); // Wait for aiocb to complete, but don't care whether it succeeded let _ = poll_aio!(&mut aiocb); @@ -579,7 +590,7 @@ fn test_aio_suspend() { f.write_all(INITIAL).unwrap(); let mut wcb = Box::pin(AioWrite::new( - f.as_raw_fd(), + f.as_fd(), 2, //offset WBUF, 0, //priority @@ -587,7 +598,7 @@ fn test_aio_suspend() { )); let mut rcb = Box::pin(AioRead::new( - f.as_raw_fd(), + f.as_fd(), 8, //offset &mut rbuf, 0, //priority @@ -624,21 +635,23 @@ fn test_aio_suspend() { #[test] fn casting() { let sev = SigevNotify::SigevNone; - let aiof = AioFsync::new(666, AioFsyncMode::O_SYNC, 0, sev); + // Only safe because we'll never await the futures + let fd = unsafe { BorrowedFd::borrow_raw(666) }; + let aiof = AioFsync::new(fd, AioFsyncMode::O_SYNC, 0, sev); assert_eq!( aiof.as_ref() as *const libc::aiocb, &aiof as *const AioFsync as *const libc::aiocb ); let mut rbuf = []; - let aior = AioRead::new(666, 0, &mut rbuf, 0, sev); + let aior = AioRead::new(fd, 0, &mut rbuf, 0, sev); assert_eq!( aior.as_ref() as *const libc::aiocb, &aior as *const AioRead as *const libc::aiocb ); let wbuf = []; - let aiow = AioWrite::new(666, 0, &wbuf, 0, sev); + let aiow = AioWrite::new(fd, 0, &wbuf, 0, sev); assert_eq!( aiow.as_ref() as *const libc::aiocb, &aiow as *const AioWrite as *const libc::aiocb @@ -654,7 +667,9 @@ fn casting_vectored() { let mut rbuf = []; let mut rbufs = [IoSliceMut::new(&mut rbuf)]; - let aiorv = AioReadv::new(666, 0, &mut rbufs[..], 0, sev); + // Only safe because we'll never await the futures + let fd = unsafe { BorrowedFd::borrow_raw(666) }; + let aiorv = AioReadv::new(fd, 0, &mut rbufs[..], 0, sev); assert_eq!( aiorv.as_ref() as *const libc::aiocb, &aiorv as *const AioReadv as *const libc::aiocb @@ -662,7 +677,7 @@ fn casting_vectored() { let wbuf = []; let wbufs = [IoSlice::new(&wbuf)]; - let aiowv = AioWritev::new(666, 0, &wbufs, 0, sev); + let aiowv = AioWritev::new(fd, 0, &wbufs, 0, sev); assert_eq!( aiowv.as_ref() as *const libc::aiocb, &aiowv as *const AioWritev as *const libc::aiocb diff --git a/test/sys/test_aio_drop.rs b/test/sys/test_aio_drop.rs index 54106dd168..47660cf62c 100644 --- a/test/sys/test_aio_drop.rs +++ b/test/sys/test_aio_drop.rs @@ -16,7 +16,7 @@ fn test_drop() { use nix::sys::aio::*; use nix::sys::signal::*; - use std::os::unix::io::AsRawFd; + use std::os::unix::io::AsFd; use tempfile::tempfile; const WBUF: &[u8] = b"CDEF"; @@ -24,7 +24,7 @@ fn test_drop() { let f = tempfile().unwrap(); f.set_len(6).unwrap(); let mut aiocb = Box::pin(AioWrite::new( - f.as_raw_fd(), + f.as_fd(), 2, //offset WBUF, 0, //priority From f577c2023ac012467ce8108bb5ac3bf58617568c Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 15 May 2024 18:59:52 -0600 Subject: [PATCH 54/64] Remove an unnecessary cast from aio_suspend's doc test (#2405) Reported by: @stevelauc --- src/sys/aio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/aio.rs b/src/sys/aio.rs index 71952aa05f..8d9e6c8061 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -1047,7 +1047,7 @@ pub fn aio_cancel_all(fd: F) -> Result { /// SigevNotify::SigevNone)); /// aiocb.as_mut().submit().unwrap(); /// aio_suspend(&[&*aiocb], None).expect("aio_suspend failed"); -/// assert_eq!(aiocb.as_mut().aio_return().unwrap() as usize, WBUF.len()); +/// assert_eq!(aiocb.as_mut().aio_return().unwrap(), WBUF.len()); /// ``` /// # References /// From e7acaff07f39ece39eae38a45c82810bfbbf58cd Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Wed, 15 May 2024 19:00:47 -0600 Subject: [PATCH 55/64] Enable O_DSYNC on FreeBSD with fcntl and aio_fsync (#2404) It first appeared in FreeBSD 13.0. --- changelog/2404.added.md | 1 + src/fcntl.rs | 2 +- src/sys/aio.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelog/2404.added.md diff --git a/changelog/2404.added.md b/changelog/2404.added.md new file mode 100644 index 0000000000..7f50f5bb01 --- /dev/null +++ b/changelog/2404.added.md @@ -0,0 +1 @@ +`O_DSYNC` may now be used with `aio_fsync` and `fcntl` on FreeBSD. diff --git a/src/fcntl.rs b/src/fcntl.rs index 043cdde473..fd8d4b847c 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -114,7 +114,7 @@ libc_bitflags!( /// If the specified path isn't a directory, fail. O_DIRECTORY; /// Implicitly follow each `write()` with an `fdatasync()`. - #[cfg(any(linux_android, apple_targets, netbsdlike))] + #[cfg(any(linux_android, apple_targets, target_os = "freebsd", netbsdlike))] O_DSYNC; /// Error out if a file was not created. O_EXCL; diff --git a/src/sys/aio.rs b/src/sys/aio.rs index 8d9e6c8061..c7ba40534c 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -55,6 +55,7 @@ libc_enum! { /// on supported operating systems only, do it like `fdatasync` #[cfg(any(apple_targets, target_os = "linux", + target_os = "freebsd", netbsdlike))] O_DSYNC } From c5af4adffd876605002e3a8a22b53fe9c83206d3 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 16 May 2024 00:09:19 -0600 Subject: [PATCH 56/64] Add Flock::relock (#2407) It can upgrade or downgrade a lock. Fixes #2356 --- changelog/2407.added.md | 1 + src/fcntl.rs | 24 +++++++++++++++++++ test/test_fcntl.rs | 52 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 changelog/2407.added.md diff --git a/changelog/2407.added.md b/changelog/2407.added.md new file mode 100644 index 0000000000..26e2cd2a21 --- /dev/null +++ b/changelog/2407.added.md @@ -0,0 +1 @@ +Added `Flock::relock` for upgrading and downgrading locks. diff --git a/src/fcntl.rs b/src/fcntl.rs index fd8d4b847c..cf87926c7b 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -955,6 +955,30 @@ impl Flock { std::mem::forget(self); Ok(inner) } + + /// Relock the file. This can upgrade or downgrade the lock type. + /// + /// # Example + /// ``` + /// # use std::fs::File; + /// # use nix::fcntl::{Flock, FlockArg}; + /// # use tempfile::tempfile; + /// let f: std::fs::File = tempfile().unwrap(); + /// let locked_file = Flock::lock(f, FlockArg::LockExclusive).unwrap(); + /// // Do stuff, then downgrade the lock + /// locked_file.relock(FlockArg::LockShared).unwrap(); + /// ``` + pub fn relock(&self, arg: FlockArg) -> Result<()> { + let flags = match arg { + FlockArg::LockShared => libc::LOCK_SH, + FlockArg::LockExclusive => libc::LOCK_EX, + FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB, + FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB, + #[allow(deprecated)] + FlockArg::Unlock | FlockArg::UnlockNonblock => return Err(Errno::EINVAL), + }; + Errno::result(unsafe { libc::flock(self.as_raw_fd(), flags) }).map(drop) + } } // Safety: `File` is not [std::clone::Clone]. diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index 2131f46c5c..5d320769d3 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -686,7 +686,7 @@ mod test_flock { /// Verify that `Flock::lock()` correctly obtains a lock, and subsequently unlocks upon drop. #[test] - fn verify_lock_and_drop() { + fn lock_and_drop() { // Get 2 `File` handles to same underlying file. let file1 = NamedTempFile::new().unwrap(); let file2 = file1.reopen().unwrap(); @@ -710,9 +710,32 @@ mod test_flock { } } + /// An exclusive lock can be downgraded + #[test] + fn downgrade() { + let file1 = NamedTempFile::new().unwrap(); + let file2 = file1.reopen().unwrap(); + let file1 = file1.into_file(); + + // Lock first handle + let lock1 = Flock::lock(file1, FlockArg::LockExclusive).unwrap(); + + // Attempt to lock second handle + let file2 = Flock::lock(file2, FlockArg::LockSharedNonblock) + .unwrap_err() + .0; + + // Downgrade the lock + lock1.relock(FlockArg::LockShared).unwrap(); + + // Attempt to lock second handle again (but successfully) + Flock::lock(file2, FlockArg::LockSharedNonblock) + .expect("Expected locking to be successful."); + } + /// Verify that `Flock::unlock()` correctly obtains unlocks. #[test] - fn verify_unlock() { + fn unlock() { // Get 2 `File` handles to same underlying file. let file1 = NamedTempFile::new().unwrap(); let file2 = file1.reopen().unwrap(); @@ -729,4 +752,29 @@ mod test_flock { panic!("Expected locking to be successful."); } } + + /// A shared lock can be upgraded + #[test] + fn upgrade() { + let file1 = NamedTempFile::new().unwrap(); + let file2 = file1.reopen().unwrap(); + let file3 = file1.reopen().unwrap(); + let file1 = file1.into_file(); + + // Lock first handle + let lock1 = Flock::lock(file1, FlockArg::LockShared).unwrap(); + + // Attempt to lock second handle + { + Flock::lock(file2, FlockArg::LockSharedNonblock) + .expect("Locking should've succeeded"); + } + + // Upgrade the lock + lock1.relock(FlockArg::LockExclusive).unwrap(); + + // Acquiring an additional shared lock should fail + Flock::lock(file3, FlockArg::LockSharedNonblock) + .expect_err("Should not have been able to lock the file"); + } } From 84c0444c3a957b8bffd5172848d4ea73eb06c3ff Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 18 May 2024 15:50:18 +0800 Subject: [PATCH 57/64] chore: bump libc to 0.2.155 (#2409) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c6a69b4654..de561291b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ targets = [ ] [dependencies] -libc = { version = "0.2.154", features = ["extra_traits"] } +libc = { version = "0.2.155", features = ["extra_traits"] } bitflags = "2.3.1" cfg-if = "1.0" pin-utils = { version = "0.1.0", optional = true } From ec4beb5a2229159021298d56af16e18a9151fb4e Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sat, 18 May 2024 16:08:28 +0800 Subject: [PATCH 58/64] docs: correct limit value of FAN_UNLIMITED_QUEUE and FAN_UNLIMITED_MARKS[skip ci] (#2408) --- src/sys/fanotify.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index 9ff306017d..a58bd5489d 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -96,9 +96,22 @@ libc_bitflags! { /// final data. FAN_CLASS_PRE_CONTENT; - /// Remove the limit of 16384 events for the event queue. + /// Remove the limit on the number of events in the event queue. + /// + /// Prior to Linux kernel 5.13, this limit was hardcoded to 16384. After + /// 5.13, one can change it via file `/proc/sys/fs/fanotify/max_queued_events`. + /// + /// See `fanotify(7)` for details about this limit. Use of this flag + /// requires the `CAP_SYS_ADMIN` capability. FAN_UNLIMITED_QUEUE; - /// Remove the limit of 8192 marks. + /// Remove the limit on the number of fanotify marks per user. + /// + /// Prior to Linux kernel 5.13, this limit was hardcoded to 8192 (per + /// group, not per user). After 5.13, one can change it via file + /// `/proc/sys/fs/fanotify/max_user_marks`. + /// + /// See `fanotify(7)` for details about this limit. Use of this flag + /// requires the `CAP_SYS_ADMIN` capability. FAN_UNLIMITED_MARKS; /// Make `FanotifyEvent::pid` return pidfd. Since Linux 5.15. From 1604723757735e59bf3142209b22b250b7412d40 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Sun, 19 May 2024 09:52:49 +0800 Subject: [PATCH 59/64] revert: impl From for SigAction (#2410) --- changelog/2326.added.md | 2 +- src/sys/signal.rs | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/changelog/2326.added.md b/changelog/2326.added.md index bcc29cb3db..fef5ebe721 100644 --- a/changelog/2326.added.md +++ b/changelog/2326.added.md @@ -1 +1 @@ -make SigAction repr(transparent) & can be converted to/from the libc raw type +make SigAction repr(transparent) & can be converted to the libc raw type diff --git a/src/sys/signal.rs b/src/sys/signal.rs index 8f41127e27..921fb28d6f 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -759,13 +759,6 @@ pub struct SigAction { sigaction: libc::sigaction } -impl From for SigAction { - fn from(value: libc::sigaction) -> Self { - Self { - sigaction: value - } - } -} impl From for libc::sigaction { fn from(value: SigAction) -> libc::sigaction { value.sigaction From 663506a602251675c66efcd9d25a2dfc3c2c61da Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 19 May 2024 17:06:38 +0800 Subject: [PATCH 60/64] fix: only close `fanotify` events with a valid fd (#2399) * fix: only close fanotify events with a valid fd * add changelog entry * add a test * make the test pass clippy --- changelog/2399.fixed.md | 1 + src/sys/fanotify.rs | 3 ++ test/sys/test_fanotify.rs | 72 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 changelog/2399.fixed.md diff --git a/changelog/2399.fixed.md b/changelog/2399.fixed.md new file mode 100644 index 0000000000..e6e0fe044a --- /dev/null +++ b/changelog/2399.fixed.md @@ -0,0 +1 @@ +No longer panics when the `fanotify` queue overflows. diff --git a/src/sys/fanotify.rs b/src/sys/fanotify.rs index a58bd5489d..e22c52753d 100644 --- a/src/sys/fanotify.rs +++ b/src/sys/fanotify.rs @@ -249,6 +249,9 @@ impl FanotifyEvent { impl Drop for FanotifyEvent { fn drop(&mut self) { + if self.0.fd == libc::FAN_NOFD { + return; + } let e = close(self.0.fd); if !std::thread::panicking() && e == Err(Errno::EBADF) { panic!("Closing an invalid file descriptor!"); diff --git a/test/sys/test_fanotify.rs b/test/sys/test_fanotify.rs index 20226c272a..13ec945913 100644 --- a/test/sys/test_fanotify.rs +++ b/test/sys/test_fanotify.rs @@ -1,9 +1,10 @@ use crate::*; +use nix::errno::Errno; use nix::sys::fanotify::{ EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags, Response, }; -use std::fs::{read_link, File, OpenOptions}; +use std::fs::{read_link, read_to_string, File, OpenOptions}; use std::io::ErrorKind; use std::io::{Read, Write}; use std::os::fd::AsRawFd; @@ -16,6 +17,7 @@ pub fn test_fanotify() { test_fanotify_notifications(); test_fanotify_responses(); + test_fanotify_overflow(); } fn test_fanotify_notifications() { @@ -147,3 +149,71 @@ fn test_fanotify_responses() { file_thread.join().unwrap(); } + +fn test_fanotify_overflow() { + let max_events: usize = + read_to_string("/proc/sys/fs/fanotify/max_queued_events") + .unwrap() + .trim() + .parse() + .unwrap(); + + // make sure the kernel is configured with the default value, + // just so this test doesn't run forever + assert_eq!(max_events, 16384); + + let group = Fanotify::init( + InitFlags::FAN_CLASS_NOTIF + | InitFlags::FAN_REPORT_TID + | InitFlags::FAN_NONBLOCK, + EventFFlags::O_RDONLY, + ) + .unwrap(); + let tempdir = tempfile::tempdir().unwrap(); + let tempfile = tempdir.path().join("test"); + + OpenOptions::new() + .write(true) + .create_new(true) + .open(&tempfile) + .unwrap(); + + group + .mark( + MarkFlags::FAN_MARK_ADD, + MaskFlags::FAN_OPEN, + None, + Some(&tempfile), + ) + .unwrap(); + + thread::scope(|s| { + // perform 10 more events to demonstrate some will be dropped + for _ in 0..(max_events + 10) { + s.spawn(|| { + File::open(&tempfile).unwrap(); + }); + } + }); + + // flush the queue until it's empty + let mut n = 0; + let mut last_event = None; + loop { + match group.read_events() { + Ok(events) => { + n += events.len(); + if let Some(event) = events.last() { + last_event = Some(event.mask()); + } + } + Err(e) if e == Errno::EWOULDBLOCK => break, + Err(e) => panic!("{e:?}"), + } + } + + // make sure we read all we expected. + // the +1 is for the overflow event. + assert_eq!(n, max_events + 1); + assert_eq!(last_event, Some(MaskFlags::FAN_Q_OVERFLOW)); +} From ecd12a99907d6b1c9f43e88c087b1c6a2f633750 Mon Sep 17 00:00:00 2001 From: SteveLauC Date: Wed, 22 May 2024 22:28:07 +0800 Subject: [PATCH 61/64] test: remove test of inode count in test_statfs.rs (#2414) * test: remove test of inode count in test_statfs.rs * revert: bring the test in assert_fs_equals_strict back as it is ignored --- test/sys/test_statfs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/sys/test_statfs.rs b/test/sys/test_statfs.rs index 66b3f2ce96..ca7934e6ed 100644 --- a/test/sys/test_statfs.rs +++ b/test/sys/test_statfs.rs @@ -44,7 +44,6 @@ fn check_statfs_strict(path: &str) { // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] fn assert_fs_equals(fs: Statfs, vfs: Statvfs) { - assert_eq!(fs.files() as u64, vfs.files() as u64); assert_eq!(fs.blocks() as u64, vfs.blocks() as u64); assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64); } From 208b80b65d9a54bac3172b97af81cfe90dd6412b Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Wed, 22 May 2024 16:04:34 -0700 Subject: [PATCH 62/64] recvmsg: Check if CMSG buffer was too small and return an error (#2413) If MSG_CTRUNC is set, it is not safe to iterate the cmsgs, since they could have been truncated. Change RecvMsg::cmsgs() to return a Result, and to check for this flag (an API change). Update tests for API change. Add test for too-small buffer. --- changelog/2413.changed.md | 1 + src/sys/socket/mod.rs | 19 ++++++++---- test/sys/test_socket.rs | 64 ++++++++++++++++++++++++--------------- 3 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 changelog/2413.changed.md diff --git a/changelog/2413.changed.md b/changelog/2413.changed.md new file mode 100644 index 0000000000..7bae72f7d8 --- /dev/null +++ b/changelog/2413.changed.md @@ -0,0 +1 @@ +`RecvMsg::cmsgs()` now returns a `Result`, and checks that cmsgs were not truncated. diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 3d1651bd3f..10afacf02d 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -13,6 +13,7 @@ use libc::{self, c_int, size_t, socklen_t}; #[cfg(all(feature = "uio", not(target_os = "redox")))] use libc::{ c_void, iovec, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE, + MSG_CTRUNC, }; #[cfg(not(target_os = "redox"))] use std::io::{IoSlice, IoSliceMut}; @@ -599,13 +600,19 @@ pub struct RecvMsg<'a, 's, S> { } impl<'a, S> RecvMsg<'a, '_, S> { - /// Iterate over the valid control messages pointed to by this - /// msghdr. - pub fn cmsgs(&self) -> CmsgIterator { - CmsgIterator { + /// Iterate over the valid control messages pointed to by this msghdr. If + /// allocated space for CMSGs was too small it is not safe to iterate, + /// instead return an `Error::ENOBUFS` error. + pub fn cmsgs(&self) -> Result { + + if self.mhdr.msg_flags & MSG_CTRUNC == MSG_CTRUNC { + return Err(Errno::ENOBUFS); + } + + Ok(CmsgIterator { cmsghdr: self.cmsghdr, mhdr: &self.mhdr - } + }) } } @@ -700,7 +707,7 @@ pub enum ControlMessageOwned { /// let mut iov = [IoSliceMut::new(&mut buffer)]; /// let r = recvmsg::(in_socket.as_raw_fd(), &mut iov, Some(&mut cmsgspace), flags) /// .unwrap(); - /// let rtime = match r.cmsgs().next() { + /// let rtime = match r.cmsgs().unwrap().next() { /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime, /// Some(_) => panic!("Unexpected control message"), /// None => panic!("No control message") diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index ee60e62b45..79c97c8720 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -55,7 +55,7 @@ pub fn test_timestamping() { .unwrap(); let mut ts = None; - for c in recv.cmsgs() { + for c in recv.cmsgs().unwrap() { if let ControlMessageOwned::ScmTimestampsns(timestamps) = c { ts = Some(timestamps.system); } @@ -117,7 +117,7 @@ pub fn test_timestamping_realtime() { .unwrap(); let mut ts = None; - for c in recv.cmsgs() { + for c in recv.cmsgs().unwrap() { if let ControlMessageOwned::ScmRealtime(timeval) = c { ts = Some(timeval); } @@ -179,7 +179,7 @@ pub fn test_timestamping_monotonic() { .unwrap(); let mut ts = None; - for c in recv.cmsgs() { + for c in recv.cmsgs().unwrap() { if let ControlMessageOwned::ScmMonotonic(timeval) = c { ts = Some(timeval); } @@ -889,7 +889,7 @@ pub fn test_scm_rights() { ) .unwrap(); - for cmsg in msg.cmsgs() { + for cmsg in msg.cmsgs().unwrap() { if let ControlMessageOwned::ScmRights(fd) = cmsg { assert_eq!(received_r, None); assert_eq!(fd.len(), 1); @@ -1330,7 +1330,7 @@ fn test_scm_rights_single_cmsg_multiple_fds() { .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - let mut cmsgs = msg.cmsgs(); + let mut cmsgs = msg.cmsgs().unwrap(); match cmsgs.next() { Some(ControlMessageOwned::ScmRights(fds)) => { assert_eq!( @@ -1399,7 +1399,7 @@ pub fn test_sendmsg_empty_cmsgs() { ) .unwrap(); - if msg.cmsgs().next().is_some() { + if msg.cmsgs().unwrap().next().is_some() { panic!("unexpected cmsg"); } assert!(!msg @@ -1466,7 +1466,7 @@ fn test_scm_credentials() { .unwrap(); let mut received_cred = None; - for cmsg in msg.cmsgs() { + for cmsg in msg.cmsgs().unwrap() { let cred = match cmsg { #[cfg(linux_android)] ControlMessageOwned::ScmCredentials(cred) => cred, @@ -1497,7 +1497,7 @@ fn test_scm_credentials() { #[test] fn test_scm_credentials_and_rights() { let space = cmsg_space!(libc::ucred, RawFd); - test_impl_scm_credentials_and_rights(space); + test_impl_scm_credentials_and_rights(space).unwrap(); } /// Ensure that passing a an oversized control message buffer to recvmsg @@ -1509,11 +1509,23 @@ fn test_scm_credentials_and_rights() { #[test] fn test_too_large_cmsgspace() { let space = vec![0u8; 1024]; - test_impl_scm_credentials_and_rights(space); + test_impl_scm_credentials_and_rights(space).unwrap(); } #[cfg(linux_android)] -fn test_impl_scm_credentials_and_rights(mut space: Vec) { +#[test] +fn test_too_small_cmsgspace() { + let space = vec![0u8; 4]; + assert_eq!( + test_impl_scm_credentials_and_rights(space), + Err(nix::errno::Errno::ENOBUFS) + ); +} + +#[cfg(linux_android)] +fn test_impl_scm_credentials_and_rights( + mut space: Vec, +) -> Result<(), nix::errno::Errno> { use libc::ucred; use nix::sys::socket::sockopt::PassCred; use nix::sys::socket::{ @@ -1573,9 +1585,9 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { .unwrap(); let mut received_cred = None; - assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); + assert_eq!(msg.cmsgs()?.count(), 2, "expected 2 cmsgs"); - for cmsg in msg.cmsgs() { + for cmsg in msg.cmsgs()? { match cmsg { ControlMessageOwned::ScmRights(fds) => { assert_eq!(received_r, None, "already received fd"); @@ -1606,6 +1618,8 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { read(received_r.as_raw_fd(), &mut buf).unwrap(); assert_eq!(&buf[..], b"world"); close(received_r).unwrap(); + + Ok(()) } // Test creating and using named unix domain sockets @@ -1837,7 +1851,7 @@ pub fn test_recv_ipv4pktinfo() { .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - let mut cmsgs = msg.cmsgs(); + let mut cmsgs = msg.cmsgs().unwrap(); if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next() { let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); @@ -1929,11 +1943,11 @@ pub fn test_recvif() { assert!(!msg .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); + assert_eq!(msg.cmsgs().unwrap().count(), 2, "expected 2 cmsgs"); let mut rx_recvif = false; let mut rx_recvdstaddr = false; - for cmsg in msg.cmsgs() { + for cmsg in msg.cmsgs().unwrap() { match cmsg { ControlMessageOwned::Ipv4RecvIf(dl) => { rx_recvif = true; @@ -2027,10 +2041,10 @@ pub fn test_recvif_ipv4() { assert!(!msg .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs"); + assert_eq!(msg.cmsgs().unwrap().count(), 1, "expected 1 cmsgs"); let mut rx_recvorigdstaddr = false; - for cmsg in msg.cmsgs() { + for cmsg in msg.cmsgs().unwrap() { match cmsg { ControlMessageOwned::Ipv4OrigDstAddr(addr) => { rx_recvorigdstaddr = true; @@ -2113,10 +2127,10 @@ pub fn test_recvif_ipv6() { assert!(!msg .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs"); + assert_eq!(msg.cmsgs().unwrap().count(), 1, "expected 1 cmsgs"); let mut rx_recvorigdstaddr = false; - for cmsg in msg.cmsgs() { + for cmsg in msg.cmsgs().unwrap() { match cmsg { ControlMessageOwned::Ipv6OrigDstAddr(addr) => { rx_recvorigdstaddr = true; @@ -2214,7 +2228,7 @@ pub fn test_recv_ipv6pktinfo() { .flags .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - let mut cmsgs = msg.cmsgs(); + let mut cmsgs = msg.cmsgs().unwrap(); if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next() { let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); @@ -2357,7 +2371,7 @@ fn test_recvmsg_timestampns() { flags, ) .unwrap(); - let rtime = match r.cmsgs().next() { + let rtime = match r.cmsgs().unwrap().next() { Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, Some(_) => panic!("Unexpected control message"), None => panic!("No control message"), @@ -2418,7 +2432,7 @@ fn test_recvmmsg_timestampns() { ) .unwrap() .collect(); - let rtime = match r[0].cmsgs().next() { + let rtime = match r[0].cmsgs().unwrap().next() { Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, Some(_) => panic!("Unexpected control message"), None => panic!("No control message"), @@ -2508,7 +2522,7 @@ fn test_recvmsg_rxq_ovfl() { MsgFlags::MSG_DONTWAIT, ) { Ok(r) => { - drop_counter = match r.cmsgs().next() { + drop_counter = match r.cmsgs().unwrap().next() { Some(ControlMessageOwned::RxqOvfl(drop_counter)) => { drop_counter } @@ -2687,7 +2701,7 @@ mod linux_errqueue { assert_eq!(msg.address, Some(sock_addr)); // Check for expected control message. - let ext_err = match msg.cmsgs().next() { + let ext_err = match msg.cmsgs().unwrap().next() { Some(cmsg) => testf(&cmsg), None => panic!("No control message"), }; @@ -2878,7 +2892,7 @@ fn test_recvmm2() -> nix::Result<()> { #[cfg(not(any(qemu, target_arch = "aarch64")))] let mut saw_time = false; let mut recvd = 0; - for cmsg in rmsg.cmsgs() { + for cmsg in rmsg.cmsgs().unwrap() { if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg { let ts = timestamps.system; From f7431971b40f9516e6c8d280db353fd55d7ac7d7 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 23 May 2024 00:13:35 +0100 Subject: [PATCH 63/64] fix ControlMessageOwned::UdpGroSegments UDP packets processing type. (#2406) * fix ControlMessageOwned::UdpGroSegments UDP packets processing type. The kernel, since 5.0, process the segmentation offload with a signed int. close #2403. * changelog --- changelog/2406.fixed.md | 1 + src/sys/socket/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog/2406.fixed.md diff --git a/changelog/2406.fixed.md b/changelog/2406.fixed.md new file mode 100644 index 0000000000..41bc9c7789 --- /dev/null +++ b/changelog/2406.fixed.md @@ -0,0 +1 @@ +Fixed ControlMessageOwned::UdpGroSegments wrapped type from u16 to i32 to reflect the used kernel's one. diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 10afacf02d..1f1869e90d 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -780,7 +780,7 @@ pub enum ControlMessageOwned { #[cfg(target_os = "linux")] #[cfg(feature = "net")] #[cfg_attr(docsrs, doc(cfg(feature = "net")))] - UdpGroSegments(u16), + UdpGroSegments(i32), /// SO_RXQ_OVFL indicates that an unsigned 32 bit value /// ancilliary msg (cmsg) should be attached to recieved @@ -956,7 +956,7 @@ impl ControlMessageOwned { #[cfg(target_os = "linux")] #[cfg(feature = "net")] (libc::SOL_UDP, libc::UDP_GRO) => { - let gso_size: u16 = unsafe { ptr::read_unaligned(p as *const _) }; + let gso_size: i32 = unsafe { ptr::read_unaligned(p as *const _) }; ControlMessageOwned::UdpGroSegments(gso_size) }, #[cfg(any(linux_android, target_os = "fuchsia"))] From 1dad4d8d04a2cd187fae87cb91c4f4e95ff0decd Mon Sep 17 00:00:00 2001 From: Steve Lau Date: Fri, 24 May 2024 20:22:21 +0800 Subject: [PATCH 64/64] chore: prepare for 0.29.0 --- CHANGELOG.md | 92 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- changelog/2044.added.md | 2 - changelog/2287.added.md | 1 - changelog/2315.changed.md | 12 ----- changelog/2322.changed.md | 1 - changelog/2324.changed.md | 1 - changelog/2325.added.md | 1 - changelog/2326.added.md | 1 - changelog/2328.added.md | 1 - changelog/2332.added.md | 1 - changelog/2339.added.md | 1 - changelog/2340.added.md | 1 - changelog/2347.added.md | 1 - changelog/2349.added.md | 1 - changelog/2355.added.md | 1 - changelog/2367.changed.md | 5 --- changelog/2374.added.md | 1 - changelog/2378.added.md | 1 - changelog/2379.added.md | 1 - changelog/2380.added.md | 1 - changelog/2382.added.md | 1 - changelog/2387.changed.md | 1 - changelog/2388.added.md | 1 - changelog/2391.added.md | 1 - changelog/2399.fixed.md | 1 - changelog/2401.changed.md | 2 - changelog/2404.added.md | 1 - changelog/2406.fixed.md | 1 - changelog/2407.added.md | 1 - changelog/2413.changed.md | 1 - 31 files changed, 93 insertions(+), 47 deletions(-) delete mode 100644 changelog/2044.added.md delete mode 100644 changelog/2287.added.md delete mode 100644 changelog/2315.changed.md delete mode 100644 changelog/2322.changed.md delete mode 100644 changelog/2324.changed.md delete mode 100644 changelog/2325.added.md delete mode 100644 changelog/2326.added.md delete mode 100644 changelog/2328.added.md delete mode 100644 changelog/2332.added.md delete mode 100644 changelog/2339.added.md delete mode 100644 changelog/2340.added.md delete mode 100644 changelog/2347.added.md delete mode 100644 changelog/2349.added.md delete mode 100644 changelog/2355.added.md delete mode 100644 changelog/2367.changed.md delete mode 100644 changelog/2374.added.md delete mode 100644 changelog/2378.added.md delete mode 100644 changelog/2379.added.md delete mode 100644 changelog/2380.added.md delete mode 100644 changelog/2382.added.md delete mode 100644 changelog/2387.changed.md delete mode 100644 changelog/2388.added.md delete mode 100644 changelog/2391.added.md delete mode 100644 changelog/2399.fixed.md delete mode 100644 changelog/2401.changed.md delete mode 100644 changelog/2404.added.md delete mode 100644 changelog/2406.fixed.md delete mode 100644 changelog/2407.added.md delete mode 100644 changelog/2413.changed.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 37e4ab2d4c..b673cac562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,98 @@ This project adheres to [Semantic Versioning](https://semver.org/). # Change Log +## [0.29.0] - 2024-05-24 + + +### Added + +- Add `getregset()/setregset()` for Linux/glibc/x86/x86_64/aarch64/riscv64 and + `getregs()/setregs()` for Linux/glibc/aarch64/riscv64 + ([#2044](https://github.com/nix-rust/nix/pull/2044)) +- Add socket option Ipv6Ttl for apple targets. + ([#2287](https://github.com/nix-rust/nix/pull/2287)) +- Add socket option UtunIfname. + ([#2325](https://github.com/nix-rust/nix/pull/2325)) +- make SigAction repr(transparent) & can be converted to the libc raw type + ([#2326](https://github.com/nix-rust/nix/pull/2326)) +- Add `From` trait implementation for conversions between `sockaddr_in` and + `SockaddrIn`, `sockaddr_in6` and `SockaddrIn6` + ([#2328](https://github.com/nix-rust/nix/pull/2328)) +- Add socket option ReusePortLb for FreeBSD. + ([#2332](https://github.com/nix-rust/nix/pull/2332)) +- Added support for openat2 on linux. + ([#2339](https://github.com/nix-rust/nix/pull/2339)) +- Add if_indextoname function. + ([#2340](https://github.com/nix-rust/nix/pull/2340)) +- Add `mount` and `unmount` API for apple targets. + ([#2347](https://github.com/nix-rust/nix/pull/2347)) +- Added `_PC_MIN_HOLE_SIZE` for `pathconf` and `fpathconf`. + ([#2349](https://github.com/nix-rust/nix/pull/2349)) +- Added `impl AsFd for pty::PtyMaster` + ([#2355](https://github.com/nix-rust/nix/pull/2355)) +- Add `open` flag `O_SEARCH` to AIX, Empscripten, FreeBSD, Fuchsia, solarish, + WASI ([#2374](https://github.com/nix-rust/nix/pull/2374)) +- Add prctl function `prctl_set_vma_anon_name` for Linux/Android. + ([#2378](https://github.com/nix-rust/nix/pull/2378)) +- Add `sync(2)` for `apple_targets/solarish/haiku/aix/hurd`, `syncfs(2)` for + `hurd` and `fdatasync(2)` for `aix/hurd` + ([#2379](https://github.com/nix-rust/nix/pull/2379)) +- Add fdatasync support for Apple targets. + ([#2380](https://github.com/nix-rust/nix/pull/2380)) +- Add `fcntl::OFlag::O_PATH` for FreeBSD and Fuchsia + ([#2382](https://github.com/nix-rust/nix/pull/2382)) +- Added `PathconfVar::MIN_HOLE_SIZE` for apple_targets. + ([#2388](https://github.com/nix-rust/nix/pull/2388)) +- Add `open` flag `O_SEARCH` to apple_targets + ([#2391](https://github.com/nix-rust/nix/pull/2391)) +- `O_DSYNC` may now be used with `aio_fsync` and `fcntl` on FreeBSD. + ([#2404](https://github.com/nix-rust/nix/pull/2404)) +- Added `Flock::relock` for upgrading and downgrading locks. + ([#2407](https://github.com/nix-rust/nix/pull/2407)) + +### Changed + +- Change the `ForkptyResult` type to the following repr so that the + uninitialized + `master` field won't be accessed in the child process: + + ```rs + pub enum ForkptyResult { + Parent { + child: Pid, + master: OwnedFd, + }, + Child, + } + ``` ([#2315](https://github.com/nix-rust/nix/pull/2315)) +- Updated `cfg_aliases` dependency from version 0.1 to 0.2 + ([#2322](https://github.com/nix-rust/nix/pull/2322)) +- Change the signature of `ptrace::write` and `ptrace::write_user` to make them + safe ([#2324](https://github.com/nix-rust/nix/pull/2324)) +- Allow use of `SignalFd` through shared reference + + Like with many other file descriptors, concurrent use of signalfds is safe. + Changing the signal mask of and reading signals from a signalfd can now be + done + with the `SignalFd` API even if other references to it exist. + ([#2367](https://github.com/nix-rust/nix/pull/2367)) +- Changed tee, splice and vmsplice RawFd arguments to AsFd. + ([#2387](https://github.com/nix-rust/nix/pull/2387)) +- Added I/O safety to the sys/aio module. Most functions that previously + accepted a `AsRawFd` argument now accept an `AsFd` instead. + ([#2401](https://github.com/nix-rust/nix/pull/2401)) +- `RecvMsg::cmsgs()` now returns a `Result`, and checks that cmsgs were not + truncated. ([#2413](https://github.com/nix-rust/nix/pull/2413)) + +### Fixed + +- No longer panics when the `fanotify` queue overflows. + ([#2399](https://github.com/nix-rust/nix/pull/2399)) +- Fixed ControlMessageOwned::UdpGroSegments wrapped type from u16 to i32 to + reflect the used kernel's one. + ([#2406](https://github.com/nix-rust/nix/pull/2406)) + + ## [0.28.0] - 2024-02-24 diff --git a/Cargo.toml b/Cargo.toml index de561291b1..edf9973a13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "nix" description = "Rust friendly bindings to *nix APIs" edition = "2021" -version = "0.28.0" +version = "0.29.0" rust-version = "1.69" authors = ["The nix-rust Project Developers"] repository = "https://github.com/nix-rust/nix" diff --git a/changelog/2044.added.md b/changelog/2044.added.md deleted file mode 100644 index 95f79a755d..0000000000 --- a/changelog/2044.added.md +++ /dev/null @@ -1,2 +0,0 @@ -Add `getregset()/setregset()` for Linux/glibc/x86/x86_64/aarch64/riscv64 and -`getregs()/setregs()` for Linux/glibc/aarch64/riscv64 diff --git a/changelog/2287.added.md b/changelog/2287.added.md deleted file mode 100644 index a42a65bcf4..0000000000 --- a/changelog/2287.added.md +++ /dev/null @@ -1 +0,0 @@ -Add socket option Ipv6Ttl for apple targets. diff --git a/changelog/2315.changed.md b/changelog/2315.changed.md deleted file mode 100644 index bf437876d6..0000000000 --- a/changelog/2315.changed.md +++ /dev/null @@ -1,12 +0,0 @@ -Change the `ForkptyResult` type to the following repr so that the uninitialized -`master` field won't be accessed in the child process: - -```rs -pub enum ForkptyResult { - Parent { - child: Pid, - master: OwnedFd, - }, - Child, -} -``` diff --git a/changelog/2322.changed.md b/changelog/2322.changed.md deleted file mode 100644 index e84e912885..0000000000 --- a/changelog/2322.changed.md +++ /dev/null @@ -1 +0,0 @@ -Updated `cfg_aliases` dependency from version 0.1 to 0.2 diff --git a/changelog/2324.changed.md b/changelog/2324.changed.md deleted file mode 100644 index aca724850c..0000000000 --- a/changelog/2324.changed.md +++ /dev/null @@ -1 +0,0 @@ -Change the signature of `ptrace::write` and `ptrace::write_user` to make them safe \ No newline at end of file diff --git a/changelog/2325.added.md b/changelog/2325.added.md deleted file mode 100644 index 94dd68b00a..0000000000 --- a/changelog/2325.added.md +++ /dev/null @@ -1 +0,0 @@ -Add socket option UtunIfname. diff --git a/changelog/2326.added.md b/changelog/2326.added.md deleted file mode 100644 index fef5ebe721..0000000000 --- a/changelog/2326.added.md +++ /dev/null @@ -1 +0,0 @@ -make SigAction repr(transparent) & can be converted to the libc raw type diff --git a/changelog/2328.added.md b/changelog/2328.added.md deleted file mode 100644 index 2e3f440f50..0000000000 --- a/changelog/2328.added.md +++ /dev/null @@ -1 +0,0 @@ -Add `From` trait implementation for conversions between `sockaddr_in` and `SockaddrIn`, `sockaddr_in6` and `SockaddrIn6` diff --git a/changelog/2332.added.md b/changelog/2332.added.md deleted file mode 100644 index 3080e89cc0..0000000000 --- a/changelog/2332.added.md +++ /dev/null @@ -1 +0,0 @@ -Add socket option ReusePortLb for FreeBSD. diff --git a/changelog/2339.added.md b/changelog/2339.added.md deleted file mode 100644 index 0f80767d84..0000000000 --- a/changelog/2339.added.md +++ /dev/null @@ -1 +0,0 @@ -Added support for openat2 on linux. diff --git a/changelog/2340.added.md b/changelog/2340.added.md deleted file mode 100644 index 9d120d4879..0000000000 --- a/changelog/2340.added.md +++ /dev/null @@ -1 +0,0 @@ -Add if_indextoname function. diff --git a/changelog/2347.added.md b/changelog/2347.added.md deleted file mode 100644 index 9000d61deb..0000000000 --- a/changelog/2347.added.md +++ /dev/null @@ -1 +0,0 @@ -Add `mount` and `unmount` API for apple targets. diff --git a/changelog/2349.added.md b/changelog/2349.added.md deleted file mode 100644 index 277ded2c98..0000000000 --- a/changelog/2349.added.md +++ /dev/null @@ -1 +0,0 @@ -Added `_PC_MIN_HOLE_SIZE` for `pathconf` and `fpathconf`. diff --git a/changelog/2355.added.md b/changelog/2355.added.md deleted file mode 100644 index 663590262c..0000000000 --- a/changelog/2355.added.md +++ /dev/null @@ -1 +0,0 @@ -Added `impl AsFd for pty::PtyMaster` diff --git a/changelog/2367.changed.md b/changelog/2367.changed.md deleted file mode 100644 index b467681600..0000000000 --- a/changelog/2367.changed.md +++ /dev/null @@ -1,5 +0,0 @@ -Allow use of `SignalFd` through shared reference - -Like with many other file descriptors, concurrent use of signalfds is safe. -Changing the signal mask of and reading signals from a signalfd can now be done -with the `SignalFd` API even if other references to it exist. diff --git a/changelog/2374.added.md b/changelog/2374.added.md deleted file mode 100644 index ed0ed25675..0000000000 --- a/changelog/2374.added.md +++ /dev/null @@ -1 +0,0 @@ -Add `open` flag `O_SEARCH` to AIX, Empscripten, FreeBSD, Fuchsia, solarish, WASI diff --git a/changelog/2378.added.md b/changelog/2378.added.md deleted file mode 100644 index 21ad0b90f6..0000000000 --- a/changelog/2378.added.md +++ /dev/null @@ -1 +0,0 @@ -Add prctl function `prctl_set_vma_anon_name` for Linux/Android. diff --git a/changelog/2379.added.md b/changelog/2379.added.md deleted file mode 100644 index 325abdc8ff..0000000000 --- a/changelog/2379.added.md +++ /dev/null @@ -1 +0,0 @@ -Add `sync(2)` for `apple_targets/solarish/haiku/aix/hurd`, `syncfs(2)` for `hurd` and `fdatasync(2)` for `aix/hurd` diff --git a/changelog/2380.added.md b/changelog/2380.added.md deleted file mode 100644 index e9445c8500..0000000000 --- a/changelog/2380.added.md +++ /dev/null @@ -1 +0,0 @@ -Add fdatasync support for Apple targets. diff --git a/changelog/2382.added.md b/changelog/2382.added.md deleted file mode 100644 index d9b2274157..0000000000 --- a/changelog/2382.added.md +++ /dev/null @@ -1 +0,0 @@ -Add `fcntl::OFlag::O_PATH` for FreeBSD and Fuchsia diff --git a/changelog/2387.changed.md b/changelog/2387.changed.md deleted file mode 100644 index 28ade908c2..0000000000 --- a/changelog/2387.changed.md +++ /dev/null @@ -1 +0,0 @@ -Changed tee, splice and vmsplice RawFd arguments to AsFd. diff --git a/changelog/2388.added.md b/changelog/2388.added.md deleted file mode 100644 index b70769d4af..0000000000 --- a/changelog/2388.added.md +++ /dev/null @@ -1 +0,0 @@ -Added `PathconfVar::MIN_HOLE_SIZE` for apple_targets. \ No newline at end of file diff --git a/changelog/2391.added.md b/changelog/2391.added.md deleted file mode 100644 index 9989be85fc..0000000000 --- a/changelog/2391.added.md +++ /dev/null @@ -1 +0,0 @@ -Add `open` flag `O_SEARCH` to apple_targets diff --git a/changelog/2399.fixed.md b/changelog/2399.fixed.md deleted file mode 100644 index e6e0fe044a..0000000000 --- a/changelog/2399.fixed.md +++ /dev/null @@ -1 +0,0 @@ -No longer panics when the `fanotify` queue overflows. diff --git a/changelog/2401.changed.md b/changelog/2401.changed.md deleted file mode 100644 index 7eb53e677a..0000000000 --- a/changelog/2401.changed.md +++ /dev/null @@ -1,2 +0,0 @@ -Added I/O safety to the sys/aio module. Most functions that previously -accepted a `AsRawFd` argument now accept an `AsFd` instead. diff --git a/changelog/2404.added.md b/changelog/2404.added.md deleted file mode 100644 index 7f50f5bb01..0000000000 --- a/changelog/2404.added.md +++ /dev/null @@ -1 +0,0 @@ -`O_DSYNC` may now be used with `aio_fsync` and `fcntl` on FreeBSD. diff --git a/changelog/2406.fixed.md b/changelog/2406.fixed.md deleted file mode 100644 index 41bc9c7789..0000000000 --- a/changelog/2406.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed ControlMessageOwned::UdpGroSegments wrapped type from u16 to i32 to reflect the used kernel's one. diff --git a/changelog/2407.added.md b/changelog/2407.added.md deleted file mode 100644 index 26e2cd2a21..0000000000 --- a/changelog/2407.added.md +++ /dev/null @@ -1 +0,0 @@ -Added `Flock::relock` for upgrading and downgrading locks. diff --git a/changelog/2413.changed.md b/changelog/2413.changed.md deleted file mode 100644 index 7bae72f7d8..0000000000 --- a/changelog/2413.changed.md +++ /dev/null @@ -1 +0,0 @@ -`RecvMsg::cmsgs()` now returns a `Result`, and checks that cmsgs were not truncated.