use std::ffi::CStr;
use std::path::Path;
use crate::errors::*;
use log::{error, info, warn};
use rustix::fs;
#[cfg(feature = "builder")]
use crate::context_node::PropertyAreaMutGuard;
use crate::context_node::{ContextNode, PropertyAreaGuard};
use crate::property_area::{PropertyArea, PropertyAreaMap};
use crate::property_info_parser::PropertyInfoAreaFile;
const PROPERTIES_SERIAL_CONTEXT: &CStr = c"u:object_r:properties_serial:s0";
pub(crate) struct ContextsSerialized {
property_info_area_file: PropertyInfoAreaFile,
context_nodes: Vec<ContextNode>,
serial_property_area_map: PropertyAreaMap,
}
impl ContextsSerialized {
pub(crate) fn new(
writable: bool,
dirname: &Path,
fsetxattr_failed: &mut bool,
load_default_path: bool,
) -> Result<Self> {
let dirname = dirname.to_path_buf();
let tree_filename = dirname.join("property_info");
let serial_filename = dirname.join("properties_serial");
let property_info_area_file = if load_default_path {
PropertyInfoAreaFile::load_default_path()
} else {
PropertyInfoAreaFile::load_path(tree_filename.as_path())
}?;
let property_info_area = property_info_area_file.property_info_area();
let num_context_nodes = property_info_area.num_contexts();
let mut context_nodes = Vec::with_capacity(num_context_nodes);
for i in 0..num_context_nodes {
let context_offset = property_info_area.context_offset(i)?;
let context_name = property_info_area.cstr(context_offset).to_str()?;
let filename = dirname.join(context_name);
context_nodes.push(ContextNode::new(writable, context_offset, filename))
}
let serial_property_area_map = if writable {
if !dirname.is_dir() {
info!("Creating directory: {dirname:?}");
fs::mkdir(
dirname.as_path(),
fs::Mode::RWXU | fs::Mode::XGRP | fs::Mode::XOTH,
)
.map_err(Error::from)?;
}
*fsetxattr_failed = false;
for node in context_nodes.iter_mut() {
node.open(fsetxattr_failed)?;
}
Self::map_serial_property_area(serial_filename.as_path(), true, fsetxattr_failed)?
} else {
Self::map_serial_property_area(serial_filename.as_path(), false, fsetxattr_failed)?
};
Ok(Self {
property_info_area_file,
context_nodes,
serial_property_area_map,
})
}
fn map_serial_property_area(
serial_filename: &Path,
access_rw: bool,
fsetxattr_failed: &mut bool,
) -> Result<PropertyAreaMap> {
let result = if access_rw {
PropertyAreaMap::new_rw(
serial_filename,
Some(PROPERTIES_SERIAL_CONTEXT),
fsetxattr_failed,
)
} else {
PropertyAreaMap::new_ro(serial_filename)
};
match &result {
Ok(_) => {}
Err(e) => error!("Failed to map serial property area {serial_filename:?}: {e}"),
}
result
}
pub(crate) fn prop_area_for_name(&self, name: &str) -> Result<(PropertyAreaGuard<'_>, u32)> {
let (index, _) = self
.property_info_area_file
.property_info_area()
.get_property_info_indexes(name);
if index == u32::MAX || index >= self.context_nodes.len() as u32 {
warn!(
"Property {} not found: index={}, max_contexts={}",
name,
index,
self.context_nodes.len()
);
return Err(Error::new_not_found(name.to_owned()));
}
let context_node = &self.context_nodes[index as usize];
match context_node.property_area() {
Ok(area) => Ok((area, index)),
Err(e) => {
error!("Failed to get property area for {name}: {e}");
Err(e)
}
}
}
#[cfg(feature = "builder")]
pub(crate) fn prop_area_mut_for_name(
&self,
name: &str,
) -> Result<(PropertyAreaMutGuard<'_>, u32)> {
let (index, _) = self
.property_info_area_file
.property_info_area()
.get_property_info_indexes(name);
if index == u32::MAX || index >= self.context_nodes.len() as u32 {
error!(
"Could not find context for property {}: index={}, max_contexts={}",
name,
index,
self.context_nodes.len()
);
return Err(Error::new_not_found(format!(
"Could not find context for property {name}"
)));
}
let context_node = &self.context_nodes[index as usize];
match context_node.property_area_mut() {
Ok(area) => Ok((area, index)),
Err(e) => {
error!("Failed to get mutable property area for {name}: {e}");
Err(e)
}
}
}
pub(crate) fn serial_prop_area(&self) -> &PropertyArea {
self.serial_property_area_map.property_area()
}
pub(crate) fn prop_area_with_index(&self, context_index: u32) -> Result<PropertyAreaGuard<'_>> {
if context_index >= self.context_nodes.len() as u32 {
error!(
"Invalid context index {}: max={}",
context_index,
self.context_nodes.len()
);
return Err(Error::new_parse(format!(
"Invalid context index: {context_index}"
)));
}
let context_node = &self.context_nodes[context_index as usize];
match context_node.property_area() {
Ok(area) => Ok(area),
Err(e) => {
error!("Failed to get property area for context index {context_index}: {e}");
Err(e)
}
}
}
#[cfg(feature = "builder")]
pub(crate) fn prop_area_mut_with_index(
&self,
context_index: u32,
) -> Result<PropertyAreaMutGuard<'_>> {
if context_index >= self.context_nodes.len() as u32 {
error!(
"Invalid context index {}: max={}",
context_index,
self.context_nodes.len()
);
return Err(Error::new_parse(format!(
"Invalid context index: {context_index}"
)));
}
let context_node = &self.context_nodes[context_index as usize];
match context_node.property_area_mut() {
Ok(area) => Ok(area),
Err(e) => {
error!(
"Failed to get mutable property area for context index {context_index}: {e}"
);
Err(e)
}
}
}
}