fsbind
The fsbind
package implements a JSR203
filesystem that can bind existing JSR203 filesystems into a single unified
namespace. It is similar in design and goals to
PhysicsFS.
Features
- Bind multiple JSR203 filesystems into a single read-only filesystem.
- Written in pure Java 21.
- OSGi ready.
- JPMS ready.
- ISC license.
- High-coverage automated test suite.
Usage
The fsbind
package provides a read-only JSR203 filesystem accessible via an
fsbind
URI scheme. To create a filesystem named example
, call:
final FBFilesystem filesystem =
(FBFilesystem) FileSystems.newFileSystem(URI.create("fsbind:example:/"), null);
The fsbind
filesystem exposes a strict, read-only, in-memory UNIX-like virtual
filesystem consisting of virtual directories and other JSR203 filesystems
mounted into filesystem tree.
For example, to create a directory hierarchy and mount a zip file into it, it's first necessary to create a directory in the filesystem. This directory is virtual in the sense that it is a complete in-memory fabrication and does not actually involve any file I/O:
final var dir = filesystem.getPath("/", "archives", "zip0");
Files.createDirectory(dir);
Then, we can take an existing ZipFS filesystem and mount it into the virtual filesystem:
final var zipfs =
FileSystems.newFileSystem(
URI.create("/path/to/some/zip_file.zip"), Map.of(), null
);
filesystem.mount(
new MountRequest(zipfs.getPath("/"),
dir
);
This will make the contents of /path/to/some/zip_file.zip
accessible at
/archives/zip0
inside the virtual filesystem. It is also possible to expose
only part of the archive by using a non-root path as the first argument to
the MountRequest
. For example, if zip_file.zip
looks like this:
Length Date Time Name
--------- ---------- ----- ----
0 2025-04-10 20:16 x/
10 2025-04-10 20:16 x/a.txt
10 2025-04-10 20:16 x/c.txt
10 2025-04-10 20:16 x/b.txt
0 2025-04-10 20:16 y/
10 2025-04-10 20:16 y/a.txt
0 2025-04-10 20:16 z/
--------- -------
40 7 files
We may choose to only expose the contents of the x
directory:
filesystem.mount(
new MountRequest(zipfs.getPath("/x"),
dir
);
We would then be able to access x/a.txt
by opening /archives/zip0/a.txt
:
Files.readString(dir.resolve("a.txt"));
Capabilities
The fsbind
filesystem is a minimal abstraction over existing JSR203
filesystems and, in most cases, simply delegates operations to those
filesystems directly. Operations that imply write access will typically
result in a FilesystemException
being raised.
The semantics of real filesystems are generally not very well documented, and
filesystems differ immensely across platforms. Furthermore, JSR203 filesystem
implementations have a great deal of freedom in how they're implemented
internally, and a fsbind
filesystem usually consists of a set of filesystems
which may have very different semantics individually placed into a single
unified namespace. Additionally, fsbind
takes a much stricter view with
respect to paths than JSR203 filesystems usually do. You can, if
you are particularly unhinged, mount an fsbind
filesystem inside an fsbind
filesystem (although they are required to be different instances). This is
not recommended.
Therefore, assume the following rules of thumb:
- If you know a directory is a virtual directory that you created, and you haven't mounted anything over the top of it, then you can safely create directories inside it.
- You can delete an empty directory that you created.
- You can only rely on BasicFileAttributes being available. Trying to use anything else will almost certainly result in an exception.
- You cannot create files.
- You cannot modify files (including setting file attributes and/or renaming).
- You cannot delete files.
- You cannot create directories inside mounted filesystems.
- You cannot delete directories inside mounted filesystems.
- You can use the WatchService, but it might not be particularly efficient compared to native services.
- You should use absolute paths as much as possible; treat relative paths as intermediate values and avoid passing them to the filesystem. Most operations require absolute paths and will raise exceptions if presented with relative paths.
Paths
An fsbind
filesystem uses UNIX-like paths. Outside of mounted filesystems,
paths are case-insensitive. All resources, whatever the underlying filesystem,
are accessed with fsbind
paths.
The fsbind
filesystem has no concept of a "current directory" and therefore
the provider requires the use of absolute instead of relative paths in most
instances.
A path component is a string not containing /
and not equal to .
, ..
,
or ...
.
An absolute path is the string "/" followed by a list of path components.
A relative path is a non-empty list of path components.
As examples, the following paths are all valid:
/
/a
/CAS.qterm/CyberAcme Systems/pty0
a/b/c
a
As examples, the following paths are not valid:
/.
/..
/CAS.qterm/.../pty0
.
Watch Service
The fsbind
filesystem provides a simple-minded WatchService
implementation that spawns one virtual thread per registered path, and
checks the modification times on those paths at a configurable rate in order
to deliver watch events.
It's possible to specify the rate that will be used by providing a configuration value in the environment map used to create the filesystem:
FileSystems.newFileSystem(
"fsbind:example:/",
Map.of(
FBFilesystemProvider.environmentWatchServiceDurationKey(),
Duration.ofMillis(250L)
)
);
The provided Duration
value is the amount of time that each virtual thread
will pause between file checks. Smaller values will check more often, publish
events more promptly, and entail more file I/O.
Note that this is unlikely to be anywhere near as performant as the native
WatchService
provided by the JVM for real filesystems.
Releases & Development Snapshots
Releases
You can subscribe to the atom feed to be notified of project releases.
The most recently released version of the package is 0.0.1.
0.0.1 Release (2025-04-16Z)
The compiled artifacts for the release (and all previous releases) are available on Maven Central.
Maven Modules
<dependency> <group>com.io7m.fsbind</group> <artifactId>com.io7m.fsbind.core</artifactId> <version>0.0.1</version> </dependency><dependency> <group>com.io7m.fsbind</group> <artifactId>com.io7m.fsbind.tests</artifactId> <version>0.0.1</version> </dependency>
Development Snapshots
At the time of writing, the current unstable development version of the package is 0.0.2-SNAPSHOT.
Development snapshots may be available in the Central Portal Snapshots repository. Snapshots are published to this repository every time the project is built by the project's continuous integration system, but snapshots do expire after around ninety days and so may or may not be available depending on when a build of the package was last triggered.
Manual
This project does not have any user manuals or other documentation beyond what might be present on the page above.
Sources
This project uses Git to manage source code.
Repository: https://www.github.com/io7m-com/fsbind
$ git clone --recursive https://www.github.com/io7m-com/fsbind
Issues
This project uses GitHub Issues to track issues.
License
Copyright © 2025 Mark Raynsford <code@io7m.com> https://www.io7m.com Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.