use std::path::PathBuf;
#[cfg(feature = "builder")]
use std::sync::RwLockWriteGuard;
use std::sync::{RwLock, RwLockReadGuard};
use log::error;
use crate::errors::*;
use crate::property_area::PropertyAreaMap;
pub(crate) struct ContextNode {
access_rw: bool,
filename: PathBuf,
property_area: RwLock<Option<PropertyAreaMap>>,
}
impl ContextNode {
pub(crate) fn new(access_rw: bool, filename: PathBuf) -> Self {
Self {
access_rw,
filename,
property_area: RwLock::new(None),
}
}
pub(crate) fn open(&self, fsetxattr_failed: &mut bool) -> Result<()> {
if !self.access_rw {
error!(
"Attempted to open context node without write access: {:?}",
self.filename
);
return Err(Error::LockError(format!(
"open() requires access_rw == true: {:?}",
self.filename
)));
}
let mut prop_area = self.property_area.write().map_err(lock_err("write"))?;
if prop_area.is_some() {
return Ok(());
}
*prop_area = Some(PropertyAreaMap::new_rw(
self.filename.as_path(),
None,
fsetxattr_failed,
)?);
Ok(())
}
pub(crate) fn property_area(&self) -> Result<PropertyAreaGuard<'_>> {
{
let guard = self.property_area.read().map_err(lock_err("read"))?;
if guard.is_some() {
return Ok(PropertyAreaGuard::from_initialized(guard));
}
}
{
let mut guard = self.property_area.write().map_err(lock_err("write"))?;
if guard.is_none() {
*guard = Some(PropertyAreaMap::new_ro(self.filename.as_path())?);
}
}
let guard = self.property_area.read().map_err(lock_err("read"))?;
Ok(PropertyAreaGuard::from_initialized(guard))
}
#[cfg(feature = "builder")]
pub(crate) fn property_area_mut(&self) -> Result<PropertyAreaMutGuard<'_>> {
let mut guard = self.property_area.write().map_err(lock_err("write"))?;
if guard.is_none() {
*guard = Some(PropertyAreaMap::new_ro(self.filename.as_path())?);
}
Ok(PropertyAreaMutGuard::from_initialized(guard))
}
}
fn lock_err<T>(kind: &'static str) -> impl Fn(std::sync::PoisonError<T>) -> Error {
move |e| {
Error::LockError(format!(
"Failed to acquire {kind} lock on property area: {e}"
))
}
}
pub(crate) struct PropertyAreaGuard<'a> {
guard: RwLockReadGuard<'a, Option<PropertyAreaMap>>,
}
impl<'a> PropertyAreaGuard<'a> {
fn from_initialized(guard: RwLockReadGuard<'a, Option<PropertyAreaMap>>) -> Self {
debug_assert!(
guard.is_some(),
"PropertyAreaGuard constructed from uninitialized Option — \
this would have panicked in release at the access site"
);
Self { guard }
}
pub(crate) fn property_area(&self) -> &PropertyAreaMap {
self.guard
.as_ref()
.expect("PropertyAreaGuard constructed only from Some(...); see ContextNode invariant")
}
}
#[cfg(feature = "builder")]
pub(crate) struct PropertyAreaMutGuard<'a> {
guard: RwLockWriteGuard<'a, Option<PropertyAreaMap>>,
}
#[cfg(feature = "builder")]
impl<'a> PropertyAreaMutGuard<'a> {
fn from_initialized(guard: RwLockWriteGuard<'a, Option<PropertyAreaMap>>) -> Self {
debug_assert!(
guard.is_some(),
"PropertyAreaMutGuard constructed from uninitialized Option — \
this would have panicked in release at the access site"
);
Self { guard }
}
pub(crate) fn property_area_mut(&mut self) -> &mut PropertyAreaMap {
self.guard.as_mut().expect(
"PropertyAreaMutGuard constructed only from Some(...); see ContextNode invariant",
)
}
}