FakeFtpServer Filesystems

FakeFtpServer provides a simulated server file system, including support for file and directory permissions and owner and group authorization based on Unix. This file system can be populated at startup (or thereafter) with directories and files (including arbitrary content) to be retrieved by an FTP client. Any files sent to the server by an FTP client exist within that file system as well, and can be accessed through the file system API, or can even be subsequently retrieved by an FTP client.

The filesystem abstraction is accessed through the FileSystem interface in the org.mockftpserver.fake.filesystem package. Two implementations of this interface are provided: WindowsFakeFileSystem and UnixFakeFileSystem. They both manage the files and directories in memory, simulating a real file system. You are also free to implement your own FileSystem implementation.

Note that both WindowsFakeFileSystem and UnixFakeFileSystem are virtual file systems, and do not depend on the real operating systems or file systems on which FakeFtpServer is running. In other words, you can configure and run a FakeFtpServer with a WindowsFakeFileSystem on top of a real Unix system, or run a FakeFtpServer with a UnixFakeFileSystem on top of a real Windows system.

See the javadoc for these classes for more information.

WindowsFakeFileSystem

WindowsFakeFileSystem is an implementation of the FileSystem interface that simulates a Microsoft Windows file system. The rules for file and directory names include:

UnixFakeFileSystem

UnixFakeFileSystem is an implementation of the FileSystem interface that simulates a Unix file system. The rules for file and directory names include:

WindowsFakeFileSystem and UnixFakeFileSystem: Common Behavior and Configuration

Both WindowsFakeFileSystem and UnixFakeFileSystem are subclasses of AbstractFakeFileSystem. They manage the files and directories in memory, simulating a real file system.

If the createParentDirectoriesAutomatically property is set to true, then creating a directory or file will automatically create any parent directories (recursively) that do not already exist. If false, then creating a directory or file throws an exception if its parent directory does not exist. This value defaults to true.

The directoryListingFormatter property holds an instance of DirectoryListingFormatter, used by the formatDirectoryListing method to format directory listings in a filesystem-specific manner. This property is initialized by concrete subclasses.

File Permissions, Owners and Groups

Each file or directory entry within a FileSystem has associated owner, group and permissions attributes. All of these attributes are optional. If none are specified for a file or directory, then full access by all users is the default.

If, however, these values are specified for a filesystem entry, then they affect whether a file can be created, read, written or deleted, and whether a directory can be created, listed or deleted.

This approach for access control is conceptually (and somewhat loosely) based on the Unix file system, but don’t expect a comprehensive implementation fully matching Unix’s capabilities.

Permissions

The permissions for a file or directory entry in the filesystem are represented by a 9-character string of the form "rwxrwxrwx", consisting of three “rwx” triples. Each triple indicates the READ (“r”), WRITE (“w”) and EXECUTE (“x”) permissions for a specific set of users. Each position can alternatively contain a “-“ to indicate no READ/WRITE/EXECUTE access, depending on its position.

The first “rwx” triple indicates the READ, WRITE and EXECUTE permissions for the owner of the file. The second triple indicates the permissions for the group associated with the file. The third triple indicates the permissions for the rest of the world.

For example, the permissions string "rwx--xrw-" is interpreted to mean that users have READ/WRITE/EXECUTE access, the group has only EXECUTE, and the world has only READ and WRITE.

There are plenty of good tutorials and references for understanding Unix file permissions, including this one.

The Permissions class represents and encapsulates the read/write/execute permissions for a file or directory. Its constructor takes a 9-character “rwx” String as described above.

The AbstractFileSystemEntry contains a permissions attribute, so that every file and directory in the file system can be assigned a unique set of permissions from a Permissions object. There is also a setPermissionsFromString() convenience setter that allows setting the permissions directly from a String.

FileSystem Access Rules

When Are READ, WRITE or EXECUTE Access Required?

