use std::path::{Path, PathBuf};
use std::sync::Arc;
use prikk_error::{PrikkError, Result};
use super::directory::{PlatformAuthority, relative_components};
use super::{failpoints, windows};
pub(super) struct WindowsAuthority {
handle: std::fs::File,
identity: prikk_ffi::FileIdentity,
}
fn io_error(path: &Path, error: std::io::Error) -> PrikkError {
PrikkError::Io(format!("{}: {error}", path.display()))
}
impl WindowsAuthority {
fn verified_anchor_path(&self) -> Result<PathBuf> {
let current_path = prikk_ffi::current_path_of(&self.handle)
.map_err(|error| io_error(Path::new("<retained Windows anchor handle>"), error))?;
failpoints::wait_at_anchor_verification();
let file = windows::open_directory_no_follow(¤t_path)?;
let identity = windows::identity_no_follow(&file, ¤t_path)?;
if identity != self.identity {
return Err(PrikkError::Integrity(format!(
"Windows anchor replaced: {} no longer identifies the directory this authority \
was bound to",
current_path.display()
)));
}
Ok(current_path)
}
pub(super) fn resolve_prepared(&self, relative: &Path) -> Result<PathBuf> {
let mut current = self.verified_anchor_path()?;
for component in relative_components(relative)? {
current.push(component);
windows::ensure_directory_component_no_follow(¤t)?;
}
Ok(current)
}
pub(super) fn resolve_existing(&self, relative: &Path) -> Result<PathBuf> {
let mut current = self.verified_anchor_path()?;
for component in relative_components(relative)? {
current.push(component);
windows::open_directory_no_follow(¤t)?;
}
Ok(current)
}
pub(super) fn resolve_existing_for_read(&self, relative: &Path) -> Result<Option<PathBuf>> {
let mut current = self.verified_anchor_path()?;
for component in relative_components(relative)? {
current.push(component);
if windows::stat_directory_no_follow(¤t)?.is_none() {
return Ok(None);
}
}
Ok(Some(current))
}
}
impl PlatformAuthority for Arc<WindowsAuthority> {
fn bind(path: &Path) -> Result<Self> {
let handle = windows::open_directory_no_follow(path)?;
let identity = windows::identity_no_follow(&handle, path)?;
Ok(Arc::new(WindowsAuthority { handle, identity }))
}
fn same_as(&self, _self_path: &Arc<PathBuf>, other: &Self, _other_path: &Arc<PathBuf>) -> bool {
Arc::ptr_eq(self, other)
}
fn ensure_child(&self, relative: &Path) -> Result<Self> {
let mut current = self.verified_anchor_path()?;
let mut handle = self
.handle
.try_clone()
.map_err(|error| io_error(¤t, error))?;
let mut identity = self.identity;
for component in relative_components(relative)? {
current.push(component);
handle = windows::ensure_directory_component_no_follow(¤t)?;
identity = windows::identity_no_follow(&handle, ¤t)?;
}
Ok(Arc::new(WindowsAuthority { handle, identity }))
}
fn open_child(&self, relative: &Path) -> Result<Self> {
let mut current = self.verified_anchor_path()?;
let mut handle = self
.handle
.try_clone()
.map_err(|error| io_error(¤t, error))?;
let mut identity = self.identity;
for component in relative_components(relative)? {
current.push(component);
handle = windows::open_directory_no_follow(¤t)?;
identity = windows::identity_no_follow(&handle, ¤t)?;
}
Ok(Arc::new(WindowsAuthority { handle, identity }))
}
}
#[cfg(test)]
mod tests;