Skip to content

Commit

Permalink
Support type-safe path FDs
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Saveau <[email protected]>
  • Loading branch information
SUPERCILEX committed Dec 8, 2022
1 parent 50dc2ef commit a56ddf2
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 145 deletions.
2 changes: 1 addition & 1 deletion src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl Dir {

/// Opens the given path as with `fcntl::openat`.
pub fn openat<Fd: AsFd, P: ?Sized + NixPath>(
dirfd: &Fd,
dirfd: Fd,
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
Expand Down
55 changes: 28 additions & 27 deletions src/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::os::unix::io::FromRawFd;
use std::os::unix::io::OwnedFd;
use std::os::unix::io::RawFd;

use crate::IntoPathFd;
#[cfg(feature = "fs")]
use crate::{sys::stat::Mode, NixPath, Result};
#[cfg(any(target_os = "android", target_os = "linux"))]
Expand Down Expand Up @@ -211,15 +212,15 @@ pub fn open<P: ?Sized + NixPath>(
// The conversion is not identical on all operating systems.
#[allow(clippy::useless_conversion)]
#[cfg(not(target_os = "redox"))]
pub fn openat<Fd: AsFd, P: ?Sized + NixPath>(
dirfd: &Fd,
pub fn openat<Fd: IntoPathFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<OwnedFd> {
let fd = path.with_nix_path(|cstr| unsafe {
libc::openat(
dirfd.as_fd().as_raw_fd(),
dirfd.as_raw_path_fd(),
cstr.as_ptr(),
oflag.bits(),
mode.bits() as c_uint,
Expand All @@ -230,18 +231,18 @@ pub fn openat<Fd: AsFd, P: ?Sized + NixPath>(
}

#[cfg(not(target_os = "redox"))]
pub fn renameat<Fd1: AsFd, P1: ?Sized + NixPath, Fd2: AsFd, P2: ?Sized + NixPath>(
old_dirfd: &Fd1,
pub fn renameat<Fd1: IntoPathFd, P1: ?Sized + NixPath, Fd2: IntoPathFd, P2: ?Sized + NixPath>(
old_dirfd: Fd1,
old_path: &P1,
new_dirfd: &Fd2,
new_dirfd: Fd2,
new_path: &P2,
) -> Result<()> {
let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat(
old_dirfd.as_fd().as_raw_fd(),
old_dirfd.as_raw_path_fd(),
old_cstr.as_ptr(),
new_dirfd.as_fd().as_raw_fd(),
new_dirfd.as_raw_path_fd(),
new_cstr.as_ptr(),
)
})
Expand All @@ -264,19 +265,19 @@ libc_bitflags! {
feature! {
#![feature = "fs"]
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub fn renameat2<Fd1: AsFd, P1: ?Sized + NixPath, Fd2: AsFd, P2: ?Sized + NixPath>(
old_dirfd: &Fd1,
pub fn renameat2<Fd1: IntoPathFd, P1: ?Sized + NixPath, Fd2: IntoPathFd, P2: ?Sized + NixPath>(
old_dirfd: Fd1,
old_path: &P1,
new_dirfd: &Fd2,
new_dirfd: Fd2,
new_path: &P2,
flags: RenameFlags,
) -> Result<()> {
let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat2(
old_dirfd.as_fd().as_raw_fd(),
old_dirfd.as_raw_path_fd(),
old_cstr.as_ptr(),
new_dirfd.as_fd().as_raw_fd(),
new_dirfd.as_raw_path_fd(),
new_cstr.as_ptr(),
flags.bits(),
)
Expand Down Expand Up @@ -404,10 +405,10 @@ pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {

#[cfg(not(target_os = "redox"))]
pub fn readlinkat<Fd: AsFd, P: ?Sized + NixPath>(
dirfd: &Fd,
dirfd: Fd,
path: &P,
) -> Result<OsString> {
inner_readlink(Some(dirfd), path)
inner_readlink(Some(&dirfd), path)
}
}

Expand Down Expand Up @@ -599,9 +600,9 @@ feature! {
/// returned.
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
fd_in: &Fd1,
fd_in: Fd1,
off_in: Option<&mut libc::loff_t>,
fd_out: &Fd2,
fd_out: Fd2,
off_out: Option<&mut libc::loff_t>,
len: usize,
) -> Result<usize> {
Expand All @@ -628,9 +629,9 @@ pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice<Fd1: AsFd, Fd2: AsFd>(
fd_in: &Fd1,
fd_in: Fd1,
off_in: Option<&mut libc::loff_t>,
fd_out: &Fd2,
fd_out: Fd2,
off_out: Option<&mut libc::loff_t>,
len: usize,
flags: SpliceFFlags,
Expand All @@ -657,8 +658,8 @@ pub fn splice<Fd1: AsFd, Fd2: AsFd>(

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn tee<Fd1: AsFd, Fd2: AsFd>(
fd_in: &Fd1,
fd_out: &Fd2,
fd_in: Fd1,
fd_out: Fd2,
len: usize,
flags: SpliceFFlags,
) -> Result<usize> {
Expand All @@ -675,7 +676,7 @@ pub fn tee<Fd1: AsFd, Fd2: AsFd>(

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn vmsplice<Fd: AsFd>(
fd: &Fd,
fd: Fd,
iov: &[std::io::IoSlice<'_>],
flags: SpliceFFlags,
) -> Result<usize> {
Expand Down Expand Up @@ -734,7 +735,7 @@ feature! {
#[cfg(any(target_os = "linux"))]
#[cfg(feature = "fs")]
pub fn fallocate<Fd: AsFd>(
fd: &Fd,
fd: Fd,
mode: FallocateFlags,
offset: libc::off_t,
len: libc::off_t,
Expand Down Expand Up @@ -810,7 +811,7 @@ impl SpacectlRange {
/// ```
#[cfg(target_os = "freebsd")]
pub fn fspacectl<Fd: AsFd>(
fd: &Fd,
fd: Fd,
range: SpacectlRange,
) -> Result<SpacectlRange> {
let mut rqsr = libc::spacectl_range {
Expand Down Expand Up @@ -861,7 +862,7 @@ pub fn fspacectl<Fd: AsFd>(
/// ```
#[cfg(target_os = "freebsd")]
pub fn fspacectl_all<Fd: AsFd>(
fd: &Fd,
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> {
Expand Down Expand Up @@ -917,7 +918,7 @@ mod posix_fadvise {
feature! {
#![feature = "fs"]
pub fn posix_fadvise<Fd: AsFd>(
fd: &Fd,
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
advice: PosixFadviseAdvice,
Expand Down Expand Up @@ -950,7 +951,7 @@ mod posix_fadvise {
target_os = "freebsd"
))]
pub fn posix_fallocate<Fd: AsFd>(
fd: &Fd,
fd: Fd,
offset: libc::off_t,
len: libc::off_t,
) -> Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion src/kmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ libc_bitflags!(
///
/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn finit_module<Fd: AsFd>(
fd: &Fd,
fd: Fd,
param_values: &CStr,
flags: ModuleInitFlags,
) -> Result<()> {
Expand Down
32 changes: 22 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ pub mod unistd;
use std::ffi::{CStr, CString, OsStr};
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
#[cfg(not(target_os = "redox"))]
use std::os::unix::io::BorrowedFd;
use std::os::unix::io::{AsFd, AsRawFd, RawFd};
use std::path::{Path, PathBuf};
use std::{ptr, result, slice};

Expand All @@ -184,16 +183,29 @@ pub type Result<T> = result::Result<T, Errno>;
/// ones.
pub type Error = Errno;

/// A file descriptor representing the current working directory.
/// A trait that models requirements for path file descriptors.
pub trait IntoPathFd {
/// Extracts the raw file descriptor.
fn as_raw_path_fd(&self) -> RawFd;
}

impl<Fd: AsFd> IntoPathFd for Fd {
fn as_raw_path_fd(&self) -> RawFd {
self.as_fd().as_raw_fd()
}
}

/// The `AT_FDCWD` marker FD.
#[cfg(not(target_os = "redox"))]
#[derive(Copy, Clone, Debug)]
pub struct Cwd;

#[cfg(not(target_os = "redox"))]
pub const AT_FDCWD: &BorrowedFd<'static> = unsafe {
&BorrowedFd::borrow_raw(if cfg!(target_os = "haiku") {
// Hack to work around BorrowedFd not allowing -1
-2
} else {
impl IntoPathFd for Cwd {
fn as_raw_path_fd(&self) -> RawFd {
libc::AT_FDCWD
})
};
}
}

/// Common trait used to represent file system paths by many Nix functions.
pub trait NixPath {
Expand Down
64 changes: 40 additions & 24 deletions src/sys/epoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::errno::Errno;
use crate::Result;
use libc::{self, c_int};
use std::mem;
use std::os::unix::io::{FromRawFd,RawFd, OwnedFd, AsFd, AsRawFd};
use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};

libc_bitflags!(
pub struct EpollFlags: c_int {
Expand Down Expand Up @@ -78,22 +78,22 @@ impl EpollEvent {
/// # fn main() -> nix::Result<()> {
/// const DATA: u64 = 17;
/// const MILLIS: u64 = 100;
///
///
/// // Create epoll
/// let epoll = Epoll::new(EpollCreateFlags::empty())?;
///
///
/// // Create eventfd & Add event
/// let eventfd = unsafe { OwnedFd::from_raw_fd(eventfd(0, EfdFlags::empty())?) };
/// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?;
///
///
/// // Arm eventfd & Time wait
/// write(eventfd.as_raw_fd(), &1u64.to_ne_bytes())?;
/// write(&eventfd, &1u64.to_ne_bytes())?;
/// let now = Instant::now();
///
///
/// // Wait on event
/// let mut events = [EpollEvent::empty()];
/// epoll.wait(&mut events, MILLIS as isize)?;
///
///
/// // Assert data correct & timeout didn't occur
/// assert_eq!(events[0].data(), DATA);
/// assert!(now.elapsed() < Duration::from_millis(MILLIS));
Expand All @@ -104,7 +104,7 @@ impl EpollEvent {
pub struct Epoll(pub OwnedFd);
impl Epoll {
/// Creates a new epoll instance and returns a file descriptor referring to that instance.
///
///
/// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html).
pub fn new(flags: EpollCreateFlags) -> Result<Self> {
let res = unsafe { libc::epoll_create1(flags.bits()) };
Expand All @@ -113,30 +113,38 @@ impl Epoll {
Ok(Self(owned_fd))
}
/// Add an entry to the interest list of the epoll file descriptor for
/// specified in events.
///
/// specified in events.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`.
pub fn add<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlAdd,fd,&mut event)
self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event)
}
/// Remove (deregister) the target file descriptor `fd` from the interest list.
///
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` .
pub fn delete<Fd: AsFd>(&self, fd: Fd) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlDel,fd,None)
self.epoll_ctl(EpollOp::EpollCtlDel, fd, None)
}
/// Change the settings associated with `fd` in the interest list to the new settings specified
/// in `event`.
///
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`.
pub fn modify<Fd: AsFd>(&self,fd: Fd, event: &mut EpollEvent) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlMod,fd,event)
pub fn modify<Fd: AsFd>(
&self,
fd: Fd,
event: &mut EpollEvent,
) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlMod, fd, event)
}
/// Waits for I/O events, blocking the calling thread if no events are currently available.
/// (This can be thought of as fetching items from the ready list of the epoll instance.)
///
///
/// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html)
pub fn wait(&self, events: &mut [EpollEvent], timeout: isize) -> Result<usize> {
pub fn wait(
&self,
events: &mut [EpollEvent],
timeout: isize,
) -> Result<usize> {
let res = unsafe {
libc::epoll_wait(
self.0.as_raw_fd(),
Expand All @@ -145,15 +153,15 @@ impl Epoll {
timeout as c_int,
)
};

Errno::result(res).map(|r| r as usize)
}
/// This system call is used to add, modify, or remove entries in the interest list of the epoll
/// instance referred to by `self`. It requests that the operation `op` be performed for the
/// target file descriptor, `fd`.
///
///
/// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`].
///
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
fn epoll_ctl<'a, Fd: AsFd, T>(
&self,
Expand All @@ -165,9 +173,17 @@ impl Epoll {
T: Into<Option<&'a mut EpollEvent>>,
{
let event: Option<&mut EpollEvent> = event.into();
let ptr = event.map(|x|&mut x.event as *mut libc::epoll_event).unwrap_or(std::ptr::null_mut());
let ptr = event
.map(|x| &mut x.event as *mut libc::epoll_event)
.unwrap_or(std::ptr::null_mut());
unsafe {
Errno::result(libc::epoll_ctl(self.0.as_raw_fd(), op as c_int, fd.as_fd().as_raw_fd(), ptr)).map(drop)
Errno::result(libc::epoll_ctl(
self.0.as_raw_fd(),
op as c_int,
fd.as_fd().as_raw_fd(),
ptr,
))
.map(drop)
}
}
}
Expand Down Expand Up @@ -231,4 +247,4 @@ pub fn epoll_wait(
};

Errno::result(res).map(|r| r as usize)
}
}
Loading

0 comments on commit a56ddf2

Please sign in to comment.