Skip to content

Commit c086d23

Browse files
committed
Auto merge of #552 - Mic92:fstatat, r=posborne
add support for openat, fstatat, readlink, readlinkat
2 parents 0eef651 + 55d7b5c commit c086d23

File tree

6 files changed

+143
-5
lines changed

6 files changed

+143
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [Unreleased]
77

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

1012
### Changed
1113
- Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ preadv_pwritev = []
2222
signalfd = []
2323

2424
[dependencies]
25-
libc = { git = "https://github.com/rust-lang/libc" }
25+
libc = { git = "https://github.com/Mic92/libc" }
2626
bitflags = "0.7"
2727
cfg-if = "0.1.0"
2828
void = "1.0.2"

src/fcntl.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use {Errno, Result, NixPath};
2-
use libc::{self, c_int, c_uint};
1+
use {Error, Errno, Result, NixPath};
2+
use libc::{self, c_int, c_uint, c_char, size_t, ssize_t};
33
use sys::stat::Mode;
44
use std::os::unix::io::RawFd;
5+
use std::ffi::OsStr;
6+
use std::os::unix::ffi::OsStrExt;
57

68
#[cfg(any(target_os = "linux", target_os = "android"))]
79
use sys::uio::IoVec; // For vmsplice
@@ -18,6 +20,25 @@ mod ffi {
1820
pub const F_GET_SEALS: c_int = 1034;
1921
}
2022

23+
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
24+
libc_bitflags!{
25+
pub flags AtFlags: c_int {
26+
AT_SYMLINK_NOFOLLOW,
27+
#[cfg(any(target_os = "linux", target_os = "android"))]
28+
AT_NO_AUTOMOUNT,
29+
#[cfg(any(target_os = "linux", target_os = "android"))]
30+
AT_EMPTY_PATH
31+
}
32+
}
33+
34+
#[cfg(any(target_os = "ios", target_os = "macos"))]
35+
bitflags!(
36+
pub flags AtFlags: c_int {
37+
// hack because bitflags require one entry
38+
const EMPTY = 0x0
39+
}
40+
);
41+
2142
pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
2243
let fd = try!(path.with_nix_path(|cstr| {
2344
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
2647
Errno::result(fd)
2748
}
2849

50+
pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
51+
let fd = try!(path.with_nix_path(|cstr| {
52+
unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
53+
}));
54+
Errno::result(fd)
55+
}
56+
57+
fn wrap_readlink_result<'a>(buffer: &'a mut[u8], res: ssize_t)
58+
-> Result<&'a OsStr> {
59+
match Errno::result(res) {
60+
Err(err) => Err(err),
61+
Ok(len) => {
62+
if (len as usize) >= buffer.len() {
63+
Err(Error::Sys(Errno::ENAMETOOLONG))
64+
} else {
65+
Ok(OsStr::from_bytes(&buffer[..(len as usize)]))
66+
}
67+
}
68+
}
69+
}
70+
71+
pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
72+
let res = try!(path.with_nix_path(|cstr| {
73+
unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
74+
}));
75+
76+
wrap_readlink_result(buffer, res)
77+
}
78+
79+
80+
pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
81+
let res = try!(path.with_nix_path(|cstr| {
82+
unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
83+
}));
84+
85+
wrap_readlink_result(buffer, res)
86+
}
87+
2988
pub enum FcntlArg<'a> {
3089
F_DUPFD(RawFd),
3190
F_DUPFD_CLOEXEC(RawFd),

src/sys/stat.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub use libc::dev_t;
22
pub use libc::stat as FileStat;
33

44
use {Errno, Result, NixPath};
5+
use fcntl::AtFlags;
56
use libc::{self, mode_t};
67
use std::mem;
78
use std::os::unix::io::RawFd;
@@ -121,3 +122,15 @@ pub fn fstat(fd: RawFd) -> Result<FileStat> {
121122

122123
Ok(dst)
123124
}
125+
126+
pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
127+
let mut dst = unsafe { mem::uninitialized() };
128+
let res = try!(pathname.with_nix_path(|cstr| {
129+
unsafe { libc::fstatat(dirfd, cstr.as_ptr(), &mut dst as *mut FileStat, f.bits() as libc::c_int) }
130+
}));
131+
132+
try!(Errno::result(res));
133+
134+
Ok(dst)
135+
}
136+

test/test_fcntl.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
1+
use nix::fcntl::{openat, open, OFlag, O_RDONLY, readlink, readlinkat};
2+
use nix::sys::stat::Mode;
3+
use nix::unistd::{close, read};
4+
use tempdir::TempDir;
5+
use tempfile::NamedTempFile;
6+
use std::io::prelude::*;
7+
use std::os::unix::fs;
8+
9+
#[test]
10+
fn test_openat() {
11+
const CONTENTS: &'static [u8] = b"abcd";
12+
let mut tmp = NamedTempFile::new().unwrap();
13+
tmp.write(CONTENTS).unwrap();
14+
15+
let dirfd = open(tmp.path().parent().unwrap(),
16+
OFlag::empty(),
17+
Mode::empty()).unwrap();
18+
let fd = openat(dirfd,
19+
tmp.path().file_name().unwrap(),
20+
O_RDONLY,
21+
Mode::empty()).unwrap();
22+
23+
let mut buf = [0u8; 1024];
24+
assert_eq!(4, read(fd, &mut buf).unwrap());
25+
assert_eq!(CONTENTS, &buf[0..4]);
26+
27+
close(fd).unwrap();
28+
close(dirfd).unwrap();
29+
}
30+
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+
OFlag::empty(),
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+
150
#[cfg(any(target_os = "linux", target_os = "android"))]
251
mod linux_android {
352
use std::io::prelude::*;

test/test_stat.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use std::os::unix::prelude::AsRawFd;
44

55
use libc::{S_IFMT, S_IFLNK};
66

7-
use nix::sys::stat::{stat, fstat, lstat};
8-
7+
use nix::fcntl;
8+
use nix::sys::stat::{self, stat, fstat, lstat};
99
use nix::sys::stat::FileStat;
1010
use nix::Result;
1111
use tempdir::TempDir;
@@ -74,6 +74,21 @@ fn test_stat_and_fstat() {
7474
assert_stat_results(fstat_result);
7575
}
7676

77+
#[test]
78+
fn test_fstatat() {
79+
let tempdir = TempDir::new("nix-test_stat_and_fstat").unwrap();
80+
let filename = tempdir.path().join("foo.txt");
81+
File::create(&filename).unwrap();
82+
let dirfd = fcntl::open(tempdir.path(),
83+
fcntl::OFlag::empty(),
84+
stat::Mode::empty());
85+
86+
let result = stat::fstatat(dirfd.unwrap(),
87+
&filename,
88+
fcntl::AtFlags::empty());
89+
assert_stat_results(result);
90+
}
91+
7792
#[test]
7893
fn test_stat_fstat_lstat() {
7994
let tempdir = TempDir::new("nix-test_stat_fstat_lstat").unwrap();

0 commit comments

Comments
 (0)