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