diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe8119..f00b801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate ### Added +- Added `faccessat` + ([#1780](https://github.com/nix-rust/nix/pull/1780)) - Added `memfd` on Android. (#[1773](https://github.com/nix-rust/nix/pull/1773)) - Added ETH_P_ALL to SockProtocol enum diff --git a/src/unistd.rs b/src/unistd.rs index d3915dc..70a06b0 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2900,6 +2900,27 @@ pub fn access(path: &P, amode: AccessFlags) -> Result<()> { })?; Errno::result(res).map(drop) } + +/// Checks the file named by `path` for accessibility according to the flags given by `mode` +/// +/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor. +/// +/// If `dirfd` is `None`, then `path` is relative to the current working directory. +/// +/// # References +/// +/// [faccessat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html) +// illumos: faccessat(2) appears to be supported, but the libc crate does not provide a binding. +// redox: does not appear to support the *at family of syscalls. +#[cfg(not(any(target_os = "illumos", target_os = "redox")))] +pub fn faccessat(dirfd: Option, path: &P, mode: AccessFlags, flags: AtFlags) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { + libc::faccessat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits(), flags.bits()) + } + })?; + Errno::result(res).map(drop) +} } feature! { diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 337ebe4..b08ab33 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -1308,3 +1308,71 @@ fn test_getpeereid_invalid_fd() { // getpeereid is not POSIX, so error codes are inconsistent between different Unices. getpeereid(-1).expect_err("assertion failed"); } + +#[test] +#[cfg(not(any(target_os = "illumos", target_os = "redox")))] +fn test_faccessat_none_not_existing() { + use nix::fcntl::AtFlags; + let tempdir = tempfile::tempdir().unwrap(); + let dir = tempdir.path().join("does_not_exist.txt"); + assert_eq!( + faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty()) + .err() + .unwrap(), + Errno::ENOENT + ); +} + +#[test] +#[cfg(not(any(target_os = "illumos", target_os = "redox")))] +fn test_faccessat_not_existing() { + use nix::fcntl::AtFlags; + let tempdir = tempfile::tempdir().unwrap(); + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let not_exist_file = "does_not_exist.txt"; + assert_eq!( + faccessat( + Some(dirfd), + not_exist_file, + AccessFlags::F_OK, + AtFlags::empty() + ) + .err() + .unwrap(), + Errno::ENOENT + ); +} + +#[test] +#[cfg(not(any(target_os = "illumos", target_os = "redox")))] +fn test_faccessat_none_file_exists() { + use nix::fcntl::AtFlags; + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().join("does_exist.txt"); + let _file = File::create(path.clone()).unwrap(); + assert!(faccessat( + None, + &path, + AccessFlags::R_OK | AccessFlags::W_OK, + AtFlags::empty() + ) + .is_ok()); +} + +#[test] +#[cfg(not(any(target_os = "illumos", target_os = "redox")))] +fn test_faccessat_file_exists() { + use nix::fcntl::AtFlags; + let tempdir = tempfile::tempdir().unwrap(); + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let exist_file = "does_exist.txt"; + let path = tempdir.path().join(exist_file); + let _file = File::create(path.clone()).unwrap(); + assert!(faccessat( + Some(dirfd), + &path, + AccessFlags::R_OK | AccessFlags::W_OK, + AtFlags::empty() + ) + .is_ok()); +}