fix(vfs): V-01 — copy dispatches to CarrierFs in carrier mode
This commit is contained in:
@@ -575,3 +575,202 @@ async fn test_carrier_file_drop_and_append_mode() {
|
||||
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_v01_carrier_fs_copy_delegation_and_functionality() {
|
||||
let path = temp_db_path("v01_copy");
|
||||
let carrier_size_bytes = 10 * 1024 * 1024; // 10 MB
|
||||
let carrier_name = "carrier_for_copy.bin";
|
||||
|
||||
let pass_decoy = "DecoyPass2026!";
|
||||
let pass_hidden = "HiddenPass2026!";
|
||||
|
||||
let kdf_params = KdfParams {
|
||||
memory_cost: MIN_MEMORY_COST_KIB,
|
||||
time_cost: MIN_TIME_COST,
|
||||
parallelism: 1,
|
||||
};
|
||||
|
||||
let salt_0 = generate_salt();
|
||||
let kek_0 = derive_kek(pass_decoy, &salt_0, &kdf_params).unwrap();
|
||||
let dek_0 = generate_dek();
|
||||
|
||||
let salt_1 = generate_salt();
|
||||
let kek_1 = derive_kek(pass_hidden, &salt_1, &kdf_params).unwrap();
|
||||
let dek_1 = generate_dek();
|
||||
|
||||
let carrier_node_id = 3i64;
|
||||
let (wrapped_0, nonce_0, tag_0) = wrap_slot0_payload(&kek_0, &dek_0, carrier_node_id).unwrap();
|
||||
let (wrapped_1, nonce_1, tag_1) =
|
||||
wrap_slot1_payload(&kek_1, &dek_1, &dek_0, carrier_node_id).unwrap();
|
||||
|
||||
let db = Database::open(&path).expect("Open database");
|
||||
|
||||
db.init_schema_with_carrier(
|
||||
&salt_0,
|
||||
&kdf_params,
|
||||
&wrapped_0,
|
||||
&nonce_0,
|
||||
&tag_0,
|
||||
Some((
|
||||
carrier_name,
|
||||
carrier_size_bytes,
|
||||
&salt_1,
|
||||
&kdf_params,
|
||||
&wrapped_1,
|
||||
&nonce_1,
|
||||
&tag_1,
|
||||
&dek_0,
|
||||
&dek_1,
|
||||
)),
|
||||
)
|
||||
.expect("Init carrier schema");
|
||||
|
||||
db.checkpoint().unwrap();
|
||||
|
||||
let meta = db.read_meta().unwrap();
|
||||
let auth_hidden = meta.authenticate(pass_hidden).expect("Auth hidden");
|
||||
|
||||
let hidden_fs = SanctumFs::with_carrier(
|
||||
db.clone(),
|
||||
auth_hidden.dek().clone(),
|
||||
auth_hidden.carrier_dek(),
|
||||
auth_hidden.carrier_node_id(),
|
||||
auth_hidden.version(),
|
||||
true,
|
||||
1,
|
||||
);
|
||||
|
||||
// Ordner anlegen
|
||||
let folder = DavPath::new("/work").unwrap();
|
||||
hidden_fs.create_dir(&folder).await.expect("Create folder");
|
||||
|
||||
// Datei anlegen und Inhalt schreiben
|
||||
let src_path = DavPath::new("/work/original.txt").unwrap();
|
||||
let test_data = b"V01 CarrierFs Copy Functionality Test Payload 2026";
|
||||
let mut file = hidden_fs
|
||||
.open(
|
||||
&src_path,
|
||||
OpenOptions {
|
||||
create: true,
|
||||
write: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("Create source file");
|
||||
file.write_bytes(Bytes::from_static(test_data))
|
||||
.await
|
||||
.expect("Write source data");
|
||||
file.flush().await.expect("Flush source file");
|
||||
drop(file);
|
||||
|
||||
// V-01: copy von /work/original.txt nach /work/copy.txt aufrufen
|
||||
let dest_path = DavPath::new("/work/copy.txt").unwrap();
|
||||
hidden_fs
|
||||
.copy(&src_path, &dest_path)
|
||||
.await
|
||||
.expect("V-01: Copy must succeed on CarrierFs");
|
||||
|
||||
// Metadaten der Kopie prüfen
|
||||
let copy_meta = hidden_fs
|
||||
.metadata(&dest_path)
|
||||
.await
|
||||
.expect("Metadata of copied file");
|
||||
assert_eq!(copy_meta.len(), test_data.len() as u64);
|
||||
assert!(!copy_meta.is_dir());
|
||||
|
||||
// Inhalt der Kopie lesen und verifizieren
|
||||
let mut read_copy = hidden_fs
|
||||
.open(
|
||||
&dest_path,
|
||||
OpenOptions {
|
||||
read: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("Open copied file");
|
||||
let copy_bytes = read_copy.read_bytes(1024).await.expect("Read copy bytes");
|
||||
assert_eq!(©_bytes[..], test_data);
|
||||
drop(read_copy);
|
||||
|
||||
// Quelldatei löschen: Die Kopie muss unabhängig erhalten bleiben!
|
||||
hidden_fs
|
||||
.remove_file(&src_path)
|
||||
.await
|
||||
.expect("Delete source file");
|
||||
assert!(hidden_fs.metadata(&src_path).await.is_err());
|
||||
|
||||
let mut read_copy_again = hidden_fs
|
||||
.open(
|
||||
&dest_path,
|
||||
OpenOptions {
|
||||
read: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("Open copied file after source delete");
|
||||
let copy_bytes_2 = read_copy_again
|
||||
.read_bytes(1024)
|
||||
.await
|
||||
.expect("Read copy bytes after source delete");
|
||||
assert_eq!(©_bytes_2[..], test_data);
|
||||
drop(read_copy_again);
|
||||
|
||||
// Overwrite-Copy testen
|
||||
let src2_path = DavPath::new("/work/source2.txt").unwrap();
|
||||
let test_data_2 = b"New Overwrite Payload Content";
|
||||
let mut file2 = hidden_fs
|
||||
.open(
|
||||
&src2_path,
|
||||
OpenOptions {
|
||||
create: true,
|
||||
write: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("Create source2");
|
||||
file2
|
||||
.write_bytes(Bytes::from_static(test_data_2))
|
||||
.await
|
||||
.expect("Write source2");
|
||||
file2.flush().await.expect("Flush source2");
|
||||
drop(file2);
|
||||
|
||||
hidden_fs
|
||||
.copy(&src2_path, &dest_path)
|
||||
.await
|
||||
.expect("Overwrite copy must succeed");
|
||||
|
||||
let mut read_overwritten = hidden_fs
|
||||
.open(
|
||||
&dest_path,
|
||||
OpenOptions {
|
||||
read: true,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("Open overwritten copy");
|
||||
let overwritten_bytes = read_overwritten
|
||||
.read_bytes(1024)
|
||||
.await
|
||||
.expect("Read overwritten bytes");
|
||||
assert_eq!(&overwritten_bytes[..], test_data_2);
|
||||
drop(read_overwritten);
|
||||
|
||||
// Checkpoint & Verify
|
||||
db.checkpoint().unwrap();
|
||||
let report = verify_container(&path, Some(&dek_0), true).expect("Verify with DEK_0");
|
||||
assert!(
|
||||
report.is_healthy(),
|
||||
"Container muss integer und gesund bleiben: {:?}",
|
||||
report.errors
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user