diff options
Diffstat (limited to 'src/store.rs')
| -rw-r--r-- | src/store.rs | 26 |
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(()) } |