Skip to content

refactor: I/O safety for sys/stat.rs #2439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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