feat: enhance safety and crash resilience in file operations; add fsync to critical writes and checks for path traversal

This commit is contained in:
asepharyana
2026-07-12 11:49:33 +07:00
parent 8767beef39
commit 87d0aac596
9 changed files with 79 additions and 26 deletions
+12 -4
View File
@@ -36,13 +36,16 @@ pub fn load_global_agents() -> Vec<AgentDefinition> {
agents
}
/// Persist a global agent definition as `<store>/agents/<name>.json`.
/// Persist a global agent definition as `<store>/agents/<name>.json`,
/// with fsync for crash safety.
///
/// Flow: ensure the `agents/` directory exists → serialize `def` to
/// pretty JSON → write to a file named after `def.name`.
/// pretty JSON → write to a temp file → fsync → rename into place →
/// fsync parent directory.
///
/// Why: writing by name overwrites any existing definition with the
/// same name, acting as an upsert.
/// same name, acting as an upsert; fsync prevents a torn write from
/// losing the definition on crash.
///
/// Return: `Ok(())` on success, or an error if directory creation,
/// serialization, or the write fails.
@@ -51,8 +54,13 @@ pub fn save_global_agent(def: &AgentDefinition) -> anyhow::Result<()> {
let agents_dir = store.base_dir.join("agents");
std::fs::create_dir_all(&agents_dir)?;
let path = agents_dir.join(format!("{}.json", def.name));
let tmp = agents_dir.join(format!("{}.json.tmp", def.name));
let content = serde_json::to_string_pretty(def)?;
std::fs::write(path, content)?;
std::fs::write(&tmp, content)?;
let f = std::fs::File::open(&tmp)?;
f.sync_all()?;
std::fs::rename(&tmp, path)?;
let _ = std::fs::File::open(&agents_dir).and_then(|d| d.sync_all());
Ok(())
}
+10 -4
View File
@@ -31,17 +31,23 @@ pub fn load_session_agents(session_dir: &Path) -> Vec<AgentDefinition> {
}
}
/// Overwrite `<session_dir>/agents.json` with the given agent list.
/// Overwrite `<session_dir>/agents.json` with the given agent list,
/// with fsync for crash safety.
///
/// Flow: serialize `agents` to pretty JSON → write to
/// `<session_dir>/agents.json`.
/// Flow: serialize `agents` to pretty JSON → write to a temp file →
/// fsync → rename over `agents.json` → fsync parent directory.
///
/// Return: `Ok(())` on success, or an error if serialization or the
/// write fails.
pub fn save_session_agents(session_dir: &Path, agents: &[AgentDefinition]) -> anyhow::Result<()> {
let agents_file = session_dir.join("agents.json");
let tmp = session_dir.join("agents.json.tmp");
let content = serde_json::to_string_pretty(agents)?;
std::fs::write(agents_file, content)?;
std::fs::write(&tmp, content)?;
let f = std::fs::File::open(&tmp)?;
f.sync_all()?;
std::fs::rename(&tmp, agents_file)?;
let _ = std::fs::File::open(session_dir).and_then(|d| d.sync_all());
Ok(())
}