Browse Source

move mount prepartions to own class

Armin Schrenk 2 years ago
parent
commit
a72ea1b9f5

+ 108 - 0
src/main/java/org/cryptomator/common/mount/Mounter.java

@@ -0,0 +1,108 @@
+package org.cryptomator.common.mount;
+
+import org.cryptomator.common.Environment;
+import org.cryptomator.common.settings.Settings;
+import org.cryptomator.common.settings.VaultSettings;
+import org.cryptomator.integrations.mount.Mount;
+import org.cryptomator.integrations.mount.MountFailedException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javafx.beans.value.ObservableValue;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+
+import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
+import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
+import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_SYSTEM_CHOSEN_PATH;
+import static org.cryptomator.integrations.mount.MountCapability.MOUNT_WITHIN_EXISTING_PARENT;
+import static org.cryptomator.integrations.mount.MountCapability.UNMOUNT_FORCED;
+
+@Singleton
+public class Mounter {
+
+	private final Settings settings;
+	private final Environment env;
+	private final WindowsDriveLetters driveLetters;
+	private final ObservableValue<ActualMountService> mountService;
+
+	@Inject
+	public Mounter(Settings settings, Environment env, WindowsDriveLetters driveLetters, ObservableValue<ActualMountService> mountService) {
+		this.settings = settings;
+		this.env = env;
+		this.driveLetters = driveLetters;
+		this.mountService = mountService;
+	}
+
+	public MountHandle mountAndcreateHandle(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
+		var mountService = this.mountService.getValue().service();
+		var builder = mountService.forFileSystem(cryptoFsRoot);
+		boolean mountWithinParent = false;
+
+		for (var capability : mountService.capabilities()) {
+			switch (capability) {
+				case FILE_SYSTEM_NAME -> builder.setFileSystemName("crypto");
+				case LOOPBACK_PORT -> builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
+				case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
+				case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
+				case MOUNT_FLAGS -> builder.setMountFlags(Objects.requireNonNullElse(vaultSettings.mountFlags().getValue(), mountService.getDefaultMountFlags()));
+				case VOLUME_ID -> builder.setVolumeId(vaultSettings.getId());
+				case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get());
+			}
+		}
+
+		//TODO: refactor logic to own method
+		var userChosenMountPoint = vaultSettings.getMountPoint();
+		var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
+		var canMountToDriveLetter = mountService.hasCapability(MOUNT_AS_DRIVE_LETTER);
+		var canMountToParent = mountService.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
+		var canMountToDir = mountService.hasCapability(MOUNT_TO_EXISTING_DIR);
+		if (userChosenMountPoint == null) {
+			if (mountService.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH)) {
+				// no need to set a mount point
+			} else if (canMountToDriveLetter) {
+				builder.setMountpoint(driveLetters.getFirstDesiredAvailable().orElseThrow()); //TODO: catch exception
+			} else if (canMountToParent) {
+				Files.createDirectories(defaultMountPointBase);
+				builder.setMountpoint(defaultMountPointBase);
+			} else if (canMountToDir) {
+				var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get());
+				Files.createDirectories(mountPoint);
+				builder.setMountpoint(mountPoint);
+			}
+		} else {
+			mountWithinParent = canMountToParent && !canMountToDir;
+			if(mountWithinParent) {
+				// TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT
+			}
+			try {
+				builder.setMountpoint(userChosenMountPoint);
+			} catch (IllegalArgumentException e) {
+				var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
+				var configNotSupported = (!canMountToDriveLetter && mpIsDriveLetter) || (!canMountToDir && !mpIsDriveLetter) || (!canMountToParent && !mpIsDriveLetter);
+				if (configNotSupported) {
+					throw new MountPointNotSupportedException(e.getMessage());
+				} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
+					//mountpoint must exist
+					throw new MountPointNotExistsException(e.getMessage());
+				} else {
+					throw new IllegalMountPointException(e.getMessage());
+				}
+				/*
+				//TODO:
+				if (!canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)) {
+					//parent must exist, mountpoint must not exist
+				}
+				 */
+			}
+		}
+
+		return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), mountWithinParent);
+	}
+
+	public record MountHandle(Mount mount, boolean supportsUnmountForced, boolean mountWithinParent) {
+
+	}
+}

+ 18 - 101
src/main/java/org/cryptomator/common/vaults/Vault.java

@@ -11,13 +11,8 @@ package org.cryptomator.common.vaults;
 import com.google.common.base.Strings;
 import org.apache.commons.lang3.SystemUtils;
 import org.cryptomator.common.Constants;
