gnodet opened a new pull request, #2205:
URL: https://github.com/apache/maven-resolver/pull/2205

   ## Summary
   
   Fixes a Windows regression introduced by PR #2083 (resolver 2.0.24 / Maven 
3.10.0): `mvn install` throws `java.nio.file.AccessDeniedException` when an IDE 
(e.g. IntelliJ IDEA) keeps the installed `.jar` open.
   
   Closes #2204 · Closes apache/maven#13329
   
   ## Root Cause
   
   PR #2083 replaced the old stream-based file copy with `Files.move()` + 
`ATOMIC_MOVE` in `PathProcessorSupport.retryingMove()` to fix the f007 security 
finding. That was the right thing to do — but it introduced a behavioral 
regression on Windows.
   
   **Old behavior (resolver ≤ 1.9.x):** `classicCopy()` opens the target with a 
truncating `FileOutputStream`. On Windows, this succeeds even when another 
process has the file open for reading, because the stream open uses 
`FILE_SHARE_READ` semantics.
   
   **New behavior (resolver 2.0.24):** `Files.move()` maps to 
`MoveFileEx`/`ReplaceFile` on Windows. These system calls **require exclusive 
access** to the destination — any open handle (including a shared read-only 
handle from an IDE's class loader) causes `AccessDeniedException`. With only 5 
retries at 50 ms (250 ms total), a persistent IDE lock is never released and 
all retries fail.
   
   | Operation | Target open by reader? | Succeeds on Windows? |
   |---|---|---|
   | `FileOutputStream(target)` (stream write) | Yes (shared read) | ✅ Yes |
   | `Files.move(src, target, ATOMIC_MOVE)` | Yes (shared read) | ❌ No — needs 
exclusive |
   
   ## Fix
   
   After exhausting all `WINDOWS_MOVE_ATTEMPTS` with `AccessDeniedException`, 
fall back to `classicCopy()` directly to the target as a last resort, logging a 
`WARN` to identify the situation. The atomicity guarantee is lost for this edge 
case (a concurrent reader may observe a partially written file), but the build 
completes — matching the pre-2.0.24 behavior that users relied on.
   
   A new system property controls the fallback:
   ```
   -Dorg.eclipse.aether.spi.io.PathProcessor.WINDOWS_CLASSIC_COPY_FALLBACK=false
   ```
   Setting it to `false` disables the fallback and propagates the original 
`AccessDeniedException` (strict never-truncate mode).
   
   The fallback is also exposed as an overridable `protected boolean 
isClassicCopyFallbackEnabled()` method for subclasses and tests.
   
   ## Note on PR #2198
   
   The open PR #2198 (hard-link install) sidesteps this problem for `mvn 
install` by not copying at all. This PR fixes the underlying `retryingMove` 
logic, which is also exercised in other code paths (download to local repo, 
`writeFile`). The two PRs are complementary.
   
   ## Changes
   
   - `maven-resolver-spi/pom.xml` — add `slf4j-api` compile dependency and 
`slf4j-simple` test dependency
   - `PathProcessorSupport.java` — add `WINDOWS_CLASSIC_COPY_FALLBACK` 
constant, `isClassicCopyFallbackEnabled()` hook, fallback logic in 
`retryingMove()`
   - `PathProcessorSupportTest.java` — rename 
`retryingMoveNeverTruncatesTargetOnFailure` → `...WhenFallbackDisabled` (adds 
subclass override), add `retryingMoveFallsBackToClassicCopyOnPersistentLock`
   
   _Hermès Agent (Claude Sonnet 4.6) on behalf of Guillaume Nodet_
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to