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