From 58f0071864e57f223ba0fd65abd74ee87e561b4b Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Mon, 31 Mar 2025 01:06:20 +0200 Subject: [PATCH] Fix malformed QMDL store writes Fix https://github.com/EFForg/rayhunter/issues/199 Fix https://github.com/EFForg/rayhunter/issues/151 rayhunter updates manifest files using write **without truncation**. This means that if the new manifest is shorter than the old one, trailing bytes of the old data will persist in the new file. Switch over to atomic file writes so that this bug is fixed + rayhunter behaves correctly if it is killed mid-write. https://github.com/EFForg/rayhunter/pull/182 could be reverted as it seems to mostly be a workaround. --- bin/src/qmdl_store.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/bin/src/qmdl_store.rs b/bin/src/qmdl_store.rs index d33c2b5..f6741f6 100644 --- a/bin/src/qmdl_store.rs +++ b/bin/src/qmdl_store.rs @@ -120,19 +120,17 @@ impl RecordingStore { fs::create_dir_all(&path) .await .map_err(RecordingStoreError::OpenDirError)?; - let mut manifest_file = File::create(&manifest_path) - .await - .map_err(RecordingStoreError::WriteManifestError)?; - let empty_manifest = Manifest { - entries: Vec::new(), + + let mut store = RecordingStore { + path: manifest_path, + manifest: Manifest { + entries: Vec::new() + }, + current_entry: None, }; - let empty_manifest_contents = - toml::to_string_pretty(&empty_manifest).expect("failed to serialize manifest"); - manifest_file - .write_all(empty_manifest_contents.as_bytes()) - .await - .map_err(RecordingStoreError::WriteManifestError)?; - RecordingStore::load(path).await + + store.write_manifest().await?; + Ok(store) } async fn read_manifest

(path: P) -> Result @@ -240,17 +238,21 @@ impl RecordingStore { } async fn write_manifest(&mut self) -> Result<(), RecordingStoreError> { - let mut manifest_file = File::options() - .write(true) - .open(self.path.join("manifest.toml")) + let tmp_path = self.path.join("manifest.toml.new"); + let mut manifest_tmp_file = File::create(&tmp_path) .await .map_err(RecordingStoreError::WriteManifestError)?; + let manifest_contents = toml::to_string_pretty(&self.manifest).expect("failed to serialize manifest"); - manifest_file + manifest_tmp_file .write_all(manifest_contents.as_bytes()) .await .map_err(RecordingStoreError::WriteManifestError)?; + + fs::rename(tmp_path, self.path.join("manifest.toml")).await + .map_err(RecordingStoreError::WriteManifestError)?; + Ok(()) }