Skip to content

Commit 8a1e9df

Browse files
authored
refactor: I/O safety for sys/stat.rs (#2439)
* refactor: I/O safety for sys/stat.rs * fix test on FreeBSD & correct changelog PR number
1 parent e0e6997 commit 8a1e9df

File tree

6 files changed

+103
-123
lines changed

6 files changed

+103
-123
lines changed

changelog/2439.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Module `sys/stat` now adopts I/O safety.

src/fcntl.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,8 @@ pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
6666
/// use nix::errno::Errno;
6767
/// use nix::fcntl::AT_FDCWD;
6868
/// use nix::sys::stat::fstat;
69-
/// use std::os::fd::AsRawFd;
7069
///
71-
/// let never = fstat(AT_FDCWD.as_raw_fd()).unwrap();
70+
/// let never = fstat(AT_FDCWD).unwrap();
7271
/// ```
7372
//
7473
// SAFETY:
@@ -585,13 +584,20 @@ unsafe fn inner_readlink<P: ?Sized + NixPath>(
585584
Some(_) => unreachable!("redox does not have readlinkat(2)"),
586585
#[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
587586
Some(dirfd) => {
587+
// SAFETY:
588+
//
589+
// If this call of `borrow_raw()` is safe or not depends on the
590+
// usage of `unsafe fn inner_readlink()`.
591+
let dirfd = unsafe {
592+
std::os::fd::BorrowedFd::borrow_raw(dirfd)
593+
};
588594
let flags = if path.is_empty() {
589595
AtFlags::AT_EMPTY_PATH
590596
} else {
591597
AtFlags::empty()
592598
};
593599
super::sys::stat::fstatat(
594-
Some(dirfd),
600+
dirfd,
595601
path,
596602
flags | AtFlags::AT_SYMLINK_NOFOLLOW,
597603
)
@@ -602,11 +608,16 @@ unsafe fn inner_readlink<P: ?Sized + NixPath>(
602608
target_os = "freebsd",
603609
target_os = "hurd"
604610
)))]
605-
Some(dirfd) => super::sys::stat::fstatat(
606-
Some(dirfd),
607-
path,
608-
AtFlags::AT_SYMLINK_NOFOLLOW,
609-
),
611+
Some(dirfd) => {
612+
// SAFETY:
613+
//
614+
// If this call of `borrow_raw()` is safe or not depends on the
615+
// usage of `unsafe fn inner_readlink()`.
616+
let dirfd = unsafe {
617+
std::os::fd::BorrowedFd::borrow_raw(dirfd)
618+
};
619+
super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW)
620+
},
610621
None => super::sys::stat::lstat(path),
611622
}
612623
.map(|x| x.st_size)

src/sys/stat.rs

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ pub use libc::stat as FileStat;
66
pub use libc::{dev_t, mode_t};
77

88
#[cfg(not(target_os = "redox"))]
9-
use crate::fcntl::{at_rawfd, AtFlags};
9+
use crate::fcntl::AtFlags;
1010
use crate::sys::time::{TimeSpec, TimeVal};
1111
use crate::{errno::Errno, NixPath, Result};
1212
use std::mem;
13-
use std::os::unix::io::RawFd;
1413

