diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/fcntl.rs | 63 | ||||
-rw-r--r-- | src/sys/stat.rs | 13 | ||||
-rw-r--r-- | test/test_fcntl.rs | 49 | ||||
-rw-r--r-- | test/test_stat.rs | 19 |
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. @@ -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"); |