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

annihilate FUPFMS
This commit is contained in:
Sebastian Stenzel
2024-01-11 10:25:21 +01:00
committed by GitHub
13 changed files with 84 additions and 79 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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"), //

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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">

View File

@@ -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"/>

View File

@@ -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