-import org.cryptomator.common.Environment;
-import org.cryptomator.common.mount.ActualMountService;
-import org.cryptomator.common.mount.IllegalMountPointException;
-import org.cryptomator.common.mount.MountPointNotExistsException;
-import org.cryptomator.common.mount.MountPointNotSupportedException;
+import org.cryptomator.common.mount.Mounter;
 import org.cryptomator.common.mount.WindowsDriveLetters;
-import org.cryptomator.common.settings.Settings;
 import org.cryptomator.common.settings.VaultSettings;
 import org.cryptomator.cryptofs.CryptoFileSystem;
 import org.cryptomator.cryptofs.CryptoFileSystemProperties;
@@ -27,11 +22,7 @@ import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
 import org.cryptomator.cryptolib.api.CryptoException;
 import org.cryptomator.cryptolib.api.MasterkeyLoader;
 import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
-import org.cryptomator.integrations.mount.Mount;
-import org.cryptomator.integrations.mount.MountBuilder;
-import org.cryptomator.integrations.mount.MountCapability;
 import org.cryptomator.integrations.mount.MountFailedException;
-import org.cryptomator.integrations.mount.MountService;
 import org.cryptomator.integrations.mount.Mountpoint;
 import org.cryptomator.integrations.mount.UnmountFailedException;
 import org.slf4j.Logger;
@@ -48,9 +39,7 @@ import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.ReadOnlyStringProperty;
 import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.value.ObservableValue;
 import java.io.IOException;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.EnumSet;
