1use libc::{_exit, mode_t, off_t}; 2use nix::errno::Errno; 3#[cfg(not(any(target_os = "redox", target_os = "haiku")))] 4use nix::fcntl::readlink; 5use nix::fcntl::OFlag; 6#[cfg(not(target_os = "redox"))] 7use nix::fcntl::{self, open}; 8#[cfg(not(any( 9 target_os = "redox", 10 target_os = "fuchsia", 11 target_os = "haiku" 12)))] 13use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt}; 14#[cfg(not(target_os = "redox"))] 15use nix::sys::signal::{ 16 sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, 17}; 18use nix::sys::stat::{self, Mode, SFlag}; 19use nix::sys::wait::*; 20use nix::unistd::ForkResult::*; 21use nix::unistd::*; 22use std::env; 23#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))] 24use std::ffi::CString; 25#[cfg(not(target_os = "redox"))] 26use std::fs::DirBuilder; 27use std::fs::{self, File}; 28use std::io::Write; 29use std::os::unix::prelude::*; 30#[cfg(not(any( 31 target_os = "fuchsia", 32 target_os = "redox", 33 target_os = "haiku" 34)))] 35use std::path::Path; 36use tempfile::{tempdir, tempfile}; 37 38use crate::*; 39 40#[test] 41#[cfg(not(any(target_os = "netbsd")))] 42fn 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] 70fn 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] 87fn 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] 102fn 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"))] 109fn 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"))] 122fn 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)))] 135fn 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)))] 156fn 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)))] 179fn 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)))] 195fn 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] 207fn 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"))] 216fn 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"))] 224mod linux_android { 225 use nix::unistd::gettid; 226 227 #[test] 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)))] 243fn 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)))] 273fn 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")))] 303macro_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 403cfg_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"))] 426execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap()); 427 428cfg_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"))] 453fn 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] 468fn 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] 493fn 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] 513fn 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"))] 529fn 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] 554fn 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] 572fn 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 587cfg_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)))] 616fn 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] 641fn 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] 654fn 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] 666fn 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] 679fn 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] 695fn 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] 710fn 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] 720fn 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] 748fn 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")))] 760fn 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] 777fn 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"))] 797static mut ALARM_CALLED: bool = false; 798 799// Used in `test_alarm`. 800#[cfg(not(target_os = "redox"))] 801pub 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"))] 813fn 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"))] 858fn 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")))] 869fn 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")))] 897fn 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")))] 927fn 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")))] 964fn 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)))] 1006fn 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")))] 1049fn 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"))] 1098fn 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"))] 1119fn 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"))] 1139fn 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] 1158fn 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] 1168fn 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] 1180fn 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] 1196fn 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)))] 1237fn 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")))] 1257fn 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)))] 1269fn 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))] 1282fn 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))] 1307fn 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"))] 1314fn 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"))] 1328fn 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"))] 1348fn 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"))] 1364fn 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))] 1386fn 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))] 1401fn 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