Skip to content

Commit d01284f

Browse files
authored
Merge pull request #12 from cryptomator/feature/mount-provider
Feature: Mount Provider
2 parents fd0b30d + 3a17610 commit d01284f

File tree

8 files changed

+315
-2
lines changed

8 files changed

+315
-2
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@
5959
<dependency>
6060
<groupId>org.junit.jupiter</groupId>
6161
<artifactId>junit-jupiter</artifactId>
62-
<version>5.8.2</version>
62+
<version>5.9.0</version>
6363
<scope>test</scope>
6464
</dependency>
6565
<dependency>
6666
<groupId>org.mockito</groupId>
6767
<artifactId>mockito-core</artifactId>
68-
<version>4.3.1</version>
68+
<version>4.8.0</version>
6969
<scope>test</scope>
7070
</dependency>
7171
</dependencies>

src/main/java/module-info.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import org.cryptomator.integrations.mount.MountProvider;
12
import org.cryptomator.integrations.tray.TrayMenuController;
23
import org.cryptomator.integrations.autostart.AutoStartProvider;
34
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
@@ -12,11 +13,13 @@
1213
exports org.cryptomator.integrations.autostart;
1314
exports org.cryptomator.integrations.common;
1415
exports org.cryptomator.integrations.keychain;
16+
exports org.cryptomator.integrations.mount;
1517
exports org.cryptomator.integrations.tray;
1618
exports org.cryptomator.integrations.uiappearance;
1719

