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