fix(io): stop the drawing lock blocking our own reads on Windows

Saving to a path that already holds a drawing fails on Windows with
"could not verify <path> before saving: ... (os error 33)". Save As over any
existing drawing is affected; saving to a new path is not.

EditLease::acquire() opens the destination and takes the platform drawing
lock on it, then the save fingerprints the same path to check nothing changed
underneath. LockFileEx is mandatory and scoped to the handle that took it, so
that second open is refused against our own lock, lands in the non-NotFound
arm and aborts the save. flock on Unix is advisory and the second open
succeeds, so neither CI nor a Linux desktop sees it.

Read through the handle the lease already holds instead. A duplicate shares
the lock ownership, so the pre-replace re-check on the save worker can use
one too rather than re-opening the path.

The same collision reached the headless open op, which read a path this
session may hold leased: automation's save_then_open_round_trips fails on
main for that reason, one line after the save it no longer reaches.
This commit is contained in:
NgoQuocViet2001 committed 2026-08-17 22:49:54 +07:00
1 parent 0b5d4bd311
commit c88f07d219
4 files changed
+173 -6

No files matched your search

+10 -1
View File
@@ -1437,6 +1437,7 @@ mod save_failure_tests {
acadrust::DxfVersion::AC1032,
false,
Some(expected),
None,
)
.unwrap_err();
@@ -1509,12 +1510,20 @@ pub fn save_owned_as_version_atomic(
version: acadrust::DxfVersion,
backup: bool,
expected_fingerprint: Option<edit_lock::FileFingerprint>,
verify_reader: Option<std::fs::File>,
) -> Result<(), SaveFailure> {
save_owned_as_version_inner(doc, path, version, backup, 0.0, move |path| {
let Some(expected) = expected_fingerprint else {
return Ok(());
};
match edit_lock::FileFingerprint::capture(path) {
// Prefer the caller's handle: it comes from the edit lease that already
// locked this path, and a fresh open would collide with that lock rather
// than read through it.
let current = match verify_reader {
Some(mut file) => edit_lock::FileFingerprint::capture_from(&mut file),
None => edit_lock::FileFingerprint::capture(path),
};
match current {
Ok(current) if current == expected => Ok(()),
_ => Err(SaveFailure::externally_modified(path)),
}