Skip to content

Commit

Permalink
add support readlink|readlinkat
Browse files Browse the repository at this point in the history
  • Loading branch information
Mic92 committed Mar 20, 2017
1 parent 1e268ed commit 08c9953
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 3 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

<!--### Added-->
- Added `openat`, `fstatat` in `::nix::unistd`
- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}`
([#497](https://github.com/nix-rust/nix/pull/551))

### Changed
Expand Down
28 changes: 27 additions & 1 deletion src/fcntl.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use {Errno, Result, NixPath};
use libc::{self, c_int, c_uint};
use libc::{self, c_int, c_uint, c_char, size_t};
use sys::stat::Mode;
use std::os::unix::io::RawFd;
use std::ffi::CStr;

#[cfg(any(target_os = "linux", target_os = "android"))]
use sys::uio::IoVec; // For vmsplice
Expand Down Expand Up @@ -34,6 +35,31 @@ pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: M
Errno::result(fd)
}

pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a CStr> {
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) }
}));

Errno::result(res).map(|_| {
let len = buffer.len();
buffer[len - 1] = 0; // ensure always null-terminated
unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) }
})
}


pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a CStr> {
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) }
}));

Errno::result(res).map(|_| {
let len = buffer.len();
buffer[len - 1] = 0; // ensure always null-terminated
unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) }
})
}

pub enum FcntlArg<'a> {
F_DUPFD(RawFd),
F_DUPFD_CLOEXEC(RawFd),
Expand Down
23 changes: 22 additions & 1 deletion test/test_fcntl.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use nix::fcntl::{openat, open, O_PATH, O_RDONLY};
use nix::fcntl::{openat, open, O_PATH, 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() {
Expand All @@ -26,6 +28,25 @@ fn test_openat() {
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(),
O_PATH,
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::*;
Expand Down

0 comments on commit 08c9953

Please sign in to comment.