Skip to content

Commit dbeae3c

Browse files
authored
Convert readlinkat_raw to use Buffer. (#1362)
1 parent 1ad8f0a commit dbeae3c

File tree

5 files changed

+69
-74
lines changed

5 files changed

+69
-74
lines changed

CHANGES.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,10 @@ a `[MaybeUninit<u8>]` instead of a `[u8]`.
288288
[`SendAncillaryBuffer`]: https://docs.rs/rustix/1.0.0/rustix/net/struct.SendAncillaryBuffer.html
289289
[`RecvAncillaryBuffer`]: https://docs.rs/rustix/1.0.0/rustix/net/struct.RecvAncillaryBuffer.html
290290

291-
[`read`], [`pread`], [`recv`], [`recvfrom`], [`getrandom`], [`epoll::wait`],
292-
[`kevent`], [`port::getn`], [`getxattr`], [`lgetxattr`], [`fgetxattr`],
293-
[`listxattr`], [`llistxattr`], and [`flistxattr`] now use the new
294-
[`Buffer` trait].
291+
[`read`], [`pread`], [`recv`], [`recvfrom`], [`getrandom`], [`readlinkat_raw`],
292+
[`epoll::wait`], [`kevent`], [`port::getn`], [`getxattr`], [`lgetxattr`],
293+
[`fgetxattr`], [`listxattr`], [`llistxattr`], and [`flistxattr`] now use the
294+
new [`Buffer` trait].
295295

296296
This replaces `read_uninit`, `pread_uninit`, `recv_uninit`, `recvfrom_uninit`,
297297
and `getrandom_uninit`, as the `Buffer` trait supports reading into
@@ -308,6 +308,7 @@ not cleared first. Consider clearing the vector before calling `epoll::wait`,
308308
[`recv`]: https://docs.rs/rustix/1.0.0/rustix/net/fn.recv.html
309309
[`recvfrom`]: https://docs.rs/rustix/1.0.0/rustix/net/fn.recvfrom.html
310310
[`getrandom`]: https://docs.rs/rustix/1.0.0/rustix/rand/fn.getrandom.html
311+
[`readlinkat_raw`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.readlinkat_raw.html
311312
[`epoll::wait`]: https://docs.rs/rustix/1.0.0/rustix/event/epoll/fn.wait.html
312313
[`getxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.getxattr.html
313314
[`lgetxattr`]: https://docs.rs/rustix/1.0.0/rustix/fs/fn.lgetxattr.html

src/backend/libc/fs/syscalls.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -289,19 +289,12 @@ pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
289289

290290
#[cfg(not(target_os = "redox"))]
291291
#[inline]
292-
pub(crate) fn readlinkat(
292+
pub(crate) unsafe fn readlinkat(
293293
dirfd: BorrowedFd<'_>,
294294
path: &CStr,
295-
buf: &mut [MaybeUninit<u8>],
295+
buf: (*mut u8, usize),
296296
) -> io::Result<usize> {
297-
unsafe {
298-
ret_usize(c::readlinkat(
299-
borrowed_fd(dirfd),
300-
c_str(path),
301-
buf.as_mut_ptr().cast(),
302-
buf.len(),
303-
) as isize)
304-
}
297+
ret_usize(c::readlinkat(borrowed_fd(dirfd), c_str(path), buf.0.cast(), buf.1) as isize)
305298
}
306299

307300
pub(crate) fn mkdir(path: &CStr, mode: Mode) -> io::Result<()> {

src/backend/linux_raw/fs/syscalls.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -976,21 +976,18 @@ pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
976976
}
977977

978978
#[inline]
979-
pub(crate) fn readlinkat(
979+
pub(crate) unsafe fn readlinkat(
980980
dirfd: BorrowedFd<'_>,
981981
path: &CStr,
982-
buf: &mut [MaybeUninit<u8>],
982+
buf: (*mut u8, usize),
983983
) -> io::Result<usize> {
984-
let (buf_addr_mut, buf_len) = slice_mut(buf);
985-
unsafe {
986-
ret_usize(syscall!(
987-
__NR_readlinkat,
988-
dirfd,
989-
path,
990-
buf_addr_mut,
991-
buf_len
992-
))
993-
}
984+
ret_usize(syscall!(
985+
__NR_readlinkat,
986+
dirfd,
987+
path,
988+
buf.0,
989+
pass_usize(buf.1)
990+
))
994991
}
995992

996993
#[inline]

src/backend/linux_raw/termios/syscalls.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -390,30 +390,36 @@ pub(crate) fn ttyname(fd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Re
390390
// SAFETY: We just wrote a valid C String.
391391
let proc_self_fd_path = unsafe { CStr::from_ptr(proc_self_fd_buf.as_ptr().cast()) };
392392

393-
// Gather the ttyname by reading the "fd" file inside `proc_self_fd`.
394-
let r = crate::backend::fs::syscalls::readlinkat(crate::fs::CWD, proc_self_fd_path, buf)?;
393+
let ptr = buf.as_mut_ptr();
394+
let len = {
395+
// Gather the ttyname by reading the "fd" file inside `proc_self_fd`.
396+
let (init, uninit) = crate::fs::readlinkat_raw(crate::fs::CWD, proc_self_fd_path, buf)?;
397+
398+
// If the number of bytes is equal to the buffer length, truncation may
399+
// have occurred. This check also ensures that we have enough space for
400+
// adding a NUL terminator.
401+
if uninit.is_empty() {
402+
return Err(io::Errno::RANGE);
403+
}
395404

396-
// If the number of bytes is equal to the buffer length, truncation may
397-
// have occurred. This check also ensures that we have enough space for
398-
// adding a NUL terminator.
399-
if r == buf.len() {
400-
return Err(io::Errno::RANGE);
401-
}
405+
// `readlinkat` returns the number of bytes placed in the buffer.
406+
// NUL-terminate the string at that offset.
407+
uninit[0].write(b'\0');
402408

403-
// `readlinkat` returns the number of bytes placed in the buffer.
404-
// NUL-terminate the string at that offset.
405-
buf[r].write(b'\0');
409+
init.len()
410+
};
406411

407412
// Check that the path we read refers to the same file as `fd`.
408413
{
409-
// SAFETY: We just wrote the NUL byte above
410-
let path = unsafe { CStr::from_ptr(buf.as_ptr().cast()) };
414+
// SAFETY: We just wrote the NUL byte above.
415+
let path = unsafe { CStr::from_ptr(ptr.cast()) };
411416

412417
let path_stat = crate::backend::fs::syscalls::stat(path)?;
413418
if path_stat.st_dev != fd_stat.st_dev || path_stat.st_ino != fd_stat.st_ino {
414419
return Err(io::Errno::NODEV);
415420
}
416421
}
417422

418-
Ok(r)
423+
// Return the length, excluding the NUL terminator.
424+
Ok(len)
419425
}

src/fs/at.rs

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
//! [`CWD`]: crate::fs::CWD
77
//! [`ABS`]: crate::fs::ABS
88
9+
#![allow(unsafe_code)]
10+
11+
use crate::buffer::Buffer;
912
use crate::fd::OwnedFd;
10-
use crate::ffi::CStr;
1113
#[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))]
1214
use crate::fs::Access;
1315
#[cfg(not(target_os = "espidf"))]
@@ -24,11 +26,14 @@ use crate::fs::{Dev, FileType};
2426
use crate::fs::{Gid, Uid};
2527
use crate::fs::{Mode, OFlags};
2628
use crate::{backend, io, path};
27-
use backend::fd::{AsFd, BorrowedFd};
28-
use core::mem::MaybeUninit;
29-
use core::slice;
29+
use backend::fd::AsFd;
3030
#[cfg(feature = "alloc")]
31-
use {crate::ffi::CString, crate::path::SMALL_PATH_BUFFER_SIZE, alloc::vec::Vec};
31+
use {
32+
crate::ffi::{CStr, CString},
33+
crate::path::SMALL_PATH_BUFFER_SIZE,
34+
alloc::vec::Vec,
35+
backend::fd::BorrowedFd,
36+
};
3237
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
3338
use {crate::fs::Timestamps, crate::timespec::Nsecs};
3439

@@ -107,8 +112,16 @@ fn _readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec<u8>) -> io::R
107112
buffer.reserve(SMALL_PATH_BUFFER_SIZE);
108113

109114
loop {
110-
let nread =
111-
backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, buffer.spare_capacity_mut())?;
115+
let buf = buffer.spare_capacity_mut();
116+
117+
// SAFETY: `readlinkat` behaves.
118+
let nread = unsafe {
119+
backend::fs::syscalls::readlinkat(
120+
dirfd.as_fd(),
121+
path,
122+
(buf.as_mut_ptr().cast(), buf.len()),
123+
)?
124+
};
112125

113126
debug_assert!(nread <= buffer.capacity());
114127
if nread < buffer.capacity() {
@@ -146,14 +159,9 @@ fn _readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec<u8>) -> io::R
146159
/// `readlinkat(fd, path)`—Reads the contents of a symlink, without
147160
/// allocating.
148161
///
149-
/// This is the "raw" version which avoids allocating, but which is
150-
/// significantly trickier to use; most users should use plain [`readlinkat`].
151-
///
152-
/// This version writes bytes into the buffer and returns two slices, one
153-
/// containing the written bytes, and one containing the remaining
154-
/// uninitialized space. If the number of written bytes is equal to the length
155-
/// of the buffer, it means the buffer wasn't big enough to hold the full
156-
/// string, and callers should try again with a bigger buffer.
162+
/// This is the "raw" version which avoids allocating, but which truncates the
163+
/// string if it doesn't fit in the provided buffer, and doesn't NUL-terminate
164+
/// the string.
157165
///
158166
/// # References
159167
/// - [POSIX]
@@ -162,27 +170,17 @@ fn _readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec<u8>) -> io::R
162170
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlinkat.html
163171
/// [Linux]: https://man7.org/linux/man-pages/man2/readlinkat.2.html
164172
#[inline]
165-
pub fn readlinkat_raw<P: path::Arg, Fd: AsFd>(
173+
pub fn readlinkat_raw<P: path::Arg, Fd: AsFd, Buf: Buffer<u8>>(
166174
dirfd: Fd,
167175
path: P,
168-
buf: &mut [MaybeUninit<u8>],
169-
) -> io::Result<(&mut [u8], &mut [MaybeUninit<u8>])> {
170-
path.into_with_c_str(|path| _readlinkat_raw(dirfd.as_fd(), path, buf))
171-
}
172-
173-
#[allow(unsafe_code)]
174-
fn _readlinkat_raw<'a>(
175-
dirfd: BorrowedFd<'_>,
176-
path: &CStr,
177-
buf: &'a mut [MaybeUninit<u8>],
178-
) -> io::Result<(&'a mut [u8], &'a mut [MaybeUninit<u8>])> {
179-
let n = backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, buf)?;
180-
unsafe {
181-
Ok((
182-
slice::from_raw_parts_mut(buf.as_mut_ptr().cast::<u8>(), n),
183-
&mut buf[n..],
184-
))
185-
}
176+
mut buf: Buf,
177+
) -> io::Result<Buf::Output> {
178+
// SAFETY: `readlinkat` behaves.
179+
let len = path.into_with_c_str(|path| unsafe {
180+
backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, buf.parts_mut())
181+
})?;
182+
// SAFETY: `readlinkat` behaves.
183+
unsafe { Ok(buf.assume_init(len)) }
186184
}
187185

188186
/// `mkdirat(fd, path, mode)`—Creates a directory.

0 commit comments

Comments
 (0)