/jfuse

Java bindings for FUSE using the FFM API

Primary LanguageJavaGNU Lesser General Public License v3.0LGPL-3.0

Build Maintainability Rating Reliability Rating Coverage Quality Gate Status

jFUSE

Zero-Dependency Java bindings for FUSE using JEP 454.

Status

This lib makes use of the Foreign Function & Memory API, requiring at least JDK 22. Older JDK versions are not supported. Please refer to an older version of this lib, if you are interested in using it with an older JDK (with --enable-preview). Older versions will not receive patches or any kind of support, though!

We attempt to support libfuse 3.x on Linux and Windows while also remaining compatible with libfuse 2.x on macOS, leading to some compromises in the API.

For libfuse 3 to ensure that the readdir operation runs in readdirplus mode, you have to add FuseOperations.Operation.INIT to the set returend by FuseOperations::supportedOperations method to the supported operations. An implementation of init is not necessary.

Supported fuse_operations

Not all fuse_operations are supported yet.

Status
getattr
fgetattr use getattr
readlink
getdir use readdir
mknod use create
mkdir
unlink
rmdir
symlink
rename
link
chmod
chown
truncate
ftruncate use truncate
utime use utimens
open
read
write
statfs
flush
release
fsync
setxattr
getxattr
listxattr
removexattr
opendir
readdir
releasedir
fsyncdir
init
destroy
access ✅ (ignored on Windows)
create
lock
utimens
bmap
ioctl
poll
write_buf
read_buf
flock
fallocate
copy_file_range
lseek

Usage

Usage examples can be found under /jfuse-examples/. You basically need to implement FuseOperations and pass it to the Fuse.builder():

<dependency>
	<groupId>org.cryptomator</groupId>
	<artifactId>jfuse</artifactId>
	<version>x.y.z</version>
</dependency>
var builder = Fuse.builder();
var fs = new MyFileSystem(builder.errno());
try (var fuse = builder.build(fs)) {
	fuse.mount("my-awesome-fs", mountPoint);
	// wait as long as the mounted volume is in use
} // closing will force-unmount (previous graceful unmount recommended)

During runtime, you will need to add allow native access from platform-specific implementations via --enable-native-access, e.g.:

java -p path/to/mods \
  -m com.example.mymodule/com.example.mymodule \
  --enable-native-access=org.cryptomator.jfuse.mac \

Supported Platforms

Due to slight differences in memory layout, each platform needs its own implementation. Currently, the following operating systems and architectures are supported:

Linux Mac (macFUSE) Windows (WinFSP)
x86_64 jfuse-linux-amd64 jfuse-mac jfuse-win
arm64 jfuse-linux-aarch64 jfuse-mac jfuse-win

Building

Changing Java implementation

Due to the magic of the Foreign Function & Memory API, you can build all modules on any platform that you can find a JDK for.

Running jextract (on demand)

Each platform has its own module. In rare cases, we need to update jextracted classes.

In most cases this requires you to run the build on the target platform, as you need access to its system-specific header files and (most likely) build tools. See module readme for specific requirements.

In order to run jextract, use the corresponding Maven profile (e.g. -Pjextract-win).

Adding a new platform

Before adding a new module, you might want to change the header search path in one of the existing modules and run jextract. If there is no diff at all, you can most likely add a @SupportedPlatform annotation to its FuseBuilder and check if it works.

Otherwise, you'd need to add a copy of the module. Make sure to open it to the api in the module-info.java, as the api module needs reflective access.

Alternatives

Over the past few years, we relied on jnr-fuse ourselves and can recommend using it for platforms that aren't supported by jFUSE. It has also become the benchmark that we wanted to beat regarding performance and API design.