Federico Mariani created CAMEL-24092:
----------------------------------------
Summary: camel-file: readLock=fileLock claims the lock after an
IOException - file processed with no lock held and marker file deleted
(possible duplicate consumption)
Key: CAMEL-24092
URL: https://issues.apache.org/jira/browse/CAMEL-24092
Project: Camel
Issue Type: Bug
Components: camel-file
Affects Versions: 4.21.0
Reporter: Federico Mariani
Attachments: FileLockReadLockIOExceptionShouldNotAcquireIssueTest.java
h3. Problem
In {{FileLockExclusiveReadLockStrategy.acquireExclusiveReadLock}}
(FileLockExclusiveReadLockStrategy.java:114-140), with the default
{{readLockTimeout=10000}}: if {{new RandomAccessFile(target, "rw")}} or
{{channel.tryLock()}} throws {{IOException}} (file deleted between poll and
lock, permission error, Windows AV/MS-Office special locks),
{{handleIOException}} logs "Cannot acquire read lock. Will try again.", sleeps
once, returns false - and control *falls out of the catch block*: the
{{finally}} (since {{exclusive == false}}) closes channel/RAF and calls
{{super.releaseExclusiveReadLockOnAbort(...)}} which *deletes the .camelLock
marker file*, then the method stores {{lock = null}} and *returns true*.
h3. Failure scenario
The consumer processes the file with (a) no OS file lock and (b) the marker
protection removed - a competing consumer in another JVM watching the same
directory can acquire the marker and process the same file concurrently ->
duplicate consumption. There is also no actual retry despite the "Will try
again" log, and the {{timeout == 0}} branch correctly returns false - the two
branches contradict each other.
h3. History
The fall-through-to-true dates to CAMEL-1195 (2008, deliberate "process anyway
on Windows-AV IOE" degradation). But CAMEL-5324 (2012) later layered marker
files into this strategy, and the finally releasing that marker before
returning true cannot be intentional - it silently drops the exact cross-JVM
protection CAMEL-5324 added. CAMEL-7988 changed the {{timeout==0}} branch from
throw to return-false, leaving the branches contradictory.
h3. Suggested fix
Return false from the IOException path (consistent with the {{timeout==0}}
branch). If the degraded consume-anyway behavior must be preserved for the
Windows-AV case, at minimum the marker must not be released and the retry log
fixed.
Adjacent nit found in the same class: {{doReleaseExclusiveReadLock}} (line
164-165) fetches the {{RandomAccessFile}} under the
{{FILE_LOCK_EXCLUSIVE_LOCK}} key instead of {{FILE_LOCK_RANDOM_ACCESS_FILE}},
so {{rac}} is always null (no fd leak today only because closing the channel
closes the shared fd).
h3. Reproducer
Attached failing JUnit test (place in
{{core/camel-core/src/test/java/org/apache/camel/component/file/strategy/}},
where the file component tests live). Makes the target file unwritable so
{{RandomAccessFile("rw")}} throws, and asserts {{acquireExclusiveReadLock}}
returns false; on current main it returns true (and has deleted the marker).
----
_This issue was researched and filed by Claude Code on behalf of [~croway]
(GitHub: Croway), as part of a deep code review of camel-file and camel-ftp. A
failing JUnit reproducer is attached; it fails deterministically on current
main (4.22.0-SNAPSHOT)._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)