Skip to content

Commit 08c9953

Browse files
committed
add support readlink|readlinkat
1 parent 1e268ed commit 08c9953

File tree

3 files changed

+50
-3
lines changed

3 files changed

+50
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [Unreleased]
77

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

1212
### Changed

src/fcntl.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use {Errno, Result, NixPath};
2-
use libc::{self, c_int, c_uint};
2+
use libc::{self, c_int, c_uint, c_char, size_t};
33
use sys::stat::Mode;
44
use std::os::unix::io::RawFd;
5+
use std::ffi::CStr;
56

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

38+
pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a CStr> {
39+
let res = try!(path.with_nix_path(|cstr| {
40+
unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
41+
}));
42+
43+
Errno::result(res).map(|_| {
44+
let len = buffer.len();
45+
buffer[len - 1] = 0; // ensure always null-terminated
46+
unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) }
47+
})
48+
}
49+
50+
51+
pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a CStr> {
52+
let res = try!(path.with_nix_path(|cstr| {
53+
unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
54+
}));
55+
56+
Errno::result(res).map(|_| {
57+
let len = buffer.len();
58+
buffer[len - 1] = 0; // ensure always null-terminated
59+
unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) }
60+
})
61+
}
62+
3763
pub enum FcntlArg<'a> {
3864
F_DUPFD(RawFd),
3965
F_DUPFD_CLOEXEC(RawFd),

test/test_fcntl.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use nix::fcntl::{openat, open, O_PATH, O_RDONLY};
1+
use nix::fcntl::{openat, open, O_PATH, O_RDONLY, readlink, readlinkat};
22
use nix::sys::stat::Mode;
33
use nix::unistd::{close, read};
4+
use tempdir::TempDir;
45
use tempfile::NamedTempFile;
56
use std::io::prelude::*;
7+
use std::os::unix::fs;
68

79
#[test]
810
fn test_openat() {
@@ -26,6 +28,25 @@ fn test_openat() {
2628
close(dirfd).unwrap();
2729
}
2830

31+
#[test]
32+
fn test_readlink() {
33+
let tempdir = TempDir::new("nix-test_readdir")
34+
.unwrap_or_else(|e| panic!("tempdir failed: {}", e));
35+
let src = tempdir.path().join("a");
36+
let dst = tempdir.path().join("b");
37+
println!("a: {:?}, b: {:?}", &src, &dst);
38+
fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
39+
let dirfd = open(tempdir.path(),
40+
O_PATH,
41+
Mode::empty()).unwrap();
42+
43+
let mut buf = vec![0; src.to_str().unwrap().len() + 1];
44+
assert_eq!(readlink(&dst, &mut buf).unwrap().to_str().unwrap(),
45+
src.to_str().unwrap());
46+
assert_eq!(readlinkat(dirfd, "b", &mut buf).unwrap().to_str().unwrap(),
47+
src.to_str().unwrap());
48+
}
49+
2950
#[cfg(any(target_os = "linux", target_os = "android"))]
3051
mod linux_android {
3152
use std::io::prelude::*;

0 commit comments

Comments
 (0)