Kaynağa Gözat

Merge pull request #3260 from cryptomator/feature/annihilate-FUPFMS

annihilate FUPFMS
Sebastian Stenzel 1 yıl önce
ebeveyn
işleme
7d7b7c1dc9

+ 0 - 9
src/main/java/org/cryptomator/common/CommonsModule.java

@@ -14,7 +14,6 @@ import org.cryptomator.common.settings.SettingsProvider;
 import org.cryptomator.common.vaults.VaultComponent;
 import org.cryptomator.common.vaults.VaultListModule;
 import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
-import org.cryptomator.integrations.mount.MountService;
 import org.cryptomator.integrations.revealpath.RevealPathService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,7 +29,6 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 @Module(subcomponents = {VaultComponent.class}, includes = {VaultListModule.class, KeychainModule.class, MountModule.class})
 public abstract class CommonsModule {
@@ -134,11 +132,4 @@ public abstract class CommonsModule {
 		LOG.error("Uncaught exception in " + thread.getName(), throwable);
 	}
 
-	@Provides
-	@Singleton
-	@Named("FUPFMS")
-	static AtomicReference<MountService> provideFirstUsedProblematicFuseMountService() {
-		return new AtomicReference<>(null);
-	}
-
 }

+ 14 - 0
src/main/java/org/cryptomator/common/mount/ConflictingMountServiceException.java

@@ -0,0 +1,14 @@
+package org.cryptomator.common.mount;
+
+import org.cryptomator.integrations.mount.MountFailedException;
+
+/**
+ * Thrown by {@link Mounter} to indicate that the selected mount service can not be used
+ * due to incompatibilities with a different mount service that is already in use.
+ */
+public class ConflictingMountServiceException extends MountFailedException {
+
+	public ConflictingMountServiceException(String msg) {
+		super(msg);
+	}
+}

+ 0 - 10
src/main/java/org/cryptomator/common/mount/FuseRestartRequiredException.java

@@ -1,10 +0,0 @@
-package org.cryptomator.common.mount;
-
-import org.cryptomator.integrations.mount.MountFailedException;
-
-public class FuseRestartRequiredException extends MountFailedException {
-
-	public FuseRestartRequiredException(String msg) {
-		super(msg);
-	}
-}

+ 12 - 1
src/main/java/org/cryptomator/common/mount/MountModule.java

@@ -6,9 +6,12 @@ import org.cryptomator.common.ObservableUtil;
 import org.cryptomator.common.settings.Settings;
 import org.cryptomator.integrations.mount.MountService;
 
+import javax.inject.Named;
 import javax.inject.Singleton;
 import javafx.beans.value.ObservableValue;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 @Module
 public class MountModule {
@@ -28,4 +31,12 @@ public class MountModule {
 				fallbackProvider);
 	}
 
-}
+
+	@Provides
+	@Singleton
+	@Named("usedMountServices")
+	static Set<MountService> provideSetOfUsedMountServices() {
+		return ConcurrentHashMap.newKeySet();
+	}
+
+}

+ 23 - 16
src/main/java/org/cryptomator/common/mount/Mounter.java

@@ -16,7 +16,8 @@ import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.Map;
+import java.util.Set;
 
 import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER;
 import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR;
@@ -27,12 +28,17 @@ import static org.cryptomator.integrations.mount.MountCapability.UNMOUNT_FORCED;
 @Singleton
 public class Mounter {
 
-	private static final List<String> CONFLICTING_MOUNT_SERVICES = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider");
+	// mount providers (key) can not be used if any of the conflicting mount providers (values) are already in use
+	private static final Map<String, Set<String>> CONFLICTING_MOUNT_SERVICES = Map.of(
+			"org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.FuseTMountProvider"),
+			"org.cryptomator.frontend.fuse.mount.FuseTMountProvider", Set.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider")
+	);
+
 	private final Environment env;
 	private final Settings settings;
 	private final WindowsDriveLetters driveLetters;
 	private final List<MountService> mountProviders;