1820
uses AutoStartProvider;
1921
uses KeychainAccessProvider;
22+
uses MountProvider;
2023
uses TrayIntegrationProvider;
2124
uses TrayMenuController;
2225
uses UiAppearanceProvider;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.cryptomator.integrations.mount;
2+
3+
import java.nio.file.Path;
4+
5+
/**
6+
* Handle to control the lifecycle of a mounted file system.
7+
* <p>
8+
* Created by {@link MountBuilder}
9+
*/
10+
public interface Mount extends AutoCloseable {
11+
12+
/**
13+
* Returns the absolute OS path, where this mount can be accessed.
14+
*
15+
* @return Absolute path to the mountpoint.
16+
*/
17+
Path getMountpoint();
18+
19+
/**
20+
* Unmounts the mounted Volume.
21+
* <p>
22+
* If possible, attempt a graceful unmount.
23+
*
24+
* @throws UnmountFailedException If the unmount was not successful.
25+
* @see #unmountForced()
26+
*/
27+
void unmout() throws UnmountFailedException;
28+
29+
/**
30+
* If supported, force-unmount the volume.
31+
*
32+
* @throws UnmountFailedException If the unmount was not successful.
33+
* @throws UnsupportedOperationException If {@link MountFeature#UNMOUNT_FORCED} is not supported
34+
*/
35+
default void unmountForced() throws UnmountFailedException {
36+
throw new UnsupportedOperationException();
37+
}
38+
39+
default void close() throws UnmountFailedException {
40+
unmout();
41+
}
42+
43+
44+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.cryptomator.integrations.mount;
2+
3+
import org.jetbrains.annotations.Contract;
4+
import org.jetbrains.annotations.Range;
5+
6+
import java.nio.file.Path;
7+
8+
/**
9+
* Builder to mount a filesystem.
10+
* <p>
11+
* The setter may attempt to validate the input, but {@link #mount()} may still fail due to missing or invalid (combination of) options.
12+
* This holds especially for {@link MountBuilder#setMountFlags(String)};
13+
*/
14+
public interface MountBuilder {
15+
16+
/**
17+
* Sets the mount point.
18+
*
19+
* @param mountPoint Where to mount the volume
20+
* @return <code>this</code>
21+
* @see MountProvider#getDefaultMountPoint(String)
22+
*/
23+
@Contract("_ -> this")
24+
MountBuilder setMountpoint(Path mountPoint);
25+
26+
/**
27+
* Sets mount flags.
28+
*
29+
* @param mountFlags Mount flags
30+
* @return <code>this</code>
31+
* @throws UnsupportedOperationException If {@link MountFeature#MOUNT_FLAGS} is not supported
32+
* @see MountProvider#getDefaultMountFlags(String)
33+
*/
34+
@Contract("_ -> this")
35+
default MountBuilder setMountFlags(String mountFlags) {
36+
throw new UnsupportedOperationException();
37+
}
38+
39+
/**
40+
* Instructs the mount to be read-only.
41+
*
42+
* @param mountReadOnly Whether to mount read-only.
43+
* @return <code>this</code>
44+
* @throws UnsupportedOperationException If {@link MountFeature#READ_ONLY} is not supported
45+
*/
46+
@Contract("_ -> this")
47+
default MountBuilder setReadOnly(boolean mountReadOnly) {
48+
throw new UnsupportedOperationException();
49+
}
50+
51+
/**
52+
* Use the given TCP port.
53+
*
54+
* @param port fixed TCP port or 0 to use a system-assigned port
55+
* @return <code>this</code>
56+
* @throws UnsupportedOperationException If {@link MountFeature#PORT} is not supported
57+
*/
58+
@Contract("_ -> this")
59+
default MountBuilder setPort(@Range(from = 0, to = Short.MAX_VALUE) int port) {
60+
throw new UnsupportedOperationException();
61+
}
62+
63+
64+
/**
65+
* Mounts the file system.
66+
*
67+
* @return A mount handle
68+
* @throws MountFailedException If mounting failed
69+
*/
70+
@Contract(" -> new")
71+
Mount mount() throws MountFailedException;
72+
73+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.cryptomator.integrations.mount;
2+
3+
public class MountFailedException extends Exception {
4+
5+
public MountFailedException(String msg) {
6+
super(msg);
7+
}
8+
9+
public MountFailedException(Exception cause) {
10+
super(cause);
11+
}
12+
13+
public MountFailedException(String msg, Exception cause) {
14+
super(msg, cause);
15+
}
16+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package org.cryptomator.integrations.mount;
2+
3+
/**
4+
* Describes what aspects of the mount implementation can or should be used.
5+
* <p>
6+
* This may be used to show or hide different configuration options depending on the chosen mount provider.
7+
*/
8+
public enum MountFeature {
9+
/**
10+
* The provider supports {@link MountProvider#getDefaultMountFlags(String)}
11+
* and the builder requires {@link MountBuilder#setMountFlags(String)}.
12+
*/
13+
MOUNT_FLAGS,
14+
15+
/**
16+
* With the exception of a provider-supplied default mount point, the mount point must be an existing dir.
17+
* <p>
18+
* This option is mutually exclusive with {@link #MOUNT_WITHIN_EXISTING_PARENT}.
19+
*
20+
* @see #DEFAULT_MOUNT_POINT
21+
*/
22+
MOUNT_TO_EXISTING_DIR,
23+
24+
/**
25+
* With the exception of a provider-supplied default mount point, the mount point must be a non-existing
26+
* child within an existing parent.
27+
* <p>
28+
* This option is mutually exclusive with {@link #MOUNT_TO_EXISTING_DIR}.
29+
*
30+
* @see #DEFAULT_MOUNT_POINT
31+
*/
32+
MOUNT_WITHIN_EXISTING_PARENT,
33+
34+
/**
35+
* The mount point may be a drive letter.
36+
*
37+
* @see #MOUNT_TO_EXISTING_DIR
38+
* @see #MOUNT_WITHIN_EXISTING_PARENT
39+
*/
40+
MOUNT_AS_DRIVE_LETTER,
41+
42+
/**
43+
* The provider supports suggesting a default mount point via {@link MountProvider#getDefaultMountPoint(String)}.
44+
* <p>
45+
* The default mount point is guaranteed to be supported by the mount builder, regardless of its normal restrictions.
46+
*/
47+
DEFAULT_MOUNT_POINT,
48+
49+
/**
50+
* The builder supports {@link MountBuilder#setReadOnly(boolean)}
51+
*/
52+
READ_ONLY,
53+
54+
/**
55+
* The mount supports {@link Mount#unmountForced()}.
56+
*/
57+
UNMOUNT_FORCED,
58+
59+
/**
60+
* The provider supports {@link MountProvider#getDefaultPort()}
61+
* and the builder requires {@link MountBuilder#setPort(int)}.
62+
*/
63+
PORT
64+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.cryptomator.integrations.mount;
2+
3+
import org.cryptomator.integrations.common.IntegrationsLoader;
4+
import org.jetbrains.annotations.Contract;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Range;
7+
8+
import java.nio.file.Path;
9+
import java.util.Set;
10+
import java.util.stream.Stream;
11+
12+
/**
13+
* A mechanism to mount a file system.
14+
*
15+
* @since 1.2.0
16+
*/
17+
public interface MountProvider {
18+
19+
/**
20+
* Loads all supported mount providers.
21+
*
22+
* @return Stream of supported MountProviders (may be empty)
23+
*/
24+
static Stream<MountProvider> get() {
25+
return IntegrationsLoader.loadAll(MountProvider.class).filter(MountProvider::isSupported);
26+
}
27+
28+
/**
29+
* Name of this provider.
30+
*
31+
* @return A human readable name of this provider
32+
*/
33+
String displayName();
34+
35+
/**
36+
* Indicates, if this provider can be used.
37+
*
38+
* @return true, if this provider is supported in the current OS environment
39+
* @implSpec This check needs to return fast and in constant time
40+
*/
41+
boolean isSupported();
42+
43+
/**
44+
* A suitable mount point suggested by this provider.
45+
* <p>
46+
* Other than caller-provided mount points, the mount point suggested by this method can be
47+
* passed to {@link MountBuilder#setMountpoint(Path)} and is guaranteed to fulfill the builder's requirements
48+
* without further ado.
49+
*
50+
* @param mountPointSuffix String used in the generation of a mount point.
51+
* @return A path to a possible mount point.
52+
* @throws UnsupportedOperationException If {@link MountFeature#DEFAULT_MOUNT_POINT} is not supported
53+
*/
54+
default Path getDefaultMountPoint(String mountPointSuffix) {
55+
throw new UnsupportedOperationException();
56+
}
57+
58+
/**
59+
* Default mount flags. May be empty.
60+
*
61+
* @param mountName Name of the mount in the OS
62+
* @return Concatenated String of valid mount flags
63+
* @throws UnsupportedOperationException If {@link MountFeature#MOUNT_FLAGS} is not supported
64+
*/
65+
default String getDefaultMountFlags(String mountName) {
66+
throw new UnsupportedOperationException();
67+
}
68+
69+
/**
70+
* The default TCP port used by this provider.
71+
*
72+
* @return fixed TCP port or 0 to use a system-assigned port
73+
* @throws UnsupportedOperationException If {@link MountFeature#PORT} is not supported
74+
*/
75+
@Range(from = 0, to = Short.MAX_VALUE)
76+
default int getDefaultPort() {
77+
throw new UnsupportedOperationException();
78+
}
79+
80+
/**
81+
* Mount features supported by this provider.
82+
*
83+
* @return Set of supported {@link MountFeature}s
84+
*/
85+
Set<MountFeature> supportedFeatures();
86+
87+
88+
/**
89+
* Creates a new mount builder.
90+
*
91+
* @param fileSystemRoot The root of the VFS to be mounted
92+
* @return New mount builder
93+
*/
94+
@Contract("_ -> new")
95+
MountBuilder forFileSystem(Path fileSystemRoot);
96+
97+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.cryptomator.integrations.mount;
2+
3+
public class UnmountFailedException extends Exception {
4+
5+
public UnmountFailedException(String msg) {
6+
super(msg);
7+
}
8+
9+
public UnmountFailedException(Exception cause) {
10+
super(cause);
11+
}
12+
13+
public UnmountFailedException(String msg, Exception cause) {
14+
super(msg, cause);
15+
}
16+
}

0 commit comments

Comments
 (0)