summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/test
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/test')
-rw-r--r--third_party/rust/nix/test/sys/mod.rs36
-rw-r--r--third_party/rust/nix/test/sys/test_aio.rs654
-rw-r--r--third_party/rust/nix/test/sys/test_aio_drop.rs32
-rw-r--r--third_party/rust/nix/test/sys/test_epoll.rs24
-rw-r--r--third_party/rust/nix/test/sys/test_ioctl.rs334
-rw-r--r--third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs111
-rw-r--r--third_party/rust/nix/test/sys/test_pthread.rs15
-rw-r--r--third_party/rust/nix/test/sys/test_ptrace.rs107
-rw-r--r--third_party/rust/nix/test/sys/test_select.rs54
-rw-r--r--third_party/rust/nix/test/sys/test_signal.rs98
-rw-r--r--third_party/rust/nix/test/sys/test_signalfd.rs25
-rw-r--r--third_party/rust/nix/test/sys/test_socket.rs685
-rw-r--r--third_party/rust/nix/test/sys/test_sockopt.rs40
-rw-r--r--third_party/rust/nix/test/sys/test_sysinfo.rs18
-rw-r--r--third_party/rust/nix/test/sys/test_termios.rs136
-rw-r--r--third_party/rust/nix/test/sys/test_uio.rs241
-rw-r--r--third_party/rust/nix/test/sys/test_wait.rs104
-rw-r--r--third_party/rust/nix/test/test.rs80
-rw-r--r--third_party/rust/nix/test/test_dir.rs46
-rw-r--r--third_party/rust/nix/test/test_fcntl.rs143
-rw-r--r--third_party/rust/nix/test/test_kmod/hello_mod/Makefile7
-rw-r--r--third_party/rust/nix/test/test_kmod/hello_mod/hello.c26
-rw-r--r--third_party/rust/nix/test/test_kmod/mod.rs152
-rw-r--r--third_party/rust/nix/test/test_mount.rs238
-rw-r--r--third_party/rust/nix/test/test_mq.rs152
-rw-r--r--third_party/rust/nix/test/test_net.rs12
-rw-r--r--third_party/rust/nix/test/test_nix_path.rs0
-rw-r--r--third_party/rust/nix/test/test_poll.rs68
-rw-r--r--third_party/rust/nix/test/test_pty.rs203
-rw-r--r--third_party/rust/nix/test/test_ptymaster_drop.rs21
-rw-r--r--third_party/rust/nix/test/test_sendfile.rs129
-rw-r--r--third_party/rust/nix/test/test_stat.rs260
-rw-r--r--third_party/rust/nix/test/test_unistd.rs576
33 files changed, 4827 insertions, 0 deletions
diff --git a/third_party/rust/nix/test/sys/mod.rs b/third_party/rust/nix/test/sys/mod.rs
new file mode 100644
index 0000000000..46f2912394
--- /dev/null
+++ b/third_party/rust/nix/test/sys/mod.rs
@@ -0,0 +1,36 @@
+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",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd"))]
+mod test_aio;
+#[cfg(target_os = "linux")]
+mod test_signalfd;
+mod test_socket;
+mod test_sockopt;
+mod test_select;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod test_sysinfo;
+mod test_termios;
+mod test_ioctl;
+mod test_wait;
+mod test_uio;
+
+#[cfg(target_os = "linux")]
+mod test_epoll;
+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;
diff --git a/third_party/rust/nix/test/sys/test_aio.rs b/third_party/rust/nix/test/sys/test_aio.rs
new file mode 100644
index 0000000000..d4b09b0b81
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_aio.rs
@@ -0,0 +1,654 @@
+use bytes::{Bytes, BytesMut};
+use libc::{c_int, c_void};
+use nix::{Error, Result};
+use nix::errno::*;
+use nix::sys::aio::*;
+use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet};
+use nix::sys::time::{TimeSpec, TimeValLike};
+use std::io::{Write, Read, Seek, SeekFrom};
+use std::ops::Deref;
+use std::os::unix::io::AsRawFd;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::{thread, time};
+use tempfile::tempfile;
+
+// Helper that polls an AioCb for completion or error
+fn poll_aio(aiocb: &mut AioCb) -> Result<()> {
+ loop {
+ let err = aiocb.error();
+ if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
+ thread::sleep(time::Duration::from_millis(10));
+ }
+}
+
+#[test]
+fn test_accessors() {
+ let mut rbuf = vec![0; 4];
+ let aiocb = AioCb::from_mut_slice( 1001,
+ 2, //offset
+ &mut rbuf,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99
+ },
+ LioOpcode::LIO_NOP);
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(Some(LioOpcode::LIO_NOP), aiocb.lio_opcode());
+ 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 AioCb.cancel. We aren't trying to test the OS's implementation, only
+// our bindings. So it's sufficient to check that AioCb.cancel returned any
+// AioCancelStat value.
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_cancel() {
+ let wbuf: &[u8] = b"CDEF";
+
+ let f = tempfile().unwrap();
+ let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
+ 0, //offset
+ wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.write().unwrap();
+ let err = aiocb.error();
+ assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+
+ let cancelstat = aiocb.cancel();
+ assert!(cancelstat.is_ok());
+
+ // Wait for aiocb to complete, but don't care whether it succeeded
+ let _ = poll_aio(&mut aiocb);
+ let _ = aiocb.aio_return();
+}
+
+// Tests using aio_cancel_all for all outstanding IOs.
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_aio_cancel_all() {
+ let wbuf: &[u8] = b"CDEF";
+
+ let f = tempfile().unwrap();
+ let mut aiocb = AioCb::from_slice(f.as_raw_fd(),
+ 0, //offset
+ wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.write().unwrap();
+ let err = aiocb.error();
+ assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+
+ let cancelstat = aio_cancel_all(f.as_raw_fd());
+ assert!(cancelstat.is_ok());
+
+ // Wait for aiocb to complete, but don't care whether it succeeded
+ let _ = poll_aio(&mut aiocb);
+ let _ = aiocb.aio_return();
+}
+
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_fsync() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
+ 0, //priority
+ SigevNotify::SigevNone);
+ let err = aiocb.fsync(AioFsyncMode::O_SYNC);
+ assert!(err.is_ok());
+ poll_aio(&mut aiocb).unwrap();
+ aiocb.aio_return().unwrap();
+}
+
+/// `AioCb::fsync` 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 test_fsync_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 aiocb = AioCb::from_fd( f.as_raw_fd(),
+ 0, //priority
+ SigevNotify::SigevNone);
+ let err = aiocb.fsync(mode);
+ assert!(err.is_err());
+}
+
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), 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 = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE);
+
+ let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ wcb.write().unwrap();
+ rcb.read().unwrap();
+ loop {
+ {
+ let cbbuf = [&wcb, &rcb];
+ assert!(aio_suspend(&cbbuf[..], Some(timeout)).is_ok());
+ }
+ if rcb.error() != Err(Error::from(Errno::EINPROGRESS)) &&
+ wcb.error() != Err(Error::from(Errno::EINPROGRESS)) {
+ break
+ }
+ }
+
+ assert!(wcb.aio_return().unwrap() as usize == WBUF.len());
+ assert!(rcb.aio_return().unwrap() as usize == rlen);
+}
+
+// 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 test_read() {
+ 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 mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 2, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.read().unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len());
+ }
+
+ assert!(EXPECT == rbuf.deref().deref());
+}
+
+/// `AioCb::read` 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 test_read_error() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
+ -1, //an invalid offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ assert!(aiocb.read().is_err());
+}
+
+// Tests from_mut_slice
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_read_into_mut_slice() {
+ 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 mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 2, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.read().unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len());
+ }
+
+ assert!(rbuf == EXPECT);
+}
+
+// Tests from_ptr
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_read_into_pointer() {
+ 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();
+ {
+ // Safety: ok because rbuf lives until after poll_aio
+ let mut aiocb = unsafe {
+ AioCb::from_mut_ptr( f.as_raw_fd(),
+ 2, //offset
+ rbuf.as_mut_ptr() as *mut c_void,
+ rbuf.len(),
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP)
+ };
+ aiocb.read().unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len());
+ }
+
+ assert!(rbuf == EXPECT);
+}
+
+// Test reading into an immutable buffer. It should fail
+// FIXME: This test fails to panic on Linux/musl
+#[test]
+#[should_panic(expected = "Can't read into an immutable buffer")]
+#[cfg_attr(target_env = "musl", ignore)]
+fn test_read_immutable_buffer() {
+ let rbuf: &[u8] = b"CDEF";
+ let f = tempfile().unwrap();
+ let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.read().unwrap();
+}
+
+
+// Test a simple aio operation with no completion notification. We must poll
+// for completion. Unlike test_aio_read, this test uses AioCb::from_slice
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_write() {
+ 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 aiocb = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ &wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.write().unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aiocb.aio_return().unwrap() as usize == wbuf.len());
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf == EXPECT);
+}
+
+// Tests `AioCb::from_boxed_slice` with `Bytes`
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_write_bytes() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let wbuf = Box::new(Bytes::from(&b"CDEF"[..]));
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+ let expected_len = wbuf.len();
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(),
+ 2, //offset
+ wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.write().unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aiocb.aio_return().unwrap() as usize == expected_len);
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf == EXPECT);
+}
+
+// Tests `AioCb::from_boxed_mut_slice` with `BytesMut`
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_read_bytes_mut_small() {
+ const INITIAL: &[u8] = b"abcdef";
+ let rbuf = Box::new(BytesMut::from(vec![0; 4]));
+ const EXPECT: &[u8] = b"cdef";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+
+ let mut aiocb = AioCb::from_boxed_mut_slice( f.as_raw_fd(),
+ 2, //offset
+ rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.read().unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
+ let buffer = aiocb.boxed_mut_slice().unwrap();
+ assert_eq!(buffer.borrow(), EXPECT);
+}
+
+// Tests `AioCb::from_ptr`
+#[test]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_write_from_pointer() {
+ 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();
+ // Safety: ok because aiocb outlives poll_aio
+ let mut aiocb = unsafe {
+ AioCb::from_ptr( f.as_raw_fd(),
+ 2, //offset
+ wbuf.as_ptr() as *const c_void,
+ wbuf.len(),
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP)
+ };
+ aiocb.write().unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aiocb.aio_return().unwrap() as usize == wbuf.len());
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf == EXPECT);
+}
+
+/// `AioCb::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 test_write_error() {
+ let wbuf = "CDEF".to_string().into_bytes();
+ let mut aiocb = AioCb::from_slice( 666, // An invalid file descriptor
+ 0, //offset
+ &wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ assert!(aiocb.write().is_err());
+}
+
+lazy_static! {
+ pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
+}
+
+extern fn sigfunc(_: c_int) {
+ SIGNALED.store(true, Ordering::Relaxed);
+}
+
+// Test an aio operation with completion delivered by a signal
+// FIXME: This test is ignored on mips because of failures in qemu in CI
+#[test]
+#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)]
+fn test_write_sigev_signal() {
+ let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ 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 aiocb = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 0 //TODO: validate in sigfunc
+ },
+ LioOpcode::LIO_NOP);
+ aiocb.write().unwrap();
+ while !SIGNALED.load(Ordering::Relaxed) {
+ thread::sleep(time::Duration::from_millis(10));
+ }
+
+ assert!(aiocb.aio_return().unwrap() as usize == WBUF.len());
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf == EXPECT);
+}
+
+// Test LioCb::listio with LIO_WAIT, so all AIO ops should be complete by the
+// time listio returns.
+#[test]
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_liocb_listio_wait() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ const WBUF: &[u8] = b"CDEF";
+ let mut rbuf = vec![0; 4];
+ let rlen = rbuf.len();
+ let mut rbuf2 = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+ let mut f = tempfile().unwrap();
+
+ f.write_all(INITIAL).unwrap();
+
+ {
+ let wcb = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE);
+
+ let rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ let mut liocb = LioCb::with_capacity(2);
+ liocb.aiocbs.push(wcb);
+ liocb.aiocbs.push(rcb);
+ let err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
+ err.expect("lio_listio");
+
+ assert!(liocb.aio_return(0).unwrap() as usize == WBUF.len());
+ assert!(liocb.aio_return(1).unwrap() as usize == rlen);
+ }
+ assert!(rbuf.deref().deref() == b"3456");
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf2).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf2 == EXPECT);
+}
+
+// Test LioCb::listio with LIO_NOWAIT and no SigEvent, so we must use some other
+// mechanism to check for the individual AioCb's completion.
+#[test]
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+fn test_liocb_listio_nowait() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ const WBUF: &[u8] = b"CDEF";
+ let mut rbuf = vec![0; 4];
+ let rlen = rbuf.len();
+ let mut rbuf2 = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+ let mut f = tempfile().unwrap();
+
+ f.write_all(INITIAL).unwrap();
+
+ {
+ let wcb = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE);
+
+ let rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ let mut liocb = LioCb::with_capacity(2);
+ liocb.aiocbs.push(wcb);
+ liocb.aiocbs.push(rcb);
+ let err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
+ err.expect("lio_listio");
+
+ poll_aio(&mut liocb.aiocbs[0]).unwrap();
+ poll_aio(&mut liocb.aiocbs[1]).unwrap();
+ assert!(liocb.aiocbs[0].aio_return().unwrap() as usize == WBUF.len());
+ assert!(liocb.aiocbs[1].aio_return().unwrap() as usize == rlen);
+ }
+ assert!(rbuf.deref().deref() == b"3456");
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf2).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf2 == EXPECT);
+}
+
+// Test LioCb::listio with LIO_NOWAIT and a SigEvent to indicate when all
+// AioCb's are complete.
+// FIXME: This test is ignored on mips/mips64 because of failures in qemu in CI.
+#[test]
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+#[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)]
+fn test_liocb_listio_signal() {
+ let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ const INITIAL: &[u8] = b"abcdef123456";
+ const WBUF: &[u8] = b"CDEF";
+ let mut rbuf = vec![0; 4];
+ let rlen = rbuf.len();
+ let mut rbuf2 = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+ let mut f = tempfile().unwrap();
+ let sa = SigAction::new(SigHandler::Handler(sigfunc),
+ SaFlags::SA_RESETHAND,
+ SigSet::empty());
+ let sigev_notify = SigevNotify::SigevSignal { signal: Signal::SIGUSR2,
+ si_value: 0 };
+
+ f.write_all(INITIAL).unwrap();
+
+ {
+ let wcb = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE);
+
+ let rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ let mut liocb = LioCb::with_capacity(2);
+ liocb.aiocbs.push(wcb);
+ liocb.aiocbs.push(rcb);
+ SIGNALED.store(false, Ordering::Relaxed);
+ unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+ let err = liocb.listio(LioMode::LIO_NOWAIT, sigev_notify);
+ err.expect("lio_listio");
+ while !SIGNALED.load(Ordering::Relaxed) {
+ thread::sleep(time::Duration::from_millis(10));
+ }
+
+ assert!(liocb.aiocbs[0].aio_return().unwrap() as usize == WBUF.len());
+ assert!(liocb.aiocbs[1].aio_return().unwrap() as usize == rlen);
+ }
+ assert!(rbuf.deref().deref() == b"3456");
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf2).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf2 == EXPECT);
+}
+
+// Try to use LioCb::listio to read into an immutable buffer. It should fail
+// FIXME: This test fails to panic on Linux/musl
+#[test]
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+#[should_panic(expected = "Can't read into an immutable buffer")]
+#[cfg_attr(target_env = "musl", ignore)]
+fn test_liocb_listio_read_immutable() {
+ let rbuf: &[u8] = b"abcd";
+ let f = tempfile().unwrap();
+
+
+ let mut liocb = LioCb::from(vec![
+ AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ)
+ ]);
+ let _ = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
+}
diff --git a/third_party/rust/nix/test/sys/test_aio_drop.rs b/third_party/rust/nix/test/sys/test_aio_drop.rs
new file mode 100644
index 0000000000..492da401ef
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_aio_drop.rs
@@ -0,0 +1,32 @@
+extern crate nix;
+extern crate tempfile;
+
+// 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"),
+ 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 = AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aiocb.write().unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_epoll.rs b/third_party/rust/nix/test/sys/test_epoll.rs
new file mode 100644
index 0000000000..e0dc5131de
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_epoll.rs
@@ -0,0 +1,24 @@
+use nix::sys::epoll::{EpollCreateFlags, EpollFlags, EpollOp, EpollEvent};
+use nix::sys::epoll::{epoll_create1, epoll_ctl};
+use nix::Error;
+use nix::errno::Errno;
+
+#[test]
+pub fn test_epoll_errno() {
+ let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
+ let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err(), Error::Sys(Errno::ENOENT));
+
+ let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err(), Error::Sys(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();
+}
diff --git a/third_party/rust/nix/test/sys/test_ioctl.rs b/third_party/rust/nix/test/sys/test_ioctl.rs
new file mode 100644
index 0000000000..0a439b3346
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_ioctl.rs
@@ -0,0 +1,334 @@
+#![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 {
+ #[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), 0x2000_710A);
+ assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF);
+ } else {
+ assert_eq!(request_code_none!(b'q', 10), 0x0000_710A);
+ assert_eq!(request_code_none!(b'a', 255), 0x0000_61FF);
+ }
+ }
+
+ #[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), 0x8001_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A);
+ } else {
+ assert_eq!(request_code_write!(b'z', 10, 1), 0x4001_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 512), 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, (1 as u64) << 32), 0x8000_7A0A);
+ } else {
+ assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A);
+ }
+
+ }
+
+ #[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), 0x4001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A);
+ } else {
+ assert_eq!(request_code_read!(b'z', 10, 1), 0x8001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512), 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, (1 as u64) << 32), 0x4000_7A0A);
+ } else {
+ assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32), 0x8000_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, (1 as u64) << 32), 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, (1 as u64) << 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, (1 as u64) << 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, (1 as u64) << 32), 0xC000_7A0A);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod linux_ioctls {
+ use std::mem;
+ use std::os::unix::io::AsRawFd;
+
+ use tempfile::tempfile;
+ use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios};
+
+ use nix::Error::Sys;
+ use nix::errno::Errno::{ENOTTY, ENOSYS};
+
+ 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(Sys(ENOTTY)));
+ }
+
+ ioctl_read_bad!(tcgets, TCGETS, termios);
+ #[test]
+ fn test_ioctl_read_bad() {
+ let file = tempfile().unwrap();
+ let mut termios = unsafe { mem::uninitialized() };
+ let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) };
+ assert_eq!(res, Err(Sys(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(Sys(ENOTTY)));
+ }
+
+ ioctl_write_ptr_bad!(tcsets, TCSETS, termios);
+ #[test]
+ fn test_ioctl_write_ptr_bad() {
+ let file = tempfile().unwrap();
+ let termios: termios = unsafe { mem::uninitialized() };
+ let res = unsafe { tcsets(file.as_raw_fd(), &termios) };
+ assert_eq!(res, Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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::uninitialized() };
+ let res = unsafe { g_audio(file.as_raw_fd(), &mut data) };
+ assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(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::uninitialized() };
+ let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) };
+ assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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 tempfile::tempfile;
+ use libc::termios;
+
+ use nix::Error::Sys;
+ use nix::errno::Errno::ENOTTY;
+
+ // 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(Sys(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::uninitialized() };
+ let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) };
+ assert_eq!(res, Err(Sys(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::uninitialized() };
+ let res = unsafe { tiocseta(file.as_raw_fd(), &termios) };
+ assert_eq!(res, Err(Sys(ENOTTY)));
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs b/third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs
new file mode 100644
index 0000000000..19ee3facf8
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs
@@ -0,0 +1,111 @@
+// vim: tw=80
+
+// Annoyingly, Cargo is unable to conditionally build an entire test binary. So
+// we must disable the test here rather than in Cargo.toml
+#![cfg(target_os = "freebsd")]
+
+extern crate nix;
+extern crate sysctl;
+extern crate tempfile;
+
+use nix::Error;
+use nix::errno::*;
+use nix::libc::off_t;
+use nix::sys::aio::*;
+use nix::sys::signal::SigevNotify;
+use nix::unistd::{SysconfVar, sysconf};
+use std::os::unix::io::AsRawFd;
+use std::{thread, time};
+use sysctl::CtlValue;
+use tempfile::tempfile;
+
+const BYTES_PER_OP: usize = 512;
+
+/// Attempt to collect final status for all of `liocb`'s operations, freeing
+/// system resources
+fn finish_liocb(liocb: &mut LioCb) {
+ for j in 0..liocb.aiocbs.len() {
+ loop {
+ let e = liocb.error(j);
+ match e {
+ Ok(()) => break,
+ Err(Error::Sys(Errno::EINPROGRESS)) =>
+ thread::sleep(time::Duration::from_millis(10)),
+ Err(x) => panic!("aio_error({:?})", x)
+ }
+ }
+ assert_eq!(liocb.aio_return(j).unwrap(), BYTES_PER_OP as isize);
+ }
+}
+
+// Deliberately exceed system resource limits, causing lio_listio to return EIO.
+// This test must run in its own process since it deliberately uses all AIO
+// resources. ATM it is only enabled on FreeBSD, because I don't know how to
+// check system AIO limits on other operating systems.
+#[test]
+fn test_lio_listio_resubmit() {
+ let mut resubmit_count = 0;
+
+ // Lookup system resource limits
+ let alm = sysconf(SysconfVar::AIO_LISTIO_MAX)
+ .expect("sysconf").unwrap() as usize;
+ let maqpp = if let CtlValue::Int(x) = sysctl::value(
+ "vfs.aio.max_aio_queue_per_proc").unwrap(){
+ x as usize
+ } else {
+ panic!("unknown sysctl");
+ };
+
+ // Find lio_listio sizes that satisfy the AIO_LISTIO_MAX constraint and also
+ // result in a final lio_listio call that can only partially be queued
+ let target_ops = maqpp + alm / 2;
+ let num_listios = (target_ops + alm - 3) / (alm - 2);
+ let ops_per_listio = (target_ops + num_listios - 1) / num_listios;
+ assert!((num_listios - 1) * ops_per_listio < maqpp,
+ "the last lio_listio won't make any progress; fix the algorithm");
+ println!("Using {:?} LioCbs of {:?} operations apiece", num_listios,
+ ops_per_listio);
+
+ let f = tempfile().unwrap();
+ let buffer_set = (0..num_listios).map(|_| {
+ (0..ops_per_listio).map(|_| {
+ vec![0u8; BYTES_PER_OP]
+ }).collect::<Vec<_>>()
+ }).collect::<Vec<_>>();
+
+ let mut liocbs = (0..num_listios).map(|i| {
+ let mut liocb = LioCb::with_capacity(ops_per_listio);
+ for j in 0..ops_per_listio {
+ let offset = (BYTES_PER_OP * (i * ops_per_listio + j)) as off_t;
+ let wcb = AioCb::from_slice( f.as_raw_fd(),
+ offset,
+ &buffer_set[i][j][..],
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE);
+ liocb.aiocbs.push(wcb);
+ }
+ let mut err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
+ while err == Err(Error::Sys(Errno::EIO)) ||
+ err == Err(Error::Sys(Errno::EAGAIN)) ||
+ err == Err(Error::Sys(Errno::EINTR)) {
+ //
+ thread::sleep(time::Duration::from_millis(10));
+ resubmit_count += 1;
+ err = liocb.listio_resubmit(LioMode::LIO_NOWAIT,
+ SigevNotify::SigevNone);
+ }
+ liocb
+ }).collect::<Vec<_>>();
+
+ // Ensure that every AioCb completed
+ for liocb in liocbs.iter_mut() {
+ finish_liocb(liocb);
+ }
+
+ if resubmit_count > 0 {
+ println!("Resubmitted {:?} times, test passed", resubmit_count);
+ } else {
+ println!("Never resubmitted. Test ambiguous");
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_pthread.rs b/third_party/rust/nix/test/sys/test_pthread.rs
new file mode 100644
index 0000000000..8928010087
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_pthread.rs
@@ -0,0 +1,15 @@
+use nix::sys::pthread::*;
+
+#[cfg(target_env = "musl")]
+#[test]
+fn test_pthread_self() {
+ let tid = pthread_self();
+ assert!(tid != ::std::ptr::null_mut());
+}
+
+#[cfg(not(target_env = "musl"))]
+#[test]
+fn test_pthread_self() {
+ let tid = pthread_self();
+ assert!(tid != 0);
+}
diff --git a/third_party/rust/nix/test/sys/test_ptrace.rs b/third_party/rust/nix/test/sys/test_ptrace.rs
new file mode 100644
index 0000000000..24d9b522ee
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_ptrace.rs
@@ -0,0 +1,107 @@
+use nix::Error;
+use nix::errno::Errno;
+use nix::unistd::getpid;
+use nix::sys::ptrace;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use nix::sys::ptrace::Options;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use std::mem;
+
+#[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
+ let err = ptrace::attach(getpid()).unwrap_err();
+ assert!(err == Error::Sys(Errno::EPERM) || err == Error::Sys(Errno::EINVAL) ||
+ err == Error::Sys(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() {
+ let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err();
+ assert!(err != Error::UnsupportedOperation);
+}
+
+// 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() {
+ let err = ptrace::getevent(getpid()).unwrap_err();
+ assert!(err != Error::UnsupportedOperation);
+}
+
+// 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() {
+ if let Err(Error::UnsupportedOperation) = ptrace::getsiginfo(getpid()) {
+ panic!("ptrace_getsiginfo returns Error::UnsupportedOperation!");
+ }
+}
+
+// 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() {
+ let siginfo = unsafe { mem::uninitialized() };
+ if let Err(Error::UnsupportedOperation) = ptrace::setsiginfo(getpid(), &siginfo) {
+ panic!("ptrace_setsiginfo returns Error::UnsupportedOperation!");
+ }
+}
+
+
+#[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::*;
+
+ let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // FIXME: qemu-user doesn't implement ptrace on all architectures
+ // and retunrs 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 == Error::Sys(Errno::ENOSYS) {
+ return;
+ }
+
+ match 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"),
+ }
+ },
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_select.rs b/third_party/rust/nix/test/sys/test_select.rs
new file mode 100644
index 0000000000..cf68700c5e
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_select.rs
@@ -0,0 +1,54 @@
+use nix::sys::select::*;
+use nix::unistd::{pipe, write};
+use nix::sys::signal::SigSet;
+use nix::sys::time::{TimeSpec, TimeValLike};
+
+#[test]
+pub fn test_pselect() {
+ let _mtx = ::SIGNAL_MTX
+ .lock()
+ .expect("Mutex got poisoned by another test");
+
+ 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));
+}
diff --git a/third_party/rust/nix/test/sys/test_signal.rs b/third_party/rust/nix/test/sys/test_signal.rs
new file mode 100644
index 0000000000..f5e4dfd9eb
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_signal.rs
@@ -0,0 +1,98 @@
+use libc;
+use nix::Error;
+use nix::sys::signal::*;
+use nix::unistd::*;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+#[test]
+fn test_kill_none() {
+ kill(getpid(), None).expect("Should be able to send signal to myself.");
+}
+
+#[test]
+fn test_old_sigaction_flags() {
+ 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 = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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_eq!(old_signal_set.contains(SIGNAL), false,
+ "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_eq!(old_signal_set.contains(SIGNAL), true,
+ "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 fn test_sigaction_handler(signal: libc::c_int) {
+ let signal = Signal::from_c_int(signal).unwrap();
+ SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
+}
+
+extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {
+}
+
+#[test]
+fn test_signal() {
+ let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+
+ 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));
+ assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler);
+
+ let action_handler = SigHandler::SigAction(test_sigaction_action);
+ assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Error::UnsupportedOperation);
+
+ // Restore default signal handler
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_signalfd.rs b/third_party/rust/nix/test/sys/test_signalfd.rs
new file mode 100644
index 0000000000..a3b6098841
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_signalfd.rs
@@ -0,0 +1,25 @@
+#[test]
+fn test_signalfd() {
+ use nix::sys::signalfd::SignalFd;
+ use nix::sys::signal::{self, raise, Signal, SigSet};
+
+ // Grab the mutex for altering signals so we don't interfere with other tests.
+ let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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::from_c_int(res.ssi_signo as i32).unwrap();
+ assert_eq!(signo, signal::SIGUSR1);
+}
diff --git a/third_party/rust/nix/test/sys/test_socket.rs b/third_party/rust/nix/test/sys/test_socket.rs
new file mode 100644
index 0000000000..1ed6c1f6ca
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_socket.rs
@@ -0,0 +1,685 @@
+use nix::sys::socket::{InetAddr, UnixAddr, getsockname};
+use std::slice;
+use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6};
+use std::path::Path;
+use std::str::FromStr;
+use std::os::unix::io::RawFd;
+use libc::c_char;
+use tempfile;
+
+#[test]
+pub fn test_inetv4_addr_to_sock_addr() {
+ let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap();
+ let addr = InetAddr::from_std(&actual);
+
+ match addr {
+ InetAddr::V4(addr) => {
+ let ip: u32 = 0x7f00_0001;
+ let port: u16 = 3000;
+ let saddr = addr.sin_addr.s_addr;
+
+ assert_eq!(saddr, ip.to_be());
+ assert_eq!(addr.sin_port, port.to_be());
+ }
+ _ => panic!("nope"),
+ }
+
+ assert_eq!(addr.to_str(), "127.0.0.1:3000");
+
+ let inet = addr.to_std();
+ assert_eq!(actual, inet);
+}
+
+#[test]
+pub fn test_inetv6_addr_to_sock_addr() {
+ let port: u16 = 3000;
+ let flowinfo: u32 = 1;
+ let scope_id: u32 = 2;
+ let ip: Ipv6Addr = "fe80::1".parse().unwrap();
+
+ let actual = SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id));
+ let addr = InetAddr::from_std(&actual);
+
+ match addr {
+ InetAddr::V6(addr) => {
+ assert_eq!(addr.sin6_port, port.to_be());
+ assert_eq!(addr.sin6_flowinfo, flowinfo);
+ assert_eq!(addr.sin6_scope_id, scope_id);
+ }
+ _ => panic!("nope"),
+ }
+
+ assert_eq!(actual, addr.to_std());
+}
+
+#[test]
+pub fn test_path_to_sock_addr() {
+ let path = "/foo/bar";
+ let actual = Path::new(path);
+ let addr = UnixAddr::new(actual).unwrap();
+
+ let expect: &[c_char] = unsafe {
+ slice::from_raw_parts(path.as_bytes().as_ptr() as *const c_char, path.len())
+ };
+ assert_eq!(&addr.0.sun_path[..8], expect);
+
+ assert_eq!(addr.path(), Some(actual));
+}
+
+// Test getting/setting abstract addresses (without unix socket creation)
+#[cfg(target_os = "linux")]
+#[test]
+pub fn test_abstract_uds_addr() {
+ let empty = String::new();
+ let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap();
+ assert_eq!(addr.as_abstract(), Some(empty.as_bytes()));
+
+ let name = String::from("nix\0abstract\0test");
+ let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+ assert_eq!(addr.as_abstract(), Some(name.as_bytes()));
+ assert_eq!(addr.path(), None);
+
+ // Internally, name is null-prefixed (abstract namespace)
+ let internal: &[u8] = unsafe {
+ slice::from_raw_parts(addr.0.sun_path.as_ptr() as *const u8, addr.1)
+ };
+ let mut abstract_name = name.clone();
+ abstract_name.insert(0, '\0');
+ assert_eq!(internal, abstract_name.as_bytes());
+}
+
+#[test]
+pub fn test_getsockname() {
+ use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag};
+ use nix::sys::socket::{bind, SockAddr};
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let sockname = tempdir.path().join("sock");
+ let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)
+ .expect("socket failed");
+ let sockaddr = SockAddr::new_unix(&sockname).unwrap();
+ bind(sock, &sockaddr).expect("bind failed");
+ assert_eq!(sockaddr.to_str(),
+ getsockname(sock).expect("getsockname failed").to_str());
+}
+
+#[test]
+pub fn test_socketpair() {
+ use nix::unistd::{read, write};
+ use nix::sys::socket::{socketpair, AddressFamily, SockType, SockFlag};
+
+ let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
+ .unwrap();
+ write(fd1, b"hello").unwrap();
+ let mut buf = [0;5];
+ read(fd2, &mut buf).unwrap();
+
+ assert_eq!(&buf[..], b"hello");
+}
+
+#[test]
+pub fn test_scm_rights() {
+ use nix::sys::uio::IoVec;
+ use nix::unistd::{pipe, read, write, close};
+ use nix::sys::socket::{socketpair, sendmsg, recvmsg,
+ AddressFamily, SockType, SockFlag,
+ ControlMessage, CmsgSpace, MsgFlags};
+
+ let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
+ .unwrap();
+ let (r, w) = pipe().unwrap();
+ let mut received_r: Option<RawFd> = None;
+
+ {
+ let iov = [IoVec::from_slice(b"hello")];
+ let fds = [r];
+ let cmsg = ControlMessage::ScmRights(&fds);
+ assert_eq!(sendmsg(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5);
+ close(r).unwrap();
+ close(fd1).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let iov = [IoVec::from_mut_slice(&mut buf[..])];
+ let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new();
+ let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
+
+ for cmsg in msg.cmsgs() {
+ if let ControlMessage::ScmRights(fd) = cmsg {
+ assert_eq!(received_r, None);
+ assert_eq!(fd.len(), 1);
+ received_r = Some(fd[0]);
+ } else {
+ panic!("unexpected cmsg");
+ }
+ }
+ assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ close(fd2).unwrap();
+ }
+
+ let received_r = received_r.expect("Did not receive passed fd");
+ // Ensure that the received file descriptor works
+ write(w, b"world").unwrap();
+ let mut buf = [0u8; 5];
+ read(received_r, &mut buf).unwrap();
+ assert_eq!(&buf[..], b"world");
+ close(received_r).unwrap();
+ close(w).unwrap();
+}
+
+/// Tests that passing multiple fds using a single `ControlMessage` works.
+#[test]
+fn test_scm_rights_single_cmsg_multiple_fds() {
+ use std::os::unix::net::UnixDatagram;
+ use std::os::unix::io::{RawFd, AsRawFd};
+ use std::thread;
+ use nix::sys::socket::{CmsgSpace, ControlMessage, MsgFlags, sendmsg, recvmsg};
+ use nix::sys::uio::IoVec;
+ use libc;
+
+ let (send, receive) = UnixDatagram::pair().unwrap();
+ let thread = thread::spawn(move || {
+ let mut buf = [0u8; 8];
+ let iovec = [IoVec::from_mut_slice(&mut buf)];
+ let mut space = CmsgSpace::<[RawFd; 2]>::new();
+ let msg = recvmsg(
+ receive.as_raw_fd(),
+ &iovec,
+ Some(&mut space),
+ MsgFlags::empty()
+ ).unwrap();
+ assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+
+ let mut cmsgs = msg.cmsgs();
+ match cmsgs.next() {
+ Some(ControlMessage::ScmRights(fds)) => {
+ assert_eq!(fds.len(), 2,
+ "unexpected fd count (expected 2 fds, got {})",
+ fds.len());
+ },
+ _ => panic!(),
+ }
+ assert!(cmsgs.next().is_none(), "unexpected control msg");
+
+ assert_eq!(iovec[0].as_slice(), [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ });
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoVec::from_slice(&slice)];
+ let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO]; // pass stdin and stdout
+ let cmsg = [ControlMessage::ScmRights(&fds)];
+ sendmsg(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None).unwrap();
+ thread.join().unwrap();
+}
+
+// Verify `sendmsg` builds a valid `msghdr` when passing an empty
+// `cmsgs` argument. This should result in a msghdr with a nullptr
+// msg_control field and a msg_controllen of 0 when calling into the
+// raw `sendmsg`.
+#[test]
+pub fn test_sendmsg_empty_cmsgs() {
+ use nix::sys::uio::IoVec;
+ use nix::unistd::close;
+ use nix::sys::socket::{socketpair, sendmsg, recvmsg,
+ AddressFamily, SockType, SockFlag,
+ CmsgSpace, MsgFlags};
+
+ let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
+ .unwrap();
+
+ {
+ let iov = [IoVec::from_slice(b"hello")];
+ assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5);
+ close(fd1).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let iov = [IoVec::from_mut_slice(&mut buf[..])];
+ let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new();
+ let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
+
+ for _ in msg.cmsgs() {
+ panic!("unexpected cmsg");
+ }
+ assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ close(fd2).unwrap();
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+fn test_scm_credentials() {
+ use libc;
+ use nix::sys::uio::IoVec;
+ use nix::unistd::{close, getpid, getuid, getgid};
+ use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt,
+ AddressFamily, SockType, SockFlag,
+ ControlMessage, CmsgSpace, MsgFlags};
+ use nix::sys::socket::sockopt::PassCred;
+
+ let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
+ .unwrap();
+ setsockopt(recv, PassCred, &true).unwrap();
+
+ {
+ let iov = [IoVec::from_slice(b"hello")];
+ let cred = libc::ucred {
+ pid: getpid().as_raw(),
+ uid: getuid().as_raw(),
+ gid: getgid().as_raw(),
+ };
+ let cmsg = ControlMessage::ScmCredentials(&cred);
+ assert_eq!(sendmsg(send, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5);
+ close(send).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let iov = [IoVec::from_mut_slice(&mut buf[..])];
+ let mut cmsgspace: CmsgSpace<libc::ucred> = CmsgSpace::new();
+ let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
+ let mut received_cred = None;
+
+ for cmsg in msg.cmsgs() {
+ if let ControlMessage::ScmCredentials(cred) = cmsg {
+ assert!(received_cred.is_none());
+ assert_eq!(cred.pid, getpid().as_raw());
+ assert_eq!(cred.uid, getuid().as_raw());
+ assert_eq!(cred.gid, getgid().as_raw());
+ received_cred = Some(*cred);
+ } else {
+ panic!("unexpected cmsg");
+ }
+ }
+ received_cred.expect("no creds received");
+ assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ close(recv).unwrap();
+ }
+}
+
+/// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single
+/// `sendmsg` call.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86
+// see https://bugs.launchpad.net/qemu/+bug/1781280
+#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
+#[test]
+fn test_scm_credentials_and_rights() {
+ use nix::sys::socket::CmsgSpace;
+ use libc;
+
+ test_impl_scm_credentials_and_rights(CmsgSpace::<(libc::ucred, CmsgSpace<RawFd>)>::new());
+}
+
+/// Ensure that passing a `CmsgSpace` with too much space for the received
+/// messages still works.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86
+// see https://bugs.launchpad.net/qemu/+bug/1781280
+#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
+#[test]
+fn test_too_large_cmsgspace() {
+ use nix::sys::socket::CmsgSpace;
+
+ test_impl_scm_credentials_and_rights(CmsgSpace::<[u8; 1024]>::new());
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_impl_scm_credentials_and_rights<T>(mut space: ::nix::sys::socket::CmsgSpace<T>) {
+ use libc;
+ use nix::sys::uio::IoVec;
+ use nix::unistd::{pipe, read, write, close, getpid, getuid, getgid};
+ use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt,
+ AddressFamily, SockType, SockFlag,
+ ControlMessage, MsgFlags};
+ use nix::sys::socket::sockopt::PassCred;
+
+ let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty())
+ .unwrap();
+ setsockopt(recv, PassCred, &true).unwrap();
+
+ let (r, w) = pipe().unwrap();
+ let mut received_r: Option<RawFd> = None;
+
+ {
+ let iov = [IoVec::from_slice(b"hello")];
+ let cred = libc::ucred {
+ pid: getpid().as_raw(),
+ uid: getuid().as_raw(),
+ gid: getgid().as_raw(),
+ };
+ let fds = [r];
+ let cmsgs = [
+ ControlMessage::ScmCredentials(&cred),
+ ControlMessage::ScmRights(&fds),
+ ];
+ assert_eq!(sendmsg(send, &iov, &cmsgs, MsgFlags::empty(), None).unwrap(), 5);
+ close(r).unwrap();
+ close(send).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let iov = [IoVec::from_mut_slice(&mut buf[..])];
+ let msg = recvmsg(recv, &iov, Some(&mut space), MsgFlags::empty()).unwrap();
+ let mut received_cred = None;
+
+ assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs");
+
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessage::ScmRights(fds) => {
+ assert_eq!(received_r, None, "already received fd");
+ assert_eq!(fds.len(), 1);
+ received_r = Some(fds[0]);
+ }
+ ControlMessage::ScmCredentials(cred) => {
+ assert!(received_cred.is_none());
+ assert_eq!(cred.pid, getpid().as_raw());
+ assert_eq!(cred.uid, getuid().as_raw());
+ assert_eq!(cred.gid, getgid().as_raw());
+ received_cred = Some(*cred);
+ }
+ _ => panic!("unexpected cmsg"),
+ }
+ }
+ received_cred.expect("no creds received");
+ assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ close(recv).unwrap();
+ }
+
+ let received_r = received_r.expect("Did not receive passed fd");
+ // Ensure that the received file descriptor works
+ write(w, b"world").unwrap();
+ let mut buf = [0u8; 5];
+ read(received_r, &mut buf).unwrap();
+ assert_eq!(&buf[..], b"world");
+ close(received_r).unwrap();
+ close(w).unwrap();
+}
+
+// Test creating and using named unix domain sockets
+#[test]
+pub fn test_unixdomain() {
+ use nix::sys::socket::{AddressFamily, SockType, SockFlag};
+ use nix::sys::socket::{bind, socket, connect, listen, accept, SockAddr};
+ use nix::unistd::{read, write, close};
+ use std::thread;
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let sockname = tempdir.path().join("sock");
+ let s1 = socket(AddressFamily::Unix, SockType::Stream,
+ SockFlag::empty(), None).expect("socket failed");
+ let sockaddr = SockAddr::new_unix(&sockname).unwrap();
+ bind(s1, &sockaddr).expect("bind failed");
+ listen(s1, 10).expect("listen failed");
+
+ let thr = thread::spawn(move || {
+ let s2 = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)
+ .expect("socket failed");
+ connect(s2, &sockaddr).expect("connect failed");
+ write(s2, b"hello").expect("write failed");
+ close(s2).unwrap();
+ });
+
+ let s3 = accept(s1).expect("accept failed");
+
+ let mut buf = [0;5];
+ read(s3, &mut buf).unwrap();
+ close(s3).unwrap();
+ close(s1).unwrap();
+ thr.join().unwrap();
+
+ assert_eq!(&buf[..], b"hello");
+}
+
+// Test creating and using named system control sockets
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[test]
+pub fn test_syscontrol() {
+ use nix::Error;
+ use nix::errno::Errno;
+ use nix::sys::socket::{AddressFamily, socket, SockAddr, SockType, SockFlag, SockProtocol};
+
+ let fd = socket(AddressFamily::System, SockType::Datagram,
+ SockFlag::empty(), SockProtocol::KextControl)
+ .expect("socket failed");
+ let _sockaddr = SockAddr::new_sys_control(fd, "com.apple.net.utun_control", 0).expect("resolving sys_control name failed");
+ assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Error::Sys(Errno::ENOENT)));
+
+ // requires root privileges
+ // connect(fd, &sockaddr).expect("connect failed");
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+), ignore)]
+#[test]
+pub fn test_recv_ipv4pktinfo() {
+ use libc;
+ use nix::ifaddrs::{getifaddrs, InterfaceAddress};
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::Ipv4PacketInfo;
+ use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType};
+ use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr};
+ use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
+ use nix::sys::uio::IoVec;
+ use std::io;
+ use std::io::Write;
+ use std::thread;
+
+ fn loopback_v4addr() -> Option<InterfaceAddress> {
+ let addrs = match getifaddrs() {
+ Ok(iter) => iter,
+ Err(e) => {
+ let stdioerr = io::stderr();
+ let mut handle = stdioerr.lock();
+ writeln!(handle, "getifaddrs: {:?}", e).unwrap();
+ return None;
+ },
+ };
+ for ifaddr in addrs {
+ if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) {
+ match ifaddr.address {
+ Some(SockAddr::Inet(InetAddr::V4(..))) => {
+ return Some(ifaddr);
+ }
+ _ => continue,
+ }
+ }
+ }
+ None
+ }
+
+ let lo_ifaddr = loopback_v4addr();
+ let (lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv4 address on interface")),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("receive socket failed");
+ bind(receive, &lo).expect("bind failed");
+ let sa = getsockname(receive).expect("getsockname failed");
+ setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed");
+
+ let thread = thread::spawn(move || {
+ let mut buf = [0u8; 8];
+ let iovec = [IoVec::from_mut_slice(&mut buf)];
+ let mut space = CmsgSpace::<libc::in_pktinfo>::new();
+ let msg = recvmsg(
+ receive,
+ &iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ ).expect("recvmsg failed");
+ assert!(
+ !msg.flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)
+ );
+
+ let mut cmsgs = msg.cmsgs();
+ match cmsgs.next() {
+ Some(ControlMessage::Ipv4PacketInfo(pktinfo)) => {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi_ifindex as libc::c_uint,
+ i,
+ "unexpected ifindex (expected {}, got {})",
+ i,
+ pktinfo.ipi_ifindex
+ );
+ }
+ _ => (),
+ }
+ assert!(cmsgs.next().is_none(), "unexpected additional control msg");
+ assert_eq!(
+ iovec[0].as_slice(),
+ [1u8, 2, 3, 4, 5, 6, 7, 8]
+ );
+ });
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoVec::from_slice(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("send socket failed");
+ sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+
+ thread.join().unwrap();
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+), ignore)]
+#[test]
+pub fn test_recv_ipv6pktinfo() {
+ use libc;
+ use nix::ifaddrs::{getifaddrs, InterfaceAddress};
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
+ use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType};
+ use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr};
+ use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags};
+ use nix::sys::uio::IoVec;
+ use std::io;
+ use std::io::Write;
+ use std::thread;
+
+ fn loopback_v6addr() -> Option<InterfaceAddress> {
+ let addrs = match getifaddrs() {
+ Ok(iter) => iter,
+ Err(e) => {
+ let stdioerr = io::stderr();
+ let mut handle = stdioerr.lock();
+ writeln!(handle, "getifaddrs: {:?}", e).unwrap();
+ return None;
+ },
+ };
+ for ifaddr in addrs {
+ if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) {
+ match ifaddr.address {
+ Some(SockAddr::Inet(InetAddr::V6(..))) => {
+ return Some(ifaddr);
+ }
+ _ => continue,
+ }
+ }
+ }
+ None
+ }
+
+ let lo_ifaddr = loopback_v6addr();
+ let (lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv4 address on interface")),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("receive socket failed");
+ bind(receive, &lo).expect("bind failed");
+ let sa = getsockname(receive).expect("getsockname failed");
+ setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed");
+
+ let thread = thread::spawn(move || {
+ let mut buf = [0u8; 8];
+ let iovec = [IoVec::from_mut_slice(&mut buf)];
+ let mut space = CmsgSpace::<libc::in6_pktinfo>::new();
+ let msg = recvmsg(
+ receive,
+ &iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ ).expect("recvmsg failed");
+ assert!(
+ !msg.flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)
+ );
+
+ let mut cmsgs = msg.cmsgs();
+ match cmsgs.next() {
+ Some(ControlMessage::Ipv6PacketInfo(pktinfo)) => {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi6_ifindex,
+ i,
+ "unexpected ifindex (expected {}, got {})",
+ i,
+ pktinfo.ipi6_ifindex
+ );
+ }
+ _ => (),
+ }
+ assert!(cmsgs.next().is_none(), "unexpected additional control msg");
+ assert_eq!(
+ iovec[0].as_slice(),
+ [1u8, 2, 3, 4, 5, 6, 7, 8]
+ );
+ });
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoVec::from_slice(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ ).expect("send socket failed");
+ sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed");
+
+ thread.join().unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_sockopt.rs b/third_party/rust/nix/test/sys/test_sockopt.rs
new file mode 100644
index 0000000000..efe2c56b55
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_sockopt.rs
@@ -0,0 +1,40 @@
+use rand::{thread_rng, Rng};
+use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, SockType, SockFlag, SockProtocol};
+
+#[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);
+}
+
+// The CI doesn't supported getsockopt and setsockopt on emulated processors.
+// It's beleived 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
+ );
+}
diff --git a/third_party/rust/nix/test/sys/test_sysinfo.rs b/third_party/rust/nix/test/sys/test_sysinfo.rs
new file mode 100644
index 0000000000..73e6586f62
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_sysinfo.rs
@@ -0,0 +1,18 @@
+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());
+}
diff --git a/third_party/rust/nix/test/sys/test_termios.rs b/third_party/rust/nix/test/sys/test_termios.rs
new file mode 100644
index 0000000000..a14b8ce1a2
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_termios.rs
@@ -0,0 +1,136 @@
+use std::os::unix::prelude::*;
+use tempfile::tempfile;
+
+use nix::{Error, fcntl};
+use nix::errno::Errno;
+use nix::pty::openpty;
+use nix::sys::termios::{self, LocalFlags, OutputFlags, Termios, tcgetattr};
+use nix::unistd::{read, write, close};
+
+/// 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 = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ let pty = openpty(None, None).expect("openpty failed");
+ assert!(termios::tcgetattr(pty.master).is_ok());
+ 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(Error::Sys(Errno::ENOTTY)));
+}
+
+// Test tcgetattr on an invalid file descriptor
+#[test]
+fn test_tcgetattr_ebadf() {
+ assert_eq!(termios::tcgetattr(-1).err(),
+ Some(Error::Sys(Errno::EBADF)));
+}
+
+// Test modifying output flags
+#[test]
+fn test_output_flags() {
+ // openpty uses ptname(3) internally
+ let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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.master).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];
+ ::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 = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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.master).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, Error::Sys(Errno::EAGAIN));
+}
+
+#[test]
+fn test_cfmakeraw() {
+ let mut termios = unsafe { Termios::default_uninit() };
+ termios::cfmakeraw(&mut termios);
+}
diff --git a/third_party/rust/nix/test/sys/test_uio.rs b/third_party/rust/nix/test/sys/test_uio.rs
new file mode 100644
index 0000000000..f0cc8897e5
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_uio.rs
@@ -0,0 +1,241 @@
+use nix::sys::uio::*;
+use nix::unistd::*;
+use rand::{thread_rng, Rng};
+use rand::distributions::Alphanumeric;
+use std::{cmp, iter};
+use std::fs::{OpenOptions};
+use std::os::unix::io::AsRawFd;
+
+use tempfile::{tempfile, tempdir};
+
+#[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).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(IoVec::from_slice(b));
+ consumed += slice_len;
+ }
+ let pipe_res = pipe();
+ assert!(pipe_res.is_ok());
+ let (reader, writer) = pipe_res.ok().unwrap();
+ // 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);
+ // Successful write
+ assert!(write_res.is_ok());
+ let written = write_res.ok().unwrap();
+ // Check whether we written all data
+ assert_eq!(to_write.len(), written);
+ let read_res = read(reader, &mut read_buf[..]);
+ // Successful read
+ assert!(read_res.is_ok());
+ let read = read_res.ok().unwrap() as usize;
+ // 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);
+ let close_res = close(writer);
+ assert!(close_res.is_ok());
+ let close_res = close(reader);
+ assert!(close_res.is_ok());
+}
+
+#[test]
+fn test_readv() {
+ let s:String = thread_rng().sample_iter(&Alphanumeric).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(IoVec::from_mut_slice(&mut v[..]));
+ }
+ let pipe_res = pipe();
+ assert!(pipe_res.is_ok());
+ let (reader, writer) = pipe_res.ok().unwrap();
+ // Blocking io, should write all data.
+ let write_res = write(writer, &to_write);
+ // Successful write
+ assert!(write_res.is_ok());
+ let read_res = readv(reader, &mut iovecs[..]);
+ assert!(read_res.is_ok());
+ let read = read_res.ok().unwrap();
+ // 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.as_slice().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);
+ let close_res = close(reader);
+ assert!(close_res.is_ok());
+ let close_res = close(writer);
+ assert!(close_res.is_ok());
+}
+
+#[test]
+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(target_os = "linux")]
+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 = [
+ IoVec::from_slice(&to_write[0..17]),
+ IoVec::from_slice(&to_write[17..64]),
+ IoVec::from_slice(&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(target_os = "linux")]
+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 iovecs: Vec<_> = buffers.iter_mut().map(
+ |buf| IoVec::from_mut_slice(&mut buf[..])).collect();
+ assert_eq!(Ok(100), preadv(file.as_raw_fd(), &iovecs, 100));
+ }
+
+ let all = buffers.concat();
+ assert_eq!(all, expected);
+}
+
+// #[test]
+// #[cfg(target_os = "linux")]
+// // FIXME: qemu-user doesn't implement process_vm_readv/writev on most arches
+// #[cfg_attr(not(any(target_arch = "x86", target_arch = "x86_64")), ignore)]
+// fn test_process_vm_readv() {
+// use nix::unistd::ForkResult::*;
+// use nix::sys::signal::*;
+// use nix::sys::wait::*;
+
+// let _ = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+// // 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 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,
+// &[IoVec::from_mut_slice(&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 { let _ = pause(); }
+// },
+// }
+// }
diff --git a/third_party/rust/nix/test/sys/test_wait.rs b/third_party/rust/nix/test/sys/test_wait.rs
new file mode 100644
index 0000000000..d07d82f0d9
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_wait.rs
@@ -0,0 +1,104 @@
+use nix::Error;
+use nix::unistd::*;
+use nix::unistd::ForkResult::*;
+use nix::sys::signal::*;
+use nix::sys::wait::*;
+use libc::_exit;
+
+#[test]
+fn test_wait_signal() {
+ let _ = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
+ match 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]
+fn test_wait_exit() {
+ let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // Safe: Child only calls `_exit`, which is async-signal-safe.
+ match fork().expect("Error: Fork Failed") {
+ Child => unsafe { _exit(12); },
+ Parent { child } => {
+ assert_eq!(waitpid(child, None), 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(Error::invalid_argument()));
+}
+
+#[test]
+fn test_waitstatus_pid() {
+ let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+ match fork().unwrap() {
+ Child => unsafe { _exit(0) },
+ Parent { child } => {
+ let status = waitpid(child, None).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 nix::sys::ptrace::{self, Options, Event};
+ use nix::sys::signal::*;
+ use nix::sys::wait::*;
+ use nix::unistd::*;
+ use nix::unistd::ForkResult::*;
+ use libc::_exit;
+
+ 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_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
+ assert!(ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT).is_ok());
+
+ // First, stop on the next system call, which will be exit()
+ assert!(ptrace::syscall(child).is_ok());
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
+ // Then get the ptrace event for the process exiting
+ assert!(ptrace::cont(child, None).is_ok());
+ 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
+ assert!(ptrace::cont(child, None).is_ok());
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
+ }
+
+ #[test]
+ fn test_wait_ptrace() {
+ let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+ match fork().expect("Error: Fork Failed") {
+ Child => ptrace_child(),
+ Parent { child } => ptrace_parent(child),
+ }
+ }
+}
diff --git a/third_party/rust/nix/test/test.rs b/third_party/rust/nix/test/test.rs
new file mode 100644
index 0000000000..a91b6348e0
--- /dev/null
+++ b/third_party/rust/nix/test/test.rs
@@ -0,0 +1,80 @@
+extern crate bytes;
+#[macro_use]
+extern crate cfg_if;
+#[macro_use]
+extern crate nix;
+#[macro_use]
+extern crate lazy_static;
+extern crate libc;
+extern crate rand;
+extern crate tempfile;
+
+macro_rules! skip_if_not_root {
+ ($name:expr) => {
+ use nix::unistd::Uid;
+ use std;
+ use std::io::Write;
+
+ if !Uid::current().is_root() {
+ let stderr = std::io::stderr();
+ let mut handle = stderr.lock();
+ writeln!(handle, "{} requires root privileges. Skipping test.", $name).unwrap();
+ return;
+ }
+ };
+}
+
+mod sys;
+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;
+mod test_net;
+mod test_nix_path;
+mod test_poll;
+mod test_pty;
+#[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+mod test_sendfile;
+mod test_stat;
+mod test_unistd;
+
+use std::os::unix::io::RawFd;
+use std::sync::Mutex;
+use nix::unistd::read;
+
+/// 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
+ /// this mutex
+ pub static ref CWD_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 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 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(());
+}
diff --git a/third_party/rust/nix/test/test_dir.rs b/third_party/rust/nix/test/test_dir.rs
new file mode 100644
index 0000000000..c42fbcd18a
--- /dev/null
+++ b/third_party/rust/nix/test/test_dir.rs
@@ -0,0 +1,46 @@
+extern crate nix;
+extern crate tempfile;
+
+use nix::dir::{Dir, Type};
+use nix::fcntl::OFlag;
+use nix::sys::stat::Mode;
+use std::fs::File;
+use self::tempfile::tempdir;
+
+#[test]
+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(), OFlag::O_DIRECTORY | OFlag::O_RDONLY | OFlag::O_CLOEXEC,
+ 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(), OFlag::O_DIRECTORY | OFlag::O_RDONLY | OFlag::O_CLOEXEC,
+ 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();
+ assert_eq!(entries1, entries2);
+}
+
+#[test]
+fn ebadf() {
+ assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::Sys(nix::errno::Errno::EBADF));
+}
diff --git a/third_party/rust/nix/test/test_fcntl.rs b/third_party/rust/nix/test/test_fcntl.rs
new file mode 100644
index 0000000000..bcc523bfcc
--- /dev/null
+++ b/third_party/rust/nix/test/test_fcntl.rs
@@ -0,0 +1,143 @@
+use nix::fcntl::{openat, open, OFlag, readlink, readlinkat};
+use nix::sys::stat::Mode;
+use nix::unistd::{close, read};
+use tempfile::{self, NamedTempFile};
+use std::io::prelude::*;
+use std::os::unix::fs;
+
+#[test]
+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]
+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 mut buf = vec![0; src.to_str().unwrap().len() + 1];
+ assert_eq!(readlink(&dst, &mut buf).unwrap().to_str().unwrap(),
+ src.to_str().unwrap());
+ assert_eq!(readlinkat(dirfd, "b", &mut buf).unwrap().to_str().unwrap(),
+ src.to_str().unwrap());
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod linux_android {
+ use std::io::prelude::*;
+ use std::os::unix::prelude::*;
+
+ use libc::loff_t;
+
+ use nix::fcntl::{SpliceFFlags, FallocateFlags, fallocate, splice, tee, vmsplice};
+ use nix::sys::uio::IoVec;
+ use nix::unistd::{close, pipe, read, write};
+
+ use tempfile::{tempfile, NamedTempFile};
+
+ #[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 mut iovecs = Vec::with_capacity(2);
+ iovecs.push(IoVec::from_slice(&buf1[0..3]));
+ iovecs.push(IoVec::from_slice(&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();
+ }
+
+ #[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());
+ }
+}
diff --git a/third_party/rust/nix/test/test_kmod/hello_mod/Makefile b/third_party/rust/nix/test/test_kmod/hello_mod/Makefile
new file mode 100644
index 0000000000..74c99b77e9
--- /dev/null
+++ b/third_party/rust/nix/test/test_kmod/hello_mod/Makefile
@@ -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
diff --git a/third_party/rust/nix/test/test_kmod/hello_mod/hello.c b/third_party/rust/nix/test/test_kmod/hello_mod/hello.c
new file mode 100644
index 0000000000..1c34987d2a
--- /dev/null
+++ b/third_party/rust/nix/test/test_kmod/hello_mod/hello.c
@@ -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");
diff --git a/third_party/rust/nix/test/test_kmod/mod.rs b/third_party/rust/nix/test/test_kmod/mod.rs
new file mode 100644
index 0000000000..e0cb0df5bd
--- /dev/null
+++ b/third_party/rust/nix/test/test_kmod/mod.rs
@@ -0,0 +1,152 @@
+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 = ::FORK_MTX
+ .lock()
+ .expect("Mutex got poisoned by another test");
+
+ 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 nix::Error;
+use std::ffi::CString;
+use std::fs::File;
+use std::io::Read;
+
+#[test]
+fn test_finit_and_delete_module() {
+ skip_if_not_root!("test_finit_module");
+
+ 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_modul_with_params() {
+ skip_if_not_root!("test_finit_module_with_params");
+
+ 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() {
+ skip_if_not_root!("test_init_module");
+
+ 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(&mut 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() {
+ skip_if_not_root!("test_init_module");
+
+ 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(&mut 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() {
+ skip_if_not_root!("test_finit_module_invalid");
+
+ 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(), Error::Sys(Errno::EINVAL));
+}
+
+#[test]
+fn test_finit_module_twice_and_delete_module() {
+ skip_if_not_root!("test_finit_module_twice_and_delete_module");
+
+ 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(), Error::Sys(Errno::EEXIST));
+
+ delete_module(
+ &CString::new(kmod_name).unwrap(),
+ DeleteModuleFlags::empty(),
+ ).expect("unable to unload kernel module");
+}
+
+#[test]
+fn test_delete_module_not_loaded() {
+ skip_if_not_root!("test_delete_module_not_loaded");
+
+ let result = delete_module(&CString::new("hello").unwrap(), DeleteModuleFlags::empty());
+
+ assert_eq!(result.unwrap_err(), Error::Sys(Errno::ENOENT));
+}
diff --git a/third_party/rust/nix/test/test_mount.rs b/third_party/rust/nix/test/test_mount.rs
new file mode 100644
index 0000000000..d2e08bc428
--- /dev/null
+++ b/third_party/rust/nix/test/test_mount.rs
@@ -0,0 +1,238 @@
+// Impelmentation 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.
+
+extern crate libc;
+extern crate nix;
+extern crate tempfile;
+
+#[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;
+
+ use tempfile;
+
+ static SCRIPT_CONTENTS: &'static [u8] = b"#!/bin/sh
+exit 23";
+
+ const EXPECTED_STATUS: i32 = 23;
+
+ const NONE: Option<&'static [u8]> = None;
+ 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 as i32,
+ 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 as i32,
+ 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_tmpfs_without_flags_allows_rwx,
+ test_mount_rdonly_disallows_write, test_mount_noexec_disallows_exec,
+ test_mount_bind};
+ setup_namespaces();
+
+ run_tests!(test_mount_tmpfs_without_flags_allows_rwx,
+ test_mount_rdonly_disallows_write,
+ test_mount_noexec_disallows_exec,
+ test_mount_bind);
+}
+
+#[cfg(not(target_os = "linux"))]
+fn main() {}
diff --git a/third_party/rust/nix/test/test_mq.rs b/third_party/rust/nix/test/test_mq.rs
new file mode 100644
index 0000000000..41ab358b33
--- /dev/null
+++ b/third_party/rust/nix/test/test_mq.rs
@@ -0,0 +1,152 @@
+use libc::c_long;
+
+use std::ffi::CString;
+use std::str;
+
+use nix::errno::Errno::*;
+use nix::Error::Sys;
+use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive};
+use nix::mqueue::{MqAttr, MQ_OFlag};
+use nix::sys::stat::Mode;
+
+#[test]
+fn test_mq_send_and_receive() {
+ const MSG_SIZE: c_long = 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(Sys(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!(prio == 1);
+
+ mq_close(mqd1).unwrap();
+ mq_close(mqd0).unwrap();
+ assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
+}
+
+
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+fn test_mq_getattr() {
+ use nix::mqueue::mq_getattr;
+ const MSG_SIZE: c_long = 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(Sys(ENOSYS)) = r {
+ println!("message queues not supported or module not loaded?");
+ return;
+ };
+ let mqd = r.unwrap();
+
+ let read_attr = mq_getattr(mqd);
+ assert!(read_attr.unwrap() == initial_attr);
+ mq_close(mqd).unwrap();
+}
+
+// FIXME: Fix failures for mips in QEMU
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
+fn test_mq_setattr() {
+ use nix::mqueue::{mq_getattr, mq_setattr};
+ const MSG_SIZE: c_long = 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(Sys(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);
+ assert!(old_attr.unwrap() == initial_attr);
+
+ let new_attr_get = mq_getattr(mqd);
+ // The following tests make sense. No changes here because according to the Linux man page only
+ // O_NONBLOCK can be set (see tests below)
+ assert!(new_attr_get.unwrap() != new_attr);
+
+ let new_attr_non_blocking = MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as c_long, 10, MSG_SIZE, 0);
+ mq_setattr(mqd, &new_attr_non_blocking).unwrap();
+ let new_attr_get = mq_getattr(mqd);
+
+ // now the O_NONBLOCK flag has been set
+ assert!(new_attr_get.unwrap() != initial_attr);
+ assert!(new_attr_get.unwrap() == new_attr_non_blocking);
+ mq_close(mqd).unwrap();
+}
+
+// FIXME: Fix failures for mips in QEMU
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
+fn test_mq_set_nonblocking() {
+ use nix::mqueue::{mq_getattr, mq_set_nonblock, mq_remove_nonblock};
+ const MSG_SIZE: c_long = 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(Sys(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);
+ assert!(new_attr.unwrap().flags() == MQ_OFlag::O_NONBLOCK.bits() as c_long);
+ mq_remove_nonblock(mqd).unwrap();
+ let new_attr = mq_getattr(mqd);
+ assert!(new_attr.unwrap().flags() == 0);
+ mq_close(mqd).unwrap();
+}
+
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+fn test_mq_unlink() {
+ use nix::mqueue::mq_unlink;
+ const MSG_SIZE: c_long = 32;
+ let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+ let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap();
+ 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(Sys(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!(res_unlink == Ok(()) );
+
+ let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
+ assert!(res_unlink_not_opened == Err(Sys(ENOENT)) );
+
+ mq_close(mqd).unwrap();
+ let res_unlink_after_close = mq_unlink(mq_name_opened);
+ assert!(res_unlink_after_close == Err(Sys(ENOENT)) );
+}
diff --git a/third_party/rust/nix/test/test_net.rs b/third_party/rust/nix/test/test_net.rs
new file mode 100644
index 0000000000..b8940e718b
--- /dev/null
+++ b/third_party/rust/nix/test/test_net.rs
@@ -0,0 +1,12 @@
+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")))]
+const LOOPBACK: &[u8] = b"lo0";
+
+#[test]
+fn test_if_nametoindex() {
+ assert!(if_nametoindex(&LOOPBACK[..]).is_ok());
+}
diff --git a/third_party/rust/nix/test/test_nix_path.rs b/third_party/rust/nix/test/test_nix_path.rs
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/rust/nix/test/test_nix_path.rs
diff --git a/third_party/rust/nix/test/test_poll.rs b/third_party/rust/nix/test/test_poll.rs
new file mode 100644
index 0000000000..23df151790
--- /dev/null
+++ b/third_party/rust/nix/test/test_poll.rs
@@ -0,0 +1,68 @@
+use nix::poll::{EventFlags, poll, PollFd};
+use nix::unistd::{write, pipe, close};
+
+#[test]
+fn test_poll() {
+ let (r, w) = pipe().unwrap();
+ let mut fds = [PollFd::new(r, EventFlags::POLLIN)];
+
+ // Poll an idle pipe. Should timeout
+ let nfds = poll(&mut fds, 100).unwrap();
+ assert_eq!(nfds, 0);
+ assert!(!fds[0].revents().unwrap().contains(EventFlags::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(EventFlags::POLLIN));
+}
+
+#[test]
+fn test_poll_debug() {
+ assert_eq!(format!("{:?}", PollFd::new(0, EventFlags::empty())),
+ "PollFd { fd: 0, events: (empty), revents: (empty) }");
+ assert_eq!(format!("{:?}", PollFd::new(1, EventFlags::POLLIN)),
+ "PollFd { fd: 1, events: POLLIN, revents: (empty) }");
+
+ // Testing revents requires doing some I/O
+ let (r, w) = pipe().unwrap();
+ let mut fds = [PollFd::new(r, EventFlags::POLLIN)];
+ write(w, b" ").unwrap();
+ close(w).unwrap();
+ poll(&mut fds, -1).unwrap();
+ assert_eq!(format!("{:?}", fds[0]),
+ format!("PollFd {{ fd: {}, events: POLLIN, revents: POLLIN | POLLHUP }}", r));
+ close(r).unwrap();
+}
+
+// ppoll(2) is the same as poll except for how it handles timeouts and signals.
+// Repeating the test for poll(2) should be sufficient to check that our
+// bindings are correct.
+#[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"))]
+#[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, EventFlags::POLLIN)];
+
+ // Poll an idle pipe. Should timeout
+ let nfds = ppoll(&mut fds, timeout, SigSet::empty()).unwrap();
+ assert_eq!(nfds, 0);
+ assert!(!fds[0].revents().unwrap().contains(EventFlags::POLLIN));
+
+ write(w, b".").unwrap();
+
+ // Poll a readable pipe. Should return an event.
+ let nfds = ppoll(&mut fds, timeout, SigSet::empty()).unwrap();
+ assert_eq!(nfds, 1);
+ assert!(fds[0].revents().unwrap().contains(EventFlags::POLLIN));
+}
diff --git a/third_party/rust/nix/test/test_pty.rs b/third_party/rust/nix/test/test_pty.rs
new file mode 100644
index 0000000000..4f428bed9a
--- /dev/null
+++ b/third_party/rust/nix/test/test_pty.rs
@@ -0,0 +1,203 @@
+use std::io::Write;
+use std::path::Path;
+use std::os::unix::prelude::*;
+use tempfile::tempfile;
+
+use nix::fcntl::{OFlag, open};
+use nix::pty::*;
+use nix::sys::stat;
+use nix::sys::termios::*;
+use nix::unistd::{write, close};
+
+/// 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 = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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 = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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!(slave_name1 == slave_name2);
+ // Also make sure that the string was actually copied and they point to different parts of
+ // memory.
+ assert!(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!(slave_name1 == slave_name2);
+ assert!(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 = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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!(slave_name1 != slave_name2);
+}
+
+/// Test opening a master/slave PTTY pair
+///
+/// This is a single larger test 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 connect master/slave PTTY
+/// pair.
+#[test]
+fn test_open_ptty_pair() {
+ let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // Open a new PTTY master
+ let master_fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
+ assert!(master_fd.as_raw_fd() > 0);
+
+ // Allow a slave to be generated for it
+ grantpt(&master_fd).expect("grantpt failed");
+ unlockpt(&master_fd).expect("unlockpt failed");
+
+ // Get the name of the slave
+ let slave_name = unsafe { ptsname(&master_fd) }.expect("ptsname failed");
+
+ // Open the slave device
+ let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap();
+ assert!(slave_fd > 0);
+}
+
+#[test]
+fn test_openpty() {
+ // openpty uses ptname(3) internally
+ let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ 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();
+ ::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];
+ ::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();
+ ::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 = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // 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.master).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();
+ ::read_exact(pty.slave, &mut buf);
+
+ assert_eq!(&buf, string.as_bytes());
+
+ // read the echo as well
+ let echoed_string = "foofoofoo\n";
+ ::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();
+ ::read_exact(pty.master, &mut buf);
+
+ assert_eq!(&buf, echoed_string2.as_bytes());
+
+ close(pty.master).unwrap();
+ close(pty.slave).unwrap();
+}
diff --git a/third_party/rust/nix/test/test_ptymaster_drop.rs b/third_party/rust/nix/test/test_ptymaster_drop.rs
new file mode 100644
index 0000000000..9b59d66435
--- /dev/null
+++ b/third_party/rust/nix/test/test_ptymaster_drop.rs
@@ -0,0 +1,21 @@
+extern crate nix;
+
+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!")]
+// In Travis on i686-unknown-linux-musl, this test gets SIGABRT. I don't know
+// why. It doesn't happen on any other target, and it doesn't happen on my PC.
+#[cfg_attr(all(target_env = "musl", target_arch = "x86"), ignore)]
+fn test_double_close() {
+ let m = posix_openpt(OFlag::O_RDWR).unwrap();
+ close(m.as_raw_fd()).unwrap();
+ drop(m); // should panic here
+}
diff --git a/third_party/rust/nix/test/test_sendfile.rs b/third_party/rust/nix/test/test_sendfile.rs
new file mode 100644
index 0000000000..3bc7932f4c
--- /dev/null
+++ b/third_party/rust/nix/test/test_sendfile.rs
@@ -0,0 +1,129 @@
+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 = "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 = "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(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);
+}
diff --git a/third_party/rust/nix/test/test_stat.rs b/third_party/rust/nix/test/test_stat.rs
new file mode 100644
index 0000000000..fae8df821c
--- /dev/null
+++ b/third_party/rust/nix/test/test_stat.rs
@@ -0,0 +1,260 @@
+use std::fs::{self, File};
+use std::os::unix::fs::symlink;
+use std::os::unix::prelude::AsRawFd;
+use std::time::{Duration, UNIX_EPOCH};
+
+#[cfg(not(any(target_os = "netbsd")))]
+use libc::{S_IFMT, S_IFLNK};
+
+use nix::fcntl;
+use nix::sys::stat::{self, fchmod, fchmodat, futimens, stat, utimes, utimensat};
+#[cfg(any(target_os = "linux",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"))]
+use nix::sys::stat::lutimes;
+use nix::sys::stat::{Mode, FchmodatFlags, UtimensatFlags};
+
+#[cfg(not(any(target_os = "netbsd")))]
+use nix::sys::stat::FileStat;
+
+use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
+use nix::unistd::chdir;
+
+#[cfg(not(any(target_os = "netbsd")))]
+use nix::Result;
+use tempfile;
+
+#[allow(unused_comparisons)]
+// uid and gid are signed on Windows, but not on other platforms. This function
+// allows warning free compiles on all platforms, and can be removed when
+// expression-level #[allow] is available.
+#[cfg(not(any(target_os = "netbsd")))]
+fn valid_uid_gid(stat: FileStat) -> bool {
+ // uid could be 0 for the `root` user. This quite possible when
+ // the tests are being run on a rooted Android device.
+ stat.st_uid >= 0 && stat.st_gid >= 0
+}
+
+#[cfg(not(any(target_os = "netbsd")))]
+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!(stats.st_nlink == 1); // there links created, must be 1
+ assert!(valid_uid_gid(stats)); // must be positive integers
+ assert!(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")))]
+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!((stats.st_mode as usize) & (S_IFMT as usize) == S_IFLNK as usize); // should be a link
+ assert!(stats.st_nlink == 1); // there links created, must be 1
+ assert!(valid_uid_gid(stats)); // must be positive integers
+ 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
+ // (Android's st_blocks is ulonglong which is always non-negative.)
+ assert!(stats.st_blocks >= 0);
+}
+
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+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")))]
+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")))]
+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 & 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 & 0o7777, mode2.bits());
+}
+
+#[test]
+fn test_fchmodat() {
+ 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 & 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 & 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.
+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]
+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 = "haiku",
+ 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]
+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]
+fn test_utimensat() {
+ 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());
+}
diff --git a/third_party/rust/nix/test/test_unistd.rs b/third_party/rust/nix/test/test_unistd.rs
new file mode 100644
index 0000000000..3fb5053512
--- /dev/null
+++ b/third_party/rust/nix/test/test_unistd.rs
@@ -0,0 +1,576 @@
+use nix::fcntl::{fcntl, FcntlArg, FdFlag, open, OFlag, readlink};
+use nix::unistd::*;
+use nix::unistd::ForkResult::*;
+use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction};
+use nix::sys::wait::*;
+use nix::sys::stat::{self, Mode, SFlag};
+use std::{env, iter};
+use std::ffi::CString;
+use std::fs::{self, File};
+use std::io::Write;
+use std::os::unix::prelude::*;
+use tempfile::{self, tempfile};
+use libc::{self, _exit, off_t};
+
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+fn test_fork_and_waitpid() {
+ let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // Safe: Child only calls `_exit`, which is signal-safe
+ match fork().expect("Error: Fork Failed") {
+ Child => unsafe { _exit(0) },
+ Parent { child } => {
+ // assert that child was created and pid > 0
+ let child_raw: ::libc::pid_t = child.into();
+ assert!(child_raw > 0);
+ let wait_status = waitpid(child, None);
+ match wait_status {
+ // assert that waitpid returned correct status and the pid is the one of the child
+ Ok(WaitStatus::Exited(pid_t, _)) => assert!(pid_t == child),
+
+ // panic, must never happen
+ s @ Ok(_) => panic!("Child exited {:?}, should never happen", s),
+
+ // panic, waitpid should never fail
+ Err(s) => panic!("Error: waitpid returned Err({:?}", s)
+ }
+
+ },
+ }
+}
+
+#[test]
+fn test_wait() {
+ // Grab FORK_MTX so wait doesn't reap a different test's child process
+ let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // Safe: Child only calls `_exit`, which is signal-safe
+ match fork().expect("Error: Fork Failed") {
+ Child => unsafe { _exit(0) },
+ Parent { child } => {
+ let wait_status = wait();
+
+ // just assert that (any) one child returns with WaitStatus::Exited
+ assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
+ },
+ }
+}
+
+#[test]
+fn test_mkstemp() {
+ let mut path = env::temp_dir();
+ path.push("nix_tempfile.XXXXXX");
+
+ let result = mkstemp(&path);
+ match result {
+ Ok((fd, path)) => {
+ close(fd).unwrap();
+ unlink(path.as_path()).unwrap();
+ },
+ Err(e) => panic!("mkstemp failed: {}", e)
+ }
+}
+
+#[test]
+fn test_mkstemp_directory() {
+ // mkstemp should fail if a directory is given
+ assert!(mkstemp(&env::temp_dir()).is_err());
+}
+
+#[test]
+fn test_mkfifo() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let mkfifo_fifo = tempdir.path().join("mkfifo_fifo");
+
+ mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap();
+
+ let stats = stat::stat(&mkfifo_fifo).unwrap();
+ let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
+ assert!(typ == SFlag::S_IFIFO);
+}
+
+#[test]
+fn test_mkfifo_directory() {
+ // mkfifo should fail if a directory is given
+ assert!(mkfifo(&env::temp_dir(), Mode::S_IRUSR).is_err());
+}
+
+#[test]
+fn test_getpid() {
+ let pid: ::libc::pid_t = getpid().into();
+ let ppid: ::libc::pid_t = getppid().into();
+ assert!(pid > 0);
+ assert!(ppid > 0);
+}
+
+#[test]
+fn test_getsid() {
+ let none_sid: ::libc::pid_t = getsid(None).unwrap().into();
+ let pid_sid: ::libc::pid_t = getsid(Some(getpid())).unwrap().into();
+ assert!(none_sid > 0);
+ assert!(none_sid == pid_sid);
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod linux_android {
+ use nix::unistd::gettid;
+
+ #[test]
+ fn test_gettid() {
+ let tid: ::libc::pid_t = gettid().into();
+ assert!(tid > 0);
+ }
+}
+
+#[test]
+// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+fn test_setgroups() {
+ // Skip this test when not run as root as `setgroups()` requires root.
+ skip_if_not_root!("test_setgroups");
+
+ let _m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // Save the existing groups
+ let old_groups = getgroups().unwrap();
+
+ // Set some new made up groups
+ let groups = [Gid::from_raw(123), Gid::from_raw(456)];
+ setgroups(&groups).unwrap();
+
+ let new_groups = getgroups().unwrap();
+ assert_eq!(new_groups, groups);
+
+ // Revert back to the old groups
+ setgroups(&old_groups).unwrap();
+}
+
+#[test]
+// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+fn test_initgroups() {
+ // Skip this test when not run as root as `initgroups()` and `setgroups()`
+ // require root.
+ skip_if_not_root!("test_initgroups");
+
+ let _m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
+
+ // Save the existing groups
+ let old_groups = getgroups().unwrap();
+
+ // It doesn't matter if the root user is not called "root" or if a user
+ // called "root" doesn't exist. We are just checking that the extra,
+ // made-up group, `123`, is set.
+ // FIXME: Test the other half of initgroups' functionality: whether the
+ // groups that the user belongs to are also set.
+ let user = CString::new("root").unwrap();
+ let group = Gid::from_raw(123);
+ let group_list = getgrouplist(&user, group).unwrap();
+ assert!(group_list.contains(&group));
+
+ initgroups(&user, group).unwrap();
+
+ let new_groups = getgroups().unwrap();
+ assert_eq!(new_groups, group_list);
+
+ // Revert back to the old groups
+ setgroups(&old_groups).unwrap();
+}
+
+macro_rules! execve_test_factory(
+ ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
+ #[test]
+ fn $test_name() {
+ let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ // The `exec`d process will write to `writer`, and we'll read that
+ // data from `reader`.
+ let (reader, writer) = pipe().unwrap();
+
+ // Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function.
+ // NOTE: Technically, this makes the macro unsafe to use because you could pass anything.
+ // The tests make sure not to do that, though.
+ match fork().unwrap() {
+ Child => {
+ // Close stdout.
+ close(1).unwrap();
+ // Make `writer` be the stdout of the new process.
+ dup(writer).unwrap();
+ // exec!
+ $syscall(
+ $exe,
+ $(&CString::new($pathname).unwrap(), )*
+ &[CString::new(b"".as_ref()).unwrap(),
+ CString::new(b"-c".as_ref()).unwrap(),
+ CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz"
+ .as_ref()).unwrap()],
+ &[CString::new(b"foo=bar".as_ref()).unwrap(),
+ CString::new(b"baz=quux".as_ref()).unwrap()]
+ $(, $flags)*).unwrap();
+ },
+ Parent { child } => {
+ // Wait for the child to exit.
+ waitpid(child, None).unwrap();
+ // Read 1024 bytes.
+ let mut buf = [0u8; 1024];
+ read(reader, &mut buf).unwrap();
+ // It should contain the things we printed using `/bin/sh`.
+ let string = String::from_utf8_lossy(&buf);
+ assert!(string.contains("nix!!!"));
+ assert!(string.contains("foo=bar"));
+ assert!(string.contains("baz=quux"));
+ }
+ }
+ }
+ )
+);
+
+cfg_if!{
+ if #[cfg(target_os = "android")] {
+ execve_test_factory!(test_execve, execve, &CString::new("/system/bin/sh").unwrap());
+ execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
+ } else if #[cfg(any(target_os = "freebsd",
+ target_os = "linux"))] {
+ execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap());
+ execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
+ } else if #[cfg(any(target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))] {
+ execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap());
+ // No fexecve() on DragonFly, ios, macos, NetBSD, OpenBSD.
+ //
+ // Note for NetBSD and OpenBSD: although rust-lang/libc includes it
+ // (under unix/bsd/netbsdlike/) fexecve is not currently implemented on
+ // NetBSD nor on OpenBSD.
+ }
+}
+
+#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))]
+execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap());
+
+cfg_if!{
+ if #[cfg(target_os = "android")] {
+ use nix::fcntl::AtFlags;
+ execve_test_factory!(test_execveat_empty, execveat, File::open("/system/bin/sh").unwrap().into_raw_fd(),
+ "", AtFlags::AT_EMPTY_PATH);
+ execve_test_factory!(test_execveat_relative, execveat, File::open("/system/bin/").unwrap().into_raw_fd(),
+ "./sh", AtFlags::empty());
+ execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(),
+ "/system/bin/sh", AtFlags::empty());
+ } else if #[cfg(all(target_os = "linux"), any(target_arch ="x86_64", target_arch ="x86"))] {
+ use nix::fcntl::AtFlags;
+ execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(),
+ "", AtFlags::AT_EMPTY_PATH);
+ execve_test_factory!(test_execveat_relative, execveat, File::open("/bin/").unwrap().into_raw_fd(),
+ "./sh", AtFlags::empty());
+ execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(),
+ "/bin/sh", AtFlags::empty());
+ }
+}
+
+#[test]
+fn test_fchdir() {
+ // fchdir changes the process's cwd
+ let _m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");
+
+ let tmpdir = tempfile::tempdir().unwrap();
+ let tmpdir_path = tmpdir.path().canonicalize().unwrap();
+ let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
+
+ assert!(fchdir(tmpdir_fd).is_ok());
+ assert_eq!(getcwd().unwrap(), tmpdir_path);
+
+ assert!(close(tmpdir_fd).is_ok());
+}
+
+#[test]
+fn test_getcwd() {
+ // chdir changes the process's cwd
+ let _m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");
+
+ let tmpdir = tempfile::tempdir().unwrap();
+ let tmpdir_path = tmpdir.path().canonicalize().unwrap();
+ assert!(chdir(&tmpdir_path).is_ok());
+ assert_eq!(getcwd().unwrap(), tmpdir_path);
+
+ // make path 500 chars longer so that buffer doubling in getcwd
+ // kicks in. Note: One path cannot be longer than 255 bytes
+ // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually
+ // 4096 on linux, 1024 on macos)
+ let mut inner_tmp_dir = tmpdir_path.to_path_buf();
+ for _ in 0..5 {
+ let newdir = iter::repeat("a").take(100).collect::<String>();
+ inner_tmp_dir.push(newdir);
+ assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok());
+ }
+ assert!(chdir(inner_tmp_dir.as_path()).is_ok());
+ assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
+}
+
+#[test]
+fn test_chown() {
+ // Testing for anything other than our own UID/GID is hard.
+ let uid = Some(getuid());
+ let gid = Some(getgid());
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let path = tempdir.path().join("file");
+ {
+ File::create(&path).unwrap();
+ }
+
+ chown(&path, uid, gid).unwrap();
+ chown(&path, uid, None).unwrap();
+ chown(&path, None, gid).unwrap();
+
+ fs::remove_file(&path).unwrap();
+ chown(&path, uid, gid).unwrap_err();
+}
+
+#[test]
+fn test_fchownat() {
+ // Testing for anything other than our own UID/GID is hard.
+ let uid = Some(getuid());
+ let gid = Some(getgid());
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let path = tempdir.path().join("file");
+ {
+ File::create(&path).unwrap();
+ }
+
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+
+ fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink).unwrap();
+
+ chdir(tempdir.path()).unwrap();
+ fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap();
+
+ fs::remove_file(&path).unwrap();
+ fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap_err();
+}
+
+#[test]
+fn test_lseek() {
+ const CONTENTS: &[u8] = b"abcdef123456";
+ let mut tmp = tempfile().unwrap();
+ tmp.write_all(CONTENTS).unwrap();
+ let tmpfd = tmp.into_raw_fd();
+
+ let offset: off_t = 5;
+ lseek(tmpfd, offset, Whence::SeekSet).unwrap();
+
+ let mut buf = [0u8; 7];
+ ::read_exact(tmpfd, &mut buf);
+ assert_eq!(b"f123456", &buf);
+
+ close(tmpfd).unwrap();
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[test]
+fn test_lseek64() {
+ const CONTENTS: &[u8] = b"abcdef123456";
+ let mut tmp = tempfile().unwrap();
+ tmp.write_all(CONTENTS).unwrap();
+ let tmpfd = tmp.into_raw_fd();
+
+ lseek64(tmpfd, 5, Whence::SeekSet).unwrap();
+
+ let mut buf = [0u8; 7];
+ ::read_exact(tmpfd, &mut buf);
+ assert_eq!(b"f123456", &buf);
+
+ close(tmpfd).unwrap();
+}
+
+// Skip on FreeBSD because FreeBSD's CI environment is jailed, and jails
+// aren't allowed to use acct(2)
+#[cfg(not(target_os = "freebsd"))]
+#[test]
+fn test_acct() {
+ use tempfile::NamedTempFile;
+ use std::process::Command;
+ use std::{thread, time};
+
+ skip_if_not_root!("test_acct");
+ let file = NamedTempFile::new().unwrap();
+ let path = file.path().to_str().unwrap();
+
+ acct::enable(path).unwrap();
+ Command::new("echo").arg("Hello world");
+ acct::disable().unwrap();
+
+ loop {
+ let len = fs::metadata(path).unwrap().len();
+ if len > 0 { break; }
+ thread::sleep(time::Duration::from_millis(10));
+ }
+}
+
+#[test]
+fn test_fpathconf_limited() {
+ let f = tempfile().unwrap();
+ // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
+ let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
+ assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0);
+}
+
+#[test]
+fn test_pathconf_limited() {
+ // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
+ let path_max = pathconf("/", PathconfVar::PATH_MAX);
+ assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0);
+}
+
+#[test]
+fn test_sysconf_limited() {
+ // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
+ let open_max = sysconf(SysconfVar::OPEN_MAX);
+ assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0);
+}
+
+#[cfg(target_os = "freebsd")]
+#[test]
+fn test_sysconf_unsupported() {
+ // I know of no sysconf variables that are unsupported everywhere, but
+ // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms
+ // we test.
+ let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
+ assert!(open_max.expect("sysconf failed").is_none())
+}
+
+// Test that we can create a pair of pipes. No need to verify that they pass
+// data; that's the domain of the OS, not nix.
+#[test]
+fn test_pipe() {
+ let (fd0, fd1) = pipe().unwrap();
+ let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode);
+ // S_IFIFO means it's a pipe
+ assert_eq!(m0, SFlag::S_IFIFO);
+ let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode);
+ assert_eq!(m1, SFlag::S_IFIFO);
+}
+
+// pipe2(2) is the same as pipe(2), except it allows setting some flags. Check
+// that we can set a flag.
+#[test]
+fn test_pipe2() {
+ let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
+ let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap());
+ assert!(f0.contains(FdFlag::FD_CLOEXEC));
+ let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap());
+ assert!(f1.contains(FdFlag::FD_CLOEXEC));
+}
+
+#[test]
+fn test_truncate() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let path = tempdir.path().join("file");
+
+ {
+ let mut tmp = File::create(&path).unwrap();
+ const CONTENTS: &[u8] = b"12345678";
+ tmp.write_all(CONTENTS).unwrap();
+ }
+
+ truncate(&path, 4).unwrap();
+
+ let metadata = fs::metadata(&path).unwrap();
+ assert_eq!(4, metadata.len());
+}
+
+#[test]
+fn test_ftruncate() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let path = tempdir.path().join("file");
+
+ let tmpfd = {
+ let mut tmp = File::create(&path).unwrap();
+ const CONTENTS: &[u8] = b"12345678";
+ tmp.write_all(CONTENTS).unwrap();
+ tmp.into_raw_fd()
+ };
+
+ ftruncate(tmpfd, 2).unwrap();
+ close(tmpfd).unwrap();
+
+ let metadata = fs::metadata(&path).unwrap();
+ assert_eq!(2, metadata.len());
+}
+
+// Used in `test_alarm`.
+static mut ALARM_CALLED: bool = false;
+
+// Used in `test_alarm`.
+pub extern fn alarm_signal_handler(raw_signal: libc::c_int) {
+ assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {}", raw_signal);
+ unsafe { ALARM_CALLED = true };
+}
+
+#[test]
+fn test_alarm() {
+ let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+
+ let handler = SigHandler::Handler(alarm_signal_handler);
+ let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
+ let old_handler = unsafe {
+ sigaction(Signal::SIGALRM, &signal_action)
+ .expect("unable to set signal handler for alarm")
+ };
+
+ // Set an alarm.
+ assert_eq!(alarm::set(60), None);
+
+ // Overwriting an alarm should return the old alarm.
+ assert_eq!(alarm::set(1), Some(60));
+
+ // We should be woken up after 1 second by the alarm, so we'll sleep for 2
+ // seconds to be sure.
+ sleep(2);
+ assert_eq!(unsafe { ALARM_CALLED }, true, "expected our alarm signal handler to be called");
+
+ // Reset the signal.
+ unsafe {
+ sigaction(Signal::SIGALRM, &old_handler)
+ .expect("unable to set signal handler for alarm");
+ }
+}
+
+#[test]
+fn test_canceling_alarm() {
+ let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+
+ assert_eq!(alarm::cancel(), None);
+
+ assert_eq!(alarm::set(60), None);
+ assert_eq!(alarm::cancel(), Some(60));
+}
+
+#[test]
+fn test_symlinkat() {
+ let mut buf = [0; 1024];
+ let tempdir = tempfile::tempdir().unwrap();
+
+ let target = tempdir.path().join("a");
+ let linkpath = tempdir.path().join("b");
+ symlinkat(&target, None, &linkpath).unwrap();
+ assert_eq!(
+ readlink(&linkpath, &mut buf).unwrap().to_str().unwrap(),
+ target.to_str().unwrap()
+ );
+
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let target = "c";
+ let linkpath = "d";
+ symlinkat(target, Some(dirfd), linkpath).unwrap();
+ assert_eq!(
+ readlink(&tempdir.path().join(linkpath), &mut buf)
+ .unwrap()
+ .to_str()
+ .unwrap(),
+ target
+ );
+}