Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ public interface FuseConnInfo {
* Capability flags that the kernel supports (read-only)
*
* @return {@code capable} value
* @apiNote Since libFUSE 3.17, this field is deprecated left over for ABI compatibility, use capable_ext
*/
int capable();

Expand All @@ -333,13 +334,17 @@ public interface FuseConnInfo {
* reasonable default values before calling the init() handler.
*
* @return {@code want} value
* @apiNote Since libFUSE 3.17, this field is deprecated left over for ABI compatibility.
* Use want_ext with the helper functions fuse_set_feature_flag() / fuse_unset_feature_flag()
*/
int want();

/**
* Sets the {@link #want()} value.
*
* @param wanted {@code want} value
* @apiNote Since libFUSE 3.17, this field is deprecated left over for ABI compatibility.
* Use want_ext with the helper functions fuse_set_feature_flag() / fuse_unset_feature_flag()
*/
void setWant(int wanted);

Expand Down Expand Up @@ -499,4 +504,87 @@ default int asyncRead() {
default void setAsyncRead(int asyncRead) {
//no-op
}

/**
* Extended capability flags that the kernel supports (read-only)
* This field provides full 64-bit capability support.
* <p>
* If the version of the loaded FUSE library is below 3.17, this method does nothing.
*
* @implSpec The default implementation always returns 0.
* @since libFUSE 3.17
*/
default long capableExt() {
return 0;
}

/**
* Extended capability flags that the filesystem wants to enable.
* This field provides full 64-bit capability support.
* <p>
* Don't set this field directly, but use the helper functions
* fuse_set_feature_flag() / fuse_unset_feature_flag()
* <p>
* If the version of the loaded FUSE library is below 3.17, this method does nothing.
*
* @implSpec The default implementation always returns 0.
* @see #setFeatureFlag(long)
* @see #unsetFeatureFlag(long)
* @since libFUSE 3.17
*/
default long wantExt() {
return 0;
}

/**
* Sets the {@link #wantExt()} value.
* <p>
* Deprecated way of setting the wantExt field. Use {@link #setFeatureFlag(long)} instead.
* If the version of the loaded FUSE library is below 3.17, this method does nothing.
*
* @param wantExt {@code want_ext} value
* @implSpec The default implementation is a no-op.
* @since libFUSE 3.17
*/
default void setWantExt(long wantExt) {
//no-op
}

/**
* Set a feature flag in the want_ext field of fuse_conn_info.
*
* @param flag feature flag to be set
* @return {@code true} if the flag was set, {@code false} if the flag is not supported
* @throws UnsupportedOperationException if the loaded fuse library does not implement the method
* @implSpec The default implementation throws {@link UnsupportedOperationException}
* @since libFUSE 3.17
*/
default boolean setFeatureFlag(long flag) throws UnsupportedOperationException {
throw new UnsupportedOperationException("Loaded library does not implement fuse_set_feature_flag");
}

/**
* Unset a feature flag in the want_ext field of fuse_conn_info.
*
* @param flag feature flag to be unset
* @throws UnsupportedOperationException if the loaded fuse library does not implement the method
* @implSpec The default implementation throws {@link UnsupportedOperationException}
* @since libFUSE 3.17
*/
default void unsetFeatureFlag(long flag) throws UnsupportedOperationException {
throw new UnsupportedOperationException("Loaded library does not implement fuse_unset_feature_flag");
}

/**
* Get the value of a feature flag in the want_ext field of fuse_conn_info.
*
* @param flag feature flag to be checked
* @return {@code true} if the flag is set, {@code false} otherwise
* @throws UnsupportedOperationException if the loaded fuse library does not implement the method
* @implSpec The default implementation throws {@link UnsupportedOperationException}
* @since libFUSE 3.17
*/
default boolean getFeatureFlag(long flag) throws UnsupportedOperationException {
throw new UnsupportedOperationException("Loaded library does not implement fuse_get_feature_flag");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
import org.cryptomator.jfuse.api.FuseConnInfo;
import org.cryptomator.jfuse.linux.aarch64.extr.fuse3.fuse_conn_info;

import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;

record FuseConnInfoImpl(MemorySegment segment) implements FuseConnInfo {
class FuseConnInfoImpl implements FuseConnInfo {

protected final MemorySegment segment;

FuseConnInfoImpl(MemorySegment segment) {
this.segment = segment;
}

//mimic record behaviour
public MemorySegment segment() {
return segment;
}

@Override
public int protoMajor() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.cryptomator.jfuse.linux.aarch64;

import org.cryptomator.jfuse.linux.aarch64.extr.fuse3.fuse_conn_info;

import java.lang.foreign.MemorySegment;

class FuseConnInfoImpl317 extends FuseConnInfoImpl {

FuseConnInfoImpl317(MemorySegment segment) {
super(segment);
}

@Override
public long capableExt() {
return fuse_conn_info.capable_ext(segment);
}

@Override
public long wantExt() {
return fuse_conn_info.want_ext(segment);
}

@Override
public void setWantExt(long wantExt) {
fuse_conn_info.want_ext(segment, wantExt);
}

@Override
public boolean setFeatureFlag(long flag) {
return FuseFunctions.fuse_set_feature_flag(segment, flag);
}

@Override
public void unsetFeatureFlag(long flag) {
FuseFunctions.fuse_unset_feature_flag(segment, flag);
}

@Override
public boolean getFeatureFlag(long flag) {
return FuseFunctions.fuse_get_feature_flag(segment, flag);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,43 @@
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
import java.lang.invoke.MethodHandle;
import java.util.Optional;

import static java.lang.foreign.ValueLayout.ADDRESS;
import static java.lang.foreign.ValueLayout.JAVA_INT;
import static java.lang.foreign.ValueLayout.JAVA_LONG;

/**
* These method references can not be jextract'ed, partly due to jextract not being able to understand {@code #define},
* partly due to slight differences in the FUSE API, which applies a versioning scheme via dlvsym, that Panama's default
* {@link java.lang.foreign.SymbolLookup} doesn't support.
* {@link SymbolLookup} doesn't support.
*/
class FuseFunctions {

// see https://github.com/libfuse/libfuse/blob/fuse-3.12.0/include/fuse_lowlevel.h#L1892-L1923
private static final FunctionDescriptor FUSE_PARSE_CMDLINE = FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS);
//https://github.com/libfuse/libfuse/blob/fuse-3.17.4/lib/fuse_lowlevel.c#L2035
private static final FunctionDescriptor FUSE_SET_FEATURE_FLAG = FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_LONG);
private static final FunctionDescriptor FUSE_UNSET_FEATURE_FLAG = FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG);
private static final FunctionDescriptor FUSE_GET_FEATURE_FLAG = FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_LONG);

private final MethodHandle fuse_parse_cmdline;
private final Optional<MethodHandle> fuse_set_feature_flag;
private final Optional<MethodHandle> fuse_unset_feature_flag;
private final Optional<MethodHandle> fuse_get_feature_flag;

private FuseFunctions() {
var lookup = SymbolLookup.loaderLookup();
var linker = Linker.nativeLinker();
this.fuse_parse_cmdline = lookup.find("fuse_parse_cmdline")
.map(symbol -> linker.downcallHandle(symbol, FUSE_PARSE_CMDLINE))
.orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol fuse_parse_cmdline"));
this.fuse_set_feature_flag = lookup.find("fuse_set_feature_flag")
.map(symbol -> linker.downcallHandle(symbol, FUSE_SET_FEATURE_FLAG));
this.fuse_unset_feature_flag = lookup.find("fuse_unset_feature_flag")
.map(symbol -> linker.downcallHandle(symbol, FUSE_UNSET_FEATURE_FLAG));
this.fuse_get_feature_flag = lookup.find("fuse_get_feature_flag")
.map(symbol -> linker.downcallHandle(symbol, FUSE_GET_FEATURE_FLAG));
}

private static class Holder {
Expand All @@ -41,4 +56,31 @@ public static int fuse_parse_cmdline(MemorySegment args, MemorySegment opts) {
}
}

public static boolean fuse_set_feature_flag(MemorySegment fuse_conn_info, long flag) throws UnsupportedOperationException {
var method = Holder.INSTANCE.fuse_set_feature_flag.orElseThrow(() -> new UnsupportedOperationException("The loaded fuse library does not implement fuse_set_feature_flag"));
try {
return ((int) method.invokeExact(fuse_conn_info, flag)) != 0;
} catch (Throwable e) {
throw new AssertionError("should not reach here", e);
}
}

public static void fuse_unset_feature_flag(MemorySegment fuse_conn_info, long flag) throws UnsupportedOperationException {
var method = Holder.INSTANCE.fuse_unset_feature_flag.orElseThrow(() -> new UnsupportedOperationException("The loaded fuse library does not implement fuse_unset_feature_flag"));
try {
method.invokeExact(fuse_conn_info, flag);
} catch (Throwable e) {
throw new AssertionError("should not reach here", e);
}
}

public static boolean fuse_get_feature_flag(MemorySegment fuse_conn_info, long flag) throws UnsupportedOperationException {
var method = Holder.INSTANCE.fuse_get_feature_flag.orElseThrow(() -> new UnsupportedOperationException("The loaded fuse library does not implement fuse_get_feature_flag"));
try {
return ((int) method.invokeExact(fuse_conn_info, flag)) != 0;
} catch (Throwable e) {
throw new AssertionError("should not reach here", e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,12 @@ protected void bind(FuseOperations.Operation operation) {
@VisibleForTesting
MemorySegment init(MemorySegment conn, MemorySegment cfg) {
var connInfo = new FuseConnInfoImpl(conn);
connInfo.setWant(connInfo.want() | FuseConnInfo.FUSE_CAP_READDIRPLUS);
if (fuse_h.fuse_version() >= 317) {
connInfo = new FuseConnInfoImpl317(conn);
connInfo.setFeatureFlag(FuseConnInfo.FUSE_CAP_READDIRPLUS);
} else {
connInfo.setWant(connInfo.want() | FuseConnInfo.FUSE_CAP_READDIRPLUS);
}
var config = new FuseConfigImpl(cfg);
fuseOperations.init(connInfo, config);
return MemorySegment.NULL;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.cryptomator.jfuse.linux.aarch64;

import org.cryptomator.jfuse.api.FuseConnInfo;
import org.cryptomator.jfuse.linux.aarch64.extr.fuse3.fuse_conn_info;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Stream;

public class FuseConnInfoImpl317Test {

@DisplayName("test long getters")
@ParameterizedTest(name = "{1}")
@MethodSource
void testGetters(SetInMemorySegment setter, GetInConnInfo getter) {
try (var arena = Arena.ofConfined()) {
var segment = fuse_conn_info.allocate(arena);
var connInfo = new FuseConnInfoImpl317(segment);

setter.accept(segment, 42L);

Assertions.assertEquals(42L, getter.apply(connInfo));
}
}

public static Stream<Arguments> testGetters() {
return Stream.of(
Arguments.arguments((SetInMemorySegment) fuse_conn_info::want_ext, Named.of("wantExt()", (GetInConnInfo) FuseConnInfo::wantExt)),
Arguments.arguments((SetInMemorySegment) fuse_conn_info::capable_ext, Named.of("capableExt()", (GetInConnInfo) FuseConnInfo::capableExt))
);
}

interface SetInMemorySegment extends BiConsumer<MemorySegment, Long> {
}

interface GetInConnInfo extends Function<FuseConnInfo, Long> {
}

@DisplayName("test setters")
@ParameterizedTest(name = "{0}")
@MethodSource
void testSetters(SetInConnInfo setter, GetInMemorySegment getter) {
try (var arena = Arena.ofConfined()) {
var segment = fuse_conn_info.allocate(arena);
var connInfo = new FuseConnInfoImpl317(segment);

setter.accept(connInfo, 42L);

Assertions.assertEquals(42L, getter.apply(segment));
}
}

public static Stream<Arguments> testSetters() {
return Stream.of(
Arguments.arguments(Named.of("setWantExt()", (SetInConnInfo) FuseConnInfo::setWantExt), (GetInMemorySegment) fuse_conn_info::want_ext)
);
}

interface SetInConnInfo extends BiConsumer<FuseConnInfo, Long> {
}

interface GetInMemorySegment extends Function<MemorySegment, Long> {
}
}
Loading
Loading