feat(anti-forensics): implement incremental auto-vacuum, chunk shredding, and plausible deniability
This commit is contained in:
+64
-67
@@ -388,12 +388,13 @@ pub struct SanctumFs {
|
||||
dek: Arc<Zeroizing<[u8; 32]>>,
|
||||
format_version: u32,
|
||||
anti_leak: bool,
|
||||
vault_id: u32,
|
||||
last_activity: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
impl SanctumFs {
|
||||
pub fn new(db: Database, dek: Zeroizing<[u8; 32]>, format_version: u32) -> Self {
|
||||
Self::with_options(db, dek, format_version, true)
|
||||
Self::with_vault(db, dek, format_version, true, 0)
|
||||
}
|
||||
|
||||
pub fn with_options(
|
||||
@@ -401,6 +402,16 @@ impl SanctumFs {
|
||||
dek: Zeroizing<[u8; 32]>,
|
||||
format_version: u32,
|
||||
anti_leak: bool,
|
||||
) -> Self {
|
||||
Self::with_vault(db, dek, format_version, anti_leak, 0)
|
||||
}
|
||||
|
||||
pub fn with_vault(
|
||||
db: Database,
|
||||
dek: Zeroizing<[u8; 32]>,
|
||||
format_version: u32,
|
||||
anti_leak: bool,
|
||||
vault_id: u32,
|
||||
) -> Self {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
@@ -411,10 +422,15 @@ impl SanctumFs {
|
||||
dek: Arc::new(dek),
|
||||
format_version,
|
||||
anti_leak,
|
||||
vault_id,
|
||||
last_activity: Arc::new(AtomicU64::new(now)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vault_id(&self) -> u32 {
|
||||
self.vault_id
|
||||
}
|
||||
|
||||
pub fn last_activity(&self) -> Arc<AtomicU64> {
|
||||
self.last_activity.clone()
|
||||
}
|
||||
@@ -442,6 +458,33 @@ impl SanctumFs {
|
||||
None => ("", trimmed),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_path(&self, path: &str) -> Result<Option<NodeRecord>, FsError> {
|
||||
self.db
|
||||
.resolve_path_in_vault(path, self.vault_id, &self.dek)
|
||||
.map_err(|_| FsError::GeneralFailure)
|
||||
}
|
||||
|
||||
fn list_children(&self, parent_id: i64) -> Result<Vec<NodeRecord>, FsError> {
|
||||
self.db
|
||||
.list_children_in_vault(parent_id, self.vault_id, &self.dek)
|
||||
.map_err(|_| FsError::GeneralFailure)
|
||||
}
|
||||
|
||||
fn create_node(&self, parent_id: i64, name: &str, is_dir: bool) -> Result<NodeRecord, FsError> {
|
||||
self.db
|
||||
.create_node_in_vault(self.vault_id, parent_id, name, is_dir, &self.dek)
|
||||
.map_err(|e| {
|
||||
error!("Fehler beim Erstellen des Knotens '{}': {e}", name);
|
||||
FsError::GeneralFailure
|
||||
})
|
||||
}
|
||||
|
||||
fn rename_node(&self, id: i64, new_parent_id: i64, new_name: &str) -> Result<(), FsError> {
|
||||
self.db
|
||||
.rename_node_in_vault(id, new_parent_id, new_name, self.vault_id, &self.dek)
|
||||
.map_err(|_| FsError::GeneralFailure)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavFileSystem for SanctumFs {
|
||||
@@ -473,10 +516,7 @@ impl DavFileSystem for SanctumFs {
|
||||
self.touch();
|
||||
debug!("VFS open aufgerufen: path='{}', options={:?}", path_str, options);
|
||||
|
||||
let existing_node = self
|
||||
.db
|
||||
.resolve_path(&path_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
let existing_node = self.resolve_path(&path_str)?;
|
||||
|
||||
let node = match existing_node {
|
||||
Some(n) => {
|
||||
@@ -511,21 +551,14 @@ impl DavFileSystem for SanctumFs {
|
||||
None => {
|
||||
if options.create || options.create_new {
|
||||
let parent = self
|
||||
.db
|
||||
.resolve_path(parent_path)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(parent_path)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
if !parent.is_dir {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
self.db
|
||||
.create_node(parent.id, file_name, false)
|
||||
.map_err(|e| {
|
||||
error!("Fehler beim Erstellen der Datei: {e}");
|
||||
FsError::GeneralFailure
|
||||
})?
|
||||
self.create_node(parent.id, file_name, false)?
|
||||
} else {
|
||||
return Err(FsError::NotFound);
|
||||
}
|
||||
@@ -552,19 +585,14 @@ impl DavFileSystem for SanctumFs {
|
||||
self.touch();
|
||||
let path_str = Self::path_to_str(path);
|
||||
let node = self
|
||||
.db
|
||||
.resolve_path(&path_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(&path_str)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
if !node.is_dir {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
let children = self
|
||||
.db
|
||||
.list_children(node.id)
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
let children = self.list_children(node.id)?;
|
||||
|
||||
let entries: Vec<Result<Box<dyn DavDirEntry>, FsError>> = children
|
||||
.into_iter()
|
||||
@@ -593,9 +621,7 @@ impl DavFileSystem for SanctumFs {
|
||||
self.touch();
|
||||
}
|
||||
let node = self
|
||||
.db
|
||||
.resolve_path(&path_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(&path_str)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
let meta = SanctumMetaData {
|
||||
@@ -623,28 +649,19 @@ impl DavFileSystem for SanctumFs {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
if self
|
||||
.db
|
||||
.resolve_path(&path_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.is_some()
|
||||
{
|
||||
if self.resolve_path(&path_str)?.is_some() {
|
||||
return Err(FsError::Exists);
|
||||
}
|
||||
|
||||
let parent = self
|
||||
.db
|
||||
.resolve_path(parent_path)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(parent_path)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
if !parent.is_dir {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
|
||||
self.db
|
||||
.create_node(parent.id, dir_name, true)
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
self.create_node(parent.id, dir_name, true)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
@@ -655,15 +672,14 @@ impl DavFileSystem for SanctumFs {
|
||||
self.touch();
|
||||
let path_str = Self::path_to_str(path);
|
||||
let node = self
|
||||
.db
|
||||
.resolve_path(&path_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(&path_str)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
if !node.is_dir {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
if node.id == 1 {
|
||||
let root_id = Database::get_root_node_id_for_vault(self.vault_id);
|
||||
if node.id == root_id {
|
||||
// Root-Verzeichnis darf nicht gelöscht werden
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
@@ -681,9 +697,7 @@ impl DavFileSystem for SanctumFs {
|
||||
self.touch();
|
||||
let path_str = Self::path_to_str(path);
|
||||
let node = self
|
||||
.db
|
||||
.resolve_path(&path_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(&path_str)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
if node.is_dir {
|
||||
@@ -709,9 +723,7 @@ impl DavFileSystem for SanctumFs {
|
||||
let to_str = Self::path_to_str(to);
|
||||
|
||||
let node = self
|
||||
.db
|
||||
.resolve_path(&from_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(&from_str)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
let (to_parent_path, to_name) = self.split_parent_and_name(&to_str);
|
||||
@@ -721,9 +733,7 @@ impl DavFileSystem for SanctumFs {
|
||||
}
|
||||
|
||||
let to_parent = self
|
||||
.db
|
||||
.resolve_path(to_parent_path)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(to_parent_path)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
if !to_parent.is_dir {
|
||||
@@ -731,11 +741,7 @@ impl DavFileSystem for SanctumFs {
|
||||
}
|
||||
|
||||
// Falls Zieldatei bereits existiert und Datei ist: überschreiben / löschen
|
||||
if let Some(dest) = self
|
||||
.db
|
||||
.resolve_path(&to_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
{
|
||||
if let Some(dest) = self.resolve_path(&to_str)? {
|
||||
if dest.is_dir {
|
||||
return Err(FsError::Forbidden);
|
||||
}
|
||||
@@ -744,9 +750,7 @@ impl DavFileSystem for SanctumFs {
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
}
|
||||
|
||||
self.db
|
||||
.rename_node(node.id, to_parent.id, to_name)
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
self.rename_node(node.id, to_parent.id, to_name)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
@@ -763,9 +767,7 @@ impl DavFileSystem for SanctumFs {
|
||||
let to_str = Self::path_to_str(to);
|
||||
|
||||
let node = self
|
||||
.db
|
||||
.resolve_path(&from_str)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(&from_str)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
if node.is_dir {
|
||||
@@ -779,15 +781,10 @@ impl DavFileSystem for SanctumFs {
|
||||
}
|
||||
|
||||
let to_parent = self
|
||||
.db
|
||||
.resolve_path(to_parent_path)
|
||||
.map_err(|_| FsError::GeneralFailure)?
|
||||
.resolve_path(to_parent_path)?
|
||||
.ok_or(FsError::NotFound)?;
|
||||
|
||||
let dest_node = self
|
||||
.db
|
||||
.create_node(to_parent.id, to_name, false)
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
let dest_node = self.create_node(to_parent.id, to_name, false)?;
|
||||
|
||||
// Kopiere alle Chunks und re-verschlüssele mit neuer node_id (wegen AAD-Bindung!)
|
||||
let total_chunks = if node.size == 0 {
|
||||
|
||||
Reference in New Issue
Block a user