diff --git a/pom.xml b/pom.xml
index 47d7119..b11d2a0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
org.cryptomator
fuse-nio-adapter
- 1.1.3
+ 1.2.0
FUSE-NIO-Adapter
Access resources at a given NIO path via FUSE.
https://github.com/cryptomator/fuse-nio-adapter
diff --git a/src/main/java/org/cryptomator/frontend/fuse/mount/AbstractMount.java b/src/main/java/org/cryptomator/frontend/fuse/mount/AbstractMount.java
index bc3b133..9674fe2 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/mount/AbstractMount.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/mount/AbstractMount.java
@@ -1,33 +1,32 @@
package org.cryptomator.frontend.fuse.mount;
-import com.google.common.collect.ObjectArrays;
+import com.google.common.base.Preconditions;
import org.cryptomator.frontend.fuse.AdapterFactory;
import org.cryptomator.frontend.fuse.FuseNioAdapter;
-import java.io.IOException;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
abstract class AbstractMount implements Mount {
- protected final EnvironmentVariables envVars;
protected final FuseNioAdapter fuseAdapter;
+ protected final EnvironmentVariables envVars;
- public AbstractMount(Path directory, EnvironmentVariables envVars) {
+ public AbstractMount(FuseNioAdapter fuseAdapter, EnvironmentVariables envVars) {
+ Preconditions.checkArgument(fuseAdapter.isMounted());
+ this.fuseAdapter = fuseAdapter;
this.envVars = envVars;
- this.fuseAdapter = AdapterFactory.createReadWriteAdapter(directory);
+
}
- protected void mount(String... additionalFuseOptions) throws CommandFailedException {
+ protected void mount() throws CommandFailedException {
try {
- fuseAdapter.mount(envVars.getMountPath(), false, false, ObjectArrays.concat(getFuseOptions(), additionalFuseOptions, String.class));
+ fuseAdapter.mount(envVars.getMountPoint(), false, false, envVars.getFuseFlags());
} catch (RuntimeException e) {
throw new CommandFailedException(e);
}
}
- protected abstract String[] getFuseOptions();
-
protected abstract ProcessBuilder getRevealCommand();
protected abstract ProcessBuilder getUnmountCommand();
diff --git a/src/main/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariables.java b/src/main/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariables.java
index 0ef2879..92cf061 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariables.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariables.java
@@ -5,13 +5,13 @@
public class EnvironmentVariables {
- private final Path mountPath;
- private final Optional mountName;
+ private final Path mountPoint;
+ private final String[] fuseFlags;
private final Optional revealCommand;
- private EnvironmentVariables(Path mountPath, Optional mountName, Optional revealCommand) {
- this.mountPath = mountPath;
- this.mountName = mountName;
+ private EnvironmentVariables(Path mountPoint, String[] fuseFlags, Optional revealCommand) {
+ this.mountPoint = mountPoint;
+ this.fuseFlags = fuseFlags;
this.revealCommand = revealCommand;
}
@@ -19,12 +19,12 @@ public static EnvironmentVariablesBuilder create() {
return new EnvironmentVariablesBuilder();
}
- public Path getMountPath() {
- return mountPath;
+ public Path getMountPoint() {
+ return mountPoint;
}
- public Optional getMountName() {
- return mountName;
+ public String[] getFuseFlags() {
+ return fuseFlags;
}
public Optional getRevealCommand() {
@@ -33,17 +33,17 @@ public Optional getRevealCommand() {
public static class EnvironmentVariablesBuilder {
- private Path mountPath = null;
- private Optional mountName = Optional.empty();
+ private Path mountPoint = null;
+ private String[] fuseFlags;
private Optional revealCommand = Optional.empty();
- public EnvironmentVariablesBuilder withMountPath(Path mountPath) {
- this.mountPath = mountPath;
+ public EnvironmentVariablesBuilder withMountPoint(Path mountPoint) {
+ this.mountPoint = mountPoint;
return this;
}
- public EnvironmentVariablesBuilder withMountName(String mountName) {
- this.mountName = Optional.ofNullable(mountName);
+ public EnvironmentVariablesBuilder withFlags(String[] fuseFlags) {
+ this.fuseFlags = fuseFlags;
return this;
}
@@ -53,7 +53,7 @@ public EnvironmentVariablesBuilder withRevealCommand(String revealCommand) {
}
public EnvironmentVariables build() {
- return new EnvironmentVariables(mountPath, mountName, revealCommand);
+ return new EnvironmentVariables(mountPoint, fuseFlags, revealCommand);
}
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/mount/LinuxMounter.java b/src/main/java/org/cryptomator/frontend/fuse/mount/LinuxMounter.java
index 0f178b7..57215ba 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/mount/LinuxMounter.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/mount/LinuxMounter.java
@@ -1,23 +1,42 @@
package org.cryptomator.frontend.fuse.mount;
import com.google.common.collect.ObjectArrays;
+import org.cryptomator.frontend.fuse.AdapterFactory;
+import org.cryptomator.frontend.fuse.FuseNioAdapter;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
class LinuxMounter implements Mounter {
private static final boolean IS_LINUX = System.getProperty("os.name").toLowerCase().contains("linux");
+ private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));
@Override
- public Mount mount(Path directory, EnvironmentVariables envVars, String... additionalMountParams) throws CommandFailedException {
- LinuxMount mount = new LinuxMount(directory, envVars);
- mount.mount(additionalMountParams);
- return mount;
+ public synchronized Mount mount(Path directory, EnvironmentVariables envVars) throws CommandFailedException {
+ FuseNioAdapter fuseAdapter = AdapterFactory.createReadWriteAdapter(directory);
+ try {
+ fuseAdapter.mount(envVars.getMountPoint(), false, false, envVars.getFuseFlags());
+ } catch (RuntimeException e) {
+ throw new CommandFailedException(e);
+ }
+ return new LinuxMount(fuseAdapter, envVars);
+ }
+
+ @Override
+ public String[] defaultMountFlags() {
+ try {
+ return new String[]{
+ "-ouid=" + Files.getAttribute(USER_HOME, "unix:uid"),
+ "-ogid=" + Files.getAttribute(USER_HOME, "unix:gid"),
+ "-oauto_unmount"
+ };
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
@Override
@@ -27,16 +46,15 @@ public boolean isApplicable() {
private static class LinuxMount extends AbstractMount {
- private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));
private static final String DEFAULT_REVEALCOMMAND_LINUX = "xdg-open";
private final ProcessBuilder revealCommand;
private final ProcessBuilder unmountCommand;
private final ProcessBuilder unmountForcedCommand;
- private LinuxMount(Path directory, EnvironmentVariables envVars) {
- super(directory, envVars);
- Path mountPoint = envVars.getMountPath();
+ private LinuxMount(FuseNioAdapter fuseAdapter, EnvironmentVariables envVars) {
+ super(fuseAdapter, envVars);
+ Path mountPoint = envVars.getMountPoint();
String[] command = envVars.getRevealCommand().orElse(DEFAULT_REVEALCOMMAND_LINUX).split("\\s+");
this.revealCommand = new ProcessBuilder(ObjectArrays.concat(command, mountPoint.toString()));
this.unmountCommand = new ProcessBuilder("fusermount", "-u", "--", mountPoint.getFileName().toString());
@@ -45,21 +63,6 @@ private LinuxMount(Path directory, EnvironmentVariables envVars) {
this.unmountForcedCommand.directory(mountPoint.getParent().toFile());
}
- @Override
- protected String[] getFuseOptions() {
- ArrayList mountOptions = new ArrayList<>(8);
- mountOptions.add(("-oatomic_o_trunc"));
- try {
- mountOptions.add("-ouid=" + Files.getAttribute(USER_HOME, "unix:uid"));
- mountOptions.add("-ogid=" + Files.getAttribute(USER_HOME, "unix:gid"));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- mountOptions.add("-oauto_unmount");
- mountOptions.add("-ofsname=CryptoFs");
- return mountOptions.toArray(new String[mountOptions.size()]);
- }
-
@Override
public ProcessBuilder getRevealCommand() {
return revealCommand;
diff --git a/src/main/java/org/cryptomator/frontend/fuse/mount/MacMounter.java b/src/main/java/org/cryptomator/frontend/fuse/mount/MacMounter.java
index ab9d16b..06d84c3 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/mount/MacMounter.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/mount/MacMounter.java
@@ -1,12 +1,20 @@
package org.cryptomator.frontend.fuse.mount;
+import org.cryptomator.frontend.fuse.AdapterFactory;
+import org.cryptomator.frontend.fuse.FuseNioAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
-import javax.xml.stream.FactoryConfigurationError;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathException;
+import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
@@ -14,22 +22,45 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
-import java.util.ArrayList;
import java.util.Arrays;
class MacMounter implements Mounter {
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 Path OSXFUSE_VERSIONFILE_LOCATION = Paths.get("/Library/Filesystems/osxfuse.fs/Contents/version.plist");
- private static final String OSXFUSE_XML_VERSION_TEXT = "CFBundleShortVersionString";
+ 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]";
@Override
- public Mount mount(Path directory, EnvironmentVariables envVars, String... additionalMountParams) throws CommandFailedException {
- MacMount mount = new MacMount(directory, envVars);
- mount.mount(additionalMountParams);
- return mount;
+ public synchronized Mount mount(Path directory, EnvironmentVariables envVars) throws CommandFailedException {
+ FuseNioAdapter fuseAdapter = AdapterFactory.createReadWriteAdapter(directory);
+ try {
+ fuseAdapter.mount(envVars.getMountPoint(), false, false, envVars.getFuseFlags());
+ } catch (RuntimeException e) {
+ throw new CommandFailedException(e);
+ }
+ return new MacMount(fuseAdapter, envVars);
+ }
+
+ @Override
+ public String[] defaultMountFlags() {
+ // see: https://github.com/osxfuse/osxfuse/wiki/Mount-options
+ try {
+ return new String[]{
+ "-ouid=" + Files.getAttribute(USER_HOME, "unix:uid"),
+ "-ogid=" + Files.getAttribute(USER_HOME, "unix:gid"),
+ "-oatomic_o_trunc",
+ "-oauto_xattr",
+ "-oauto_cache",
+ "-omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC", // show files names in Unicode NFD encoding
+ "-onoappledouble", // vastly impacts performance for some reason...
+ "-odefault_permissions" // let the kernel assume permissions based on file attributes etc
+ };
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
/**
@@ -43,7 +74,7 @@ public boolean isApplicable() {
public boolean installedVersionSupported() {
String versionString = getVersionString();
if (versionString == null) {
- LOG.error("Did not find {} in document {}.", OSXFUSE_XML_VERSION_TEXT, OSXFUSE_VERSIONFILE_LOCATION);
+ LOG.error("Did not find {} in document {}.", OSXFUSE_VERSIONFILE_XPATH, OSXFUSE_VERSIONFILE_LOCATION);
return false;
}
@@ -64,38 +95,40 @@ public boolean installedVersionSupported() {
}
+ /**
+ * @return Value for {@value OSXFUSE_VERSIONFILE_XPATH} in {@value OSXFUSE_VERSIONFILE_LOCATION} or null
if this value is not present.
+ */
private String getVersionString() {
- String version = null;
- try (InputStream in = Files.newInputStream(OSXFUSE_VERSIONFILE_LOCATION, StandardOpenOption.READ)) {
- XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(in);
- while (reader.hasNext()) {
- reader.next();
- if (reader.getEventType() == XMLStreamReader.CHARACTERS && OSXFUSE_XML_VERSION_TEXT.equalsIgnoreCase(reader.getText())) {
- reader.next();
- reader.next();
- reader.next();
- version = reader.getElementText();
- }
+ Path plistFile = Paths.get(OSXFUSE_VERSIONFILE_LOCATION);
+ DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ try (InputStream in = Files.newInputStream(plistFile, StandardOpenOption.READ)) {
+ Document doc = domFactory.newDocumentBuilder().parse(in);
+ NodeList nodeList = (NodeList) xPath.compile(OSXFUSE_VERSIONFILE_XPATH).evaluate(doc, XPathConstants.NODESET);
+ Node node = nodeList.item(0);
+ if (node == null) {
+ return null; // not found
+ } else {
+ return node.getTextContent();
}
- } catch (XMLStreamException | FactoryConfigurationError e) {
+ } catch (ParserConfigurationException | SAXException | XPathException e) {
LOG.error("Could not parse file {} to detect version of OSXFUSE.", OSXFUSE_VERSIONFILE_LOCATION);
- } catch (IOException e1) {
+ return null;
+ } catch (IOException e) {
LOG.error("Could not read file {} to detect version of OSXFUSE.", OSXFUSE_VERSIONFILE_LOCATION);
+ return null;
}
- return version;
}
private static class MacMount extends AbstractMount {
- private static final Path USER_HOME = Paths.get(System.getProperty("user.home"));
-
private final ProcessBuilder revealCommand;
private final ProcessBuilder unmountCommand;
private final ProcessBuilder unmountForcedCommand;
- private MacMount(Path directory, EnvironmentVariables envVars) {
- super(directory, envVars);
- Path mountPoint = envVars.getMountPath();
+ private MacMount(FuseNioAdapter fuseAdapter, EnvironmentVariables envVars) {
+ super(fuseAdapter, envVars);
+ Path mountPoint = envVars.getMountPoint();
this.revealCommand = new ProcessBuilder("open", ".");
this.revealCommand.directory(mountPoint.toFile());
this.unmountCommand = new ProcessBuilder("umount", "--", mountPoint.getFileName().toString());
@@ -104,26 +137,6 @@ private MacMount(Path directory, EnvironmentVariables envVars) {
this.unmountForcedCommand.directory(mountPoint.getParent().toFile());
}
- @Override
- protected String[] getFuseOptions() {
- // see: https://github.com/osxfuse/osxfuse/wiki/Mount-options
- ArrayList mountOptions = new ArrayList<>();
- try {
- mountOptions.add("-ouid=" + Files.getAttribute(USER_HOME, "unix:uid"));
- mountOptions.add("-ogid=" + Files.getAttribute(USER_HOME, "unix:gid"));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- mountOptions.add("-oatomic_o_trunc");
- mountOptions.add("-ovolname=" + envVars.getMountName().orElse("vault"));
- mountOptions.add("-oauto_xattr");
- mountOptions.add("-oauto_cache");
- mountOptions.add("-omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC"); // show files names in Unicode NFD encoding
- mountOptions.add("-onoappledouble"); // vastly impacts performance for some reason...
- mountOptions.add("-odefault_permissions"); // let the kernel assume permissions based on file attributes etc
- return mountOptions.toArray(new String[mountOptions.size()]);
- }
-
@Override
public ProcessBuilder getRevealCommand() {
return revealCommand;
diff --git a/src/main/java/org/cryptomator/frontend/fuse/mount/Mounter.java b/src/main/java/org/cryptomator/frontend/fuse/mount/Mounter.java
index 125a594..2db5d6c 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/mount/Mounter.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/mount/Mounter.java
@@ -4,7 +4,9 @@
public interface Mounter {
- Mount mount(Path directory, EnvironmentVariables envVars, String... additionalMountParams) throws CommandFailedException;
+ Mount mount(Path directory, EnvironmentVariables envVars) throws CommandFailedException;
+
+ String[] defaultMountFlags();
boolean isApplicable();
diff --git a/src/test/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariablesTest.java b/src/test/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariablesTest.java
index 2e1e871..6a8a102 100644
--- a/src/test/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariablesTest.java
+++ b/src/test/java/org/cryptomator/frontend/fuse/mount/EnvironmentVariablesTest.java
@@ -9,11 +9,13 @@
public class EnvironmentVariablesTest {
@Test
- public void testEnvironmentVariablesBuilder(){
- Path mountPath = Paths.get("/home/testuser/mnt");
- String mountName = "coolStuff";
- EnvironmentVariables envVars = EnvironmentVariables.create().withMountName(mountName).withMountPath(mountPath).build();
- Assertions.assertEquals(mountName, envVars.getMountName().get());
- Assertions.assertEquals(mountPath, envVars.getMountPath());
+ public void testEnvironmentVariablesBuilder() {
+ String[] flags = new String[]{"--test", "--debug"};
+ Path mountPoint = Paths.get("/home/testuser/mnt");
+
+ EnvironmentVariables envVars = EnvironmentVariables.create().withFlags(flags).withMountPoint(mountPoint).build();
+
+ Assertions.assertEquals(flags, envVars.getFuseFlags());
+ Assertions.assertEquals(mountPoint, envVars.getMountPoint());
}
}
diff --git a/src/test/java/org/cryptomator/frontend/fuse/mount/LinuxEnvironmentTest.java b/src/test/java/org/cryptomator/frontend/fuse/mount/LinuxEnvironmentTest.java
index 5afbb6e..8b64b1b 100644
--- a/src/test/java/org/cryptomator/frontend/fuse/mount/LinuxEnvironmentTest.java
+++ b/src/test/java/org/cryptomator/frontend/fuse/mount/LinuxEnvironmentTest.java
@@ -13,13 +13,14 @@ public class LinuxEnvironmentTest {
public static void main(String[] args) throws IOException {
if (IS_LINUX) {
Path mountPoint = Files.createTempDirectory("fuse-mount");
+ Mounter mounter = FuseMountFactory.getMounter();
EnvironmentVariables envVars = EnvironmentVariables.create()
- .withMountName("yolo")
- .withMountPath(mountPoint)
+ .withFlags(mounter.defaultMountFlags())
+ .withMountPoint(mountPoint)
.withRevealCommand("nautilus")
.build();
Path tmp = Paths.get("/tmp");
- try (Mount mnt = FuseMountFactory.getMounter().mount(tmp, envVars)) {
+ try (Mount mnt = mounter.mount(tmp, envVars)) {
mnt.revealInFileManager();
System.out.println("Wait for it...");
System.in.read();
diff --git a/src/test/java/org/cryptomator/frontend/fuse/mount/MirroringFuseMountTest.java b/src/test/java/org/cryptomator/frontend/fuse/mount/MirroringFuseMountTest.java
index adda3d9..7e46457 100644
--- a/src/test/java/org/cryptomator/frontend/fuse/mount/MirroringFuseMountTest.java
+++ b/src/test/java/org/cryptomator/frontend/fuse/mount/MirroringFuseMountTest.java
@@ -127,8 +127,8 @@ public static void main(String args[]) throws IOException {
private static void mount(Path pathToMirror, Path mountPoint) {
Mounter mounter = FuseMountFactory.getMounter();
EnvironmentVariables envVars = EnvironmentVariables.create()
- .withMountName("FuseMirror")
- .withMountPath(mountPoint)
+ .withFlags(mounter.defaultMountFlags())
+ .withMountPoint(mountPoint)
.build();
try (Mount mnt = mounter.mount(pathToMirror, envVars)) {
LOG.info("Mounted successfully. Enter anything to stop the server...");