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