Skip to content

Fix file:write CREATE_NEW temp-file path resolution on Windows [Issue #5016] - #297

Merged
ravindu25 merged 3 commits into
wso2-extensions:masterfrom
ravindu25:fix/issue-5016-windows-write-create-new
Jun 2, 2026
Merged

Fix file:write CREATE_NEW temp-file path resolution on Windows [Issue #5016]#297
ravindu25 merged 3 commits into
wso2-extensions:masterfrom
ravindu25:fix/issue-5016-windows-write-create-new

Conversation

@ravindu25

@ravindu25 ravindu25 commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Root Cause

In WriteFile.java lines 448–449 (CREATE_NEW branch), the temp file was constructed by string-concatenating targetFile.getParent().getName().getPath() + "/" + basename + ".tmp" and passing that string to fileSystemHandlerConnection.resolveFileWithSuspension(String).

FileName.getPath() in Commons VFS intentionally returns only the path component relative to the rootFile — on Windows, the drive letter (C:) is part of the rootFile and is not included in getPath(). The concatenated string therefore has no URI scheme and no drive letter (e.g. /wso2/mi/LocalEntry/out/fileName.csv.tmp), which matches the URI in the customer's stack trace exactly. DefaultFileSystemManager.resolveFile(String) cannot find a provider for a scheme-less string with no drive letter and throws the documented exception.

The same string-concatenation pattern accidentally "works" on Linux because the resulting bare absolute path under / happens to look valid to VFS — but it silently resolves to the wrong filesystem location (a latent data-loss bug).

Fix

Replace the manual string concatenation with the standard VFS parent-relative resolve idiom:

// Before (buggy):
try (FileObject tempFile = fileSystemHandlerConnection.resolveFileWithSuspension(
        targetFile.getParent().getName().getPath() + Const.FILE_SEPARATOR
                + targetFile.getName().getBaseName() + ".tmp")) {

// After (fixed):
try (FileObject tempFile = targetFile.getParent().resolveFile(
        targetFile.getName().getBaseName() + ".tmp")) {

FileObject.resolveFile(String) resolves the child name against the parent FileObject, which already carries the scheme and rootFile (drive letter or UNC share). This restores the behavior of v4.0.28 (fsManager.resolveFile(parent, basename + ".tmp")), which worked correctly on Windows.

No new imports. No other lines changed. The body of the try-with-resources block (tempFile.createFile(), performContentWrite/performBodyWrite, tempFile.moveTo(targetFile)) is untouched.

Verification

Verified at library level against the exact commons-vfs2_2.2.0.wso2v20_3.jar shipped in wso2mi-4.4.0.30.zip:

Case Before (buggy) After (patched)
Windows file:///C:/... /C:/wso2/mi/.../fileName.csv.tmp (no scheme, no drive — matches customer stack trace) file:///C:/wso2/mi/.../fileName.csv.tmp
Linux file:///... /wso2/mi/.../fileName.csv.tmp (resolves to wrong VFS location) file:///wso2/mi/.../fileName.csv.tmp
UNC file:////SERVER/share/... /wso2/.../fileName.csv.tmp (no server+share) file:////SERVER/share/.../fileName.csv.tmp
End-to-end I/O (createFile() → write → moveTo) N/A (would throw FileSystemException) 23-byte CSV written and renamed successfully ✓

Note on live-server verification: Connector v6.0.2 is compiled against the WSO2-orbit-shaded org.wso2.org.apache.commons.vfs2.* namespace, which ships from MI U49 onward. The only product pack available in this environment is U30. End-to-end live verification requires a U49+ pack (WSO2 subscription required) — the reviewer / release engineer should verify on U49+ before customer hand-off.

Tests Added

src/test/java/org/wso2/carbon/connector/operations/WriteFileTempFileResolutionTest.java — 10 TestNG tests (9 pass, 1 skipped if smb2 provider absent) covering:

  • Windows-parsed file:///C:/… — scheme + drive letter preserved
  • Linux-parsed file:///… — parent-relative behavior
  • UNC file:////SERVER/share/… — server + share preserved
  • smb2://host/share/… — scheme + host preserved (skipped if provider unavailable)
  • URI-reserved chars / %20-encoded path — encoding preserved
  • Drive-root parent edge case — no double-slash in resolved URI
  • End-to-end createFile() → write → moveTo() succeeds
  • Diagnostic: OLD bare-path concat is the necessary-and-sufficient condition for the customer's FileSystemException

Run with:

mvn -q test-compile && mvn -q dependency:build-classpath -DincludeScope=test -Dmdep.outputFile=/tmp/test-classpath.txt
CP="target/test-classes:target/classes:$(cat /tmp/test-classpath.txt)"
java -cp "$CP" org.testng.TestNG src/test/resources/testng-unit.xml

(The repo's surefire config has a hard-coded -XX:MaxPermSize=128m that fails on JDK 9+ — direct TestNG invocation is the documented workaround until the pom's argLine is parameterised.)

Known Limitations / Follow-up

  1. PR base branch: esb-connector-file has no wso2-support/* or release-6.0.x branch (release branches stop at release-5.0.3; v6.x continues on master). Per the WSO2 support patch convention, a PR for a customer patch should NOT target master. Maintainers should confirm whether master is acceptable for this patch release or whether a wso2-support/6.0.x branch should be created first.

  2. Sibling operations not fixed: The same getName().getPath() + FILE_SEPARATOR + … concatenation pattern exists in CheckFileExist, MoveFiles, and other operations. They are explicitly out of scope for this customer-patch ticket but will fail on Windows with the same exception. A follow-up cleanup ticket against esb-connector-file should be opened.

  3. pom version bump: The plan defers the pom.xml version bump from 6.0.2 to 6.0.3 to release time, per connector maintainer convention.

ravindu25 added 3 commits May 28, 2026 11:49
Replace manual string concatenation of `parent.getName().getPath() + "/" + basename + ".tmp"` with
`targetFile.getParent().resolveFile(basename + ".tmp")` in WriteFile.java's CREATE_NEW branch.

The old code stripped the URI scheme and Windows drive letter from the path (VFS FileName.getPath()
returns only the path component relative to the rootFile), causing DefaultFileSystemManager to throw
"Could not find file with URI ... because it is a relative path, and no base URI was provided" on
Windows hosts. The fix restores the v4.0.28 parent-relative resolve idiom so the temp file URI
preserves the scheme and drive letter on Windows, Linux, and UNC paths.

Add WriteFileTempFileResolutionTest (TestNG) with 10 tests covering Windows, Linux, UNC, smb2,
reserved-char encoding, drive-root edge case, and end-to-end createFile/write/moveTo.

Fixes: wso2/product-integrator-mi#4907
@ravindu25
ravindu25 force-pushed the fix/issue-5016-windows-write-create-new branch from 2393a43 to 654d834 Compare May 28, 2026 06:25
@ravindu25
ravindu25 merged commit bba6068 into wso2-extensions:master Jun 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants