use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use spin::Mutex;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RetentionMode {
Compliance,
Governance,
}
#[derive(Debug, Clone)]
pub struct WormFile {
pub file_id: u64,
pub dataset_id: u64,
pub mode: RetentionMode,
pub retention_period: u64,
pub lock_time: u64,
pub expiration_time: u64,
pub legal_hold: bool,
pub content_hash: [u8; 32],
}
impl WormFile {
pub fn new(
file_id: u64,
dataset_id: u64,
mode: RetentionMode,
retention_period: u64,
lock_time: u64,
content_hash: [u8; 32],
) -> Self {
Self {
file_id,
dataset_id,
mode,
retention_period,
lock_time,
expiration_time: lock_time + retention_period,
legal_hold: false,
content_hash,
}
}
pub fn is_locked(&self, current_time: u64) -> bool {
current_time < self.expiration_time || self.legal_hold
}
pub fn can_delete(&self, current_time: u64, has_governance_override: bool) -> bool {
if self.legal_hold {
return false; }
if current_time < self.expiration_time {
match self.mode {
RetentionMode::Compliance => false, RetentionMode::Governance => has_governance_override, }
} else {
true }
}
pub fn can_modify(&self, _current_time: u64) -> bool {
false }
pub fn verify_integrity(&self, actual_hash: &[u8; 32]) -> bool {
self.content_hash == *actual_hash
}
}
#[derive(Debug, Clone)]
pub struct AuditEntry {
pub entry_id: u64,
pub timestamp: u64,
pub operation: WormOperation,
pub file_id: u64,
pub user_id: u64,
pub success: bool,
pub details: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WormOperation {
Lock,
AttemptModify,
AttemptDelete,
LegalHoldSet,
LegalHoldRemove,
RetentionExtend,
GovernanceOverride,
IntegrityCheck,
}
impl WormOperation {
pub fn name(&self) -> &'static str {
match self {
WormOperation::Lock => "lock",
WormOperation::AttemptModify => "attempt_modify",
WormOperation::AttemptDelete => "attempt_delete",
WormOperation::LegalHoldSet => "legal_hold_set",
WormOperation::LegalHoldRemove => "legal_hold_remove",
WormOperation::RetentionExtend => "retention_extend",
WormOperation::GovernanceOverride => "governance_override",
WormOperation::IntegrityCheck => "integrity_check",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct WormStats {
pub total_files: usize,
pub compliance_files: usize,
pub governance_files: usize,
pub legal_hold_files: usize,
pub modify_attempts: u64,
pub delete_attempts: u64,
pub integrity_failures: u64,
}
lazy_static! {
static ref WORM_MANAGER: Mutex<WormManager> = Mutex::new(WormManager::new());
}
pub struct WormManager {
files: BTreeMap<u64, WormFile>,
audit_log: Vec<AuditEntry>,
next_audit_id: u64,
stats: WormStats,
}
impl Default for WormManager {
fn default() -> Self {
Self::new()
}
}
impl WormManager {
pub fn new() -> Self {
Self {
files: BTreeMap::new(),
audit_log: Vec::new(),
next_audit_id: 1,
stats: WormStats::default(),
}
}
pub fn lock_file(&mut self, file: WormFile, user_id: u64) -> Result<(), &'static str> {
let file_id = file.file_id;
let mode = file.mode;
let current_time = file.lock_time;
if self.files.contains_key(&file_id) {
return Err("File already locked");
}
self.files.insert(file_id, file);
self.stats.total_files += 1;
match mode {
RetentionMode::Compliance => self.stats.compliance_files += 1,
RetentionMode::Governance => self.stats.governance_files += 1,
}
self.add_audit_entry(
current_time,
WormOperation::Lock,
file_id,
user_id,
true,
"File locked",
);
Ok(())
}
pub fn can_modify(&mut self, file_id: u64, current_time: u64, user_id: u64) -> bool {
if let Some(worm_file) = self.files.get(&file_id) {
let allowed = worm_file.can_modify(current_time);
if !allowed {
self.stats.modify_attempts += 1;
self.add_audit_entry(
current_time,
WormOperation::AttemptModify,
file_id,
user_id,
false,
"Modification denied",
);
}
allowed
} else {
true }
}
pub fn can_delete(
&mut self,
file_id: u64,
current_time: u64,
has_governance_override: bool,
user_id: u64,
) -> bool {
if let Some(worm_file) = self.files.get(&file_id) {
let allowed = worm_file.can_delete(current_time, has_governance_override);
if !allowed {
self.stats.delete_attempts += 1;
self.add_audit_entry(
current_time,
WormOperation::AttemptDelete,
file_id,
user_id,
false,
"Deletion denied",
);
} else if has_governance_override {
self.add_audit_entry(
current_time,
WormOperation::GovernanceOverride,
file_id,
user_id,
true,
"Governance override used",
);
}
allowed
} else {
true }
}
pub fn set_legal_hold(
&mut self,
file_id: u64,
current_time: u64,
user_id: u64,
) -> Result<(), &'static str> {
let worm_file = self.files.get_mut(&file_id).ok_or("File not WORM")?;
if !worm_file.legal_hold {
worm_file.legal_hold = true;
self.stats.legal_hold_files += 1;
self.add_audit_entry(
current_time,
WormOperation::LegalHoldSet,
file_id,
user_id,
true,
"Legal hold set",
);
}
Ok(())
}
pub fn remove_legal_hold(
&mut self,
file_id: u64,
current_time: u64,
user_id: u64,
) -> Result<(), &'static str> {
let worm_file = self.files.get_mut(&file_id).ok_or("File not WORM")?;
if worm_file.legal_hold {
worm_file.legal_hold = false;
self.stats.legal_hold_files = self.stats.legal_hold_files.saturating_sub(1);
self.add_audit_entry(
current_time,
WormOperation::LegalHoldRemove,
file_id,
user_id,
true,
"Legal hold removed",
);
}
Ok(())
}
pub fn extend_retention(
&mut self,
file_id: u64,
additional_time: u64,
current_time: u64,
user_id: u64,
) -> Result<(), &'static str> {
let worm_file = self.files.get_mut(&file_id).ok_or("File not WORM")?;
worm_file.retention_period += additional_time;
worm_file.expiration_time += additional_time;
self.add_audit_entry(
current_time,
WormOperation::RetentionExtend,
file_id,
user_id,
true,
"Retention extended",
);
Ok(())
}
pub fn verify_integrity(
&mut self,
file_id: u64,
actual_hash: &[u8; 32],
current_time: u64,
user_id: u64,
) -> bool {
if let Some(worm_file) = self.files.get(&file_id) {
let valid = worm_file.verify_integrity(actual_hash);
if !valid {
self.stats.integrity_failures += 1;
}
self.add_audit_entry(
current_time,
WormOperation::IntegrityCheck,
file_id,
user_id,
valid,
if valid {
"Integrity valid"
} else {
"Integrity FAILED"
},
);
valid
} else {
true }
}
fn add_audit_entry(
&mut self,
timestamp: u64,
operation: WormOperation,
file_id: u64,
user_id: u64,
success: bool,
details: &'static str,
) {
let entry = AuditEntry {
entry_id: self.next_audit_id,
timestamp,
operation,
file_id,
user_id,
success,
details,
};
self.audit_log.push(entry);
self.next_audit_id += 1;
}
pub fn get_audit_log(&self, file_id: u64) -> Vec<AuditEntry> {
self.audit_log
.iter()
.filter(|e| e.file_id == file_id)
.cloned()
.collect()
}
pub fn get_stats(&self) -> WormStats {
self.stats.clone()
}
pub fn get_file(&self, file_id: u64) -> Option<&WormFile> {
self.files.get(&file_id)
}
}
pub struct WormEngine;
impl WormEngine {
pub fn lock_file(
file_id: u64,
dataset_id: u64,
mode: RetentionMode,
retention_period: u64,
current_time: u64,
content_hash: [u8; 32],
user_id: u64,
) -> Result<(), &'static str> {
let file = WormFile::new(
file_id,
dataset_id,
mode,
retention_period,
current_time,
content_hash,
);
let mut mgr = WORM_MANAGER.lock();
mgr.lock_file(file, user_id)
}
pub fn can_modify(file_id: u64, current_time: u64, user_id: u64) -> bool {
let mut mgr = WORM_MANAGER.lock();
mgr.can_modify(file_id, current_time, user_id)
}
pub fn can_delete(file_id: u64, current_time: u64, has_override: bool, user_id: u64) -> bool {
let mut mgr = WORM_MANAGER.lock();
mgr.can_delete(file_id, current_time, has_override, user_id)
}
pub fn set_legal_hold(
file_id: u64,
current_time: u64,
user_id: u64,
) -> Result<(), &'static str> {
let mut mgr = WORM_MANAGER.lock();
mgr.set_legal_hold(file_id, current_time, user_id)
}
pub fn remove_legal_hold(
file_id: u64,
current_time: u64,
user_id: u64,
) -> Result<(), &'static str> {
let mut mgr = WORM_MANAGER.lock();
mgr.remove_legal_hold(file_id, current_time, user_id)
}
pub fn extend_retention(
file_id: u64,
additional_time: u64,
current_time: u64,
user_id: u64,
) -> Result<(), &'static str> {
let mut mgr = WORM_MANAGER.lock();
mgr.extend_retention(file_id, additional_time, current_time, user_id)
}
pub fn verify_integrity(
file_id: u64,
actual_hash: &[u8; 32],
current_time: u64,
user_id: u64,
) -> bool {
let mut mgr = WORM_MANAGER.lock();
mgr.verify_integrity(file_id, actual_hash, current_time, user_id)
}
pub fn audit_log(file_id: u64) -> Vec<AuditEntry> {
let mgr = WORM_MANAGER.lock();
mgr.get_audit_log(file_id)
}
pub fn stats() -> WormStats {
let mgr = WORM_MANAGER.lock();
mgr.get_stats()
}
}
#[cfg(test)]
mod tests {
use super::*;
impl WormManager {
fn test_lock_file(
&mut self,
file_id: u64,
dataset_id: u64,
mode: RetentionMode,
retention_period: u64,
current_time: u64,
content_hash: [u8; 32],
user_id: u64,
) -> Result<(), &'static str> {
let file = WormFile::new(
file_id,
dataset_id,
mode,
retention_period,
current_time,
content_hash,
);
self.lock_file(file, user_id)
}
}
#[test]
fn test_worm_file_lock() {
let hash = [0u8; 32];
let worm = WormFile::new(1, 100, RetentionMode::Compliance, 3600, 0, hash);
assert!(worm.is_locked(1000)); assert!(!worm.is_locked(4000)); }
#[test]
fn test_compliance_mode_no_delete() {
let hash = [0u8; 32];
let worm = WormFile::new(1, 100, RetentionMode::Compliance, 3600, 0, hash);
assert!(!worm.can_delete(1000, true));
assert!(!worm.can_delete(1000, false));
assert!(worm.can_delete(4000, false));
}
#[test]
fn test_governance_mode_override() {
let hash = [0u8; 32];
let worm = WormFile::new(1, 100, RetentionMode::Governance, 3600, 0, hash);
assert!(!worm.can_delete(1000, false));
assert!(worm.can_delete(1000, true));
}
#[test]
fn test_legal_hold_prevents_deletion() {
let hash = [0u8; 32];
let mut worm = WormFile::new(1, 100, RetentionMode::Governance, 3600, 0, hash);
worm.legal_hold = true;
assert!(!worm.can_delete(10000, true));
}
#[test]
fn test_never_modifiable() {
let hash = [0u8; 32];
let worm = WormFile::new(1, 100, RetentionMode::Compliance, 3600, 0, hash);
assert!(!worm.can_modify(0));
assert!(!worm.can_modify(1000));
assert!(!worm.can_modify(10000));
}
#[test]
fn test_integrity_verification() {
let hash = [1u8; 32];
let worm = WormFile::new(1, 100, RetentionMode::Compliance, 3600, 0, hash);
assert!(worm.verify_integrity(&[1u8; 32])); assert!(!worm.verify_integrity(&[2u8; 32])); }
#[test]
fn test_worm_manager_lock() {
let mut mgr = WormManager::new();
let hash = [0u8; 32];
mgr.test_lock_file(1, 100, RetentionMode::Compliance, 3600, 0, hash, 999)
.expect("test: operation should succeed");
assert_eq!(mgr.stats.total_files, 1);
assert_eq!(mgr.stats.compliance_files, 1);
}
#[test]
fn test_modify_attempt_tracking() {
let mut mgr = WormManager::new();
let hash = [0u8; 32];
mgr.test_lock_file(1, 100, RetentionMode::Compliance, 3600, 0, hash, 999)
.expect("test: operation should succeed");
assert!(!mgr.can_modify(1, 1000, 999));
assert_eq!(mgr.stats.modify_attempts, 1);
let log = mgr.get_audit_log(1);
assert_eq!(log.len(), 2); }
#[test]
fn test_legal_hold() {
let mut mgr = WormManager::new();
let hash = [0u8; 32];
mgr.test_lock_file(1, 100, RetentionMode::Governance, 3600, 0, hash, 999)
.expect("test: operation should succeed");
mgr.set_legal_hold(1, 1000, 999)
.expect("test: operation should succeed");
assert_eq!(mgr.stats.legal_hold_files, 1);
assert!(!mgr.can_delete(1, 10000, true, 999));
mgr.remove_legal_hold(1, 10001, 999)
.expect("test: operation should succeed");
assert_eq!(mgr.stats.legal_hold_files, 0);
assert!(mgr.can_delete(1, 10002, false, 999));
}
#[test]
fn test_retention_extension() {
let mut mgr = WormManager::new();
let hash = [0u8; 32];
mgr.test_lock_file(1, 100, RetentionMode::Compliance, 3600, 0, hash, 999)
.expect("test: operation should succeed");
mgr.extend_retention(1, 3600, 1000, 999)
.expect("test: operation should succeed");
let worm = mgr.get_file(1).expect("test: operation should succeed");
assert_eq!(worm.retention_period, 7200); assert_eq!(worm.expiration_time, 7200);
}
}