diff --git a/Cargo.lock b/Cargo.lock index d018aa027..fff01f81d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6498,6 +6498,7 @@ dependencies = [ "roxmltree 0.21.2", "rustix 1.1.6", "ruzstd", + "same-file", "serde", "serde_json", "skrifa 0.47.0", diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index 836d4b744..0fccbcdec 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -67,6 +67,11 @@ log = { workspace = true } [target.'cfg(unix)'.dependencies] rustix = { workspace = true, features = ["std", "process"] } +# Whether a recovery area's lock file is still the one that was opened (`FolderStore::still_named`). +# Already in Cargo.lock (walkdir), so this adds no new crate. +[target.'cfg(windows)'.dependencies] +same-file = "1.0.6" + # The tiles of a HEIC photo decode in parallel on native; wasm stays single-threaded. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] heic-rs = { version = "=0.1.4", default-features = false, features = ["std", "parallel"], optional = true } diff --git a/crates/engine/src/cmd/recovery.rs b/crates/engine/src/cmd/recovery.rs index ffd3badf0..b75066ec6 100644 --- a/crates/engine/src/cmd/recovery.rs +++ b/crates/engine/src/cmd/recovery.rs @@ -342,7 +342,8 @@ impl FolderStore { } /// Does `path` still name `file`? On Unix the two must be the same file (device and inode); on - /// Windows they must have the same creation time; elsewhere `path` must exist. + /// Windows they must be the same file (volume serial number and file index); elsewhere `path` + /// must exist. When either file can't be read, it does not. fn still_named(file: &std::fs::File, path: &std::path::Path) -> bool { #[cfg(unix)] { @@ -354,9 +355,9 @@ impl FolderStore { } #[cfg(windows)] { - use std::os::windows::fs::MetadataExt as _; - match (file.metadata(), std::fs::symlink_metadata(path)) { - (Ok(a), Ok(b)) => a.creation_time() == b.creation_time(), + let opened = file.try_clone().and_then(same_file::Handle::from_file); + match (opened, same_file::Handle::from_path(path)) { + (Ok(a), Ok(b)) => a == b, _ => false, } } diff --git a/crates/engine/src/tests/recovery.rs b/crates/engine/src/tests/recovery.rs index ce76b612c..4994a9072 100644 --- a/crates/engine/src/tests/recovery.rs +++ b/crates/engine/src/tests/recovery.rs @@ -572,8 +572,9 @@ fn an_area_tidied_away_before_it_is_locked_is_busy() { // The folder went before it was checked or its lock file opened. std::fs::remove_dir(&area).unwrap(); assert!(matches!(FolderStore::lock_area(&area), Ok(Lock::Busy))); - // A lock file made again under the same name is not the one opened. - #[cfg(unix)] + // A lock file made again under the same name is not the one opened, also where it keeps the + // removed one's creation time (NTFS does within 15 seconds). + #[cfg(any(unix, windows))] { std::fs::create_dir_all(&area).unwrap(); let opened = open(true);