diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d59d217bd..dc3b45c2e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `AioCb::from_boxed_slice` ([#582](https://github.com/nix-rust/nix/pull/582) -- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}` +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat, access, faccessat}` ([#551](https://github.com/nix-rust/nix/pull/551)) + ([#605](https://github.com/nix-rust/nix/pull/605)) - Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` ([#556](https://github.com/nix-rust/nix/pull/556) diff --git a/src/unistd.rs b/src/unistd.rs index 566212d0bb..be5e9f7597 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -1,14 +1,14 @@ //! Safe wrappers around functions found in libc "unistd.h" header use {Errno, Error, Result, NixPath}; -use fcntl::{fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC}; +use fcntl::{AtFlags, fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC}; use fcntl::FcntlArg::F_SETFD; use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t, mode_t}; use std::mem; use std::ffi::{CString, CStr, OsString, OsStr}; use std::os::unix::ffi::{OsStringExt, OsStrExt}; use std::os::unix::io::RawFd; -use std::path::{PathBuf}; +use std::path::PathBuf; use void::Void; use sys::stat::Mode; @@ -819,6 +819,79 @@ pub fn mkstemp(template: &P) -> Result<(RawFd, PathBuf)> { Ok((fd, PathBuf::from(pathname))) } +libc_bitflags!{ + /// Flags that determine what permissions to check for in [`access`](fn.access.html) + /// and [`faccessat`](fn.faccessat.html) functions. + pub flags AccessFlags: c_int { + /// Read permission. + R_OK, + /// Write permission. + W_OK, + /// Execute permission. + X_OK, + } +} + +/// Whether [`access`](fn.access.html) and [`faccessat`](fn.faccessat.html) functions +/// should check whether a file exists or whether the current process has the requested +/// permissions to access it. +pub enum AccessMode { + /// Check whether the file exists. + F_OK, + /// Check whether the file exists and the current process has the requested + /// permissions to access it. + Flags(AccessFlags), +} + +impl AccessMode { + /// Convert the `enum AccessMode` + `flags AccessFlags` combo to the plain + /// `mode: c_int` syscalls expect. + fn to_libc_type(&self) -> c_int { + match self { + &AccessMode::F_OK => libc::F_OK, + &AccessMode::Flags(flags) => flags.bits(), + } + } +} + +/// Check whether a file exists or whether the current process can access a file ([see access(2)] +/// (http://man7.org/linux/man-pages/man2/access.2.html)). +/// +/// # Example +/// ``` +/// use nix::unistd::*; +/// +/// let devnull_exists = access("/dev/null", AccessMode::F_OK); +/// assert!(devnull_exists.is_ok()); +/// ``` +pub fn access(path: &P, mode: AccessMode) -> Result<()> { + let res = try!(path.with_nix_path(|path| + unsafe { + libc::access(path.as_ptr(), mode.to_libc_type()) + } + )); + Errno::result(res).map(drop) +} + +/// Check whether a file exists or whether the current process can access a file, +/// relative to a directory file descriptor ([see faccessat(2)] +/// (http://man7.org/linux/man-pages/man2/faccessat.2.html)). +pub fn faccessat(dirfd: RawFd, + path: &P, + mode: AccessMode, + flags: AtFlags) + -> Result<()> { + let res = try!(path.with_nix_path(|path| + unsafe { + libc::faccessat(dirfd, + path.as_ptr(), + mode.to_libc_type(), + flags.bits()) + } + )); + Errno::result(res).map(drop) +} + #[cfg(any(target_os = "linux", target_os = "android"))] mod linux { use libc::{self, uid_t, gid_t};