If the permissions are configured for a file or directory within the FileSystem, then those permissions affect whether and how that file/directory can be accessed. Here are the rules for applying permissions for file access:

Operation Required Permissions
Create a new file EXECUTE access to the directory and WRITE access to the directory
Read a file EXECUTE access to the directory and READ access to the file
Write a file EXECUTE access to the directory and WRITE access to the file
Delete a file WRITE access to the directory
Rename a file READ access to the FROM file and WRITE access to the directory
Create a directory WRITE and EXECUTE acccess to the parent directory
List a directory READ acccess to the directory/file
CD to a directory EXECUTE acccess to the directory
Delete a directory WRITE acccess to the parent directory

How Do Owner and Group Affect Access?

Each file and directory in the filesystem (subclass of AbstractFileSystemEntry) contains owner and group attributes. These attributes are optional.

If the owner is configured for a file/directory, AND the permissions are configured as well, then the owner triple from the permissions are applied if and only if the UserAccount for the currently logged in FTP user (client) matches the owner configured for the file/directory.

Similarly, if the group is configured for a file/directory, AND the permissions are configured as well, then the group triple from the permissions are applied if and only if groups configured for the UserAccount for the currently logged in FTP user (client) contain the group configured for the file/directory.

Otherwise, the world triple from the permissions are applied.

Example Code

This example illustrates setting the permissions, owner and group for directories and files within the FakeFtpServer filesystem. In this case, the filesystem is an instance of WindowsFakeFileSystem, but the code would be almost exactly the same for UnixFakeFileSystem as well.

  final String USER1 = "joe";
  final String USER2 = "mary";
  final String GROUP = "dev";
  final String CONTENTS = "abcdef 1234567890";

  FileSystem fileSystem = new WindowsFakeFileSystem();
  DirectoryEntry directoryEntry1 = new DirectoryEntry("c:\\");
  directoryEntry1.setPermissions(new Permissions("rwxrwx---"));
  directoryEntry1.setOwner(USER1);
  directoryEntry1.setGroup(GROUP);

  DirectoryEntry directoryEntry2 = new DirectoryEntry("c:\\data");
  directoryEntry2.setPermissions(Permissions.ALL);
  directoryEntry2.setOwner(USER1);
  directoryEntry2.setGroup(GROUP);

  FileEntry fileEntry1 = new FileEntry("c:\\data\\file1.txt", CONTENTS);
  fileEntry1.setPermissionsFromString("rw-rw-rw-");
  fileEntry1.setOwner(USER1);
  fileEntry1.setGroup(GROUP);

  FileEntry fileEntry2 = new FileEntry("c:\\data\\run.exe");
  fileEntry2.setPermissionsFromString("rwxrwx---");
  fileEntry2.setOwner(USER2);
  fileEntry2.setGroup(GROUP);

  fileSystem.add(directoryEntry1);
  fileSystem.add(directoryEntry2);
  fileSystem.add(fileEntry1);
  fileSystem.add(fileEntry2);

  FakeFtpServer fakeFtpServer = new FakeFtpServer();
  fakeFtpServer.setFileSystem(fileSystem);

Things to note about the above example:

When you want to retrieve and/or verify the contents of the FakeFtpServer filesystem, you can use the FileSystem#getEntry(String path) method, as shown in the following code.

  DirectoryEntry dirEntry = (DirectoryEntry)fileSystem.getEntry("c:/data");

  FileEntry fileEntry = (FileEntry)fileSystem.getEntry("c:/data/file1.txt");

  FileEntry newFileEntry = (FileEntry)fileSystem.getEntry("c:/data/new.txt");
  InputStream inputStream = newFileEntry.createInputStream();
  // read the file contents using inputStream

See the javadoc for FileSystem, FileEntry and DirectoryEntry for more information on the methods available.

Example Using Spring Configuration

See the FakeFtpServer Getting Started - Spring Configuration for an example of how to configure a FakeFtpServer instance and associated filesystem in the Spring Framework.