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. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy