Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1,149 @@
use cfg_if::cfg_if;
#[macro_export]
macro_rules! skip {
($($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"))] {
#[macro_export] macro_rules! require_capability {
($name:expr, $capname:ident) => {
use ::caps::{Capability, CapSet, has_cap};
if !has_cap(None, CapSet::Effective, Capability::$capname)
.unwrap()
{
skip!("{} requires capability {}. Skipping test.", $name, Capability::$capname);
}
}
}
} else if #[cfg(not(target_os = "redox"))] {
#[macro_export] macro_rules! require_capability {
($name:expr, $capname:ident) => {}
}
}
}
/// Skip the test if we don't have the ability to mount file systems.
#[cfg(target_os = "freebsd")]
#[macro_export]
macro_rules! require_mount {
($name:expr) => {
use ::sysctl::{CtlValue, Sysctl};
use nix::unistd::Uid;
let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap()
{
skip!(
"{} requires the ability to mount file systems. Skipping test.",
$name
);
}
};
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[macro_export]
macro_rules! skip_if_cirrus {
($reason:expr) => {
if std::env::var_os("CIRRUS_CI").is_some() {
skip!("{}", $reason);
}
};
}
#[cfg(target_os = "freebsd")]
#[macro_export]
macro_rules! skip_if_jailed {
($name:expr) => {
use ::sysctl::{CtlValue, Sysctl};
let ctl = ::sysctl::Ctl::new("security.jail.jailed").unwrap();
if let CtlValue::Int(1) = ctl.value().unwrap() {
skip!("{} cannot run in a jail. Skipping test.", $name);
}
};
}
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
#[macro_export]
macro_rules! skip_if_not_root {
($name:expr) => {
use nix::unistd::Uid;
if !Uid::current().is_root() {
skip!("{} requires root privileges. Skipping test.", $name);
}
};
}
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
#[macro_export] macro_rules! skip_if_seccomp {
($name:expr) => {
if let Ok(s) = std::fs::read_to_string("/proc/self/status") {
for l in s.lines() {
let mut fields = l.split_whitespace();
if fields.next() == Some("Seccomp:") &&
fields.next() != Some("0")
{
skip!("{} cannot be run in Seccomp mode. Skipping test.",
stringify!($name));
}
}
}
}
}
} else if #[cfg(not(target_os = "redox"))] {
#[macro_export] macro_rules! skip_if_seccomp {
($name:expr) => {}
}
}
}
cfg_if! {
if #[cfg(target_os = "linux")] {
#[macro_export] macro_rules! require_kernel_version {
($name:expr, $version_requirement:expr) => {
use semver::{Version, VersionReq};
let version_requirement = VersionReq::parse($version_requirement)
.expect("Bad match_version provided");
let uname = nix::sys::utsname::uname().unwrap();
println!("{}", uname.sysname().to_str().unwrap());
println!("{}", uname.nodename().to_str().unwrap());
println!("{}", uname.release().to_str().unwrap());
println!("{}", uname.version().to_str().unwrap());
println!("{}", uname.machine().to_str().unwrap());
// Fix stuff that the semver parser can't handle
let fixed_release = &uname.release().to_str().unwrap().to_string()
// Fedora 33 reports version as 4.18.el8_2.x86_64 or
// 5.18.200-fc33.x86_64. Remove the underscore.
.replace("_", "-")
// Cirrus-CI reports version as 4.19.112+ . Remove the +
.replace("+", "");
let mut version = Version::parse(fixed_release).unwrap();
//Keep only numeric parts
version.pre = semver::Prerelease::EMPTY;
version.build = semver::BuildMetadata::EMPTY;
if !version_requirement.matches(&version) {
skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`",
stringify!($name), version, version_requirement);
}
}
}
}
}

60
third-party/vendor/nix/test/sys/mod.rs vendored Normal file
View file

@ -0,0 +1,60 @@
mod test_signal;
// NOTE: DragonFly lacks a kernel-level implementation of Posix AIO as of
// this writing. There is an user-level implementation, but whether aio
// works or not heavily depends on which pthread implementation is chosen
// by the user at link time. For this reason we do not want to run aio test
// cases on DragonFly.
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
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"
)))]
mod test_ioctl;
#[cfg(not(target_os = "redox"))]
mod test_mman;
#[cfg(not(target_os = "redox"))]
mod test_select;
#[cfg(target_os = "linux")]
mod test_signalfd;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
mod test_socket;
#[cfg(not(any(target_os = "redox")))]
mod test_sockopt;
mod test_stat;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod test_sysinfo;
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku"
)))]
mod test_termios;
mod test_uio;
mod test_wait;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod test_epoll;
#[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"
))]
mod test_ptrace;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod test_timerfd;

View file

@ -0,0 +1,626 @@
use std::{
io::{Read, Seek, SeekFrom, Write},
ops::Deref,
os::unix::io::AsRawFd,
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
thread, time,
};
use libc::c_int;
use nix::{
errno::*,
sys::{
aio::*,
signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, SigevNotify,
Signal,
},
time::{TimeSpec, TimeValLike},
},
};
use tempfile::tempfile;
lazy_static! {
pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
}
extern "C" fn sigfunc(_: c_int) {
SIGNALED.store(true, Ordering::Relaxed);
}
// Helper that polls an AioCb for completion or error
macro_rules! poll_aio {
($aiocb: expr) => {
loop {
let err = $aiocb.as_mut().error();
if err != Err(Errno::EINPROGRESS) {
break err;
};
thread::sleep(time::Duration::from_millis(10));
}
};
}
mod aio_fsync {
use super::*;
#[test]
fn test_accessors() {
let aiocb = AioFsync::new(
1001,
AioFsyncMode::O_SYNC,
42,
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
/// `AioFsync::submit` should not modify the `AioCb` object if
/// `libc::aio_fsync` returns an error
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn error() {
use std::mem;
const INITIAL: &[u8] = b"abcdef123456";
// Create an invalid AioFsyncMode
let mode = unsafe { mem::transmute(666) };
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiof = Box::pin(AioFsync::new(
f.as_raw_fd(),
mode,
0,
SigevNotify::SigevNone,
));
let err = aiof.as_mut().submit();
err.expect_err("assertion failed");
}
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let fd = f.as_raw_fd();
let mut aiof = Box::pin(AioFsync::new(
fd,
AioFsyncMode::O_SYNC,
0,
SigevNotify::SigevNone,
));
aiof.as_mut().submit().unwrap();
poll_aio!(&mut aiof).unwrap();
aiof.as_mut().aio_return().unwrap();
}
}
mod aio_read {
use super::*;
#[test]
fn test_accessors() {
let mut rbuf = vec![0; 4];
let aiocb = AioRead::new(
1001,
2, //offset
&mut rbuf,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(4, aiocb.nbytes());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
// Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
// only our bindings. So it's sufficient to check that cancel
// returned any AioCancelStat value.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn cancel() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let fd = f.as_raw_fd();
let mut aior =
Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone));
aior.as_mut().submit().unwrap();
aior.as_mut().cancel().unwrap();
// Wait for aiow to complete, but don't care whether it succeeded
let _ = poll_aio!(&mut aior);
let _ = aior.as_mut().aio_return();
}
/// `AioRead::submit` should not modify the `AioCb` object if
/// `libc::aio_read` returns an error
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn error() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aior = Box::pin(AioRead::new(
f.as_raw_fd(),
-1, //an invalid offset
&mut rbuf,
0, //priority
SigevNotify::SigevNone,
));
aior.as_mut().submit().expect_err("assertion failed");
}
// Test a simple aio operation with no completion notification. We must
// poll for completion
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
const EXPECT: &[u8] = b"cdef";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_raw_fd();
let mut aior = Box::pin(AioRead::new(
fd,
2,
&mut rbuf,
0,
SigevNotify::SigevNone,
));
aior.as_mut().submit().unwrap();
let err = poll_aio!(&mut aior);
assert_eq!(err, Ok(()));
assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
}
assert_eq!(EXPECT, rbuf.deref().deref());
}
// Like ok, but allocates the structure on the stack.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn on_stack() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
const EXPECT: &[u8] = b"cdef";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_raw_fd();
let mut aior =
AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone);
let mut aior = unsafe { Pin::new_unchecked(&mut aior) };
aior.as_mut().submit().unwrap();
let err = poll_aio!(&mut aior);
assert_eq!(err, Ok(()));
assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
}
assert_eq!(EXPECT, rbuf.deref().deref());
}
}
#[cfg(target_os = "freebsd")]
#[cfg(fbsd14)]
mod aio_readv {
use std::io::IoSliceMut;
use super::*;
#[test]
fn test_accessors() {
let mut rbuf0 = vec![0; 4];
let mut rbuf1 = vec![0; 8];
let mut rbufs =
[IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
let aiocb = AioReadv::new(
1001,
2, //offset
&mut rbufs,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(2, aiocb.iovlen());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf0 = vec![0; 4];
let mut rbuf1 = vec![0; 2];
let mut rbufs =
[IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
const EXPECT0: &[u8] = b"cdef";
const EXPECT1: &[u8] = b"12";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_raw_fd();
let mut aior = Box::pin(AioReadv::new(
fd,
2,
&mut rbufs,
0,
SigevNotify::SigevNone,
));
aior.as_mut().submit().unwrap();
let err = poll_aio!(&mut aior);
assert_eq!(err, Ok(()));
assert_eq!(
aior.as_mut().aio_return().unwrap(),
EXPECT0.len() + EXPECT1.len()
);
}
assert_eq!(&EXPECT0, &rbuf0);
assert_eq!(&EXPECT1, &rbuf1);
}
}
mod aio_write {
use super::*;
#[test]
fn test_accessors() {
let wbuf = vec![0; 4];
let aiocb = AioWrite::new(
1001,
2, //offset
&wbuf,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(4, aiocb.nbytes());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
// Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
// only our bindings. So it's sufficient to check that cancel
// returned any AioCancelStat value.
#[test]
#[cfg_attr(target_env = "musl", ignore)]
fn cancel() {
let wbuf: &[u8] = b"CDEF";
let f = tempfile().unwrap();
let mut aiow = Box::pin(AioWrite::new(
f.as_raw_fd(),
0,
wbuf,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = aiow.as_mut().error();
assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
aiow.as_mut().cancel().unwrap();
// Wait for aiow to complete, but don't care whether it succeeded
let _ = poll_aio!(&mut aiow);
let _ = aiow.as_mut().aio_return();
}
// Test a simple aio operation with no completion notification. We must
// poll for completion.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let wbuf = "CDEF".to_string().into_bytes();
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"abCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = Box::pin(AioWrite::new(
f.as_raw_fd(),
2,
&wbuf,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
f.seek(SeekFrom::Start(0)).unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
// Like ok, but allocates the structure on the stack.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn on_stack() {
const INITIAL: &[u8] = b"abcdef123456";
let wbuf = "CDEF".to_string().into_bytes();
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"abCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = AioWrite::new(
f.as_raw_fd(),
2, //offset
&wbuf,
0, //priority
SigevNotify::SigevNone,
);
let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
f.seek(SeekFrom::Start(0)).unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
/// `AioWrite::write` should not modify the `AioCb` object if
/// `libc::aio_write` returns an error.
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn error() {
let wbuf = "CDEF".to_string().into_bytes();
let mut aiow = Box::pin(AioWrite::new(
666, // An invalid file descriptor
0, //offset
&wbuf,
0, //priority
SigevNotify::SigevNone,
));
aiow.as_mut().submit().expect_err("assertion failed");
// Dropping the AioWrite at this point should not panic
}
}
#[cfg(target_os = "freebsd")]
#[cfg(fbsd14)]
mod aio_writev {
use std::io::IoSlice;
use super::*;
#[test]
fn test_accessors() {
let wbuf0 = vec![0; 4];
let wbuf1 = vec![0; 8];
let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)];
let aiocb = AioWritev::new(
1001,
2, //offset
&wbufs,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(1001, aiocb.fd());
assert_eq!(2, aiocb.iovlen());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
// Test a simple aio operation with no completion notification. We must
// poll for completion.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let wbuf0 = b"BC";
let wbuf1 = b"DEF";
let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
let wlen = wbuf0.len() + wbuf1.len();
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"aBCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = Box::pin(AioWritev::new(
f.as_raw_fd(),
1,
&wbufs,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
f.seek(SeekFrom::Start(0)).unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
}
// Test an aio operation with completion delivered by a signal
#[test]
#[cfg_attr(
any(
all(target_env = "musl", target_arch = "x86_64"),
target_arch = "mips",
target_arch = "mips64"
),
ignore
)]
fn sigev_signal() {
let _m = crate::SIGNAL_MTX.lock();
let sa = SigAction::new(
SigHandler::Handler(sigfunc),
SaFlags::SA_RESETHAND,
SigSet::empty(),
);
SIGNALED.store(false, Ordering::Relaxed);
unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
const INITIAL: &[u8] = b"abcdef123456";
const WBUF: &[u8] = b"CDEF";
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"abCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiow = Box::pin(AioWrite::new(
f.as_raw_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 0, //TODO: validate in sigfunc
},
));
aiow.as_mut().submit().unwrap();
while !SIGNALED.load(Ordering::Relaxed) {
thread::sleep(time::Duration::from_millis(10));
}
assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
f.seek(SeekFrom::Start(0)).unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
// Tests using aio_cancel_all for all outstanding IOs.
#[test]
#[cfg_attr(target_env = "musl", ignore)]
fn test_aio_cancel_all() {
let wbuf: &[u8] = b"CDEF";
let f = tempfile().unwrap();
let mut aiocb = Box::pin(AioWrite::new(
f.as_raw_fd(),
0, //offset
wbuf,
0, //priority
SigevNotify::SigevNone,
));
aiocb.as_mut().submit().unwrap();
let err = aiocb.as_mut().error();
assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
aio_cancel_all(f.as_raw_fd()).unwrap();
// Wait for aiocb to complete, but don't care whether it succeeded
let _ = poll_aio!(&mut aiocb);
let _ = aiocb.as_mut().aio_return();
}
#[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";
let timeout = TimeSpec::seconds(10);
let mut rbuf = vec![0; 4];
let rlen = rbuf.len();
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut wcb = Box::pin(AioWrite::new(
f.as_raw_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevNone,
));
let mut rcb = Box::pin(AioRead::new(
f.as_raw_fd(),
8, //offset
&mut rbuf,
0, //priority
SigevNotify::SigevNone,
));
wcb.as_mut().submit().unwrap();
rcb.as_mut().submit().unwrap();
loop {
{
let cbbuf = [
&*wcb as &dyn AsRef<libc::aiocb>,
&*rcb as &dyn AsRef<libc::aiocb>,
];
let r = aio_suspend(&cbbuf[..], Some(timeout));
match r {
Err(Errno::EINTR) => continue,
Err(e) => panic!("aio_suspend returned {:?}", e),
Ok(_) => (),
};
}
if rcb.as_mut().error() != Err(Errno::EINPROGRESS)
&& wcb.as_mut().error() != Err(Errno::EINPROGRESS)
{
break;
}
}
assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len());
assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen);
}

View file

@ -0,0 +1,35 @@
// Test dropping an AioCb that hasn't yet finished.
// This must happen in its own process, because on OSX this test seems to hose
// the AIO subsystem and causes subsequent tests to fail
#[test]
#[should_panic(expected = "Dropped an in-progress AioCb")]
#[cfg(all(
not(target_env = "musl"),
not(target_env = "uclibc"),
any(
target_os = "linux",
target_os = "ios",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd"
)
))]
fn test_drop() {
use nix::sys::aio::*;
use nix::sys::signal::*;
use std::os::unix::io::AsRawFd;
use tempfile::tempfile;
const WBUF: &[u8] = b"CDEF";
let f = tempfile().unwrap();
f.set_len(6).unwrap();
let mut aiocb = Box::pin(AioWrite::new(
f.as_raw_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevNone,
));
aiocb.as_mut().submit().unwrap();
}

View file

@ -0,0 +1,24 @@
use nix::errno::Errno;
use nix::sys::epoll::{epoll_create1, epoll_ctl};
use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp};
#[test]
pub fn test_epoll_errno() {
let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
result.expect_err("assertion failed");
assert_eq!(result.unwrap_err(), Errno::ENOENT);
let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
result.expect_err("assertion failed");
assert_eq!(result.unwrap_err(), Errno::EINVAL);
}
#[test]
pub fn test_epoll_ctl() {
let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
let mut event =
EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap();
}

View file

@ -0,0 +1,65 @@
use nix::errno::Errno;
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
use std::ffi::OsString;
use std::fs::{rename, File};
#[test]
pub fn test_inotify() {
let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
let tempdir = tempfile::tempdir().unwrap();
instance
.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
.unwrap();
let events = instance.read_events();
assert_eq!(events.unwrap_err(), Errno::EAGAIN);
File::create(tempdir.path().join("test")).unwrap();
let events = instance.read_events().unwrap();
assert_eq!(events[0].name, Some(OsString::from("test")));
}
#[test]
pub fn test_inotify_multi_events() {
let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
let tempdir = tempfile::tempdir().unwrap();
instance
.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
.unwrap();
let events = instance.read_events();
assert_eq!(events.unwrap_err(), Errno::EAGAIN);
File::create(tempdir.path().join("test")).unwrap();
rename(tempdir.path().join("test"), tempdir.path().join("test2")).unwrap();
// Now there should be 5 events in queue:
// - IN_CREATE on test
// - IN_OPEN on test
// - IN_CLOSE_WRITE on test
// - IN_MOVED_FROM on test with a cookie
// - IN_MOVED_TO on test2 with the same cookie
let events = instance.read_events().unwrap();
assert_eq!(events.len(), 5);
assert_eq!(events[0].mask, AddWatchFlags::IN_CREATE);
assert_eq!(events[0].name, Some(OsString::from("test")));
assert_eq!(events[1].mask, AddWatchFlags::IN_OPEN);
assert_eq!(events[1].name, Some(OsString::from("test")));
assert_eq!(events[2].mask, AddWatchFlags::IN_CLOSE_WRITE);
assert_eq!(events[2].name, Some(OsString::from("test")));
assert_eq!(events[3].mask, AddWatchFlags::IN_MOVED_FROM);
assert_eq!(events[3].name, Some(OsString::from("test")));
assert_eq!(events[4].mask, AddWatchFlags::IN_MOVED_TO);
assert_eq!(events[4].name, Some(OsString::from("test2")));
assert_eq!(events[3].cookie, events[4].cookie);
}

View file

@ -0,0 +1,376 @@
#![allow(dead_code)]
// Simple tests to ensure macro generated fns compile
ioctl_none_bad!(do_bad, 0x1234);
ioctl_read_bad!(do_bad_read, 0x1234, u16);
ioctl_write_int_bad!(do_bad_write_int, 0x1234);
ioctl_write_ptr_bad!(do_bad_write_ptr, 0x1234, u8);
ioctl_readwrite_bad!(do_bad_readwrite, 0x1234, u32);
ioctl_none!(do_none, 0, 0);
ioctl_read!(read_test, 0, 0, u32);
ioctl_write_int!(write_ptr_int, 0, 0);
ioctl_write_ptr!(write_ptr_u8, 0, 0, u8);
ioctl_write_ptr!(write_ptr_u32, 0, 0, u32);
ioctl_write_ptr!(write_ptr_u64, 0, 0, u64);
ioctl_readwrite!(readwrite_test, 0, 0, u64);
ioctl_read_buf!(readbuf_test, 0, 0, u32);
const SPI_IOC_MAGIC: u8 = b'k';
const SPI_IOC_MESSAGE: u8 = 0;
ioctl_write_buf!(writebuf_test_consts, SPI_IOC_MAGIC, SPI_IOC_MESSAGE, u8);
ioctl_write_buf!(writebuf_test_u8, 0, 0, u8);
ioctl_write_buf!(writebuf_test_u32, 0, 0, u32);
ioctl_write_buf!(writebuf_test_u64, 0, 0, u64);
ioctl_readwrite_buf!(readwritebuf_test, 0, 0, u32);
// See C code for source of values for op calculations (does NOT work for mips/powerpc):
// https://gist.github.com/posborne/83ea6880770a1aef332e
//
// 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"))]
mod linux {
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_none() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips64",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
assert_eq!(request_code_none!(b'q', 10) as u32, 0x2000_710A);
assert_eq!(request_code_none!(b'a', 255) as u32, 0x2000_61FF);
} else {
assert_eq!(request_code_none!(b'q', 10) as u32, 0x0000_710A);
assert_eq!(request_code_none!(b'a', 255) as u32, 0x0000_61FF);
}
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_write() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips64",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x8001_7A0A);
assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x8200_7A0A);
} else {
assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x4001_7A0A);
assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x4200_7A0A);
}
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_write_64() {
if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
assert_eq!(
request_code_write!(b'z', 10, 1u64 << 32) as u32,
0x8000_7A0A
);
} else {
assert_eq!(
request_code_write!(b'z', 10, 1u64 << 32) as u32,
0x4000_7A0A
);
}
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_read() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips64",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x4001_7A0A);
assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x4200_7A0A);
} else {
assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x8001_7A0A);
assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x8200_7A0A);
}
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_64() {
if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
assert_eq!(
request_code_read!(b'z', 10, 1u64 << 32) as u32,
0x4000_7A0A
);
} else {
assert_eq!(
request_code_read!(b'z', 10, 1u64 << 32) as u32,
0x8000_7A0A
);
}
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_read_write() {
assert_eq!(request_code_readwrite!(b'z', 10, 1) as u32, 0xC001_7A0A);
assert_eq!(request_code_readwrite!(b'z', 10, 512) as u32, 0xC200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_write_64() {
assert_eq!(
request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
0xC000_7A0A
);
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
mod bsd {
#[test]
fn test_op_none() {
assert_eq!(request_code_none!(b'q', 10), 0x2000_710A);
assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF);
}
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
#[test]
fn test_op_write_int() {
assert_eq!(request_code_write_int!(b'v', 4), 0x2004_7604);
assert_eq!(request_code_write_int!(b'p', 2), 0x2004_7002);
}
#[test]
fn test_op_write() {
assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A);
assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_write_64() {
assert_eq!(request_code_write!(b'z', 10, 1u64 << 32), 0x8000_7A0A);
}
#[test]
fn test_op_read() {
assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A);
assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_64() {
assert_eq!(request_code_read!(b'z', 10, 1u64 << 32), 0x4000_7A0A);
}
#[test]
fn test_op_read_write() {
assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A);
assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_write_64() {
assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32), 0xC000_7A0A);
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
mod linux_ioctls {
use std::mem;
use std::os::unix::io::AsRawFd;
use libc::{termios, TCGETS, TCSBRK, TCSETS, TIOCNXCL};
use tempfile::tempfile;
use nix::errno::Errno;
ioctl_none_bad!(tiocnxcl, TIOCNXCL);
#[test]
fn test_ioctl_none_bad() {
let file = tempfile().unwrap();
let res = unsafe { tiocnxcl(file.as_raw_fd()) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_read_bad!(tcgets, TCGETS, termios);
#[test]
fn test_ioctl_read_bad() {
let file = tempfile().unwrap();
let mut termios = unsafe { mem::zeroed() };
let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_int_bad!(tcsbrk, TCSBRK);
#[test]
fn test_ioctl_write_int_bad() {
let file = tempfile().unwrap();
let res = unsafe { tcsbrk(file.as_raw_fd(), 0) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_ptr_bad!(tcsets, TCSETS, termios);
#[test]
fn test_ioctl_write_ptr_bad() {
let file = tempfile().unwrap();
let termios: termios = unsafe { mem::zeroed() };
let res = unsafe { tcsets(file.as_raw_fd(), &termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
// FIXME: Find a suitable example for `ioctl_readwrite_bad`
// From linux/videodev2.h
ioctl_none!(log_status, b'V', 70);
#[test]
fn test_ioctl_none() {
let file = tempfile().unwrap();
let res = unsafe { log_status(file.as_raw_fd()) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
#[repr(C)]
pub struct v4l2_audio {
index: u32,
name: [u8; 32],
capability: u32,
mode: u32,
reserved: [u32; 2],
}
// From linux/videodev2.h
ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
#[test]
fn test_ioctl_write_ptr() {
let file = tempfile().unwrap();
let data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { s_audio(file.as_raw_fd(), &data) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/net/bluetooth/hci_sock.h
const HCI_IOC_MAGIC: u8 = b'H';
const HCI_IOC_HCIDEVUP: u8 = 201;
ioctl_write_int!(hcidevup, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
#[test]
fn test_ioctl_write_int() {
let file = tempfile().unwrap();
let res = unsafe { hcidevup(file.as_raw_fd(), 0) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/videodev2.h
ioctl_read!(g_audio, b'V', 33, v4l2_audio);
#[test]
fn test_ioctl_read() {
let file = tempfile().unwrap();
let mut data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { g_audio(file.as_raw_fd(), &mut data) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/videodev2.h
ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
#[test]
fn test_ioctl_readwrite() {
let file = tempfile().unwrap();
let mut data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// FIXME: Find a suitable example for `ioctl_read_buf`.
#[repr(C)]
pub struct spi_ioc_transfer {
tx_buf: u64,
rx_buf: u64,
len: u32,
speed_hz: u32,
delay_usecs: u16,
bits_per_word: u8,
cs_change: u8,
tx_nbits: u8,
rx_nbits: u8,
pad: u16,
}
// From linux/spi/spidev.h
ioctl_write_buf!(
spi_ioc_message,
super::SPI_IOC_MAGIC,
super::SPI_IOC_MESSAGE,
spi_ioc_transfer
);
#[test]
fn test_ioctl_write_buf() {
let file = tempfile().unwrap();
let data: [spi_ioc_transfer; 4] = unsafe { mem::zeroed() };
let res = unsafe { spi_ioc_message(file.as_raw_fd(), &data[..]) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// FIXME: Find a suitable example for `ioctl_readwrite_buf`.
}
#[cfg(target_os = "freebsd")]
mod freebsd_ioctls {
use std::mem;
use std::os::unix::io::AsRawFd;
use libc::termios;
use tempfile::tempfile;
use nix::errno::Errno;
// From sys/sys/ttycom.h
const TTY_IOC_MAGIC: u8 = b't';
const TTY_IOC_TYPE_NXCL: u8 = 14;
const TTY_IOC_TYPE_GETA: u8 = 19;
const TTY_IOC_TYPE_SETA: u8 = 20;
ioctl_none!(tiocnxcl, TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL);
#[test]
fn test_ioctl_none() {
let file = tempfile().unwrap();
let res = unsafe { tiocnxcl(file.as_raw_fd()) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_read!(tiocgeta, TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA, termios);
#[test]
fn test_ioctl_read() {
let file = tempfile().unwrap();
let mut termios = unsafe { mem::zeroed() };
let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_ptr!(tiocseta, TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA, termios);
#[test]
fn test_ioctl_write_ptr() {
let file = tempfile().unwrap();
let termios: termios = unsafe { mem::zeroed() };
let res = unsafe { tiocseta(file.as_raw_fd(), &termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
}

View file

@ -0,0 +1,117 @@
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
#[test]
fn test_mmap_anonymous() {
unsafe {
let ptr = mmap(
std::ptr::null_mut(),
1,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
-1,
0,
)
.unwrap() as *mut u8;
assert_eq!(*ptr, 0x00u8);
*ptr = 0xffu8;
assert_eq!(*ptr, 0xffu8);
}
}
#[test]
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
fn test_mremap_grow() {
use nix::libc::{c_void, size_t};
use nix::sys::mman::{mremap, MRemapFlags};
const ONE_K: size_t = 1024;
let slice: &mut [u8] = unsafe {
let mem = mmap(
std::ptr::null_mut(),
ONE_K,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
-1,
0,
)
.unwrap();
std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
};
assert_eq!(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
assert_eq!(slice[ONE_K - 1], 0xFF);
let slice: &mut [u8] = unsafe {
#[cfg(target_os = "linux")]
let mem = mremap(
slice.as_mut_ptr() as *mut c_void,
ONE_K,
10 * ONE_K,
MRemapFlags::MREMAP_MAYMOVE,
None,
)
.unwrap();
#[cfg(target_os = "netbsd")]
let mem = mremap(
slice.as_mut_ptr() as *mut c_void,
ONE_K,
10 * ONE_K,
MRemapFlags::MAP_REMAPDUP,
None,
)
.unwrap();
std::slice::from_raw_parts_mut(mem as *mut u8, 10 * ONE_K)
};
// The first KB should still have the old data in it.
assert_eq!(slice[ONE_K - 1], 0xFF);
// The additional range should be zero-init'd and accessible.
assert_eq!(slice[10 * ONE_K - 1], 0x00);
slice[10 * ONE_K - 1] = 0xFF;
assert_eq!(slice[10 * ONE_K - 1], 0xFF);
}
#[test]
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
// 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::sys::mman::{mremap, MRemapFlags};
const ONE_K: size_t = 1024;
let slice: &mut [u8] = unsafe {
let mem = mmap(
std::ptr::null_mut(),
10 * ONE_K,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
-1,
0,
)
.unwrap();
std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
};
assert_eq!(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
assert_eq!(slice[ONE_K - 1], 0xFF);
let slice: &mut [u8] = unsafe {
let mem = mremap(
slice.as_mut_ptr() as *mut c_void,
10 * ONE_K,
ONE_K,
MRemapFlags::empty(),
None,
)
.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)
};
// The first KB should still be accessible and have the old data in it.
assert_eq!(slice[ONE_K - 1], 0xFF);
}

View file

@ -0,0 +1,22 @@
use nix::sys::pthread::*;
#[cfg(any(target_env = "musl", target_os = "redox"))]
#[test]
fn test_pthread_self() {
let tid = pthread_self();
assert!(!tid.is_null());
}
#[cfg(not(any(target_env = "musl", target_os = "redox")))]
#[test]
fn test_pthread_self() {
let tid = pthread_self();
assert_ne!(tid, 0);
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_pthread_kill_none() {
pthread_kill(pthread_self(), None)
.expect("Should be able to send signal to my thread.");
}

View file

@ -0,0 +1,275 @@
#[cfg(all(
target_os = "linux",
any(target_arch = "x86_64", target_arch = "x86"),
target_env = "gnu"
))]
use memoffset::offset_of;
use nix::errno::Errno;
use nix::sys::ptrace;
#[cfg(any(target_os = "android", target_os = "linux"))]
use nix::sys::ptrace::Options;
use nix::unistd::getpid;
#[cfg(any(target_os = "android", target_os = "linux"))]
use std::mem;
use crate::*;
#[test]
fn test_ptrace() {
// Just make sure ptrace can be called at all, for now.
// FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
require_capability!("test_ptrace", CAP_SYS_PTRACE);
let err = ptrace::attach(getpid()).unwrap_err();
assert!(
err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS
);
}
// Just make sure ptrace_setoptions can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_setoptions() {
require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
.unwrap_err();
assert_ne!(err, Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getevent can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_getevent() {
require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
let err = ptrace::getevent(getpid()).unwrap_err();
assert_ne!(err, Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getsiginfo can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_getsiginfo() {
require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
}
}
// Just make sure ptrace_setsiginfo can be called at all, for now.
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_setsiginfo() {
require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE);
let siginfo = unsafe { mem::zeroed() };
if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
}
}
#[test]
fn test_ptrace_cont() {
use nix::sys::ptrace;
use nix::sys::signal::{raise, Signal};
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_cont", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
// FIXME: qemu-user doesn't implement ptrace on all architectures
// and returns ENOSYS in this case.
// We (ab)use this behavior to detect the affected platforms
// and skip the test then.
// On valid platforms the ptrace call should return Errno::EPERM, this
// is already tested by `test_ptrace`.
let err = ptrace::attach(getpid()).unwrap_err();
if err == Errno::ENOSYS {
return;
}
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
// As recommended by ptrace(2), raise SIGTRAP to pause the child
// until the parent is ready to continue
loop {
raise(Signal::SIGTRAP).unwrap();
}
}
Parent { child } => {
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
ptrace::cont(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
if pid == child =>
{
// FIXME It's been observed on some systems (apple) the
// tracee may not be killed but remain as a zombie process
// affecting other wait based tests. Add an extra kill just
// to make sure there are no zombies.
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
}
}
_ => panic!("The process should have been killed"),
}
}
}
}
#[cfg(target_os = "linux")]
#[test]
fn test_ptrace_interrupt() {
use nix::sys::ptrace;
use nix::sys::signal::Signal;
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
use std::thread::sleep;
use std::time::Duration;
require_capability!("test_ptrace_interrupt", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => loop {
sleep(Duration::from_millis(1000));
},
Parent { child } => {
ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
.unwrap();
ptrace::interrupt(child).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))
);
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceSyscall(child))
);
ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
if pid == child =>
{
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
}
}
_ => panic!("The process should have been killed"),
}
}
}
}
// ptrace::{setoptions, getregs} are only available in these platforms
#[cfg(all(
target_os = "linux",
any(target_arch = "x86_64", target_arch = "x86"),
target_env = "gnu"
))]
#[test]
fn test_ptrace_syscall() {
use nix::sys::ptrace;
use nix::sys::signal::kill;
use nix::sys::signal::Signal;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::fork;
use nix::unistd::getpid;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
// first sigstop until parent is ready to continue
let pid = getpid();
kill(pid, Signal::SIGSTOP).unwrap();
kill(pid, Signal::SIGTERM).unwrap();
unsafe {
::libc::_exit(0);
}
}
Parent { child } => {
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))
);
// set this option to recognize syscall-stops
ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
.unwrap();
#[cfg(target_arch = "x86_64")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
#[cfg(target_arch = "x86")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
// this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
#[cfg(target_arch = "x86_64")]
let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
#[cfg(target_arch = "x86")]
let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
let get_syscall_from_user_area = || {
// Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
let rax_offset = offset_of!(libc::user, regs) + rax_offset;
ptrace::read_user(child, rax_offset as _).unwrap()
as libc::c_long
};
// kill entry
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceSyscall(child))
);
assert_eq!(get_syscall_id(), ::libc::SYS_kill);
assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
// kill exit
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceSyscall(child))
);
assert_eq!(get_syscall_id(), ::libc::SYS_kill);
assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
// receive signal
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTERM))
);
// inject signal
ptrace::syscall(child, Signal::SIGTERM).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))
);
}
}
}

View file

@ -0,0 +1,81 @@
use nix::sys::select::*;
use nix::sys::signal::SigSet;
use nix::sys::time::{TimeSpec, TimeValLike};
use nix::unistd::{pipe, write};
#[test]
pub fn test_pselect() {
let _mtx = crate::SIGNAL_MTX.lock();
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);
fd_set.insert(r2);
let timeout = TimeSpec::seconds(10);
let sigmask = SigSet::empty();
assert_eq!(
1,
pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap()
);
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
}
#[test]
pub fn test_pselect_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);
fd_set.insert(r2);
let timeout = TimeSpec::seconds(10);
assert_eq!(
1,
pselect(
::std::cmp::max(r1, r2) + 1,
&mut fd_set,
None,
None,
&timeout,
None
)
.unwrap()
);
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
}
macro_rules! generate_fdset_bad_fd_tests {
($fd:expr, $($method:ident),* $(,)?) => {
$(
#[test]
#[should_panic]
fn $method() {
FdSet::new().$method($fd);
}
)*
}
}
mod test_fdset_negative_fd {
use super::*;
generate_fdset_bad_fd_tests!(-1, insert, remove, contains);
}
mod test_fdset_too_large_fd {
use super::*;
use std::convert::TryInto;
generate_fdset_bad_fd_tests!(
FD_SETSIZE.try_into().unwrap(),
insert,
remove,
contains,
);
}

View file

@ -0,0 +1,147 @@
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
use nix::sys::signal::*;
use nix::unistd::*;
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, Ordering};
#[test]
fn test_kill_none() {
kill(getpid(), None).expect("Should be able to send signal to myself.");
}
#[test]
#[cfg(not(target_os = "fuchsia"))]
fn test_killpg_none() {
killpg(getpgrp(), None)
.expect("Should be able to send signal to my process group.");
}
#[test]
fn test_old_sigaction_flags() {
let _m = crate::SIGNAL_MTX.lock();
extern "C" fn handler(_: ::libc::c_int) {}
let act = SigAction::new(
SigHandler::Handler(handler),
SaFlags::empty(),
SigSet::empty(),
);
let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
let _flags = oact.flags();
let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
let _flags = oact.flags();
}
#[test]
fn test_sigprocmask_noop() {
sigprocmask(SigmaskHow::SIG_BLOCK, None, None)
.expect("this should be an effective noop");
}
#[test]
fn test_sigprocmask() {
let _m = crate::SIGNAL_MTX.lock();
// This needs to be a signal that rust doesn't use in the test harness.
const SIGNAL: Signal = Signal::SIGCHLD;
let mut old_signal_set = SigSet::empty();
sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
.expect("expect to be able to retrieve old signals");
// Make sure the old set doesn't contain the signal, otherwise the following
// test don't make sense.
assert!(
!old_signal_set.contains(SIGNAL),
"the {:?} signal is already blocked, please change to a \
different one",
SIGNAL
);
// Now block the signal.
let mut signal_set = SigSet::empty();
signal_set.add(SIGNAL);
sigprocmask(SigmaskHow::SIG_BLOCK, Some(&signal_set), None)
.expect("expect to be able to block signals");
// And test it again, to make sure the change was effective.
old_signal_set.clear();
sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
.expect("expect to be able to retrieve old signals");
assert!(
old_signal_set.contains(SIGNAL),
"expected the {:?} to be blocked",
SIGNAL
);
// Reset the signal.
sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
.expect("expect to be able to block signals");
}
lazy_static! {
static ref SIGNALED: AtomicBool = AtomicBool::new(false);
}
extern "C" fn test_sigaction_handler(signal: libc::c_int) {
let signal = Signal::try_from(signal).unwrap();
SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
}
#[cfg(not(target_os = "redox"))]
extern "C" fn test_sigaction_action(
_: libc::c_int,
_: *mut libc::siginfo_t,
_: *mut libc::c_void,
) {
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_signal_sigaction() {
let _m = crate::SIGNAL_MTX.lock();
let action_handler = SigHandler::SigAction(test_sigaction_action);
assert_eq!(
unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(),
Errno::ENOTSUP
);
}
#[test]
fn test_signal() {
let _m = crate::SIGNAL_MTX.lock();
unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
raise(Signal::SIGINT).unwrap();
assert_eq!(
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
SigHandler::SigIgn
);
let handler = SigHandler::Handler(test_sigaction_handler);
assert_eq!(
unsafe { signal(Signal::SIGINT, handler) }.unwrap(),
SigHandler::SigDfl
);
raise(Signal::SIGINT).unwrap();
assert!(SIGNALED.load(Ordering::Relaxed));
#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
assert_eq!(
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
handler
);
// 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"))]
assert_eq!(
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
SigHandler::SigDfl
);
// Restore default signal handler
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
}

View file

@ -0,0 +1,27 @@
use std::convert::TryFrom;
#[test]
fn test_signalfd() {
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();
mask.add(signal::SIGUSR1);
mask.thread_block().unwrap();
let mut fd = SignalFd::new(&mask).unwrap();
// Send a SIGUSR1 signal to the current process. Note that this uses `raise` instead of `kill`
// because `kill` with `getpid` isn't correct during multi-threaded execution like during a
// 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);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,356 @@
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::*;
use nix::sys::socket::{
getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag,
SockProtocol, SockType,
};
use rand::{thread_rng, Rng};
// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",))]
#[test]
pub fn test_local_peercred_seqpacket() {
use nix::{
sys::socket::socketpair,
unistd::{Gid, Uid},
};
let (fd1, _fd2) = socketpair(
AddressFamily::Unix,
SockType::SeqPacket,
None,
SockFlag::empty(),
)
.unwrap();
let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
assert_eq!(xucred.version(), 0);
assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "ios"
))]
#[test]
pub fn test_local_peercred_stream() {
use nix::{
sys::socket::socketpair,
unistd::{Gid, Uid},
};
let (fd1, _fd2) = socketpair(
AddressFamily::Unix,
SockType::Stream,
None,
SockFlag::empty(),
)
.unwrap();
let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
assert_eq!(xucred.version(), 0);
assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
}
#[cfg(target_os = "linux")]
#[test]
fn is_so_mark_functional() {
use nix::sys::socket::sockopt;
require_capability!("is_so_mark_functional", CAP_NET_ADMIN);
let s = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(s, sockopt::Mark, &1337).unwrap();
let mark = getsockopt(s, sockopt::Mark).unwrap();
assert_eq!(mark, 1337);
}
#[test]
fn test_so_buf() {
let fd = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
SockProtocol::Udp,
)
.unwrap();
let bufsize: usize = thread_rng().gen_range(4096..131_072);
setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap();
let actual = getsockopt(fd, sockopt::SndBuf).unwrap();
assert!(actual >= bufsize);
setsockopt(fd, sockopt::RcvBuf, &bufsize).unwrap();
let actual = getsockopt(fd, sockopt::RcvBuf).unwrap();
assert!(actual >= bufsize);
}
#[test]
fn test_so_tcp_maxseg() {
use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn};
use nix::unistd::{close, 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 rsock = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
bind(rsock, &sock_addr).unwrap();
listen(rsock, 10).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"))] {
let segsize: u32 = 873;
assert!(initial < segsize);
setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
} else {
assert!(initial < 700);
}
}
// Connect and check the MSS that was advertised
let ssock = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
connect(ssock, &sock_addr).unwrap();
let rsess = accept(rsock).unwrap();
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"))] {
assert!((segsize - 100) <= actual);
assert!(actual <= segsize);
} else {
assert!(initial < actual);
assert!(536 < actual);
}
}
close(rsock).unwrap();
close(ssock).unwrap();
}
#[test]
fn test_so_type() {
let sockfd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
assert_eq!(Ok(SockType::Stream), getsockopt(sockfd, sockopt::SockType));
}
/// 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",))]
#[test]
fn test_so_type_unknown() {
use nix::errno::Errno;
require_capability!("test_so_type", CAP_NET_RAW);
let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last());
assert_eq!(Err(Errno::EINVAL), getsockopt(sockfd, sockopt::SockType));
}
// The CI doesn't supported getsockopt and setsockopt on emulated processors.
// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
// So the syscall doesn't work properly unless the kernel is also emulated.
#[test]
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
any(target_os = "freebsd", target_os = "linux")
))]
fn test_tcp_congestion() {
use std::ffi::OsString;
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let val = getsockopt(fd, sockopt::TcpCongestion).unwrap();
setsockopt(fd, sockopt::TcpCongestion, &val).unwrap();
setsockopt(
fd,
sockopt::TcpCongestion,
&OsString::from("tcp_congestion_does_not_exist"),
)
.unwrap_err();
assert_eq!(getsockopt(fd, sockopt::TcpCongestion).unwrap(), val);
}
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_bindtodevice() {
skip_if_not_root!("test_bindtodevice");
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)
.unwrap();
let val = getsockopt(fd, sockopt::BindToDevice).unwrap();
setsockopt(fd, sockopt::BindToDevice, &val).unwrap();
assert_eq!(getsockopt(fd, sockopt::BindToDevice).unwrap(), val);
}
#[test]
fn test_so_tcp_keepalive() {
let fd = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
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"
))]
{
let x = getsockopt(fd, sockopt::TcpKeepIdle).unwrap();
setsockopt(fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap();
assert_eq!(getsockopt(fd, sockopt::TcpKeepIdle).unwrap(), x + 1);
let x = getsockopt(fd, sockopt::TcpKeepCount).unwrap();
setsockopt(fd, sockopt::TcpKeepCount, &(x + 1)).unwrap();
assert_eq!(getsockopt(fd, sockopt::TcpKeepCount).unwrap(), x + 1);
let x = getsockopt(fd, sockopt::TcpKeepInterval).unwrap();
setsockopt(fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap();
assert_eq!(getsockopt(fd, sockopt::TcpKeepInterval).unwrap(), x + 1);
}
}
#[test]
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
fn test_ttl_opts() {
let fd4 = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(fd4, sockopt::Ipv4Ttl, &1)
.expect("setting ipv4ttl on an inet socket should succeed");
let fd6 = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(fd6, sockopt::Ipv6Ttl, &1)
.expect("setting ipv6ttl on an inet6 socket should succeed");
}
#[test]
#[cfg(any(target_os = "ios", target_os = "macos"))]
fn test_dontfrag_opts() {
let fd4 = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(fd4, sockopt::IpDontFrag, &true)
.expect("setting IP_DONTFRAG on an inet stream socket should succeed");
setsockopt(fd4, sockopt::IpDontFrag, &false).expect(
"unsetting IP_DONTFRAG on an inet stream socket should succeed",
);
let fd4d = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(fd4d, sockopt::IpDontFrag, &true).expect(
"setting IP_DONTFRAG on an inet datagram socket should succeed",
);
setsockopt(fd4d, sockopt::IpDontFrag, &false).expect(
"unsetting IP_DONTFRAG on an inet datagram socket should succeed",
);
}
#[test]
#[cfg(any(
target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
))]
// Disable the test under emulation because it fails in Cirrus-CI. Lack
// of QEMU support is suspected.
#[cfg_attr(qemu, ignore)]
fn test_v6dontfrag_opts() {
let fd6 = socket(
AddressFamily::Inet6,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
setsockopt(fd6, sockopt::Ipv6DontFrag, &true).expect(
"setting IPV6_DONTFRAG on an inet6 stream socket should succeed",
);
setsockopt(fd6, sockopt::Ipv6DontFrag, &false).expect(
"unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed",
);
let fd6d = socket(
AddressFamily::Inet6,
SockType::Datagram,
SockFlag::empty(),
None,
)
.unwrap();
setsockopt(fd6d, sockopt::Ipv6DontFrag, &true).expect(
"setting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
);
setsockopt(fd6d, sockopt::Ipv6DontFrag, &false).expect(
"unsetting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
);
}

View file

@ -0,0 +1,29 @@
// The conversion is not useless on all platforms.
#[allow(clippy::useless_conversion)]
#[cfg(target_os = "freebsd")]
#[test]
fn test_chflags() {
use nix::{
sys::stat::{fstat, FileFlag},
unistd::chflags,
};
use std::os::unix::io::AsRawFd;
use tempfile::NamedTempFile;
let f = NamedTempFile::new().unwrap();
let initial = FileFlag::from_bits_truncate(
fstat(f.as_raw_fd()).unwrap().st_flags.into(),
);
// UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted
// in any way, so it's handy for testing.
let commanded = initial ^ FileFlag::UF_OFFLINE;
chflags(f.path(), commanded).unwrap();
let changed = FileFlag::from_bits_truncate(
fstat(f.as_raw_fd()).unwrap().st_flags.into(),
);
assert_eq!(commanded, changed);
}

View file

@ -0,0 +1,20 @@
use nix::sys::sysinfo::*;
#[test]
fn sysinfo_works() {
let info = sysinfo().unwrap();
let (l1, l5, l15) = info.load_average();
assert!(l1 >= 0.0);
assert!(l5 >= 0.0);
assert!(l15 >= 0.0);
info.uptime(); // just test Duration construction
assert!(
info.swap_free() <= info.swap_total(),
"more swap available than installed (free: {}, total: {})",
info.swap_free(),
info.swap_total()
);
}

View file

@ -0,0 +1,136 @@
use std::os::unix::prelude::*;
use tempfile::tempfile;
use nix::errno::Errno;
use nix::fcntl;
use nix::pty::openpty;
use nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags};
use nix::unistd::{close, read, write};
/// Helper function analogous to `std::io::Write::write_all`, but for `RawFd`s
fn write_all(f: RawFd, buf: &[u8]) {
let mut len = 0;
while len < buf.len() {
len += write(f, &buf[len..]).unwrap();
}
}
// Test tcgetattr on a terminal
#[test]
fn test_tcgetattr_pty() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
let pty = openpty(None, None).expect("openpty failed");
termios::tcgetattr(pty.slave).unwrap();
close(pty.master).expect("closing the master failed");
close(pty.slave).expect("closing the slave failed");
}
// Test tcgetattr on something that isn't a terminal
#[test]
fn test_tcgetattr_enotty() {
let file = tempfile().unwrap();
assert_eq!(
termios::tcgetattr(file.as_raw_fd()).err(),
Some(Errno::ENOTTY)
);
}
// Test tcgetattr on an invalid file descriptor
#[test]
fn test_tcgetattr_ebadf() {
assert_eq!(termios::tcgetattr(-1).err(), Some(Errno::EBADF));
}
// Test modifying output flags
#[test]
fn test_output_flags() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
let pty = openpty(None, None).expect("openpty failed");
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.slave).expect("tcgetattr failed");
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};
// Make sure postprocessing '\r' isn't specified by default or this test is useless.
assert!(!termios
.output_flags
.contains(OutputFlags::OPOST | OutputFlags::OCRNL));
// Specify that '\r' characters should be transformed to '\n'
// OPOST is specified to enable post-processing
termios
.output_flags
.insert(OutputFlags::OPOST | OutputFlags::OCRNL);
// Open a pty
let pty = openpty(None, &termios).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
// Write into the master
let string = "foofoofoo\r";
write_all(pty.master, string.as_bytes());
// Read from the slave verifying that the output has been properly transformed
let mut buf = [0u8; 10];
crate::read_exact(pty.slave, &mut buf);
let transformed_string = "foofoofoo\n";
close(pty.master).unwrap();
close(pty.slave).unwrap();
assert_eq!(&buf, transformed_string.as_bytes());
}
// Test modifying local flags
#[test]
fn test_local_flags() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
let pty = openpty(None, None).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.slave).unwrap();
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};
// Make sure echo is specified by default or this test is useless.
assert!(termios.local_flags.contains(LocalFlags::ECHO));
// Disable local echo
termios.local_flags.remove(LocalFlags::ECHO);
// Open a new pty with our modified termios settings
let pty = openpty(None, &termios).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
// Set the master is in nonblocking mode or reading will never return.
let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
let new_flags =
fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap();
// Write into the master
let string = "foofoofoo\r";
write_all(pty.master, string.as_bytes());
// Try to read from the master, which should not have anything as echoing was disabled.
let mut buf = [0u8; 10];
let read = read(pty.master, &mut buf).unwrap_err();
close(pty.master).unwrap();
close(pty.slave).unwrap();
assert_eq!(read, Errno::EAGAIN);
}

View file

@ -0,0 +1,69 @@
use nix::sys::time::{TimeSpec, TimeValLike};
use nix::sys::timerfd::{
ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags,
};
use std::time::Instant;
#[test]
pub fn test_timerfd_oneshot() {
let timer =
TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
let before = Instant::now();
timer
.set(
Expiration::OneShot(TimeSpec::seconds(1)),
TimerSetTimeFlags::empty(),
)
.unwrap();
timer.wait().unwrap();
let millis = before.elapsed().as_millis();
assert!(millis > 900);
}
#[test]
pub fn test_timerfd_interval() {
let timer =
TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
let before = Instant::now();
timer
.set(
Expiration::IntervalDelayed(
TimeSpec::seconds(1),
TimeSpec::seconds(2),
),
TimerSetTimeFlags::empty(),
)
.unwrap();
timer.wait().unwrap();
let start_delay = before.elapsed().as_millis();
assert!(start_delay > 900);
timer.wait().unwrap();
let interval_delay = before.elapsed().as_millis();
assert!(interval_delay > 2900);
}
#[test]
pub fn test_timerfd_unset() {
let timer =
TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
timer
.set(
Expiration::OneShot(TimeSpec::seconds(1)),
TimerSetTimeFlags::empty(),
)
.unwrap();
timer.unset().unwrap();
assert!(timer.get().unwrap().is_none());
}

View file

@ -0,0 +1,270 @@
use nix::sys::uio::*;
use nix::unistd::*;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use std::fs::OpenOptions;
use std::io::IoSlice;
use std::os::unix::io::AsRawFd;
use std::{cmp, iter};
#[cfg(not(target_os = "redox"))]
use std::io::IoSliceMut;
use tempfile::tempdir;
#[cfg(not(target_os = "redox"))]
use tempfile::tempfile;
#[test]
fn test_writev() {
let mut to_write = Vec::with_capacity(16 * 128);
for _ in 0..16 {
let s: String = thread_rng()
.sample_iter(&Alphanumeric)
.map(char::from)
.take(128)
.collect();
let b = s.as_bytes();
to_write.extend(b.iter().cloned());
}
// Allocate and fill iovecs
let mut iovecs = Vec::new();
let mut consumed = 0;
while consumed < to_write.len() {
let left = to_write.len() - consumed;
let slice_len = if left <= 64 {
left
} else {
thread_rng().gen_range(64..cmp::min(256, left))
};
let b = &to_write[consumed..consumed + slice_len];
iovecs.push(IoSlice::new(b));
consumed += slice_len;
}
let pipe_res = pipe();
let (reader, writer) = pipe_res.expect("Couldn't create pipe");
// FileDesc will close its filedesc (reader).
let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
// 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 = 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(writer).expect("closed writer");
close(reader).expect("closed reader");
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_readv() {
let s: String = thread_rng()
.sample_iter(&Alphanumeric)
.map(char::from)
.take(128)
.collect();
let to_write = s.as_bytes().to_vec();
let mut storage = Vec::new();
let mut allocated = 0;
while allocated < to_write.len() {
let left = to_write.len() - allocated;
let vec_len = if left <= 64 {
left
} else {
thread_rng().gen_range(64..cmp::min(256, left))
};
let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
storage.push(v);
allocated += vec_len;
}
let mut iovecs = Vec::with_capacity(storage.len());
for v in &mut storage {
iovecs.push(IoSliceMut::new(&mut v[..]));
}
let (reader, writer) = pipe().expect("couldn't create pipe");
// Blocking io, should write all data.
write(writer, &to_write).expect("write failed");
let read = readv(reader, &mut iovecs[..]).expect("read failed");
// Check whether we've read all data
assert_eq!(to_write.len(), read);
// Cccumulate data from iovecs
let mut read_buf = Vec::with_capacity(to_write.len());
for iovec in &iovecs {
read_buf.extend(iovec.iter().cloned());
}
// Check whether iovecs contain all written data
assert_eq!(read_buf.len(), to_write.len());
// Check equality of written and read data
assert_eq!(&read_buf, &to_write);
close(reader).expect("couldn't close reader");
close(writer).expect("couldn't close writer");
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_pwrite() {
use std::io::Read;
let mut file = tempfile().unwrap();
let buf = [1u8; 8];
assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8));
let mut file_content = Vec::new();
file.read_to_end(&mut file_content).unwrap();
let mut expected = vec![0u8; 8];
expected.extend(vec![1; 8]);
assert_eq!(file_content, expected);
}
#[test]
fn test_pread() {
use std::io::Write;
let tempdir = tempdir().unwrap();
let path = tempdir.path().join("pread_test_file");
let mut file = OpenOptions::new()
.write(true)
.read(true)
.create(true)
.truncate(true)
.open(path)
.unwrap();
let file_content: Vec<u8> = (0..64).collect();
file.write_all(&file_content).unwrap();
let mut buf = [0u8; 16];
assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16));
let expected: Vec<_> = (16..32).collect();
assert_eq!(&buf[..], &expected[..]);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_pwritev() {
use std::io::Read;
let to_write: Vec<u8> = (0..128).collect();
let expected: Vec<u8> = [vec![0; 100], to_write.clone()].concat();
let iovecs = [
IoSlice::new(&to_write[0..17]),
IoSlice::new(&to_write[17..64]),
IoSlice::new(&to_write[64..128]),
];
let tempdir = tempdir().unwrap();
// pwritev them into a temporary file
let path = tempdir.path().join("pwritev_test_file");
let mut file = OpenOptions::new()
.write(true)
.read(true)
.create(true)
.truncate(true)
.open(path)
.unwrap();
let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap();
assert_eq!(written, to_write.len());
// Read the data back and make sure it matches
let mut contents = Vec::new();
file.read_to_end(&mut contents).unwrap();
assert_eq!(contents, expected);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_preadv() {
use std::io::Write;
let to_write: Vec<u8> = (0..200).collect();
let expected: Vec<u8> = (100..200).collect();
let tempdir = tempdir().unwrap();
let path = tempdir.path().join("preadv_test_file");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)
.unwrap();
file.write_all(&to_write).unwrap();
let mut buffers: Vec<Vec<u8>> = vec![vec![0; 24], vec![0; 1], vec![0; 75]];
{
// Borrow the buffers into IoVecs and preadv into them
let mut iovecs: Vec<_> = buffers
.iter_mut()
.map(|buf| IoSliceMut::new(&mut buf[..]))
.collect();
assert_eq!(Ok(100), preadv(file.as_raw_fd(), &mut iovecs, 100));
}
let all = buffers.concat();
assert_eq!(all, expected);
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
// uclibc doesn't implement process_vm_readv
// qemu-user doesn't implement process_vm_readv/writev on most arches
#[cfg_attr(qemu, ignore)]
fn test_process_vm_readv() {
use crate::*;
use nix::sys::signal::*;
use nix::sys::wait::*;
use nix::unistd::ForkResult::*;
require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
// Pre-allocate memory in the child, since allocation isn't safe
// post-fork (~= async-signal-safe)
let mut vector = vec![1u8, 2, 3, 4, 5];
let (r, w) = pipe().unwrap();
match unsafe { fork() }.expect("Error: Fork Failed") {
Parent { child } => {
close(w).unwrap();
// wait for child
read(r, &mut [0u8]).unwrap();
close(r).unwrap();
let ptr = vector.as_ptr() as usize;
let remote_iov = RemoteIoVec { base: ptr, len: 5 };
let mut buf = vec![0u8; 5];
let ret = process_vm_readv(
child,
&mut [IoSliceMut::new(&mut buf)],
&[remote_iov],
);
kill(child, SIGTERM).unwrap();
waitpid(child, None).unwrap();
assert_eq!(Ok(5), ret);
assert_eq!(20u8, buf.iter().sum());
}
Child => {
let _ = close(r);
for i in &mut vector {
*i += 1;
}
let _ = write(w, b"\0");
let _ = close(w);
loop {
pause();
}
}
}
}

View file

@ -0,0 +1,257 @@
use libc::_exit;
use nix::errno::Errno;
use nix::sys::signal::*;
use nix::sys::wait::*;
use nix::unistd::ForkResult::*;
use nix::unistd::*;
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_wait_signal() {
let _m = crate::FORK_MTX.lock();
// Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
pause();
unsafe { _exit(123) }
}
Parent { child } => {
kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Signaled(child, SIGKILL, false))
);
}
}
}
#[test]
#[cfg(any(
target_os = "android",
target_os = "freebsd",
//target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
fn test_waitid_signal() {
let _m = crate::FORK_MTX.lock();
// Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
pause();
unsafe { _exit(123) }
}
Parent { child } => {
kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
assert_eq!(
waitid(Id::Pid(child), WaitPidFlag::WEXITED),
Ok(WaitStatus::Signaled(child, SIGKILL, false)),
);
}
}
}
#[test]
fn test_wait_exit() {
let _m = crate::FORK_MTX.lock();
// Safe: Child only calls `_exit`, which is async-signal-safe.
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => unsafe {
_exit(12);
},
Parent { child } => {
assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12)));
}
}
}
#[cfg(not(target_os = "haiku"))]
#[test]
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
fn test_waitid_exit() {
let _m = crate::FORK_MTX.lock();
// Safe: Child only calls `_exit`, which is async-signal-safe.
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => unsafe {
_exit(12);
},
Parent { child } => {
assert_eq!(
waitid(Id::Pid(child), WaitPidFlag::WEXITED),
Ok(WaitStatus::Exited(child, 12)),
);
}
}
}
#[test]
fn test_waitstatus_from_raw() {
let pid = Pid::from_raw(1);
assert_eq!(
WaitStatus::from_raw(pid, 0x0002),
Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))
);
assert_eq!(
WaitStatus::from_raw(pid, 0x0200),
Ok(WaitStatus::Exited(pid, 2))
);
assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL));
}
#[test]
fn test_waitstatus_pid() {
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.unwrap() {
Child => unsafe { _exit(0) },
Parent { child } => {
let status = waitpid(child, None).unwrap();
assert_eq!(status.pid(), Some(child));
}
}
}
#[test]
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
fn test_waitid_pid() {
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.unwrap() {
Child => unsafe { _exit(0) },
Parent { child } => {
let status = waitid(Id::Pid(child), WaitPidFlag::WEXITED).unwrap();
assert_eq!(status.pid(), Some(child));
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
// FIXME: qemu-user doesn't implement ptrace on most arches
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod ptrace {
use crate::*;
use libc::_exit;
use nix::sys::ptrace::{self, Event, Options};
use nix::sys::signal::*;
use nix::sys::wait::*;
use nix::unistd::ForkResult::*;
use nix::unistd::*;
fn ptrace_child() -> ! {
ptrace::traceme().unwrap();
// As recommended by ptrace(2), raise SIGTRAP to pause the child
// until the parent is ready to continue
raise(SIGTRAP).unwrap();
unsafe { _exit(0) }
}
fn ptrace_wait_parent(child: Pid) {
// Wait for the raised SIGTRAP
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, SIGTRAP))
);
// We want to test a syscall stop and a PTRACE_EVENT stop
ptrace::setoptions(
child,
Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
)
.expect("setoptions failed");
// First, stop on the next system call, which will be exit()
ptrace::syscall(child, None).expect("syscall failed");
assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
// Then get the ptrace event for the process exiting
ptrace::cont(child, None).expect("cont failed");
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceEvent(
child,
SIGTRAP,
Event::PTRACE_EVENT_EXIT as i32
))
);
// Finally get the normal wait() result, now that the process has exited
ptrace::cont(child, None).expect("cont failed");
assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
}
#[cfg(not(target_env = "uclibc"))]
fn ptrace_waitid_parent(child: Pid) {
// Wait for the raised SIGTRAP
//
// Unlike waitpid(), waitid() can distinguish trap events from regular
// stop events, so unlike ptrace_wait_parent(), we get a PtraceEvent here
assert_eq!(
waitid(Id::Pid(child), WaitPidFlag::WEXITED),
Ok(WaitStatus::PtraceEvent(child, SIGTRAP, 0)),
);
// We want to test a syscall stop and a PTRACE_EVENT stop
ptrace::setoptions(
child,
Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
)
.expect("setopts failed");
// First, stop on the next system call, which will be exit()
ptrace::syscall(child, None).expect("syscall failed");
assert_eq!(
waitid(Id::Pid(child), WaitPidFlag::WEXITED),
Ok(WaitStatus::PtraceSyscall(child)),
);
// Then get the ptrace event for the process exiting
ptrace::cont(child, None).expect("cont failed");
assert_eq!(
waitid(Id::Pid(child), WaitPidFlag::WEXITED),
Ok(WaitStatus::PtraceEvent(
child,
SIGTRAP,
Event::PTRACE_EVENT_EXIT as i32
)),
);
// Finally get the normal wait() result, now that the process has exited
ptrace::cont(child, None).expect("cont failed");
assert_eq!(
waitid(Id::Pid(child), WaitPidFlag::WEXITED),
Ok(WaitStatus::Exited(child, 0)),
);
}
#[test]
fn test_wait_ptrace() {
require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => ptrace_child(),
Parent { child } => ptrace_wait_parent(child),
}
}
#[test]
#[cfg(not(target_env = "uclibc"))]
fn test_waitid_ptrace() {
require_capability!("test_waitid_ptrace", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => ptrace_child(),
Parent { child } => ptrace_waitid_parent(child),
}
}
}

123
third-party/vendor/nix/test/test.rs vendored Normal file
View file

@ -0,0 +1,123 @@
#[macro_use]
extern crate cfg_if;
#[cfg_attr(not(any(target_os = "redox", target_os = "haiku")), macro_use)]
extern crate nix;
#[macro_use]
extern crate lazy_static;
mod common;
mod sys;
#[cfg(not(target_os = "redox"))]
mod test_dir;
mod test_fcntl;
#[cfg(any(target_os = "android", target_os = "linux"))]
mod test_kmod;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fushsia",
target_os = "linux",
target_os = "netbsd"
))]
mod test_mq;
#[cfg(not(target_os = "redox"))]
mod test_net;
mod test_nix_path;
#[cfg(target_os = "freebsd")]
mod test_nmount;
mod test_poll;
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku"
)))]
mod test_pty;
mod test_resource;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
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"
))]
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};
use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
use std::os::unix::io::RawFd;
use std::path::PathBuf;
/// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s
fn read_exact(f: RawFd, buf: &mut [u8]) {
let mut len = 0;
while len < buf.len() {
// get_mut would be better than split_at_mut, but it requires nightly
let (_, remaining) = buf.split_at_mut(len);
len += read(f, remaining).unwrap();
}
}
lazy_static! {
/// Any test that changes the process's current working directory must grab
/// the RwLock exclusively. Any process that cares about the current
/// working directory must grab it shared.
pub static ref CWD_LOCK: RwLock<()> = RwLock::new(());
/// Any test that creates child processes must grab this mutex, regardless
/// of what it does with those children.
pub static ref FORK_MTX: Mutex<()> = Mutex::new(());
/// Any test that changes the process's supplementary groups must grab this
/// mutex
pub static ref GROUPS_MTX: Mutex<()> = Mutex::new(());
/// Any tests that loads or unloads kernel modules must grab this mutex
pub static ref KMOD_MTX: Mutex<()> = Mutex::new(());
/// Any test that calls ptsname(3) must grab this mutex.
pub static ref PTSNAME_MTX: Mutex<()> = Mutex::new(());
/// Any test that alters signal handling must grab this mutex.
pub static ref SIGNAL_MTX: Mutex<()> = Mutex::new(());
}
/// RAII object that restores a test's original directory on drop
struct DirRestore<'a> {
d: PathBuf,
_g: RwLockWriteGuard<'a, ()>,
}
impl<'a> DirRestore<'a> {
fn new() -> Self {
let guard = crate::CWD_LOCK.write();
DirRestore {
_g: guard,
d: getcwd().unwrap(),
}
}
}
impl<'a> Drop for DirRestore<'a> {
fn drop(&mut self) {
let r = chdir(&self.d);
if std::thread::panicking() {
r.unwrap();
}
}
}

View file

@ -0,0 +1,9 @@
use std::env;
#[test]
fn clearenv() {
env::set_var("FOO", "BAR");
unsafe { nix::env::clearenv() }.unwrap();
assert_eq!(env::var("FOO").unwrap_err(), env::VarError::NotPresent);
assert_eq!(env::vars().count(), 0);
}

65
third-party/vendor/nix/test/test_dir.rs vendored Normal file
View file

@ -0,0 +1,65 @@
use nix::dir::{Dir, Type};
use nix::fcntl::OFlag;
use nix::sys::stat::Mode;
use std::fs::File;
use tempfile::tempdir;
#[cfg(test)]
fn flags() -> OFlag {
#[cfg(target_os = "illumos")]
let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC;
#[cfg(not(target_os = "illumos"))]
let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC | OFlag::O_DIRECTORY;
f
}
#[test]
#[allow(clippy::unnecessary_sort_by)] // False positive
fn read() {
let tmp = tempdir().unwrap();
File::create(tmp.path().join("foo")).unwrap();
std::os::unix::fs::symlink("foo", tmp.path().join("bar")).unwrap();
let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap();
let mut entries: Vec<_> = dir.iter().map(|e| e.unwrap()).collect();
entries.sort_by(|a, b| a.file_name().cmp(b.file_name()));
let entry_names: Vec<_> = entries
.iter()
.map(|e| e.file_name().to_str().unwrap().to_owned())
.collect();
assert_eq!(&entry_names[..], &[".", "..", "bar", "foo"]);
// Check file types. The system is allowed to return DT_UNKNOWN (aka None here) but if it does
// return a type, ensure it's correct.
assert!(&[Some(Type::Directory), None].contains(&entries[0].file_type())); // .: dir
assert!(&[Some(Type::Directory), None].contains(&entries[1].file_type())); // ..: dir
assert!(&[Some(Type::Symlink), None].contains(&entries[2].file_type())); // bar: symlink
assert!(&[Some(Type::File), None].contains(&entries[3].file_type())); // foo: regular file
}
#[test]
fn rewind() {
let tmp = tempdir().unwrap();
let mut dir = Dir::open(tmp.path(), flags(), Mode::empty()).unwrap();
let entries1: Vec<_> = dir
.iter()
.map(|e| e.unwrap().file_name().to_owned())
.collect();
let entries2: Vec<_> = dir
.iter()
.map(|e| e.unwrap().file_name().to_owned())
.collect();
let entries3: Vec<_> = dir
.into_iter()
.map(|e| e.unwrap().file_name().to_owned())
.collect();
assert_eq!(entries1, entries2);
assert_eq!(entries2, entries3);
}
#[cfg(not(target_os = "haiku"))]
#[test]
fn ebadf() {
assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::EBADF);
}

View file

@ -0,0 +1,565 @@
#[cfg(not(target_os = "redox"))]
use nix::errno::*;
#[cfg(not(target_os = "redox"))]
use nix::fcntl::{open, readlink, OFlag};
#[cfg(not(target_os = "redox"))]
use nix::fcntl::{openat, readlinkat, renameat};
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x32",
target_arch = "powerpc",
target_arch = "s390x"
)
))]
use nix::fcntl::{renameat2, RenameFlags};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::Mode;
#[cfg(not(target_os = "redox"))]
use nix::unistd::{close, read};
#[cfg(not(target_os = "redox"))]
use std::fs::File;
#[cfg(not(target_os = "redox"))]
use std::io::prelude::*;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs;
#[cfg(not(target_os = "redox"))]
use tempfile::{self, NamedTempFile};
#[test]
#[cfg(not(target_os = "redox"))]
// QEMU does not handle openat well enough to satisfy this test
// https://gitlab.com/qemu-project/qemu/-/issues/829
#[cfg_attr(qemu, ignore)]
fn test_openat() {
const CONTENTS: &[u8] = b"abcd";
let mut tmp = NamedTempFile::new().unwrap();
tmp.write_all(CONTENTS).unwrap();
let dirfd =
open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty())
.unwrap();
let fd = openat(
dirfd,
tmp.path().file_name().unwrap(),
OFlag::O_RDONLY,
Mode::empty(),
)
.unwrap();
let mut buf = [0u8; 1024];
assert_eq!(4, read(fd, &mut buf).unwrap());
assert_eq!(CONTENTS, &buf[0..4]);
close(fd).unwrap();
close(dirfd).unwrap();
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_renameat() {
let old_dir = tempfile::tempdir().unwrap();
let old_dirfd =
open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
let old_path = old_dir.path().join("old");
File::create(old_path).unwrap();
let new_dir = tempfile::tempdir().unwrap();
let new_dirfd =
open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
assert_eq!(
renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
Errno::ENOENT
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());
}
#[test]
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x32",
target_arch = "powerpc",
target_arch = "s390x"
)
))]
fn test_renameat2_behaves_like_renameat_with_no_flags() {
let old_dir = tempfile::tempdir().unwrap();
let old_dirfd =
open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
let old_path = old_dir.path().join("old");
File::create(old_path).unwrap();
let new_dir = tempfile::tempdir().unwrap();
let new_dirfd =
open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::empty(),
)
.unwrap();
assert_eq!(
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::empty()
)
.unwrap_err(),
Errno::ENOENT
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());
}
#[test]
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x32",
target_arch = "powerpc",
target_arch = "s390x"
)
))]
fn test_renameat2_exchange() {
let old_dir = tempfile::tempdir().unwrap();
let old_dirfd =
open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
let old_path = old_dir.path().join("old");
{
let mut old_f = File::create(&old_path).unwrap();
old_f.write_all(b"old").unwrap();
}
let new_dir = tempfile::tempdir().unwrap();
let new_dirfd =
open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
let new_path = new_dir.path().join("new");
{
let mut new_f = File::create(&new_path).unwrap();
new_f.write_all(b"new").unwrap();
}
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::RENAME_EXCHANGE,
)
.unwrap();
let mut buf = String::new();
let mut new_f = File::open(&new_path).unwrap();
new_f.read_to_string(&mut buf).unwrap();
assert_eq!(buf, "old");
buf = "".to_string();
let mut old_f = File::open(&old_path).unwrap();
old_f.read_to_string(&mut buf).unwrap();
assert_eq!(buf, "new");
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
}
#[test]
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x32",
target_arch = "powerpc",
target_arch = "s390x"
)
))]
fn test_renameat2_noreplace() {
let old_dir = tempfile::tempdir().unwrap();
let old_dirfd =
open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
let old_path = old_dir.path().join("old");
File::create(old_path).unwrap();
let new_dir = tempfile::tempdir().unwrap();
let new_dirfd =
open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
let new_path = new_dir.path().join("new");
File::create(new_path).unwrap();
assert_eq!(
renameat2(
Some(old_dirfd),
"old",
Some(new_dirfd),
"new",
RenameFlags::RENAME_NOREPLACE
)
.unwrap_err(),
Errno::EEXIST
);
close(old_dirfd).unwrap();
close(new_dirfd).unwrap();
assert!(new_dir.path().join("new").exists());
assert!(old_dir.path().join("old").exists());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_readlink() {
let tempdir = tempfile::tempdir().unwrap();
let src = tempdir.path().join("a");
let dst = tempdir.path().join("b");
println!("a: {:?}, b: {:?}", &src, &dst);
fs::symlink(src.as_path(), dst.as_path()).unwrap();
let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
let expected_dir = src.to_str().unwrap();
assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
assert_eq!(
readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
expected_dir
);
}
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
use libc::loff_t;
use std::io::prelude::*;
use std::io::{IoSlice, SeekFrom};
use std::os::unix::prelude::*;
use nix::fcntl::*;
use nix::unistd::{close, pipe, read, write};
use tempfile::tempfile;
#[cfg(any(target_os = "linux"))]
use tempfile::NamedTempFile;
use crate::*;
/// This test creates a temporary file containing the contents
/// 'foobarbaz' and uses the `copy_file_range` call to transfer
/// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
/// resulting file is read and should contain the contents `bar`.
/// The from_offset should be updated by the call to reflect
/// the 3 bytes read (6).
#[test]
// QEMU does not support copy_file_range. Skip under qemu
#[cfg_attr(qemu, ignore)]
fn test_copy_file_range() {
const CONTENTS: &[u8] = b"foobarbaz";
let mut tmp1 = tempfile().unwrap();
let mut tmp2 = tempfile().unwrap();
tmp1.write_all(CONTENTS).unwrap();
tmp1.flush().unwrap();
let mut from_offset: i64 = 3;
copy_file_range(
tmp1.as_raw_fd(),
Some(&mut from_offset),
tmp2.as_raw_fd(),
None,
3,
)
.unwrap();
let mut res: String = String::new();
tmp2.seek(SeekFrom::Start(0)).unwrap();
tmp2.read_to_string(&mut res).unwrap();
assert_eq!(res, String::from("bar"));
assert_eq!(from_offset, 6);
}
#[test]
fn test_splice() {
const CONTENTS: &[u8] = b"abcdef123456";
let mut tmp = tempfile().unwrap();
tmp.write_all(CONTENTS).unwrap();
let (rd, wr) = pipe().unwrap();
let mut offset: loff_t = 5;
let res = splice(
tmp.as_raw_fd(),
Some(&mut offset),
wr,
None,
2,
SpliceFFlags::empty(),
)
.unwrap();
assert_eq!(2, res);
let mut buf = [0u8; 1024];
assert_eq!(2, read(rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);
close(rd).unwrap();
close(wr).unwrap();
}
#[test]
fn test_tee() {
let (rd1, wr1) = pipe().unwrap();
let (rd2, wr2) = pipe().unwrap();
write(wr1, b"abc").unwrap();
let res = tee(rd1, wr2, 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!(b"ab", &buf[0..2]);
// Check all the bytes are still at rd1.
assert_eq!(3, read(rd1, &mut buf).unwrap());
assert_eq!(b"abc", &buf[0..3]);
close(rd1).unwrap();
close(wr1).unwrap();
close(rd2).unwrap();
close(wr2).unwrap();
}
#[test]
fn test_vmsplice() {
let (rd, wr) = pipe().unwrap();
let buf1 = b"abcdef";
let buf2 = b"defghi";
let iovecs = vec![IoSlice::new(&buf1[0..3]), IoSlice::new(&buf2[0..3])];
let res = vmsplice(wr, &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!(b"abcdef", &buf[0..6]);
close(rd).unwrap();
close(wr).unwrap();
}
#[cfg(any(target_os = "linux"))]
#[test]
fn test_fallocate() {
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();
// Check if we read exactly 100 bytes
let mut buf = [0u8; 200];
assert_eq!(100, read(fd, &mut buf).unwrap());
}
// The tests below are disabled for the listed targets
// due to OFD locks not being available in the kernel/libc
// versions used in the CI environment, probably because
// they run under QEMU.
#[test]
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
#[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
fn test_ofd_write_lock() {
use nix::sys::stat::fstat;
use std::mem;
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap();
if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
// OverlayFS is a union file system. It returns one inode value in
// stat(2), but a different one shows up in /proc/locks. So we must
// skip the test.
skip!("/proc/locks does not work on overlayfs");
}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
let mut flock: libc::flock = unsafe {
mem::zeroed() // required for Linux/mips
};
flock.l_type = libc::F_WRLCK as libc::c_short;
flock.l_whence = libc::SEEK_SET as libc::c_short;
flock.l_start = 0;
flock.l_len = 0;
flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "WRITE".to_string())),
lock_info(inode)
);
flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
assert_eq!(None, lock_info(inode));
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
#[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
fn test_ofd_read_lock() {
use nix::sys::stat::fstat;
use std::mem;
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap();
if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
// OverlayFS is a union file system. It returns one inode value in
// stat(2), but a different one shows up in /proc/locks. So we must
// skip the test.
skip!("/proc/locks does not work on overlayfs");
}
let inode = fstat(fd).expect("fstat failed").st_ino as usize;
let mut flock: libc::flock = unsafe {
mem::zeroed() // required for Linux/mips
};
flock.l_type = libc::F_RDLCK as libc::c_short;
flock.l_whence = libc::SEEK_SET as libc::c_short;
flock.l_start = 0;
flock.l_len = 0;
flock.l_pid = 0;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
assert_eq!(
Some(("OFDLCK".to_string(), "READ".to_string())),
lock_info(inode)
);
flock.l_type = libc::F_UNLCK as libc::c_short;
fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
assert_eq!(None, lock_info(inode));
}
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn lock_info(inode: usize) -> Option<(String, String)> {
use std::{fs::File, io::BufReader};
let file = File::open("/proc/locks").expect("open /proc/locks failed");
let buf = BufReader::new(file);
for line in buf.lines() {
let line = line.unwrap();
let parts: Vec<_> = line.split_whitespace().collect();
let lock_type = parts[1];
let lock_access = parts[3];
let ino_parts: Vec<_> = parts[5].split(':').collect();
let ino: usize = ino_parts[2].parse().unwrap();
if ino == inode {
return Some((lock_type.to_string(), lock_access.to_string()));
}
}
None
}
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "wasi",
target_env = "uclibc",
target_os = "freebsd"
))]
mod test_posix_fadvise {
use nix::errno::Errno;
use nix::fcntl::*;
use nix::unistd::pipe;
use std::os::unix::io::{AsRawFd, RawFd};
use tempfile::NamedTempFile;
#[test]
fn test_success() {
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
.expect("posix_fadvise failed");
}
#[test]
fn test_errno() {
let (rd, _wr) = pipe().unwrap();
let res = posix_fadvise(
rd as RawFd,
0,
100,
PosixFadviseAdvice::POSIX_FADV_WILLNEED,
);
assert_eq!(res, Err(Errno::ESPIPE));
}
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "dragonfly",
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 tempfile::NamedTempFile;
#[test]
fn success() {
const LEN: usize = 100;
let mut tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
let res = posix_fallocate(fd, 0, LEN as libc::off_t);
match res {
Ok(_) => {
let mut data = [1u8; LEN];
assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
assert_eq!(&data[..], &[0u8; LEN][..]);
}
Err(Errno::EINVAL) => {
// POSIX requires posix_fallocate to return EINVAL both for
// invalid arguments (i.e. len < 0) and if the operation is not
// supported by the file system.
// There's no way to tell for sure whether the file system
// supports posix_fallocate, so we must pass the test if it
// returns EINVAL.
}
_ => res.unwrap(),
}
}
#[test]
fn errno() {
let (rd, _wr) = pipe().unwrap();
let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
match err {
Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (),
errno => panic!("unexpected errno {}", errno,),
}
}
}

View file

@ -0,0 +1,7 @@
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

View file

@ -0,0 +1,26 @@
/*
* SPDX-License-Identifier: GPL-2.0+ or MIT
*/
#include <linux/module.h>
#include <linux/kernel.h>
static int number= 1;
static char *who = "World";
module_param(number, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(myint, "Just some number");
module_param(who, charp, 0000);
MODULE_PARM_DESC(who, "Whot to greet");
int init_module(void)
{
printk(KERN_INFO "Hello %s (%d)!\n", who, number);
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye %s (%d)!\n", who, number);
}
MODULE_LICENSE("Dual MIT/GPL");

View file

@ -0,0 +1,188 @@
use crate::*;
use std::fs::copy;
use std::path::PathBuf;
use std::process::Command;
use tempfile::{tempdir, TempDir};
fn compile_kernel_module() -> (PathBuf, String, TempDir) {
let _m = crate::FORK_MTX.lock();
let tmp_dir =
tempdir().expect("unable to create temporary build directory");
copy(
"test/test_kmod/hello_mod/hello.c",
tmp_dir.path().join("hello.c"),
)
.expect("unable to copy hello.c to temporary build directory");
copy(
"test/test_kmod/hello_mod/Makefile",
tmp_dir.path().join("Makefile"),
)
.expect("unable to copy Makefile to temporary build directory");
let status = Command::new("make")
.current_dir(tmp_dir.path())
.status()
.expect("failed to run make");
assert!(status.success());
// Return the relative path of the build kernel module
(tmp_dir.path().join("hello.ko"), "hello".to_owned(), tmp_dir)
}
use nix::errno::Errno;
use nix::kmod::{delete_module, DeleteModuleFlags};
use nix::kmod::{finit_module, init_module, ModuleInitFlags};
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
#[test]
fn test_finit_and_delete_module() {
require_capability!("test_finit_and_delete_module", CAP_SYS_MODULE);
let _m0 = crate::KMOD_MTX.lock();
let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
let f = File::open(kmod_path).expect("unable to open kernel module");
finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty())
.expect("unable to load kernel module");
delete_module(
&CString::new(kmod_name).unwrap(),
DeleteModuleFlags::empty(),
)
.expect("unable to unload kernel module");
}
#[test]
fn test_finit_and_delete_module_with_params() {
require_capability!(
"test_finit_and_delete_module_with_params",
CAP_SYS_MODULE
);
let _m0 = crate::KMOD_MTX.lock();
let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
let f = File::open(kmod_path).expect("unable to open kernel module");
finit_module(
&f,
&CString::new("who=Rust number=2018").unwrap(),
ModuleInitFlags::empty(),
)
.expect("unable to load kernel module");
delete_module(
&CString::new(kmod_name).unwrap(),
DeleteModuleFlags::empty(),
)
.expect("unable to unload kernel module");
}
#[test]
fn test_init_and_delete_module() {
require_capability!("test_init_and_delete_module", CAP_SYS_MODULE);
let _m0 = crate::KMOD_MTX.lock();
let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
let mut f = File::open(kmod_path).expect("unable to open kernel module");
let mut contents: Vec<u8> = Vec::new();
f.read_to_end(&mut contents)
.expect("unable to read kernel module content to buffer");
init_module(&contents, &CString::new("").unwrap())
.expect("unable to load kernel module");
delete_module(
&CString::new(kmod_name).unwrap(),
DeleteModuleFlags::empty(),
)
.expect("unable to unload kernel module");
}
#[test]
fn test_init_and_delete_module_with_params() {
require_capability!(
"test_init_and_delete_module_with_params",
CAP_SYS_MODULE
);
let _m0 = crate::KMOD_MTX.lock();
let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
let mut f = File::open(kmod_path).expect("unable to open kernel module");
let mut contents: Vec<u8> = Vec::new();
f.read_to_end(&mut contents)
.expect("unable to read kernel module content to buffer");
init_module(&contents, &CString::new("who=Nix number=2015").unwrap())
.expect("unable to load kernel module");
delete_module(
&CString::new(kmod_name).unwrap(),
DeleteModuleFlags::empty(),
)
.expect("unable to unload kernel module");
}
#[test]
fn test_finit_module_invalid() {
require_capability!("test_finit_module_invalid", CAP_SYS_MODULE);
let _m0 = crate::KMOD_MTX.lock();
let _m1 = crate::CWD_LOCK.read();
let kmod_path = "/dev/zero";
let f = File::open(kmod_path).expect("unable to open kernel module");
let result =
finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
assert_eq!(result.unwrap_err(), Errno::EINVAL);
}
#[test]
fn test_finit_module_twice_and_delete_module() {
require_capability!(
"test_finit_module_twice_and_delete_module",
CAP_SYS_MODULE
);
let _m0 = crate::KMOD_MTX.lock();
let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
let f = File::open(kmod_path).expect("unable to open kernel module");
finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty())
.expect("unable to load kernel module");
let result =
finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
assert_eq!(result.unwrap_err(), Errno::EEXIST);
delete_module(
&CString::new(kmod_name).unwrap(),
DeleteModuleFlags::empty(),
)
.expect("unable to unload kernel module");
}
#[test]
fn test_delete_module_not_loaded() {
require_capability!("test_delete_module_not_loaded", CAP_SYS_MODULE);
let _m0 = crate::KMOD_MTX.lock();
let _m1 = crate::CWD_LOCK.read();
let result = delete_module(
&CString::new("hello").unwrap(),
DeleteModuleFlags::empty(),
);
assert_eq!(result.unwrap_err(), Errno::ENOENT);
}

View file

@ -0,0 +1,271 @@
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
exit 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();
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)
.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));
// 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));
}
pub fn test_mount_rdonly_disallows_write() {
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()
);
umount(tempdir.path())
.unwrap_or_else(|e| panic!("umount failed: {}", e));
}
pub fn test_mount_noexec_disallows_exec() {
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)),
);
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));
}
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);
}
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: {}. Are unprivileged user namespaces available?",
e).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 {} 1\n", uid).as_bytes()))
.unwrap_or_else(|e| panic!("could not write uid map: {}", e));
}
}
// 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() {}

190
third-party/vendor/nix/test/test_mq.rs vendored Normal file
View file

@ -0,0 +1,190 @@
use cfg_if::cfg_if;
use std::ffi::CString;
use std::str;
use nix::errno::Errno;
use nix::mqueue::{mq_attr_member_t, mq_close, mq_open, mq_receive, mq_send};
use nix::mqueue::{MQ_OFlag, MqAttr};
use nix::sys::stat::Mode;
// Defined as a macro such that the error source is reported as the caller's location.
macro_rules! assert_attr_eq {
($read_attr:ident, $initial_attr:ident) => {
cfg_if! {
if #[cfg(any(target_os = "dragonfly", target_os = "netbsd"))] {
// NetBSD (and others which inherit its implementation) include other flags
// in read_attr, such as those specified by oflag. Just make sure at least
// the correct bits are set.
assert_eq!($read_attr.flags() & $initial_attr.flags(), $initial_attr.flags());
assert_eq!($read_attr.maxmsg(), $initial_attr.maxmsg());
assert_eq!($read_attr.msgsize(), $initial_attr.msgsize());
assert_eq!($read_attr.curmsgs(), $initial_attr.curmsgs());
} else {
assert_eq!($read_attr, $initial_attr);
}
}
}
}
#[test]
fn test_mq_send_and_receive() {
const MSG_SIZE: mq_attr_member_t = 32;
let attr = MqAttr::new(0, 10, MSG_SIZE, 0);
let mq_name = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap();
let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r0 = mq_open(mq_name, oflag0, mode, Some(&attr));
if let Err(Errno::ENOSYS) = r0 {
println!("message queues not supported or module not loaded?");
return;
};
let mqd0 = r0.unwrap();
let msg_to_send = "msg_1";
mq_send(&mqd0, msg_to_send.as_bytes(), 1).unwrap();
let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap();
let mut buf = [0u8; 32];
let mut prio = 0u32;
let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
assert_eq!(prio, 1);
mq_close(mqd1).unwrap();
mq_close(mqd0).unwrap();
assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
}
#[test]
fn test_mq_getattr() {
use nix::mqueue::mq_getattr;
const MSG_SIZE: mq_attr_member_t = 32;
let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
let mqd = r.unwrap();
let read_attr = mq_getattr(&mqd).unwrap();
assert_attr_eq!(read_attr, initial_attr);
mq_close(mqd).unwrap();
}
// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg_attr(
all(qemu, any(target_arch = "mips", target_arch = "mips64")),
ignore
)]
fn test_mq_setattr() {
use nix::mqueue::{mq_getattr, mq_setattr};
const MSG_SIZE: mq_attr_member_t = 32;
let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
let mqd = r.unwrap();
let new_attr = MqAttr::new(0, 20, MSG_SIZE * 2, 100);
let old_attr = mq_setattr(&mqd, &new_attr).unwrap();
assert_attr_eq!(old_attr, initial_attr);
// No changes here because according to the Linux man page only
// O_NONBLOCK can be set (see tests below)
#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
{
let new_attr_get = mq_getattr(&mqd).unwrap();
assert_ne!(new_attr_get, new_attr);
}
let new_attr_non_blocking = MqAttr::new(
MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t,
10,
MSG_SIZE,
0,
);
mq_setattr(&mqd, &new_attr_non_blocking).unwrap();
let new_attr_get = mq_getattr(&mqd).unwrap();
// now the O_NONBLOCK flag has been set
#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
{
assert_ne!(new_attr_get, initial_attr);
}
assert_attr_eq!(new_attr_get, new_attr_non_blocking);
mq_close(mqd).unwrap();
}
// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg_attr(
all(qemu, any(target_arch = "mips", target_arch = "mips64")),
ignore
)]
fn test_mq_set_nonblocking() {
use nix::mqueue::{mq_getattr, mq_remove_nonblock, mq_set_nonblock};
const MSG_SIZE: mq_attr_member_t = 32;
let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap();
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name, oflag, mode, Some(&initial_attr));
if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
let mqd = r.unwrap();
mq_set_nonblock(&mqd).unwrap();
let new_attr = mq_getattr(&mqd);
let o_nonblock_bits = MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t;
assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, o_nonblock_bits);
mq_remove_nonblock(&mqd).unwrap();
let new_attr = mq_getattr(&mqd);
assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, 0);
mq_close(mqd).unwrap();
}
#[test]
fn test_mq_unlink() {
use nix::mqueue::mq_unlink;
const MSG_SIZE: mq_attr_member_t = 32;
let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
let mq_name_not_opened =
&CString::new(b"/mq_unlink_test".as_ref()).unwrap();
let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr));
if let Err(Errno::ENOSYS) = r {
println!("message queues not supported or module not loaded?");
return;
};
let mqd = r.unwrap();
let res_unlink = mq_unlink(mq_name_opened);
assert_eq!(res_unlink, Ok(()));
// NetBSD (and others which inherit its implementation) defer removing the message
// queue name until all references are closed, whereas Linux and others remove the
// message queue name immediately.
#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
{
let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
assert_eq!(res_unlink_not_opened, Err(Errno::ENOENT));
}
mq_close(mqd).unwrap();
let res_unlink_after_close = mq_unlink(mq_name_opened);
assert_eq!(res_unlink_after_close, Err(Errno::ENOENT));
}

19
third-party/vendor/nix/test/test_net.rs vendored Normal file
View file

@ -0,0 +1,19 @@
use nix::net::if_::*;
#[cfg(any(target_os = "android", target_os = "linux"))]
const LOOPBACK: &[u8] = b"lo";
#[cfg(not(any(
target_os = "android",
target_os = "linux",
target_os = "haiku"
)))]
const LOOPBACK: &[u8] = b"lo0";
#[cfg(target_os = "haiku")]
const LOOPBACK: &[u8] = b"loop";
#[test]
fn test_if_nametoindex() {
if_nametoindex(LOOPBACK).expect("assertion failed");
}

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,49 @@
use crate::*;
use nix::{
errno::Errno,
mount::{unmount, MntFlags, Nmount},
};
use std::{ffi::CString, fs::File, path::Path};
use tempfile::tempdir;
#[test]
fn ok() {
require_mount!("nullfs");
let mountpoint = tempdir().unwrap();
let target = tempdir().unwrap();
let _sentry = File::create(target.path().join("sentry")).unwrap();
let fstype = CString::new("fstype").unwrap();
let nullfs = CString::new("nullfs").unwrap();
Nmount::new()
.str_opt(&fstype, &nullfs)
.str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
.str_opt_owned("target", target.path().to_str().unwrap())
.nmount(MntFlags::empty())
.unwrap();
// Now check that the sentry is visible through the mountpoint
let exists = Path::exists(&mountpoint.path().join("sentry"));
// Cleanup the mountpoint before asserting
unmount(mountpoint.path(), MntFlags::empty()).unwrap();
assert!(exists);
}
#[test]
fn bad_fstype() {
let mountpoint = tempdir().unwrap();
let target = tempdir().unwrap();
let _sentry = File::create(target.path().join("sentry")).unwrap();
let e = Nmount::new()
.str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
.str_opt_owned("target", target.path().to_str().unwrap())
.nmount(MntFlags::empty())
.unwrap_err();
assert_eq!(e.error(), Errno::EINVAL);
assert_eq!(e.errmsg(), Some("Invalid fstype"));
}

View file

@ -0,0 +1,84 @@
use nix::{
errno::Errno,
poll::{poll, PollFd, PollFlags},
unistd::{pipe, write},
};
macro_rules! loop_while_eintr {
($poll_expr: expr) => {
loop {
match $poll_expr {
Ok(nfds) => break nfds,
Err(Errno::EINTR) => (),
Err(e) => panic!("{}", e),
}
}
};
}
#[test]
fn test_poll() {
let (r, w) = pipe().unwrap();
let mut fds = [PollFd::new(r, PollFlags::POLLIN)];
// Poll an idle pipe. Should timeout
let nfds = loop_while_eintr!(poll(&mut fds, 100));
assert_eq!(nfds, 0);
assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN));
write(w, b".").unwrap();
// Poll a readable pipe. Should return an event.
let nfds = poll(&mut fds, 100).unwrap();
assert_eq!(nfds, 1);
assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
}
// 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"
))]
#[test]
fn test_ppoll() {
use nix::poll::ppoll;
use nix::sys::signal::SigSet;
use nix::sys::time::{TimeSpec, TimeValLike};
let timeout = TimeSpec::milliseconds(1);
let (r, w) = pipe().unwrap();
let mut fds = [PollFd::new(r, PollFlags::POLLIN)];
// Poll an idle pipe. Should timeout
let sigset = SigSet::empty();
let nfds = loop_while_eintr!(ppoll(&mut fds, Some(timeout), Some(sigset)));
assert_eq!(nfds, 0);
assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN));
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));
}
#[test]
fn test_pollfd_fd() {
use std::os::unix::io::AsRawFd;
let pfd = PollFd::new(0x1234, PollFlags::empty());
assert_eq!(pfd.as_raw_fd(), 0x1234);
}
#[test]
fn test_pollfd_events() {
let mut pfd = PollFd::new(-1, PollFlags::POLLIN);
assert_eq!(pfd.events(), PollFlags::POLLIN);
pfd.set_events(PollFlags::POLLOUT);
assert_eq!(pfd.events(), PollFlags::POLLOUT);
}

313
third-party/vendor/nix/test/test_pty.rs vendored Normal file
View file

@ -0,0 +1,313 @@
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::prelude::*;
use std::path::Path;
use tempfile::tempfile;
use libc::{_exit, STDOUT_FILENO};
use nix::fcntl::{open, OFlag};
use nix::pty::*;
use nix::sys::stat;
use nix::sys::termios::*;
use nix::unistd::{close, pause, write};
/// Regression test for Issue #659
/// This is the correct way to explicitly close a `PtyMaster`
#[test]
fn test_explicit_close() {
let mut f = {
let m = posix_openpt(OFlag::O_RDWR).unwrap();
close(m.into_raw_fd()).unwrap();
tempfile().unwrap()
};
// This should work. But if there's been a double close, then it will
// return EBADF
f.write_all(b"whatever").unwrap();
}
/// Test equivalence of `ptsname` and `ptsname_r`
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_equivalence() {
let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master_fd.as_raw_fd() > 0);
// Get the name of the slave
let slave_name = unsafe { ptsname(&master_fd) }.unwrap();
let slave_name_r = ptsname_r(&master_fd).unwrap();
assert_eq!(slave_name, slave_name_r);
}
/// 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"))]
fn test_ptsname_copy() {
let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master_fd.as_raw_fd() > 0);
// Get the name of the slave
let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap();
let slave_name2 = unsafe { ptsname(&master_fd) }.unwrap();
assert_eq!(slave_name1, slave_name2);
// Also make sure that the string was actually copied and they point to different parts of
// memory.
assert_ne!(slave_name1.as_ptr(), slave_name2.as_ptr());
}
/// Test data copying of `ptsname_r`
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_r_copy() {
// Open a new PTTY master
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master_fd.as_raw_fd() > 0);
// Get the name of the slave
let slave_name1 = ptsname_r(&master_fd).unwrap();
let slave_name2 = ptsname_r(&master_fd).unwrap();
assert_eq!(slave_name1, slave_name2);
assert_ne!(slave_name1.as_ptr(), slave_name2.as_ptr());
}
/// Test that `ptsname` returns different names for different devices
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_unique() {
let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master1_fd.as_raw_fd() > 0);
// Open a second PTTY master
let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap();
assert!(master2_fd.as_raw_fd() > 0);
// Get the name of the slave
let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap();
let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap();
assert_ne!(slave_name1, slave_name2);
}
/// Common setup for testing PTTY pairs
fn open_ptty_pair() -> (PtyMaster, File) {
let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
// Allow a slave to be generated for it
grantpt(&master).expect("grantpt failed");
unlockpt(&master).expect("unlockpt failed");
// Get the name of the slave
let slave_name = unsafe { ptsname(&master) }.expect("ptsname failed");
// Open the slave device
let slave_fd =
open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())
.unwrap();
#[cfg(target_os = "illumos")]
// TODO: rewrite using ioctl!
#[allow(clippy::comparison_chain)]
{
use libc::{ioctl, I_FIND, I_PUSH};
// On illumos systems, as per pts(7D), one must push STREAMS modules
// after opening a device path returned from ptsname().
let ptem = b"ptem\0";
let ldterm = b"ldterm\0";
let r = unsafe { ioctl(slave_fd, I_FIND, ldterm.as_ptr()) };
if r < 0 {
panic!("I_FIND failure");
} else if r == 0 {
if unsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 {
panic!("I_PUSH ptem failure");
}
if unsafe { ioctl(slave_fd, I_PUSH, ldterm.as_ptr()) } < 0 {
panic!("I_PUSH ldterm failure");
}
}
}
let slave = unsafe { File::from_raw_fd(slave_fd) };
(master, slave)
}
/// Test opening a master/slave PTTY pair
///
/// This uses a common `open_ptty_pair` because much of these functions aren't useful by
/// themselves. So for this test we perform the basic act of getting a file handle for a
/// master/slave PTTY pair, then just sanity-check the raw values.
#[test]
fn test_open_ptty_pair() {
let (master, slave) = open_ptty_pair();
assert!(master.as_raw_fd() > 0);
assert!(slave.as_raw_fd() > 0);
}
/// Put the terminal in raw mode.
fn make_raw(fd: RawFd) {
let mut termios = tcgetattr(fd).unwrap();
cfmakeraw(&mut termios);
tcsetattr(fd, SetArg::TCSANOW, &termios).unwrap();
}
/// Test `io::Read` on the PTTY master
#[test]
fn test_read_ptty_pair() {
let (mut master, mut slave) = open_ptty_pair();
make_raw(slave.as_raw_fd());
let mut buf = [0u8; 5];
slave.write_all(b"hello").unwrap();
master.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"hello");
let mut master = &master;
slave.write_all(b"hello").unwrap();
master.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"hello");
}
/// Test `io::Write` on the PTTY master
#[test]
fn test_write_ptty_pair() {
let (mut master, mut slave) = open_ptty_pair();
make_raw(slave.as_raw_fd());
let mut buf = [0u8; 5];
master.write_all(b"adios").unwrap();
slave.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"adios");
let mut master = &master;
master.write_all(b"adios").unwrap();
slave.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"adios");
}
#[test]
fn test_openpty() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
let pty = openpty(None, None).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
// Writing to one should be readable on the other one
let string = "foofoofoo\n";
let mut buf = [0u8; 10];
write(pty.master, string.as_bytes()).unwrap();
crate::read_exact(pty.slave, &mut buf);
assert_eq!(&buf, string.as_bytes());
// Read the echo as well
let echoed_string = "foofoofoo\r\n";
let mut buf = [0u8; 11];
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string.as_bytes());
let string2 = "barbarbarbar\n";
let echoed_string2 = "barbarbarbar\r\n";
let mut buf = [0u8; 14];
write(pty.slave, string2.as_bytes()).unwrap();
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string2.as_bytes());
close(pty.master).unwrap();
close(pty.slave).unwrap();
}
#[test]
fn test_openpty_with_termios() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
let pty = openpty(None, None).unwrap();
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.slave).unwrap();
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};
// Make sure newlines are not transformed so the data is preserved when sent.
termios.output_flags.remove(OutputFlags::ONLCR);
let pty = openpty(None, &termios).unwrap();
// Must be valid file descriptors
assert!(pty.master > 0);
assert!(pty.slave > 0);
// Writing to one should be readable on the other one
let string = "foofoofoo\n";
let mut buf = [0u8; 10];
write(pty.master, string.as_bytes()).unwrap();
crate::read_exact(pty.slave, &mut buf);
assert_eq!(&buf, string.as_bytes());
// read the echo as well
let echoed_string = "foofoofoo\n";
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string.as_bytes());
let string2 = "barbarbarbar\n";
let echoed_string2 = "barbarbarbar\n";
let mut buf = [0u8; 13];
write(pty.slave, string2.as_bytes()).unwrap();
crate::read_exact(pty.master, &mut buf);
assert_eq!(&buf, echoed_string2.as_bytes());
close(pty.master).unwrap();
close(pty.slave).unwrap();
}
#[test]
fn test_forkpty() {
use nix::sys::signal::*;
use nix::sys::wait::wait;
use nix::unistd::ForkResult::*;
// forkpty calls openpty which uses ptname(3) internally.
let _m0 = crate::PTSNAME_MTX.lock();
// forkpty spawns a child process
let _m1 = crate::FORK_MTX.lock();
let string = "naninani\n";
let echoed_string = "naninani\r\n";
let pty = unsafe { forkpty(None, None).unwrap() };
match pty.fork_result {
Child => {
write(STDOUT_FILENO, string.as_bytes()).unwrap();
pause(); // we need the child to stay alive until the parent calls read
unsafe {
_exit(0);
}
}
Parent { child } => {
let mut buf = [0u8; 10];
assert!(child.as_raw() > 0);
crate::read_exact(pty.master, &mut buf);
kill(child, SIGTERM).unwrap();
wait().unwrap(); // keep other tests using generic wait from getting our child
assert_eq!(&buf, echoed_string.as_bytes());
close(pty.master).unwrap();
}
}
}

View file

@ -0,0 +1,20 @@
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
mod t {
use nix::fcntl::OFlag;
use nix::pty::*;
use nix::unistd::close;
use std::os::unix::io::AsRawFd;
/// Regression test for Issue #659
///
/// `PtyMaster` should panic rather than double close the file descriptor
/// This must run in its own test process because it deliberately creates a
/// race condition.
#[test]
#[should_panic(expected = "Closing an invalid file descriptor!")]
fn test_double_close() {
let m = posix_openpt(OFlag::O_RDWR).unwrap();
close(m.as_raw_fd()).unwrap();
drop(m); // should panic here
}
}

View file

@ -0,0 +1,34 @@
#[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);
}

View file

@ -0,0 +1,35 @@
use nix::sched::{sched_getaffinity, sched_setaffinity, CpuSet};
use nix::unistd::Pid;
#[test]
fn test_sched_affinity() {
// If pid is zero, then the mask of the calling process is returned.
let initial_affinity = sched_getaffinity(Pid::from_raw(0)).unwrap();
let mut at_least_one_cpu = false;
let mut last_valid_cpu = 0;
for field in 0..CpuSet::count() {
if initial_affinity.is_set(field).unwrap() {
at_least_one_cpu = true;
last_valid_cpu = field;
}
}
assert!(at_least_one_cpu);
// Now restrict the running CPU
let mut new_affinity = CpuSet::new();
new_affinity.set(last_valid_cpu).unwrap();
sched_setaffinity(Pid::from_raw(0), &new_affinity).unwrap();
// And now re-check the affinity which should be only the one we set.
let updated_affinity = sched_getaffinity(Pid::from_raw(0)).unwrap();
for field in 0..CpuSet::count() {
// Should be set only for the CPU we set previously
assert_eq!(
updated_affinity.is_set(field).unwrap(),
field == last_valid_cpu
)
}
// Finally, reset the initial CPU set
sched_setaffinity(Pid::from_raw(0), &initial_affinity).unwrap();
}

View file

@ -0,0 +1,208 @@
use std::io::prelude::*;
use std::os::unix::prelude::*;
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"))] {
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[test]
fn test_sendfile_linux() {
const CONTENTS: &[u8] = b"abcdef123456";
let mut tmp = tempfile().unwrap();
tmp.write_all(CONTENTS).unwrap();
let (rd, wr) = pipe().unwrap();
let mut offset: off_t = 5;
let res = sendfile(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap();
assert_eq!(2, res);
let mut buf = [0u8; 1024];
assert_eq!(2, read(rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);
close(rd).unwrap();
close(wr).unwrap();
}
#[cfg(target_os = "linux")]
#[test]
fn test_sendfile64_linux() {
const CONTENTS: &[u8] = b"abcdef123456";
let mut tmp = tempfile().unwrap();
tmp.write_all(CONTENTS).unwrap();
let (rd, wr) = pipe().unwrap();
let mut offset: libc::off64_t = 5;
let res = sendfile64(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap();
assert_eq!(2, res);
let mut buf = [0u8; 1024];
assert_eq!(2, read(rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);
close(rd).unwrap();
close(wr).unwrap();
}
#[cfg(target_os = "freebsd")]
#[test]
fn test_sendfile_freebsd() {
// Declare the content
let header_strings =
vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
let body = "Xabcdef123456";
let body_offset = 1;
let trailer_strings = vec!["\n", "Served by Make Believe\n"];
// Write the body to a file
let mut tmp = tempfile().unwrap();
tmp.write_all(body.as_bytes()).unwrap();
// Prepare headers and trailers for sendfile
let headers: Vec<&[u8]> =
header_strings.iter().map(|s| s.as_bytes()).collect();
let trailers: Vec<&[u8]> =
trailer_strings.iter().map(|s| s.as_bytes()).collect();
// Prepare socket pair
let (mut rd, wr) = UnixStream::pair().unwrap();
// Call the test method
let (res, bytes_written) = sendfile(
tmp.as_raw_fd(),
wr.as_raw_fd(),
body_offset as off_t,
None,
Some(headers.as_slice()),
Some(trailers.as_slice()),
SfFlags::empty(),
0,
);
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 as usize, 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 as usize, bytes_read);
assert_eq!(expected_string, read_string);
}
#[cfg(target_os = "dragonfly")]
#[test]
fn test_sendfile_dragonfly() {
// Declare the content
let header_strings =
vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
let body = "Xabcdef123456";
let body_offset = 1;
let trailer_strings = vec!["\n", "Served by Make Believe\n"];
// Write the body to a file
let mut tmp = tempfile().unwrap();
tmp.write_all(body.as_bytes()).unwrap();
// Prepare headers and trailers for sendfile
let headers: Vec<&[u8]> =
header_strings.iter().map(|s| s.as_bytes()).collect();
let trailers: Vec<&[u8]> =
trailer_strings.iter().map(|s| s.as_bytes()).collect();
// Prepare socket pair
let (mut rd, wr) = UnixStream::pair().unwrap();
// Call the test method
let (res, bytes_written) = sendfile(
tmp.as_raw_fd(),
wr.as_raw_fd(),
body_offset as off_t,
None,
Some(headers.as_slice()),
Some(trailers.as_slice()),
);
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 as usize, 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 as usize, bytes_read);
assert_eq!(expected_string, read_string);
}
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[test]
fn test_sendfile_darwin() {
// Declare the content
let header_strings =
vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
let body = "Xabcdef123456";
let body_offset = 1;
let trailer_strings = vec!["\n", "Served by Make Believe\n"];
// Write the body to a file
let mut tmp = tempfile().unwrap();
tmp.write_all(body.as_bytes()).unwrap();
// Prepare headers and trailers for sendfile
let headers: Vec<&[u8]> =
header_strings.iter().map(|s| s.as_bytes()).collect();
let trailers: Vec<&[u8]> =
trailer_strings.iter().map(|s| s.as_bytes()).collect();
// Prepare socket pair
let (mut rd, wr) = UnixStream::pair().unwrap();
// Call the test method
let (res, bytes_written) = sendfile(
tmp.as_raw_fd(),
wr.as_raw_fd(),
body_offset as off_t,
None,
Some(headers.as_slice()),
Some(trailers.as_slice()),
);
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 as usize, 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 as usize, bytes_read);
assert_eq!(expected_string, read_string);
}

421
third-party/vendor/nix/test/test_stat.rs vendored Normal file
View file

@ -0,0 +1,421 @@
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::fs;
use std::fs::File;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs::symlink;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::os::unix::fs::PermissionsExt;
use std::os::unix::prelude::AsRawFd;
#[cfg(not(target_os = "redox"))]
use std::path::Path;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::time::{Duration, UNIX_EPOCH};
use libc::mode_t;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use libc::{S_IFLNK, S_IFMT};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::fcntl;
#[cfg(any(
target_os = "linux",
target_os = "ios",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd"
))]
use nix::sys::stat::lutimes;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::utimensat;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::FchmodatFlags;
use nix::sys::stat::Mode;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::UtimensatFlags;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{self};
use nix::sys::stat::{fchmod, stat};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{fchmodat, mkdirat};
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::{futimens, utimes};
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::sys::stat::FileStat;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
#[cfg(not(target_os = "redox"))]
use nix::unistd::chdir;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::Result;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn assert_stat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
}
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
// (Android's st_blocks is ulonglong which is always non-negative.)
#[cfg_attr(target_os = "android", allow(unused_comparisons))]
#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
fn assert_lstat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
// st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
// (u16 on Android), and that will be a compile error.
// On other platforms they are the same (either both are u16 or u32).
assert_eq!(
(stats.st_mode as usize) & (S_IFMT as usize),
S_IFLNK as usize
); // should be a link
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert!(stats.st_size > 0); // size is > 0 because it points to another file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
// st_blocks depends on whether the machine's file system uses fast
// or slow symlinks, so just make sure it's not negative
assert!(stats.st_blocks >= 0);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_and_fstat() {
use nix::sys::stat::fstat;
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let fstat_result = fstat(file.as_raw_fd());
assert_stat_results(fstat_result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_fstatat() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
File::create(&filename).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty());
let result =
stat::fstatat(dirfd.unwrap(), &filename, fcntl::AtFlags::empty());
assert_stat_results(result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_fstat_lstat() {
use nix::sys::stat::{fstat, lstat};
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("bar.txt");
let linkname = tempdir.path().join("barlink");
File::create(&filename).unwrap();
symlink("bar.txt", &linkname).unwrap();
let link = File::open(&linkname).unwrap();
// should be the same result as calling stat,
// since it's a regular file
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let lstat_result = lstat(&linkname);
assert_lstat_results(lstat_result);
let fstat_result = fstat(link.as_raw_fd());
assert_stat_results(fstat_result);
}
#[test]
fn test_fchmod() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmod(file.as_raw_fd(), mode1).unwrap();
let file_stat1 = stat(&filename).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(file.as_raw_fd(), mode2).unwrap();
let file_stat2 = stat(&filename).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchmodat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
File::create(&fullpath).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink)
.unwrap();
let file_stat1 = stat(&fullpath).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
chdir(tempdir.path()).unwrap();
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap();
let file_stat2 = stat(&fullpath).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
/// Asserts that the atime and mtime in a file's metadata match expected values.
///
/// The atime and mtime are expressed with a resolution of seconds because some file systems
/// (like macOS's HFS+) do not have higher granularity.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn assert_times_eq(
exp_atime_sec: u64,
exp_mtime_sec: u64,
attr: &fs::Metadata,
) {
assert_eq!(
Duration::new(exp_atime_sec, 0),
attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
assert_eq!(
Duration::new(exp_mtime_sec, 0),
attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimes() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
.unwrap();
assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(any(
target_os = "linux",
target_os = "ios",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd"
))]
fn test_lutimes() {
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join("target");
let fullpath = tempdir.path().join("symlink");
drop(File::create(&target).unwrap());
symlink(&target, &fullpath).unwrap();
let exp_target_metadata = fs::symlink_metadata(&target).unwrap();
lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230))
.unwrap();
assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap());
let target_metadata = fs::symlink_metadata(&target).unwrap();
assert_eq!(
exp_target_metadata.accessed().unwrap(),
target_metadata.accessed().unwrap(),
"atime of symlink target was unexpectedly modified"
);
assert_eq!(
exp_target_metadata.modified().unwrap(),
target_metadata.modified().unwrap(),
"mtime of symlink target was unexpectedly modified"
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens() {
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();
futimens(fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat() {
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();
utimensat(
Some(dirfd),
filename,
&TimeSpec::seconds(12345),
&TimeSpec::seconds(678),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap());
chdir(tempdir.path()).unwrap();
utimensat(
None,
filename,
&TimeSpec::seconds(500),
&TimeSpec::seconds(800),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_success_path() {
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
assert!(Path::exists(&tempdir.path().join(filename)));
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_mkdirat_success_mode() {
let expected_bits =
stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
let permissions = fs::metadata(tempdir.path().join(filename))
.unwrap()
.permissions();
let mode = permissions.mode();
assert_eq!(mode as mode_t, expected_bits)
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_fail() {
let tempdir = tempfile::tempdir().unwrap();
let not_dir_filename = "example_not_dir";
let filename = "example_subdir_dir";
let dirfd = fcntl::open(
&tempdir.path().join(not_dir_filename),
fcntl::OFlag::O_CREAT,
stat::Mode::empty(),
)
.unwrap();
let result = mkdirat(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",
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknod() {
use stat::{lstat, mknod, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join(file_name);
mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = lstat(&target).unwrap().st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "haiku",
target_os = "redox"
)))]
fn test_mknodat() {
use fcntl::{AtFlags, OFlag};
use nix::dir::Dir;
use stat::{fstatat, mknodat, SFlag};
let file_name = "test_file";
let tempdir = tempfile::tempdir().unwrap();
let target_dir =
Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
mknodat(
target_dir.as_raw_fd(),
file_name,
SFlag::S_IFREG,
Mode::S_IRWXU,
0,
)
.unwrap();
let mode = fstatat(
target_dir.as_raw_fd(),
file_name,
AtFlags::AT_SYMLINK_NOFOLLOW,
)
.unwrap()
.st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}

View file

@ -0,0 +1,59 @@
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "linux",
target_os = "android",
target_os = "emscripten",
))]
use nix::time::clock_getcpuclockid;
use nix::time::{clock_gettime, ClockId};
#[cfg(not(target_os = "redox"))]
#[test]
pub fn test_clock_getres() {
nix::time::clock_getres(ClockId::CLOCK_REALTIME).expect("assertion failed");
}
#[test]
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",
))]
#[test]
pub fn test_clock_getcpuclockid() {
let clock_id = clock_getcpuclockid(nix::unistd::Pid::this()).unwrap();
clock_gettime(clock_id).unwrap();
}
#[cfg(not(target_os = "redox"))]
#[test]
pub fn test_clock_id_res() {
ClockId::CLOCK_REALTIME.res().unwrap();
}
#[test]
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",
))]
#[test]
pub fn test_clock_id_pid_cpu_clock_id() {
ClockId::pid_cpu_clock_id(nix::unistd::Pid::this())
.map(ClockId::now)
.unwrap()
.unwrap();
}

View file

@ -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");
}
}

1378
third-party/vendor/nix/test/test_unistd.rs vendored Normal file

File diff suppressed because it is too large Load diff