Skip to content

Commit

Permalink
Merge branch 'release/1.3.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
infeo committed Aug 16, 2021
2 parents 1e06aeb + 7b69b7f commit c53318d
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 44 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Provides directory contents specified by a `java.nio.file.Path` via a FUSE files

Uses [jnr-fuse](https://github.com/SerCeMan/jnr-fuse), i.e. you need to install the specified fuse drivers for your OS.

## Configuration Parameters
The following system properties are used:
* `org.cryptomator.frontend.fuse.mountTimeOut` - The mount timeout threshold in milliseonds. If the mounting operation exceeds it, the mounting is aborted.

## License

This project is dual-licensed under the AGPLv3 for FOSS projects as well as a commercial license for independent software vendors and resellers. If you want to use this library in applications, that are *not* licensed under the AGPL, feel free to contact our [support team](https://cryptomator.org/help/).
27 changes: 21 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>fuse-nio-adapter</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
<name>FUSE-NIO-Adapter</name>
<description>Access resources at a given NIO path via FUSE.</description>
<url>https://github.com/cryptomator/fuse-nio-adapter</url>
Expand All @@ -20,13 +20,15 @@

<!-- dependencies -->
<jnrfuse.version>0.5.5</jnrfuse.version>
<dagger.version>2.31.2</dagger.version>
<guava.version>30.1-jre</guava.version>
<slf4j.version>1.7.30</slf4j.version>
<!-- TODO: bump dependency once this project has a proper module.info -->
<dagger.version>2.36</dagger.version>
<guava.version>30.1.1-jre</guava.version>
<slf4j.version>1.7.32</slf4j.version>

<!-- test dependencies -->
<junit.jupiter.version>5.7.1</junit.jupiter.version>
<mockito.version>3.7.7</mockito.version>
<junit.jupiter.version>5.7.2</junit.jupiter.version>
<mockito.version>3.11.2</mockito.version>
<!--TODO: bump dependency once 1.6.0 is officially released -->
<cryptofs.version>1.9.14</cryptofs.version>
</properties>

Expand Down Expand Up @@ -116,6 +118,19 @@
</dependency>
</dependencies>

<repositories>
<repository>
<id>maven-central</id>
<name>Maven Central Repo</name>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
<repository>
<id>google-maven</id>
<name>Google Maven Repo</name>
<url>https://maven.google.com</url>
</repository>
</repositories>

<build>
<plugins>
<plugin>
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/org/cryptomator/frontend/fuse/VersionCompare.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.cryptomator.frontend.fuse;

/**
* from: https://www.baeldung.com/java-comparing-versions#customSolution
*/
public class VersionCompare {

public static int compareVersions(String version1, String version2) {
int comparisonResult = 0;

String[] version1Splits = version1.split("\\.");
String[] version2Splits = version2.split("\\.");
int maxLengthOfVersionSplits = Math.max(version1Splits.length, version2Splits.length);

for (int i = 0; i < maxLengthOfVersionSplits; i++){
Integer v1 = i < version1Splits.length ? Integer.parseInt(version1Splits[i]) : 0;
Integer v2 = i < version2Splits.length ? Integer.parseInt(version2Splits[i]) : 0;
int compare = v1.compareTo(v2);
if (compare != 0) {
comparisonResult = compare;
break;
}
}
return comparisonResult;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public abstract class AbstractMounter implements Mounter {

private static final int MOUNT_TIMEOUT_MILLIS = 10000;
private static final int MOUNT_TIMEOUT_MILLIS = Integer.getInteger("org.cryptomator.frontend.fuse.mountTimeOut",10000);
private static final AtomicInteger MOUNT_COUNTER = new AtomicInteger(0);

@Override
Expand Down
49 changes: 14 additions & 35 deletions src/main/java/org/cryptomator/frontend/fuse/mount/MacMounter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.cryptomator.frontend.fuse.FileNameTranscoder;
import org.cryptomator.frontend.fuse.FuseNioAdapter;
import org.cryptomator.frontend.fuse.VersionCompare;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
Expand All @@ -26,17 +27,16 @@
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.Normalizer;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

class MacMounter extends AbstractMounter {

private static final Logger LOG = LoggerFactory.getLogger(MacMounter.class);
private static final boolean IS_MAC = System.getProperty("os.name").toLowerCase().contains("mac");
private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));
private static final int[] OSXFUSE_MINIMUM_SUPPORTED_VERSION = new int[]{3, 8, 2};
private static final String OSXFUSE_VERSIONFILE_LOCATION = "/Library/Filesystems/osxfuse.fs/Contents/version.plist";
private static final String OSXFUSE_VERSIONFILE_XPATH = "/plist/dict/key[.='CFBundleShortVersionString']/following-sibling::string[1]";
private static final String MACFUSE_MINIMUM_SUPPORTED_VERSION = "4.0.4";
private static final String MACFUSE_VERSIONFILE_LOCATION = "/Library/Filesystems/macfuse.fs/Contents/version.plist";
private static final String MACFUSE_VERSIONFILE_XPATH = "/plist/dict/key[.='CFBundleShortVersionString']/following-sibling::string[1]";
private static final String PLIST_DTD_URL = "http://www.apple.com/DTDs/PropertyList-1.0.dtd";

@Override
Expand All @@ -62,13 +62,9 @@ public FileNameTranscoder defaultFileNameTranscoder() {
return FileNameTranscoder.transcoder().withFuseNormalization(Normalizer.Form.NFD);
}

/**
* @return <code>true</code> if on OS X and osxfuse with a higher version than the minimum supported one is installed.
*/
@Override
public boolean isApplicable() {
return IS_MAC && Files.exists(Paths.get("/usr/local/lib/libosxfuse.2.dylib")); //
// && installedVersionSupported(); // FIXME: #52
return IS_MAC && Files.exists(Paths.get("/usr/local/lib/libosxfuse.2.dylib")) && installedVersionSupported();
}

@Override
Expand All @@ -77,52 +73,35 @@ protected Mount createMountObject(FuseNioAdapter fuseNioAdapter, EnvironmentVari
}

public boolean installedVersionSupported() {
String versionString = getVersionString();
if (versionString == null) {
LOG.error("Did not find {} in document {}.", OSXFUSE_VERSIONFILE_XPATH, OSXFUSE_VERSIONFILE_LOCATION);
String installedVersion = getInstalledVersion(MACFUSE_VERSIONFILE_LOCATION, MACFUSE_VERSIONFILE_XPATH);
if (installedVersion == null) {
return false;
}

Integer[] parsedVersion = Arrays.stream(versionString.split("\\.")).map(s -> Integer.valueOf(s)).toArray(Integer[]::new);
for (int i = 0; i < OSXFUSE_MINIMUM_SUPPORTED_VERSION.length && i < parsedVersion.length; i++) {
if (parsedVersion[i] < OSXFUSE_MINIMUM_SUPPORTED_VERSION[i]) {
return false;
} else if (parsedVersion[i] > OSXFUSE_MINIMUM_SUPPORTED_VERSION[i]) {
return true;
}
}

if (OSXFUSE_MINIMUM_SUPPORTED_VERSION.length <= parsedVersion.length) {
return true;
} else {
return false;
return VersionCompare.compareVersions(installedVersion, MACFUSE_MINIMUM_SUPPORTED_VERSION) >= 0;
}
}


/**
* @return Value for {@value OSXFUSE_VERSIONFILE_XPATH} in {@value OSXFUSE_VERSIONFILE_LOCATION} or <code>null</code> if this value is not present.
*/
private String getVersionString() {
Path plistFile = Paths.get(OSXFUSE_VERSIONFILE_LOCATION);
private String getInstalledVersion(String plistFileLocation, String versionXPath) {
Path plistFile = Paths.get(plistFileLocation);
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
XPath xPath = XPathFactory.newInstance().newXPath();
try (InputStream in = Files.newInputStream(plistFile, StandardOpenOption.READ)) {
DocumentBuilder docBuilder = domFactory.newDocumentBuilder();
docBuilder.setEntityResolver(this::resolveEntity);
Document doc = docBuilder.parse(in);
NodeList nodeList = (NodeList) xPath.compile(OSXFUSE_VERSIONFILE_XPATH).evaluate(doc, XPathConstants.NODESET);
NodeList nodeList = (NodeList) xPath.compile(versionXPath).evaluate(doc, XPathConstants.NODESET);
Node node = nodeList.item(0);
if (node == null) {
LOG.error("Did not find {} in document {}.", versionXPath, plistFileLocation);
return null; // not found
} else {
return node.getTextContent();
}
} catch (ParserConfigurationException | SAXException | XPathException e) {
LOG.error("Could not parse " + OSXFUSE_VERSIONFILE_LOCATION + " to detect version of OSXFUSE.", e);
LOG.error("Could not parse " + plistFileLocation + " to detect version of macFUSE.", e);
return null;
} catch (IOException e) {
LOG.error("Could not read " + OSXFUSE_VERSIONFILE_LOCATION + " to detect version of OSXFUSE.", e);
LOG.error("Could not read " + plistFileLocation + " to detect version of macFUSE.", e);
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
import jnr.ffi.Runtime;
import jnr.ffi.provider.jffi.ByteBufferMemoryIO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.io.TempDir;
import org.slf4j.impl.SimpleLogger;
import ru.serce.jnrfuse.FuseException;
import ru.serce.jnrfuse.struct.FuseFileInfo;
import ru.serce.jnrfuse.utils.WinPathUtils;

import java.nio.ByteBuffer;
import java.nio.file.Path;
Expand All @@ -31,6 +34,7 @@ public class AccessPatternIntegrationTest {

@BeforeEach
void setup(@TempDir Path tmpDir) {
Assumptions.assumeTrue(onWindowsWinFspInstalled(), "WinFSP seem not to be installed.");
adapter = AdapterFactory.createReadWriteAdapter(tmpDir);
}

Expand Down Expand Up @@ -74,6 +78,15 @@ void testAppleAutosaveAccessPattern() {
Assertions.assertArrayEquals("asdasd".getBytes(US_ASCII), Arrays.copyOf(buf.array(), numRead));
}

private boolean onWindowsWinFspInstalled() {
try {
return !WinPathUtils.getWinFspPath().isBlank();
} catch (FuseException e) {
//TODO: log?
return false;
}
}

@Test
@DisplayName("create, move and delete symlinks")
@DisabledOnOs(OS.WINDOWS) // Symlinks require either admin privileges or enabled developer mode on windows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.nio.file.Paths;
import java.util.Scanner;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

public class MirroringFuseMountTest {
Expand Down Expand Up @@ -184,8 +186,9 @@ private static void mount(Path pathToMirror, Path mountPoint) {
.withMountPoint(mountPoint)
.withFileNameTranscoder(mounter.defaultFileNameTranscoder())
.build();
Consumer<Throwable> onFuseMainExit = throwable -> System.out.println("The fuse main loop exited.");
try (Mount mnt = mounter.mount(pathToMirror, envVars, onFuseMainExit)) {
CountDownLatch barrier = new CountDownLatch(1);
Consumer<Throwable> onFuseMainExit = throwable -> barrier.countDown();
try (Mount mnt = mounter.mount(pathToMirror, envVars, onFuseMainExit, false)) {
LOG.info("Mounted successfully. Enter anything to stop the server...");
try {
mnt.reveal(new AwtFrameworkRevealer());
Expand All @@ -204,5 +207,16 @@ private static void mount(Path pathToMirror, Path mountPoint) {
} catch (IOException | FuseMountException e) {
LOG.error("Mount failed", e);
}

try {
if (!barrier.await(5000, TimeUnit.MILLISECONDS)) {
LOG.error("Wait on onFuseExit action to finish exceeded timeout. Exiting ...");
} else {
LOG.info("onExit action executed.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Main thread interrupted. Exiting without waiting for onFuseExit action");
}
}
}

0 comments on commit c53318d

Please sign in to comment.