'Get Linux file descriptor of FileChannel

Is there a way to get the Linux file descriptor for an opened FileChannel?

I need it to call mount.fuse -o fd=... (for implementing FUSE).

As a hacky workaround, I'm doing:

var pid = ProcessHandle.current().pid();
var fd = Files.list(Path.of("/proc/"+pid+"/fd")).count();
var fc = FileChannel.open(path);
System.out.println("file descriptor: " + fd);

Note that two file descriptors appear. One for path and another socket. I'm using the first one. What is the socket for?



Solution 1:[1]

You can use reflection to get the file descriptor from a RandomAccessFile:

long pid = ProcessHandle.current().pid();
long guessedFd = Files.list(Path.of("/proc/"+pid+"/fd")).count();

var file = new java.io.RandomAccessFile(FUSE_DEVICE_PATH.toFile(), "rw");
FileChannel fc = file.getChannel(); // Use FileChannel for fast NIO
var javaFd = file.getFD();
try {
    Field f = FileDescriptor.class.getDeclaredField("fd");
    f.setAccessible(true);
    var trueFd = (int) f.get(javaFd);
    return trueFd;
}
catch (InaccessibleObjectException | NoSuchFieldException | IllegalAccessException e) {
    return guessedFd;
}

On Java 11+ you will the jvm option --add-opens=java.base/java.io=ALL-UNNAMED at startup. Tested on Java 18.


In Java 17 you can use SharedSecrets instead of reflection:

FileDescriptor javaFd = ...
try {
    int trueFd = jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().get(javaFd);
    return trueFd;
}
catch (IllegalAccessError e) {
    return guessedFd;
}

You will need to add --add-exports=java.base/jdk.internal.access=ALL-UNNAMED when compiling and running. See for Maven.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1