-	private final AtomicReference<MountService> firstUsedProblematicFuseMountService;
+	private final Set<MountService> usedMountServices;
 	private final ObservableValue<MountService> defaultMountService;
 
 	@Inject
@@ -40,13 +46,13 @@ public class Mounter {
 				   Settings settings, //
 				   WindowsDriveLetters driveLetters, //
 				   List<MountService> mountProviders, //
-				   @Named("FUPFMS") AtomicReference<MountService> firstUsedProblematicFuseMountService, //
+				   @Named("usedMountServices") Set<MountService> usedMountServices, //
 				   ObservableValue<MountService> defaultMountService) {
 		this.env = env;
 		this.settings = settings;
 		this.driveLetters = driveLetters;
 		this.mountProviders = mountProviders;
-		this.firstUsedProblematicFuseMountService = firstUsedProblematicFuseMountService;
+		this.usedMountServices = usedMountServices;
 		this.defaultMountService = defaultMountService;
 	}
 
@@ -149,23 +155,24 @@ public class Mounter {
 	}
 
 	public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
-		var selMntServ = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
+		var mountService = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
 
-		var targetIsProblematicFuse = isProblematicFuseService(selMntServ);
-		if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) {
-			firstUsedProblematicFuseMountService.set(selMntServ);
-		} else if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(selMntServ)) {
-			throw new FuseRestartRequiredException("Failed to mount the specified mount service.");
+		if (isConflictingMountService(mountService)) {
+			var msg = STR."\{mountService.getClass()} unavailable due to conflict with either of \{CONFLICTING_MOUNT_SERVICES.get(mountService.getClass().getName())}";
+			throw new ConflictingMountServiceException(msg);
 		}
 
-		var builder = selMntServ.forFileSystem(cryptoFsRoot);
-		var internal = new SettledMounter(selMntServ, builder, vaultSettings);
+		usedMountServices.add(mountService);
+
+		var builder = mountService.forFileSystem(cryptoFsRoot);
+		var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
 		var cleanup = internal.prepare();
-		return new MountHandle(builder.mount(), selMntServ.hasCapability(UNMOUNT_FORCED), cleanup);
+		return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
 	}
 