1514
libc_bitflags!(
1615
/// "File type" flags for `mknod` and related functions.
@@ -168,17 +167,18 @@ pub fn mknod<P: ?Sized + NixPath>(
168167

169168
/// Create a special or ordinary file, relative to a given directory.
170169
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
171-
pub fn mknodat<P: ?Sized + NixPath>(
172-
dirfd: Option<RawFd>,
170+
pub fn mknodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
171+
dirfd: Fd,
173172
path: &P,
174173
kind: SFlag,
175174
perm: Mode,
176175
dev: dev_t,
177176
) -> Result<()> {
178-
let dirfd = at_rawfd(dirfd);
177+
use std::os::fd::AsRawFd;
178+
179179
let res = path.with_nix_path(|cstr| unsafe {
180180
libc::mknodat(
181-
dirfd,
181+
dirfd.as_fd().as_raw_fd(),
182182
cstr.as_ptr(),
183183
kind.bits() | perm.bits() as mode_t,
184184
dev,
@@ -233,26 +233,29 @@ pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
233233
Ok(unsafe { dst.assume_init() })
234234
}
235235

236-
pub fn fstat(fd: RawFd) -> Result<FileStat> {
236+
pub fn fstat<Fd: std::os::fd::AsFd>(fd: Fd) -> Result<FileStat> {
237+
use std::os::fd::AsRawFd;
238+
237239
let mut dst = mem::MaybeUninit::uninit();
238-
let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
240+
let res = unsafe { libc::fstat(fd.as_fd().as_raw_fd(), dst.as_mut_ptr()) };
239241

240242
Errno::result(res)?;
241243

242244
Ok(unsafe { dst.assume_init() })
243245
}
244246

245247
#[cfg(not(target_os = "redox"))]
246-
pub fn fstatat<P: ?Sized + NixPath>(
247-
dirfd: Option<RawFd>,
248+
pub fn fstatat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
249+
dirfd: Fd,
248250
pathname: &P,
249251
f: AtFlags,
250252
) -> Result<FileStat> {
251-
let dirfd = at_rawfd(dirfd);
253+
use std::os::fd::AsRawFd;
254+
252255
let mut dst = mem::MaybeUninit::uninit();
253256
let res = pathname.with_nix_path(|cstr| unsafe {
254257
libc::fstatat(
255-
dirfd,
258+
dirfd.as_fd().as_raw_fd(),
256259
cstr.as_ptr(),
257260
dst.as_mut_ptr(),
258261
f.bits() as libc::c_int,
@@ -269,8 +272,11 @@ pub fn fstatat<P: ?Sized + NixPath>(
269272
/// # References
270273
///
271274
/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
272-
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
273-
let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
275+
pub fn fchmod<Fd: std::os::fd::AsFd>(fd: Fd, mode: Mode) -> Result<()> {
276+
use std::os::fd::AsRawFd;
277+
278+
let res =
279+
unsafe { libc::fchmod(fd.as_fd().as_raw_fd(), mode.bits() as mode_t) };
274280

275281
Errno::result(res).map(drop)
276282
}
@@ -299,19 +305,21 @@ pub enum FchmodatFlags {
299305
///
300306
/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
301307
#[cfg(not(target_os = "redox"))]
302-
pub fn fchmodat<P: ?Sized + NixPath>(
303-
dirfd: Option<RawFd>,
308+
pub fn fchmodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
309+
dirfd: Fd,
304310
path: &P,
305311
mode: Mode,
306312
flag: FchmodatFlags,
307313
) -> Result<()> {
314+
use std::os::fd::AsRawFd;
315+
308316
let atflag = match flag {
309317
FchmodatFlags::FollowSymlink => AtFlags::empty(),
310318
FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
311319
};
312320
let res = path.with_nix_path(|cstr| unsafe {
313321
libc::fchmodat(
314-
at_rawfd(dirfd),
322+
dirfd.as_fd().as_raw_fd(),
315323
cstr.as_ptr(),
316324
mode.bits() as mode_t,
317325
atflag.bits() as libc::c_int,
@@ -383,9 +391,15 @@ pub fn lutimes<P: ?Sized + NixPath>(
383391
///
384392
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
385393
#[inline]
386-
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
394+
pub fn futimens<Fd: std::os::fd::AsFd>(
395+
fd: Fd,
396+
atime: &TimeSpec,
397+
mtime: &TimeSpec,
398+
) -> Result<()> {
399+
use std::os::fd::AsRawFd;
400+
387401
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
388-
let res = unsafe { libc::futimens(fd, &times[0]) };
402+
let res = unsafe { libc::futimens(fd.as_fd().as_raw_fd(), &times[0]) };
389403

390404
Errno::result(res).map(drop)
391405
}
@@ -418,21 +432,23 @@ pub enum UtimensatFlags {
418432
///
419433
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
420434
#[cfg(not(target_os = "redox"))]
421-
pub fn utimensat<P: ?Sized + NixPath>(
422-
dirfd: Option<RawFd>,
435+
pub fn utimensat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
436+
dirfd: Fd,
423437
path: &P,
424438
atime: &TimeSpec,
425439
mtime: &TimeSpec,
426440
flag: UtimensatFlags,
427441
) -> Result<()> {
442+
use std::os::fd::AsRawFd;
443+
428444
let atflag = match flag {
429445
UtimensatFlags::FollowSymlink => AtFlags::empty(),
430446
UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
431447
};
432448
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
433449
let res = path.with_nix_path(|cstr| unsafe {
434450
libc::utimensat(
435-
at_rawfd(dirfd),
451+
dirfd.as_fd().as_raw_fd(),
436452
cstr.as_ptr(),
437453
&times[0],
438454
atflag.bits() as libc::c_int,
@@ -443,14 +459,19 @@ pub fn utimensat<P: ?Sized + NixPath>(
443459
}
444460

445461
#[cfg(not(target_os = "redox"))]
446-
pub fn mkdirat<P: ?Sized + NixPath>(
447-
fd: Option<RawFd>,
462+
pub fn mkdirat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
463+
fd: Fd,
448464
path: &P,
449465
mode: Mode,
450466
) -> Result<()> {
451-
let fd = at_rawfd(fd);
467+
use std::os::fd::AsRawFd;
468+
452469
let res = path.with_nix_path(|cstr| unsafe {
453-
libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
470+
libc::mkdirat(
471+
fd.as_fd().as_raw_fd(),
472+
cstr.as_ptr(),
473+
mode.bits() as mode_t,
474+
)
454475
})?;
455476

456477
Errno::result(res).map(drop)

0 commit comments

Comments
 (0)