1 use nix::fcntl::{self, fcntl, FcntlArg, FdFlag, open, OFlag, readlink};
2 use nix::unistd::*;
3 use nix::unistd::ForkResult::*;
4 use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction};
5 use nix::sys::wait::*;
6 use nix::sys::stat::{self, Mode, SFlag};
7 use nix::errno::Errno;
8 use nix::Error;
9 use std::{env, iter};
10 use std::ffi::CString;
11 use std::fs::{self, DirBuilder, File};
12 use std::io::Write;
13 use std::os::unix::prelude::*;
14 use tempfile::{self, tempfile};
15 use libc::{self, _exit, off_t};
16 
17 #[test]
18 #[cfg(not(any(target_os = "netbsd")))]
test_fork_and_waitpid()19 fn test_fork_and_waitpid() {
20     let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
21 
22     // Safe: Child only calls `_exit`, which is signal-safe
23     match fork().expect("Error: Fork Failed") {
24         Child => unsafe { _exit(0) },
25         Parent { child } => {
26             // assert that child was created and pid > 0
27             let child_raw: ::libc::pid_t = child.into();
28             assert!(child_raw > 0);
29             let wait_status = waitpid(child, None);
30             match wait_status {
31                 // assert that waitpid returned correct status and the pid is the one of the child
32                 Ok(WaitStatus::Exited(pid_t, _)) =>  assert!(pid_t == child),
33 
34                 // panic, must never happen
35                 s @ Ok(_) => panic!("Child exited {:?}, should never happen", s),
36 
37                 // panic, waitpid should never fail
38                 Err(s) => panic!("Error: waitpid returned Err({:?}", s)
39             }
40 
41         },
42     }
43 }
44 
45 #[test]
test_wait()46 fn test_wait() {
47     // Grab FORK_MTX so wait doesn't reap a different test's child process
48     let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
49 
50     // Safe: Child only calls `_exit`, which is signal-safe
51     match fork().expect("Error: Fork Failed") {
52         Child => unsafe { _exit(0) },
53         Parent { child } => {
54             let wait_status = wait();
55 
56             // just assert that (any) one child returns with WaitStatus::Exited
57             assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
58         },
59     }
60 }
61 
62 #[test]
test_mkstemp()63 fn test_mkstemp() {
64     let mut path = env::temp_dir();
65     path.push("nix_tempfile.XXXXXX");
66 
67     let result = mkstemp(&path);
68     match result {
69         Ok((fd, path)) => {
70             close(fd).unwrap();
71             unlink(path.as_path()).unwrap();
72         },
73         Err(e) => panic!("mkstemp failed: {}", e)
74     }
75 }
76 
77 #[test]
test_mkstemp_directory()78 fn test_mkstemp_directory() {
79     // mkstemp should fail if a directory is given
80     assert!(mkstemp(&env::temp_dir()).is_err());
81 }
82 
83 #[test]
test_mkfifo()84 fn test_mkfifo() {
85     let tempdir = tempfile::tempdir().unwrap();
86     let mkfifo_fifo = tempdir.path().join("mkfifo_fifo");
87 
88     mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap();
89 
90     let stats = stat::stat(&mkfifo_fifo).unwrap();
91     let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
92     assert!(typ == SFlag::S_IFIFO);
93 }
94 
95 #[test]
test_mkfifo_directory()96 fn test_mkfifo_directory() {
97     // mkfifo should fail if a directory is given
98     assert!(mkfifo(&env::temp_dir(), Mode::S_IRUSR).is_err());
99 }
100 
101 #[test]
test_getpid()102 fn test_getpid() {
103     let pid: ::libc::pid_t = getpid().into();
104     let ppid: ::libc::pid_t = getppid().into();
105     assert!(pid > 0);
106     assert!(ppid > 0);
107 }
108 
109 #[test]
test_getsid()110 fn test_getsid() {
111     let none_sid: ::libc::pid_t = getsid(None).unwrap().into();
112     let pid_sid: ::libc::pid_t = getsid(Some(getpid())).unwrap().into();
113     assert!(none_sid > 0);
114     assert!(none_sid == pid_sid);
115 }
116 
117 #[cfg(any(target_os = "linux", target_os = "android"))]
118 mod linux_android {
119     use nix::unistd::gettid;
120 
121     #[test]
test_gettid()122     fn test_gettid() {
123         let tid: ::libc::pid_t = gettid().into();
124         assert!(tid > 0);
125     }
126 }
127 
128 #[test]
129 // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
130 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
test_setgroups()131 fn test_setgroups() {
132     // Skip this test when not run as root as `setgroups()` requires root.
133     skip_if_not_root!("test_setgroups");
134 
135     let _m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
136 
137     // Save the existing groups
138     let old_groups = getgroups().unwrap();
139 
140     // Set some new made up groups
141     let groups = [Gid::from_raw(123), Gid::from_raw(456)];
142     setgroups(&groups).unwrap();
143 
144     let new_groups = getgroups().unwrap();
145     assert_eq!(new_groups, groups);
146 
147     // Revert back to the old groups
148     setgroups(&old_groups).unwrap();
149 }
150 
151 #[test]
152 // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
153 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
test_initgroups()154 fn test_initgroups() {
155     // Skip this test when not run as root as `initgroups()` and `setgroups()`
156     // require root.
157     skip_if_not_root!("test_initgroups");
158 
159     let _m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
160 
161     // Save the existing groups
162     let old_groups = getgroups().unwrap();
163 
164     // It doesn't matter if the root user is not called "root" or if a user
165     // called "root" doesn't exist. We are just checking that the extra,
166     // made-up group, `123`, is set.
167     // FIXME: Test the other half of initgroups' functionality: whether the
168     // groups that the user belongs to are also set.
169     let user = CString::new("root").unwrap();
170     let group = Gid::from_raw(123);
171     let group_list = getgrouplist(&user, group).unwrap();
172     assert!(group_list.contains(&group));
173 
174     initgroups(&user, group).unwrap();
175 
176     let new_groups = getgroups().unwrap();
177     assert_eq!(new_groups, group_list);
178 
179     // Revert back to the old groups
180     setgroups(&old_groups).unwrap();
181 }
182 
183 macro_rules! execve_test_factory(
184     ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
185     #[test]
186     fn $test_name() {
187         let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
188         // The `exec`d process will write to `writer`, and we'll read that
189         // data from `reader`.
190         let (reader, writer) = pipe().unwrap();
191 
192         // Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function.
193         // NOTE: Technically, this makes the macro unsafe to use because you could pass anything.
194         //       The tests make sure not to do that, though.
195         match fork().unwrap() {
196             Child => {
197                 // Close stdout.
198                 close(1).unwrap();
199                 // Make `writer` be the stdout of the new process.
200                 dup(writer).unwrap();
201                 // exec!
202                 $syscall(
203                     $exe,
204                     $(&CString::new($pathname).unwrap(), )*
205                     &[CString::new(b"".as_ref()).unwrap(),
206                       CString::new(b"-c".as_ref()).unwrap(),
207                       CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz"
208                                    .as_ref()).unwrap()],
209                     &[CString::new(b"foo=bar".as_ref()).unwrap(),
210                       CString::new(b"baz=quux".as_ref()).unwrap()]
211                     $(, $flags)*).unwrap();
212             },
213             Parent { child } => {
214                 // Wait for the child to exit.
215                 waitpid(child, None).unwrap();
216                 // Read 1024 bytes.
217                 let mut buf = [0u8; 1024];
218                 read(reader, &mut buf).unwrap();
219                 // It should contain the things we printed using `/bin/sh`.
220                 let string = String::from_utf8_lossy(&buf);
221                 assert!(string.contains("nix!!!"));
222                 assert!(string.contains("foo=bar"));
223                 assert!(string.contains("baz=quux"));
224             }
225         }
226     }
227     )
228 );
229 
230 cfg_if!{
231     if #[cfg(target_os = "android")] {
232         execve_test_factory!(test_execve, execve, &CString::new("/system/bin/sh").unwrap());
233         execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
234     } else if #[cfg(any(target_os = "freebsd",
235                         target_os = "linux"))] {
236         execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap());
237         execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
238     } else if #[cfg(any(target_os = "dragonfly",
239                         target_os = "ios",
240                         target_os = "macos",
241                         target_os = "netbsd",
242                         target_os = "openbsd"))] {
243         execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap());
244         // No fexecve() on DragonFly, ios, macos, NetBSD, OpenBSD.
245         //
246         // Note for NetBSD and OpenBSD: although rust-lang/libc includes it
247         // (under unix/bsd/netbsdlike/) fexecve is not currently implemented on
248         // NetBSD nor on OpenBSD.
249     }
250 }
251 
252 #[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))]
253 execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap());
254 
255 cfg_if!{
256     if #[cfg(target_os = "android")] {
257         use nix::fcntl::AtFlags;
258         execve_test_factory!(test_execveat_empty, execveat, File::open("/system/bin/sh").unwrap().into_raw_fd(),
259                              "", AtFlags::AT_EMPTY_PATH);
260         execve_test_factory!(test_execveat_relative, execveat, File::open("/system/bin/").unwrap().into_raw_fd(),
261                              "./sh", AtFlags::empty());
262         execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(),
263                              "/system/bin/sh", AtFlags::empty());
264     } else if #[cfg(all(target_os = "linux"), any(target_arch ="x86_64", target_arch ="x86"))] {
265         use nix::fcntl::AtFlags;
266         execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(),
267                              "", AtFlags::AT_EMPTY_PATH);
268         execve_test_factory!(test_execveat_relative, execveat, File::open("/bin/").unwrap().into_raw_fd(),
269                              "./sh", AtFlags::empty());
270         execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(),
271                              "/bin/sh", AtFlags::empty());
272     }
273 }
274 
275 #[test]
test_fchdir()276 fn test_fchdir() {
277     // fchdir changes the process's cwd
278     let _dr = ::DirRestore::new();
279 
280     let tmpdir = tempfile::tempdir().unwrap();
281     let tmpdir_path = tmpdir.path().canonicalize().unwrap();
282     let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
283 
284     assert!(fchdir(tmpdir_fd).is_ok());
285     assert_eq!(getcwd().unwrap(), tmpdir_path);
286 
287     assert!(close(tmpdir_fd).is_ok());
288 }
289 
290 #[test]
test_getcwd()291 fn test_getcwd() {
292     // chdir changes the process's cwd
293     let _dr = ::DirRestore::new();
294 
295     let tmpdir = tempfile::tempdir().unwrap();
296     let tmpdir_path = tmpdir.path().canonicalize().unwrap();
297     assert!(chdir(&tmpdir_path).is_ok());
298     assert_eq!(getcwd().unwrap(), tmpdir_path);
299 
300     // make path 500 chars longer so that buffer doubling in getcwd
301     // kicks in.  Note: One path cannot be longer than 255 bytes
302     // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually
303     // 4096 on linux, 1024 on macos)
304     let mut inner_tmp_dir = tmpdir_path.to_path_buf();
305     for _ in 0..5 {
306         let newdir = iter::repeat("a").take(100).collect::<String>();
307         inner_tmp_dir.push(newdir);
308         assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok());
309     }
310     assert!(chdir(inner_tmp_dir.as_path()).is_ok());
311     assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
312 }
313 
314 #[test]
test_chown()315 fn test_chown() {
316     // Testing for anything other than our own UID/GID is hard.
317     let uid = Some(getuid());
318     let gid = Some(getgid());
319 
320     let tempdir = tempfile::tempdir().unwrap();
321     let path = tempdir.path().join("file");
322     {
323         File::create(&path).unwrap();
324     }
325 
326     chown(&path, uid, gid).unwrap();
327     chown(&path, uid, None).unwrap();
328     chown(&path, None, gid).unwrap();
329 
330     fs::remove_file(&path).unwrap();
331     chown(&path, uid, gid).unwrap_err();
332 }
333 
334 #[test]
test_fchownat()335 fn test_fchownat() {
336     let _dr = ::DirRestore::new();
337     // Testing for anything other than our own UID/GID is hard.
338     let uid = Some(getuid());
339     let gid = Some(getgid());
340 
341     let tempdir = tempfile::tempdir().unwrap();
342     let path = tempdir.path().join("file");
343     {
344         File::create(&path).unwrap();
345     }
346 
347     let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
348 
349     fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink).unwrap();
350 
351     chdir(tempdir.path()).unwrap();
352     fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap();
353 
354     fs::remove_file(&path).unwrap();
355     fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap_err();
356 }
357 
358 #[test]
test_lseek()359 fn test_lseek() {
360     const CONTENTS: &[u8] = b"abcdef123456";
361     let mut tmp = tempfile().unwrap();
362     tmp.write_all(CONTENTS).unwrap();
363     let tmpfd = tmp.into_raw_fd();
364 
365     let offset: off_t = 5;
366     lseek(tmpfd, offset, Whence::SeekSet).unwrap();
367 
368     let mut buf = [0u8; 7];
369     ::read_exact(tmpfd, &mut buf);
370     assert_eq!(b"f123456", &buf);
371 
372     close(tmpfd).unwrap();
373 }
374 
375 #[cfg(any(target_os = "linux", target_os = "android"))]
376 #[test]
test_lseek64()377 fn test_lseek64() {
378     const CONTENTS: &[u8] = b"abcdef123456";
379     let mut tmp = tempfile().unwrap();
380     tmp.write_all(CONTENTS).unwrap();
381     let tmpfd = tmp.into_raw_fd();
382 
383     lseek64(tmpfd, 5, Whence::SeekSet).unwrap();
384 
385     let mut buf = [0u8; 7];
386     ::read_exact(tmpfd, &mut buf);
387     assert_eq!(b"f123456", &buf);
388 
389     close(tmpfd).unwrap();
390 }
391 
392 cfg_if!{
393     if #[cfg(any(target_os = "android", target_os = "linux"))] {
394         macro_rules! require_acct{
395             () => {
396                 require_capability!(CAP_SYS_PACCT);
397             }
398         }
399     } else if #[cfg(target_os = "freebsd")] {
400         macro_rules! require_acct{
401             () => {
402                 skip_if_not_root!("test_acct");
403                 skip_if_jailed!("test_acct");
404             }
405         }
406     } else {
407         macro_rules! require_acct{
408             () => {
409                 skip_if_not_root!("test_acct");
410             }
411         }
412     }
413 }
414 
415 #[test]
test_acct()416 fn test_acct() {
417     use tempfile::NamedTempFile;
418     use std::process::Command;
419     use std::{thread, time};
420 
421     let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
422     require_acct!();
423 
424     let file = NamedTempFile::new().unwrap();
425     let path = file.path().to_str().unwrap();
426 
427     acct::enable(path).unwrap();
428 
429     loop {
430         Command::new("echo").arg("Hello world");
431         let len = fs::metadata(path).unwrap().len();
432         if len > 0 { break; }
433         thread::sleep(time::Duration::from_millis(10));
434     }
435     acct::disable().unwrap();
436 }
437 
438 #[test]
test_fpathconf_limited()439 fn test_fpathconf_limited() {
440     let f = tempfile().unwrap();
441     // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
442     let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
443     assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0);
444 }
445 
446 #[test]
test_pathconf_limited()447 fn test_pathconf_limited() {
448     // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
449     let path_max = pathconf("/", PathconfVar::PATH_MAX);
450     assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0);
451 }
452 
453 #[test]
test_sysconf_limited()454 fn test_sysconf_limited() {
455     // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
456     let open_max = sysconf(SysconfVar::OPEN_MAX);
457     assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0);
458 }
459 
460 #[cfg(target_os = "freebsd")]
461 #[test]
test_sysconf_unsupported()462 fn test_sysconf_unsupported() {
463     // I know of no sysconf variables that are unsupported everywhere, but
464     // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms
465     // we test.
466     let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
467     assert!(open_max.expect("sysconf failed").is_none())
468 }
469 
470 // Test that we can create a pair of pipes.  No need to verify that they pass
471 // data; that's the domain of the OS, not nix.
472 #[test]
test_pipe()473 fn test_pipe() {
474     let (fd0, fd1) = pipe().unwrap();
475     let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode);
476     // S_IFIFO means it's a pipe
477     assert_eq!(m0, SFlag::S_IFIFO);
478     let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode);
479     assert_eq!(m1, SFlag::S_IFIFO);
480 }
481 
482 // pipe2(2) is the same as pipe(2), except it allows setting some flags.  Check
483 // that we can set a flag.
484 #[test]
test_pipe2()485 fn test_pipe2() {
486     let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
487     let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap());
488     assert!(f0.contains(FdFlag::FD_CLOEXEC));
489     let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap());
490     assert!(f1.contains(FdFlag::FD_CLOEXEC));
491 }
492 
493 #[test]
test_truncate()494 fn test_truncate() {
495     let tempdir = tempfile::tempdir().unwrap();
496     let path = tempdir.path().join("file");
497 
498     {
499         let mut tmp = File::create(&path).unwrap();
500         const CONTENTS: &[u8] = b"12345678";
501         tmp.write_all(CONTENTS).unwrap();
502     }
503 
504     truncate(&path, 4).unwrap();
505 
506     let metadata = fs::metadata(&path).unwrap();
507     assert_eq!(4, metadata.len());
508 }
509 
510 #[test]
test_ftruncate()511 fn test_ftruncate() {
512     let tempdir = tempfile::tempdir().unwrap();
513     let path = tempdir.path().join("file");
514 
515     let tmpfd = {
516         let mut tmp = File::create(&path).unwrap();
517         const CONTENTS: &[u8] = b"12345678";
518         tmp.write_all(CONTENTS).unwrap();
519         tmp.into_raw_fd()
520     };
521 
522     ftruncate(tmpfd, 2).unwrap();
523     close(tmpfd).unwrap();
524 
525     let metadata = fs::metadata(&path).unwrap();
526     assert_eq!(2, metadata.len());
527 }
528 
529 // Used in `test_alarm`.
530 static mut ALARM_CALLED: bool = false;
531 
532 // Used in `test_alarm`.
alarm_signal_handler(raw_signal: libc::c_int)533 pub extern fn alarm_signal_handler(raw_signal: libc::c_int) {
534     assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {}", raw_signal);
535     unsafe { ALARM_CALLED = true };
536 }
537 
538 #[test]
test_alarm()539 fn test_alarm() {
540     let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
541 
542     let handler = SigHandler::Handler(alarm_signal_handler);
543     let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
544     let old_handler = unsafe {
545         sigaction(Signal::SIGALRM, &signal_action)
546             .expect("unable to set signal handler for alarm")
547     };
548 
549     // Set an alarm.
550     assert_eq!(alarm::set(60), None);
551 
552     // Overwriting an alarm should return the old alarm.
553     assert_eq!(alarm::set(1), Some(60));
554 
555     // We should be woken up after 1 second by the alarm, so we'll sleep for 2
556     // seconds to be sure.
557     sleep(2);
558     assert_eq!(unsafe { ALARM_CALLED }, true, "expected our alarm signal handler to be called");
559 
560     // Reset the signal.
561     unsafe {
562         sigaction(Signal::SIGALRM, &old_handler)
563             .expect("unable to set signal handler for alarm");
564     }
565 }
566 
567 #[test]
test_canceling_alarm()568 fn test_canceling_alarm() {
569     let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
570 
571     assert_eq!(alarm::cancel(), None);
572 
573     assert_eq!(alarm::set(60), None);
574     assert_eq!(alarm::cancel(), Some(60));
575 }
576 
577 #[test]
test_symlinkat()578 fn test_symlinkat() {
579     let mut buf = [0; 1024];
580     let tempdir = tempfile::tempdir().unwrap();
581 
582     let target = tempdir.path().join("a");
583     let linkpath = tempdir.path().join("b");
584     symlinkat(&target, None, &linkpath).unwrap();
585     assert_eq!(
586         readlink(&linkpath, &mut buf).unwrap().to_str().unwrap(),
587         target.to_str().unwrap()
588     );
589 
590     let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
591     let target = "c";
592     let linkpath = "d";
593     symlinkat(target, Some(dirfd), linkpath).unwrap();
594     assert_eq!(
595         readlink(&tempdir.path().join(linkpath), &mut buf)
596             .unwrap()
597             .to_str()
598             .unwrap(),
599         target
600     );
601 }
602 
603 
604 #[test]
test_unlinkat_dir_noremovedir()605 fn test_unlinkat_dir_noremovedir() {
606     let tempdir = tempfile::tempdir().unwrap();
607     let dirname = "foo_dir";
608     let dirpath = tempdir.path().join(dirname);
609 
610     // Create dir
611     DirBuilder::new().recursive(true).create(&dirpath).unwrap();
612 
613     // Get file descriptor for base directory
614     let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
615 
616     // Attempt unlink dir at relative path without proper flag
617     let err_result = unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
618     assert!(err_result == Error::Sys(Errno::EISDIR) || err_result == Error::Sys(Errno::EPERM));
619  }
620 
621 #[test]
test_unlinkat_dir_removedir()622 fn test_unlinkat_dir_removedir() {
623     let tempdir = tempfile::tempdir().unwrap();
624     let dirname = "foo_dir";
625     let dirpath = tempdir.path().join(dirname);
626 
627     // Create dir
628     DirBuilder::new().recursive(true).create(&dirpath).unwrap();
629 
630     // Get file descriptor for base directory
631     let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
632 
633     // Attempt unlink dir at relative path with proper flag
634     unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
635     assert!(!dirpath.exists());
636  }
637 
638 #[test]
test_unlinkat_file()639 fn test_unlinkat_file() {
640     let tempdir = tempfile::tempdir().unwrap();
641     let filename = "foo.txt";
642     let filepath = tempdir.path().join(filename);
643 
644     // Create file
645     File::create(&filepath).unwrap();
646 
647     // Get file descriptor for base directory
648     let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap();
649 
650     // Attempt unlink file at relative path
651     unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
652     assert!(!filepath.exists());
653  }
654 
655 #[test]
test_access_not_existing()656 fn test_access_not_existing() {
657     let tempdir = tempfile::tempdir().unwrap();
658     let dir = tempdir.path().join("does_not_exist.txt");
659     assert_eq!(access(&dir, AccessFlags::F_OK).err().unwrap().as_errno().unwrap(),
660                Errno::ENOENT);
661 }
662 
663 #[test]
test_access_file_exists()664 fn test_access_file_exists() {
665     let tempdir = tempfile::tempdir().unwrap();
666     let path  = tempdir.path().join("does_exist.txt");
667     let _file = File::create(path.clone()).unwrap();
668     assert!(access(&path, AccessFlags::R_OK | AccessFlags::W_OK).is_ok());
669 }
670