summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/test/test_unistd.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/test/test_unistd.rs')
-rw-r--r--third_party/rust/nix/test/test_unistd.rs1407
1 files changed, 1407 insertions, 0 deletions
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..9e20f977ec
--- /dev/null
+++ b/third_party/rust/nix/test/test_unistd.rs
@@ -0,0 +1,1407 @@
+use libc::{_exit, mode_t, off_t};
+use nix::errno::Errno;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::fcntl::readlink;
+use nix::fcntl::OFlag;
+#[cfg(not(target_os = "redox"))]
+use nix::fcntl::{self, open};
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
+#[cfg(not(target_os = "redox"))]
+use nix::sys::signal::{
+ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal,
+};
+use nix::sys::stat::{self, Mode, SFlag};
+use nix::sys::wait::*;
+use nix::unistd::ForkResult::*;
+use nix::unistd::*;
+use std::env;
+#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
+use std::ffi::CString;
+#[cfg(not(target_os = "redox"))]
+use std::fs::DirBuilder;
+use std::fs::{self, File};
+use std::io::Write;
+use std::os::unix::prelude::*;
+#[cfg(not(any(
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+use std::path::Path;
+use tempfile::{tempdir, tempfile};
+
+use crate::*;
+
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+fn test_fork_and_waitpid() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is signal-safe
+ match unsafe { 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_eq!(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 = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is signal-safe
+ match unsafe { 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
+ mkstemp(&env::temp_dir()).expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_mkfifo() {
+ let tempdir = 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 as mode_t);
+ assert_eq!(typ, SFlag::S_IFIFO);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_mkfifo_directory() {
+ // mkfifo should fail if a directory is given
+ mkfifo(&env::temp_dir(), Mode::S_IRUSR).expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat_none() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+ let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo");
+
+ mkfifoat(None, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();
+
+ let stats = stat::stat(&mkfifoat_fifo).unwrap();
+ let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
+ assert_eq!(typ, SFlag::S_IFIFO);
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat() {
+ use nix::fcntl;
+
+ let tempdir = tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let mkfifoat_name = "mkfifoat_name";
+
+ mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap();
+
+ let stats =
+ stat::fstatat(dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap();
+ let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
+ assert_eq!(typ, SFlag::S_IFIFO);
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat_directory_none() {
+ let _m = crate::CWD_LOCK.read();
+
+ // mkfifoat should fail if a directory is given
+ mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR)
+ .expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat_directory() {
+ // mkfifoat should fail if a directory is given
+ let tempdir = tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let mkfifoat_dir = "mkfifoat_dir";
+ stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
+
+ mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR)
+ .expect_err("assertion failed");
+}
+
+#[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]
+#[cfg(not(target_os = "redox"))]
+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_eq!(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",
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_setgroups() {
+ // Skip this test when not run as root as `setgroups()` requires root.
+ skip_if_not_root!("test_setgroups");
+
+ let _m = crate::GROUPS_MTX.lock();
+
+ // 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",
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos"
+)))]
+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 = crate::GROUPS_MTX.lock();
+
+ // 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();
+}
+
+#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
+macro_rules! execve_test_factory (
+ ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
+
+ #[cfg(test)]
+ mod $test_name {
+ use std::ffi::CStr;
+ use super::*;
+
+ const EMPTY: &'static [u8] = b"\0";
+ const DASH_C: &'static [u8] = b"-c\0";
+ const BIGARG: &'static [u8] = b"echo nix!!! && echo foo=$foo && echo baz=$baz\0";
+ const FOO: &'static [u8] = b"foo=bar\0";
+ const BAZ: &'static [u8] = b"baz=quux\0";
+
+ fn syscall_cstr_ref() -> Result<std::convert::Infallible, nix::Error> {
+ $syscall(
+ $exe,
+ $(CString::new($pathname).unwrap().as_c_str(), )*
+ &[CStr::from_bytes_with_nul(EMPTY).unwrap(),
+ CStr::from_bytes_with_nul(DASH_C).unwrap(),
+ CStr::from_bytes_with_nul(BIGARG).unwrap()],
+ &[CStr::from_bytes_with_nul(FOO).unwrap(),
+ CStr::from_bytes_with_nul(BAZ).unwrap()]
+ $(, $flags)*)
+ }
+
+ fn syscall_cstring() -> Result<std::convert::Infallible, nix::Error> {
+ $syscall(
+ $exe,
+ $(CString::new($pathname).unwrap().as_c_str(), )*
+ &[CString::from(CStr::from_bytes_with_nul(EMPTY).unwrap()),
+ CString::from(CStr::from_bytes_with_nul(DASH_C).unwrap()),
+ CString::from(CStr::from_bytes_with_nul(BIGARG).unwrap())],
+ &[CString::from(CStr::from_bytes_with_nul(FOO).unwrap()),
+ CString::from(CStr::from_bytes_with_nul(BAZ).unwrap())]
+ $(, $flags)*)
+ }
+
+ fn common_test(syscall: fn() -> Result<std::convert::Infallible, nix::Error>) {
+ if "execveat" == stringify!($syscall) {
+ // Though undocumented, Docker's default seccomp profile seems to
+ // block this syscall. https://github.com/nix-rust/nix/issues/1122
+ skip_if_seccomp!($test_name);
+ }
+
+ let m = crate::FORK_MTX.lock();
+ // 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 unsafe{fork()}.unwrap() {
+ Child => {
+ // Make `writer` be the stdout of the new process.
+ dup2(writer, 1).unwrap();
+ let r = syscall();
+ let _ = std::io::stderr()
+ .write_all(format!("{:?}", r).as_bytes());
+ // Should only get here in event of error
+ unsafe{ _exit(1) };
+ },
+ Parent { child } => {
+ // Wait for the child to exit.
+ let ws = waitpid(child, None);
+ drop(m);
+ assert_eq!(ws, Ok(WaitStatus::Exited(child, 0)));
+ // 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"));
+ }
+ }
+ }
+
+ // These tests frequently fail on musl, probably due to
+ // https://github.com/nix-rust/nix/issues/555
+ #[cfg_attr(target_env = "musl", ignore)]
+ #[test]
+ fn test_cstr_ref() {
+ common_test(syscall_cstr_ref);
+ }
+
+ // These tests frequently fail on musl, probably due to
+ // https://github.com/nix-rust/nix/issues/555
+ #[cfg_attr(target_env = "musl", ignore)]
+ #[test]
+ fn test_cstring() {
+ common_test(syscall_cstring);
+ }
+ }
+
+ )
+);
+
+cfg_if! {
+ if #[cfg(target_os = "android")] {
+ execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str());
+ execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
+ } else if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"))] {
+ // These tests frequently fail on musl, probably due to
+ // https://github.com/nix-rust/nix/issues/555
+ execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
+ execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
+ } else if #[cfg(any(target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))] {
+ execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
+ // No fexecve() on ios, macos, NetBSD, OpenBSD.
+ }
+}
+
+#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))]
+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]
+#[cfg(not(target_os = "fuchsia"))]
+fn test_fchdir() {
+ // fchdir changes the process's cwd
+ let _dr = crate::DirRestore::new();
+
+ let tmpdir = tempdir().unwrap();
+ let tmpdir_path = tmpdir.path().canonicalize().unwrap();
+ let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
+
+ fchdir(tmpdir_fd).expect("assertion failed");
+ assert_eq!(getcwd().unwrap(), tmpdir_path);
+
+ close(tmpdir_fd).expect("assertion failed");
+}
+
+#[test]
+fn test_getcwd() {
+ // chdir changes the process's cwd
+ let _dr = crate::DirRestore::new();
+
+ let tmpdir = tempdir().unwrap();
+ let tmpdir_path = tmpdir.path().canonicalize().unwrap();
+ chdir(&tmpdir_path).expect("assertion failed");
+ 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;
+ for _ in 0..5 {
+ let newdir = "a".repeat(100);
+ inner_tmp_dir.push(newdir);
+ mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU)
+ .expect("assertion failed");
+ }
+ chdir(inner_tmp_dir.as_path()).expect("assertion failed");
+ 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 = 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_fchown() {
+ // Testing for anything other than our own UID/GID is hard.
+ let uid = Some(getuid());
+ let gid = Some(getgid());
+
+ let path = tempfile().unwrap();
+ let fd = path.as_raw_fd();
+
+ fchown(fd, uid, gid).unwrap();
+ fchown(fd, uid, None).unwrap();
+ fchown(fd, None, gid).unwrap();
+ fchown(999999999, uid, gid).unwrap_err();
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_fchownat() {
+ let _dr = crate::DirRestore::new();
+ // Testing for anything other than our own UID/GID is hard.
+ let uid = Some(getuid());
+ let gid = Some(getgid());
+
+ let tempdir = 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];
+ crate::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];
+ crate::read_exact(tmpfd, &mut buf);
+ assert_eq!(b"f123456", &buf);
+
+ close(tmpfd).unwrap();
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ macro_rules! require_acct{
+ () => {
+ require_capability!("test_acct", CAP_SYS_PACCT);
+ }
+ }
+ } else if #[cfg(target_os = "freebsd")] {
+ macro_rules! require_acct{
+ () => {
+ skip_if_not_root!("test_acct");
+ skip_if_jailed!("test_acct");
+ }
+ }
+ } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "haiku")))] {
+ macro_rules! require_acct{
+ () => {
+ skip_if_not_root!("test_acct");
+ }
+ }
+ }
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_acct() {
+ use std::process::Command;
+ use std::{thread, time};
+ use tempfile::NamedTempFile;
+
+ let _m = crate::FORK_MTX.lock();
+ require_acct!();
+
+ let file = NamedTempFile::new().unwrap();
+ let path = file.path().to_str().unwrap();
+
+ acct::enable(path).unwrap();
+
+ loop {
+ Command::new("echo").arg("Hello world").output().unwrap();
+ let len = fs::metadata(path).unwrap().len();
+ if len > 0 {
+ break;
+ }
+ thread::sleep(time::Duration::from_millis(10));
+ }
+ acct::disable().unwrap();
+}
+
+#[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())
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+#[test]
+fn test_getresuid() {
+ let resuids = getresuid().unwrap();
+ assert_ne!(resuids.real.as_raw(), libc::uid_t::MAX);
+ assert_ne!(resuids.effective.as_raw(), libc::uid_t::MAX);
+ assert_ne!(resuids.saved.as_raw(), libc::uid_t::MAX);
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+#[test]
+fn test_getresgid() {
+ let resgids = getresgid().unwrap();
+ assert_ne!(resgids.real.as_raw(), libc::gid_t::MAX);
+ assert_ne!(resgids.effective.as_raw(), libc::gid_t::MAX);
+ assert_ne!(resgids.saved.as_raw(), libc::gid_t::MAX);
+}
+
+// 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 as mode_t,
+ );
+ // S_IFIFO means it's a pipe
+ assert_eq!(m0, SFlag::S_IFIFO);
+ let m1 = stat::SFlag::from_bits_truncate(
+ stat::fstat(fd1).unwrap().st_mode as mode_t,
+ );
+ 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.
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris"
+))]
+#[test]
+fn test_pipe2() {
+ use nix::fcntl::{fcntl, FcntlArg, FdFlag};
+
+ let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
+ let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap());
+ 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]
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+fn test_truncate() {
+ let tempdir = 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 = 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`.
+#[cfg(not(target_os = "redox"))]
+static mut ALARM_CALLED: bool = false;
+
+// Used in `test_alarm`.
+#[cfg(not(target_os = "redox"))]
+pub extern "C" fn alarm_signal_handler(raw_signal: libc::c_int) {
+ assert_eq!(
+ raw_signal,
+ libc::SIGALRM,
+ "unexpected signal: {}",
+ raw_signal
+ );
+ unsafe { ALARM_CALLED = true };
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_alarm() {
+ use std::{
+ thread,
+ time::{Duration, Instant},
+ };
+
+ // Maybe other tests that fork interfere with this one?
+ let _m = crate::SIGNAL_MTX.lock();
+
+ 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 3
+ // seconds to be sure.
+ let starttime = Instant::now();
+ loop {
+ thread::sleep(Duration::from_millis(100));
+ if unsafe { ALARM_CALLED } {
+ break;
+ }
+ if starttime.elapsed() > Duration::from_secs(3) {
+ panic!("Timeout waiting for SIGALRM");
+ }
+ }
+
+ // Reset the signal.
+ unsafe {
+ sigaction(Signal::SIGALRM, &old_handler)
+ .expect("unable to set signal handler for alarm");
+ }
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_canceling_alarm() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ assert_eq!(alarm::cancel(), None);
+
+ assert_eq!(alarm::set(60), None);
+ assert_eq!(alarm::cancel(), Some(60));
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_symlinkat() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+
+ let target = tempdir.path().join("a");
+ let linkpath = tempdir.path().join("b");
+ symlinkat(&target, None, &linkpath).unwrap();
+ assert_eq!(
+ readlink(&linkpath).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))
+ .unwrap()
+ .to_str()
+ .unwrap(),
+ target
+ );
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_file() {
+ let tempdir = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir.path().join(oldfilename);
+
+ let newfilename = "bar.txt";
+ let newfilepath = tempdir.path().join(newfilename);
+
+ // Create file
+ File::create(oldfilepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt hard link file at relative path
+ linkat(
+ Some(dirfd),
+ oldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+ assert!(newfilepath.exists());
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_olddirfd_none() {
+ let _dr = crate::DirRestore::new();
+
+ let tempdir_oldfile = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir_oldfile.path().join(oldfilename);
+
+ let tempdir_newfile = tempdir().unwrap();
+ let newfilename = "bar.txt";
+ let newfilepath = tempdir_newfile.path().join(newfilename);
+
+ // Create file
+ File::create(oldfilepath).unwrap();
+
+ // Get file descriptor for base directory of new file
+ let dirfd = fcntl::open(
+ tempdir_newfile.path(),
+ fcntl::OFlag::empty(),
+ stat::Mode::empty(),
+ )
+ .unwrap();
+
+ // Attempt hard link file using curent working directory as relative path for old file path
+ chdir(tempdir_oldfile.path()).unwrap();
+ linkat(
+ None,
+ oldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+ assert!(newfilepath.exists());
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_newdirfd_none() {
+ let _dr = crate::DirRestore::new();
+
+ let tempdir_oldfile = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir_oldfile.path().join(oldfilename);
+
+ let tempdir_newfile = tempdir().unwrap();
+ let newfilename = "bar.txt";
+ let newfilepath = tempdir_newfile.path().join(newfilename);
+
+ // Create file
+ File::create(oldfilepath).unwrap();
+
+ // Get file descriptor for base directory of old file
+ let dirfd = fcntl::open(
+ tempdir_oldfile.path(),
+ fcntl::OFlag::empty(),
+ stat::Mode::empty(),
+ )
+ .unwrap();
+
+ // Attempt hard link file using current working directory as relative path for new file path
+ chdir(tempdir_newfile.path()).unwrap();
+ linkat(
+ Some(dirfd),
+ oldfilename,
+ None,
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+ assert!(newfilepath.exists());
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_linkat_no_follow_symlink() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir.path().join(oldfilename);
+
+ let symoldfilename = "symfoo.txt";
+ let symoldfilepath = tempdir.path().join(symoldfilename);
+
+ let newfilename = "nofollowsymbar.txt";
+ let newfilepath = tempdir.path().join(newfilename);
+
+ // Create file
+ File::create(&oldfilepath).unwrap();
+
+ // Create symlink to file
+ symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt link symlink of file at relative path
+ linkat(
+ Some(dirfd),
+ symoldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::NoSymlinkFollow,
+ )
+ .unwrap();
+
+ // Assert newfile is actually a symlink to oldfile.
+ assert_eq!(
+ readlink(&newfilepath).unwrap().to_str().unwrap(),
+ oldfilepath.to_str().unwrap()
+ );
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_follow_symlink() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir.path().join(oldfilename);
+
+ let symoldfilename = "symfoo.txt";
+ let symoldfilepath = tempdir.path().join(symoldfilename);
+
+ let newfilename = "nofollowsymbar.txt";
+ let newfilepath = tempdir.path().join(newfilename);
+
+ // Create file
+ File::create(&oldfilepath).unwrap();
+
+ // Create symlink to file
+ symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt link target of symlink of file at relative path
+ linkat(
+ Some(dirfd),
+ symoldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+
+ let newfilestat = stat::stat(&newfilepath).unwrap();
+
+ // Check the file type of the new link
+ assert_eq!(
+ (stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t)
+ & SFlag::S_IFMT),
+ SFlag::S_IFREG
+ );
+
+ // Check the number of hard links to the original file
+ assert_eq!(newfilestat.st_nlink, 2);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_unlinkat_dir_noremovedir() {
+ let tempdir = tempdir().unwrap();
+ let dirname = "foo_dir";
+ let dirpath = tempdir.path().join(dirname);
+
+ // Create dir
+ DirBuilder::new().recursive(true).create(dirpath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt unlink dir at relative path without proper flag
+ let err_result =
+ unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
+ assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_unlinkat_dir_removedir() {
+ let tempdir = tempdir().unwrap();
+ let dirname = "foo_dir";
+ let dirpath = tempdir.path().join(dirname);
+
+ // Create dir
+ DirBuilder::new().recursive(true).create(&dirpath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt unlink dir at relative path with proper flag
+ unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
+ assert!(!dirpath.exists());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_unlinkat_file() {
+ let tempdir = tempdir().unwrap();
+ let filename = "foo.txt";
+ let filepath = tempdir.path().join(filename);
+
+ // Create file
+ File::create(&filepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt unlink file at relative path
+ unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
+ assert!(!filepath.exists());
+}
+
+#[test]
+fn test_access_not_existing() {
+ let tempdir = tempdir().unwrap();
+ let dir = tempdir.path().join("does_not_exist.txt");
+ assert_eq!(
+ access(&dir, AccessFlags::F_OK).err().unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+fn test_access_file_exists() {
+ let tempdir = tempdir().unwrap();
+ let path = tempdir.path().join("does_exist.txt");
+ let _file = File::create(path.clone()).unwrap();
+ access(&path, AccessFlags::R_OK | AccessFlags::W_OK)
+ .expect("assertion failed");
+}
+
+//Clippy false positive https://github.com/rust-lang/rust-clippy/issues/9111
+#[allow(clippy::needless_borrow)]
+#[cfg(not(target_os = "redox"))]
+#[test]
+fn test_user_into_passwd() {
+ // get the UID of the "nobody" user
+ #[cfg(not(target_os = "haiku"))]
+ let test_username = "nobody";
+ // "nobody" unavailable on haiku
+ #[cfg(target_os = "haiku")]
+ let test_username = "user";
+
+ let nobody = User::from_name(test_username).unwrap().unwrap();
+ let pwd: libc::passwd = nobody.into();
+ let _: User = (&pwd).into();
+}
+
+/// Tests setting the filesystem UID with `setfsuid`.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[test]
+fn test_setfsuid() {
+ use std::os::unix::fs::PermissionsExt;
+ use std::{fs, io, thread};
+ require_capability!("test_setfsuid", CAP_SETUID);
+
+ // get the UID of the "nobody" user
+ let nobody = User::from_name("nobody").unwrap().unwrap();
+
+ // create a temporary file with permissions '-rw-r-----'
+ let file = tempfile::NamedTempFile::new_in("/var/tmp").unwrap();
+ let temp_path = file.into_temp_path();
+ let temp_path_2 = temp_path.to_path_buf();
+ let mut permissions = fs::metadata(&temp_path).unwrap().permissions();
+ permissions.set_mode(0o640);
+
+ // spawn a new thread where to test setfsuid
+ thread::spawn(move || {
+ // set filesystem UID
+ let fuid = setfsuid(nobody.uid);
+ // trying to open the temporary file should fail with EACCES
+ let res = fs::File::open(&temp_path);
+ let err = res.expect_err("assertion failed");
+ assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
+
+ // assert fuid actually changes
+ let prev_fuid = setfsuid(Uid::from_raw(-1i32 as u32));
+ assert_ne!(prev_fuid, fuid);
+ })
+ .join()
+ .unwrap();
+
+ // open the temporary file with the current thread filesystem UID
+ fs::File::open(temp_path_2).unwrap();
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_ttyname() {
+ let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
+ assert!(fd.as_raw_fd() > 0);
+
+ // on linux, we can just call ttyname on the pty master directly, but
+ // apparently osx requires that ttyname is called on a slave pty (can't
+ // find this documented anywhere, but it seems to empirically be the case)
+ grantpt(&fd).expect("grantpt failed");
+ unlockpt(&fd).expect("unlockpt failed");
+ let sname = unsafe { ptsname(&fd) }.expect("ptsname failed");
+ let fds = open(Path::new(&sname), OFlag::O_RDWR, stat::Mode::empty())
+ .expect("open failed");
+ assert!(fds > 0);
+
+ let name = ttyname(fds).expect("ttyname failed");
+ assert!(name.starts_with("/dev"));
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+fn test_ttyname_not_pty() {
+ let fd = File::open("/dev/zero").unwrap();
+ assert!(fd.as_raw_fd() > 0);
+ assert_eq!(ttyname(fd.as_raw_fd()), Err(Errno::ENOTTY));
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_ttyname_invalid_fd() {
+ assert_eq!(ttyname(-1), Err(Errno::EBADF));
+}
+
+#[test]
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+))]
+fn test_getpeereid() {
+ use std::os::unix::net::UnixStream;
+ let (sock_a, sock_b) = UnixStream::pair().unwrap();
+
+ let (uid_a, gid_a) = getpeereid(sock_a.as_raw_fd()).unwrap();
+ let (uid_b, gid_b) = getpeereid(sock_b.as_raw_fd()).unwrap();
+
+ let uid = geteuid();
+ let gid = getegid();
+
+ assert_eq!(uid, uid_a);
+ assert_eq!(gid, gid_a);
+ assert_eq!(uid_a, uid_b);
+ assert_eq!(gid_a, gid_b);
+}
+
+#[test]
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+))]
+fn test_getpeereid_invalid_fd() {
+ // getpeereid is not POSIX, so error codes are inconsistent between different Unices.
+ getpeereid(-1).expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_none_not_existing() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let dir = tempdir.path().join("does_not_exist.txt");
+ assert_eq!(
+ faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty())
+ .err()
+ .unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_not_existing() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let not_exist_file = "does_not_exist.txt";
+ assert_eq!(
+ faccessat(
+ Some(dirfd),
+ not_exist_file,
+ AccessFlags::F_OK,
+ AtFlags::empty(),
+ )
+ .err()
+ .unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_none_file_exists() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let path = tempdir.path().join("does_exist.txt");
+ let _file = File::create(path.clone()).unwrap();
+ assert!(faccessat(
+ None,
+ &path,
+ AccessFlags::R_OK | AccessFlags::W_OK,
+ AtFlags::empty(),
+ )
+ .is_ok());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_file_exists() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let exist_file = "does_exist.txt";
+ let path = tempdir.path().join(exist_file);
+ let _file = File::create(path.clone()).unwrap();
+ assert!(faccessat(
+ Some(dirfd),
+ &path,
+ AccessFlags::R_OK | AccessFlags::W_OK,
+ AtFlags::empty(),
+ )
+ .is_ok());
+}
+
+#[test]
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+fn test_eaccess_not_existing() {
+ let tempdir = tempdir().unwrap();
+ let dir = tempdir.path().join("does_not_exist.txt");
+ assert_eq!(
+ eaccess(&dir, AccessFlags::F_OK).err().unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+fn test_eaccess_file_exists() {
+ let tempdir = tempdir().unwrap();
+ let path = tempdir.path().join("does_exist.txt");
+ let _file = File::create(path.clone()).unwrap();
+ eaccess(&path, AccessFlags::R_OK | AccessFlags::W_OK)
+ .expect("assertion failed");
+}