use cap_std::ambient_authority;
use cap_std::fs::{Dir, OpenOptions, OpenOptionsExt};
use nix::errno::Errno;
use nix::fcntl::{fcntl, FcntlArg, OFlag};
use nix::sys::stat::Mode;
use std::io;
use std::os::fd::{IntoRawFd, RawFd};
use std::path::{Path, PathBuf};
pub(crate) fn resolve_beneath(
root: &Path,
relative: &Path,
flags: OFlag,
mode: Mode,
) -> Result<RawFd, Errno> {
let dir = Dir::open_ambient_dir(root, ambient_authority()).map_err(io_to_errno)?;
if flags.contains(OFlag::O_DIRECTORY) {
let sub = dir.open_dir(relative).map_err(io_to_errno)?;
return Ok(sub.into_raw_fd());
}
let acc = flags & OFlag::O_ACCMODE;
let write = acc == OFlag::O_WRONLY || acc == OFlag::O_RDWR;
let mut opts = OpenOptions::new();
opts.read(!write)
.write(write)
.create(flags.contains(OFlag::O_CREAT))
.create_new(flags.contains(OFlag::O_EXCL))
.truncate(flags.contains(OFlag::O_TRUNC));
if acc == OFlag::O_RDWR {
opts.read(true);
}
if flags.contains(OFlag::O_APPEND) {
opts.append(true);
}
opts.mode(u32::from(mode.bits()));
if flags.contains(OFlag::O_NOFOLLOW) {
opts.custom_flags(OFlag::O_NOFOLLOW.bits());
}
let file = dir.open_with(relative, &opts).map_err(io_to_errno)?;
Ok(file.into_raw_fd())
}
pub(crate) fn fd_real_path(fd: RawFd) -> io::Result<PathBuf> {
let mut path = PathBuf::new();
fcntl(fd, FcntlArg::F_GETPATH(&mut path))
.map_err(|errno| io::Error::from_raw_os_error(errno as i32))?;
Ok(path)
}
fn io_to_errno(error: io::Error) -> Errno {
if let Some(raw) = error.raw_os_error() {
Errno::from_raw(raw)
} else if error.kind() == io::ErrorKind::PermissionDenied {
Errno::EXDEV
} else {
Errno::EIO
}
}