aboutsummaryrefslogtreecommitdiffstats
path: root/src/store.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/store.rs')
-rw-r--r--src/store.rs26
1 files changed, 19 insertions, 7 deletions
diff --git a/src/store.rs b/src/store.rs
index 18a7641..98c5481 100644
--- a/src/store.rs
+++ b/src/store.rs
@@ -45,14 +45,12 @@ impl PasteStore {
// ── Paste CRUD ──────────────────────────────────
- /// Write a paste to disk. The key (32 bytes) is prepended
- /// to the file so [`original_keys`] can reconstruct it.
+ /// Write a paste to disk atomically (write-to-temp + rename).
+ /// The key (32 bytes) is prepended to the file so
+ /// [`original_keys`] can reconstruct it.
pub fn put_paste(&self, key: &[u8], value: &[u8]) -> std::io::Result<()> {
let path = self.paste_path(key);
- let mut f = fs::File::create(path)?;
- f.write_all(key)?;
- f.write_all(value)?;
- Ok(())
+ atomic_write(&path, &[key, value])
}
/// Read a paste from disk. Returns `None` if the paste
@@ -179,6 +177,20 @@ impl PasteStore {
}
}
+/// Write data to `path` atomically: write to a temporary file in
+/// the same directory, then rename over the target. This prevents
+/// corruption if the process is killed mid-write.
+fn atomic_write(path: &Path, chunks: &[&[u8]]) -> std::io::Result<()> {
+ let parent = path.parent().unwrap_or(Path::new("."));
+ let tmp = parent.join(format!(".tmp.{}", std::process::id()));
+ let mut f = fs::File::create(&tmp)?;
+ for chunk in chunks {
+ f.write_all(chunk)?;
+ }
+ f.sync_all()?;
+ fs::rename(&tmp, path)
+}
+
// ── tesseras-dht persistence traits ─────────────────
impl tesseras_dht::persist::RoutingPersistence for PasteStore {
@@ -198,7 +210,7 @@ impl tesseras_dht::persist::RoutingPersistence for PasteStore {
buf.extend_from_slice(id);
buf.extend_from_slice(addr_bytes);
}
- fs::write(&path, &buf).map_err(tesseras_dht::Error::Io)?;
+ atomic_write(&path, &[&buf]).map_err(tesseras_dht::Error::Io)?;
log::info!("store: persisted {} routing contacts", contacts.len());
Ok(())
}