use std::fs;
use std::io;
use std::io::Read;
use std::path::Path;
use cap_std::fs::Dir;
use cap_tempfile::cap_std;
use rustix::fd::AsFd;
use rustix::fd::BorrowedFd;
use rustix::fs::OFlags;
use rustix::fs::ResolveFlags;
use rustix::path::Arg;
pub(crate) fn open_beneath_rdonly(start: &BorrowedFd, path: &Path) -> io::Result<fs::File> {
let r = path.into_with_c_str(|path_c_str| 'start: loop {
match rustix::fs::openat2(
start,
path_c_str,
OFlags::CLOEXEC | OFlags::RDONLY,
rustix::fs::Mode::empty(),
ResolveFlags::IN_ROOT | ResolveFlags::NO_MAGICLINKS,
) {
Ok(file) => {
return Ok(file);
}
Err(rustix::io::Errno::AGAIN | rustix::io::Errno::INTR) => {
continue 'start;
}
Err(e) => {
return Err(e);
}
}
})?;
Ok(r.into())
}
#[derive(Debug)]
pub struct RootDir(Dir);
impl RootDir {
pub fn new(src: &Dir, path: impl AsRef<Path>) -> io::Result<Self> {
src.open_dir(path).map(Self)
}
pub fn open_ambient_root(
path: impl AsRef<Path>,
authority: cap_std::AmbientAuthority,
) -> io::Result<Self> {
Dir::open_ambient_dir(path, authority).map(Self)
}
pub fn open(&self, path: impl AsRef<Path>) -> io::Result<fs::File> {
let path = path.as_ref();
open_beneath_rdonly(&self.0.as_fd(), path)
}
pub fn open_optional(&self, path: impl AsRef<Path>) -> io::Result<Option<fs::File>> {
crate::dirext::map_optional(self.open(path))
}
pub fn read(&self, path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
let mut f = self.open(path.as_ref())?;
let mut r = Vec::new();
f.read_to_end(&mut r)?;
Ok(r)
}
pub fn read_to_string(&self, path: impl AsRef<Path>) -> io::Result<String> {
let mut f = self.open(path.as_ref())?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
pub fn entries(&self) -> io::Result<cap_std::fs::ReadDir> {
self.0.entries()
}
pub fn read_dir(&self, path: impl AsRef<Path>) -> io::Result<cap_std::fs::ReadDir> {
self.0.read_dir(path.as_ref())
}
pub fn reopen_cap_std(&self) -> io::Result<Dir> {
Dir::reopen_dir(&self.0.as_fd())
}
}
impl From<Dir> for RootDir {
fn from(dir: Dir) -> Self {
Self(dir)
}
}