-	public static boolean isProblematicFuseService(MountService service) {
-		return CONFLICTING_MOUNT_SERVICES.contains(service.getClass().getName());
+	public boolean isConflictingMountService(MountService service) {
+		var conflictingServices = CONFLICTING_MOUNT_SERVICES.getOrDefault(service.getClass().getName(), Set.of());
+		return usedMountServices.stream().map(MountService::getClass).map(Class::getName).anyMatch(conflictingServices::contains);
 	}
 
 	public record MountHandle(Mount mountObj, boolean supportsUnmountForced, Runnable specialCleanup) {

+ 1 - 1
src/main/java/org/cryptomator/ui/common/FxmlFile.java

@@ -45,7 +45,7 @@ public enum FxmlFile {
 	REMOVE_VAULT("/fxml/remove_vault.fxml"), //
 	UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
 	UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
-	UNLOCK_FUSE_RESTART_REQUIRED("/fxml/unlock_fuse_restart_required.fxml"), //
+	UNLOCK_REQUIRES_RESTART("/fxml/unlock_requires_restart.fxml"), //
 	UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
 	UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), //
 	UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //

+ 5 - 5
src/main/java/org/cryptomator/ui/unlock/UnlockModule.java

@@ -82,10 +82,10 @@ abstract class UnlockModule {
 	}
 
 	@Provides
-	@FxmlScene(FxmlFile.UNLOCK_FUSE_RESTART_REQUIRED)
+	@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART)
 	@UnlockScoped
-	static Scene provideFuseRestartRequiredScene(@UnlockWindow FxmlLoaderFactory fxmlLoaders) {
-		return fxmlLoaders.createScene(FxmlFile.UNLOCK_FUSE_RESTART_REQUIRED);
+	static Scene provideRestartRequiredScene(@UnlockWindow FxmlLoaderFactory fxmlLoaders) {
+		return fxmlLoaders.createScene(FxmlFile.UNLOCK_REQUIRES_RESTART);
 	}
 
 	// ------------------
@@ -102,7 +102,7 @@ abstract class UnlockModule {
 
 	@Binds
 	@IntoMap
-	@FxControllerKey(UnlockFuseRestartRequiredController.class)
-	abstract FxController bindUnlockFuseRestartRequiredController(UnlockFuseRestartRequiredController controller);
+	@FxControllerKey(UnlockRequiresRestartController.class)
+	abstract FxController bindUnlockRequiresRestartController(UnlockRequiresRestartController controller);
 
 }

+ 5 - 5
src/main/java/org/cryptomator/ui/unlock/UnlockFuseRestartRequiredController.java

@@ -11,7 +11,7 @@ import javafx.stage.Stage;
 import java.util.ResourceBundle;
 
 @UnlockScoped
-public class UnlockFuseRestartRequiredController implements FxController {
+public class UnlockRequiresRestartController implements FxController {
 
 	private final Stage window;
 	private final ResourceBundle resourceBundle;
@@ -19,10 +19,10 @@ public class UnlockFuseRestartRequiredController implements FxController {
 	private final Vault vault;
 
 	@Inject
-	UnlockFuseRestartRequiredController(@UnlockWindow Stage window, //
-										ResourceBundle resourceBundle, //
-										FxApplicationWindows appWindows, //
-										@UnlockWindow Vault vault) {
+	UnlockRequiresRestartController(@UnlockWindow Stage window, //
+									ResourceBundle resourceBundle, //
+									FxApplicationWindows appWindows, //
+									@UnlockWindow Vault vault) {
 		this.window = window;
 		this.resourceBundle = resourceBundle;
 		this.appWindows = appWindows;

+ 10 - 12
src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java

@@ -1,7 +1,7 @@
 package org.cryptomator.ui.unlock;
 
 import dagger.Lazy;
-import org.cryptomator.common.mount.FuseRestartRequiredException;
+import org.cryptomator.common.mount.ConflictingMountServiceException;
 import org.cryptomator.common.mount.IllegalMountPointException;
 import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.common.vaults.VaultState;
@@ -38,7 +38,7 @@ public class UnlockWorkflow extends Task<Void> {
 	private final VaultService vaultService;
 	private final Lazy<Scene> successScene;
 	private final Lazy<Scene> invalidMountPointScene;
-	private final Lazy<Scene> fuseRestartRequiredScene;
+	private final Lazy<Scene> restartRequiredScene;
 	private final FxApplicationWindows appWindows;
 	private final KeyLoadingStrategy keyLoadingStrategy;
 	private final ObjectProperty<IllegalMountPointException> illegalMountPointException;
@@ -49,7 +49,7 @@ public class UnlockWorkflow extends Task<Void> {
 				   VaultService vaultService, //
 				   @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, //
 				   @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, //
-				   @FxmlScene(FxmlFile.UNLOCK_FUSE_RESTART_REQUIRED) Lazy<Scene> fuseRestartRequiredScene, //
+				   @FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, //
 				   FxApplicationWindows appWindows, //
 				   @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, //
 				   @UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
@@ -58,7 +58,7 @@ public class UnlockWorkflow extends Task<Void> {
 		this.vaultService = vaultService;
 		this.successScene = successScene;
 		this.invalidMountPointScene = invalidMountPointScene;
-		this.fuseRestartRequiredScene = fuseRestartRequiredScene;
+		this.restartRequiredScene = restartRequiredScene;
 		this.appWindows = appWindows;
 		this.keyLoadingStrategy = keyLoadingStrategy;
 		this.illegalMountPointException = illegalMountPointException;
@@ -87,9 +87,9 @@ public class UnlockWorkflow extends Task<Void> {
 		});
 	}
 
-	private void handleFuseRestartRequiredError() {
+	private void handleConflictingMountServiceException() {
 		Platform.runLater(() -> {
-			window.setScene(fuseRestartRequiredScene.get());
+			window.setScene(restartRequiredScene.get());
 			window.show();
 		});
 	}
@@ -122,12 +122,10 @@ public class UnlockWorkflow extends Task<Void> {
 	protected void failed() {
 		LOG.info("Unlock of '{}' failed.", vault.getDisplayName());
 		Throwable throwable = super.getException();
-		if(throwable instanceof IllegalMountPointException impe) {
-			handleIllegalMountPointError(impe);
-		} else if (throwable instanceof FuseRestartRequiredException _) {
-			handleFuseRestartRequiredError();
-		} else {
-			handleGenericError(throwable);
+		switch (throwable) {
+			case IllegalMountPointException e -> handleIllegalMountPointError(e);
+			case ConflictingMountServiceException _ -> handleConflictingMountServiceException();
+			default -> handleGenericError(throwable);
 		}
 		vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
 	}

+ 7 - 13
src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java

@@ -15,7 +15,6 @@ import org.cryptomator.ui.preferences.SelectedPreferencesTab;
 import org.cryptomator.ui.preferences.VolumePreferencesController;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 import javafx.application.Application;
 import javafx.beans.binding.Bindings;
 import javafx.beans.value.ObservableValue;
@@ -38,7 +37,6 @@ import java.util.List;
 import java.util.Optional;
 import java.util.ResourceBundle;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
 
 @VaultOptionsScoped
 public class MountOptionsController implements FxController {
@@ -60,7 +58,7 @@ public class MountOptionsController implements FxController {
 	private final List<MountService> mountProviders;
 	private final ObservableValue<MountService> defaultMountService;
 	private final ObservableValue<MountService> selectedMountService;
-	private final ObservableValue<Boolean> fuseRestartRequired;
+	private final ObservableValue<Boolean> selectedMountServiceRequiresRestart;
 	private final ObservableValue<Boolean> loopbackPortChangeable;
 
 
@@ -87,7 +85,7 @@ public class MountOptionsController implements FxController {
 						   FxApplicationWindows applicationWindows, //
 						   Lazy<Application> application, //
 						   List<MountService> mountProviders, //
-						   @Named("FUPFMS") AtomicReference<MountService> firstUsedProblematicFuseMountService, //
+						   Mounter mounter, //
 						   ObservableValue<MountService> defaultMountService) {
 		this.window = window;
 		this.vaultSettings = vault.getVaultSettings();
@@ -99,11 +97,7 @@ public class MountOptionsController implements FxController {
 		this.mountProviders = mountProviders;
 		this.defaultMountService = defaultMountService;
 		this.selectedMountService = Bindings.createObjectBinding(this::reselectMountService, defaultMountService, vaultSettings.mountService);
-		this.fuseRestartRequired = selectedMountService.map(s -> {
-			return firstUsedProblematicFuseMountService.get() != null //
-					&& Mounter.isProblematicFuseService(s) //
-					&& !firstUsedProblematicFuseMountService.get().equals(s);
-		});
+		this.selectedMountServiceRequiresRestart = selectedMountService.map(mounter::isConflictingMountService);
 
 		this.defaultMountFlags = selectedMountService.map(s -> {
 			if (s.hasCapability(MountCapability.MOUNT_FLAGS)) {
@@ -367,12 +361,12 @@ public class MountOptionsController implements FxController {
 		return directoryPath.getValue();
 	}
 
-	public ObservableValue<Boolean> fuseRestartRequiredProperty() {
-		return fuseRestartRequired;
+	public ObservableValue<Boolean> selectedMountServiceRequiresRestartProperty() {
+		return selectedMountServiceRequiresRestart;
 	}
 
-	public boolean getFuseRestartRequired() {
-		return fuseRestartRequired.getValue();
+	public boolean getSelectedMountServiceRequiresRestart() {
+		return selectedMountServiceRequiresRestart.getValue();
 	}
 
 	public ObservableValue<Boolean> loopbackPortChangeableProperty() {

+ 3 - 3
src/main/resources/fxml/unlock_fuse_restart_required.fxml

@@ -14,7 +14,7 @@
 <?import javafx.scene.shape.Circle?>
 <HBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
-	  fx:controller="org.cryptomator.ui.unlock.UnlockFuseRestartRequiredController"
+	  fx:controller="org.cryptomator.ui.unlock.UnlockRequiresRestartController"
 	  minWidth="400"
 	  maxWidth="400"
 	  minHeight="145"
@@ -34,13 +34,13 @@
 			</StackPane>
 		</Group>
 		<VBox HBox.hgrow="ALWAYS">
-			<Label styleClass="label-large" text="%unlock.error.fuseRestartRequired.message" wrapText="true" textAlignment="LEFT">
+			<Label styleClass="label-large" text="%unlock.error.restartRequired.message" wrapText="true" textAlignment="LEFT">
 				<padding>
 					<Insets bottom="6" top="6"/>
 				</padding>
 			</Label>
 
-			<Label text="%unlock.error.fuseRestartRequired.description" wrapText="true" textAlignment="LEFT"/>
+			<Label text="%unlock.error.restartRequired.description" wrapText="true" textAlignment="LEFT"/>
 
 			<Region VBox.vgrow="ALWAYS" minHeight="18"/>
 			<ButtonBar buttonMinWidth="120" buttonOrder="+CI">

+ 1 - 1
src/main/resources/fxml/vault_options_mount.fxml

@@ -46,7 +46,7 @@
 			</Hyperlink>
 		</HBox>
 
-		<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.fuseRestartRequired" visible="${controller.fuseRestartRequired}" managed="${controller.fuseRestartRequired}"/>
+		<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
 
 		<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
 			<Label text="%vaultOptions.mount.volume.tcp.port"/>

+ 3 - 3
src/main/resources/i18n/strings.properties

@@ -142,8 +142,8 @@ unlock.error.customPath.description.hideawayNotDir=The temporary, hidden file "%
 unlock.error.customPath.description.couldNotBeCleaned=Your vault could not be mounted to the path "%s". Please try again or choose a different path.
 unlock.error.customPath.description.notEmptyDir=The custom mount path "%s" is not an empty folder. Please choose an empty folder and try again.
 unlock.error.customPath.description.generic=You have selected a custom mount path for this vault, but using it failed with the message: %2$s
-unlock.error.fuseRestartRequired.message=Unable to unlock vault
-unlock.error.fuseRestartRequired.description=Change the volume type in vault options or restart Cryptomator.
+unlock.error.restartRequired.message=Unable to unlock vault
+unlock.error.restartRequired.description=Change the volume type in vault options or restart Cryptomator.
 unlock.error.title=Unlock "%s" failed
 ## Hub
 hub.noKeychain.message=Unable to access device key
@@ -452,7 +452,7 @@ vaultOptions.mount.mountPoint.custom=Use chosen directory
 vaultOptions.mount.mountPoint.directoryPickerButton=Choose…
 vaultOptions.mount.mountPoint.directoryPickerTitle=Pick a directory
 vaultOptions.mount.volumeType.default=Default (%s)
-vaultOptions.mount.volumeType.fuseRestartRequired=To use this volume type, Cryptomator needs to be restarted.
+vaultOptions.mount.volumeType.restartRequired=To use this volume type, Cryptomator needs to be restarted.
 vaultOptions.mount.volume.tcp.port=TCP Port
 vaultOptions.mount.volume.type=Volume Type
 ## Master Key