@@ -58,11 +47,6 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
-import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
-import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_SYSTEM_CHOSEN_PATH;
-import static org.cryptomator.integrations.mount.MountCapability.MOUNT_WITHIN_EXISTING_PARENT;
-
 @PerVault
 public class Vault {
 
@@ -70,13 +54,10 @@ public class Vault {
 	private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME);
 	private static final int UNLIMITED_FILENAME_LENGTH = Integer.MAX_VALUE;
 
-	private final Environment env;
-	private final Settings settings;
 	private final VaultSettings vaultSettings;
 	private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
 	private final VaultState state;
 	private final ObjectProperty<Exception> lastKnownException;
-	private final ObservableValue<ActualMountService> mountService;
 	private final VaultConfigCache configCache;
 	private final VaultStats stats;
 	private final StringBinding displayablePath;
@@ -88,20 +69,18 @@ public class Vault {
 	private final BooleanBinding unknownError;
 	private final ObjectBinding<Mountpoint> mountPoint;
 	private final WindowsDriveLetters windowsDriveLetters;
+	private final Mounter mounter;
 	private final BooleanProperty showingStats;
 
-	private AtomicReference<MountHandle> mountHandle = new AtomicReference<>(null);
+	private AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
 
 	@Inject
-	Vault(Environment env, Settings settings, VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, ObservableValue<ActualMountService> mountService, VaultStats stats, WindowsDriveLetters windowsDriveLetters) {
-		this.env = env;
-		this.settings = settings;
+	Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) {
 		this.vaultSettings = vaultSettings;
 		this.configCache = configCache;
 		this.cryptoFileSystem = cryptoFileSystem;
 		this.state = state;
 		this.lastKnownException = lastKnownException;
-		this.mountService = mountService;
 		this.stats = stats;
 		this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path());
 		this.locked = Bindings.createBooleanBinding(this::isLocked, state);
@@ -112,6 +91,7 @@ public class Vault {
 		this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
 		this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
 		this.windowsDriveLetters = windowsDriveLetters;
+		this.mounter = mounter;
 		this.showingStats = new SimpleBooleanProperty(false);
 	}
 
@@ -161,67 +141,6 @@ public class Vault {
 		}
 	}
 
-	private MountBuilder prepareMount(MountService mountService, Path cryptoRoot) throws IOException {
-		var builder = mountService.forFileSystem(cryptoRoot);
-
-		for (var capability : mountService.capabilities()) {
-			switch (capability) {
-				case FILE_SYSTEM_NAME -> builder.setFileSystemName("crypto");
-				case LOOPBACK_PORT -> builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault)
-				case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName);
-				case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get());
-				case MOUNT_FLAGS -> builder.setMountFlags(Objects.requireNonNullElse(vaultSettings.mountFlags().getValue(), mountService.getDefaultMountFlags()));
-				case VOLUME_ID -> builder.setVolumeId(vaultSettings.getId());
-				case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get());
-			}
-		}
-
-		var userChosenMountPoint = vaultSettings.getMountPoint();
-		var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
-		var canMountToDriveLetter = mountService.hasCapability(MOUNT_AS_DRIVE_LETTER);
-		var canMountToParent = mountService.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
-		var canMountToDir = mountService.hasCapability(MOUNT_TO_EXISTING_DIR);
-		if (userChosenMountPoint == null) {
-			if (mountService.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH)) {
-				// no need to set a mount point
-			} else if (canMountToDriveLetter) {
-				builder.setMountpoint(windowsDriveLetters.getFirstDesiredAvailable().orElseThrow());
-			} else if (canMountToParent) {
-				Files.createDirectories(defaultMountPointBase);
-				builder.setMountpoint(defaultMountPointBase);
-			} else if (canMountToDir) {
-				var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get());
-				Files.createDirectories(mountPoint);
-				builder.setMountpoint(mountPoint);
-			}
-		} else {
-			// TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT?
-			try {
-				builder.setMountpoint(userChosenMountPoint);
-			} catch (IllegalArgumentException e) {
-				//TODO: move code elsewhere
-				var mpIsDriveLetter = userChosenMountPoint.toString().matches("[A-Z]:\\\\");
-				var configNotSupported = (!canMountToDriveLetter && mpIsDriveLetter) || (!canMountToDir && !mpIsDriveLetter) || (!canMountToParent && !mpIsDriveLetter);
-				if(configNotSupported) {
-					throw new MountPointNotSupportedException(e.getMessage());
-				} else if (canMountToDir && !canMountToParent && !Files.exists(userChosenMountPoint)) {
-					//mountpoint must exist
-					throw new MountPointNotExistsException(e.getMessage());
-				} else {
-					throw new IllegalMountPointException(e.getMessage());
-				}
-				/*
-				//TODO:
-				if (!canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)) {
-					//parent must exist, mountpoint must not exist
-				}
-				 */
-			}
-		}
-
-		return builder;
-	}
-
 	public synchronized void unlock(MasterkeyLoader keyLoader) throws CryptoException, IOException, MountFailedException {
 		if (cryptoFileSystem.get() != null) {
 			throw new IllegalStateException("Already unlocked.");
@@ -231,9 +150,7 @@ public class Vault {
 		try {
 			cryptoFileSystem.set(fs);
 			var rootPath = fs.getRootDirectories().iterator().next();
-			var actualMountService = mountService.getValue().service();
-			var supportsForcedUnmount = actualMountService.hasCapability(MountCapability.UNMOUNT_FORCED);
-			var mountHandle = new MountHandle(prepareMount(actualMountService, rootPath).mount(), supportsForcedUnmount);
+			var mountHandle = mounter.mountAndcreateHandle(vaultSettings, rootPath);
 			success = this.mountHandle.compareAndSet(null, mountHandle);
 		} finally {
 			if (!success) {
@@ -242,7 +159,6 @@ public class Vault {
 		}
 	}
 
-
 	public synchronized void lock(boolean forced) throws UnmountFailedException, IOException {
 		var mountHandle = this.mountHandle.get();
 		if (mountHandle == null) {
@@ -250,14 +166,17 @@ public class Vault {
 			return;
 		}
 
-		if (forced && mountHandle.supportsUnmountForced) {
-			mountHandle.mount.unmountForced();
+		if (forced && mountHandle.supportsUnmountForced()) {
+			mountHandle.mount().unmountForced();
 		} else {
-			mountHandle.mount.unmount();
+			mountHandle.mount().unmount();
 		}
 
 		try {
-			mountHandle.mount.close();
+			mountHandle.mount().close();
+			if(mountHandle.mountWithinParent()) {
+				//TODO: cleanup
+			}
 		} finally {
 			destroyCryptoFileSystem();
 		}
@@ -352,7 +271,7 @@ public class Vault {
 
 	public Mountpoint getMountPoint() {
 		var handle = mountHandle.get();
-		return handle == null ? null : handle.mount.getMountpoint();
+		return handle == null ? null : handle.mount().getMountpoint();
 	}
 
 	public StringBinding displayablePathProperty() {
@@ -434,16 +353,14 @@ public class Vault {
 		}
 	}
 
-
 	public boolean supportsForcedUnmount() {
 		var mh = mountHandle.get();
-		if(mh == null) {
+		if (mh == null) {
+			//TODO: or return false?
 			throw new IllegalStateException("Vault is not mounted");
-		};
+		}
+		;
 		return mountHandle.get().supportsUnmountForced();
 	}
 
-	private record MountHandle(Mount mount, boolean supportsUnmountForced) {
-
-	}
 }