use std::path::Path;
use prikk_error::{PrikkError, Result};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use super::{failpoints, io_error};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use rustix::fd::{AsFd, OwnedFd};
#[cfg(any(target_os = "linux", target_os = "macos"))]
use rustix::fs::{self, FileType, Mode, OFlags};
pub(super) fn required_parent(path: &Path) -> Result<&Path> {
path.parent()
.ok_or_else(|| PrikkError::Io("relative mutation path has no parent".to_string()))
}
pub(super) fn required_file_name(path: &Path) -> Result<&std::ffi::OsStr> {
path.file_name()
.ok_or_else(|| PrikkError::Io("relative mutation path has no file name".to_string()))
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(super) fn open_new_regular(
directory: impl AsFd,
name: &std::ffi::OsStr,
) -> rustix::io::Result<OwnedFd> {
failpoints::required_open().map_err(|_| rustix::io::Errno::IO)?;
fs::openat(
directory,
name,
OFlags::WRONLY
| OFlags::CREATE
| OFlags::EXCL
| OFlags::NONBLOCK
| OFlags::NOFOLLOW
| OFlags::CLOEXEC,
Mode::from_raw_mode(0o600),
)
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(super) fn open_existing_regular(
directory: impl AsFd,
name: &std::ffi::OsStr,
flags: OFlags,
) -> Result<OwnedFd> {
failpoints::required_open()?;
let fd = fs::openat(
directory,
name,
flags | OFlags::NONBLOCK | OFlags::NOFOLLOW | OFlags::CLOEXEC,
Mode::empty(),
)
.map_err(io_error)?;
validate_regular(&fd)?;
Ok(fd)
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(super) fn open_existing_regular_if_exists(
directory: impl AsFd,
name: &std::ffi::OsStr,
flags: OFlags,
) -> Result<Option<OwnedFd>> {
failpoints::required_open()?;
let fd = match fs::openat(
directory,
name,
flags | OFlags::NONBLOCK | OFlags::NOFOLLOW | OFlags::CLOEXEC,
Mode::empty(),
) {
Ok(fd) => fd,
Err(rustix::io::Errno::NOENT) => return Ok(None),
Err(error) => return Err(io_error(error)),
};
validate_regular(&fd)?;
Ok(Some(fd))
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(super) fn open_append_regular(directory: impl AsFd, name: &std::ffi::OsStr) -> Result<OwnedFd> {
open_existing_regular(directory, name, OFlags::WRONLY | OFlags::APPEND)
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn validate_regular(fd: impl AsFd) -> Result<()> {
let stat = fs::fstat(fd).map_err(io_error)?;
if FileType::from_raw_mode(stat.st_mode).is_file() {
return Ok(());
}
Err(PrikkError::Integrity(
"mutation target is not a regular file".to_string(),
))
}