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.
This commit is contained in:
Markus Unterwaditzer
2025-03-31 01:06:20 +02:00
committed by Will Greenberg
parent 3c0716c877
commit 58f0071864
+18 -16
View File
@@ -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<P>(path: P) -> Result<Manifest, RecordingStoreError>
@@ -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(())
}