Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Mount Provider #12

Merged
merged 11 commits into from
Oct 27, 2022
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.3.1</version>
<version>4.8.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import org.cryptomator.integrations.mount.MountProvider;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
Expand All @@ -12,11 +13,13 @@
exports org.cryptomator.integrations.autostart;
exports org.cryptomator.integrations.common;
exports org.cryptomator.integrations.keychain;
exports org.cryptomator.integrations.mount;
exports org.cryptomator.integrations.tray;
exports org.cryptomator.integrations.uiappearance;

uses AutoStartProvider;
uses KeychainAccessProvider;
uses MountProvider;
uses TrayIntegrationProvider;
uses TrayMenuController;
uses UiAppearanceProvider;
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/org/cryptomator/integrations/mount/Mount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.cryptomator.integrations.mount;

import java.nio.file.Path;

/**
* Handle to control the lifecycle of a mounted file system.
* <p>
* Created by {@link MountBuilder}
*/
public interface Mount extends AutoCloseable {

/**
* Returns the absolute OS path, where this mount can be accessed.
*
* @return Absolute path to the mountpoint.
*/
Path getMountpoint();

/**
* Unmounts the mounted Volume.
* <p>
* If possible, attempt a graceful unmount.
*
* @throws UnmountFailedException If the unmount was not successful.
* @see #unmountForced()
*/
void unmout() throws UnmountFailedException;

/**
* If supported, force-unmount the volume.
*
* @throws UnmountFailedException If the unmount was not successful.
* @throws UnsupportedOperationException If {@link MountFeature#UNMOUNT_FORCED} is not supported
*/
default void unmountForced() throws UnmountFailedException {
throw new UnsupportedOperationException();
}

default void close() throws UnmountFailedException {
unmout();
}


}
73 changes: 73 additions & 0 deletions src/main/java/org/cryptomator/integrations/mount/MountBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.cryptomator.integrations.mount;

import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Range;

import java.nio.file.Path;

/**
* Builder to mount a filesystem.
* <p>
* The setter may attempt to validate the input, but {@link #mount()} may still fail due to missing or invalid (combination of) options.
* This holds especially for {@link MountBuilder#setMountFlags(String)};
*/
public interface MountBuilder {

/**
* Sets the mount point.
*
* @param mountPoint Where to mount the volume
* @return <code>this</code>
* @see MountProvider#getDefaultMountPoint(String)
*/
@Contract("_ -> this")
MountBuilder setMountpoint(Path mountPoint);

/**
* Sets mount flags.
*
* @param mountFlags Mount flags
* @return <code>this</code>
* @throws UnsupportedOperationException If {@link MountFeature#MOUNT_FLAGS} is not supported
* @see MountProvider#getDefaultMountFlags(String)
*/
@Contract("_ -> this")
default MountBuilder setMountFlags(String mountFlags) {
throw new UnsupportedOperationException();
}

/**
* Instructs the mount to be read-only.
*
* @param mountReadOnly Whether to mount read-only.
* @return <code>this</code>
* @throws UnsupportedOperationException If {@link MountFeature#READ_ONLY} is not supported
*/
@Contract("_ -> this")
default MountBuilder setReadOnly(boolean mountReadOnly) {
throw new UnsupportedOperationException();
}

/**
* Use the given TCP port.
*
* @param port fixed TCP port or 0 to use a system-assigned port
* @return <code>this</code>
* @throws UnsupportedOperationException If {@link MountFeature#PORT} is not supported
*/
@Contract("_ -> this")
default MountBuilder setPort(@Range(from = 0, to = Short.MAX_VALUE) int port) {
throw new UnsupportedOperationException();
}


/**
* Mounts the file system.
*
* @return A mount handle
* @throws MountFailedException If mounting failed
*/
@Contract(" -> new")
Mount mount() throws MountFailedException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.cryptomator.integrations.mount;

public class MountFailedException extends Exception {

public MountFailedException(String msg) {
super(msg);
}

public MountFailedException(Exception cause) {
super(cause);
}

public MountFailedException(String msg, Exception cause) {
super(msg, cause);
}
}
64 changes: 64 additions & 0 deletions src/main/java/org/cryptomator/integrations/mount/MountFeature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.cryptomator.integrations.mount;

/**
* Describes what aspects of the mount implementation can or should be used.
* <p>
* This may be used to show or hide different configuration options depending on the chosen mount provider.
*/
public enum MountFeature {
/**
* The provider supports {@link MountProvider#getDefaultMountFlags(String)}
* and the builder requires {@link MountBuilder#setMountFlags(String)}.
*/
MOUNT_FLAGS,

/**
* With the exception of a provider-supplied default mount point, the mount point must be an existing dir.
* <p>
* This option is mutually exclusive with {@link #MOUNT_WITHIN_EXISTING_PARENT}.
*
* @see #DEFAULT_MOUNT_POINT
*/
MOUNT_TO_EXISTING_DIR,

/**
* With the exception of a provider-supplied default mount point, the mount point must be a non-existing
* child within an existing parent.
* <p>
* This option is mutually exclusive with {@link #MOUNT_TO_EXISTING_DIR}.
*
* @see #DEFAULT_MOUNT_POINT
*/
MOUNT_WITHIN_EXISTING_PARENT,

/**
* The mount point may be a drive letter.
*
* @see #MOUNT_TO_EXISTING_DIR
* @see #MOUNT_WITHIN_EXISTING_PARENT
*/
MOUNT_AS_DRIVE_LETTER,

/**
* The provider supports suggesting a default mount point via {@link MountProvider#getDefaultMountPoint(String)}.
* <p>
* The default mount point is guaranteed to be supported by the mount builder, regardless of its normal restrictions.
*/
DEFAULT_MOUNT_POINT,

/**
* The builder supports {@link MountBuilder#setReadOnly(boolean)}
*/
READ_ONLY,

/**
* The mount supports {@link Mount#unmountForced()}.
*/
UNMOUNT_FORCED,

/**
* The provider supports {@link MountProvider#getDefaultPort()}
* and the builder requires {@link MountBuilder#setPort(int)}.
*/
PORT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.cryptomator.integrations.mount;

import org.cryptomator.integrations.common.IntegrationsLoader;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;

import java.nio.file.Path;
import java.util.Set;
import java.util.stream.Stream;

/**
* A mechanism to mount a file system.
*
* @since 1.2.0
*/
public interface MountProvider {

/**
* Loads all supported mount providers.
*
* @return Stream of supported MountProviders (may be empty)
*/
static Stream<MountProvider> get() {
return IntegrationsLoader.loadAll(MountProvider.class).filter(MountProvider::isSupported);
}

/**
* Name of this provider.
*
* @return A human readable name of this provider
*/
String displayName();

/**
* Indicates, if this provider can be used.
*
* @return true, if this provider is supported in the current OS environment
* @implSpec This check needs to return fast and in constant time
*/
boolean isSupported();

/**
* A suitable mount point suggested by this provider.
* <p>
* Other than caller-provided mount points, the mount point suggested by this method can be
* passed to {@link MountBuilder#setMountpoint(Path)} and is guaranteed to fulfill the builder's requirements
* without further ado.
*
* @param mountPointSuffix String used in the generation of a mount point.
* @return A path to a possible mount point.
* @throws UnsupportedOperationException If {@link MountFeature#DEFAULT_MOUNT_POINT} is not supported
*/
default Path getDefaultMountPoint(String mountPointSuffix) {
throw new UnsupportedOperationException();
}

/**
* Default mount flags. May be empty.
*
* @param mountName Name of the mount in the OS
* @return Concatenated String of valid mount flags
* @throws UnsupportedOperationException If {@link MountFeature#MOUNT_FLAGS} is not supported
*/
default String getDefaultMountFlags(String mountName) {
throw new UnsupportedOperationException();
}

/**
* The default TCP port used by this provider.
*
* @return fixed TCP port or 0 to use a system-assigned port
* @throws UnsupportedOperationException If {@link MountFeature#PORT} is not supported
*/
@Range(from = 0, to = Short.MAX_VALUE)
default int getDefaultPort() {
throw new UnsupportedOperationException();
}

/**
* Mount features supported by this provider.
*
* @return Set of supported {@link MountFeature}s
*/
Set<MountFeature> supportedFeatures();


/**
* Creates a new mount builder.
*
* @param fileSystemRoot The root of the VFS to be mounted
* @return New mount builder
*/
@Contract("_ -> new")
MountBuilder forFileSystem(Path fileSystemRoot);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.cryptomator.integrations.mount;

public class UnmountFailedException extends Exception {

public UnmountFailedException(String msg) {
super(msg);
}

public UnmountFailedException(Exception cause) {
super(cause);
}

public UnmountFailedException(String msg, Exception cause) {
super(msg, cause);
}
}