Skip to content
Closed
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
18 changes: 17 additions & 1 deletion crates/fspy_nostd/src/fs/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustix::fd::FromRawFd as _;

use crate::{
AsRawFd as _, BorrowedFd, CStr, Error, Fat, OwnedFd, Result, Thin,
fs::{Mode, OFlags},
fs::{AtFlags, Mode, OFlags},
};

// Linux UAPI `PATH_MAX`.
Expand Down Expand Up @@ -40,6 +40,22 @@ pub(super) fn openat<R>(
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}

#[expect(clippy::needless_pass_by_value, reason = "CStr is a borrowed value type")]
pub(super) fn unlinkat<R>(dirfd: BorrowedFd<'_>, path: CStr<'_, R>, flags: AtFlags) -> Result<()> {
// SAFETY: `dirfd` remains borrowed and `path` is NUL-terminated for the
// syscall.
unsafe {
syscalls::syscall3(
syscalls::Sysno::unlinkat,
syscall_fd(dirfd)?,
path.as_ptr().addr(),
usize::try_from(flags.bits()).map_err(|_| Error::INVAL)?,
)
}
.map_err(|errno| Error::from_raw_os_error(errno.into_raw()))?;
Ok(())
}

/// Reads the target of `path` relative to `dirfd` into `buf`.
///
/// The returned bytes borrow the initialized prefix of `buf`. As with the
Expand Down
17 changes: 16 additions & 1 deletion crates/fspy_nostd/src/fs/mac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use core::{mem::MaybeUninit, slice};

use rustix::{
fd::{AsFd as _, AsRawFd as _, FromRawFd as _, OwnedFd},
fs::{Mode, OFlags},
fs::{AtFlags, Mode, OFlags},
};

use crate::{BorrowedFd, CStr, CWD, Error, Fat, Result, Thin};
Expand Down Expand Up @@ -37,6 +37,21 @@ pub(super) fn openat<R>(
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}

#[expect(clippy::needless_pass_by_value, reason = "CStr is a borrowed value type")]
pub(super) fn unlinkat<R>(dirfd: BorrowedFd<'_>, path: CStr<'_, R>, flags: AtFlags) -> Result<()> {
// SAFETY: `dirfd` remains borrowed and `path` is NUL-terminated for the
// call.
let result = unsafe {
libc::unlinkat(dirfd.as_raw_fd(), path.as_ptr().cast(), flags.bits().cast_signed())
};
if result == -1 {
// SAFETY: libSystem stored this call's error before returning -1.
Err(Error::from_raw_os_error(unsafe { *libc::__error() }))
} else {
Ok(())
}
}

/// Gets the path associated with `fd`.
///
/// `F_GETPATH` writes a NUL-terminated path into its `MAXPATHLEN` buffer but
Expand Down
11 changes: 10 additions & 1 deletion crates/fspy_nostd/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use core::mem::MaybeUninit;

pub use rustix::fs::{Mode, OFlags, fstat};
pub use rustix::fs::{AtFlags, Mode, OFlags, fstat, ftruncate};

use crate::{BorrowedFd, CStr, Fat, OwnedFd, Result};

Expand Down Expand Up @@ -37,6 +37,15 @@ pub fn openat<R>(
imp::openat(dirfd, path, flags, mode)
}

/// Removes `path` relative to `dirfd`.
///
/// # Errors
///
/// Returns the error reported by `unlinkat`.
pub fn unlinkat<R>(dirfd: BorrowedFd<'_>, path: CStr<'_, R>, flags: AtFlags) -> Result<()> {
imp::unlinkat(dirfd, path, flags)
}

