use std::fs::File;
use std::io;
use std::path::Path;
#[cfg(not(target_arch = "wasm32"))]
use std::path::{Component, PathBuf};
pub fn open_file_within_root<P, R>(path: P, root: R) -> io::Result<File>
where
P: AsRef<Path>,
R: AsRef<Path>,
{
#[cfg(target_arch = "wasm32")]
{
let _ = (path, root);
Err(io::Error::new(
io::ErrorKind::Unsupported,
"root-confined native file opening is unsupported on WebAssembly",
))
}
#[cfg(not(target_arch = "wasm32"))]
{
let root = root.as_ref();
let relative = relative_path(path.as_ref(), root)?;
#[cfg(unix)]
{
super::unix::open(root, &relative)
}
#[cfg(windows)]
{
super::windows::open(root, &relative)
}
#[cfg(not(any(unix, windows)))]
{
let _ = (root, relative);
Err(io::Error::new(
io::ErrorKind::Unsupported,
"root-confined native file opening is unsupported on this target",
))
}
}
}
#[cfg(not(target_arch = "wasm32"))]
fn relative_path(path: &Path, root: &Path) -> io::Result<PathBuf> {
let relative = if path.is_absolute() {
path.strip_prefix(root).map_err(|_| {
io::Error::new(
io::ErrorKind::PermissionDenied,
format!("path {} is outside root {}", path.display(), root.display()),
)
})?
} else {
path
};
if relative.as_os_str().is_empty() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path resolves to the root directory",
));
}
if relative
.components()
.any(|component| !matches!(component, Component::Normal(_)))
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"path {} contains a non-normal component",
relative.display()
),
));
}
Ok(relative.to_path_buf())
}