Skip to content

Commit 22b2cc5

Browse files
Merge #1109
1109: fix readlink/readlinkat to return too long only when it is long r=asomers a=sendilkumarn Currently readlink returns `ENAMETOOLONG` when the bufferSize is equal to the result size. Consider the below case ```c++ int main(int argc, const char * argv[]) { std::string filename = "/tmp/test-dir/target"; size_t bufferSize = 9; char* buffer = new char[bufferSize]; size_t rc = readlink (filename.c_str(), buffer, bufferSize); // Since `readlink's` output depends on the `bufferSize`. If the`bufferSize` is 9 or greater than // 9 then it will return 9 or else it will return `bufferSize` as the value of `rc`. return 0; } ``` Co-authored-by: Sendil Kumar <[email protected]> Co-authored-by: Sendil Kumar N <[email protected]>
2 parents 414cc86 + fa50f63 commit 22b2cc5

File tree

4 files changed

+37
-23
lines changed

4 files changed

+37
-23
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [Unreleased] - ReleaseDate
77
### Added
88
### Changed
9+
- Changed `readlink` and `readlinkat` to return `OsString`
10+
([#1109](https://github.com/nix-rust/nix/pull/1109))
11+
12+
```rust
13+
# use nix::fcntl::{readlink, readlinkat};
14+
// the buffer argument of `readlink` and `readlinkat` has been removed,
15+
// and the return value is now an owned type (`OsString`).
16+
// Existing code can be updated by removing the buffer argument
17+
// and removing any clone or similar operation on the output
18+
19+
// old code `readlink(&path, &mut buf)` can be replaced with the following
20+
let _: OsString = readlink(&path);
21+
22+
// old code `readlinkat(dirfd, &path, &mut buf)` can be replaced with the following
23+
let _: OsString = readlinkat(dirfd, &path);
24+
```
25+
926
### Fixed
1027
### Removed
1128

src/fcntl.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use {Error, Result, NixPath};
1+
use {Result, NixPath};
22
use errno::Errno;
33
use libc::{self, c_int, c_uint, c_char, size_t, ssize_t};
44
use sys::stat::Mode;
55
use std::os::raw;
66
use std::os::unix::io::RawFd;
7-
use std::ffi::OsStr;
8-
use std::os::unix::ffi::OsStrExt;
7+
use std::ffi::OsString;
8+
use std::os::unix::ffi::OsStringExt;
99

1010
#[cfg(any(target_os = "android", target_os = "linux"))]
1111
use std::ptr; // For splice and copy_file_range
@@ -177,34 +177,33 @@ pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(old_dirfd: Option<Ra
177177
Errno::result(res).map(drop)
178178
}
179179

180-
fn wrap_readlink_result(buffer: &mut[u8], res: ssize_t) -> Result<&OsStr> {
180+
fn wrap_readlink_result(v: &mut Vec<u8>, res: ssize_t) -> Result<OsString> {
181181
match Errno::result(res) {
182182
Err(err) => Err(err),
183183
Ok(len) => {
184-
if (len as usize) >= buffer.len() {
185-
Err(Error::Sys(Errno::ENAMETOOLONG))
186-
} else {
187-
Ok(OsStr::from_bytes(&buffer[..(len as usize)]))
188-
}
184+
unsafe { v.set_len(len as usize) }
185+
Ok(OsString::from_vec(v.to_vec()))
189186
}
190187
}
191188
}
192189

193-
pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
190+
pub fn readlink<'a, P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
191+
let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
194192
let res = path.with_nix_path(|cstr| {
195-
unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
193+
unsafe { libc::readlink(cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t) }
196194
})?;
197195

198-
wrap_readlink_result(buffer, res)
196+
wrap_readlink_result(&mut v, res)
199197
}
200198

201199

202-
pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
200+
pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
201+
let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
203202
let res = path.with_nix_path(|cstr| {
204-
unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
203+
unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t) }
205204
})?;
206205

207-
wrap_readlink_result(buffer, res)
206+
wrap_readlink_result(&mut v, res)
208207
}
209208

210209
/// Computes the raw fd consumed by a function of the form `*at`.

test/test_fcntl.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,11 @@ fn test_readlink() {
5656
let dirfd = open(tempdir.path(),
5757
OFlag::empty(),
5858
Mode::empty()).unwrap();
59+
let expected_dir = src.to_str().unwrap();
60+
61+
assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
62+
assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);
5963

60-
let mut buf = vec![0; src.to_str().unwrap().len() + 1];
61-
assert_eq!(readlink(&dst, &mut buf).unwrap().to_str().unwrap(),
62-
src.to_str().unwrap());
63-
assert_eq!(readlinkat(dirfd, "b", &mut buf).unwrap().to_str().unwrap(),
64-
src.to_str().unwrap());
6564
}
6665

6766
#[cfg(any(target_os = "linux", target_os = "android"))]

test/test_unistd.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,13 @@ fn test_canceling_alarm() {
576576

577577
#[test]
578578
fn test_symlinkat() {
579-
let mut buf = [0; 1024];
580579
let tempdir = tempfile::tempdir().unwrap();
581580

582581
let target = tempdir.path().join("a");
583582
let linkpath = tempdir.path().join("b");
584583
symlinkat(&target, None, &linkpath).unwrap();
585584
assert_eq!(
586-
readlink(&linkpath, &mut buf).unwrap().to_str().unwrap(),
585+
readlink(&linkpath).unwrap().to_str().unwrap(),
587586
target.to_str().unwrap()
588587
);
589588

@@ -592,7 +591,7 @@ fn test_symlinkat() {
592591
let linkpath = "d";
593592
symlinkat(target, Some(dirfd), linkpath).unwrap();
594593
assert_eq!(
595-
readlink(&tempdir.path().join(linkpath), &mut buf)
594+
readlink(&tempdir.path().join(linkpath))
596595
.unwrap()
597596
.to_str()
598597
.unwrap(),

0 commit comments

Comments
 (0)