diff --git a/src/main/java/org/cryptomator/common/CommonsModule.java b/src/main/java/org/cryptomator/common/CommonsModule.java index 5ea69da6d..a1e3c0950 100644 --- a/src/main/java/org/cryptomator/common/CommonsModule.java +++ b/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 provideFirstUsedProblematicFuseMountService() { - return new AtomicReference<>(null); - } - } diff --git a/src/main/java/org/cryptomator/common/mount/ConflictingMountServiceException.java b/src/main/java/org/cryptomator/common/mount/ConflictingMountServiceException.java new file mode 100644 index 000000000..b4d87169a --- /dev/null +++ b/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); + } +} diff --git a/src/main/java/org/cryptomator/common/mount/FuseRestartRequiredException.java b/src/main/java/org/cryptomator/common/mount/FuseRestartRequiredException.java deleted file mode 100644 index 5e4420889..000000000 --- a/src/main/java/org/cryptomator/common/mount/FuseRestartRequiredException.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/org/cryptomator/common/mount/MountModule.java b/src/main/java/org/cryptomator/common/mount/MountModule.java index 3b6fa63c7..4a5f0c6af 100644 --- a/src/main/java/org/cryptomator/common/mount/MountModule.java +++ b/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 provideSetOfUsedMountServices() { + return ConcurrentHashMap.newKeySet(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/cryptomator/common/mount/Mounter.java b/src/main/java/org/cryptomator/common/mount/Mounter.java index bf9fb5b1f..b63a12b1f 100644 --- a/src/main/java/org/cryptomator/common/mount/Mounter.java +++ b/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 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> 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 mountProviders; - private final AtomicReference firstUsedProblematicFuseMountService; + private final Set usedMountServices; private final ObservableValue defaultMountService; @Inject @@ -40,13 +46,13 @@ public class Mounter { Settings settings, // WindowsDriveLetters driveLetters, // List mountProviders, // - @Named("FUPFMS") AtomicReference firstUsedProblematicFuseMountService, // + @Named("usedMountServices") Set usedMountServices, // ObservableValue 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) { diff --git a/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/src/main/java/org/cryptomator/ui/common/FxmlFile.java index fd451d255..7298d3487 100644 --- a/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/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"), // diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index 9c3f84c6b..95f13d383 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/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); } diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockFuseRestartRequiredController.java b/src/main/java/org/cryptomator/ui/unlock/UnlockRequiresRestartController.java similarity index 78% rename from src/main/java/org/cryptomator/ui/unlock/UnlockFuseRestartRequiredController.java rename to src/main/java/org/cryptomator/ui/unlock/UnlockRequiresRestartController.java index 21fe13733..497194dff 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockFuseRestartRequiredController.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockRequiresRestartController.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; diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index 9b5fa74b1..98a49dec5 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/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 { private final VaultService vaultService; private final Lazy successScene; private final Lazy invalidMountPointScene; - private final Lazy fuseRestartRequiredScene; + private final Lazy restartRequiredScene; private final FxApplicationWindows appWindows; private final KeyLoadingStrategy keyLoadingStrategy; private final ObjectProperty illegalMountPointException; @@ -49,7 +49,7 @@ public class UnlockWorkflow extends Task { VaultService vaultService, // @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, // @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, // - @FxmlScene(FxmlFile.UNLOCK_FUSE_RESTART_REQUIRED) Lazy fuseRestartRequiredScene, // + @FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy restartRequiredScene, // FxApplicationWindows appWindows, // @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, // @UnlockWindow ObjectProperty illegalMountPointException) { @@ -58,7 +58,7 @@ public class UnlockWorkflow extends Task { 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 { }); } - 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 { 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); } diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java b/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java index 6340852f4..106623985 100644 --- a/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java +++ b/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 mountProviders; private final ObservableValue defaultMountService; private final ObservableValue selectedMountService; - private final ObservableValue fuseRestartRequired; + private final ObservableValue selectedMountServiceRequiresRestart; private final ObservableValue loopbackPortChangeable; @@ -87,7 +85,7 @@ public class MountOptionsController implements FxController { FxApplicationWindows applicationWindows, // Lazy application, // List mountProviders, // - @Named("FUPFMS") AtomicReference firstUsedProblematicFuseMountService, // + Mounter mounter, // ObservableValue 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 fuseRestartRequiredProperty() { - return fuseRestartRequired; + public ObservableValue selectedMountServiceRequiresRestartProperty() { + return selectedMountServiceRequiresRestart; } - public boolean getFuseRestartRequired() { - return fuseRestartRequired.getValue(); + public boolean getSelectedMountServiceRequiresRestart() { + return selectedMountServiceRequiresRestart.getValue(); } public ObservableValue loopbackPortChangeableProperty() { diff --git a/src/main/resources/fxml/unlock_fuse_restart_required.fxml b/src/main/resources/fxml/unlock_requires_restart.fxml similarity index 84% rename from src/main/resources/fxml/unlock_fuse_restart_required.fxml rename to src/main/resources/fxml/unlock_requires_restart.fxml index 854d4e2de..571ea6a32 100644 --- a/src/main/resources/fxml/unlock_fuse_restart_required.fxml +++ b/src/main/resources/fxml/unlock_requires_restart.fxml @@ -14,7 +14,7 @@ - -