use crate::{
error::{Error, ErrorImpl},
flags::{OpenFlags, ResolverFlags},
resolvers::PartialLookup,
syscalls::{self, OpenHow},
utils::PathIterExt,
Handle,
};
use std::{
fs::File,
os::unix::io::AsFd,
path::{Path, PathBuf},
};
pub(crate) fn open(
root: impl AsFd,
path: impl AsRef<Path>,
rflags: ResolverFlags,
oflags: OpenFlags,
) -> Result<File, Error> {
let rflags = libc::RESOLVE_IN_ROOT | libc::RESOLVE_NO_MAGICLINKS | rflags.bits();
let how = OpenHow {
flags: oflags.bits() as u64,
resolve: rflags,
..Default::default()
};
syscalls::openat2_follow(&root, path.as_ref(), how)
.map(File::from)
.map_err(|err| {
ErrorImpl::RawOsError {
operation: "openat2 one-shot open".into(),
source: err,
}
.into()
})
}
pub(crate) fn resolve(
root: impl AsFd,
path: impl AsRef<Path>,
rflags: ResolverFlags,
no_follow_trailing: bool,
) -> Result<Handle, Error> {
let mut oflags = OpenFlags::O_PATH;
if no_follow_trailing {
oflags.insert(OpenFlags::O_NOFOLLOW);
}
let rflags = libc::RESOLVE_IN_ROOT | libc::RESOLVE_NO_MAGICLINKS | rflags.bits();
let how = OpenHow {
flags: oflags.bits() as u64,
resolve: rflags,
..Default::default()
};
syscalls::openat2_follow(&root, path.as_ref(), how)
.map(Handle::from_fd)
.map_err(|err| {
ErrorImpl::RawOsError {
operation: "openat2 subpath".into(),
source: err,
}
.into()
})
}
pub(crate) fn resolve_partial(
root: impl AsFd,
path: impl AsRef<Path>,
rflags: ResolverFlags,
no_follow_trailing: bool,
) -> Result<PartialLookup<Handle>, Error> {
let root = root.as_fd();
let path = path.as_ref();
let mut last_error = match resolve(root, path, rflags, no_follow_trailing) {
Ok(handle) => return Ok(PartialLookup::Complete(handle)),
Err(err) => err,
};
for (path, remaining) in path.partial_ancestors() {
if last_error.is_safety_violation() {
return Err(last_error);
}
match resolve(root, path, rflags, no_follow_trailing) {
Ok(handle) => {
return Ok(PartialLookup::Partial {
handle,
remaining: remaining.map(PathBuf::from).unwrap_or("".into()),
last_error,
})
}
Err(err) => last_error = err,
}
}
Err(last_error)
}