Release the clear's lock fully and clear conversion leftovers

The exclusive traffic.lock a launch takes to clear is let go before it
is taken shared, since Windows would otherwise hold both. Clearing the
blob store also deletes what an interrupted encryption left beside it,
so opening doesn't recover the old bodies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Gregory SchierandClaude Opus 5.5 authored and Stetta task tk_mw85knosvvdsacxu committed 2026-11-09 12:26:38 -07:00
1 parent 52d9c9596b
commit 3f3fd15959
2 files changed
+24 -3

No files matched your search

+21 -2
View File
@@ -271,9 +271,11 @@ pub fn remove_captured(data_dir: &Path) -> std::result::Result<(), String> {
} }
/// Deletes `blobs.sqlite`, every body and message payload, for one made empty as it opens. /// Deletes `blobs.sqlite`, every body and message payload, for one made empty as it opens.
/// The data key stays, as `proxy.db` is still encrypted with it. Nothing may have it open. /// What an interrupted conversion to encryption left beside it goes too, or opening would
/// recover it, bodies and all. The data key stays, as `proxy.db` is still encrypted with it.
/// Nothing may have it open.
pub fn remove_blobs(data_dir: &Path) -> std::result::Result<(), String> { pub fn remove_blobs(data_dir: &Path) -> std::result::Result<(), String> {
for suffix in ["", "-wal", "-shm"] { for suffix in ["", "-wal", "-shm", "-plain", "-encrypting"] {
let path = data_dir.join(format!("{BLOBS_DB}{suffix}")); let path = data_dir.join(format!("{BLOBS_DB}{suffix}"));
match std::fs::remove_file(&path) { match std::fs::remove_file(&path) {
Err(e) if e.kind() != std::io::ErrorKind::NotFound => { Err(e) if e.kind() != std::io::ErrorKind::NotFound => {
@@ -739,4 +741,21 @@ mod tests {
let old = crate::flows::find(&db, "fl_old").unwrap(); let old = crate::flows::find(&db, "fl_old").unwrap();
assert!(!old.tunnelled); assert!(!old.tunnelled);
} }
/// Removing the blob store takes what an interrupted conversion left too, which opening
/// would otherwise recover, and leaves `proxy.db` and the key.
#[test]
fn removing_the_blobs_takes_a_conversion_s_leftovers() {
let dir = tempfile::tempdir().unwrap();
let blobs = ["", "-wal", "-shm", "-plain", "-encrypting"].map(|s| format!("{BLOBS_DB}{s}"));
let kept = [PROXY_DB, DATA_KEY];
for name in blobs.iter().map(String::as_str).chain(kept) {
std::fs::write(dir.path().join(name), b"x").unwrap();
}
remove_blobs(dir.path()).unwrap();
assert!(blobs.iter().all(|name| !dir.path().join(name).exists()));
assert!(kept.iter().all(|name| dir.path().join(name).exists()));
// Nothing there is fine too.
remove_blobs(dir.path()).unwrap();
}
} }
+3 -1
View File
@@ -1484,8 +1484,10 @@ struct ShareOnDrop<'a>(&'a Mutex<Option<std::fs::File>>);
impl Drop for ShareOnDrop<'_> { impl Drop for ShareOnDrop<'_> {
fn drop(&mut self) { fn drop(&mut self) {
let held = self.0.lock().unwrap_or_else(PoisonError::into_inner); let held = self.0.lock().unwrap_or_else(PoisonError::into_inner);
// Let go first: Windows would otherwise add the shared lock beside the exclusive one.
// Unix's flock lets go between the two as well, so this is no wider a gap.
if let Some(file) = held.as_ref() if let Some(file) = held.as_ref()
&& let Err(e) = file.lock_shared() && let Err(e) = file.unlock().and_then(|()| file.lock_shared())
{ {
warn!("Failed to share {TRAFFIC_LOCK}: {e}"); warn!("Failed to share {TRAFFIC_LOCK}: {e}");
} }