/// Writes the absolute pathname of the current working directory into `buf`.
///
/// The returned C string borrows `buf`, starts at the same address as `buf`,
Expand Down
2 changes: 1 addition & 1 deletion crates/fspy_nostd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ macro_rules! wide_cstr {

#[cfg(unix)]
pub use rustix::{
fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd},
fd::{AsRawFd, BorrowedFd, OwnedFd},
fs::CWD,
io::Errno as Error,
};
Expand Down
56 changes: 29 additions & 27 deletions crates/fspy_shm/src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
use std::{
env::temp_dir,
ffi::OsStr,
fs::{self, File, OpenOptions},
io,
num::NonZeroUsize,
os::unix::{ffi::OsStrExt as _, fs::OpenOptionsExt as _, io::IntoRawFd as _},
os::unix::ffi::OsStrExt as _,
path::PathBuf,
ptr::{self, NonNull},
};
Expand Down Expand Up @@ -75,19 +74,20 @@ pub fn create(size: usize) -> io::Result<(ShmKeeper, ShmHandle)> {
let path = std::path::absolute(temp_dir())?
.join(format!("{BACKING_PREFIX}{}.shm", Uuid::new_v4().simple()));

let file = OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
let file = open_file(
path.as_os_str(),
fspy_nostd::fs::OFlags::RDWR
| fspy_nostd::fs::OFlags::CREATE
| fspy_nostd::fs::OFlags::EXCL
| fspy_nostd::fs::OFlags::CLOEXEC,
// Only the creating user may open the mapping.
.mode(0o600)
.open(&path)?;
fspy_nostd::fs::Mode::RUSR | fspy_nostd::fs::Mode::WUSR,
)?;
// The keeper exists from here on, so every error path below cleans up.
let keeper = ShmKeeper { path };

// Every byte reads as zero because the file is all holes.
file.set_len(size_u64)?;
let file = into_nostd_fd(file);
fspy_nostd::fs::ftruncate(&file, size_u64).map_err(error_to_io)?;

Ok((keeper, ShmHandle { file, size }))
}
Expand All @@ -102,7 +102,11 @@ pub fn create(size: usize) -> io::Result<(ShmKeeper, ShmHandle)> {
/// Returns an error if the shared memory is unavailable, which is the common
/// case once its keeper has been dropped.
pub fn open(id: &OsStr) -> io::Result<ShmHandle> {
let file = open_file(id)?;
let file = open_file(
id,
fspy_nostd::fs::OFlags::RDWR | fspy_nostd::fs::OFlags::CLOEXEC,
fspy_nostd::fs::Mode::empty(),
)?;
// If another process shrinks the file before `map`, mapping fails. If it
// resizes afterwards, nothing here touches the mapped pages. A concurrent
// resize cannot make a mapping access invalid memory.
Expand All @@ -113,16 +117,21 @@ pub fn open(id: &OsStr) -> io::Result<ShmHandle> {
Ok(ShmHandle { file, size })
}

fn open_file(path: &OsStr) -> io::Result<fspy_nostd::OwnedFd> {
fn open_file(
path: &OsStr,
flags: fspy_nostd::fs::OFlags,
mode: fspy_nostd::fs::Mode,
) -> io::Result<fspy_nostd::OwnedFd> {
let mut buf = [0_u8; fspy_nostd::fs::PATH_MAX];
let path = copy_path(path, &mut buf)?;
fspy_nostd::fs::openat(
fspy_nostd::CWD,
path,
fspy_nostd::fs::OFlags::RDWR | fspy_nostd::fs::OFlags::CLOEXEC,
fspy_nostd::fs::Mode::empty(),
)
.map_err(error_to_io)
fspy_nostd::fs::openat(fspy_nostd::CWD, path, flags, mode).map_err(error_to_io)
}

fn remove_file(path: &OsStr) -> io::Result<()> {
let mut buf = [0_u8; fspy_nostd::fs::PATH_MAX];
let path = copy_path(path, &mut buf)?;
fspy_nostd::fs::unlinkat(fspy_nostd::CWD, path, fspy_nostd::fs::AtFlags::empty())
.map_err(error_to_io)
}

fn copy_path<'buf>(
Expand Down Expand Up @@ -150,16 +159,9 @@ fn error_to_io(error: fspy_nostd::Error) -> io::Error {
io::Error::from_raw_os_error(error.raw_os_error())
}

fn into_nostd_fd(file: File) -> fspy_nostd::OwnedFd {
let fd = file.into_raw_fd();
// SAFETY: ownership of `file`'s descriptor transfers without closing or
// duplicating it.
unsafe { fspy_nostd::FromRawFd::from_raw_fd(fd) }
}

impl Drop for ShmKeeper {
fn drop(&mut self) {
let _ = fs::remove_file(&self.path);
let _ = remove_file(self.path.as_os_str());
}
}

Expand Down
Loading