From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- third_party/rust/nix/test/common/mod.rs | 10 +- third_party/rust/nix/test/sys/mod.rs | 54 ++- third_party/rust/nix/test/sys/test_aio.rs | 66 +++- third_party/rust/nix/test/sys/test_aio_drop.rs | 3 +- third_party/rust/nix/test/sys/test_event.rs | 41 +++ third_party/rust/nix/test/sys/test_fanotify.rs | 149 ++++++++ third_party/rust/nix/test/sys/test_ioctl.rs | 33 +- third_party/rust/nix/test/sys/test_mman.rs | 55 ++- third_party/rust/nix/test/sys/test_ptrace.rs | 12 +- third_party/rust/nix/test/sys/test_resource.rs | 43 +++ third_party/rust/nix/test/sys/test_select.rs | 247 ++++++++++++- third_party/rust/nix/test/sys/test_signal.rs | 320 ++++++++++++++++- third_party/rust/nix/test/sys/test_signalfd.rs | 63 ++++ third_party/rust/nix/test/sys/test_socket.rs | 474 +++++++++++++++++++------ third_party/rust/nix/test/sys/test_sockopt.rs | 454 +++++++++++++++++++++-- third_party/rust/nix/test/sys/test_statfs.rs | 99 ++++++ third_party/rust/nix/test/sys/test_statvfs.rs | 13 + third_party/rust/nix/test/sys/test_termios.rs | 13 +- third_party/rust/nix/test/sys/test_time.rs | 91 +++++ third_party/rust/nix/test/sys/test_timer.rs | 102 ++++++ third_party/rust/nix/test/sys/test_uio.rs | 36 +- third_party/rust/nix/test/sys/test_utsname.rs | 17 + third_party/rust/nix/test/sys/test_wait.rs | 16 +- third_party/rust/nix/test/test.rs | 32 +- third_party/rust/nix/test/test_dir.rs | 4 +- third_party/rust/nix/test/test_errno.rs | 16 + third_party/rust/nix/test/test_fcntl.rs | 182 ++++++++-- third_party/rust/nix/test/test_mount.rs | 390 ++++++++------------ third_party/rust/nix/test/test_mq.rs | 20 +- third_party/rust/nix/test/test_net.rs | 8 +- third_party/rust/nix/test/test_poll.rs | 31 +- third_party/rust/nix/test/test_pty.rs | 24 +- third_party/rust/nix/test/test_resource.rs | 34 -- third_party/rust/nix/test/test_sendfile.rs | 90 +++-- third_party/rust/nix/test/test_stat.rs | 104 +++++- third_party/rust/nix/test/test_time.rs | 49 +-- third_party/rust/nix/test/test_timer.rs | 102 ------ third_party/rust/nix/test/test_unistd.rs | 260 +++++++------- 38 files changed, 2805 insertions(+), 952 deletions(-) create mode 100644 third_party/rust/nix/test/sys/test_event.rs create mode 100644 third_party/rust/nix/test/sys/test_fanotify.rs create mode 100644 third_party/rust/nix/test/sys/test_resource.rs create mode 100644 third_party/rust/nix/test/sys/test_statfs.rs create mode 100644 third_party/rust/nix/test/sys/test_statvfs.rs create mode 100644 third_party/rust/nix/test/sys/test_time.rs create mode 100644 third_party/rust/nix/test/sys/test_timer.rs create mode 100644 third_party/rust/nix/test/sys/test_utsname.rs create mode 100644 third_party/rust/nix/test/test_errno.rs delete mode 100644 third_party/rust/nix/test/test_resource.rs delete mode 100644 third_party/rust/nix/test/test_timer.rs (limited to 'third_party/rust/nix/test') diff --git a/third_party/rust/nix/test/common/mod.rs b/third_party/rust/nix/test/common/mod.rs index bb056aab87..db4aed2598 100644 --- a/third_party/rust/nix/test/common/mod.rs +++ b/third_party/rust/nix/test/common/mod.rs @@ -2,18 +2,18 @@ use cfg_if::cfg_if; #[macro_export] macro_rules! skip { - ($($reason: expr),+) => { + ($($reason: expr),+) => {{ use ::std::io::{self, Write}; let stderr = io::stderr(); let mut handle = stderr.lock(); writeln!(handle, $($reason),+).unwrap(); return; - } + }} } cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + if #[cfg(linux_android)] { #[macro_export] macro_rules! require_capability { ($name:expr, $capname:ident) => { use ::caps::{Capability, CapSet, has_cap}; @@ -51,7 +51,7 @@ macro_rules! require_mount { }; } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] #[macro_export] macro_rules! skip_if_cirrus { ($reason:expr) => { @@ -87,7 +87,7 @@ macro_rules! skip_if_not_root { } cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + if #[cfg(linux_android)] { #[macro_export] macro_rules! skip_if_seccomp { ($name:expr) => { if let Ok(s) = std::fs::read_to_string("/proc/self/status") { diff --git a/third_party/rust/nix/test/sys/mod.rs b/third_party/rust/nix/test/sys/mod.rs index 20312120a6..fb3f6be0e5 100644 --- a/third_party/rust/nix/test/sys/mod.rs +++ b/third_party/rust/nix/test/sys/mod.rs @@ -7,16 +7,16 @@ mod test_signal; // cases on DragonFly. #[cfg(any( target_os = "freebsd", - target_os = "ios", + apple_targets, all(target_os = "linux", not(target_env = "uclibc")), - target_os = "macos", target_os = "netbsd" ))] mod test_aio; #[cfg(not(any( target_os = "redox", target_os = "fuchsia", - target_os = "haiku" + target_os = "haiku", + target_os = "hurd" )))] mod test_ioctl; #[cfg(not(target_os = "redox"))] @@ -30,7 +30,7 @@ mod test_socket; #[cfg(not(any(target_os = "redox")))] mod test_sockopt; mod test_stat; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] mod test_sysinfo; #[cfg(not(any( target_os = "redox", @@ -41,20 +41,44 @@ mod test_termios; mod test_uio; mod test_wait; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] mod test_epoll; #[cfg(target_os = "linux")] +mod test_fanotify; +#[cfg(target_os = "linux")] mod test_inotify; mod test_pthread; -#[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" -))] + +#[cfg(any(linux_android, freebsdlike, netbsdlike, apple_targets))] mod test_ptrace; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] mod test_timerfd; + +#[cfg(all( + any( + target_os = "freebsd", + solarish, + target_os = "linux", + target_os = "netbsd" + ), + feature = "time", + feature = "signal" +))] +mod test_timer; + +#[cfg(bsd)] +mod test_event; +mod test_statvfs; +mod test_time; +mod test_utsname; + +#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "openbsd"))] +mod test_statfs; + +#[cfg(not(any( + target_os = "redox", + target_os = "fuchsia", + solarish, + target_os = "haiku" +)))] +mod test_resource; diff --git a/third_party/rust/nix/test/sys/test_aio.rs b/third_party/rust/nix/test/sys/test_aio.rs index 5035b5a08f..ba5ad02ec3 100644 --- a/third_party/rust/nix/test/sys/test_aio.rs +++ b/third_party/rust/nix/test/sys/test_aio.rs @@ -67,7 +67,7 @@ mod aio_fsync { // Skip on Linux, because Linux's AIO implementation can't detect errors // synchronously #[test] - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", apple_targets))] fn error() { use std::mem; @@ -157,7 +157,7 @@ mod aio_read { // Skip on Linux, because Linux's AIO implementation can't detect errors // synchronously #[test] - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", apple_targets))] fn error() { const INITIAL: &[u8] = b"abcdef123456"; let mut rbuf = vec![0; 4]; @@ -411,7 +411,7 @@ mod aio_write { // Skip on Linux, because Linux's AIO implementation can't detect errors // synchronously #[test] - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", apple_targets))] fn error() { let wbuf = "CDEF".to_string().into_bytes(); let mut aiow = Box::pin(AioWrite::new( @@ -498,7 +498,9 @@ mod aio_writev { any( all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", - target_arch = "mips64" + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" ), ignore )] @@ -567,12 +569,6 @@ fn test_aio_cancel_all() { } #[test] -// On Cirrus on Linux, this test fails due to a glibc bug. -// https://github.com/nix-rust/nix/issues/1099 -#[cfg_attr(target_os = "linux", ignore)] -// On Cirrus, aio_suspend is failing with EINVAL -// https://github.com/nix-rust/nix/issues/1361 -#[cfg_attr(target_os = "macos", ignore)] fn test_aio_suspend() { const INITIAL: &[u8] = b"abcdef123456"; const WBUF: &[u8] = b"CDEFG"; @@ -622,3 +618,53 @@ fn test_aio_suspend() { assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len()); assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen); } + +/// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb +/// pointers. This test ensures that such casts are valid. +#[test] +fn casting() { + let sev = SigevNotify::SigevNone; + let aiof = AioFsync::new(666, 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); + 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); + assert_eq!( + aiow.as_ref() as *const libc::aiocb, + &aiow as *const AioWrite as *const libc::aiocb + ); +} + +#[cfg(target_os = "freebsd")] +#[test] +fn casting_vectored() { + use std::io::{IoSlice, IoSliceMut}; + + let sev = SigevNotify::SigevNone; + + let mut rbuf = []; + let mut rbufs = [IoSliceMut::new(&mut rbuf)]; + let aiorv = AioReadv::new(666, 0, &mut rbufs[..], 0, sev); + assert_eq!( + aiorv.as_ref() as *const libc::aiocb, + &aiorv as *const AioReadv as *const libc::aiocb + ); + + let wbuf = []; + let wbufs = [IoSlice::new(&wbuf)]; + let aiowv = AioWritev::new(666, 0, &wbufs, 0, sev); + assert_eq!( + aiowv.as_ref() as *const libc::aiocb, + &aiowv as *const AioWritev as *const libc::aiocb + ); +} diff --git a/third_party/rust/nix/test/sys/test_aio_drop.rs b/third_party/rust/nix/test/sys/test_aio_drop.rs index bbe6623fd7..54106dd168 100644 --- a/third_party/rust/nix/test/sys/test_aio_drop.rs +++ b/third_party/rust/nix/test/sys/test_aio_drop.rs @@ -8,8 +8,7 @@ not(target_env = "uclibc"), any( target_os = "linux", - target_os = "ios", - target_os = "macos", + apple_targets, target_os = "freebsd", target_os = "netbsd" ) diff --git a/third_party/rust/nix/test/sys/test_event.rs b/third_party/rust/nix/test/sys/test_event.rs new file mode 100644 index 0000000000..a10b1e5d12 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_event.rs @@ -0,0 +1,41 @@ +use libc::intptr_t; +use nix::sys::event::{EventFilter, EventFlag, FilterFlag, KEvent}; + +#[test] +fn test_struct_kevent() { + use std::mem; + + let udata: intptr_t = 12345; + let data: intptr_t = 0x1337; + + let actual = KEvent::new( + 0xdead_beef, + EventFilter::EVFILT_READ, + EventFlag::EV_ONESHOT | EventFlag::EV_ADD, + FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT, + data, + udata, + ); + assert_eq!(0xdead_beef, actual.ident()); + assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap()); + assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits()); + assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits()); + assert_eq!(data, actual.data()); + assert_eq!(udata, actual.udata()); + assert_eq!(mem::size_of::(), mem::size_of::()); +} + +#[test] +fn test_kevent_filter() { + let udata: intptr_t = 12345; + + let actual = KEvent::new( + 0xdead_beef, + EventFilter::EVFILT_READ, + EventFlag::EV_ONESHOT | EventFlag::EV_ADD, + FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT, + 0x1337, + udata, + ); + assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap()); +} diff --git a/third_party/rust/nix/test/sys/test_fanotify.rs b/third_party/rust/nix/test/sys/test_fanotify.rs new file mode 100644 index 0000000000..20226c272a --- /dev/null +++ b/third_party/rust/nix/test/sys/test_fanotify.rs @@ -0,0 +1,149 @@ +use crate::*; +use nix::sys::fanotify::{ + EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags, + Response, +}; +use std::fs::{read_link, File, OpenOptions}; +use std::io::ErrorKind; +use std::io::{Read, Write}; +use std::os::fd::AsRawFd; +use std::thread; + +#[test] +/// Run fanotify tests sequentially to avoid tmp files races +pub fn test_fanotify() { + require_capability!("test_fanotify", CAP_SYS_ADMIN); + + test_fanotify_notifications(); + test_fanotify_responses(); +} + +fn test_fanotify_notifications() { + let group = + Fanotify::init(InitFlags::FAN_CLASS_NOTIF, 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 | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE, + None, + Some(&tempfile), + ) + .unwrap(); + + // modify test file + { + let mut f = OpenOptions::new().write(true).open(&tempfile).unwrap(); + f.write_all(b"hello").unwrap(); + } + + let mut events = group.read_events().unwrap(); + assert_eq!(events.len(), 1, "should have read exactly one event"); + let event = events.pop().unwrap(); + assert!(event.check_version()); + assert_eq!( + event.mask(), + MaskFlags::FAN_OPEN + | MaskFlags::FAN_MODIFY + | MaskFlags::FAN_CLOSE_WRITE + ); + let fd_opt = event.fd(); + let fd = fd_opt.as_ref().unwrap(); + let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap(); + assert_eq!(path, tempfile); + + // read test file + { + let mut f = File::open(&tempfile).unwrap(); + let mut s = String::new(); + f.read_to_string(&mut s).unwrap(); + } + + let mut events = group.read_events().unwrap(); + assert_eq!(events.len(), 1, "should have read exactly one event"); + let event = events.pop().unwrap(); + assert!(event.check_version()); + assert_eq!( + event.mask(), + MaskFlags::FAN_OPEN | MaskFlags::FAN_CLOSE_NOWRITE + ); + let fd_opt = event.fd(); + let fd = fd_opt.as_ref().unwrap(); + let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap(); + assert_eq!(path, tempfile); +} + +fn test_fanotify_responses() { + let group = + Fanotify::init(InitFlags::FAN_CLASS_CONTENT, 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_PERM, + None, + Some(&tempfile), + ) + .unwrap(); + + let file_thread = thread::spawn({ + let tempfile = tempfile.clone(); + + move || { + // first open, should fail + let Err(e) = File::open(&tempfile) else { + panic!("The first open should fail"); + }; + assert_eq!(e.kind(), ErrorKind::PermissionDenied); + + // second open, should succeed + File::open(&tempfile).unwrap(); + } + }); + + // Deny the first open try + let mut events = group.read_events().unwrap(); + assert_eq!(events.len(), 1, "should have read exactly one event"); + let event = events.pop().unwrap(); + assert!(event.check_version()); + assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM); + let fd_opt = event.fd(); + let fd = fd_opt.as_ref().unwrap(); + let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap(); + assert_eq!(path, tempfile); + group + .write_response(FanotifyResponse::new(*fd, Response::FAN_DENY)) + .unwrap(); + + // Allow the second open try + let mut events = group.read_events().unwrap(); + assert_eq!(events.len(), 1, "should have read exactly one event"); + let event = events.pop().unwrap(); + assert!(event.check_version()); + assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM); + let fd_opt = event.fd(); + let fd = fd_opt.as_ref().unwrap(); + let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap(); + assert_eq!(path, tempfile); + group + .write_response(FanotifyResponse::new(*fd, Response::FAN_ALLOW)) + .unwrap(); + + file_thread.join().unwrap(); +} diff --git a/third_party/rust/nix/test/sys/test_ioctl.rs b/third_party/rust/nix/test/sys/test_ioctl.rs index 40f60cfdbc..08843bf61c 100644 --- a/third_party/rust/nix/test/sys/test_ioctl.rs +++ b/third_party/rust/nix/test/sys/test_ioctl.rs @@ -28,7 +28,7 @@ ioctl_readwrite_buf!(readwritebuf_test, 0, 0, u32); // TODO: Need a way to compute these constants at test time. Using precomputed // values is fragile and needs to be maintained. -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] mod linux { // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] @@ -36,7 +36,9 @@ mod linux { fn test_op_none() { if cfg!(any( target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "powerpc", target_arch = "powerpc64" )) { @@ -54,7 +56,9 @@ mod linux { fn test_op_write() { if cfg!(any( target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "powerpc", target_arch = "powerpc64" )) { @@ -69,7 +73,11 @@ mod linux { #[cfg(target_pointer_width = "64")] #[test] fn test_op_write_64() { - if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) { + if cfg!(any( + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "powerpc64" + )) { assert_eq!( request_code_write!(b'z', 10, 1u64 << 32) as u32, 0x8000_7A0A @@ -88,7 +96,9 @@ mod linux { fn test_op_read() { if cfg!(any( target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "powerpc", target_arch = "powerpc64" )) { @@ -103,7 +113,11 @@ mod linux { #[cfg(target_pointer_width = "64")] #[test] fn test_op_read_64() { - if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) { + if cfg!(any( + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "powerpc64" + )) { assert_eq!( request_code_read!(b'z', 10, 1u64 << 32) as u32, 0x4000_7A0A @@ -134,14 +148,7 @@ mod linux { } } -#[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" -))] +#[cfg(bsd)] mod bsd { #[test] fn test_op_none() { @@ -149,7 +156,7 @@ mod bsd { assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF); } - #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + #[cfg(freebsdlike)] #[test] fn test_op_write_int() { assert_eq!(request_code_write_int!(b'v', 4), 0x2004_7604); @@ -193,7 +200,7 @@ mod bsd { } } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] mod linux_ioctls { use std::mem; use std::os::unix::io::AsRawFd; diff --git a/third_party/rust/nix/test/sys/test_mman.rs b/third_party/rust/nix/test/sys/test_mman.rs index b4674e53fa..3689f642be 100644 --- a/third_party/rust/nix/test/sys/test_mman.rs +++ b/third_party/rust/nix/test/sys/test_mman.rs @@ -1,44 +1,44 @@ -use nix::sys::mman::{mmap, MapFlags, ProtFlags}; -use std::{num::NonZeroUsize, os::unix::io::BorrowedFd}; +#![allow(clippy::redundant_slicing)] + +use nix::sys::mman::{mmap_anonymous, MapFlags, ProtFlags}; +use std::num::NonZeroUsize; #[test] fn test_mmap_anonymous() { unsafe { - let ptr = mmap::( + let mut ptr = mmap_anonymous( None, NonZeroUsize::new(1).unwrap(), ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, - MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, - None, - 0, + MapFlags::MAP_PRIVATE, ) - .unwrap() as *mut u8; - assert_eq!(*ptr, 0x00u8); - *ptr = 0xffu8; - assert_eq!(*ptr, 0xffu8); + .unwrap() + .cast::(); + assert_eq!(*ptr.as_ref(), 0x00u8); + *ptr.as_mut() = 0xffu8; + assert_eq!(*ptr.as_ref(), 0xffu8); } } #[test] #[cfg(any(target_os = "linux", target_os = "netbsd"))] fn test_mremap_grow() { - use nix::libc::{c_void, size_t}; + use nix::libc::size_t; use nix::sys::mman::{mremap, MRemapFlags}; + use std::ptr::NonNull; const ONE_K: size_t = 1024; let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap(); let slice: &mut [u8] = unsafe { - let mem = mmap::( + let mem = mmap_anonymous( None, one_k_non_zero, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, - MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, - None, - 0, + MapFlags::MAP_PRIVATE, ) .unwrap(); - std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K) + std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K) }; assert_eq!(slice[ONE_K - 1], 0x00); slice[ONE_K - 1] = 0xFF; @@ -47,7 +47,7 @@ fn test_mremap_grow() { let slice: &mut [u8] = unsafe { #[cfg(target_os = "linux")] let mem = mremap( - slice.as_mut_ptr() as *mut c_void, + NonNull::from(&mut slice[..]).cast(), ONE_K, 10 * ONE_K, MRemapFlags::MREMAP_MAYMOVE, @@ -56,14 +56,14 @@ fn test_mremap_grow() { .unwrap(); #[cfg(target_os = "netbsd")] let mem = mremap( - slice.as_mut_ptr() as *mut c_void, + NonNull::from(&mut slice[..]).cast(), ONE_K, 10 * ONE_K, MRemapFlags::MAP_REMAPDUP, None, ) .unwrap(); - std::slice::from_raw_parts_mut(mem as *mut u8, 10 * ONE_K) + std::slice::from_raw_parts_mut(mem.cast().as_ptr(), 10 * ONE_K) }; // The first KB should still have the old data in it. @@ -80,23 +80,22 @@ fn test_mremap_grow() { // Segfaults for unknown reasons under QEMU for 32-bit targets #[cfg_attr(all(target_pointer_width = "32", qemu), ignore)] fn test_mremap_shrink() { - use nix::libc::{c_void, size_t}; + use nix::libc::size_t; use nix::sys::mman::{mremap, MRemapFlags}; use std::num::NonZeroUsize; + use std::ptr::NonNull; const ONE_K: size_t = 1024; let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap(); let slice: &mut [u8] = unsafe { - let mem = mmap::( + let mem = mmap_anonymous( None, ten_one_k, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, - MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE, - None, - 0, + MapFlags::MAP_PRIVATE, ) .unwrap(); - std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K) + std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K) }; assert_eq!(slice[ONE_K - 1], 0x00); slice[ONE_K - 1] = 0xFF; @@ -104,7 +103,7 @@ fn test_mremap_shrink() { let slice: &mut [u8] = unsafe { let mem = mremap( - slice.as_mut_ptr() as *mut c_void, + NonNull::from(&mut slice[..]).cast(), ten_one_k.into(), ONE_K, MRemapFlags::empty(), @@ -113,8 +112,8 @@ fn test_mremap_shrink() { .unwrap(); // Since we didn't supply MREMAP_MAYMOVE, the address should be the // same. - assert_eq!(mem, slice.as_mut_ptr() as *mut c_void); - std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K) + assert_eq!(mem.as_ptr(), NonNull::from(&mut slice[..]).cast().as_ptr()); + std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K) }; // The first KB should still be accessible and have the old data in it. diff --git a/third_party/rust/nix/test/sys/test_ptrace.rs b/third_party/rust/nix/test/sys/test_ptrace.rs index 530560fe17..246b35445d 100644 --- a/third_party/rust/nix/test/sys/test_ptrace.rs +++ b/third_party/rust/nix/test/sys/test_ptrace.rs @@ -6,11 +6,11 @@ use memoffset::offset_of; use nix::errno::Errno; use nix::sys::ptrace; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] use nix::sys::ptrace::Options; use nix::unistd::getpid; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] use std::mem; use crate::*; @@ -28,7 +28,7 @@ fn test_ptrace() { // Just make sure ptrace_setoptions can be called at all, for now. #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptrace_setoptions() { require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE); let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD) @@ -38,7 +38,7 @@ fn test_ptrace_setoptions() { // Just make sure ptrace_getevent can be called at all, for now. #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptrace_getevent() { require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE); let err = ptrace::getevent(getpid()).unwrap_err(); @@ -47,7 +47,7 @@ fn test_ptrace_getevent() { // Just make sure ptrace_getsiginfo can be called at all, for now. #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptrace_getsiginfo() { require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE); if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) { @@ -57,7 +57,7 @@ fn test_ptrace_getsiginfo() { // Just make sure ptrace_setsiginfo can be called at all, for now. #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptrace_setsiginfo() { require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE); let siginfo = unsafe { mem::zeroed() }; diff --git a/third_party/rust/nix/test/sys/test_resource.rs b/third_party/rust/nix/test/sys/test_resource.rs new file mode 100644 index 0000000000..8b12a9495b --- /dev/null +++ b/third_party/rust/nix/test/sys/test_resource.rs @@ -0,0 +1,43 @@ +use nix::sys::resource::{getrlimit, setrlimit, Resource}; +use nix::sys::resource::{getrusage, UsageWho}; + +/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers +/// to the maximum file descriptor number that can be opened by the process (aka the maximum number +/// of file descriptors that the process can open, since Linux 4.5). +/// +/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the +/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit() +/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have +/// been updated. +#[test] +pub fn test_resource_limits_nofile() { + let (mut soft_limit, hard_limit) = + getrlimit(Resource::RLIMIT_NOFILE).unwrap(); + + soft_limit -= 1; + assert_ne!(soft_limit, hard_limit); + setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); + + let (new_soft_limit, _) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); + assert_eq!(new_soft_limit, soft_limit); +} + +#[test] +pub fn test_self_cpu_time() { + // Make sure some CPU time is used. + let mut numbers: Vec = (1..1_000_000).collect(); + numbers.iter_mut().for_each(|item| *item *= 2); + + // FIXME: this is here to help ensure the compiler does not optimize the whole + // thing away. Replace the assert with test::black_box once stabilized. + assert_eq!(numbers[100..200].iter().sum::(), 30_100); + + let usage = getrusage(UsageWho::RUSAGE_SELF) + .expect("Failed to call getrusage for SELF"); + let rusage = usage.as_ref(); + + let user = usage.user_time(); + assert!(user.tv_sec() > 0 || user.tv_usec() > 0); + assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec); + assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec); +} diff --git a/third_party/rust/nix/test/sys/test_select.rs b/third_party/rust/nix/test/sys/test_select.rs index 79f75de3b4..e39a31923a 100644 --- a/third_party/rust/nix/test/sys/test_select.rs +++ b/third_party/rust/nix/test/sys/test_select.rs @@ -1,22 +1,20 @@ use nix::sys::select::*; use nix::sys::signal::SigSet; -use nix::sys::time::{TimeSpec, TimeValLike}; +use nix::sys::time::{TimeSpec, TimeVal, TimeValLike}; use nix::unistd::{pipe, write}; -use std::os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd}; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; #[test] pub fn test_pselect() { let _mtx = crate::SIGNAL_MTX.lock(); let (r1, w1) = pipe().unwrap(); - write(w1, b"hi!").unwrap(); - let r1 = unsafe { OwnedFd::from_raw_fd(r1) }; + write(&w1, b"hi!").unwrap(); let (r2, _w2) = pipe().unwrap(); - let r2 = unsafe { OwnedFd::from_raw_fd(r2) }; let mut fd_set = FdSet::new(); - fd_set.insert(&r1); - fd_set.insert(&r2); + fd_set.insert(r1.as_fd()); + fd_set.insert(r2.as_fd()); let timeout = TimeSpec::seconds(10); let sigmask = SigSet::empty(); @@ -24,21 +22,19 @@ pub fn test_pselect() { 1, pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap() ); - assert!(fd_set.contains(&r1)); - assert!(!fd_set.contains(&r2)); + assert!(fd_set.contains(r1.as_fd())); + assert!(!fd_set.contains(r2.as_fd())); } #[test] pub fn test_pselect_nfds2() { let (r1, w1) = pipe().unwrap(); - write(w1, b"hi!").unwrap(); - let r1 = unsafe { OwnedFd::from_raw_fd(r1) }; + write(&w1, b"hi!").unwrap(); let (r2, _w2) = pipe().unwrap(); - let r2 = unsafe { OwnedFd::from_raw_fd(r2) }; let mut fd_set = FdSet::new(); - fd_set.insert(&r1); - fd_set.insert(&r2); + fd_set.insert(r1.as_fd()); + fd_set.insert(r2.as_fd()); let timeout = TimeSpec::seconds(10); assert_eq!( @@ -53,8 +49,8 @@ pub fn test_pselect_nfds2() { ) .unwrap() ); - assert!(fd_set.contains(&r1)); - assert!(!fd_set.contains(&r2)); + assert!(fd_set.contains(r1.as_fd())); + assert!(!fd_set.contains(r2.as_fd())); } macro_rules! generate_fdset_bad_fd_tests { @@ -64,7 +60,7 @@ macro_rules! generate_fdset_bad_fd_tests { #[should_panic] fn $method() { let bad_fd = unsafe{BorrowedFd::borrow_raw($fd)}; - FdSet::new().$method(&bad_fd); + FdSet::new().$method(bad_fd); } )* } @@ -72,7 +68,6 @@ macro_rules! generate_fdset_bad_fd_tests { mod test_fdset_too_large_fd { use super::*; - use std::convert::TryInto; generate_fdset_bad_fd_tests!( FD_SETSIZE.try_into().unwrap(), insert, @@ -80,3 +75,219 @@ mod test_fdset_too_large_fd { contains, ); } + +#[test] +fn fdset_insert() { + let mut fd_set = FdSet::new(); + + for i in 0..FD_SETSIZE { + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(borrowed_i)); + } + + let fd_seven = unsafe { BorrowedFd::borrow_raw(7) }; + fd_set.insert(fd_seven); + + assert!(fd_set.contains(fd_seven)); +} + +#[test] +fn fdset_remove() { + let mut fd_set = FdSet::new(); + + for i in 0..FD_SETSIZE { + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(borrowed_i)); + } + + let fd_seven = unsafe { BorrowedFd::borrow_raw(7) }; + fd_set.insert(fd_seven); + fd_set.remove(fd_seven); + + for i in 0..FD_SETSIZE { + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(borrowed_i)); + } +} + +#[test] +#[allow(non_snake_case)] +fn fdset_clear() { + let mut fd_set = FdSet::new(); + let fd_one = unsafe { BorrowedFd::borrow_raw(1) }; + let fd_FD_SETSIZE_divided_by_two = + unsafe { BorrowedFd::borrow_raw((FD_SETSIZE / 2) as RawFd) }; + let fd_FD_SETSIZE_minus_one = + unsafe { BorrowedFd::borrow_raw((FD_SETSIZE - 1) as RawFd) }; + fd_set.insert(fd_one); + fd_set.insert(fd_FD_SETSIZE_divided_by_two); + fd_set.insert(fd_FD_SETSIZE_minus_one); + + fd_set.clear(); + + for i in 0..FD_SETSIZE { + let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) }; + assert!(!fd_set.contains(borrowed_i)); + } +} + +#[test] +fn fdset_highest() { + let mut set = FdSet::new(); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + None + ); + let fd_zero = unsafe { BorrowedFd::borrow_raw(0) }; + let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) }; + set.insert(fd_zero); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(0) + ); + set.insert(fd_ninety); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(90) + ); + set.remove(fd_zero); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(90) + ); + set.remove(fd_ninety); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + None + ); + + let fd_four = unsafe { BorrowedFd::borrow_raw(4) }; + let fd_five = unsafe { BorrowedFd::borrow_raw(5) }; + let fd_seven = unsafe { BorrowedFd::borrow_raw(7) }; + set.insert(fd_four); + set.insert(fd_five); + set.insert(fd_seven); + assert_eq!( + set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()), + Some(7) + ); +} + +#[test] +fn fdset_fds() { + let mut set = FdSet::new(); + let fd_zero = unsafe { BorrowedFd::borrow_raw(0) }; + let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) }; + assert_eq!( + set.fds(None) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![] + ); + set.insert(fd_zero); + assert_eq!( + set.fds(None) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0] + ); + set.insert(fd_ninety); + assert_eq!( + set.fds(None) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0, 90] + ); + + // highest limit + assert_eq!( + set.fds(Some(89)) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0] + ); + assert_eq!( + set.fds(Some(90)) + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .collect::>(), + vec![0, 90] + ); +} + +#[test] +fn test_select() { + let (r1, w1) = pipe().unwrap(); + let (r2, _w2) = pipe().unwrap(); + + write(&w1, b"hi!").unwrap(); + let mut fd_set = FdSet::new(); + fd_set.insert(r1.as_fd()); + fd_set.insert(r2.as_fd()); + + let mut timeout = TimeVal::seconds(10); + assert_eq!( + 1, + select(None, &mut fd_set, None, None, &mut timeout).unwrap() + ); + assert!(fd_set.contains(r1.as_fd())); + assert!(!fd_set.contains(r2.as_fd())); +} + +#[test] +fn test_select_nfds() { + let (r1, w1) = pipe().unwrap(); + let (r2, _w2) = pipe().unwrap(); + + write(&w1, b"hi!").unwrap(); + let mut fd_set = FdSet::new(); + fd_set.insert(r1.as_fd()); + fd_set.insert(r2.as_fd()); + + let mut timeout = TimeVal::seconds(10); + { + assert_eq!( + 1, + select( + Some( + fd_set + .highest() + .map(|borrowed_fd| borrowed_fd.as_raw_fd()) + .unwrap() + + 1 + ), + &mut fd_set, + None, + None, + &mut timeout + ) + .unwrap() + ); + } + assert!(fd_set.contains(r1.as_fd())); + assert!(!fd_set.contains(r2.as_fd())); +} + +#[test] +fn test_select_nfds2() { + let (r1, w1) = pipe().unwrap(); + write(&w1, b"hi!").unwrap(); + let (r2, _w2) = pipe().unwrap(); + let mut fd_set = FdSet::new(); + fd_set.insert(r1.as_fd()); + fd_set.insert(r2.as_fd()); + + let mut timeout = TimeVal::seconds(10); + assert_eq!( + 1, + select( + std::cmp::max(r1.as_raw_fd(), r2.as_raw_fd()) + 1, + &mut fd_set, + None, + None, + &mut timeout + ) + .unwrap() + ); + assert!(fd_set.contains(r1.as_fd())); + assert!(!fd_set.contains(r2.as_fd())); +} diff --git a/third_party/rust/nix/test/sys/test_signal.rs b/third_party/rust/nix/test/sys/test_signal.rs index ca25ff9ab0..bf607497be 100644 --- a/third_party/rust/nix/test/sys/test_signal.rs +++ b/third_party/rust/nix/test/sys/test_signal.rs @@ -1,9 +1,10 @@ -#[cfg(not(target_os = "redox"))] use nix::errno::Errno; use nix::sys::signal::*; use nix::unistd::*; -use std::convert::TryFrom; +use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicBool, Ordering}; +#[cfg(not(target_os = "redox"))] +use std::thread; #[test] fn test_kill_none() { @@ -124,7 +125,7 @@ fn test_signal() { raise(Signal::SIGINT).unwrap(); assert!(SIGNALED.load(Ordering::Relaxed)); - #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] + #[cfg(not(solarish))] assert_eq!( unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler @@ -132,7 +133,7 @@ fn test_signal() { // System V based OSes (e.g. illumos and Solaris) always resets the // disposition to SIG_DFL prior to calling the signal handler - #[cfg(any(target_os = "illumos", target_os = "solaris"))] + #[cfg(solarish)] assert_eq!( unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigDfl @@ -141,3 +142,314 @@ fn test_signal() { // Restore default signal handler unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(); } + +#[test] +fn test_contains() { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + assert!(mask.contains(SIGUSR1)); + assert!(!mask.contains(SIGUSR2)); + + let all = SigSet::all(); + assert!(all.contains(SIGUSR1)); + assert!(all.contains(SIGUSR2)); +} + +#[test] +fn test_clear() { + let mut set = SigSet::all(); + set.clear(); + for signal in Signal::iterator() { + assert!(!set.contains(signal)); + } +} + +#[test] +fn test_from_str_round_trips() { + for signal in Signal::iterator() { + assert_eq!(signal.as_ref().parse::().unwrap(), signal); + assert_eq!(signal.to_string().parse::().unwrap(), signal); + } +} + +#[test] +fn test_from_str_invalid_value() { + let errval = Err(Errno::EINVAL); + assert_eq!("NOSIGNAL".parse::(), errval); + assert_eq!("kill".parse::(), errval); + assert_eq!("9".parse::(), errval); +} + +#[test] +fn test_extend() { + let mut one_signal = SigSet::empty(); + one_signal.add(SIGUSR1); + + let mut two_signals = SigSet::empty(); + two_signals.add(SIGUSR2); + two_signals.extend(&one_signal); + + assert!(two_signals.contains(SIGUSR1)); + assert!(two_signals.contains(SIGUSR2)); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_thread_signal_set_mask() { + thread::spawn(|| { + let prev_mask = SigSet::thread_get_mask() + .expect("Failed to get existing signal mask!"); + + let mut test_mask = prev_mask; + test_mask.add(SIGUSR1); + + test_mask.thread_set_mask().expect("assertion failed"); + let new_mask = + SigSet::thread_get_mask().expect("Failed to get new mask!"); + + assert!(new_mask.contains(SIGUSR1)); + assert!(!new_mask.contains(SIGUSR2)); + + prev_mask + .thread_set_mask() + .expect("Failed to revert signal mask!"); + }) + .join() + .unwrap(); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_thread_signal_block() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + mask.thread_block().expect("assertion failed"); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }) + .join() + .unwrap(); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_thread_signal_unblock() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + mask.thread_unblock().expect("assertion failed"); + + assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }) + .join() + .unwrap(); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_thread_signal_swap() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + mask.thread_block().unwrap(); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + + let mut mask2 = SigSet::empty(); + mask2.add(SIGUSR2); + + let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap(); + + assert!(oldmask.contains(SIGUSR1)); + assert!(!oldmask.contains(SIGUSR2)); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2)); + }) + .join() + .unwrap(); +} + +#[test] +fn test_from_and_into_iterator() { + let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]); + let signals = sigset.into_iter().collect::>(); + assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_sigaction() { + thread::spawn(|| { + extern "C" fn test_sigaction_handler(_: libc::c_int) {} + extern "C" fn test_sigaction_action( + _: libc::c_int, + _: *mut libc::siginfo_t, + _: *mut libc::c_void, + ) { + } + + let handler_sig = SigHandler::Handler(test_sigaction_handler); + + let flags = + SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO; + + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + let action_sig = SigAction::new(handler_sig, flags, mask); + + assert_eq!( + action_sig.flags(), + SaFlags::SA_ONSTACK | SaFlags::SA_RESTART + ); + assert_eq!(action_sig.handler(), handler_sig); + + mask = action_sig.mask(); + assert!(mask.contains(SIGUSR1)); + assert!(!mask.contains(SIGUSR2)); + + let handler_act = SigHandler::SigAction(test_sigaction_action); + let action_act = SigAction::new(handler_act, flags, mask); + assert_eq!(action_act.handler(), handler_act); + + let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask); + assert_eq!(action_dfl.handler(), SigHandler::SigDfl); + + let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask); + assert_eq!(action_ign.handler(), SigHandler::SigIgn); + }) + .join() + .unwrap(); +} + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_sigwait() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + mask.add(SIGUSR2); + mask.thread_block().unwrap(); + + raise(SIGUSR1).unwrap(); + assert_eq!(mask.wait().unwrap(), SIGUSR1); + }) + .join() + .unwrap(); +} + +#[cfg(any( + bsd, + linux_android, + solarish, + target_os = "haiku", + target_os = "hurd", + target_os = "aix", + target_os = "fushsia" +))] +#[test] +fn test_sigsuspend() { + // This test change signal handler + let _m = crate::SIGNAL_MTX.lock(); + static SIGNAL_RECIEVED: AtomicBool = AtomicBool::new(false); + extern "C" fn test_sigsuspend_handler(_: libc::c_int) { + assert!(!SIGNAL_RECIEVED.swap(true, Ordering::SeqCst)); + } + thread::spawn(|| { + const SIGNAL: Signal = Signal::SIGUSR1; + + // Add signal mask to this thread + let mut signal_set = SigSet::empty(); + signal_set.add(SIGNAL); + signal_set.thread_block().unwrap(); + + // Set signal handler and save old one. + let act = SigAction::new( + SigHandler::Handler(test_sigsuspend_handler), + SaFlags::empty(), + SigSet::empty(), + ); + let old_act = unsafe { sigaction(SIGNAL, &act) } + .expect("expect to be able to set new action and get old action"); + + raise(SIGNAL).expect("expect be able to send signal"); + // Now `SIGNAL` was sended but it is blocked. + let mut not_wait_set = SigSet::all(); + not_wait_set.remove(SIGNAL); + // signal handler must run in SigSet::suspend() + assert!(!SIGNAL_RECIEVED.load(Ordering::SeqCst)); + not_wait_set.suspend().unwrap(); + assert!(SIGNAL_RECIEVED.load(Ordering::SeqCst)); + + // Restore the signal handler. + unsafe { sigaction(SIGNAL, &old_act) } + .expect("expect to be able to restore old action "); + }) + .join() + .unwrap(); +} + +#[test] +fn test_from_sigset_t_unchecked() { + let src_set = SigSet::empty(); + let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) }; + + for signal in Signal::iterator() { + assert!(!set.contains(signal)); + } + + let src_set = SigSet::all(); + let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) }; + + for signal in Signal::iterator() { + assert!(set.contains(signal)); + } +} + +#[test] +fn test_eq_empty() { + let set0 = SigSet::empty(); + let set1 = SigSet::empty(); + assert_eq!(set0, set1); +} + +#[test] +fn test_eq_all() { + let set0 = SigSet::all(); + let set1 = SigSet::all(); + assert_eq!(set0, set1); +} + +#[test] +fn test_hash_empty() { + use std::collections::hash_map::DefaultHasher; + + let set0 = SigSet::empty(); + let mut h0 = DefaultHasher::new(); + set0.hash(&mut h0); + + let set1 = SigSet::empty(); + let mut h1 = DefaultHasher::new(); + set1.hash(&mut h1); + + assert_eq!(h0.finish(), h1.finish()); +} + +#[test] +fn test_hash_all() { + use std::collections::hash_map::DefaultHasher; + + let set0 = SigSet::all(); + let mut h0 = DefaultHasher::new(); + set0.hash(&mut h0); + + let set1 = SigSet::all(); + let mut h1 = DefaultHasher::new(); + set1.hash(&mut h1); + + assert_eq!(h0.finish(), h1.finish()); +} diff --git a/third_party/rust/nix/test/sys/test_signalfd.rs b/third_party/rust/nix/test/sys/test_signalfd.rs index 87153c9572..4e0971aba7 100644 --- a/third_party/rust/nix/test/sys/test_signalfd.rs +++ b/third_party/rust/nix/test/sys/test_signalfd.rs @@ -1,5 +1,39 @@ use std::convert::TryFrom; +#[test] +fn create_signalfd() { + use nix::sys::{signal::SigSet, signalfd::SignalFd}; + + let mask = SigSet::empty(); + SignalFd::new(&mask).unwrap(); +} + +#[test] +fn create_signalfd_with_opts() { + use nix::sys::{ + signal::SigSet, + signalfd::{SfdFlags, SignalFd}, + }; + + let mask = SigSet::empty(); + SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK) + .unwrap(); +} + +#[test] +fn read_empty_signalfd() { + use nix::sys::{ + signal::SigSet, + signalfd::{SfdFlags, SignalFd}, + }; + + let mask = SigSet::empty(); + let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); + + let res = fd.read_signal(); + assert!(res.unwrap().is_none()); +} + #[test] fn test_signalfd() { use nix::sys::signal::{self, raise, SigSet, Signal}; @@ -25,3 +59,32 @@ fn test_signalfd() { let signo = Signal::try_from(res.ssi_signo as i32).unwrap(); assert_eq!(signo, signal::SIGUSR1); } + +/// Update the signal mask of an already existing signalfd. +#[test] +fn test_signalfd_setmask() { + use nix::sys::signal::{self, raise, SigSet, Signal}; + use nix::sys::signalfd::SignalFd; + + // Grab the mutex for altering signals so we don't interfere with other tests. + let _m = crate::SIGNAL_MTX.lock(); + + // Block the SIGUSR1 signal from automatic processing for this thread + let mut mask = SigSet::empty(); + + let mut fd = SignalFd::new(&mask).unwrap(); + + mask.add(signal::SIGUSR1); + mask.thread_block().unwrap(); + fd.set_mask(&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 + // cargo test session. Instead use `raise` which does the correct thing by default. + raise(signal::SIGUSR1).expect("Error: raise(SIGUSR1) failed"); + + // And now catch that same signal. + let res = fd.read_signal().unwrap().unwrap(); + let signo = Signal::try_from(res.ssi_signo as i32).unwrap(); + assert_eq!(signo, signal::SIGUSR1); +} diff --git a/third_party/rust/nix/test/sys/test_socket.rs b/third_party/rust/nix/test/sys/test_socket.rs index ed1686e87d..90b8a6f528 100644 --- a/third_party/rust/nix/test/sys/test_socket.rs +++ b/third_party/rust/nix/test/sys/test_socket.rs @@ -1,4 +1,4 @@ -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] use crate::*; use libc::c_char; use nix::sys::socket::{getsockname, AddressFamily, UnixAddr}; @@ -21,7 +21,7 @@ pub fn test_timestamping() { }; use std::io::{IoSlice, IoSliceMut}; - let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap(); + let sock_addr = SockaddrIn::from_str("127.0.0.1:6797").unwrap(); let ssock = socket( AddressFamily::Inet, @@ -72,15 +72,134 @@ pub fn test_timestamping() { assert!(std::time::Duration::from(diff).as_secs() < 60); } +#[cfg(target_os = "freebsd")] +#[test] +pub fn test_timestamping_realtime() { + use nix::sys::socket::{ + recvmsg, sendmsg, setsockopt, socket, sockopt::ReceiveTimestamp, + sockopt::TsClock, ControlMessageOwned, MsgFlags, SockFlag, SockType, + SockaddrIn, SocketTimestamp, + }; + use std::io::{IoSlice, IoSliceMut}; + + let sock_addr = SockaddrIn::from_str("127.0.0.1:6792").unwrap(); + + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .expect("send socket failed"); + + let rsock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + + setsockopt(&rsock, ReceiveTimestamp, &true).unwrap(); + setsockopt(&rsock, TsClock, &SocketTimestamp::SO_TS_REALTIME).unwrap(); + + let sbuf = [0u8; 2048]; + let mut rbuf = [0u8; 2048]; + let flags = MsgFlags::empty(); + let iov1 = [IoSlice::new(&sbuf)]; + let mut iov2 = [IoSliceMut::new(&mut rbuf)]; + + let mut cmsg = cmsg_space!(nix::sys::time::TimeVal); + sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap(); + let recv = + recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags) + .unwrap(); + + let mut ts = None; + for c in recv.cmsgs() { + if let ControlMessageOwned::ScmRealtime(timeval) = c { + ts = Some(timeval); + } + } + let ts = ts.expect("ScmRealtime is present"); + let sys_time = + ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME) + .unwrap(); + let diff = if ts > sys_time { + ts - sys_time + } else { + sys_time - ts + }; + assert!(std::time::Duration::from(diff).as_secs() < 60); +} + +#[cfg(target_os = "freebsd")] +#[test] +pub fn test_timestamping_monotonic() { + use nix::sys::socket::{ + recvmsg, sendmsg, setsockopt, socket, sockopt::ReceiveTimestamp, + sockopt::TsClock, ControlMessageOwned, MsgFlags, SockFlag, SockType, + SockaddrIn, SocketTimestamp, + }; + use std::io::{IoSlice, IoSliceMut}; + + let sock_addr = SockaddrIn::from_str("127.0.0.1:6803").unwrap(); + + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .expect("send socket failed"); + + let rsock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + + setsockopt(&rsock, ReceiveTimestamp, &true).unwrap(); + setsockopt(&rsock, TsClock, &SocketTimestamp::SO_TS_MONOTONIC).unwrap(); + + let sbuf = [0u8; 2048]; + let mut rbuf = [0u8; 2048]; + let flags = MsgFlags::empty(); + let iov1 = [IoSlice::new(&sbuf)]; + let mut iov2 = [IoSliceMut::new(&mut rbuf)]; + + let mut cmsg = cmsg_space!(nix::sys::time::TimeVal); + sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap(); + let recv = + recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags) + .unwrap(); + + let mut ts = None; + for c in recv.cmsgs() { + if let ControlMessageOwned::ScmMonotonic(timeval) = c { + ts = Some(timeval); + } + } + let ts = ts.expect("ScmMonotonic is present"); + let sys_time = + ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_MONOTONIC) + .unwrap(); + let diff = sys_time - ts; // Monotonic clock sys_time must be greater + assert!(std::time::Duration::from(diff).as_secs() < 60); +} + #[test] pub fn test_path_to_sock_addr() { let path = "/foo/bar"; let actual = Path::new(path); let addr = UnixAddr::new(actual).unwrap(); - let expect: &[c_char] = unsafe { - slice::from_raw_parts(path.as_ptr() as *const c_char, path.len()) - }; + let expect: &[c_char] = + unsafe { slice::from_raw_parts(path.as_ptr().cast(), path.len()) }; assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect); assert_eq!(addr.path(), Some(actual)); @@ -105,7 +224,7 @@ pub fn test_addr_equality_path() { assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] pub fn test_abstract_sun_path_too_long() { let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough"); @@ -113,7 +232,7 @@ pub fn test_abstract_sun_path_too_long() { addr.expect_err("assertion failed"); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] pub fn test_addr_equality_abstract() { let name = String::from("nix\0abstract\0test"); @@ -129,7 +248,7 @@ pub fn test_addr_equality_abstract() { } // Test getting/setting abstract addresses (without unix socket creation) -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] pub fn test_abstract_uds_addr() { let empty = String::new(); @@ -151,7 +270,7 @@ pub fn test_abstract_uds_addr() { } // Test getting an unnamed address (without unix socket creation) -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] pub fn test_unnamed_uds_addr() { use crate::nix::sys::socket::SockaddrLike; @@ -200,7 +319,7 @@ pub fn test_socketpair() { SockFlag::empty(), ) .unwrap(); - write(fd1.as_raw_fd(), b"hello").unwrap(); + write(&fd1, b"hello").unwrap(); let mut buf = [0; 5]; read(fd2.as_raw_fd(), &mut buf).unwrap(); @@ -315,7 +434,7 @@ mod recvfrom { #[test] pub fn udp() { - let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap(); + let std_sa = SocketAddrV4::from_str("127.0.0.1:6795").unwrap(); let sock_addr = SockaddrIn::from(std_sa); let rsock = socket( AddressFamily::Inet, @@ -437,12 +556,7 @@ mod recvfrom { } } - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - ))] + #[cfg(any(linux_android, target_os = "freebsd", target_os = "netbsd"))] #[test] pub fn udp_sendmmsg() { use std::io::IoSlice; @@ -504,12 +618,7 @@ mod recvfrom { assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap()); } - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - ))] + #[cfg(any(linux_android, target_os = "freebsd", target_os = "netbsd"))] #[test] pub fn udp_recvmmsg() { use nix::sys::socket::{recvmmsg, MsgFlags}; @@ -565,7 +674,7 @@ mod recvfrom { let res: Vec> = recvmmsg( rsock.as_raw_fd(), &mut data, - msgs.iter(), + msgs.iter_mut(), MsgFlags::empty(), None, ) @@ -585,12 +694,7 @@ mod recvfrom { send_thread.join().unwrap(); } - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - ))] + #[cfg(any(linux_android, target_os = "freebsd", target_os = "netbsd"))] #[test] pub fn udp_recvmmsg_dontwait_short_read() { use nix::sys::socket::{recvmmsg, MsgFlags}; @@ -653,7 +757,7 @@ mod recvfrom { let res: Vec> = recvmmsg( rsock.as_raw_fd(), &mut data, - msgs.iter(), + msgs.iter_mut(), MsgFlags::MSG_DONTWAIT, None, ) @@ -674,10 +778,10 @@ mod recvfrom { #[test] pub fn udp_inet6() { let addr = std::net::Ipv6Addr::from_str("::1").unwrap(); - let rport = 6789; + let rport = 6796; let rstd_sa = SocketAddrV6::new(addr, rport, 0, 0); let raddr = SockaddrIn6::from(rstd_sa); - let sport = 6790; + let sport = 6798; let sstd_sa = SocketAddrV6::new(addr, sport, 0, 0); let saddr = SockaddrIn6::from(sstd_sa); let rsock = socket( @@ -757,7 +861,7 @@ pub fn test_scm_rights() { { let iov = [IoSlice::new(b"hello")]; - let fds = [r]; + let fds = [r.as_raw_fd()]; let cmsg = ControlMessage::ScmRights(&fds); assert_eq!( sendmsg::<()>( @@ -770,7 +874,6 @@ pub fn test_scm_rights() { .unwrap(), 5 ); - close(r).unwrap(); } { @@ -803,16 +906,15 @@ pub fn test_scm_rights() { let received_r = received_r.expect("Did not receive passed fd"); // Ensure that the received file descriptor works - write(w.as_raw_fd(), b"world").unwrap(); + write(&w, b"world").unwrap(); let mut buf = [0u8; 5]; read(received_r.as_raw_fd(), &mut buf).unwrap(); assert_eq!(&buf[..], b"world"); close(received_r).unwrap(); - close(w).unwrap(); } // Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] #[cfg_attr(qemu, ignore)] #[test] pub fn test_af_alg_cipher() { @@ -905,7 +1007,7 @@ pub fn test_af_alg_cipher() { // Disable the test on emulated platforms due to not enabled support of AF_ALG // in QEMU from rust cross -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] #[cfg_attr(qemu, ignore)] #[test] pub fn test_af_alg_aead() { @@ -1039,7 +1141,7 @@ pub fn test_af_alg_aead() { // This would be a more interesting test if we could assume that the test host // has more than one IP address (since we could select a different address to // test from). -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))] +#[cfg(any(target_os = "linux", apple_targets, target_os = "netbsd"))] #[test] pub fn test_sendmsg_ipv4packetinfo() { use cfg_if::cfg_if; @@ -1101,7 +1203,7 @@ pub fn test_sendmsg_ipv4packetinfo() { // test from). #[cfg(any( target_os = "linux", - target_os = "macos", + apple_targets, target_os = "netbsd", target_os = "freebsd" ))] @@ -1158,12 +1260,7 @@ pub fn test_sendmsg_ipv6packetinfo() { // // Note that binding to 0.0.0.0 is *required* on FreeBSD; sendmsg // returns EINVAL otherwise. (See FreeBSD's ip(4) man page.) -#[cfg(any( - target_os = "netbsd", - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", -))] +#[cfg(any(freebsdlike, netbsdlike))] #[test] pub fn test_sendmsg_ipv4sendsrcaddr() { use nix::sys::socket::{ @@ -1302,7 +1399,7 @@ pub fn test_sendmsg_empty_cmsgs() { ) .unwrap(); - for _ in msg.cmsgs() { + if msg.cmsgs().next().is_some() { panic!("unexpected cmsg"); } assert!(!msg @@ -1312,19 +1409,14 @@ pub fn test_sendmsg_empty_cmsgs() { } } -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", -))] +#[cfg(any(linux_android, freebsdlike))] #[test] fn test_scm_credentials() { use nix::sys::socket::{ recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage, ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixCredentials, }; - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] use nix::sys::socket::{setsockopt, sockopt::PassCred}; use nix::unistd::{getgid, getpid, getuid}; use std::io::{IoSlice, IoSliceMut}; @@ -1336,16 +1428,16 @@ fn test_scm_credentials() { SockFlag::empty(), ) .unwrap(); - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] setsockopt(&recv, PassCred, &true).unwrap(); { let iov = [IoSlice::new(b"hello")]; - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] let cred = UnixCredentials::new(); - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] let cmsg = ControlMessage::ScmCredentials(&cred); - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + #[cfg(freebsdlike)] let cmsg = ControlMessage::ScmCreds; assert_eq!( sendmsg::<()>( @@ -1376,9 +1468,9 @@ fn test_scm_credentials() { for cmsg in msg.cmsgs() { let cred = match cmsg { - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(linux_android)] ControlMessageOwned::ScmCredentials(cred) => cred, - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + #[cfg(freebsdlike)] ControlMessageOwned::ScmCreds(cred) => cred, other => panic!("unexpected cmsg {other:?}"), }; @@ -1398,7 +1490,7 @@ fn test_scm_credentials() { /// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single /// `sendmsg` call. -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] // qemu's handling of multiple cmsgs is bugged, ignore tests under emulation // see https://bugs.launchpad.net/qemu/+bug/1781280 #[cfg_attr(qemu, ignore)] @@ -1410,7 +1502,7 @@ fn test_scm_credentials_and_rights() { /// Ensure that passing a an oversized control message buffer to recvmsg /// still works. -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] // qemu's handling of multiple cmsgs is bugged, ignore tests under emulation // see https://bugs.launchpad.net/qemu/+bug/1781280 #[cfg_attr(qemu, ignore)] @@ -1420,7 +1512,7 @@ fn test_too_large_cmsgspace() { test_impl_scm_credentials_and_rights(space); } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_impl_scm_credentials_and_rights(mut space: Vec) { use libc::ucred; use nix::sys::socket::sockopt::PassCred; @@ -1451,7 +1543,7 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { gid: getgid().as_raw(), } .into(); - let fds = [r]; + let fds = [r.as_raw_fd()]; let cmsgs = [ ControlMessage::ScmCredentials(&cred), ControlMessage::ScmRights(&fds), @@ -1467,7 +1559,6 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { .unwrap(), 5 ); - close(r).unwrap(); } { @@ -1510,18 +1601,19 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { let received_r = received_r.expect("Did not receive passed fd"); // Ensure that the received file descriptor works - write(w.as_raw_fd(), b"world").unwrap(); + write(&w, b"world").unwrap(); let mut buf = [0u8; 5]; read(received_r.as_raw_fd(), &mut buf).unwrap(); assert_eq!(&buf[..], b"world"); close(received_r).unwrap(); - close(w).unwrap(); } // Test creating and using named unix domain sockets #[test] pub fn test_named_unixdomain() { - use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr}; + use nix::sys::socket::{ + accept, bind, connect, listen, socket, Backlog, UnixAddr, + }; use nix::sys::socket::{SockFlag, SockType}; use nix::unistd::{read, write}; use std::thread; @@ -1537,7 +1629,7 @@ pub fn test_named_unixdomain() { .expect("socket failed"); let sockaddr = UnixAddr::new(&sockname).unwrap(); bind(s1.as_raw_fd(), &sockaddr).expect("bind failed"); - listen(&s1, 10).expect("listen failed"); + listen(&s1, Backlog::new(10).unwrap()).expect("listen failed"); let thr = thread::spawn(move || { let s2 = socket( @@ -1548,7 +1640,7 @@ pub fn test_named_unixdomain() { ) .expect("socket failed"); connect(s2.as_raw_fd(), &sockaddr).expect("connect failed"); - write(s2.as_raw_fd(), b"hello").expect("write failed"); + write(&s2, b"hello").expect("write failed"); }); let s3 = accept(s1.as_raw_fd()).expect("accept failed"); @@ -1560,8 +1652,16 @@ pub fn test_named_unixdomain() { assert_eq!(&buf[..], b"hello"); } +#[test] +pub fn test_listen_wrongbacklog() { + use nix::sys::socket::Backlog; + + assert!(Backlog::new(libc::SOMAXCONN + 1).is_err()); + assert!(Backlog::new(-2).is_err()); +} + // Test using unnamed unix domain addresses -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] pub fn test_unnamed_unixdomain() { use nix::sys::socket::{getsockname, socketpair}; @@ -1581,7 +1681,7 @@ pub fn test_unnamed_unixdomain() { } // Test creating and using unnamed unix domain addresses for autobinding sockets -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] pub fn test_unnamed_unixdomain_autobind() { use nix::sys::socket::{bind, getsockname, socket}; @@ -1609,7 +1709,7 @@ pub fn test_unnamed_unixdomain_autobind() { } // Test creating and using named system control sockets -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(apple_targets)] #[test] pub fn test_syscontrol() { use nix::errno::Errno; @@ -1635,15 +1735,7 @@ pub fn test_syscontrol() { // connect(fd.as_raw_fd(), &sockaddr).expect("connect failed"); } -#[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", -))] +#[cfg(any(bsd, linux_android))] fn loopback_address( family: AddressFamily, ) -> Option { @@ -1670,20 +1762,16 @@ fn loopback_address( }) } -#[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", -))] +#[cfg(any(linux_android, apple_targets, target_os = "netbsd"))] // qemu doesn't seem to be emulating this correctly in these architectures #[cfg_attr( all( qemu, any( target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "powerpc64", ) ), @@ -1765,20 +1853,16 @@ pub fn test_recv_ipv4pktinfo() { } } -#[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", -))] +#[cfg(bsd)] // qemu doesn't seem to be emulating this correctly in these architectures #[cfg_attr( all( qemu, any( target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "powerpc64", ) ), @@ -1883,7 +1967,7 @@ pub fn test_recvif() { } } -#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +#[cfg(any(linux_android, target_os = "freebsd"))] #[cfg_attr(qemu, ignore)] #[test] pub fn test_recvif_ipv4() { @@ -1969,7 +2053,7 @@ pub fn test_recvif_ipv4() { } } -#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +#[cfg(any(linux_android, target_os = "freebsd"))] #[cfg_attr(qemu, ignore)] #[test] pub fn test_recvif_ipv6() { @@ -2055,22 +2139,16 @@ pub fn test_recvif_ipv6() { } } -#[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", -))] +#[cfg(any(linux_android, target_os = "freebsd", apple_targets, netbsdlike))] // qemu doesn't seem to be emulating this correctly in these architectures #[cfg_attr( all( qemu, any( target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "powerpc64", ) ), @@ -2152,7 +2230,7 @@ pub fn test_recv_ipv6pktinfo() { } } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] pub fn test_vsock() { use nix::sys::socket::SockaddrLike; @@ -2192,7 +2270,7 @@ pub fn test_vsock() { assert_eq!(addr3.as_ref().svm_port, addr1.port()); } -#[cfg(target_os = "macos")] +#[cfg(apple_targets)] #[test] pub fn test_vsock() { use nix::sys::socket::SockaddrLike; @@ -2329,12 +2407,17 @@ fn test_recvmmsg_timestampns() { // Receive the message let mut buffer = vec![0u8; message.len()]; let cmsgspace = nix::cmsg_space!(TimeSpec); - let iov = vec![[IoSliceMut::new(&mut buffer)]]; + let mut iov = vec![[IoSliceMut::new(&mut buffer)]]; let mut data = MultiHeaders::preallocate(1, Some(cmsgspace)); - let r: Vec> = - recvmmsg(in_socket.as_raw_fd(), &mut data, iov.iter(), flags, None) - .unwrap() - .collect(); + let r: Vec> = recvmmsg( + in_socket.as_raw_fd(), + &mut data, + iov.iter_mut(), + flags, + None, + ) + .unwrap() + .collect(); let rtime = match r[0].cmsgs().next() { Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, Some(_) => panic!("Unexpected control message"), @@ -2353,7 +2436,7 @@ fn test_recvmmsg_timestampns() { // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack // of QEMU support is suspected. #[cfg_attr(qemu, ignore)] -#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] +#[cfg(any(linux_android, target_os = "fuchsia"))] #[test] fn test_recvmsg_rxq_ovfl() { use nix::sys::socket::sockopt::{RcvBuf, RxqOvfl}; @@ -2447,7 +2530,7 @@ fn test_recvmsg_rxq_ovfl() { assert_eq!(drop_counter, 1); } -#[cfg(any(target_os = "linux", target_os = "android",))] +#[cfg(linux_android)] mod linux_errqueue { use super::FromStr; use nix::sys::socket::*; @@ -2685,3 +2768,160 @@ pub fn test_txtime() { recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, None, MsgFlags::empty()) .unwrap(); } + +// cfg needed for capability check. +#[cfg(linux_android)] +#[test] +fn test_icmp_protocol() { + use nix::sys::socket::{ + sendto, socket, AddressFamily, MsgFlags, SockFlag, SockProtocol, + SockType, SockaddrIn, + }; + + require_capability!("test_icmp_protocol", CAP_NET_RAW); + + let owned_fd = socket( + AddressFamily::Inet, + SockType::Raw, + SockFlag::empty(), + SockProtocol::Icmp, + ) + .unwrap(); + + // Send a minimal ICMP packet with no payload. + let packet = [ + 0x08, // Type + 0x00, // Code + 0x84, 0x85, // Checksum + 0x73, 0x8a, // ID + 0x00, 0x00, // Sequence Number + ]; + + let dest_addr = SockaddrIn::new(127, 0, 0, 1, 0); + sendto(owned_fd.as_raw_fd(), &packet, &dest_addr, MsgFlags::empty()) + .unwrap(); +} + +// test contains both recvmmsg and timestaping which is linux only +// there are existing tests for recvmmsg only in tests/ +#[cfg_attr(qemu, ignore)] +#[cfg(target_os = "linux")] +#[test] +fn test_recvmm2() -> nix::Result<()> { + use nix::sys::{ + socket::{ + bind, recvmmsg, sendmsg, setsockopt, socket, sockopt::Timestamping, + AddressFamily, ControlMessageOwned, MsgFlags, MultiHeaders, + SockFlag, SockType, SockaddrIn, TimestampingFlag, Timestamps, + }, + time::TimeSpec, + }; + use std::io::{IoSlice, IoSliceMut}; + use std::os::unix::io::AsRawFd; + use std::str::FromStr; + + let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap(); + + let ssock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + )?; + + let rsock = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::SOCK_NONBLOCK, + None, + )?; + + bind(rsock.as_raw_fd(), &sock_addr)?; + + setsockopt(&rsock, Timestamping, &TimestampingFlag::all())?; + + let sbuf = (0..400).map(|i| i as u8).collect::>(); + + let mut recv_buf = vec![0; 1024]; + + let mut recv_iovs = Vec::new(); + let mut pkt_iovs = Vec::new(); + + for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() { + pkt_iovs.push(IoSliceMut::new(chunk)); + if ix % 2 == 1 { + recv_iovs.push(pkt_iovs); + pkt_iovs = Vec::new(); + } + } + drop(pkt_iovs); + + let flags = MsgFlags::empty(); + let iov1 = [IoSlice::new(&sbuf)]; + + let cmsg = cmsg_space!(Timestamps); + sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap(); + + let mut data = MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg)); + + let t = TimeSpec::from_duration(std::time::Duration::from_secs(10)); + + let recv = recvmmsg( + rsock.as_raw_fd(), + &mut data, + recv_iovs.iter_mut(), + flags, + Some(t), + )?; + + for rmsg in recv { + #[cfg(not(any(qemu, target_arch = "aarch64")))] + let mut saw_time = false; + let mut recvd = 0; + for cmsg in rmsg.cmsgs() { + if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg { + let ts = timestamps.system; + + let sys_time = nix::time::clock_gettime( + nix::time::ClockId::CLOCK_REALTIME, + )?; + let diff = if ts > sys_time { + ts - sys_time + } else { + sys_time - ts + }; + assert!(std::time::Duration::from(diff).as_secs() < 60); + #[cfg(not(any(qemu, target_arch = "aarch64")))] + { + saw_time = true; + } + } + } + + #[cfg(not(any(qemu, target_arch = "aarch64")))] + assert!(saw_time); + + for iov in rmsg.iovs() { + recvd += iov.len(); + } + assert_eq!(recvd, 400); + } + + Ok(()) +} + +#[cfg(not(target_os = "redox"))] +#[test] +fn can_use_cmsg_space() { + let _ = cmsg_space!(u8); +} + +#[cfg(not(any(linux_android, target_os = "redox", target_os = "haiku")))] +#[test] +fn can_open_routing_socket() { + use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType}; + + let _ = + socket(AddressFamily::Route, SockType::Raw, SockFlag::empty(), None) + .expect("Failed to open routing socket"); +} diff --git a/third_party/rust/nix/test/sys/test_sockopt.rs b/third_party/rust/nix/test/sys/test_sockopt.rs index 0e34917325..a99d4e39ed 100644 --- a/third_party/rust/nix/test/sys/test_sockopt.rs +++ b/third_party/rust/nix/test/sys/test_sockopt.rs @@ -1,14 +1,14 @@ -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] use crate::*; use nix::sys::socket::{ getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag, SockProtocol, SockType, }; use rand::{thread_rng, Rng}; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd}; // NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not. -#[cfg(any(target_os = "dragonfly", target_os = "freebsd",))] +#[cfg(freebsdlike)] #[test] pub fn test_local_peercred_seqpacket() { use nix::{ @@ -29,12 +29,7 @@ pub fn test_local_peercred_seqpacket() { assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); } -#[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "macos", - target_os = "ios" -))] +#[cfg(any(freebsdlike, apple_targets))] #[test] pub fn test_local_peercred_stream() { use nix::{ @@ -55,7 +50,7 @@ pub fn test_local_peercred_stream() { assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); } -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(apple_targets)] #[test] pub fn test_local_peer_pid() { use nix::sys::socket::socketpair; @@ -108,15 +103,42 @@ fn test_so_buf() { assert!(actual >= bufsize); } +#[cfg(target_os = "freebsd")] +#[test] +fn test_so_listen_q_limit() { + use nix::sys::socket::{bind, listen, Backlog, SockaddrIn}; + use std::net::SocketAddrV4; + use std::str::FromStr; + + let std_sa = SocketAddrV4::from_str("127.0.0.1:4004").unwrap(); + let sock_addr = SockaddrIn::from(std_sa); + + let rsock = socket( + AddressFamily::Inet, + SockType::Stream, + SockFlag::empty(), + SockProtocol::Tcp, + ) + .unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + let pre_limit = getsockopt(&rsock, sockopt::ListenQLimit).unwrap(); + assert_eq!(pre_limit, 0); + listen(&rsock, Backlog::new(42).unwrap()).unwrap(); + let post_limit = getsockopt(&rsock, sockopt::ListenQLimit).unwrap(); + assert_eq!(post_limit, 42); +} + #[test] fn test_so_tcp_maxseg() { - use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn}; + use nix::sys::socket::{ + accept, bind, connect, getsockname, listen, Backlog, SockaddrIn, + }; use nix::unistd::write; use std::net::SocketAddrV4; use std::str::FromStr; - let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); + let std_sa = SocketAddrV4::from_str("127.0.0.1:0").unwrap(); + let mut sock_addr = SockaddrIn::from(std_sa); let rsock = socket( AddressFamily::Inet, @@ -126,13 +148,14 @@ fn test_so_tcp_maxseg() { ) .unwrap(); bind(rsock.as_raw_fd(), &sock_addr).unwrap(); - listen(&rsock, 10).unwrap(); + sock_addr = getsockname(rsock.as_raw_fd()).unwrap(); + listen(&rsock, Backlog::new(10).unwrap()).unwrap(); let initial = getsockopt(&rsock, sockopt::TcpMaxSeg).unwrap(); // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger // than 700 cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + if #[cfg(linux_android)] { let segsize: u32 = 873; assert!(initial < segsize); setsockopt(&rsock, sockopt::TcpMaxSeg, &segsize).unwrap(); @@ -151,12 +174,13 @@ fn test_so_tcp_maxseg() { .unwrap(); connect(ssock.as_raw_fd(), &sock_addr).unwrap(); let rsess = accept(rsock.as_raw_fd()).unwrap(); - write(rsess, b"hello").unwrap(); + let rsess = unsafe { OwnedFd::from_raw_fd(rsess) }; + write(&rsess, b"hello").unwrap(); let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap(); // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary. cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + if #[cfg(linux_android)] { assert!((segsize - 100) <= actual); assert!(actual <= segsize); } else { @@ -181,11 +205,10 @@ fn test_so_type() { /// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket /// types. Regression test for https://github.com/nix-rust/nix/issues/1819 -#[cfg(any(target_os = "android", target_os = "linux",))] +#[cfg(linux_android)] #[test] fn test_so_type_unknown() { use nix::errno::Errno; - use std::os::unix::io::{FromRawFd, OwnedFd}; require_capability!("test_so_type", CAP_NET_RAW); let raw_fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) }; @@ -229,7 +252,7 @@ fn test_tcp_congestion() { } #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_bindtodevice() { skip_if_not_root!("test_bindtodevice"); @@ -259,12 +282,7 @@ fn test_so_tcp_keepalive() { setsockopt(&fd, sockopt::KeepAlive, &true).unwrap(); assert!(getsockopt(&fd, sockopt::KeepAlive).unwrap()); - #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux" - ))] + #[cfg(any(linux_android, freebsdlike))] { let x = getsockopt(&fd, sockopt::TcpKeepIdle).unwrap(); setsockopt(&fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap(); @@ -281,14 +299,14 @@ fn test_so_tcp_keepalive() { } #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[cfg_attr(qemu, ignore)] fn test_get_mtu() { use nix::sys::socket::{bind, connect, SockaddrIn}; use std::net::SocketAddrV4; use std::str::FromStr; - let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap(); + let std_sa = SocketAddrV4::from_str("127.0.0.1:0").unwrap(); let std_sb = SocketAddrV4::from_str("127.0.0.1:4002").unwrap(); let usock = socket( @@ -308,7 +326,7 @@ fn test_get_mtu() { } #[test] -#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] +#[cfg(any(linux_android, target_os = "freebsd"))] fn test_ttl_opts() { let fd4 = socket( AddressFamily::Inet, @@ -331,7 +349,48 @@ fn test_ttl_opts() { } #[test] -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(any(linux_android, target_os = "freebsd"))] +fn test_multicast_ttl_opts_ipv4() { + let fd4 = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + setsockopt(&fd4, sockopt::IpMulticastTtl, &2) + .expect("setting ipmulticastttl on an inet socket should succeed"); +} + +#[test] +#[cfg(linux_android)] +fn test_multicast_ttl_opts_ipv6() { + let fd6 = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + setsockopt(&fd6, sockopt::IpMulticastTtl, &2) + .expect("setting ipmulticastttl on an inet6 socket should succeed"); +} + +#[test] +fn test_ipv6_multicast_hops() { + let fd6 = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + setsockopt(&fd6, sockopt::Ipv6MulticastHops, &7) + .expect("setting ipv6multicasthops on an inet6 socket should succeed"); +} + +#[test] +#[cfg(apple_targets)] fn test_dontfrag_opts() { let fd4 = socket( AddressFamily::Inet, @@ -361,12 +420,7 @@ fn test_dontfrag_opts() { } #[test] -#[cfg(any( - target_os = "android", - target_os = "ios", - target_os = "linux", - target_os = "macos", -))] +#[cfg(any(linux_android, apple_targets))] // Disable the test under emulation because it fails in Cirrus-CI. Lack // of QEMU support is suspected. #[cfg_attr(qemu, ignore)] @@ -446,3 +500,331 @@ fn test_ipv6_tclass() { setsockopt(&fd, sockopt::Ipv6TClass, &class).unwrap(); assert_eq!(getsockopt(&fd, sockopt::Ipv6TClass).unwrap(), class); } + +#[test] +#[cfg(target_os = "freebsd")] +fn test_receive_timestamp() { + let fd = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap(); + assert!(getsockopt(&fd, sockopt::ReceiveTimestamp).unwrap()); +} + +#[test] +#[cfg(target_os = "freebsd")] +fn test_ts_clock_realtime_micro() { + use nix::sys::socket::SocketTimestamp; + + let fd = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + + // FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP. + setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap(); + + setsockopt( + &fd, + sockopt::TsClock, + &SocketTimestamp::SO_TS_REALTIME_MICRO, + ) + .unwrap(); + assert_eq!( + getsockopt(&fd, sockopt::TsClock).unwrap(), + SocketTimestamp::SO_TS_REALTIME_MICRO + ); +} + +#[test] +#[cfg(target_os = "freebsd")] +fn test_ts_clock_bintime() { + use nix::sys::socket::SocketTimestamp; + + let fd = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + + // FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP. + setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap(); + + setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_BINTIME).unwrap(); + assert_eq!( + getsockopt(&fd, sockopt::TsClock).unwrap(), + SocketTimestamp::SO_TS_BINTIME + ); +} + +#[test] +#[cfg(target_os = "freebsd")] +fn test_ts_clock_realtime() { + use nix::sys::socket::SocketTimestamp; + + let fd = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + + // FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP. + setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap(); + + setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_REALTIME) + .unwrap(); + assert_eq!( + getsockopt(&fd, sockopt::TsClock).unwrap(), + SocketTimestamp::SO_TS_REALTIME + ); +} + +#[test] +#[cfg(target_os = "freebsd")] +fn test_ts_clock_monotonic() { + use nix::sys::socket::SocketTimestamp; + + let fd = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + + // FreeBSD setsockopt docs say to set SO_TS_CLOCK after setting SO_TIMESTAMP. + setsockopt(&fd, sockopt::ReceiveTimestamp, &true).unwrap(); + + setsockopt(&fd, sockopt::TsClock, &SocketTimestamp::SO_TS_MONOTONIC) + .unwrap(); + assert_eq!( + getsockopt(&fd, sockopt::TsClock).unwrap(), + SocketTimestamp::SO_TS_MONOTONIC + ); +} + +#[test] +#[cfg(linux_android)] +// Disable the test under emulation because it fails with ENOPROTOOPT in CI +// on cross target. Lack of QEMU support is suspected. +#[cfg_attr(qemu, ignore)] +fn test_ip_bind_address_no_port() { + let fd = socket( + AddressFamily::Inet, + SockType::Stream, + SockFlag::empty(), + SockProtocol::Tcp, + ) + .unwrap(); + setsockopt(&fd, sockopt::IpBindAddressNoPort, &true).expect( + "setting IP_BIND_ADDRESS_NO_PORT on an inet stream socket should succeed", + ); + assert!(getsockopt(&fd, sockopt::IpBindAddressNoPort).expect( + "getting IP_BIND_ADDRESS_NO_PORT on an inet stream socket should succeed", + )); + setsockopt(&fd, sockopt::IpBindAddressNoPort, &false).expect( + "unsetting IP_BIND_ADDRESS_NO_PORT on an inet stream socket should succeed", + ); + assert!(!getsockopt(&fd, sockopt::IpBindAddressNoPort).expect( + "getting IP_BIND_ADDRESS_NO_PORT on an inet stream socket should succeed", + )); +} + +#[test] +#[cfg(linux_android)] +fn test_tcp_fast_open_connect() { + let fd = socket( + AddressFamily::Inet, + SockType::Stream, + SockFlag::empty(), + SockProtocol::Tcp, + ) + .unwrap(); + setsockopt(&fd, sockopt::TcpFastOpenConnect, &true).expect( + "setting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed", + ); + assert!(getsockopt(&fd, sockopt::TcpFastOpenConnect).expect( + "getting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed", + )); + setsockopt(&fd, sockopt::TcpFastOpenConnect, &false).expect( + "unsetting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed", + ); + assert!(!getsockopt(&fd, sockopt::TcpFastOpenConnect).expect( + "getting TCP_FASTOPEN_CONNECT on an inet stream socket should succeed", + )); +} + +#[cfg(linux_android)] +#[test] +fn can_get_peercred_on_unix_socket() { + use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType}; + + let (a, b) = socketpair( + AddressFamily::Unix, + SockType::Stream, + None, + SockFlag::empty(), + ) + .unwrap(); + let a_cred = getsockopt(&a, sockopt::PeerCredentials).unwrap(); + let b_cred = getsockopt(&b, sockopt::PeerCredentials).unwrap(); + assert_eq!(a_cred, b_cred); + assert_ne!(a_cred.pid(), 0); +} + +#[test] +fn is_socket_type_unix() { + use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType}; + + let (a, _b) = socketpair( + AddressFamily::Unix, + SockType::Stream, + None, + SockFlag::empty(), + ) + .unwrap(); + let a_type = getsockopt(&a, sockopt::SockType).unwrap(); + assert_eq!(a_type, SockType::Stream); +} + +#[test] +fn is_socket_type_dgram() { + use nix::sys::socket::{ + getsockopt, sockopt, AddressFamily, SockFlag, SockType, + }; + + let s = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ) + .unwrap(); + let s_type = getsockopt(&s, sockopt::SockType).unwrap(); + assert_eq!(s_type, SockType::Datagram); +} + +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +#[test] +fn can_get_listen_on_tcp_socket() { + use nix::sys::socket::{ + getsockopt, listen, socket, sockopt, AddressFamily, Backlog, SockFlag, + SockType, + }; + + let s = socket( + AddressFamily::Inet, + SockType::Stream, + SockFlag::empty(), + None, + ) + .unwrap(); + let s_listening = getsockopt(&s, sockopt::AcceptConn).unwrap(); + assert!(!s_listening); + listen(&s, Backlog::new(10).unwrap()).unwrap(); + let s_listening2 = getsockopt(&s, sockopt::AcceptConn).unwrap(); + assert!(s_listening2); +} + +#[cfg(target_os = "linux")] +// Some architectures running under cross don't support `setsockopt(SOL_TCP, TCP_ULP)` +// because the cross image is based on Ubuntu 16.04 which predates TCP ULP support +// (it was added in kernel v4.13 released in 2017). For these architectures, +// the `setsockopt(SOL_TCP, TCP_ULP, "tls", sizeof("tls"))` call succeeds +// but the subsequent `setsockopt(SOL_TLS, TLS_TX, ...)` call fails with `ENOPROTOOPT`. +// It's as if the first `setsockopt` call enabled some other option, not `TCP_ULP`. +// For example, `strace` says: +// +// [pid 813] setsockopt(4, SOL_TCP, 0x1f /* TCP_??? */, [7564404], 4) = 0 +// +// It's not clear why `setsockopt(SOL_TCP, TCP_ULP)` succeeds if the container image libc doesn't support it, +// but in any case we can't run the test on such an architecture, so skip it. +#[cfg_attr(qemu, ignore)] +#[test] +fn test_ktls() { + use nix::sys::socket::{ + accept, bind, connect, getsockname, listen, Backlog, SockaddrIn, + }; + use std::net::SocketAddrV4; + use std::str::FromStr; + + let std_sa = SocketAddrV4::from_str("127.0.0.1:0").unwrap(); + let mut sock_addr = SockaddrIn::from(std_sa); + + let rsock = socket( + AddressFamily::Inet, + SockType::Stream, + SockFlag::empty(), + SockProtocol::Tcp, + ) + .unwrap(); + bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + sock_addr = getsockname(rsock.as_raw_fd()).unwrap(); + listen(&rsock, Backlog::new(10).unwrap()).unwrap(); + + let ssock = socket( + AddressFamily::Inet, + SockType::Stream, + SockFlag::empty(), + SockProtocol::Tcp, + ) + .unwrap(); + connect(ssock.as_raw_fd(), &sock_addr).unwrap(); + + let _rsess = accept(rsock.as_raw_fd()).unwrap(); + + match setsockopt(&ssock, sockopt::TcpUlp::default(), b"tls") { + Ok(()) => (), + + // TLS ULP is not enabled, so we can't test kTLS. + Err(nix::Error::ENOENT) => skip!("TLS ULP is not enabled"), + + Err(err) => panic!("{err:?}"), + } + + // In real life we would do a TLS handshake and extract the protocol version and secrets. + // For this test we just make some up. + + let tx = sockopt::TlsCryptoInfo::Aes128Gcm(libc::tls12_crypto_info_aes_gcm_128 { + info: libc::tls_crypto_info { + version: libc::TLS_1_2_VERSION, + cipher_type: libc::TLS_CIPHER_AES_GCM_128, + }, + iv: *b"\x04\x05\x06\x07\x08\x09\x0a\x0b", + key: *b"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + salt: *b"\x00\x01\x02\x03", + rec_seq: *b"\x00\x00\x00\x00\x00\x00\x00\x00", + }); + setsockopt(&ssock, sockopt::TcpTlsTx, &tx) + .expect("setting TLS_TX after enabling TLS ULP should succeed"); + + let rx = sockopt::TlsCryptoInfo::Aes128Gcm(libc::tls12_crypto_info_aes_gcm_128 { + info: libc::tls_crypto_info { + version: libc::TLS_1_2_VERSION, + cipher_type: libc::TLS_CIPHER_AES_GCM_128, + }, + iv: *b"\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb", + key: *b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef", + salt: *b"\xf0\xf1\xf2\xf3", + rec_seq: *b"\x00\x00\x00\x00\x00\x00\x00\x00", + }); + match setsockopt(&ssock, sockopt::TcpTlsRx, &rx) { + Ok(()) => (), + Err(nix::Error::ENOPROTOOPT) => { + // TLS_TX was added in v4.13 and TLS_RX in v4.17, so we appear to be between that range. + // It's good enough that TLS_TX worked, so let the test succeed. + } + Err(err) => panic!("{err:?}"), + } +} diff --git a/third_party/rust/nix/test/sys/test_statfs.rs b/third_party/rust/nix/test/sys/test_statfs.rs new file mode 100644 index 0000000000..66b3f2ce96 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_statfs.rs @@ -0,0 +1,99 @@ +use nix::sys::statfs::*; +use nix::sys::statvfs::*; +use std::fs::File; +use std::path::Path; + +fn check_fstatfs(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()).unwrap(); + let file = File::open(path).unwrap(); + let fs = fstatfs(&file).unwrap(); + assert_fs_equals(fs, vfs); +} + +fn check_statfs(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()).unwrap(); + let fs = statfs(path.as_bytes()).unwrap(); + assert_fs_equals(fs, vfs); +} + +fn check_fstatfs_strict(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()); + let file = File::open(path).unwrap(); + let fs = fstatfs(&file); + assert_fs_equals_strict(fs.unwrap(), vfs.unwrap()) +} + +fn check_statfs_strict(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()); + let fs = statfs(path.as_bytes()); + assert_fs_equals_strict(fs.unwrap(), vfs.unwrap()) +} + +// 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); +} + +#[test] +fn statfs_call() { + check_statfs("/tmp"); + check_statfs("/dev"); + check_statfs("/run"); + check_statfs("/"); +} + +#[test] +fn fstatfs_call() { + check_fstatfs("/tmp"); + check_fstatfs("/dev"); + check_fstatfs("/run"); + check_fstatfs("/"); +} + +// This test is ignored because files_free/blocks_free can change after statvfs call and before +// statfs call. +#[test] +#[ignore] +fn statfs_call_strict() { + check_statfs_strict("/tmp"); + check_statfs_strict("/dev"); + check_statfs_strict("/run"); + check_statfs_strict("/"); +} + +// This test is ignored because files_free/blocks_free can change after statvfs call and before +// fstatfs call. +#[test] +#[ignore] +fn fstatfs_call_strict() { + check_fstatfs_strict("/tmp"); + check_fstatfs_strict("/dev"); + check_fstatfs_strict("/run"); + check_fstatfs_strict("/"); +} + +// The cast is not unnecessary on all platforms. +#[allow(clippy::unnecessary_cast)] +fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) { + assert_eq!(fs.files_free() as u64, vfs.files_free() as u64); + assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64); + assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64); + 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); +} diff --git a/third_party/rust/nix/test/sys/test_statvfs.rs b/third_party/rust/nix/test/sys/test_statvfs.rs new file mode 100644 index 0000000000..5c80965253 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_statvfs.rs @@ -0,0 +1,13 @@ +use nix::sys::statvfs::*; +use std::fs::File; + +#[test] +fn statvfs_call() { + statvfs(&b"/"[..]).unwrap(); +} + +#[test] +fn fstatvfs_call() { + let root = File::open("/").unwrap(); + fstatvfs(&root).unwrap(); +} diff --git a/third_party/rust/nix/test/sys/test_termios.rs b/third_party/rust/nix/test/sys/test_termios.rs index 83919378a7..35cc7ab739 100644 --- a/third_party/rust/nix/test/sys/test_termios.rs +++ b/third_party/rust/nix/test/sys/test_termios.rs @@ -4,17 +4,26 @@ use tempfile::tempfile; use nix::errno::Errno; use nix::fcntl; use nix::pty::openpty; -use nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags}; +use nix::sys::termios::{self, tcgetattr, BaudRate, LocalFlags, OutputFlags}; use nix::unistd::{read, write}; /// Helper function analogous to `std::io::Write::write_all`, but for `Fd`s fn write_all(f: Fd, buf: &[u8]) { let mut len = 0; while len < buf.len() { - len += write(f.as_fd().as_raw_fd(), &buf[len..]).unwrap(); + len += write(f.as_fd(), &buf[len..]).unwrap(); } } +#[test] +fn test_baudrate_try_from() { + assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0)); + #[cfg(not(target_os = "haiku"))] + BaudRate::try_from(999999999).expect_err("assertion failed"); + #[cfg(target_os = "haiku")] + BaudRate::try_from(99).expect_err("assertion failed"); +} + // Test tcgetattr on a terminal #[test] fn test_tcgetattr_pty() { diff --git a/third_party/rust/nix/test/sys/test_time.rs b/third_party/rust/nix/test/sys/test_time.rs new file mode 100644 index 0000000000..0510225a92 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_time.rs @@ -0,0 +1,91 @@ +use nix::sys::time::{TimeSpec, TimeVal, TimeValLike}; +use std::time::Duration; + +#[test] +pub fn test_timespec() { + assert_ne!(TimeSpec::seconds(1), TimeSpec::zero()); + assert_eq!( + TimeSpec::seconds(1) + TimeSpec::seconds(2), + TimeSpec::seconds(3) + ); + assert_eq!( + TimeSpec::minutes(3) + TimeSpec::seconds(2), + TimeSpec::seconds(182) + ); +} + +#[test] +pub fn test_timespec_from() { + let duration = Duration::new(123, 123_456_789); + let timespec = TimeSpec::nanoseconds(123_123_456_789); + + assert_eq!(TimeSpec::from(duration), timespec); + assert_eq!(Duration::from(timespec), duration); +} + +#[test] +pub fn test_timespec_neg() { + let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123); + let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123); + + assert_eq!(a, -b); +} + +#[test] +pub fn test_timespec_ord() { + assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000)); + assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001)); + assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999)); + assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999)); + assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001)); +} + +#[test] +pub fn test_timespec_fmt() { + assert_eq!(TimeSpec::zero().to_string(), "0 seconds"); + assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds"); + assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds"); +} + +#[test] +pub fn test_timeval() { + assert_ne!(TimeVal::seconds(1), TimeVal::zero()); + assert_eq!( + TimeVal::seconds(1) + TimeVal::seconds(2), + TimeVal::seconds(3) + ); + assert_eq!( + TimeVal::minutes(3) + TimeVal::seconds(2), + TimeVal::seconds(182) + ); +} + +#[test] +pub fn test_timeval_ord() { + assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000)); + assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001)); + assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999)); + assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999)); + assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001)); +} + +#[test] +pub fn test_timeval_neg() { + let a = TimeVal::seconds(1) + TimeVal::microseconds(123); + let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123); + + assert_eq!(a, -b); +} + +#[test] +pub fn test_timeval_fmt() { + assert_eq!(TimeVal::zero().to_string(), "0 seconds"); + assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds"); + assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds"); +} diff --git a/third_party/rust/nix/test/sys/test_timer.rs b/third_party/rust/nix/test/sys/test_timer.rs new file mode 100644 index 0000000000..ffd146867b --- /dev/null +++ b/third_party/rust/nix/test/sys/test_timer.rs @@ -0,0 +1,102 @@ +use nix::sys::signal::{ + sigaction, SaFlags, SigAction, SigEvent, SigHandler, SigSet, SigevNotify, + Signal, +}; +use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags}; +use nix::time::ClockId; +use std::convert::TryFrom; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; +use std::time::{Duration, Instant}; + +const SIG: Signal = Signal::SIGALRM; +static ALARM_CALLED: AtomicBool = AtomicBool::new(false); + +pub extern "C" fn handle_sigalarm(raw_signal: libc::c_int) { + let signal = Signal::try_from(raw_signal).unwrap(); + if signal == SIG { + ALARM_CALLED.store(true, Ordering::Release); + } +} + +#[test] +fn alarm_fires() { + // Avoid interfering with other signal using tests by taking a mutex shared + // among other tests in this crate. + let _m = crate::SIGNAL_MTX.lock(); + const TIMER_PERIOD: Duration = Duration::from_millis(100); + + // + // Setup + // + + // Create a handler for the test signal, `SIG`. The handler is responsible + // for flipping `ALARM_CALLED`. + let handler = SigHandler::Handler(handle_sigalarm); + let signal_action = + SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty()); + let old_handler = unsafe { + sigaction(SIG, &signal_action) + .expect("unable to set signal handler for alarm") + }; + + // Create the timer. We use the monotonic clock here, though any would do + // really. The timer is set to fire every 250 milliseconds with no delay for + // the initial firing. + let clockid = ClockId::CLOCK_MONOTONIC; + let sigevent = SigEvent::new(SigevNotify::SigevSignal { + signal: SIG, + si_value: 0, + }); + let mut timer = + Timer::new(clockid, sigevent).expect("failed to create timer"); + let expiration = Expiration::Interval(TIMER_PERIOD.into()); + let flags = TimerSetTimeFlags::empty(); + timer.set(expiration, flags).expect("could not set timer"); + + // + // Test + // + + // Determine that there's still an expiration tracked by the + // timer. Depending on when this runs either an `Expiration::Interval` or + // `Expiration::IntervalDelayed` will be present. That is, if the timer has + // not fired yet we'll get our original `expiration`, else the one that + // represents a delay to the next expiration. We're only interested in the + // timer still being extant. + match timer.get() { + Ok(Some(exp)) => assert!(matches!( + exp, + Expiration::Interval(..) | Expiration::IntervalDelayed(..) + )), + _ => panic!("timer lost its expiration"), + } + + // Wait for 2 firings of the alarm before checking that it has fired and + // been handled at least the once. If we wait for 3 seconds and the handler + // is never called something has gone sideways and the test fails. + let starttime = Instant::now(); + loop { + thread::sleep(2 * TIMER_PERIOD); + if ALARM_CALLED.load(Ordering::Acquire) { + break; + } + if starttime.elapsed() > Duration::from_secs(3) { + panic!("Timeout waiting for SIGALRM"); + } + } + + // Cleanup: + // 1) deregister the OS's timer. + // 2) Wait for a full timer period, since POSIX does not require that + // disabling the timer will clear pending signals, and on NetBSD at least + // it does not. + // 2) Replace the old signal handler now that we've completed the test. If + // the test fails this process panics, so the fact we might not get here + // is okay. + drop(timer); + thread::sleep(TIMER_PERIOD); + unsafe { + sigaction(SIG, &old_handler).expect("unable to reset signal handler"); + } +} diff --git a/third_party/rust/nix/test/sys/test_uio.rs b/third_party/rust/nix/test/sys/test_uio.rs index fc09465f19..d035a7bb04 100644 --- a/third_party/rust/nix/test/sys/test_uio.rs +++ b/third_party/rust/nix/test/sys/test_uio.rs @@ -4,7 +4,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use std::fs::OpenOptions; use std::io::IoSlice; -use std::os::unix::io::{FromRawFd, OwnedFd}; +use std::os::unix::io::AsRawFd; use std::{cmp, iter}; #[cfg(not(target_os = "redox"))] @@ -44,22 +44,17 @@ fn test_writev() { // FileDesc will close its filedesc (reader). let mut read_buf: Vec = iter::repeat(0u8).take(128 * 16).collect(); - // Temporary workaround to cope with the existing RawFd pipe(2), should be - // removed when pipe(2) becomes I/O-safe. - let writer = unsafe { OwnedFd::from_raw_fd(writer) }; - // Blocking io, should write all data. let write_res = writev(&writer, &iovecs); let written = write_res.expect("couldn't write"); // Check whether we written all data assert_eq!(to_write.len(), written); - let read_res = read(reader, &mut read_buf[..]); + let read_res = read(reader.as_raw_fd(), &mut read_buf[..]); let read = read_res.expect("couldn't read"); // Check we have read as much as we written assert_eq!(read, written); // Check equality of written and read data assert_eq!(&to_write, &read_buf); - close(reader).expect("closed reader"); } #[test] @@ -92,10 +87,6 @@ fn test_readv() { // Blocking io, should write all data. write(writer, &to_write).expect("write failed"); - // Temporary workaround to cope with the existing RawFd pipe(2), should be - // removed when pipe(2) becomes I/O-safe. - let reader = unsafe { OwnedFd::from_raw_fd(reader) }; - let read = readv(&reader, &mut iovecs[..]).expect("read failed"); // Check whether we've read all data assert_eq!(to_write.len(), read); @@ -108,7 +99,6 @@ fn test_readv() { assert_eq!(read_buf.len(), to_write.len()); // Check equality of written and read data assert_eq!(&read_buf, &to_write); - close(writer).expect("couldn't close writer"); } #[test] @@ -150,7 +140,11 @@ fn test_pread() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "haiku")))] +#[cfg(not(any( + target_os = "redox", + target_os = "haiku", + target_os = "solaris" +)))] fn test_pwritev() { use std::io::Read; @@ -185,7 +179,11 @@ fn test_pwritev() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "haiku")))] +#[cfg(not(any( + target_os = "redox", + target_os = "haiku", + target_os = "solaris" +)))] fn test_preadv() { use std::io::Write; @@ -230,6 +228,7 @@ fn test_process_vm_readv() { use nix::sys::signal::*; use nix::sys::wait::*; use nix::unistd::ForkResult::*; + use std::os::unix::io::AsRawFd; require_capability!("test_process_vm_readv", CAP_SYS_PTRACE); let _m = crate::FORK_MTX.lock(); @@ -241,10 +240,10 @@ fn test_process_vm_readv() { let (r, w) = pipe().unwrap(); match unsafe { fork() }.expect("Error: Fork Failed") { Parent { child } => { - close(w).unwrap(); + drop(w); // wait for child - read(r, &mut [0u8]).unwrap(); - close(r).unwrap(); + read(r.as_raw_fd(), &mut [0u8]).unwrap(); + drop(r); let ptr = vector.as_ptr() as usize; let remote_iov = RemoteIoVec { base: ptr, len: 5 }; @@ -263,12 +262,11 @@ fn test_process_vm_readv() { assert_eq!(20u8, buf.iter().sum()); } Child => { - let _ = close(r); + drop(r); for i in &mut vector { *i += 1; } let _ = write(w, b"\0"); - let _ = close(w); loop { pause(); } diff --git a/third_party/rust/nix/test/sys/test_utsname.rs b/third_party/rust/nix/test/sys/test_utsname.rs new file mode 100644 index 0000000000..8f84ac074f --- /dev/null +++ b/third_party/rust/nix/test/sys/test_utsname.rs @@ -0,0 +1,17 @@ +#[cfg(target_os = "linux")] +#[test] +pub fn test_uname_linux() { + assert_eq!(nix::sys::utsname::uname().unwrap().sysname(), "Linux"); +} + +#[cfg(apple_targets)] +#[test] +pub fn test_uname_darwin() { + assert_eq!(nix::sys::utsname::uname().unwrap().sysname(), "Darwin"); +} + +#[cfg(target_os = "freebsd")] +#[test] +pub fn test_uname_freebsd() { + assert_eq!(nix::sys::utsname::uname().unwrap().sysname(), "FreeBSD"); +} diff --git a/third_party/rust/nix/test/sys/test_wait.rs b/third_party/rust/nix/test/sys/test_wait.rs index d472f1ec19..365b0165f8 100644 --- a/third_party/rust/nix/test/sys/test_wait.rs +++ b/third_party/rust/nix/test/sys/test_wait.rs @@ -33,7 +33,12 @@ fn test_wait_signal() { //target_os = "haiku", all(target_os = "linux", not(target_env = "uclibc")), ))] -#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] +#[cfg(not(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" +)))] fn test_waitid_signal() { let _m = crate::FORK_MTX.lock(); @@ -76,7 +81,12 @@ fn test_wait_exit() { target_os = "haiku", all(target_os = "linux", not(target_env = "uclibc")), ))] -#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] +#[cfg(not(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" +)))] fn test_waitid_exit() { let _m = crate::FORK_MTX.lock(); @@ -140,7 +150,7 @@ fn test_waitid_pid() { } } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] // FIXME: qemu-user doesn't implement ptrace on most arches #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod ptrace { diff --git a/third_party/rust/nix/test/test.rs b/third_party/rust/nix/test/test.rs index 7e73bb3056..c7231426c2 100644 --- a/third_party/rust/nix/test/test.rs +++ b/third_party/rust/nix/test/test.rs @@ -7,12 +7,14 @@ mod common; mod sys; #[cfg(not(target_os = "redox"))] mod test_dir; +mod test_errno; mod test_fcntl; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] mod test_kmod; +#[cfg(target_os = "linux")] +mod test_mount; #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", + freebsdlike, target_os = "fushsia", target_os = "linux", target_os = "netbsd" @@ -30,36 +32,16 @@ mod test_poll; target_os = "haiku" )))] mod test_pty; -mod test_resource; #[cfg(any( - target_os = "android", + linux_android, target_os = "dragonfly", all(target_os = "freebsd", fbsd14), - target_os = "linux" ))] mod test_sched; -#[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos" -))] +#[cfg(any(linux_android, freebsdlike, apple_targets, solarish))] mod test_sendfile; mod test_stat; mod test_time; -#[cfg(all( - any( - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd" - ), - feature = "time", - feature = "signal" -))] -mod test_timer; mod test_unistd; use nix::unistd::{chdir, getcwd, read}; diff --git a/third_party/rust/nix/test/test_dir.rs b/third_party/rust/nix/test/test_dir.rs index 2af4aa5c0a..24ecd6963e 100644 --- a/third_party/rust/nix/test/test_dir.rs +++ b/third_party/rust/nix/test/test_dir.rs @@ -6,10 +6,10 @@ use tempfile::tempdir; #[cfg(test)] fn flags() -> OFlag { - #[cfg(target_os = "illumos")] + #[cfg(solarish)] let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC; - #[cfg(not(target_os = "illumos"))] + #[cfg(not(solarish))] let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC | OFlag::O_DIRECTORY; f diff --git a/third_party/rust/nix/test/test_errno.rs b/third_party/rust/nix/test/test_errno.rs new file mode 100644 index 0000000000..750fc924ff --- /dev/null +++ b/third_party/rust/nix/test/test_errno.rs @@ -0,0 +1,16 @@ +use nix::errno::Errno; + +#[test] +fn errno_set_and_read() { + Errno::ENFILE.set(); + assert_eq!(Errno::last(), Errno::ENFILE); +} + +#[test] +fn errno_set_and_clear() { + Errno::ENFILE.set(); + assert_eq!(Errno::last(), Errno::ENFILE); + + Errno::clear(); + assert_eq!(Errno::last(), Errno::from_raw(0)); +} diff --git a/third_party/rust/nix/test/test_fcntl.rs b/third_party/rust/nix/test/test_fcntl.rs index 5fef04ba9b..6572e8af8d 100644 --- a/third_party/rust/nix/test/test_fcntl.rs +++ b/third_party/rust/nix/test/test_fcntl.rs @@ -42,7 +42,7 @@ fn test_openat() { open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty()) .unwrap(); let fd = openat( - dirfd, + Some(dirfd), tmp.path().file_name().unwrap(), OFlag::O_RDONLY, Mode::empty(), @@ -222,7 +222,7 @@ fn test_readlink() { assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir); assert_eq!( - readlinkat(dirfd, "b").unwrap().to_str().unwrap(), + readlinkat(Some(dirfd), "b").unwrap().to_str().unwrap(), expected_dir ); } @@ -234,10 +234,9 @@ fn test_readlink() { /// The from_offset should be updated by the call to reflect /// the 3 bytes read (6). #[cfg(any( - target_os = "linux", + linux_android, // Not available until FreeBSD 13.0 all(target_os = "freebsd", fbsd14), - target_os = "android" ))] #[test] // QEMU does not support copy_file_range. Skip under qemu @@ -272,7 +271,7 @@ fn test_copy_file_range() { assert_eq!(from_offset, 6); } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] mod linux_android { use libc::loff_t; use std::io::prelude::*; @@ -280,7 +279,7 @@ mod linux_android { use std::os::unix::prelude::*; use nix::fcntl::*; - use nix::unistd::{close, pipe, read, write}; + use nix::unistd::{pipe, read, write}; use tempfile::tempfile; #[cfg(target_os = "linux")] @@ -299,7 +298,7 @@ mod linux_android { let res = splice( tmp.as_raw_fd(), Some(&mut offset), - wr, + wr.as_raw_fd(), None, 2, SpliceFFlags::empty(), @@ -309,12 +308,9 @@ mod linux_android { assert_eq!(2, res); let mut buf = [0u8; 1024]; - assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap()); assert_eq!(b"f1", &buf[0..2]); assert_eq!(7, offset); - - close(rd).unwrap(); - close(wr).unwrap(); } #[test] @@ -323,24 +319,21 @@ mod linux_android { let (rd2, wr2) = pipe().unwrap(); write(wr1, b"abc").unwrap(); - let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap(); + let res = + tee(rd1.as_raw_fd(), wr2.as_raw_fd(), 2, SpliceFFlags::empty()) + .unwrap(); assert_eq!(2, res); let mut buf = [0u8; 1024]; // Check the tee'd bytes are at rd2. - assert_eq!(2, read(rd2, &mut buf).unwrap()); + assert_eq!(2, read(rd2.as_raw_fd(), &mut buf).unwrap()); assert_eq!(b"ab", &buf[0..2]); // Check all the bytes are still at rd1. - assert_eq!(3, read(rd1, &mut buf).unwrap()); + assert_eq!(3, read(rd1.as_raw_fd(), &mut buf).unwrap()); assert_eq!(b"abc", &buf[0..3]); - - close(rd1).unwrap(); - close(wr1).unwrap(); - close(rd2).unwrap(); - close(wr2).unwrap(); } #[test] @@ -351,17 +344,15 @@ mod linux_android { let buf2 = b"defghi"; let iovecs = [IoSlice::new(&buf1[0..3]), IoSlice::new(&buf2[0..3])]; - let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap(); + let res = vmsplice(wr.as_raw_fd(), &iovecs[..], SpliceFFlags::empty()) + .unwrap(); assert_eq!(6, res); // Check the bytes can be read at rd. let mut buf = [0u8; 32]; - assert_eq!(6, read(rd, &mut buf).unwrap()); + assert_eq!(6, read(rd.as_raw_fd(), &mut buf).unwrap()); assert_eq!(b"abcdef", &buf[0..6]); - - close(rd).unwrap(); - close(wr).unwrap(); } #[cfg(target_os = "linux")] @@ -481,8 +472,7 @@ mod linux_android { } #[cfg(any( - target_os = "linux", - target_os = "android", + linux_android, target_os = "emscripten", target_os = "fuchsia", target_os = "wasi", @@ -494,7 +484,7 @@ mod test_posix_fadvise { use nix::errno::Errno; use nix::fcntl::*; use nix::unistd::pipe; - use std::os::unix::io::{AsRawFd, RawFd}; + use std::os::unix::io::AsRawFd; use tempfile::NamedTempFile; #[test] @@ -509,7 +499,7 @@ mod test_posix_fadvise { fn test_errno() { let (rd, _wr) = pipe().unwrap(); let res = posix_fadvise( - rd as RawFd, + rd.as_raw_fd(), 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED, @@ -519,23 +509,18 @@ mod test_posix_fadvise { } #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "dragonfly", + linux_android, + freebsdlike, target_os = "emscripten", target_os = "fuchsia", target_os = "wasi", - target_os = "freebsd" ))] mod test_posix_fallocate { use nix::errno::Errno; use nix::fcntl::*; use nix::unistd::pipe; - use std::{ - io::Read, - os::unix::io::{AsRawFd, RawFd}, - }; + use std::{io::Read, os::unix::io::AsRawFd}; use tempfile::NamedTempFile; #[test] @@ -565,10 +550,133 @@ mod test_posix_fallocate { #[test] fn errno() { let (rd, _wr) = pipe().unwrap(); - let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err(); + let err = posix_fallocate(rd.as_raw_fd(), 0, 100).unwrap_err(); match err { Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (), errno => panic!("unexpected errno {errno}",), } } } + +#[cfg(any(target_os = "dragonfly", target_os = "netbsd", apple_targets))] +#[test] +fn test_f_get_path() { + use nix::fcntl::*; + use std::{os::unix::io::AsRawFd, path::PathBuf}; + + let tmp = NamedTempFile::new().unwrap(); + let fd = tmp.as_raw_fd(); + let mut path = PathBuf::new(); + let res = + fcntl(fd, FcntlArg::F_GETPATH(&mut path)).expect("get path failed"); + assert_ne!(res, -1); + assert_eq!( + path.as_path().canonicalize().unwrap(), + tmp.path().canonicalize().unwrap() + ); +} + +#[cfg(apple_targets)] +#[test] +fn test_f_get_path_nofirmlink() { + use nix::fcntl::*; + use std::{os::unix::io::AsRawFd, path::PathBuf}; + + let tmp = NamedTempFile::new().unwrap(); + let fd = tmp.as_raw_fd(); + let mut path = PathBuf::new(); + let res = fcntl(fd, FcntlArg::F_GETPATH_NOFIRMLINK(&mut path)) + .expect("get path failed"); + let mut tmpstr = String::from("/System/Volumes/Data"); + tmpstr.push_str( + &tmp.path() + .canonicalize() + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + ); + assert_ne!(res, -1); + assert_eq!( + path.as_path() + .canonicalize() + .unwrap() + .into_os_string() + .into_string() + .unwrap(), + tmpstr + ); +} + +#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] +#[test] +fn test_f_kinfo() { + use nix::fcntl::*; + use std::{os::unix::io::AsRawFd, path::PathBuf}; + + let tmp = NamedTempFile::new().unwrap(); + // With TMPDIR set with UFS, the vnode name is not entered + // into the name cache thus path is always empty. + // Therefore, we reopen the tempfile a second time for the test + // to pass. + let tmp2 = File::open(tmp.path()).unwrap(); + let fd = tmp2.as_raw_fd(); + let mut path = PathBuf::new(); + let res = fcntl(fd, FcntlArg::F_KINFO(&mut path)).expect("get path failed"); + assert_ne!(res, -1); + assert_eq!(path, tmp.path()); +} + +/// Test `Flock` and associated functions. +/// +#[cfg(not(any(target_os = "redox", target_os = "solaris")))] +mod test_flock { + use nix::fcntl::*; + use tempfile::NamedTempFile; + + /// Verify that `Flock::lock()` correctly obtains a lock, and subsequently unlocks upon drop. + #[test] + fn verify_lock_and_drop() { + // Get 2 `File` handles to same underlying file. + 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 = match Flock::lock(file2, FlockArg::LockExclusiveNonblock) { + Ok(_) => panic!("Expected second exclusive lock to fail."), + Err((f, _)) => f, + }; + + // Drop first lock + std::mem::drop(lock1); + + // Attempt to lock second handle again (but successfully) + if Flock::lock(file2, FlockArg::LockExclusiveNonblock).is_err() { + panic!("Expected locking to be successful."); + } + } + + /// Verify that `Flock::unlock()` correctly obtains unlocks. + #[test] + fn verify_unlock() { + // Get 2 `File` handles to same underlying file. + 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(); + + // Unlock and retain file so any erroneous flocks also remain present. + let _file1 = lock1.unlock().unwrap(); + + // Attempt to lock second handle. + if Flock::lock(file2, FlockArg::LockExclusiveNonblock).is_err() { + panic!("Expected locking to be successful."); + } + } +} diff --git a/third_party/rust/nix/test/test_mount.rs b/third_party/rust/nix/test/test_mount.rs index 5cf00408e8..a4f0903dba 100644 --- a/third_party/rust/nix/test/test_mount.rs +++ b/third_party/rust/nix/test/test_mount.rs @@ -1,267 +1,183 @@ -mod common; - -// Implementation note: to allow unprivileged users to run it, this test makes -// use of user and mount namespaces. On systems that allow unprivileged user -// namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run -// without root. - -#[cfg(target_os = "linux")] -mod test_mount { - use std::fs::{self, File}; - use std::io::{self, Read, Write}; - use std::os::unix::fs::OpenOptionsExt; - use std::os::unix::fs::PermissionsExt; - use std::process::{self, Command}; - - use libc::{EACCES, EROFS}; - - use nix::errno::Errno; - use nix::mount::{mount, umount, MsFlags}; - use nix::sched::{unshare, CloneFlags}; - use nix::sys::stat::{self, Mode}; - use nix::unistd::getuid; - - static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh +use std::fs::{self, File}; +use std::io::{Read, Write}; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::fs::PermissionsExt; +use std::process::Command; + +use libc::{EACCES, EROFS}; + +use nix::mount::{mount, umount, MsFlags}; +use nix::sys::stat::{self, Mode}; + +use crate::*; + +static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh exit 23"; - const EXPECTED_STATUS: i32 = 23; +const EXPECTED_STATUS: i32 = 23; - const NONE: Option<&'static [u8]> = None; - #[allow(clippy::bind_instead_of_map)] // False positive - pub fn test_mount_tmpfs_without_flags_allows_rwx() { - let tempdir = tempfile::tempdir().unwrap(); +const NONE: Option<&'static [u8]> = None; - mount( - NONE, - tempdir.path(), - Some(b"tmpfs".as_ref()), - MsFlags::empty(), - NONE, - ) - .unwrap_or_else(|e| panic!("mount failed: {e}")); +#[test] +fn test_mount_tmpfs_without_flags_allows_rwx() { + require_capability!( + "test_mount_tmpfs_without_flags_allows_rwx", + CAP_SYS_ADMIN + ); + let tempdir = tempfile::tempdir().unwrap(); + + mount( + NONE, + tempdir.path(), + Some(b"tmpfs".as_ref()), + MsFlags::empty(), + NONE, + ) + .unwrap_or_else(|e| panic!("mount failed: {e}")); + + let test_path = tempdir.path().join("test"); + + // Verify write. + fs::OpenOptions::new() + .create(true) + .write(true) + .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) + .open(&test_path) + .and_then(|mut f| f.write(SCRIPT_CONTENTS)) + .unwrap_or_else(|e| panic!("write failed: {e}")); + + // Verify read. + let mut buf = Vec::new(); + File::open(&test_path) + .and_then(|mut f| f.read_to_end(&mut buf)) + .unwrap_or_else(|e| panic!("read failed: {e}")); + assert_eq!(buf, SCRIPT_CONTENTS); + + // Verify execute. + assert_eq!( + EXPECTED_STATUS, + Command::new(&test_path) + .status() + .unwrap_or_else(|e| panic!("exec failed: {e}")) + .code() + .unwrap_or_else(|| panic!("child killed by signal")) + ); - let test_path = tempdir.path().join("test"); + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); +} - // Verify write. - fs::OpenOptions::new() - .create(true) - .write(true) - .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) - .open(&test_path) - .or_else(|e| { - if Errno::from_i32(e.raw_os_error().unwrap()) - == Errno::EOVERFLOW - { - // Skip tests on certain Linux kernels which have a bug - // regarding tmpfs in namespaces. - // Ubuntu 14.04 and 16.04 are known to be affected; 16.10 is - // not. There is no legitimate reason for open(2) to return - // EOVERFLOW here. - // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087 - let stderr = io::stderr(); - let mut handle = stderr.lock(); - writeln!( - handle, - "Buggy Linux kernel detected. Skipping test." - ) - .unwrap(); - process::exit(0); - } else { - panic!("open failed: {e}"); - } - }) - .and_then(|mut f| f.write(SCRIPT_CONTENTS)) - .unwrap_or_else(|e| panic!("write failed: {e}")); +#[test] +fn test_mount_rdonly_disallows_write() { + require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN); + let tempdir = tempfile::tempdir().unwrap(); + + mount( + NONE, + tempdir.path(), + Some(b"tmpfs".as_ref()), + MsFlags::MS_RDONLY, + NONE, + ) + .unwrap_or_else(|e| panic!("mount failed: {e}")); + + // EROFS: Read-only file system + assert_eq!( + EROFS, + File::create(tempdir.path().join("test")) + .unwrap_err() + .raw_os_error() + .unwrap() + ); - // Verify read. - let mut buf = Vec::new(); - File::open(&test_path) - .and_then(|mut f| f.read_to_end(&mut buf)) - .unwrap_or_else(|e| panic!("read failed: {e}")); - assert_eq!(buf, SCRIPT_CONTENTS); - - // Verify execute. - assert_eq!( - EXPECTED_STATUS, - Command::new(&test_path) - .status() - .unwrap_or_else(|e| panic!("exec failed: {e}")) - .code() - .unwrap_or_else(|| panic!("child killed by signal")) - ); - - umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); - } + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); +} - pub fn test_mount_rdonly_disallows_write() { - let tempdir = tempfile::tempdir().unwrap(); +#[test] +fn test_mount_noexec_disallows_exec() { + require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN); + let tempdir = tempfile::tempdir().unwrap(); + + mount( + NONE, + tempdir.path(), + Some(b"tmpfs".as_ref()), + MsFlags::MS_NOEXEC, + NONE, + ) + .unwrap_or_else(|e| panic!("mount failed: {e}")); + + let test_path = tempdir.path().join("test"); + + fs::OpenOptions::new() + .create(true) + .write(true) + .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) + .open(&test_path) + .and_then(|mut f| f.write(SCRIPT_CONTENTS)) + .unwrap_or_else(|e| panic!("write failed: {e}")); + + // Verify that we cannot execute despite a+x permissions being set. + let mode = stat::Mode::from_bits_truncate( + fs::metadata(&test_path) + .map(|md| md.permissions().mode()) + .unwrap_or_else(|e| panic!("metadata failed: {e}")), + ); - mount( - NONE, - tempdir.path(), - Some(b"tmpfs".as_ref()), - MsFlags::MS_RDONLY, - NONE, - ) - .unwrap_or_else(|e| panic!("mount failed: {e}")); + assert!( + mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH), + "{:?} did not have execute permissions", + &test_path + ); + + // EACCES: Permission denied + assert_eq!( + EACCES, + Command::new(&test_path) + .status() + .unwrap_err() + .raw_os_error() + .unwrap() + ); - // EROFS: Read-only file system - assert_eq!( - EROFS, - File::create(tempdir.path().join("test")) - .unwrap_err() - .raw_os_error() - .unwrap() - ); + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); +} - umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); - } +#[test] +fn test_mount_bind() { + require_capability!("test_mount_bind", CAP_SYS_ADMIN); + let tempdir = tempfile::tempdir().unwrap(); + let file_name = "test"; - pub fn test_mount_noexec_disallows_exec() { - let tempdir = tempfile::tempdir().unwrap(); + { + let mount_point = tempfile::tempdir().unwrap(); mount( + Some(tempdir.path()), + mount_point.path(), NONE, - tempdir.path(), - Some(b"tmpfs".as_ref()), - MsFlags::MS_NOEXEC, + MsFlags::MS_BIND, NONE, ) .unwrap_or_else(|e| panic!("mount failed: {e}")); - let test_path = tempdir.path().join("test"); - fs::OpenOptions::new() .create(true) .write(true) .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) - .open(&test_path) + .open(mount_point.path().join(file_name)) .and_then(|mut f| f.write(SCRIPT_CONTENTS)) .unwrap_or_else(|e| panic!("write failed: {e}")); - // Verify that we cannot execute despite a+x permissions being set. - let mode = stat::Mode::from_bits_truncate( - fs::metadata(&test_path) - .map(|md| md.permissions().mode()) - .unwrap_or_else(|e| panic!("metadata failed: {e}")), - ); - - assert!( - mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH), - "{:?} did not have execute permissions", - &test_path - ); - - // EACCES: Permission denied - assert_eq!( - EACCES, - Command::new(&test_path) - .status() - .unwrap_err() - .raw_os_error() - .unwrap() - ); - - umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}")); + umount(mount_point.path()) + .unwrap_or_else(|e| panic!("umount failed: {e}")); } - pub fn test_mount_bind() { - let tempdir = tempfile::tempdir().unwrap(); - let file_name = "test"; - - { - let mount_point = tempfile::tempdir().unwrap(); - - mount( - Some(tempdir.path()), - mount_point.path(), - NONE, - MsFlags::MS_BIND, - NONE, - ) - .unwrap_or_else(|e| panic!("mount failed: {e}")); - - fs::OpenOptions::new() - .create(true) - .write(true) - .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits()) - .open(mount_point.path().join(file_name)) - .and_then(|mut f| f.write(SCRIPT_CONTENTS)) - .unwrap_or_else(|e| panic!("write failed: {e}")); - - umount(mount_point.path()) - .unwrap_or_else(|e| panic!("umount failed: {e}")); - } - - // Verify the file written in the mount shows up in source directory, even - // after unmounting. - - let mut buf = Vec::new(); - File::open(tempdir.path().join(file_name)) - .and_then(|mut f| f.read_to_end(&mut buf)) - .unwrap_or_else(|e| panic!("read failed: {e}")); - assert_eq!(buf, SCRIPT_CONTENTS); - } + // Verify the file written in the mount shows up in source directory, even + // after unmounting. - pub fn setup_namespaces() { - // Hold on to the uid in the parent namespace. - let uid = getuid(); - - unshare(CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER).unwrap_or_else(|e| { - let stderr = io::stderr(); - let mut handle = stderr.lock(); - writeln!(handle, - "unshare failed: {e}. Are unprivileged user namespaces available?").unwrap(); - writeln!(handle, "mount is not being tested").unwrap(); - // Exit with success because not all systems support unprivileged user namespaces, and - // that's not what we're testing for. - process::exit(0); - }); - - // Map user as uid 1000. - fs::OpenOptions::new() - .write(true) - .open("/proc/self/uid_map") - .and_then(|mut f| f.write(format!("1000 {uid} 1\n").as_bytes())) - .unwrap_or_else(|e| panic!("could not write uid map: {e}")); - } + let mut buf = Vec::new(); + File::open(tempdir.path().join(file_name)) + .and_then(|mut f| f.read_to_end(&mut buf)) + .unwrap_or_else(|e| panic!("read failed: {e}")); + assert_eq!(buf, SCRIPT_CONTENTS); } - -// Test runner - -/// Mimic normal test output (hackishly). -#[cfg(target_os = "linux")] -macro_rules! run_tests { - ( $($test_fn:ident),* ) => {{ - println!(); - - $( - print!("test test_mount::{} ... ", stringify!($test_fn)); - $test_fn(); - println!("ok"); - )* - - println!(); - }} -} - -#[cfg(target_os = "linux")] -fn main() { - use test_mount::{ - setup_namespaces, test_mount_bind, test_mount_noexec_disallows_exec, - test_mount_rdonly_disallows_write, - test_mount_tmpfs_without_flags_allows_rwx, - }; - skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1351"); - setup_namespaces(); - - run_tests!( - test_mount_tmpfs_without_flags_allows_rwx, - test_mount_rdonly_disallows_write, - test_mount_noexec_disallows_exec, - test_mount_bind - ); -} - -#[cfg(not(target_os = "linux"))] -fn main() {} diff --git a/third_party/rust/nix/test/test_mq.rs b/third_party/rust/nix/test/test_mq.rs index 1fd8929c17..874a72b44d 100644 --- a/third_party/rust/nix/test/test_mq.rs +++ b/third_party/rust/nix/test/test_mq.rs @@ -112,7 +112,15 @@ fn test_mq_getattr() { // FIXME: Fix failures for mips in QEMU #[test] #[cfg_attr( - all(qemu, any(target_arch = "mips", target_arch = "mips64")), + all( + qemu, + any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ), ignore )] fn test_mq_setattr() { @@ -162,7 +170,15 @@ fn test_mq_setattr() { // FIXME: Fix failures for mips in QEMU #[test] #[cfg_attr( - all(qemu, any(target_arch = "mips", target_arch = "mips64")), + all( + qemu, + any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ), ignore )] fn test_mq_set_nonblocking() { diff --git a/third_party/rust/nix/test/test_net.rs b/third_party/rust/nix/test/test_net.rs index c44655a4c9..faba8503fe 100644 --- a/third_party/rust/nix/test/test_net.rs +++ b/third_party/rust/nix/test/test_net.rs @@ -1,13 +1,9 @@ use nix::net::if_::*; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] const LOOPBACK: &[u8] = b"lo"; -#[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "haiku" -)))] +#[cfg(not(any(linux_android, target_os = "haiku")))] const LOOPBACK: &[u8] = b"lo0"; #[cfg(target_os = "haiku")] diff --git a/third_party/rust/nix/test/test_poll.rs b/third_party/rust/nix/test/test_poll.rs index 045ccd3df1..fcb325494e 100644 --- a/third_party/rust/nix/test/test_poll.rs +++ b/third_party/rust/nix/test/test_poll.rs @@ -1,9 +1,9 @@ use nix::{ errno::Errno, - poll::{poll, PollFd, PollFlags}, - unistd::{close, pipe, write}, + poll::{poll, PollFd, PollFlags, PollTimeout}, + unistd::{pipe, write}, }; -use std::os::unix::io::{BorrowedFd, FromRawFd, OwnedFd}; +use std::os::unix::io::{AsFd, BorrowedFd}; macro_rules! loop_while_eintr { ($poll_expr: expr) => { @@ -20,32 +20,25 @@ macro_rules! loop_while_eintr { #[test] fn test_poll() { let (r, w) = pipe().unwrap(); - let r = unsafe { OwnedFd::from_raw_fd(r) }; - let mut fds = [PollFd::new(&r, PollFlags::POLLIN)]; + let mut fds = [PollFd::new(r.as_fd(), PollFlags::POLLIN)]; // Poll an idle pipe. Should timeout - let nfds = loop_while_eintr!(poll(&mut fds, 100)); + let nfds = loop_while_eintr!(poll(&mut fds, PollTimeout::from(100u8))); assert_eq!(nfds, 0); assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN)); - write(w, b".").unwrap(); + write(&w, b".").unwrap(); // Poll a readable pipe. Should return an event. - let nfds = poll(&mut fds, 100).unwrap(); + let nfds = poll(&mut fds, PollTimeout::from(100u8)).unwrap(); assert_eq!(nfds, 1); assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); - close(w).unwrap(); } // ppoll(2) is the same as poll except for how it handles timeouts and signals. // Repeating the test for poll(2) should be sufficient to check that our // bindings are correct. -#[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux" -))] +#[cfg(any(linux_android, freebsdlike))] #[test] fn test_ppoll() { use nix::poll::ppoll; @@ -54,8 +47,7 @@ fn test_ppoll() { let timeout = TimeSpec::milliseconds(1); let (r, w) = pipe().unwrap(); - let r = unsafe { OwnedFd::from_raw_fd(r) }; - let mut fds = [PollFd::new(&r, PollFlags::POLLIN)]; + let mut fds = [PollFd::new(r.as_fd(), PollFlags::POLLIN)]; // Poll an idle pipe. Should timeout let sigset = SigSet::empty(); @@ -63,19 +55,18 @@ fn test_ppoll() { assert_eq!(nfds, 0); assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN)); - write(w, b".").unwrap(); + write(&w, b".").unwrap(); // Poll a readable pipe. Should return an event. let nfds = ppoll(&mut fds, Some(timeout), None).unwrap(); assert_eq!(nfds, 1); assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); - close(w).unwrap(); } #[test] fn test_pollfd_events() { let fd_zero = unsafe { BorrowedFd::borrow_raw(0) }; - let mut pfd = PollFd::new(&fd_zero, PollFlags::POLLIN); + let mut pfd = PollFd::new(fd_zero.as_fd(), PollFlags::POLLIN); assert_eq!(pfd.events(), PollFlags::POLLIN); pfd.set_events(PollFlags::POLLOUT); assert_eq!(pfd.events(), PollFlags::POLLOUT); diff --git a/third_party/rust/nix/test/test_pty.rs b/third_party/rust/nix/test/test_pty.rs index 4cc6620c3c..368ec129b0 100644 --- a/third_party/rust/nix/test/test_pty.rs +++ b/third_party/rust/nix/test/test_pty.rs @@ -1,9 +1,9 @@ use std::fs::File; -use std::io::{Read, Write}; +use std::io::{stdout, Read, Write}; use std::os::unix::prelude::*; use std::path::Path; -use libc::{_exit, STDOUT_FILENO}; +use libc::_exit; use nix::fcntl::{open, OFlag}; use nix::pty::*; use nix::sys::stat; @@ -12,7 +12,7 @@ use nix::unistd::{pause, write}; /// Test equivalence of `ptsname` and `ptsname_r` #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptsname_equivalence() { let _m = crate::PTSNAME_MTX.lock(); @@ -29,7 +29,7 @@ fn test_ptsname_equivalence() { /// Test data copying of `ptsname` // TODO need to run in a subprocess, since ptsname is non-reentrant #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptsname_copy() { let _m = crate::PTSNAME_MTX.lock(); @@ -47,7 +47,7 @@ fn test_ptsname_copy() { /// Test data copying of `ptsname_r` #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptsname_r_copy() { // Open a new PTTY master let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); @@ -61,7 +61,7 @@ fn test_ptsname_r_copy() { /// Test that `ptsname` returns different names for different devices #[test] -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] fn test_ptsname_unique() { let _m = crate::PTSNAME_MTX.lock(); @@ -96,7 +96,7 @@ fn open_ptty_pair() -> (PtyMaster, File) { open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()) .unwrap(); - #[cfg(target_os = "illumos")] + #[cfg(solarish)] // TODO: rewrite using ioctl! #[allow(clippy::comparison_chain)] { @@ -185,7 +185,7 @@ fn test_openpty() { // Writing to one should be readable on the other one let string = "foofoofoo\n"; let mut buf = [0u8; 10]; - write(pty.master.as_raw_fd(), string.as_bytes()).unwrap(); + write(&pty.master, string.as_bytes()).unwrap(); crate::read_exact(&pty.slave, &mut buf); assert_eq!(&buf, string.as_bytes()); @@ -199,7 +199,7 @@ fn test_openpty() { let string2 = "barbarbarbar\n"; let echoed_string2 = "barbarbarbar\r\n"; let mut buf = [0u8; 14]; - write(pty.slave.as_raw_fd(), string2.as_bytes()).unwrap(); + write(&pty.slave, string2.as_bytes()).unwrap(); crate::read_exact(&pty.master, &mut buf); assert_eq!(&buf, echoed_string2.as_bytes()); @@ -224,7 +224,7 @@ fn test_openpty_with_termios() { // Writing to one should be readable on the other one let string = "foofoofoo\n"; let mut buf = [0u8; 10]; - write(pty.master.as_raw_fd(), string.as_bytes()).unwrap(); + write(&pty.master, string.as_bytes()).unwrap(); crate::read_exact(&pty.slave, &mut buf); assert_eq!(&buf, string.as_bytes()); @@ -237,7 +237,7 @@ fn test_openpty_with_termios() { let string2 = "barbarbarbar\n"; let echoed_string2 = "barbarbarbar\n"; let mut buf = [0u8; 13]; - write(pty.slave.as_raw_fd(), string2.as_bytes()).unwrap(); + write(&pty.slave, string2.as_bytes()).unwrap(); crate::read_exact(&pty.master, &mut buf); assert_eq!(&buf, echoed_string2.as_bytes()); @@ -258,7 +258,7 @@ fn test_forkpty() { let pty = unsafe { forkpty(None, None).unwrap() }; match pty.fork_result { Child => { - write(STDOUT_FILENO, string.as_bytes()).unwrap(); + write(stdout(), string.as_bytes()).unwrap(); pause(); // we need the child to stay alive until the parent calls read unsafe { _exit(0); diff --git a/third_party/rust/nix/test/test_resource.rs b/third_party/rust/nix/test/test_resource.rs deleted file mode 100644 index 2ab581ba29..0000000000 --- a/third_party/rust/nix/test/test_resource.rs +++ /dev/null @@ -1,34 +0,0 @@ -#[cfg(not(any( - target_os = "redox", - target_os = "fuchsia", - target_os = "illumos", - target_os = "haiku" -)))] -use nix::sys::resource::{getrlimit, setrlimit, Resource}; - -/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers -/// to the maximum file descriptor number that can be opened by the process (aka the maximum number -/// of file descriptors that the process can open, since Linux 4.5). -/// -/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the -/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit() -/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have -/// been updated. -#[test] -#[cfg(not(any( - target_os = "redox", - target_os = "fuchsia", - target_os = "illumos", - target_os = "haiku" -)))] -pub fn test_resource_limits_nofile() { - let (mut soft_limit, hard_limit) = - getrlimit(Resource::RLIMIT_NOFILE).unwrap(); - - soft_limit -= 1; - assert_ne!(soft_limit, hard_limit); - setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); - - let (new_soft_limit, _) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); - assert_eq!(new_soft_limit, soft_limit); -} diff --git a/third_party/rust/nix/test/test_sendfile.rs b/third_party/rust/nix/test/test_sendfile.rs index b85e030fd3..6333bf8662 100644 --- a/third_party/rust/nix/test/test_sendfile.rs +++ b/third_party/rust/nix/test/test_sendfile.rs @@ -1,21 +1,20 @@ use std::io::prelude::*; -#[cfg(any(target_os = "android", target_os = "linux"))] -use std::os::unix::io::{FromRawFd, OwnedFd}; use libc::off_t; use nix::sys::sendfile::*; use tempfile::tempfile; cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { - use nix::unistd::{close, pipe, read}; - } else if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos"))] { + if #[cfg(linux_android)] { + use nix::unistd::{pipe, read}; + use std::os::unix::io::AsRawFd; + } else if #[cfg(any(freebsdlike, apple_targets, solarish))] { use std::net::Shutdown; use std::os::unix::net::UnixStream; } } -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(linux_android)] #[test] fn test_sendfile_linux() { const CONTENTS: &[u8] = b"abcdef123456"; @@ -24,21 +23,14 @@ fn test_sendfile_linux() { let (rd, wr) = pipe().unwrap(); let mut offset: off_t = 5; - // The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)` - // becomes I/O-safe: - // pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error> - // then it is no longer needed. - let wr = unsafe { OwnedFd::from_raw_fd(wr) }; let res = sendfile(&wr, &tmp, Some(&mut offset), 2).unwrap(); assert_eq!(2, res); let mut buf = [0u8; 1024]; - assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap()); assert_eq!(b"f1", &buf[0..2]); assert_eq!(7, offset); - - close(rd).unwrap(); } #[cfg(target_os = "linux")] @@ -50,21 +42,14 @@ fn test_sendfile64_linux() { let (rd, wr) = pipe().unwrap(); let mut offset: libc::off64_t = 5; - // The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)` - // becomes I/O-safe: - // pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error> - // then it is no longer needed. - let wr = unsafe { OwnedFd::from_raw_fd(wr) }; let res = sendfile64(&wr, &tmp, Some(&mut offset), 2).unwrap(); assert_eq!(2, res); let mut buf = [0u8; 1024]; - assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap()); assert_eq!(b"f1", &buf[0..2]); assert_eq!(7, offset); - - close(rd).unwrap(); } #[cfg(target_os = "freebsd")] @@ -167,7 +152,7 @@ fn test_sendfile_dragonfly() { assert_eq!(expected_string, read_string); } -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(apple_targets)] #[test] fn test_sendfile_darwin() { // Declare the content @@ -215,3 +200,62 @@ fn test_sendfile_darwin() { assert_eq!(bytes_written as usize, bytes_read); assert_eq!(expected_string, read_string); } + +#[cfg(solarish)] +#[test] +fn test_sendfilev() { + use std::os::fd::AsFd; + // Declare the content + let header_strings = + ["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + let body = "Xabcdef123456"; + let body_offset = 1usize; + let trailer_strings = ["\n", "Served by Make Believe\n"]; + + // Write data to files + let mut header_data = tempfile().unwrap(); + header_data + .write_all(header_strings.concat().as_bytes()) + .unwrap(); + let mut body_data = tempfile().unwrap(); + body_data.write_all(body.as_bytes()).unwrap(); + let mut trailer_data = tempfile().unwrap(); + trailer_data + .write_all(trailer_strings.concat().as_bytes()) + .unwrap(); + let (mut rd, wr) = UnixStream::pair().unwrap(); + let vec: &[SendfileVec] = &[ + SendfileVec::new( + header_data.as_fd(), + 0, + header_strings.iter().map(|s| s.len()).sum(), + ), + SendfileVec::new( + body_data.as_fd(), + body_offset as off_t, + body.len() - body_offset, + ), + SendfileVec::new( + trailer_data.as_fd(), + 0, + trailer_strings.iter().map(|s| s.len()).sum(), + ), + ]; + + let (res, bytes_written) = sendfilev(&wr, vec); + assert!(res.is_ok()); + wr.shutdown(Shutdown::Both).unwrap(); + + // Prepare the expected result + let expected_string = header_strings.concat() + + &body[body_offset..] + + &trailer_strings.concat(); + + // Verify the message that was sent + assert_eq!(bytes_written, expected_string.as_bytes().len()); + + let mut read_string = String::new(); + let bytes_read = rd.read_to_string(&mut read_string).unwrap(); + assert_eq!(bytes_written, bytes_read); + assert_eq!(expected_string, read_string); +} diff --git a/third_party/rust/nix/test/test_stat.rs b/third_party/rust/nix/test/test_stat.rs index 55f15c0771..386f1084cc 100644 --- a/third_party/rust/nix/test/test_stat.rs +++ b/third_party/rust/nix/test/test_stat.rs @@ -21,8 +21,7 @@ use nix::errno::Errno; use nix::fcntl; #[cfg(any( target_os = "linux", - target_os = "ios", - target_os = "macos", + apple_targets, target_os = "freebsd", target_os = "netbsd" ))] @@ -117,7 +116,7 @@ fn test_fstatat() { fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()); let result = - stat::fstatat(dirfd.unwrap(), &filename, fcntl::AtFlags::empty()); + stat::fstatat(Some(dirfd.unwrap()), &filename, fcntl::AtFlags::empty()); assert_stat_results(result); } @@ -235,8 +234,7 @@ fn test_utimes() { #[test] #[cfg(any( target_os = "linux", - target_os = "ios", - target_os = "macos", + apple_targets, target_os = "freebsd", target_os = "netbsd" ))] @@ -323,7 +321,7 @@ fn test_mkdirat_success_path() { let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()) .unwrap(); - mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed"); + mkdirat(Some(dirfd), filename, Mode::S_IRWXU).expect("mkdirat failed"); assert!(Path::exists(&tempdir.path().join(filename))); } @@ -337,7 +335,7 @@ fn test_mkdirat_success_mode() { let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()) .unwrap(); - mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed"); + mkdirat(Some(dirfd), filename, Mode::S_IRWXU).expect("mkdirat failed"); let permissions = fs::metadata(tempdir.path().join(filename)) .unwrap() .permissions(); @@ -357,16 +355,14 @@ fn test_mkdirat_fail() { stat::Mode::empty(), ) .unwrap(); - let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err(); + let result = mkdirat(Some(dirfd), filename, Mode::S_IRWXU).unwrap_err(); assert_eq!(result, Errno::ENOTDIR); } #[test] #[cfg(not(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "macos", + freebsdlike, + apple_targets, target_os = "haiku", target_os = "redox" )))] @@ -384,11 +380,9 @@ fn test_mknod() { #[test] #[cfg(not(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "ios", - target_os = "macos", + solarish, + freebsdlike, + apple_targets, target_os = "haiku", target_os = "redox" )))] @@ -402,7 +396,7 @@ fn test_mknodat() { let target_dir = Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap(); mknodat( - target_dir.as_raw_fd(), + Some(target_dir.as_raw_fd()), file_name, SFlag::S_IFREG, Mode::S_IRWXU, @@ -410,7 +404,7 @@ fn test_mknodat() { ) .unwrap(); let mode = fstatat( - target_dir.as_raw_fd(), + Some(target_dir.as_raw_fd()), file_name, AtFlags::AT_SYMLINK_NOFOLLOW, ) @@ -419,3 +413,75 @@ fn test_mknodat() { assert_eq!(mode & libc::S_IFREG, libc::S_IFREG); assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU); } + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "haiku")))] +fn test_futimens_unchanged() { + let tempdir = tempfile::tempdir().unwrap(); + let fullpath = tempdir.path().join("file"); + drop(File::create(&fullpath).unwrap()); + let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty()) + .unwrap(); + + let old_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let old_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + + futimens(fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap(); + + let new_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let new_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + assert_eq!(old_atime, new_atime); + assert_eq!(old_mtime, new_mtime); +} + +#[test] +#[cfg(not(any(target_os = "redox", target_os = "haiku")))] +fn test_utimensat_unchanged() { + let _dr = crate::DirRestore::new(); + let tempdir = tempfile::tempdir().unwrap(); + let filename = "foo.txt"; + let fullpath = tempdir.path().join(filename); + drop(File::create(&fullpath).unwrap()); + let dirfd = + fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()) + .unwrap(); + + let old_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let old_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + utimensat( + Some(dirfd), + filename, + &TimeSpec::UTIME_OMIT, + &TimeSpec::UTIME_OMIT, + UtimensatFlags::NoFollowSymlink, + ) + .unwrap(); + let new_atime = fs::metadata(fullpath.as_path()) + .unwrap() + .accessed() + .unwrap(); + let new_mtime = fs::metadata(fullpath.as_path()) + .unwrap() + .modified() + .unwrap(); + assert_eq!(old_atime, new_atime); + assert_eq!(old_mtime, new_mtime); +} diff --git a/third_party/rust/nix/test/test_time.rs b/third_party/rust/nix/test/test_time.rs index 5f76e61a2d..64c8161dbf 100644 --- a/third_party/rust/nix/test/test_time.rs +++ b/third_party/rust/nix/test/test_time.rs @@ -1,10 +1,4 @@ -#[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "linux", - target_os = "android", - target_os = "emscripten", -))] +#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))] use nix::time::clock_getcpuclockid; use nix::time::{clock_gettime, ClockId}; @@ -19,13 +13,7 @@ pub fn test_clock_gettime() { clock_gettime(ClockId::CLOCK_REALTIME).expect("assertion failed"); } -#[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "linux", - target_os = "android", - target_os = "emscripten", -))] +#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))] #[test] pub fn test_clock_getcpuclockid() { let clock_id = clock_getcpuclockid(nix::unistd::Pid::this()).unwrap(); @@ -43,13 +31,7 @@ pub fn test_clock_id_now() { ClockId::CLOCK_REALTIME.now().unwrap(); } -#[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "linux", - target_os = "android", - target_os = "emscripten", -))] +#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))] #[test] pub fn test_clock_id_pid_cpu_clock_id() { ClockId::pid_cpu_clock_id(nix::unistd::Pid::this()) @@ -57,3 +39,28 @@ pub fn test_clock_id_pid_cpu_clock_id() { .unwrap() .unwrap(); } + +#[cfg(any( + linux_android, + solarish, + freebsdlike, + target_os = "netbsd", + target_os = "hurd", + target_os = "aix" +))] +#[test] +pub fn test_clock_nanosleep() { + use nix::{ + sys::time::{TimeSpec, TimeValLike}, + time::{clock_nanosleep, ClockNanosleepFlags}, + }; + + let sleep_time = TimeSpec::microseconds(1); + let res = clock_nanosleep( + ClockId::CLOCK_MONOTONIC, + ClockNanosleepFlags::empty(), + &sleep_time, + ); + let expected = TimeSpec::microseconds(0); + assert_eq!(res, Ok(expected)); +} diff --git a/third_party/rust/nix/test/test_timer.rs b/third_party/rust/nix/test/test_timer.rs deleted file mode 100644 index ffd146867b..0000000000 --- a/third_party/rust/nix/test/test_timer.rs +++ /dev/null @@ -1,102 +0,0 @@ -use nix::sys::signal::{ - sigaction, SaFlags, SigAction, SigEvent, SigHandler, SigSet, SigevNotify, - Signal, -}; -use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags}; -use nix::time::ClockId; -use std::convert::TryFrom; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::thread; -use std::time::{Duration, Instant}; - -const SIG: Signal = Signal::SIGALRM; -static ALARM_CALLED: AtomicBool = AtomicBool::new(false); - -pub extern "C" fn handle_sigalarm(raw_signal: libc::c_int) { - let signal = Signal::try_from(raw_signal).unwrap(); - if signal == SIG { - ALARM_CALLED.store(true, Ordering::Release); - } -} - -#[test] -fn alarm_fires() { - // Avoid interfering with other signal using tests by taking a mutex shared - // among other tests in this crate. - let _m = crate::SIGNAL_MTX.lock(); - const TIMER_PERIOD: Duration = Duration::from_millis(100); - - // - // Setup - // - - // Create a handler for the test signal, `SIG`. The handler is responsible - // for flipping `ALARM_CALLED`. - let handler = SigHandler::Handler(handle_sigalarm); - let signal_action = - SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty()); - let old_handler = unsafe { - sigaction(SIG, &signal_action) - .expect("unable to set signal handler for alarm") - }; - - // Create the timer. We use the monotonic clock here, though any would do - // really. The timer is set to fire every 250 milliseconds with no delay for - // the initial firing. - let clockid = ClockId::CLOCK_MONOTONIC; - let sigevent = SigEvent::new(SigevNotify::SigevSignal { - signal: SIG, - si_value: 0, - }); - let mut timer = - Timer::new(clockid, sigevent).expect("failed to create timer"); - let expiration = Expiration::Interval(TIMER_PERIOD.into()); - let flags = TimerSetTimeFlags::empty(); - timer.set(expiration, flags).expect("could not set timer"); - - // - // Test - // - - // Determine that there's still an expiration tracked by the - // timer. Depending on when this runs either an `Expiration::Interval` or - // `Expiration::IntervalDelayed` will be present. That is, if the timer has - // not fired yet we'll get our original `expiration`, else the one that - // represents a delay to the next expiration. We're only interested in the - // timer still being extant. - match timer.get() { - Ok(Some(exp)) => assert!(matches!( - exp, - Expiration::Interval(..) | Expiration::IntervalDelayed(..) - )), - _ => panic!("timer lost its expiration"), - } - - // Wait for 2 firings of the alarm before checking that it has fired and - // been handled at least the once. If we wait for 3 seconds and the handler - // is never called something has gone sideways and the test fails. - let starttime = Instant::now(); - loop { - thread::sleep(2 * TIMER_PERIOD); - if ALARM_CALLED.load(Ordering::Acquire) { - break; - } - if starttime.elapsed() > Duration::from_secs(3) { - panic!("Timeout waiting for SIGALRM"); - } - } - - // Cleanup: - // 1) deregister the OS's timer. - // 2) Wait for a full timer period, since POSIX does not require that - // disabling the timer will clear pending signals, and on NetBSD at least - // it does not. - // 2) Replace the old signal handler now that we've completed the test. If - // the test fails this process panics, so the fact we might not get here - // is okay. - drop(timer); - thread::sleep(TIMER_PERIOD); - unsafe { - sigaction(SIG, &old_handler).expect("unable to reset signal handler"); - } -} diff --git a/third_party/rust/nix/test/test_unistd.rs b/third_party/rust/nix/test/test_unistd.rs index 10284e4127..aa2e5e56d7 100644 --- a/third_party/rust/nix/test/test_unistd.rs +++ b/third_party/rust/nix/test/test_unistd.rs @@ -66,6 +66,37 @@ fn test_fork_and_waitpid() { } } +#[test] +#[cfg(target_os = "freebsd")] +fn test_rfork_and_waitpid() { + let _m = crate::FORK_MTX.lock(); + + // Safe: Child only calls `_exit`, which is signal-safe + match unsafe { rfork(RforkFlags::RFPROC | RforkFlags::RFTHREAD) } + .expect("Error: Rfork Failed") + { + Child => unsafe { _exit(0) }, + Parent { child } => { + // assert that child was created and pid > 0 + let child_raw: ::libc::pid_t = child.into(); + assert!(child_raw > 0); + let wait_status = waitpid(child, None); + match wait_status { + // assert that waitpid returned correct status and the pid is the one of the child + Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child), + + // panic, must never happen + s @ Ok(_) => { + panic!("Child exited {s:?}, should never happen") + } + + // panic, waitpid should never fail + Err(s) => panic!("Error: waitpid returned Err({s:?}"), + } + } + } +} + #[test] fn test_wait() { // Grab FORK_MTX so wait doesn't reap a different test's child process @@ -126,8 +157,7 @@ fn test_mkfifo_directory() { #[test] #[cfg(not(any( - target_os = "macos", - target_os = "ios", + apple_targets, target_os = "android", target_os = "redox", target_os = "haiku" @@ -147,8 +177,7 @@ fn test_mkfifoat_none() { #[test] #[cfg(not(any( - target_os = "macos", - target_os = "ios", + apple_targets, target_os = "android", target_os = "redox", target_os = "haiku" @@ -163,15 +192,15 @@ fn test_mkfifoat() { mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap(); let stats = - stat::fstatat(dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap(); + stat::fstatat(Some(dirfd), mkfifoat_name, fcntl::AtFlags::empty()) + .unwrap(); let typ = stat::SFlag::from_bits_truncate(stats.st_mode); assert_eq!(typ, SFlag::S_IFIFO); } #[test] #[cfg(not(any( - target_os = "macos", - target_os = "ios", + apple_targets, target_os = "android", target_os = "redox", target_os = "haiku" @@ -186,8 +215,7 @@ fn test_mkfifoat_directory_none() { #[test] #[cfg(not(any( - target_os = "macos", - target_os = "ios", + apple_targets, target_os = "android", target_os = "redox", target_os = "haiku" @@ -197,7 +225,7 @@ fn test_mkfifoat_directory() { let tempdir = tempdir().unwrap(); let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); let mkfifoat_dir = "mkfifoat_dir"; - stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap(); + stat::mkdirat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).unwrap(); mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR) .expect_err("assertion failed"); @@ -220,7 +248,7 @@ fn test_getsid() { assert_eq!(none_sid, pid_sid); } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] mod linux_android { use nix::unistd::gettid; @@ -234,8 +262,7 @@ mod linux_android { #[test] // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms #[cfg(not(any( - target_os = "ios", - target_os = "macos", + apple_targets, target_os = "redox", target_os = "fuchsia", target_os = "haiku" @@ -263,12 +290,11 @@ fn test_setgroups() { #[test] // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms #[cfg(not(any( - target_os = "ios", - target_os = "macos", + apple_targets, target_os = "redox", target_os = "fuchsia", target_os = "haiku", - target_os = "illumos" + solarish )))] fn test_initgroups() { // Skip this test when not run as root as `initgroups()` and `setgroups()` @@ -356,7 +382,7 @@ macro_rules! execve_test_factory ( match unsafe{fork()}.unwrap() { Child => { // Make `writer` be the stdout of the new process. - dup2(writer, 1).unwrap(); + dup2(writer.as_raw_fd(), 1).unwrap(); let r = syscall(); let _ = std::io::stderr() .write_all(format!("{:?}", r).as_bytes()); @@ -370,7 +396,7 @@ macro_rules! execve_test_factory ( assert_eq!(ws, Ok(WaitStatus::Exited(child, 0))); // Read 1024 bytes. let mut buf = [0u8; 1024]; - read(reader, &mut buf).unwrap(); + read(reader.as_raw_fd(), &mut buf).unwrap(); // It should contain the things we printed using `/bin/sh`. let string = String::from_utf8_lossy(&buf); assert!(string.contains("nix!!!")); @@ -404,46 +430,44 @@ cfg_if! { if #[cfg(target_os = "android")] { execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str()); execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd()); - } else if #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux"))] { + } else if #[cfg(any(freebsdlike, target_os = "linux", target_os = "hurd"))] { // These tests frequently fail on musl, probably due to // https://github.com/nix-rust/nix/issues/555 execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str()); execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd()); - } else if #[cfg(any(target_os = "illumos", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris"))] { + } else if #[cfg(any(solarish, apple_targets, netbsdlike))] { execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str()); // No fexecve() on ios, macos, NetBSD, OpenBSD. } } -#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))] +#[cfg(any( + target_os = "haiku", + target_os = "hurd", + target_os = "linux", + target_os = "openbsd" +))] execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap()); cfg_if! { if #[cfg(target_os = "android")] { use nix::fcntl::AtFlags; execve_test_factory!(test_execveat_empty, execveat, - File::open("/system/bin/sh").unwrap().into_raw_fd(), + Some(File::open("/system/bin/sh").unwrap().into_raw_fd()), "", AtFlags::AT_EMPTY_PATH); execve_test_factory!(test_execveat_relative, execveat, - File::open("/system/bin/").unwrap().into_raw_fd(), + Some(File::open("/system/bin/").unwrap().into_raw_fd()), "./sh", AtFlags::empty()); execve_test_factory!(test_execveat_absolute, execveat, - File::open("/").unwrap().into_raw_fd(), + Some(File::open("/").unwrap().into_raw_fd()), "/system/bin/sh", AtFlags::empty()); } else if #[cfg(all(target_os = "linux", any(target_arch ="x86_64", target_arch ="x86")))] { use nix::fcntl::AtFlags; - execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(), + execve_test_factory!(test_execveat_empty, execveat, Some(File::open("/bin/sh").unwrap().into_raw_fd()), "", AtFlags::AT_EMPTY_PATH); - execve_test_factory!(test_execveat_relative, execveat, File::open("/bin/").unwrap().into_raw_fd(), + execve_test_factory!(test_execveat_relative, execveat, Some(File::open("/bin/").unwrap().into_raw_fd()), "./sh", AtFlags::empty()); - execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(), + execve_test_factory!(test_execveat_absolute, execveat, Some(File::open("/").unwrap().into_raw_fd()), "/bin/sh", AtFlags::empty()); } } @@ -527,6 +551,8 @@ fn test_fchown() { #[test] #[cfg(not(target_os = "redox"))] fn test_fchownat() { + use nix::fcntl::AtFlags; + let _dr = crate::DirRestore::new(); // Testing for anything other than our own UID/GID is hard. let uid = Some(getuid()); @@ -540,14 +566,13 @@ fn test_fchownat() { let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); - fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink) - .unwrap(); + fchownat(Some(dirfd), "file", uid, gid, AtFlags::empty()).unwrap(); chdir(tempdir.path()).unwrap(); - fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap(); + fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap(); fs::remove_file(&path).unwrap(); - fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap_err(); + fchownat(None, "file", uid, gid, AtFlags::empty()).unwrap_err(); } #[test] @@ -564,7 +589,7 @@ fn test_lseek() { assert_eq!(b"f123456", &buf); } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] #[test] fn test_lseek64() { const CONTENTS: &[u8] = b"abcdef123456"; @@ -579,7 +604,7 @@ fn test_lseek64() { } cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + if #[cfg(linux_android)] { macro_rules! require_acct{ () => { require_capability!("test_acct", CAP_SYS_PACCT); @@ -631,11 +656,12 @@ fn test_acct() { acct::disable().unwrap(); } +#[cfg_attr(target_os = "hurd", ignore)] #[test] fn test_fpathconf_limited() { let f = tempfile().unwrap(); - // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test - let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX); + // PATH_MAX is limited on most platforms, so it makes a good test + let path_max = fpathconf(f, PathconfVar::PATH_MAX); assert!( path_max .expect("fpathconf failed") @@ -644,9 +670,10 @@ fn test_fpathconf_limited() { ); } +#[cfg_attr(target_os = "hurd", ignore)] #[test] fn test_pathconf_limited() { - // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test + // PATH_MAX is limited on most platforms, so it makes a good test let path_max = pathconf("/", PathconfVar::PATH_MAX); assert!( path_max @@ -656,9 +683,10 @@ fn test_pathconf_limited() { ); } +#[cfg_attr(target_os = "hurd", ignore)] #[test] fn test_sysconf_limited() { - // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test + // OPEN_MAX is limited on most platforms, so it makes a good test let open_max = sysconf(SysconfVar::OPEN_MAX); assert!( open_max @@ -678,13 +706,7 @@ fn test_sysconf_unsupported() { assert!(open_max.expect("sysconf failed").is_none()) } -#[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "openbsd" -))] +#[cfg(any(linux_android, freebsdlike, target_os = "openbsd"))] #[test] fn test_getresuid() { let resuids = getresuid().unwrap(); @@ -693,13 +715,7 @@ fn test_getresuid() { assert_ne!(resuids.saved.as_raw(), libc::uid_t::MAX); } -#[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "openbsd" -))] +#[cfg(any(linux_android, freebsdlike, target_os = "openbsd"))] #[test] fn test_getresgid() { let resgids = getresgid().unwrap(); @@ -714,12 +730,12 @@ fn test_getresgid() { fn test_pipe() { let (fd0, fd1) = pipe().unwrap(); let m0 = stat::SFlag::from_bits_truncate( - stat::fstat(fd0).unwrap().st_mode as mode_t, + stat::fstat(fd0.as_raw_fd()).unwrap().st_mode as mode_t, ); // S_IFIFO means it's a pipe assert_eq!(m0, SFlag::S_IFIFO); let m1 = stat::SFlag::from_bits_truncate( - stat::fstat(fd1).unwrap().st_mode as mode_t, + stat::fstat(fd1.as_raw_fd()).unwrap().st_mode as mode_t, ); assert_eq!(m1, SFlag::S_IFIFO); } @@ -727,25 +743,25 @@ fn test_pipe() { // pipe2(2) is the same as pipe(2), except it allows setting some flags. Check // that we can set a flag. #[cfg(any( - target_os = "android", - target_os = "dragonfly", + linux_android, + freebsdlike, + solarish, + netbsdlike, target_os = "emscripten", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", target_os = "redox", - target_os = "solaris" ))] #[test] fn test_pipe2() { use nix::fcntl::{fcntl, FcntlArg, FdFlag}; let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap(); - let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap()); + let f0 = FdFlag::from_bits_truncate( + fcntl(fd0.as_raw_fd(), FcntlArg::F_GETFD).unwrap(), + ); assert!(f0.contains(FdFlag::FD_CLOEXEC)); - let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap()); + let f1 = FdFlag::from_bits_truncate( + fcntl(fd1.as_raw_fd(), FcntlArg::F_GETFD).unwrap(), + ); assert!(f1.contains(FdFlag::FD_CLOEXEC)); } @@ -881,6 +897,8 @@ fn test_symlinkat() { #[test] #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_file() { + use nix::fcntl::AtFlags; + let tempdir = tempdir().unwrap(); let oldfilename = "foo.txt"; let oldfilepath = tempdir.path().join(oldfilename); @@ -902,7 +920,7 @@ fn test_linkat_file() { oldfilename, Some(dirfd), newfilename, - LinkatFlags::SymlinkFollow, + AtFlags::AT_SYMLINK_FOLLOW, ) .unwrap(); assert!(newfilepath.exists()); @@ -911,6 +929,8 @@ fn test_linkat_file() { #[test] #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_olddirfd_none() { + use nix::fcntl::AtFlags; + let _dr = crate::DirRestore::new(); let tempdir_oldfile = tempdir().unwrap(); @@ -939,7 +959,7 @@ fn test_linkat_olddirfd_none() { oldfilename, Some(dirfd), newfilename, - LinkatFlags::SymlinkFollow, + AtFlags::AT_SYMLINK_FOLLOW, ) .unwrap(); assert!(newfilepath.exists()); @@ -948,6 +968,8 @@ fn test_linkat_olddirfd_none() { #[test] #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_newdirfd_none() { + use nix::fcntl::AtFlags; + let _dr = crate::DirRestore::new(); let tempdir_oldfile = tempdir().unwrap(); @@ -976,20 +998,17 @@ fn test_linkat_newdirfd_none() { oldfilename, None, newfilename, - LinkatFlags::SymlinkFollow, + AtFlags::AT_SYMLINK_FOLLOW, ) .unwrap(); assert!(newfilepath.exists()); } #[test] -#[cfg(not(any( - target_os = "ios", - target_os = "macos", - target_os = "redox", - target_os = "haiku" -)))] +#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))] fn test_linkat_no_follow_symlink() { + use nix::fcntl::AtFlags; + let _m = crate::CWD_LOCK.read(); let tempdir = tempdir().unwrap(); @@ -1019,7 +1038,7 @@ fn test_linkat_no_follow_symlink() { symoldfilename, Some(dirfd), newfilename, - LinkatFlags::NoSymlinkFollow, + AtFlags::empty(), ) .unwrap(); @@ -1033,6 +1052,8 @@ fn test_linkat_no_follow_symlink() { #[test] #[cfg(not(any(target_os = "redox", target_os = "haiku")))] fn test_linkat_follow_symlink() { + use nix::fcntl::AtFlags; + let _m = crate::CWD_LOCK.read(); let tempdir = tempdir().unwrap(); @@ -1062,7 +1083,7 @@ fn test_linkat_follow_symlink() { symoldfilename, Some(dirfd), newfilename, - LinkatFlags::SymlinkFollow, + AtFlags::AT_SYMLINK_FOLLOW, ) .unwrap(); @@ -1070,8 +1091,8 @@ fn test_linkat_follow_symlink() { // Check the file type of the new link assert_eq!( - (stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t) - & SFlag::S_IFMT), + stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t) + & SFlag::S_IFMT, SFlag::S_IFREG ); @@ -1159,8 +1180,6 @@ fn test_access_file_exists() { .expect("assertion failed"); } -//Clippy false positive https://github.com/rust-lang/rust-clippy/issues/9111 -#[allow(clippy::needless_borrow)] #[cfg(not(target_os = "redox"))] #[test] fn test_user_into_passwd() { @@ -1177,7 +1196,7 @@ fn test_user_into_passwd() { } /// Tests setting the filesystem UID with `setfsuid`. -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(linux_android)] #[test] fn test_setfsuid() { use std::os::unix::fs::PermissionsExt; @@ -1230,9 +1249,11 @@ fn test_ttyname() { grantpt(&fd).expect("grantpt failed"); unlockpt(&fd).expect("unlockpt failed"); let sname = unsafe { ptsname(&fd) }.expect("ptsname failed"); - let fds = open(Path::new(&sname), OFlag::O_RDWR, stat::Mode::empty()) + let fds = fs::OpenOptions::new() + .read(true) + .write(true) + .open(Path::new(&sname)) .expect("open failed"); - assert!(fds > 0); let name = ttyname(fds).expect("ttyname failed"); assert!(name.starts_with("/dev")); @@ -1242,35 +1263,17 @@ fn test_ttyname() { #[cfg(not(any(target_os = "redox", target_os = "fuchsia")))] fn test_ttyname_not_pty() { let fd = File::open("/dev/zero").unwrap(); - assert!(fd.as_raw_fd() > 0); - assert_eq!(ttyname(fd.as_raw_fd()), Err(Errno::ENOTTY)); + assert_eq!(ttyname(fd), Err(Errno::ENOTTY)); } #[test] -#[cfg(not(any( - target_os = "redox", - target_os = "fuchsia", - target_os = "haiku" -)))] -fn test_ttyname_invalid_fd() { - assert_eq!(ttyname(-1), Err(Errno::EBADF)); -} - -#[test] -#[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "dragonfly", -))] +#[cfg(bsd)] fn test_getpeereid() { use std::os::unix::net::UnixStream; let (sock_a, sock_b) = UnixStream::pair().unwrap(); - let (uid_a, gid_a) = getpeereid(sock_a.as_raw_fd()).unwrap(); - let (uid_b, gid_b) = getpeereid(sock_b.as_raw_fd()).unwrap(); + let (uid_a, gid_a) = getpeereid(sock_a).unwrap(); + let (uid_b, gid_b) = getpeereid(sock_b).unwrap(); let uid = geteuid(); let gid = getegid(); @@ -1281,20 +1284,6 @@ fn test_getpeereid() { assert_eq!(gid_a, gid_b); } -#[test] -#[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "dragonfly", -))] -fn test_getpeereid_invalid_fd() { - // getpeereid is not POSIX, so error codes are inconsistent between different Unices. - getpeereid(-1).expect_err("assertion failed"); -} - #[test] #[cfg(not(target_os = "redox"))] fn test_faccessat_none_not_existing() { @@ -1364,11 +1353,7 @@ fn test_faccessat_file_exists() { } #[test] -#[cfg(any( - all(target_os = "linux", not(target_env = "uclibc")), - target_os = "freebsd", - target_os = "dragonfly" -))] +#[cfg(any(all(target_os = "linux", not(target_env = "uclibc")), freebsdlike))] fn test_eaccess_not_existing() { let tempdir = tempdir().unwrap(); let dir = tempdir.path().join("does_not_exist.txt"); @@ -1379,11 +1364,7 @@ fn test_eaccess_not_existing() { } #[test] -#[cfg(any( - all(target_os = "linux", not(target_env = "uclibc")), - target_os = "freebsd", - target_os = "dragonfly" -))] +#[cfg(any(all(target_os = "linux", not(target_env = "uclibc")), freebsdlike))] fn test_eaccess_file_exists() { let tempdir = tempdir().unwrap(); let path = tempdir.path().join("does_exist.txt"); @@ -1391,3 +1372,14 @@ fn test_eaccess_file_exists() { eaccess(&path, AccessFlags::R_OK | AccessFlags::W_OK) .expect("assertion failed"); } + +#[test] +#[cfg(bsd)] +fn test_group_from() { + let group = Group::from_name("wheel").unwrap().unwrap(); + assert!(group.name == "wheel"); + let group_id = group.gid; + let group = Group::from_gid(group_id).unwrap().unwrap(); + assert_eq!(group.gid, group_id); + assert_eq!(group.name, "wheel"); +} -- cgit v1.2.3