Skip to content

Fix FtpFileObject.exists() returning true for root-level folders after connection drop#757

Open
ilang wants to merge 1 commit intoapache:masterfrom
new-proimage:VFS-root-exists-on-disconnect
Open

Fix FtpFileObject.exists() returning true for root-level folders after connection drop#757
ilang wants to merge 1 commit intoapache:masterfrom
new-proimage:VFS-root-exists-on-disconnect

Conversation

@ilang
Copy link
Copy Markdown

@ilang ilang commented Mar 30, 2026

Summary

When getParent() returns null (root-level FTP path with userDirIsRoot=true), setFTPFile() blindly assumed the directory exists by creating an FTPFile with DIRECTORY_TYPE without contacting the server. This caused exists() to return true even after the FTP connection was lost, while non-root folders correctly reported the failure via FileSystemException.

Fix

Use CWD (Change Working Directory) to verify the directory actually exists on the server. CWD is a lightweight control-channel command with no data transfer, unlike LIST which would transfer the entire directory contents.

Changes

  • FtpClient: add changeDirectory() default method
  • FTPClientWrapper: implement changeDirectory() via FTPClient.changeWorkingDirectory()
  • FtpFileObject.setFTPFile(): replace blind DIRECTORY_TYPE assumption with verifyRootDirectory() using CWD
  • FtpRootExistsOnDisconnectTest: regression test — fails without fix, passes with fix

Test plan

  • New test FtpRootExistsOnDisconnectTest verifies exists() detects connection drops on root-level folders
  • Full VFS2 test suite passes (3203 tests, 0 failures — only 2 pre-existing HTTP timeout failures unrelated to this change)

@ilang ilang force-pushed the VFS-root-exists-on-disconnect branch from 2c619c8 to 5984094 Compare March 31, 2026 01:01
Copy link
Copy Markdown
Member

@garydgregory garydgregory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1: don't write to the console.

*/
package org.apache.commons.vfs2.provider.ftp;

import static org.apache.commons.vfs2.VfsTestUtils.getTestDirectory;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only use static imports for JUnit assertions.

@ilang ilang force-pushed the VFS-root-exists-on-disconnect branch from 5984094 to 29f5342 Compare March 31, 2026 08:02
@ilang ilang marked this pull request as draft March 31, 2026 20:36
@ilang ilang marked this pull request as ready for review March 31, 2026 21:55
@ilang ilang requested a review from garydgregory March 31, 2026 21:55
@ilang ilang marked this pull request as draft March 31, 2026 22:34
…s after connection drop

setFTPFile() blindly assumed that root-level directories (where
getParent() returns null) always exist by creating a synthetic FTPFile
with DIRECTORY_TYPE. This caused exists() to return true even after the
FTP connection was lost.

Fix: add verifyRootDirectory() which uses CWD "." to verify the
directory exists on the server. CWD "." checks the current directory
(the logical VFS root) which is correct for both configurations:
- userDirIsRoot=true: current dir is the user's login directory
- userDirIsRoot=false: current dir is "/" (set by the factory CWD)

Using "." rather than "/" avoids a semantic mismatch on non-chroot
servers with userDirIsRoot=true, where "/" would go to the actual
server root instead of the user's login directory.

Add FtpClient.changeDirectory() default method and FTPClientWrapper
implementation to support CWD from FtpFileObject.
@ilang ilang force-pushed the VFS-root-exists-on-disconnect branch from 0a9e786 to 18bf29e Compare April 1, 2026 06:35
@ilang ilang marked this pull request as ready for review April 1, 2026 09:53
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