fix(core): resolve all 12 adversarial review findings
- Zeroize passwords in CLI prompts, handlers, and mount authentication - Preserve carrier_node_id when recovering Slot 0 via recovery key - Add --slot parameter to restore-header for targeted slot recovery - Implement online_backup and restore_from_backup using SQLite Online Backup API - Expose 'sanctum backup' and 'sanctum restore' CLI subcommands - Fix inactivity auto-lock by removing touch() from PROPFIND metadata/read_dir - Support O_APPEND by setting file cursor to file size on handle creation - Prevent data loss by implementing Drop for CarrierFile to flush dirty blocks - Optimize CSPRNG padding to only fill unwritten slack space - Batch carrier initialization in 500-block transactions to prevent UI/CLI freeze - Enforce 64-byte savings threshold for LZ4 compression - Add WebClient service diagnostic hint for Windows net use mount errors - Add unit and integration tests covering all new features
This commit is contained in:
@@ -253,6 +253,16 @@ impl Database {
|
||||
Ok(count as usize)
|
||||
}
|
||||
|
||||
/// Sucht nach einem existierenden Carrier-Knoten im Decoy-Wurzelverzeichnis (parent_id = 1, is_dir = 0).
|
||||
pub fn find_carrier_node_id(&self) -> Result<Option<i64>> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id FROM nodes WHERE parent_id = 1 AND is_dir = 0 ORDER BY id ASC LIMIT 1",
|
||||
)?;
|
||||
let id = stmt.query_row([], |r| r.get::<_, i64>(0)).optional()?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Überschreibt Chunks eines Knotens vor dem Löschen mit kryptografischem Zufallsrauschen (Chunk Shredding).
|
||||
pub fn shred_chunks_for_node(&self, node_id: i64) -> Result<()> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
@@ -459,6 +469,7 @@ impl Database {
|
||||
let mut dummy_noise = vec![0u8; CHUNK_SIZE];
|
||||
OsRng.fill_bytes(&mut dummy_noise);
|
||||
|
||||
conn.execute_batch("BEGIN TRANSACTION;")?;
|
||||
for b in 1..total_blocks {
|
||||
let (ct, nonce, tag) = crate::crypto::encrypt_chunk(
|
||||
dek_0,
|
||||
@@ -474,7 +485,12 @@ impl Database {
|
||||
tag.as_slice(),
|
||||
ct,
|
||||
])?;
|
||||
|
||||
if b % 500 == 0 {
|
||||
conn.execute_batch("COMMIT; BEGIN TRANSACTION;")?;
|
||||
}
|
||||
}
|
||||
conn.execute_batch("COMMIT;")?;
|
||||
|
||||
Some(c_id)
|
||||
} else {
|
||||
@@ -1204,6 +1220,61 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Erstellt ein konsistentes Online-Live-Backup der gesamten Container-Datenbank via SQLite Online Backup API.
|
||||
/// Kann auch während eines aktiven WebDAV-Mounts ohne Lese-/Schreibkonflikte ausgeführt werden.
|
||||
pub fn online_backup<P: AsRef<Path>>(&self, dest_path: P) -> Result<()> {
|
||||
let dest_path = dest_path.as_ref();
|
||||
if let Some(parent) = dest_path.parent() {
|
||||
if !parent.as_os_str().is_empty() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut dest_conn = Connection::open(dest_path)?;
|
||||
let src_conn = self.conn.lock().unwrap();
|
||||
|
||||
let backup = rusqlite::backup::Backup::new(&src_conn, &mut dest_conn)?;
|
||||
backup.run_to_completion(100, std::time::Duration::from_millis(20), None)?;
|
||||
drop(backup);
|
||||
|
||||
dest_conn.execute_batch("PRAGMA wal_checkpoint(TRUNCATE);")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stellt einen Container vollständig aus einer Sicherungskopie wieder her und verifiziert die Konsistenz.
|
||||
pub fn restore_from_backup<P: AsRef<Path>>(backup_path: P, dest_path: P) -> Result<()> {
|
||||
let backup_path = backup_path.as_ref();
|
||||
let dest_path = dest_path.as_ref();
|
||||
|
||||
if !backup_path.exists() {
|
||||
bail!("Backup-Datei '{}' existiert nicht.", backup_path.display());
|
||||
}
|
||||
|
||||
if let Some(parent) = dest_path.parent() {
|
||||
if !parent.as_os_str().is_empty() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
}
|
||||
|
||||
let src_conn = Connection::open(backup_path)?;
|
||||
let mut dest_conn = Connection::open(dest_path)?;
|
||||
|
||||
let backup = rusqlite::backup::Backup::new(&src_conn, &mut dest_conn)?;
|
||||
backup.run_to_completion(100, std::time::Duration::from_millis(20), None)?;
|
||||
drop(backup);
|
||||
drop(src_conn);
|
||||
|
||||
dest_conn.execute_batch("PRAGMA wal_checkpoint(TRUNCATE);")?;
|
||||
|
||||
// B-Tree Integritätsprüfung
|
||||
let check: String = dest_conn.query_row("PRAGMA quick_check;", [], |r| r.get(0))?;
|
||||
if check != "ok" {
|
||||
bail!("Integritätsprüfung des wiederhergestellten Containers fehlgeschlagen: {check}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schreibt oder stellt die Metadaten in der `meta`-Tabelle wieder her (z. B. nach Restore oder Header-Neugenerierung).
|
||||
pub fn restore_meta(&self, meta: &ContainerMeta) -> Result<()> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user