use crate::{
error::{self, Error, ErrorExt},
resolvers::{self, ResolverFlags},
syscalls::unstable,
Handle,
};
use std::{fs::File, os::unix::io::AsRawFd, path::Path};
use snafu::ResultExt;
lazy_static! {
pub(crate) static ref IS_SUPPORTED: bool =
unstable::openat2(libc::AT_FDCWD, ".", &Default::default()).is_ok();
}
pub(crate) fn resolve<P: AsRef<Path>>(
root: &File,
path: P,
flags: ResolverFlags,
) -> Result<Handle, Error> {
ensure!(*IS_SUPPORTED, error::NotSupported { feature: "openat2" });
let mut how = unstable::OpenHow::default();
how.flags = libc::O_PATH as u64;
how.resolve = unstable::RESOLVE_IN_ROOT | unstable::RESOLVE_NO_MAGICLINKS | flags.bits;
let mut handle: Option<File> = None;
for _ in 0..16 {
match unstable::openat2(root.as_raw_fd(), path.as_ref(), &how) {
Ok(file) => {
handle = Some(file);
break;
}
Err(err) => match err.root_cause().raw_os_error() {
Some(libc::ENOSYS) => break, Some(libc::EAGAIN) => continue,
_ => {
return Err(err).context(error::RawOsError {
operation: "openat2 subpath",
})?
}
},
}
}
handle.map_or_else(
|| {
resolvers::user::resolve(root, path, flags)
.wrap("fallback user-space resolution for RESOLVE_IN_ROOT")
},
|file| Ok(Handle::from_file_unchecked(file)),
)
}