fix(storage): V-09 — ensure truncate chunks after shreds data blocks before deletion
This commit is contained in:
+44
-6
@@ -1761,13 +1761,14 @@ impl Database {
|
|||||||
)?;
|
)?;
|
||||||
let chunks_to_shred: Vec<(u32, usize)> = stmt
|
let chunks_to_shred: Vec<(u32, usize)> = stmt
|
||||||
.query_map(params![node_id, max_chunk_index], |row| {
|
.query_map(params![node_id, max_chunk_index], |row| {
|
||||||
Ok((row.get(0)?, row.get::<_, usize>(1)?))
|
let idx: u32 = row.get(0)?;
|
||||||
|
let len: usize = row.get::<_, Option<usize>>(1)?.unwrap_or(0);
|
||||||
|
Ok((idx, len))
|
||||||
})?
|
})?
|
||||||
.filter_map(|r| r.ok())
|
.collect::<std::result::Result<Vec<_>, rusqlite::Error>>()?;
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut update_stmt = tx.prepare(
|
let mut update_stmt = tx.prepare(
|
||||||
"UPDATE chunks SET nonce = ?1, tag = ?2, ciphertext = ?3 WHERE node_id = ?4 AND chunk_index = ?5",
|
"UPDATE chunks SET nonce = ?1, tag = ?2, ciphertext = ?3, generation = 0 WHERE node_id = ?4 AND chunk_index = ?5",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
for (idx, len) in chunks_to_shred {
|
for (idx, len) in chunks_to_shred {
|
||||||
@@ -1778,13 +1779,13 @@ impl Database {
|
|||||||
OsRng.fill_bytes(&mut dummy_tag);
|
OsRng.fill_bytes(&mut dummy_tag);
|
||||||
OsRng.fill_bytes(&mut dummy_payload);
|
OsRng.fill_bytes(&mut dummy_payload);
|
||||||
|
|
||||||
let _ = update_stmt.execute(params![
|
update_stmt.execute(params![
|
||||||
dummy_nonce.as_slice(),
|
dummy_nonce.as_slice(),
|
||||||
dummy_tag.as_slice(),
|
dummy_tag.as_slice(),
|
||||||
dummy_payload.as_slice(),
|
dummy_payload.as_slice(),
|
||||||
node_id,
|
node_id,
|
||||||
idx
|
idx
|
||||||
]);
|
])?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2597,6 +2598,43 @@ mod tests {
|
|||||||
assert_ne!(chunk.tag, c_tag, "Tag muss überschrieben sein");
|
assert_ne!(chunk.tag, c_tag, "Tag muss überschrieben sein");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_v09_truncate_chunks_after_shreds_before_deletion() {
|
||||||
|
let db = Database::open_in_memory().unwrap();
|
||||||
|
let salt = [1u8; 16];
|
||||||
|
let kdf_params = KdfParams::default();
|
||||||
|
let wrapped_dek = vec![2u8; 32];
|
||||||
|
let nonce = [3u8; 12];
|
||||||
|
let tag = [4u8; 16];
|
||||||
|
|
||||||
|
db.init_schema(&salt, &kdf_params, &wrapped_dek, &nonce, &tag)
|
||||||
|
.unwrap();
|
||||||
|
let root = db.resolve_path("/").unwrap().expect("Root");
|
||||||
|
let file = db.create_node(root.id, "truncate_test.dat", false).unwrap();
|
||||||
|
|
||||||
|
let payload_0 = b"CHUNK_ZERO_MUST_SURVIVE";
|
||||||
|
let payload_1 = b"CHUNK_ONE_MUST_BE_SHREDDED_AND_DELETED";
|
||||||
|
let payload_2 = b"CHUNK_TWO_MUST_BE_SHREDDED_AND_DELETED";
|
||||||
|
|
||||||
|
db.write_chunk(file.id, 0, 0, &[1u8; 12], &[2u8; 16], payload_0)
|
||||||
|
.unwrap();
|
||||||
|
db.write_chunk(file.id, 1, 0, &[3u8; 12], &[4u8; 16], payload_1)
|
||||||
|
.unwrap();
|
||||||
|
db.write_chunk(file.id, 2, 0, &[5u8; 12], &[6u8; 16], payload_2)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Kürze alle Chunks nach Index 0 (also Chunks 1 und 2)
|
||||||
|
db.truncate_chunks_after(file.id, 0).unwrap();
|
||||||
|
|
||||||
|
// Chunk 0 muss unberührt geblieben sein
|
||||||
|
let c0 = db.read_chunk(file.id, 0).unwrap().expect("Chunk 0 survives");
|
||||||
|
assert_eq!(c0.ciphertext, payload_0);
|
||||||
|
|
||||||
|
// Chunks 1 und 2 müssen gelöscht sein
|
||||||
|
assert!(db.read_chunk(file.id, 1).unwrap().is_none());
|
||||||
|
assert!(db.read_chunk(file.id, 2).unwrap().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hidden_vault_isolation_and_filename_encryption() {
|
fn test_hidden_vault_isolation_and_filename_encryption() {
|
||||||
let db = Database::open_in_memory().unwrap();
|
let db = Database::open_in_memory().unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user