fix(vfs): V-04 — structured anti-leak shield rules and custom filter list

This commit is contained in:
2026-09-19 09:58:52 +02:00
parent 17908a64fe
commit 45572b7370
4 changed files with 355 additions and 30 deletions
+53 -9
View File
@@ -23,7 +23,7 @@ use zeroize::{Zeroize, Zeroizing};
use crate::crypto::{decrypt_chunk, encrypt_chunk, CHUNK_SIZE};
use crate::storage::Database;
use crate::vfs::{is_leak_file, SanctumDirEntry, SanctumMetaData};
use crate::vfs::{is_leak_file_with_custom, SanctumDirEntry, SanctumMetaData};
pub const CARRIER_MAGIC: &[u8; 8] = b"SANCTCAR";
pub const CARRIER_VERSION: u32 = 1;
@@ -271,11 +271,16 @@ pub struct CarrierFsInner {
pub dek_inner: Arc<Zeroizing<[u8; 32]>>,
pub format_version: u32,
pub anti_leak: bool,
pub custom_leak_rules: Arc<Vec<String>>,
pub leak_counter: Arc<AtomicU64>,
pub manifest: CarrierManifest,
pub last_activity: Arc<AtomicU64>,
}
impl CarrierFsInner {
pub fn is_leak(&self, name: &str) -> bool {
self.anti_leak && is_leak_file_with_custom(name, &self.custom_leak_rules)
}
pub fn save_manifest(&mut self) -> Result<()> {
let manifest_bytes = serde_json::to_vec(&self.manifest)?;
if manifest_bytes.len() > (crate::crypto::CHUNK_SIZE - 64) {
@@ -369,14 +374,15 @@ pub struct CarrierFs {
}
impl CarrierFs {
/// Lädt ein bestehendes Carrier-Dateisystem aus Block 0 der Trägerdatei.
pub fn load(
/// Lädt ein bestehendes Carrier-Dateisystem mit benutzerdefinierten Anti-Leak-Regeln (V-04).
pub fn load_with_leak_rules(
db: Database,
carrier_node_id: i64,
dek_outer: Arc<Zeroizing<[u8; 32]>>,
dek_inner: Arc<Zeroizing<[u8; 32]>>,
format_version: u32,
anti_leak: bool,
custom_leak_rules: Vec<String>,
) -> Result<Self> {
let manifest_bytes = read_carrier_block(
&db,
@@ -408,6 +414,8 @@ impl CarrierFs {
dek_inner,
format_version,
anti_leak,
custom_leak_rules: Arc::new(custom_leak_rules),
leak_counter: Arc::new(AtomicU64::new(0)),
manifest,
last_activity: Arc::new(AtomicU64::new(now)),
};
@@ -417,8 +425,33 @@ impl CarrierFs {
})
}
/// Lädt ein bestehendes Carrier-Dateisystem aus Block 0 der Trägerdatei.
pub fn load(
db: Database,
carrier_node_id: i64,
dek_outer: Arc<Zeroizing<[u8; 32]>>,
dek_inner: Arc<Zeroizing<[u8; 32]>>,
format_version: u32,
anti_leak: bool,
) -> Result<Self> {
Self::load_with_leak_rules(
db,
carrier_node_id,
dek_outer,
dek_inner,
format_version,
anti_leak,
Vec::new(),
)
}
pub fn leak_counter(&self) -> Arc<AtomicU64> {
let inner = self.inner.lock().unwrap_or_else(|e| e.into_inner());
inner.leak_counter.clone()
}
pub fn last_activity(&self) -> Arc<AtomicU64> {
let inner = self.inner.lock().unwrap();
let inner = self.inner.lock().unwrap_or_else(|e| e.into_inner());
inner.last_activity.clone()
}
@@ -449,13 +482,14 @@ impl DavFileSystem for CarrierFs {
let mut inner = self.inner.lock().unwrap();
let (parent_path, file_name) = inner.split_parent_and_name(&path_str);
if inner.anti_leak && is_leak_file(file_name) {
if inner.is_leak(file_name) {
if options.create
|| options.create_new
|| options.write
|| options.append
|| options.truncate
{
inner.leak_counter.fetch_add(1, Ordering::Relaxed);
return Err(FsError::Forbidden);
}
}
@@ -554,7 +588,14 @@ impl DavFileSystem for CarrierFs {
.inodes
.values()
.filter(|child| child.parent_id == Some(node.id))
.filter(|child| !inner.anti_leak || !is_leak_file(&child.name))
.filter(|child| {
if inner.is_leak(&child.name) {
inner.leak_counter.fetch_add(1, Ordering::Relaxed);
false
} else {
true
}
})
.map(|child| {
Ok(Box::new(SanctumDirEntry {
name: child.name.clone(),
@@ -601,7 +642,8 @@ impl DavFileSystem for CarrierFs {
let mut inner = self.inner.lock().unwrap();
let (parent_path, dir_name) = inner.split_parent_and_name(&path_str);
if inner.anti_leak && is_leak_file(dir_name) {
if inner.is_leak(dir_name) {
inner.leak_counter.fetch_add(1, Ordering::Relaxed);
return Err(FsError::Forbidden);
}
@@ -712,7 +754,8 @@ impl DavFileSystem for CarrierFs {
let node = inner.resolve_path(&from_str).ok_or(FsError::NotFound)?;
let (to_parent_path, to_name) = inner.split_parent_and_name(&to_str);
if inner.anti_leak && is_leak_file(to_name) {
if inner.is_leak(to_name) {
inner.leak_counter.fetch_add(1, Ordering::Relaxed);
return Err(FsError::Forbidden);
}
@@ -776,7 +819,8 @@ impl DavFileSystem for CarrierFs {
}
let (to_parent_path, to_name) = inner.split_parent_and_name(&to_str);
if inner.anti_leak && is_leak_file(to_name) {
if inner.is_leak(to_name) {
inner.leak_counter.fetch_add(1, Ordering::Relaxed);
return Err(FsError::Forbidden);
}