fix(carrier): C-04 — document hidden vault capacity limit and add 80% mount warning
This commit is contained in:
@@ -1312,3 +1312,145 @@ async fn test_c03_manifest_dirty_decoupling_and_unmount_flush() {
|
||||
drop(db);
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_c04_manifest_capacity_limit_and_warning() {
|
||||
let path = temp_db_path("c04_capacity");
|
||||
let carrier_size_bytes = 10 * 1024 * 1024; // 10 MB
|
||||
let carrier_name = "carrier_c04.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 cfs = sanctum::carrier::CarrierFs::load(
|
||||
db.clone(),
|
||||
carrier_node_id,
|
||||
std::sync::Arc::new(auth_hidden.carrier_dek().unwrap().clone()),
|
||||
std::sync::Arc::new(auth_hidden.dek().clone()),
|
||||
auth_hidden.version(),
|
||||
true,
|
||||
)
|
||||
.expect("CarrierFs load");
|
||||
|
||||
// 1. Initialer Zustand: Manifest minimal (< 5 KB, << 80%)
|
||||
let (len, cap, pct) = cfs.manifest_usage();
|
||||
assert_eq!(cap, sanctum::carrier::CARRIER_MANIFEST_CAPACITY);
|
||||
assert!(len < 5_000);
|
||||
assert!(pct < 1.0);
|
||||
|
||||
// 2. Fülle Manifest gezielt auf > 80% (> 838.809 Bytes)
|
||||
// Erstelle Verzeichnisse mit langen Pfadnamen, bis die Größe über 840 KB liegt
|
||||
for i in 0..1000 {
|
||||
let dir_path = DavPath::new(&format!("/d_{:04}_{}", i, "a".repeat(1000))).unwrap();
|
||||
cfs.create_dir(&dir_path).await.expect("create_dir");
|
||||
let (current_len, _, _) = cfs.manifest_usage();
|
||||
if current_len >= sanctum::carrier::CARRIER_MANIFEST_WARN_THRESHOLD {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let (len80, _, pct80) = cfs.manifest_usage();
|
||||
assert!(
|
||||
len80 >= sanctum::carrier::CARRIER_MANIFEST_WARN_THRESHOLD,
|
||||
"Manifest size must exceed 80% threshold, got: {}",
|
||||
len80
|
||||
);
|
||||
assert!(
|
||||
pct80 >= 80.0,
|
||||
"Manifest pct must be >= 80.0%, got: {}",
|
||||
pct80
|
||||
);
|
||||
|
||||
// Speichern (sync_manifest) muss mit Warnung gelingen
|
||||
cfs.sync_manifest()
|
||||
.expect("sync_manifest at > 80% must succeed");
|
||||
drop(cfs);
|
||||
|
||||
// Neu laden: muss erfolgreich laden und beim Laden die 80%-Warnung auslösen
|
||||
let cfs_loaded = sanctum::carrier::CarrierFs::load(
|
||||
db.clone(),
|
||||
carrier_node_id,
|
||||
std::sync::Arc::new(auth_hidden.carrier_dek().unwrap().clone()),
|
||||
std::sync::Arc::new(auth_hidden.dek().clone()),
|
||||
auth_hidden.version(),
|
||||
true,
|
||||
)
|
||||
.expect("CarrierFs load at > 80% must succeed");
|
||||
let (reloaded_len, _, reloaded_pct) = cfs_loaded.manifest_usage();
|
||||
assert!(reloaded_pct >= 80.0);
|
||||
assert_eq!(reloaded_len, len80);
|
||||
|
||||
// 3. Grenze überschreiten: weiter füllen bis > 1.048.512 Bytes
|
||||
for i in 1000..1500 {
|
||||
let dir_path = DavPath::new(&format!("/overflow_{:04}_{}", i, "x".repeat(1000))).unwrap();
|
||||
cfs_loaded
|
||||
.create_dir(&dir_path)
|
||||
.await
|
||||
.expect("create_dir overflow");
|
||||
let (cur_len, _, _) = cfs_loaded.manifest_usage();
|
||||
if cur_len > sanctum::carrier::CARRIER_MANIFEST_CAPACITY {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// sync_manifest() MUSS jetzt mit Fehler abbrechen
|
||||
let err = cfs_loaded.sync_manifest().unwrap_err();
|
||||
let err_str = err.to_string();
|
||||
assert!(
|
||||
err_str.contains("Carrier-Manifest überschreitet die maximale Blockgröße"),
|
||||
"Error message must specify carrier capacity limit: {}",
|
||||
err_str
|
||||
);
|
||||
assert!(
|
||||
err_str.contains("7.000 Dateien/Verzeichnisse"),
|
||||
"Error message must recommend file limit: {}",
|
||||
err_str
|
||||
);
|
||||
|
||||
drop(cfs_loaded);
|
||||
drop(db);
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user