fix(storage): S-06 — container advisory lock prevents parallel mount and concurrent writes
This commit is contained in:
+104
-2
@@ -61,6 +61,17 @@ pub struct UnlockedKeys(
|
||||
pub Option<i64>, // 4: Carrier Node ID (Inode der Alibi-Datei in nodes)
|
||||
);
|
||||
|
||||
/// RAII-Guard für den Advisory-Lock eines gemounteten Containers (S-06).
|
||||
pub struct AdvisoryLockGuard {
|
||||
db: Database,
|
||||
}
|
||||
|
||||
impl Drop for AdvisoryLockGuard {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.db.release_advisory_lock();
|
||||
}
|
||||
}
|
||||
|
||||
impl UnlockedKeys {
|
||||
pub fn dek(&self) -> &Zeroizing<[u8; 32]> {
|
||||
&self.0
|
||||
@@ -411,6 +422,11 @@ impl Database {
|
||||
[],
|
||||
);
|
||||
|
||||
// Spalten für Advisory-Lock (S-06)
|
||||
let _ = conn.execute("ALTER TABLE meta ADD COLUMN lock_pid INTEGER", []);
|
||||
let _ = conn.execute("ALTER TABLE meta ADD COLUMN lock_host TEXT", []);
|
||||
let _ = conn.execute("ALTER TABLE meta ADD COLUMN lock_time INTEGER", []);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -576,7 +592,10 @@ impl Database {
|
||||
header_nonce BLOB NOT NULL,
|
||||
header_tag BLOB NOT NULL,
|
||||
metadata_mac BLOB,
|
||||
metadata_gen INTEGER NOT NULL DEFAULT 0
|
||||
metadata_gen INTEGER NOT NULL DEFAULT 0,
|
||||
lock_pid INTEGER,
|
||||
lock_host TEXT,
|
||||
lock_time INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
@@ -783,7 +802,10 @@ impl Database {
|
||||
header_nonce BLOB NOT NULL,
|
||||
header_tag BLOB NOT NULL,
|
||||
metadata_mac BLOB,
|
||||
metadata_gen INTEGER NOT NULL DEFAULT 0
|
||||
metadata_gen INTEGER NOT NULL DEFAULT 0,
|
||||
lock_pid INTEGER,
|
||||
lock_host TEXT,
|
||||
lock_time INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS nodes (
|
||||
@@ -1820,6 +1842,86 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prüft den aktuellen Advisory-Lock-Status (S-06).
|
||||
/// Gibt `Some((pid, host, timestamp))` zurück, falls ein Lock aktiv und der Prozess noch am Leben ist.
|
||||
pub fn check_advisory_lock(&self) -> Result<Option<(u32, String, u64)>> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT lock_pid, lock_host, lock_time FROM meta WHERE slot_id = 0 LIMIT 1")?;
|
||||
let lock_info = stmt
|
||||
.query_row([], |row| {
|
||||
let pid: Option<i64> = row.get(0)?;
|
||||
let host: Option<String> = row.get(1)?;
|
||||
let time: Option<i64> = row.get(2)?;
|
||||
Ok((pid, host, time))
|
||||
})
|
||||
.optional()?;
|
||||
|
||||
if let Some((Some(pid), Some(host), Some(time))) = lock_info {
|
||||
let current_host = std::env::var("COMPUTERNAME")
|
||||
.or_else(|_| std::env::var("HOSTNAME"))
|
||||
.unwrap_or_else(|_| "localhost".to_string());
|
||||
|
||||
// Falls gleicher Host, prüfe ob PID noch läuft
|
||||
if host == current_host {
|
||||
if crate::platform::is_process_alive(pid as u32) {
|
||||
return Ok(Some((pid as u32, host, time as u64)));
|
||||
} else {
|
||||
// Verwaister Lock von abgestürztem Prozess -> ignorieren/löschen
|
||||
drop(stmt);
|
||||
drop(conn);
|
||||
let _ = self.release_advisory_lock();
|
||||
return Ok(None);
|
||||
}
|
||||
} else {
|
||||
return Ok(Some((pid as u32, host, time as u64)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Setzt einen Advisory Lock auf den Container (S-06).
|
||||
pub fn acquire_advisory_lock(&self, force: bool) -> Result<()> {
|
||||
if !force {
|
||||
if let Some((pid, host, time)) = self.check_advisory_lock()? {
|
||||
bail!(
|
||||
"Container ist gesperrt: Wird aktuell von Prozess {} auf Host '{}' verwendet (seit UNIX-Zeit {}). Verwenden Sie --force zum Überschreiben.",
|
||||
pid, host, time
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let pid = std::process::id();
|
||||
let host = std::env::var("COMPUTERNAME")
|
||||
.or_else(|_| std::env::var("HOSTNAME"))
|
||||
.unwrap_or_else(|_| "localhost".to_string());
|
||||
let now = current_timestamp();
|
||||
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"UPDATE meta SET lock_pid = ?1, lock_host = ?2, lock_time = ?3 WHERE slot_id = 0",
|
||||
params![pid as i64, host, now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Entfernt den Advisory Lock (S-06).
|
||||
pub fn release_advisory_lock(&self) -> Result<()> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"UPDATE meta SET lock_pid = NULL, lock_host = NULL, lock_time = NULL WHERE slot_id = 0",
|
||||
[],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Setzt einen Advisory Lock und gibt einen RAII Guard zurück (S-06).
|
||||
pub fn acquire_advisory_lock_guard(&self, force: bool) -> Result<AdvisoryLockGuard> {
|
||||
self.acquire_advisory_lock(force)?;
|
||||
Ok(AdvisoryLockGuard { db: self.clone() })
|
||||
}
|
||||
|
||||
/// Erzeugt die deterministische kanonische Byterepräsentation aller Knoten für den Metadaten-MAC (K-01).
|
||||
pub fn canonical_nodes_bytes(&self) -> Result<Vec<u8>> {
|
||||
self.canonical_nodes_bytes_for_vault(0)
|
||||
|
||||
Reference in New Issue
Block a user