diff --git a/src/storage.rs b/src/storage.rs index 0e3639b..8fae82a 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1761,13 +1761,14 @@ impl Database { )?; let chunks_to_shred: Vec<(u32, usize)> = stmt .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>(1)?.unwrap_or(0); + Ok((idx, len)) })? - .filter_map(|r| r.ok()) - .collect(); + .collect::, rusqlite::Error>>()?; 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 { @@ -1778,13 +1779,13 @@ impl Database { OsRng.fill_bytes(&mut dummy_tag); OsRng.fill_bytes(&mut dummy_payload); - let _ = update_stmt.execute(params![ + update_stmt.execute(params![ dummy_nonce.as_slice(), dummy_tag.as_slice(), dummy_payload.as_slice(), node_id, idx - ]); + ])?; } } @@ -2597,6 +2598,43 @@ mod tests { 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] fn test_hidden_vault_isolation_and_filename_encryption() { let db = Database::open_in_memory().unwrap();