From 1704bef5816445e922c229bbd036ba67711ff0ff Mon Sep 17 00:00:00 2001 From: Martin Ball Date: Sun, 1 Nov 2026 18:38:59 +0000 Subject: [PATCH] =?UTF-8?q?CP4.1:=20review=20=E2=80=94=20a=20staging=20fol?= =?UTF-8?q?der=20left=20by=20an=20earlier=20process=20with=20the=20same=20?= =?UTF-8?q?id=20is=20cleared=20before=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2 --- crates/font-store/src/language_packs/tests.rs | 15 +++++++++++++++ crates/font-store/src/store.rs | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/crates/font-store/src/language_packs/tests.rs b/crates/font-store/src/language_packs/tests.rs index f9ef14e8..1107e719 100644 --- a/crates/font-store/src/language_packs/tests.rs +++ b/crates/font-store/src/language_packs/tests.rs @@ -318,3 +318,18 @@ fn hostile_archives_are_refused() { unpack(&pack_archive("nl", "x"), &dest).unwrap(); assert!(dest.join("LICENCES/COPYING").is_file()); } + +/// A staging folder an earlier process with the same id left behind is cleared, so an unpack +/// (`create_new`) can't fail on, or mix in, its files. +#[test] +fn a_stale_stage_folder_is_cleared() { + let store = temp("stale-stage"); + let staging = store.join(".staging"); + for n in 0..512 { + let d = staging.join(format!("{}-{n}", std::process::id())); + std::fs::create_dir_all(d.join("nl")).unwrap(); + std::fs::write(d.join("nl/pack.toml"), b"old").unwrap(); + } + let stage = Stage::new(&store).unwrap(); + assert_eq!(std::fs::read_dir(&stage.dir).unwrap().count(), 0, "{}", stage.dir.display()); +} diff --git a/crates/font-store/src/store.rs b/crates/font-store/src/store.rs index e2cd1161..7c3ed0f3 100644 --- a/crates/font-store/src/store.rs +++ b/crates/font-store/src/store.rs @@ -840,6 +840,10 @@ impl Stage { pub(crate) fn new(store: &Path) -> Result { static N: AtomicU64 = AtomicU64::new(0); let dir = store.join(STAGING).join(format!("{}-{}", std::process::id(), N.fetch_add(1, Ordering::Relaxed))); + // A folder left by an earlier process with the same id is cleared, not mixed in. + if std::fs::symlink_metadata(&dir).is_ok() { + std::fs::remove_dir_all(&dir).map_err(io("clearing an old download folder"))?; + } std::fs::create_dir_all(&dir).map_err(io("creating a download folder"))?; Ok(Self { dir }) }