Skip to content

Commit

Permalink
refactor: I/O safety for sys/stat.rs (#2439)
Browse files Browse the repository at this point in the history
* refactor: I/O safety for sys/stat.rs

* fix test on FreeBSD & correct changelog PR number
  • Loading branch information
SteveLauC authored Jun 9, 2024
1 parent e0e6997 commit 8a1e9df
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 123 deletions.
1 change: 1 addition & 0 deletions changelog/2439.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Module `sys/stat` now adopts I/O safety.
27 changes: 19 additions & 8 deletions src/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
/// use nix::errno::Errno;
/// use nix::fcntl::AT_FDCWD;
/// use nix::sys::stat::fstat;
/// use std::os::fd::AsRawFd;
///
/// let never = fstat(AT_FDCWD.as_raw_fd()).unwrap();
/// let never = fstat(AT_FDCWD).unwrap();
/// ```
//
// SAFETY:
Expand Down Expand Up @@ -585,13 +584,20 @@ unsafe fn inner_readlink<P: ?Sized + NixPath>(
Some(_) => unreachable!("redox does not have readlinkat(2)"),
#[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
Some(dirfd) => {
// SAFETY:
//
// If this call of `borrow_raw()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let dirfd = unsafe {
std::os::fd::BorrowedFd::borrow_raw(dirfd)
};
let flags = if path.is_empty() {
AtFlags::AT_EMPTY_PATH
} else {
AtFlags::empty()
};
super::sys::stat::fstatat(
Some(dirfd),
dirfd,
path,
flags | AtFlags::AT_SYMLINK_NOFOLLOW,
)
Expand All @@ -602,11 +608,16 @@ unsafe fn inner_readlink<P: ?Sized + NixPath>(
target_os = "freebsd",
target_os = "hurd"
)))]
Some(dirfd) => super::sys::stat::fstatat(
Some(dirfd),
path,
AtFlags::AT_SYMLINK_NOFOLLOW,
),
Some(dirfd) => {
// SAFETY:
//
// If this call of `borrow_raw()` is safe or not depends on the
// usage of `unsafe fn inner_readlink()`.
let dirfd = unsafe {
std::os::fd::BorrowedFd::borrow_raw(dirfd)
};
super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW)
},
None => super::sys::stat::lstat(path),
}
.map(|x| x.st_size)
Expand Down
73 changes: 47 additions & 26 deletions src/sys/stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ pub use libc::stat as FileStat;
pub use libc::{dev_t, mode_t};

#[cfg(not(target_os = "redox"))]
use crate::fcntl::{at_rawfd, AtFlags};
use crate::fcntl::AtFlags;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::{errno::Errno, NixPath, Result};
use std::mem;
use std::os::unix::io::RawFd;

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

/// Create a special or ordinary file, relative to a given directory.
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
pub fn mknodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn mknodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
kind: SFlag,
perm: Mode,
dev: dev_t,
) -> Result<()> {
let dirfd = at_rawfd(dirfd);
use std::os::fd::AsRawFd;

let res = path.with_nix_path(|cstr| unsafe {
libc::mknodat(
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
kind.bits() | perm.bits() as mode_t,
dev,
Expand Down Expand Up @@ -233,26 +233,29 @@ pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
Ok(unsafe { dst.assume_init() })
}

pub fn fstat(fd: RawFd) -> Result<FileStat> {
pub fn fstat<Fd: std::os::fd::AsFd>(fd: Fd) -> Result<FileStat> {
use std::os::fd::AsRawFd;

let mut dst = mem::MaybeUninit::uninit();
let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
let res = unsafe { libc::fstat(fd.as_fd().as_raw_fd(), dst.as_mut_ptr()) };

Errno::result(res)?;

Ok(unsafe { dst.assume_init() })
}

#[cfg(not(target_os = "redox"))]
pub fn fstatat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn fstatat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
pathname: &P,
f: AtFlags,
) -> Result<FileStat> {
let dirfd = at_rawfd(dirfd);
use std::os::fd::AsRawFd;

let mut dst = mem::MaybeUninit::uninit();
let res = pathname.with_nix_path(|cstr| unsafe {
libc::fstatat(
dirfd,
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
dst.as_mut_ptr(),
f.bits() as libc::c_int,
Expand All @@ -269,8 +272,11 @@ pub fn fstatat<P: ?Sized + NixPath>(
/// # References
///
/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
pub fn fchmod<Fd: std::os::fd::AsFd>(fd: Fd, mode: Mode) -> Result<()> {
use std::os::fd::AsRawFd;

let res =
unsafe { libc::fchmod(fd.as_fd().as_raw_fd(), mode.bits() as mode_t) };

Errno::result(res).map(drop)
}
Expand Down Expand Up @@ -299,19 +305,21 @@ pub enum FchmodatFlags {
///
/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchmodat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn fchmodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
flag: FchmodatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;

let atflag = match flag {
FchmodatFlags::FollowSymlink => AtFlags::empty(),
FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let res = path.with_nix_path(|cstr| unsafe {
libc::fchmodat(
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
atflag.bits() as libc::c_int,
Expand Down Expand Up @@ -383,9 +391,15 @@ pub fn lutimes<P: ?Sized + NixPath>(
///
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
#[inline]
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
pub fn futimens<Fd: std::os::fd::AsFd>(
fd: Fd,
atime: &TimeSpec,
mtime: &TimeSpec,
) -> Result<()> {
use std::os::fd::AsRawFd;

let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = unsafe { libc::futimens(fd, &times[0]) };
let res = unsafe { libc::futimens(fd.as_fd().as_raw_fd(), &times[0]) };

Errno::result(res).map(drop)
}
Expand Down Expand Up @@ -418,21 +432,23 @@ pub enum UtimensatFlags {
///
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
pub fn utimensat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
pub fn utimensat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
atime: &TimeSpec,
mtime: &TimeSpec,
flag: UtimensatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;

let atflag = match flag {
UtimensatFlags::FollowSymlink => AtFlags::empty(),
UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::utimensat(
at_rawfd(dirfd),
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
&times[0],
atflag.bits() as libc::c_int,
Expand All @@ -443,14 +459,19 @@ pub fn utimensat<P: ?Sized + NixPath>(
}

#[cfg(not(target_os = "redox"))]
pub fn mkdirat<P: ?Sized + NixPath>(
fd: Option<RawFd>,
pub fn mkdirat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
fd: Fd,
path: &P,
mode: Mode,
) -> Result<()> {
let fd = at_rawfd(fd);
use std::os::fd::AsRawFd;

let res = path.with_nix_path(|cstr| unsafe {
libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
libc::mkdirat(
fd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
)
})?;

Errno::result(res).map(drop)
Expand Down
Loading

0 comments on commit 8a1e9df

Please sign in to comment.