summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--Cargo.toml2
-rw-r--r--src/fcntl.rs63
-rw-r--r--src/sys/stat.rs13
-rw-r--r--test/test_fcntl.rs49
-rw-r--r--test/test_stat.rs19
6 files changed, 143 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bcef57a..045756dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
<!--### Added-->
+- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}`
+ ([#497](https://github.com/nix-rust/nix/pull/551))
### Changed
- Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe.
diff --git a/Cargo.toml b/Cargo.toml
index 29c631ec..e0cd5485 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@ preadv_pwritev = []
signalfd = []
[dependencies]
-libc = { git = "https://github.com/rust-lang/libc" }
+libc = { git = "https://github.com/Mic92/libc" }
bitflags = "0.7"
cfg-if = "0.1.0"
void = "1.0.2"
diff --git a/src/fcntl.rs b/src/fcntl.rs
index e224d3b1..a295de3a 100644
--- a/src/fcntl.rs
+++ b/src/fcntl.rs
@@ -1,7 +1,9 @@
-use {Errno, Result, NixPath};
-use libc::{self, c_int, c_uint};
+use {Error, Errno, Result, NixPath};
+use libc::{self, c_int, c_uint, c_char, size_t, ssize_t};
use sys::stat::Mode;
use std::os::unix::io::RawFd;
+use std::ffi::OsStr;
+use std::os::unix::ffi::OsStrExt;
#[cfg(any(target_os = "linux", target_os = "android"))]
use sys::uio::IoVec; // For vmsplice
@@ -18,6 +20,25 @@ mod ffi {
pub const F_GET_SEALS: c_int = 1034;
}
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+libc_bitflags!{
+ pub flags AtFlags: c_int {
+ AT_SYMLINK_NOFOLLOW,
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ AT_NO_AUTOMOUNT,
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ AT_EMPTY_PATH
+ }
+}
+
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+bitflags!(
+ pub flags AtFlags: c_int {
+ // hack because bitflags require one entry
+ const EMPTY = 0x0
+ }
+);
+
pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
let fd = try!(path.with_nix_path(|cstr| {
unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
@@ -26,6 +47,44 @@ pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<R
Errno::result(fd)
}
+pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
+ let fd = try!(path.with_nix_path(|cstr| {
+ unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
+ }));
+ Errno::result(fd)
+}
+
+fn wrap_readlink_result<'a>(buffer: &'a mut[u8], res: ssize_t)
+ -> Result<&'a OsStr> {
+ match Errno::result(res) {
+ Err(err) => Err(err),
+ Ok(len) => {
+ if (len as usize) >= buffer.len() {
+ Err(Error::Sys(Errno::ENAMETOOLONG))
+ } else {
+ Ok(OsStr::from_bytes(&buffer[..(len as usize)]))
+ }
+ }
+ }
+}
+
+pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
+ let res = try!(path.with_nix_path(|cstr| {
+ unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
+ }));
+
+ wrap_readlink_result(buffer, res)
+}
+
+
+pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
+ let res = try!(path.with_nix_path(|cstr| {
+ unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
+ }));
+
+ wrap_readlink_result(buffer, res)
+}
+
pub enum FcntlArg<'a> {
F_DUPFD(RawFd),
F_DUPFD_CLOEXEC(RawFd),
diff --git a/src/sys/stat.rs b/src/sys/stat.rs
index 1ff35923..054aedc1 100644
--- a/src/sys/stat.rs
+++ b/src/sys/stat.rs
@@ -2,6 +2,7 @@ pub use libc::dev_t;
pub use libc::stat as FileStat;
use {Errno, Result, NixPath};
+use fcntl::AtFlags;
use libc::{self, mode_t};
use std::mem;
use std::os::unix::io::RawFd;
@@ -121,3 +122,15 @@ pub fn fstat(fd: RawFd) -> Result<FileStat> {
Ok(dst)
}
+
+pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
+ let mut dst = unsafe { mem::uninitialized() };
+ let res = try!(pathname.with_nix_path(|cstr| {
+ unsafe { libc::fstatat(dirfd, cstr.as_ptr(), &mut dst as *mut FileStat, f.bits() as libc::c_int) }
+ }));
+
+ try!(Errno::result(res));
+
+ Ok(dst)
+}
+
diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs
index ca2e89a3..43bfc091 100644
--- a/test/test_fcntl.rs
+++ b/test/test_fcntl.rs
@@ -1,3 +1,52 @@
+use nix::fcntl::{openat, open, OFlag, O_RDONLY, readlink, readlinkat};
+use nix::sys::stat::Mode;
+use nix::unistd::{close, read};
+use tempdir::TempDir;
+use tempfile::NamedTempFile;
+use std::io::prelude::*;
+use std::os::unix::fs;
+
+#[test]
+fn test_openat() {
+ const CONTENTS: &'static [u8] = b"abcd";
+ let mut tmp = NamedTempFile::new().unwrap();
+ tmp.write(CONTENTS).unwrap();
+
+ let dirfd = open(tmp.path().parent().unwrap(),
+ OFlag::empty(),
+ Mode::empty()).unwrap();
+ let fd = openat(dirfd,
+ tmp.path().file_name().unwrap(),
+ O_RDONLY,
+ Mode::empty()).unwrap();
+
+ let mut buf = [0u8; 1024];
+ assert_eq!(4, read(fd, &mut buf).unwrap());
+ assert_eq!(CONTENTS, &buf[0..4]);
+
+ close(fd).unwrap();
+ close(dirfd).unwrap();
+}
+
+#[test]
+fn test_readlink() {
+ let tempdir = TempDir::new("nix-test_readdir")
+ .unwrap_or_else(|e| panic!("tempdir failed: {}", e));
+ let src = tempdir.path().join("a");
+ let dst = tempdir.path().join("b");
+ println!("a: {:?}, b: {:?}", &src, &dst);
+ fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
+ let dirfd = open(tempdir.path(),
+ OFlag::empty(),
+ Mode::empty()).unwrap();
+
+ let mut buf = vec![0; src.to_str().unwrap().len() + 1];
+ assert_eq!(readlink(&dst, &mut buf).unwrap().to_str().unwrap(),
+ src.to_str().unwrap());
+ assert_eq!(readlinkat(dirfd, "b", &mut buf).unwrap().to_str().unwrap(),
+ src.to_str().unwrap());
+}
+
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
use std::io::prelude::*;
diff --git a/test/test_stat.rs b/test/test_stat.rs
index 4b22c296..765d4fa1 100644
--- a/test/test_stat.rs
+++ b/test/test_stat.rs
@@ -4,8 +4,8 @@ use std::os::unix::prelude::AsRawFd;
use libc::{S_IFMT, S_IFLNK};
-use nix::sys::stat::{stat, fstat, lstat};
-
+use nix::fcntl;
+use nix::sys::stat::{self, stat, fstat, lstat};
use nix::sys::stat::FileStat;
use nix::Result;
use tempdir::TempDir;
@@ -75,6 +75,21 @@ fn test_stat_and_fstat() {
}
#[test]
+fn test_fstatat() {
+ let tempdir = TempDir::new("nix-test_stat_and_fstat").unwrap();
+ let filename = tempdir.path().join("foo.txt");
+ File::create(&filename).unwrap();
+ let dirfd = fcntl::open(tempdir.path(),
+ fcntl::OFlag::empty(),
+ stat::Mode::empty());
+
+ let result = stat::fstatat(dirfd.unwrap(),
+ &filename,
+ fcntl::AtFlags::empty());
+ assert_stat_results(result);
+}
+
+#[test]
fn test_stat_fstat_lstat() {
let tempdir = TempDir::new("nix-test_stat_fstat_lstat").unwrap();
let filename = tempdir.path().join("bar.txt");