1use libc::{_exit, mode_t, off_t};
2use nix::errno::Errno;
3#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
4use nix::fcntl::readlink;
5use nix::fcntl::OFlag;
6#[cfg(not(target_os = "redox"))]
7use nix::fcntl::{self, open};
8#[cfg(not(any(
9    target_os = "redox",
10    target_os = "fuchsia",
11    target_os = "haiku"
12)))]
13use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
14#[cfg(not(target_os = "redox"))]
15use nix::sys::signal::{
16    sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal,
17};
18use nix::sys::stat::{self, Mode, SFlag};
19use nix::sys::wait::*;
20use nix::unistd::ForkResult::*;
21use nix::unistd::*;
22use std::env;
23#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
24use std::ffi::CString;
25#[cfg(not(target_os = "redox"))]
26use std::fs::DirBuilder;
27use std::fs::{self, File};
28use std::io::Write;
29use std::os::unix::prelude::*;
30#[cfg(not(any(
31    target_os = "fuchsia",
32    target_os = "redox",
33    target_os = "haiku"
34)))]
35use std::path::Path;
36use tempfile::{tempdir, tempfile};
37
38use crate::*;
39
40#[test]
41#[cfg(not(any(target_os = "netbsd")))]
42fn test_fork_and_waitpid() {
43    let _m = crate::FORK_MTX.lock();
44
45    // Safe: Child only calls `_exit`, which is signal-safe
46    match unsafe { fork() }.expect("Error: Fork Failed") {
47        Child => unsafe { _exit(0) },
48        Parent { child } => {
49            // assert that child was created and pid > 0
50            let child_raw: ::libc::pid_t = child.into();
51            assert!(child_raw > 0);
52            let wait_status = waitpid(child, None);
53            match wait_status {
54                // assert that waitpid returned correct status and the pid is the one of the child
55                Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child),
56
57                // panic, must never happen
58                s @ Ok(_) => {
59                    panic!("Child exited {:?}, should never happen", s)
60                }
61
62                // panic, waitpid should never fail
63                Err(s) => panic!("Error: waitpid returned Err({:?}", s),
64            }
65        }
66    }
67}
68
69#[test]
70fn test_wait() {
71    // Grab FORK_MTX so wait doesn't reap a different test's child process
72    let _m = crate::FORK_MTX.lock();
73
74    // Safe: Child only calls `_exit`, which is signal-safe
75    match unsafe { fork() }.expect("Error: Fork Failed") {
76        Child => unsafe { _exit(0) },
77        Parent { child } => {
78            let wait_status = wait();
79
80            // just assert that (any) one child returns with WaitStatus::Exited
81            assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
82        }
83    }
84}
85
86#[test]
87fn test_mkstemp() {
88    let mut path = env::temp_dir();
89    path.push("nix_tempfile.XXXXXX");
90
91    let result = mkstemp(&path);
92    match result {
93        Ok((fd, path)) => {
94            close(fd).unwrap();
95            unlink(path.as_path()).unwrap();
96        }
97        Err(e) => panic!("mkstemp failed: {}", e),
98    }
99}
100
101#[test]
102fn test_mkstemp_directory() {
103    // mkstemp should fail if a directory is given
104    mkstemp(&env::temp_dir()).expect_err("assertion failed");
105}
106
107#[test]
108#[cfg(not(target_os = "redox"))]
109fn test_mkfifo() {
110    let tempdir = tempdir().unwrap();
111    let mkfifo_fifo = tempdir.path().join("mkfifo_fifo");
112
113    mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap();
114
115    let stats = stat::stat(&mkfifo_fifo).unwrap();
116    let typ = stat::SFlag::from_bits_truncate(stats.st_mode as mode_t);
117    assert_eq!(typ, SFlag::S_IFIFO);
118}
119
120#[test]
121#[cfg(not(target_os = "redox"))]
122fn test_mkfifo_directory() {
123    // mkfifo should fail if a directory is given
124    mkfifo(&env::temp_dir(), Mode::S_IRUSR).expect_err("assertion failed");
125}
126
127#[test]
128#[cfg(not(any(
129    target_os = "macos",
130    target_os = "ios",
131    target_os = "android",
132    target_os = "redox",
133    target_os = "haiku"
134)))]
135fn test_mkfifoat_none() {
136    let _m = crate::CWD_LOCK.read();
137
138    let tempdir = tempdir().unwrap();
139    let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo");
140
141    mkfifoat(None, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();
142
143    let stats = stat::stat(&mkfifoat_fifo).unwrap();
144    let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
145    assert_eq!(typ, SFlag::S_IFIFO);
146}
147
148#[test]
149#[cfg(not(any(
150    target_os = "macos",
151    target_os = "ios",
152    target_os = "android",
153    target_os = "redox",
154    target_os = "haiku"
155)))]
156fn test_mkfifoat() {
157    use nix::fcntl;
158
159    let tempdir = tempdir().unwrap();
160    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
161    let mkfifoat_name = "mkfifoat_name";
162
163    mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap();
164
165    let stats =
166        stat::fstatat(dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap();
167    let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
168    assert_eq!(typ, SFlag::S_IFIFO);
169}
170
171#[test]
172#[cfg(not(any(
173    target_os = "macos",
174    target_os = "ios",
175    target_os = "android",
176    target_os = "redox",
177    target_os = "haiku"
178)))]
179fn test_mkfifoat_directory_none() {
180    let _m = crate::CWD_LOCK.read();
181
182    // mkfifoat should fail if a directory is given
183    mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR)
184        .expect_err("assertion failed");
185}
186
187#[test]
188#[cfg(not(any(
189    target_os = "macos",
190    target_os = "ios",
191    target_os = "android",
192    target_os = "redox",
193    target_os = "haiku"
194)))]
195fn test_mkfifoat_directory() {
196    // mkfifoat should fail if a directory is given
197    let tempdir = tempdir().unwrap();
198    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
199    let mkfifoat_dir = "mkfifoat_dir";
200    stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
201
202    mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR)
203        .expect_err("assertion failed");
204}
205
206#[test]
207fn test_getpid() {
208    let pid: ::libc::pid_t = getpid().into();
209    let ppid: ::libc::pid_t = getppid().into();
210    assert!(pid > 0);
211    assert!(ppid > 0);
212}
213
214#[test]
215#[cfg(not(target_os = "redox"))]
216fn test_getsid() {
217    let none_sid: ::libc::pid_t = getsid(None).unwrap().into();
218    let pid_sid: ::libc::pid_t = getsid(Some(getpid())).unwrap().into();
219    assert!(none_sid > 0);
220    assert_eq!(none_sid, pid_sid);
221}
222
223#[cfg(any(target_os = "linux", target_os = "android"))]
224mod linux_android {
225    use nix::unistd::gettid;
226
227    #[test]
228    fn test_gettid() {
229        let tid: ::libc::pid_t = gettid().into();
230        assert!(tid > 0);
231    }
232}
233
234#[test]
235// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
236#[cfg(not(any(
237    target_os = "ios",
238    target_os = "macos",
239    target_os = "redox",
240    target_os = "fuchsia",
241    target_os = "haiku"
242)))]
243fn test_setgroups() {
244    // Skip this test when not run as root as `setgroups()` requires root.
245    skip_if_not_root!("test_setgroups");
246
247    let _m = crate::GROUPS_MTX.lock();
248
249    // Save the existing groups
250    let old_groups = getgroups().unwrap();
251
252    // Set some new made up groups
253    let groups = [Gid::from_raw(123), Gid::from_raw(456)];
254    setgroups(&groups).unwrap();
255
256    let new_groups = getgroups().unwrap();
257    assert_eq!(new_groups, groups);
258
259    // Revert back to the old groups
260    setgroups(&old_groups).unwrap();
261}
262
263#[test]
264// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
265#[cfg(not(any(
266    target_os = "ios",
267    target_os = "macos",
268    target_os = "redox",
269    target_os = "fuchsia",
270    target_os = "haiku",
271    target_os = "illumos"
272)))]
273fn test_initgroups() {
274    // Skip this test when not run as root as `initgroups()` and `setgroups()`
275    // require root.
276    skip_if_not_root!("test_initgroups");
277
278    let _m = crate::GROUPS_MTX.lock();
279
280    // Save the existing groups
281    let old_groups = getgroups().unwrap();
282
283    // It doesn't matter if the root user is not called "root" or if a user
284    // called "root" doesn't exist. We are just checking that the extra,
285    // made-up group, `123`, is set.
286    // FIXME: Test the other half of initgroups' functionality: whether the
287    // groups that the user belongs to are also set.
288    let user = CString::new("root").unwrap();
289    let group = Gid::from_raw(123);
290    let group_list = getgrouplist(&user, group).unwrap();
291    assert!(group_list.contains(&group));
292
293    initgroups(&user, group).unwrap();
294
295    let new_groups = getgroups().unwrap();
296    assert_eq!(new_groups, group_list);
297
298    // Revert back to the old groups
299    setgroups(&old_groups).unwrap();
300}
301
302#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
303macro_rules! execve_test_factory (
304    ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
305
306    #[cfg(test)]
307    mod $test_name {
308    use std::ffi::CStr;
309    use super::*;
310
311    const EMPTY: &'static [u8] = b"\0";
312    const DASH_C: &'static [u8] = b"-c\0";
313    const BIGARG: &'static [u8] = b"echo nix!!! && echo foo=$foo && echo baz=$baz\0";
314    const FOO: &'static [u8] = b"foo=bar\0";
315    const BAZ: &'static [u8] = b"baz=quux\0";
316
317    fn syscall_cstr_ref() -> Result<std::convert::Infallible, nix::Error> {
318        $syscall(
319            $exe,
320            $(CString::new($pathname).unwrap().as_c_str(), )*
321            &[CStr::from_bytes_with_nul(EMPTY).unwrap(),
322              CStr::from_bytes_with_nul(DASH_C).unwrap(),
323              CStr::from_bytes_with_nul(BIGARG).unwrap()],
324            &[CStr::from_bytes_with_nul(FOO).unwrap(),
325              CStr::from_bytes_with_nul(BAZ).unwrap()]
326            $(, $flags)*)
327    }
328
329    fn syscall_cstring() -> Result<std::convert::Infallible, nix::Error> {
330        $syscall(
331            $exe,
332            $(CString::new($pathname).unwrap().as_c_str(), )*
333            &[CString::from(CStr::from_bytes_with_nul(EMPTY).unwrap()),
334              CString::from(CStr::from_bytes_with_nul(DASH_C).unwrap()),
335              CString::from(CStr::from_bytes_with_nul(BIGARG).unwrap())],
336            &[CString::from(CStr::from_bytes_with_nul(FOO).unwrap()),
337              CString::from(CStr::from_bytes_with_nul(BAZ).unwrap())]
338            $(, $flags)*)
339    }
340
341    fn common_test(syscall: fn() -> Result<std::convert::Infallible, nix::Error>) {
342        if "execveat" == stringify!($syscall) {
343            // Though undocumented, Docker's default seccomp profile seems to
344            // block this syscall.  https://github.com/nix-rust/nix/issues/1122
345            skip_if_seccomp!($test_name);
346        }
347
348        let m = crate::FORK_MTX.lock();
349        // The `exec`d process will write to `writer`, and we'll read that
350        // data from `reader`.
351        let (reader, writer) = pipe().unwrap();
352
353        // Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function.
354        // NOTE: Technically, this makes the macro unsafe to use because you could pass anything.
355        //       The tests make sure not to do that, though.
356        match unsafe{fork()}.unwrap() {
357            Child => {
358                // Make `writer` be the stdout of the new process.
359                dup2(writer, 1).unwrap();
360                let r = syscall();
361                let _ = std::io::stderr()
362                    .write_all(format!("{:?}", r).as_bytes());
363                // Should only get here in event of error
364                unsafe{ _exit(1) };
365            },
366            Parent { child } => {
367                // Wait for the child to exit.
368                let ws = waitpid(child, None);
369                drop(m);
370                assert_eq!(ws, Ok(WaitStatus::Exited(child, 0)));
371                // Read 1024 bytes.
372                let mut buf = [0u8; 1024];
373                read(reader, &mut buf).unwrap();
374                // It should contain the things we printed using `/bin/sh`.
375                let string = String::from_utf8_lossy(&buf);
376                assert!(string.contains("nix!!!"));
377                assert!(string.contains("foo=bar"));
378                assert!(string.contains("baz=quux"));
379            }
380        }
381    }
382
383    // These tests frequently fail on musl, probably due to
384        // https://github.com/nix-rust/nix/issues/555
385    #[cfg_attr(target_env = "musl", ignore)]
386    #[test]
387    fn test_cstr_ref() {
388        common_test(syscall_cstr_ref);
389    }
390
391    // These tests frequently fail on musl, probably due to
392        // https://github.com/nix-rust/nix/issues/555
393    #[cfg_attr(target_env = "musl", ignore)]
394    #[test]
395    fn test_cstring() {
396        common_test(syscall_cstring);
397    }
398    }
399
400    )
401);
402
403cfg_if! {
404    if #[cfg(target_os = "android")] {
405        execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str());
406        execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
407    } else if #[cfg(any(target_os = "dragonfly",
408                        target_os = "freebsd",
409                        target_os = "linux"))] {
410        // These tests frequently fail on musl, probably due to
411        // https://github.com/nix-rust/nix/issues/555
412        execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
413        execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
414    } else if #[cfg(any(target_os = "illumos",
415                        target_os = "ios",
416                        target_os = "macos",
417                        target_os = "netbsd",
418                        target_os = "openbsd",
419                        target_os = "solaris"))] {
420        execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
421        // No fexecve() on ios, macos, NetBSD, OpenBSD.
422    }
423}
424
425#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))]
426execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap());
427
428cfg_if! {
429    if #[cfg(target_os = "android")] {
430        use nix::fcntl::AtFlags;
431        execve_test_factory!(test_execveat_empty, execveat,
432                             File::open("/system/bin/sh").unwrap().into_raw_fd(),
433                             "", AtFlags::AT_EMPTY_PATH);
434        execve_test_factory!(test_execveat_relative, execveat,
435                             File::open("/system/bin/").unwrap().into_raw_fd(),
436                             "./sh", AtFlags::empty());
437        execve_test_factory!(test_execveat_absolute, execveat,
438                             File::open("/").unwrap().into_raw_fd(),
439                             "/system/bin/sh", AtFlags::empty());
440    } else if #[cfg(all(target_os = "linux", any(target_arch ="x86_64", target_arch ="x86")))] {
441        use nix::fcntl::AtFlags;
442        execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(),
443                             "", AtFlags::AT_EMPTY_PATH);
444        execve_test_factory!(test_execveat_relative, execveat, File::open("/bin/").unwrap().into_raw_fd(),
445                             "./sh", AtFlags::empty());
446        execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(),
447                             "/bin/sh", AtFlags::empty());
448    }
449}
450
451#[test]
452#[cfg(not(target_os = "fuchsia"))]
453fn test_fchdir() {
454    // fchdir changes the process's cwd
455    let _dr = crate::DirRestore::new();
456
457    let tmpdir = tempdir().unwrap();
458    let tmpdir_path = tmpdir.path().canonicalize().unwrap();
459    let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
460
461    fchdir(tmpdir_fd).expect("assertion failed");
462    assert_eq!(getcwd().unwrap(), tmpdir_path);
463
464    close(tmpdir_fd).expect("assertion failed");
465}
466
467#[test]
468fn test_getcwd() {
469    // chdir changes the process's cwd
470    let _dr = crate::DirRestore::new();
471
472    let tmpdir = tempdir().unwrap();
473    let tmpdir_path = tmpdir.path().canonicalize().unwrap();
474    chdir(&tmpdir_path).expect("assertion failed");
475    assert_eq!(getcwd().unwrap(), tmpdir_path);
476
477    // make path 500 chars longer so that buffer doubling in getcwd
478    // kicks in.  Note: One path cannot be longer than 255 bytes
479    // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually
480    // 4096 on linux, 1024 on macos)
481    let mut inner_tmp_dir = tmpdir_path;
482    for _ in 0..5 {
483        let newdir = "a".repeat(100);
484        inner_tmp_dir.push(newdir);
485        mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU)
486            .expect("assertion failed");
487    }
488    chdir(inner_tmp_dir.as_path()).expect("assertion failed");
489    assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
490}
491
492#[test]
493fn test_chown() {
494    // Testing for anything other than our own UID/GID is hard.
495    let uid = Some(getuid());
496    let gid = Some(getgid());
497
498    let tempdir = tempdir().unwrap();
499    let path = tempdir.path().join("file");
500    {
501        File::create(&path).unwrap();
502    }
503
504    chown(&path, uid, gid).unwrap();
505    chown(&path, uid, None).unwrap();
506    chown(&path, None, gid).unwrap();
507
508    fs::remove_file(&path).unwrap();
509    chown(&path, uid, gid).unwrap_err();
510}
511
512#[test]
513fn test_fchown() {
514    // Testing for anything other than our own UID/GID is hard.
515    let uid = Some(getuid());
516    let gid = Some(getgid());
517
518    let path = tempfile().unwrap();
519    let fd = path.as_raw_fd();
520
521    fchown(fd, uid, gid).unwrap();
522    fchown(fd, uid, None).unwrap();
523    fchown(fd, None, gid).unwrap();
524    fchown(999999999, uid, gid).unwrap_err();
525}
526
527#[test]
528#[cfg(not(target_os = "redox"))]
529fn test_fchownat() {
530    let _dr = crate::DirRestore::new();
531    // Testing for anything other than our own UID/GID is hard.
532    let uid = Some(getuid());
533    let gid = Some(getgid());
534
535    let tempdir = tempdir().unwrap();
536    let path = tempdir.path().join("file");
537    {
538        File::create(&path).unwrap();
539    }
540
541    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
542
543    fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink)
544        .unwrap();
545
546    chdir(tempdir.path()).unwrap();
547    fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap();
548
549    fs::remove_file(&path).unwrap();
550    fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap_err();
551}
552
553#[test]
554fn test_lseek() {
555    const CONTENTS: &[u8] = b"abcdef123456";
556    let mut tmp = tempfile().unwrap();
557    tmp.write_all(CONTENTS).unwrap();
558    let tmpfd = tmp.into_raw_fd();
559
560    let offset: off_t = 5;
561    lseek(tmpfd, offset, Whence::SeekSet).unwrap();
562
563    let mut buf = [0u8; 7];
564    crate::read_exact(tmpfd, &mut buf);
565    assert_eq!(b"f123456", &buf);
566
567    close(tmpfd).unwrap();
568}
569
570#[cfg(any(target_os = "linux", target_os = "android"))]
571#[test]
572fn test_lseek64() {
573    const CONTENTS: &[u8] = b"abcdef123456";
574    let mut tmp = tempfile().unwrap();
575    tmp.write_all(CONTENTS).unwrap();
576    let tmpfd = tmp.into_raw_fd();
577
578    lseek64(tmpfd, 5, Whence::SeekSet).unwrap();
579
580    let mut buf = [0u8; 7];
581    crate::read_exact(tmpfd, &mut buf);
582    assert_eq!(b"f123456", &buf);
583
584    close(tmpfd).unwrap();
585}
586
587cfg_if! {
588    if #[cfg(any(target_os = "android", target_os = "linux"))] {
589        macro_rules! require_acct{
590            () => {
591                require_capability!("test_acct", CAP_SYS_PACCT);
592            }
593        }
594    } else if #[cfg(target_os = "freebsd")] {
595        macro_rules! require_acct{
596            () => {
597                skip_if_not_root!("test_acct");
598                skip_if_jailed!("test_acct");
599            }
600        }
601    } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "haiku")))] {
602        macro_rules! require_acct{
603            () => {
604                skip_if_not_root!("test_acct");
605            }
606        }
607    }
608}
609
610#[test]
611#[cfg(not(any(
612    target_os = "redox",
613    target_os = "fuchsia",
614    target_os = "haiku"
615)))]
616fn test_acct() {
617    use std::process::Command;
618    use std::{thread, time};
619    use tempfile::NamedTempFile;
620
621    let _m = crate::FORK_MTX.lock();
622    require_acct!();
623
624    let file = NamedTempFile::new().unwrap();
625    let path = file.path().to_str().unwrap();
626
627    acct::enable(path).unwrap();
628
629    loop {
630        Command::new("echo").arg("Hello world").output().unwrap();
631        let len = fs::metadata(path).unwrap().len();
632        if len > 0 {
633            break;
634        }
635        thread::sleep(time::Duration::from_millis(10));
636    }
637    acct::disable().unwrap();
638}
639
640#[test]
641fn test_fpathconf_limited() {
642    let f = tempfile().unwrap();
643    // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
644    let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
645    assert!(
646        path_max
647            .expect("fpathconf failed")
648            .expect("PATH_MAX is unlimited")
649            > 0
650    );
651}
652
653#[test]
654fn test_pathconf_limited() {
655    // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
656    let path_max = pathconf("/", PathconfVar::PATH_MAX);
657    assert!(
658        path_max
659            .expect("pathconf failed")
660            .expect("PATH_MAX is unlimited")
661            > 0
662    );
663}
664
665#[test]
666fn test_sysconf_limited() {
667    // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
668    let open_max = sysconf(SysconfVar::OPEN_MAX);
669    assert!(
670        open_max
671            .expect("sysconf failed")
672            .expect("OPEN_MAX is unlimited")
673            > 0
674    );
675}
676
677#[cfg(target_os = "freebsd")]
678#[test]
679fn test_sysconf_unsupported() {
680    // I know of no sysconf variables that are unsupported everywhere, but
681    // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms
682    // we test.
683    let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
684    assert!(open_max.expect("sysconf failed").is_none())
685}
686
687#[cfg(any(
688    target_os = "android",
689    target_os = "dragonfly",
690    target_os = "freebsd",
691    target_os = "linux",
692    target_os = "openbsd"
693))]
694#[test]
695fn test_getresuid() {
696    let resuids = getresuid().unwrap();
697    assert_ne!(resuids.real.as_raw(), libc::uid_t::MAX);
698    assert_ne!(resuids.effective.as_raw(), libc::uid_t::MAX);
699    assert_ne!(resuids.saved.as_raw(), libc::uid_t::MAX);
700}
701
702#[cfg(any(
703    target_os = "android",
704    target_os = "dragonfly",
705    target_os = "freebsd",
706    target_os = "linux",
707    target_os = "openbsd"
708))]
709#[test]
710fn test_getresgid() {
711    let resgids = getresgid().unwrap();
712    assert_ne!(resgids.real.as_raw(), libc::gid_t::MAX);
713    assert_ne!(resgids.effective.as_raw(), libc::gid_t::MAX);
714    assert_ne!(resgids.saved.as_raw(), libc::gid_t::MAX);
715}
716
717// Test that we can create a pair of pipes.  No need to verify that they pass
718// data; that's the domain of the OS, not nix.
719#[test]
720fn test_pipe() {
721    let (fd0, fd1) = pipe().unwrap();
722    let m0 = stat::SFlag::from_bits_truncate(
723        stat::fstat(fd0).unwrap().st_mode as mode_t,
724    );
725    // S_IFIFO means it's a pipe
726    assert_eq!(m0, SFlag::S_IFIFO);
727    let m1 = stat::SFlag::from_bits_truncate(
728        stat::fstat(fd1).unwrap().st_mode as mode_t,
729    );
730    assert_eq!(m1, SFlag::S_IFIFO);
731}
732
733// pipe2(2) is the same as pipe(2), except it allows setting some flags.  Check
734// that we can set a flag.
735#[cfg(any(
736    target_os = "android",
737    target_os = "dragonfly",
738    target_os = "emscripten",
739    target_os = "freebsd",
740    target_os = "illumos",
741    target_os = "linux",
742    target_os = "netbsd",
743    target_os = "openbsd",
744    target_os = "redox",
745    target_os = "solaris"
746))]
747#[test]
748fn test_pipe2() {
749    use nix::fcntl::{fcntl, FcntlArg, FdFlag};
750
751    let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
752    let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap());
753    assert!(f0.contains(FdFlag::FD_CLOEXEC));
754    let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap());
755    assert!(f1.contains(FdFlag::FD_CLOEXEC));
756}
757
758#[test]
759#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
760fn test_truncate() {
761    let tempdir = tempdir().unwrap();
762    let path = tempdir.path().join("file");
763
764    {
765        let mut tmp = File::create(&path).unwrap();
766        const CONTENTS: &[u8] = b"12345678";
767        tmp.write_all(CONTENTS).unwrap();
768    }
769
770    truncate(&path, 4).unwrap();
771
772    let metadata = fs::metadata(&path).unwrap();
773    assert_eq!(4, metadata.len());
774}
775
776#[test]
777fn test_ftruncate() {
778    let tempdir = tempdir().unwrap();
779    let path = tempdir.path().join("file");
780
781    let tmpfd = {
782        let mut tmp = File::create(&path).unwrap();
783        const CONTENTS: &[u8] = b"12345678";
784        tmp.write_all(CONTENTS).unwrap();
785        tmp.into_raw_fd()
786    };
787
788    ftruncate(tmpfd, 2).unwrap();
789    close(tmpfd).unwrap();
790
791    let metadata = fs::metadata(&path).unwrap();
792    assert_eq!(2, metadata.len());
793}
794
795// Used in `test_alarm`.
796#[cfg(not(target_os = "redox"))]
797static mut ALARM_CALLED: bool = false;
798
799// Used in `test_alarm`.
800#[cfg(not(target_os = "redox"))]
801pub extern "C" fn alarm_signal_handler(raw_signal: libc::c_int) {
802    assert_eq!(
803        raw_signal,
804        libc::SIGALRM,
805        "unexpected signal: {}",
806        raw_signal
807    );
808    unsafe { ALARM_CALLED = true };
809}
810
811#[test]
812#[cfg(not(target_os = "redox"))]
813fn test_alarm() {
814    use std::{
815        thread,
816        time::{Duration, Instant},
817    };
818
819    // Maybe other tests that fork interfere with this one?
820    let _m = crate::SIGNAL_MTX.lock();
821
822    let handler = SigHandler::Handler(alarm_signal_handler);
823    let signal_action =
824        SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
825    let old_handler = unsafe {
826        sigaction(Signal::SIGALRM, &signal_action)
827            .expect("unable to set signal handler for alarm")
828    };
829
830    // Set an alarm.
831    assert_eq!(alarm::set(60), None);
832
833    // Overwriting an alarm should return the old alarm.
834    assert_eq!(alarm::set(1), Some(60));
835
836    // We should be woken up after 1 second by the alarm, so we'll sleep for 3
837    // seconds to be sure.
838    let starttime = Instant::now();
839    loop {
840        thread::sleep(Duration::from_millis(100));
841        if unsafe { ALARM_CALLED } {
842            break;
843        }
844        if starttime.elapsed() > Duration::from_secs(3) {
845            panic!("Timeout waiting for SIGALRM");
846        }
847    }
848
849    // Reset the signal.
850    unsafe {
851        sigaction(Signal::SIGALRM, &old_handler)
852            .expect("unable to set signal handler for alarm");
853    }
854}
855
856#[test]
857#[cfg(not(target_os = "redox"))]
858fn test_canceling_alarm() {
859    let _m = crate::SIGNAL_MTX.lock();
860
861    assert_eq!(alarm::cancel(), None);
862
863    assert_eq!(alarm::set(60), None);
864    assert_eq!(alarm::cancel(), Some(60));
865}
866
867#[test]
868#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
869fn test_symlinkat() {
870    let _m = crate::CWD_LOCK.read();
871
872    let tempdir = tempdir().unwrap();
873
874    let target = tempdir.path().join("a");
875    let linkpath = tempdir.path().join("b");
876    symlinkat(&target, None, &linkpath).unwrap();
877    assert_eq!(
878        readlink(&linkpath).unwrap().to_str().unwrap(),
879        target.to_str().unwrap()
880    );
881
882    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
883    let target = "c";
884    let linkpath = "d";
885    symlinkat(target, Some(dirfd), linkpath).unwrap();
886    assert_eq!(
887        readlink(&tempdir.path().join(linkpath))
888            .unwrap()
889            .to_str()
890            .unwrap(),
891        target
892    );
893}
894
895#[test]
896#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
897fn test_linkat_file() {
898    let tempdir = tempdir().unwrap();
899    let oldfilename = "foo.txt";
900    let oldfilepath = tempdir.path().join(oldfilename);
901
902    let newfilename = "bar.txt";
903    let newfilepath = tempdir.path().join(newfilename);
904
905    // Create file
906    File::create(oldfilepath).unwrap();
907
908    // Get file descriptor for base directory
909    let dirfd =
910        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
911            .unwrap();
912
913    // Attempt hard link file at relative path
914    linkat(
915        Some(dirfd),
916        oldfilename,
917        Some(dirfd),
918        newfilename,
919        LinkatFlags::SymlinkFollow,
920    )
921    .unwrap();
922    assert!(newfilepath.exists());
923}
924
925#[test]
926#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
927fn test_linkat_olddirfd_none() {
928    let _dr = crate::DirRestore::new();
929
930    let tempdir_oldfile = tempdir().unwrap();
931    let oldfilename = "foo.txt";
932    let oldfilepath = tempdir_oldfile.path().join(oldfilename);
933
934    let tempdir_newfile = tempdir().unwrap();
935    let newfilename = "bar.txt";
936    let newfilepath = tempdir_newfile.path().join(newfilename);
937
938    // Create file
939    File::create(oldfilepath).unwrap();
940
941    // Get file descriptor for base directory of new file
942    let dirfd = fcntl::open(
943        tempdir_newfile.path(),
944        fcntl::OFlag::empty(),
945        stat::Mode::empty(),
946    )
947    .unwrap();
948
949    // Attempt hard link file using curent working directory as relative path for old file path
950    chdir(tempdir_oldfile.path()).unwrap();
951    linkat(
952        None,
953        oldfilename,
954        Some(dirfd),
955        newfilename,
956        LinkatFlags::SymlinkFollow,
957    )
958    .unwrap();
959    assert!(newfilepath.exists());
960}
961
962#[test]
963#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
964fn test_linkat_newdirfd_none() {
965    let _dr = crate::DirRestore::new();
966
967    let tempdir_oldfile = tempdir().unwrap();
968    let oldfilename = "foo.txt";
969    let oldfilepath = tempdir_oldfile.path().join(oldfilename);
970
971    let tempdir_newfile = tempdir().unwrap();
972    let newfilename = "bar.txt";
973    let newfilepath = tempdir_newfile.path().join(newfilename);
974
975    // Create file
976    File::create(oldfilepath).unwrap();
977
978    // Get file descriptor for base directory of old file
979    let dirfd = fcntl::open(
980        tempdir_oldfile.path(),
981        fcntl::OFlag::empty(),
982        stat::Mode::empty(),
983    )
984    .unwrap();
985
986    // Attempt hard link file using current working directory as relative path for new file path
987    chdir(tempdir_newfile.path()).unwrap();
988    linkat(
989        Some(dirfd),
990        oldfilename,
991        None,
992        newfilename,
993        LinkatFlags::SymlinkFollow,
994    )
995    .unwrap();
996    assert!(newfilepath.exists());
997}
998
999#[test]
1000#[cfg(not(any(
1001    target_os = "ios",
1002    target_os = "macos",
1003    target_os = "redox",
1004    target_os = "haiku"
1005)))]
1006fn test_linkat_no_follow_symlink() {
1007    let _m = crate::CWD_LOCK.read();
1008
1009    let tempdir = tempdir().unwrap();
1010    let oldfilename = "foo.txt";
1011    let oldfilepath = tempdir.path().join(oldfilename);
1012
1013    let symoldfilename = "symfoo.txt";
1014    let symoldfilepath = tempdir.path().join(symoldfilename);
1015
1016    let newfilename = "nofollowsymbar.txt";
1017    let newfilepath = tempdir.path().join(newfilename);
1018
1019    // Create file
1020    File::create(&oldfilepath).unwrap();
1021
1022    // Create symlink to file
1023    symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
1024
1025    // Get file descriptor for base directory
1026    let dirfd =
1027        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
1028            .unwrap();
1029
1030    // Attempt link symlink of file at relative path
1031    linkat(
1032        Some(dirfd),
1033        symoldfilename,
1034        Some(dirfd),
1035        newfilename,
1036        LinkatFlags::NoSymlinkFollow,
1037    )
1038    .unwrap();
1039
1040    // Assert newfile is actually a symlink to oldfile.
1041    assert_eq!(
1042        readlink(&newfilepath).unwrap().to_str().unwrap(),
1043        oldfilepath.to_str().unwrap()
1044    );
1045}
1046
1047#[test]
1048#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
1049fn test_linkat_follow_symlink() {
1050    let _m = crate::CWD_LOCK.read();
1051
1052    let tempdir = tempdir().unwrap();
1053    let oldfilename = "foo.txt";
1054    let oldfilepath = tempdir.path().join(oldfilename);
1055
1056    let symoldfilename = "symfoo.txt";
1057    let symoldfilepath = tempdir.path().join(symoldfilename);
1058
1059    let newfilename = "nofollowsymbar.txt";
1060    let newfilepath = tempdir.path().join(newfilename);
1061
1062    // Create file
1063    File::create(&oldfilepath).unwrap();
1064
1065    // Create symlink to file
1066    symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
1067
1068    // Get file descriptor for base directory
1069    let dirfd =
1070        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
1071            .unwrap();
1072
1073    // Attempt link target of symlink of file at relative path
1074    linkat(
1075        Some(dirfd),
1076        symoldfilename,
1077        Some(dirfd),
1078        newfilename,
1079        LinkatFlags::SymlinkFollow,
1080    )
1081    .unwrap();
1082
1083    let newfilestat = stat::stat(&newfilepath).unwrap();
1084
1085    // Check the file type of the new link
1086    assert_eq!(
1087        (stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t)
1088            & SFlag::S_IFMT),
1089        SFlag::S_IFREG
1090    );
1091
1092    // Check the number of hard links to the original file
1093    assert_eq!(newfilestat.st_nlink, 2);
1094}
1095
1096#[test]
1097#[cfg(not(target_os = "redox"))]
1098fn test_unlinkat_dir_noremovedir() {
1099    let tempdir = tempdir().unwrap();
1100    let dirname = "foo_dir";
1101    let dirpath = tempdir.path().join(dirname);
1102
1103    // Create dir
1104    DirBuilder::new().recursive(true).create(dirpath).unwrap();
1105
1106    // Get file descriptor for base directory
1107    let dirfd =
1108        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
1109            .unwrap();
1110
1111    // Attempt unlink dir at relative path without proper flag
1112    let err_result =
1113        unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
1114    assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);
1115}
1116
1117#[test]
1118#[cfg(not(target_os = "redox"))]
1119fn test_unlinkat_dir_removedir() {
1120    let tempdir = tempdir().unwrap();
1121    let dirname = "foo_dir";
1122    let dirpath = tempdir.path().join(dirname);
1123
1124    // Create dir
1125    DirBuilder::new().recursive(true).create(&dirpath).unwrap();
1126
1127    // Get file descriptor for base directory
1128    let dirfd =
1129        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
1130            .unwrap();
1131
1132    // Attempt unlink dir at relative path with proper flag
1133    unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
1134    assert!(!dirpath.exists());
1135}
1136
1137#[test]
1138#[cfg(not(target_os = "redox"))]
1139fn test_unlinkat_file() {
1140    let tempdir = tempdir().unwrap();
1141    let filename = "foo.txt";
1142    let filepath = tempdir.path().join(filename);
1143
1144    // Create file
1145    File::create(&filepath).unwrap();
1146
1147    // Get file descriptor for base directory
1148    let dirfd =
1149        fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
1150            .unwrap();
1151
1152    // Attempt unlink file at relative path
1153    unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
1154    assert!(!filepath.exists());
1155}
1156
1157#[test]
1158fn test_access_not_existing() {
1159    let tempdir = tempdir().unwrap();
1160    let dir = tempdir.path().join("does_not_exist.txt");
1161    assert_eq!(
1162        access(&dir, AccessFlags::F_OK).err().unwrap(),
1163        Errno::ENOENT
1164    );
1165}
1166
1167#[test]
1168fn test_access_file_exists() {
1169    let tempdir = tempdir().unwrap();
1170    let path = tempdir.path().join("does_exist.txt");
1171    let _file = File::create(path.clone()).unwrap();
1172    access(&path, AccessFlags::R_OK | AccessFlags::W_OK)
1173        .expect("assertion failed");
1174}
1175
1176//Clippy false positive https://github.com/rust-lang/rust-clippy/issues/9111
1177#[allow(clippy::needless_borrow)]
1178#[cfg(not(target_os = "redox"))]
1179#[test]
1180fn test_user_into_passwd() {
1181    // get the UID of the "nobody" user
1182    #[cfg(not(target_os = "haiku"))]
1183    let test_username = "nobody";
1184    // "nobody" unavailable on haiku
1185    #[cfg(target_os = "haiku")]
1186    let test_username = "user";
1187
1188    let nobody = User::from_name(test_username).unwrap().unwrap();
1189    let pwd: libc::passwd = nobody.into();
1190    let _: User = (&pwd).into();
1191}
1192
1193/// Tests setting the filesystem UID with `setfsuid`.
1194#[cfg(any(target_os = "linux", target_os = "android"))]
1195#[test]
1196fn test_setfsuid() {
1197    use std::os::unix::fs::PermissionsExt;
1198    use std::{fs, io, thread};
1199    require_capability!("test_setfsuid", CAP_SETUID);
1200
1201    // get the UID of the "nobody" user
1202    let nobody = User::from_name("nobody").unwrap().unwrap();
1203
1204    // create a temporary file with permissions '-rw-r-----'
1205    let file = tempfile::NamedTempFile::new_in("/var/tmp").unwrap();
1206    let temp_path = file.into_temp_path();
1207    let temp_path_2 = temp_path.to_path_buf();
1208    let mut permissions = fs::metadata(&temp_path).unwrap().permissions();
1209    permissions.set_mode(0o640);
1210
1211    // spawn a new thread where to test setfsuid
1212    thread::spawn(move || {
1213        // set filesystem UID
1214        let fuid = setfsuid(nobody.uid);
1215        // trying to open the temporary file should fail with EACCES
1216        let res = fs::File::open(&temp_path);
1217        let err = res.expect_err("assertion failed");
1218        assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
1219
1220        // assert fuid actually changes
1221        let prev_fuid = setfsuid(Uid::from_raw(-1i32 as u32));
1222        assert_ne!(prev_fuid, fuid);
1223    })
1224    .join()
1225    .unwrap();
1226
1227    // open the temporary file with the current thread filesystem UID
1228    fs::File::open(temp_path_2).unwrap();
1229}
1230
1231#[test]
1232#[cfg(not(any(
1233    target_os = "redox",
1234    target_os = "fuchsia",
1235    target_os = "haiku"
1236)))]
1237fn test_ttyname() {
1238    let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
1239    assert!(fd.as_raw_fd() > 0);
1240
1241    // on linux, we can just call ttyname on the pty master directly, but
1242    // apparently osx requires that ttyname is called on a slave pty (can't
1243    // find this documented anywhere, but it seems to empirically be the case)
1244    grantpt(&fd).expect("grantpt failed");
1245    unlockpt(&fd).expect("unlockpt failed");
1246    let sname = unsafe { ptsname(&fd) }.expect("ptsname failed");
1247    let fds = open(Path::new(&sname), OFlag::O_RDWR, stat::Mode::empty())
1248        .expect("open failed");
1249    assert!(fds > 0);
1250
1251    let name = ttyname(fds).expect("ttyname failed");
1252    assert!(name.starts_with("/dev"));
1253}
1254
1255#[test]
1256#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
1257fn test_ttyname_not_pty() {
1258    let fd = File::open("/dev/zero").unwrap();
1259    assert!(fd.as_raw_fd() > 0);
1260    assert_eq!(ttyname(fd.as_raw_fd()), Err(Errno::ENOTTY));
1261}
1262
1263#[test]
1264#[cfg(not(any(
1265    target_os = "redox",
1266    target_os = "fuchsia",
1267    target_os = "haiku"
1268)))]
1269fn test_ttyname_invalid_fd() {
1270    assert_eq!(ttyname(-1), Err(Errno::EBADF));
1271}
1272
1273#[test]
1274#[cfg(any(
1275    target_os = "macos",
1276    target_os = "ios",
1277    target_os = "freebsd",
1278    target_os = "openbsd",
1279    target_os = "netbsd",
1280    target_os = "dragonfly",
1281))]
1282fn test_getpeereid() {
1283    use std::os::unix::net::UnixStream;
1284    let (sock_a, sock_b) = UnixStream::pair().unwrap();
1285
1286    let (uid_a, gid_a) = getpeereid(sock_a.as_raw_fd()).unwrap();
1287    let (uid_b, gid_b) = getpeereid(sock_b.as_raw_fd()).unwrap();
1288
1289    let uid = geteuid();
1290    let gid = getegid();
1291
1292    assert_eq!(uid, uid_a);
1293    assert_eq!(gid, gid_a);
1294    assert_eq!(uid_a, uid_b);
1295    assert_eq!(gid_a, gid_b);
1296}
1297
1298#[test]
1299#[cfg(any(
1300    target_os = "macos",
1301    target_os = "ios",
1302    target_os = "freebsd",
1303    target_os = "openbsd",
1304    target_os = "netbsd",
1305    target_os = "dragonfly",
1306))]
1307fn test_getpeereid_invalid_fd() {
1308    // getpeereid is not POSIX, so error codes are inconsistent between different Unices.
1309    getpeereid(-1).expect_err("assertion failed");
1310}
1311
1312#[test]
1313#[cfg(not(target_os = "redox"))]
1314fn test_faccessat_none_not_existing() {
1315    use nix::fcntl::AtFlags;
1316    let tempdir = tempfile::tempdir().unwrap();
1317    let dir = tempdir.path().join("does_not_exist.txt");
1318    assert_eq!(
1319        faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty())
1320            .err()
1321            .unwrap(),
1322        Errno::ENOENT
1323    );
1324}
1325
1326#[test]
1327#[cfg(not(target_os = "redox"))]
1328fn test_faccessat_not_existing() {
1329    use nix::fcntl::AtFlags;
1330    let tempdir = tempfile::tempdir().unwrap();
1331    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
1332    let not_exist_file = "does_not_exist.txt";
1333    assert_eq!(
1334        faccessat(
1335            Some(dirfd),
1336            not_exist_file,
1337            AccessFlags::F_OK,
1338            AtFlags::empty(),
1339        )
1340        .err()
1341        .unwrap(),
1342        Errno::ENOENT
1343    );
1344}
1345
1346#[test]
1347#[cfg(not(target_os = "redox"))]
1348fn test_faccessat_none_file_exists() {
1349    use nix::fcntl::AtFlags;
1350    let tempdir = tempfile::tempdir().unwrap();
1351    let path = tempdir.path().join("does_exist.txt");
1352    let _file = File::create(path.clone()).unwrap();
1353    assert!(faccessat(
1354        None,
1355        &path,
1356        AccessFlags::R_OK | AccessFlags::W_OK,
1357        AtFlags::empty(),
1358    )
1359    .is_ok());
1360}
1361
1362#[test]
1363#[cfg(not(target_os = "redox"))]
1364fn test_faccessat_file_exists() {
1365    use nix::fcntl::AtFlags;
1366    let tempdir = tempfile::tempdir().unwrap();
1367    let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
1368    let exist_file = "does_exist.txt";
1369    let path = tempdir.path().join(exist_file);
1370    let _file = File::create(path.clone()).unwrap();
1371    assert!(faccessat(
1372        Some(dirfd),
1373        &path,
1374        AccessFlags::R_OK | AccessFlags::W_OK,
1375        AtFlags::empty(),
1376    )
1377    .is_ok());
1378}
1379
1380#[test]
1381#[cfg(any(
1382    all(target_os = "linux", not(target_env = "uclibc")),
1383    target_os = "freebsd",
1384    target_os = "dragonfly"
1385))]
1386fn test_eaccess_not_existing() {
1387    let tempdir = tempdir().unwrap();
1388    let dir = tempdir.path().join("does_not_exist.txt");
1389    assert_eq!(
1390        eaccess(&dir, AccessFlags::F_OK).err().unwrap(),
1391        Errno::ENOENT
1392    );
1393}
1394
1395#[test]
1396#[cfg(any(
1397    all(target_os = "linux", not(target_env = "uclibc")),
1398    target_os = "freebsd",
1399    target_os = "dragonfly"
1400))]
1401fn test_eaccess_file_exists() {
1402    let tempdir = tempdir().unwrap();
1403    let path = tempdir.path().join("does_exist.txt");
1404    let _file = File::create(path.clone()).unwrap();
1405    eaccess(&path, AccessFlags::R_OK | AccessFlags::W_OK)
1406        .expect("assertion failed");
1407}
1408