use std::panic::AssertUnwindSafe;
use std::path::Path;
use std::time::{Duration, SystemTime};
use anyhow::{Context, Result};
use trusty_common::memory_core::store::kg_redb::KgStoreRedb;
use trusty_common::memory_core::store::OpenIntent;
pub(crate) const KG_FILE: &str = "kg.redb";
pub(crate) const SCRATCH_PREFIX: &str = "trusty-memory-readonly-";
pub(crate) const STALE_COPY_AGE: Duration = Duration::from_secs(60 * 60);
pub(crate) fn with_store_copy<T>(
data_dir: &Path,
scratch_parent: &Path,
read: impl FnOnce(KgStoreRedb) -> Result<T>,
) -> Result<Option<T>> {
let live = data_dir.join(KG_FILE);
let present = live
.try_exists()
.map_err(|e| anyhow::anyhow!("cannot stat {}: {e}", live.display()))?;
if !present {
return Ok(None);
}
let scratch = tempfile::TempDir::with_prefix_in(SCRATCH_PREFIX, scratch_parent)
.context("create scratch dir for read-only palace copy")?;
let copy = scratch.path().join(KG_FILE);
std::fs::copy(&live, ©)
.with_context(|| format!("copy {} for read-only inspection", live.display()))?;
let opened = std::panic::catch_unwind(AssertUnwindSafe(|| {
let store = KgStoreRedb::open_with_intent(©, OpenIntent::ReadOnlyClient)
.with_context(|| format!("open copy of KG store {}", live.display()))?;
read(store)
}));
match opened {
Ok(result) => result.map(Some),
Err(_) => anyhow::bail!(
"panic while opening or reading the copy of KG store {} \
(torn copy or internal error)",
live.display()
),
}
}
pub(crate) fn sweep_stale_copies(scratch_parent: &Path, max_age: Duration) -> usize {
let entries = match std::fs::read_dir(scratch_parent) {
Ok(entries) => entries,
Err(e) => {
tracing::warn!(dir = %scratch_parent.display(), "cannot sweep stale store copies: {e}");
return 0;
}
};
let now = SystemTime::now();
let mut removed = 0;
for entry in entries.flatten() {
if !entry
.file_name()
.to_string_lossy()
.starts_with(SCRATCH_PREFIX)
{
continue;
}
let path = entry.path();
let stale = std::fs::symlink_metadata(&path).is_ok_and(|m| {
m.is_dir()
&& m.modified()
.is_ok_and(|t| now.duration_since(t).is_ok_and(|age| age >= max_age))
});
if !stale {
continue;
}
match std::fs::remove_dir_all(&path) {
Ok(()) => removed += 1,
Err(e) => {
tracing::warn!(dir = %path.display(), "cannot remove stale store copy: {e}");
}
}
}
removed
}