use std::ffi::CStr;
use std::fs::File;
use std::io;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::CPath;
#[derive(Clone, Copy, Debug)]
pub struct AbsolutePath;
impl AsRawFd for AbsolutePath {
fn as_raw_fd(&self) -> RawFd {
-libc::EBADF
}
}
impl AsFd for AbsolutePath {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(-libc::EBADF) }
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct RawOpenHow {
pub flags: u64,
pub mode: u64,
pub resolve: u64,
}
impl Default for RawOpenHow {
fn default() -> Self {
Self::new()
}
}
impl RawOpenHow {
pub const fn new() -> Self {
Self {
flags: libc::O_CLOEXEC as u64,
mode: 0,
resolve: 0,
}
}
pub const fn new_empty() -> Self {
Self {
flags: 0,
mode: 0,
resolve: 0,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct OpenHow<'a> {
pub how: RawOpenHow,
pub fd: Option<BorrowedFd<'a>>,
}
impl Default for OpenHow<'static> {
fn default() -> Self {
Self::new()
}
}
impl OpenHow<'static> {
pub const fn new_empty() -> Self {
Self {
how: RawOpenHow::new_empty(),
fd: None,
}
}
pub const fn new() -> Self {
Self {
how: RawOpenHow::new(),
fd: None,
}
}
pub const fn new_read() -> Self {
let mut how = RawOpenHow::new();
how.flags |= libc::O_RDONLY as u64;
Self { how, fd: None }
}
pub const fn new_write() -> Self {
let mut how = RawOpenHow::new();
how.flags |= libc::O_WRONLY as u64;
Self { how, fd: None }
}
pub const fn new_rw() -> Self {
let mut how = RawOpenHow::new();
how.flags |= libc::O_RDWR as u64;
Self { how, fd: None }
}
pub const fn new_directory() -> Self {
let mut how = RawOpenHow::new();
how.flags |= libc::O_DIRECTORY as u64;
Self { how, fd: None }
}
}
impl OpenHow<'_> {
pub fn set_flags(mut self, on: bool, flags: u64) -> Self {
if on {
self.how.flags |= flags;
} else {
self.how.flags &= !flags;
}
self
}
fn set_resolve(mut self, on: bool, resolve: u64) -> Self {
if on {
self.how.resolve |= resolve;
} else {
self.how.resolve &= !resolve;
}
self
}
pub fn resolve_beneath(self, on: bool) -> Self {
self.set_resolve(on, libc::RESOLVE_BENEATH)
}
pub fn resolve_in_root(self, on: bool) -> Self {
self.set_resolve(on, libc::RESOLVE_IN_ROOT)
}
pub fn at_fd<F>(self, fd: &F) -> OpenHow<'_>
where
F: ?Sized + AsFd,
{
OpenHow {
how: self.how,
fd: Some(fd.as_fd()),
}
}
pub unsafe fn at_fd_raw(self, fd: RawFd) -> OpenHow<'static> {
OpenHow {
how: self.how,
fd: Some(unsafe { BorrowedFd::borrow_raw(fd) }),
}
}
pub fn resolve_no_magiclinks(self, on: bool) -> Self {
self.set_resolve(on, libc::RESOLVE_NO_MAGICLINKS)
}
pub fn resolve_no_symlinks(self, on: bool) -> Self {
self.set_resolve(on, libc::RESOLVE_NO_SYMLINKS)
}
pub fn resolve_no_xdev(self, on: bool) -> Self {
self.set_resolve(on, libc::RESOLVE_NO_XDEV)
}
pub fn resolve_cached_only(self, on: bool) -> Self {
self.set_resolve(on, libc::RESOLVE_CACHED)
}
pub fn mode(mut self, mode: u64) -> Self {
self.how.mode = mode;
self
}
pub fn flags(self, flags: u64) -> Self {
self.set_flags(true, flags)
}
pub fn directory(self, on: bool) -> Self {
self.set_flags(on, libc::O_DIRECTORY as u64)
}
pub fn create(self, on: bool) -> Self {
self.set_flags(on, libc::O_CREAT as u64)
}
pub fn fail_if_exists(self, on: bool) -> Self {
self.set_flags(on, libc::O_EXCL as u64)
}
pub fn truncate(self, on: bool) -> Self {
self.set_flags(on, libc::O_TRUNC as u64)
}
pub fn no_final_symlink(self, on: bool) -> Self {
self.set_flags(on, libc::O_NOFOLLOW as u64)
}
pub fn append(self, on: bool) -> Self {
self.set_flags(on, libc::O_APPEND as u64)
}
}
impl OpenHow<'_> {
pub fn open<P>(&self, path: &P) -> io::Result<OwnedFd>
where
P: ?Sized + CPath,
{
path.c_path(|path| self.open_raw(path))?
}
pub fn open_raw(&self, path: &CStr) -> io::Result<OwnedFd> {
self.open_at_raw(
self.fd.map(|fd| fd.as_raw_fd()).unwrap_or(libc::AT_FDCWD),
path,
)
}
pub fn open_at_raw(&self, dirfd: RawFd, path: &CStr) -> io::Result<OwnedFd> {
let res = unsafe {
libc::syscall(
libc::SYS_openat2,
dirfd,
path.as_ptr(),
&self.how,
std::mem::size_of_val(&self.how),
)
};
if res < 0 {
return Err(io::Error::last_os_error());
}
Ok(unsafe { OwnedFd::from_raw_fd(res as RawFd) })
}
pub fn open_file<P>(&self, path: &P) -> io::Result<File>
where
P: ?Sized + CPath,
{
let fd = self.open(path)?;
Ok(unsafe { File::from_raw_fd(fd.into_raw_fd()) })
}
}