Handle illegelArgumentException when setting custom mountpoint

This commit is contained in:
Armin Schrenk 2023-01-12 17:21:54 +01:00
parent 5c61944757
commit 6395f17736
No known key found for this signature in database
GPG Key ID: 8F2992163CBBA7FC
9 changed files with 121 additions and 16 deletions

View File

@ -0,0 +1,9 @@
package org.cryptomator.common.mount;
public class IllegalMountPointException extends IllegalArgumentException {
public IllegalMountPointException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,8 @@
package org.cryptomator.common.mount;
public class MountPointNotExistsException extends IllegalMountPointException {
public MountPointNotExistsException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,8 @@
package org.cryptomator.common.mount;
public class MountPointNotSupportedException extends IllegalMountPointException {
public MountPointNotSupportedException(String msg) {
super(msg);
}
}

View File

@ -13,6 +13,9 @@ 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.WindowsDriveLetters;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.VaultSettings;
@ -175,22 +178,45 @@ public class Vault {
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 (mountService.hasCapability(MOUNT_AS_DRIVE_LETTER)) {
} else if (canMountToDriveLetter) {
builder.setMountpoint(windowsDriveLetters.getFirstDesiredAvailable().orElseThrow());
} else if (mountService.hasCapability(MOUNT_WITHIN_EXISTING_PARENT)) {
} else if (canMountToParent) {
Files.createDirectories(defaultMountPointBase);
builder.setMountpoint(defaultMountPointBase);
} else if (mountService.hasCapability(MOUNT_TO_EXISTING_DIR)) {
} else if (canMountToDir) {
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get());
Files.createDirectories(mountPoint);
builder.setMountpoint(mountPoint);
}
} else if (mountService.hasCapability(MOUNT_TO_EXISTING_DIR) || mountService.hasCapability(MOUNT_WITHIN_EXISTING_PARENT) || mountService.hasCapability(MOUNT_AS_DRIVE_LETTER)) {
} else {
// TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT?
builder.setMountpoint(userChosenMountPoint);
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;

View File

@ -1,11 +1,18 @@
package org.cryptomator.ui.unlock;
import org.cryptomator.common.mount.MountPointNotExistsException;
import org.cryptomator.common.mount.MountPointNotSupportedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FormattedLabel;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicReference;
//At the current point in time only the CustomMountPointChooser may cause this window to be shown.
@UnlockScoped
@ -13,11 +20,32 @@ public class UnlockInvalidMountPointController implements FxController {
private final Stage window;
private final Vault vault;
private final AtomicReference<Throwable> unlockException;
private final FxApplicationWindows appWindows;
private final ResourceBundle resourceBundle;
public FormattedLabel dialogDescription;
@Inject
UnlockInvalidMountPointController(@UnlockWindow Stage window, @UnlockWindow Vault vault) {
UnlockInvalidMountPointController(@UnlockWindow Stage window, @UnlockWindow Vault vault, @UnlockWindow AtomicReference<Throwable> unlockException, FxApplicationWindows appWindows, ResourceBundle resourceBundle) {
this.window = window;
this.vault = vault;
this.unlockException = unlockException;
this.appWindows = appWindows;
this.resourceBundle = resourceBundle;
}
@FXML
public void initialize() {
var e = unlockException.get();
String translationKey = "unlock.error.customPath.description.generic";
if (e instanceof MountPointNotSupportedException) {
translationKey = "unlock.error.customPath.description.notSupported";
} else if (e instanceof MountPointNotExistsException) {
translationKey = "unlock.error.customPath.description.notExists";
}
dialogDescription.setFormat(resourceBundle.getString(translationKey));
dialogDescription.setArg1(e.getMessage());
}
@FXML
@ -25,4 +53,10 @@ public class UnlockInvalidMountPointController implements FxController {
window.close();
}
@FXML
public void closeAndOpenPreferences() {
appWindows.showPreferencesWindow(SelectedPreferencesTab.VOLUME);
window.close();
}
}

View File

@ -23,6 +23,7 @@ import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicReference;
@Module(subcomponents = {KeyLoadingComponent.class})
abstract class UnlockModule {
@ -57,6 +58,13 @@ abstract class UnlockModule {
return compBuilder.vault(vault).window(window).build().keyloadingStrategy();
}
@Provides
@UnlockWindow
@UnlockScoped
static AtomicReference<Throwable> unlockException() {
return new AtomicReference<>();
}
@Provides
@FxmlScene(FxmlFile.UNLOCK_SUCCESS)
@UnlockScoped

View File

@ -2,6 +2,7 @@ package org.cryptomator.ui.unlock;
import com.google.common.base.Throwables;
import dagger.Lazy;
import org.cryptomator.common.mount.IllegalMountPointException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.cryptolib.api.CryptoException;
@ -20,6 +21,7 @@ import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
/**
* A multi-step task that consists of background activities as well as user interaction.
@ -38,9 +40,10 @@ public class UnlockWorkflow extends Task<Boolean> {
private final Lazy<Scene> invalidMountPointScene;
private final FxApplicationWindows appWindows;
private final KeyLoadingStrategy keyLoadingStrategy;
private final AtomicReference<Throwable> unlockFailedException;
@Inject
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy) {
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy, @UnlockWindow AtomicReference<Throwable> unlockFailedException) {
this.window = window;
this.vault = vault;
this.vaultService = vaultService;
@ -48,6 +51,7 @@ public class UnlockWorkflow extends Task<Boolean> {
this.invalidMountPointScene = invalidMountPointScene;
this.appWindows = appWindows;
this.keyLoadingStrategy = keyLoadingStrategy;
this.unlockFailedException = unlockFailedException;
}
@Override
@ -67,13 +71,15 @@ public class UnlockWorkflow extends Task<Boolean> {
} catch (Exception e) {
Throwables.propagateIfPossible(e, IOException.class);
Throwables.propagateIfPossible(e, CryptoException.class);
Throwables.propagateIfPossible(e, IllegalMountPointException.class);
Throwables.propagateIfPossible(e, MountFailedException.class);
throw new IllegalStateException("unexpected exception type", e);
}
}
private void showInvalidMountPointScene() {
private void handleIllegalMountPointError(IllegalMountPointException impe) {
Platform.runLater(() -> {
unlockFailedException.set(impe);
window.setScene(invalidMountPointScene.get());
window.show();
});
@ -107,7 +113,11 @@ public class UnlockWorkflow extends Task<Boolean> {
protected void failed() {
LOG.info("Unlock of '{}' failed.", vault.getDisplayName());
Throwable throwable = super.getException();
handleGenericError(throwable);
if(throwable instanceof IllegalMountPointException impe) {
handleIllegalMountPointError(impe);
} else {
handleGenericError(throwable);
}
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}

View File

@ -34,16 +34,19 @@
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="%unlock.error.message" wrapText="true" textAlignment="LEFT">
<Label styleClass="label-large" text="%unlock.error.customPath.message" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<FormattedLabel fx:id="dialogDescription" wrapText="true" textAlignment="LEFT"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
<buttons>
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
<Button text="%hub.noKeychain.openBtn" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#closeAndOpenPreferences"/>
</buttons>
</ButtonBar>
</VBox>

View File

@ -124,11 +124,10 @@ unlock.success.description=Content in vault "%s" is now accessible over its moun
unlock.success.rememberChoice=Remember my choice, don't ask again
unlock.success.revealBtn=Reveal Drive
## Failure
unlock.error.message=Unable to unlock vault
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Mount point "%s" is not a directory, not empty or does not exist.
unlock.error.invalidMountPoint.existing=Mount point "%s" already exists or parent folder is missing.
unlock.error.invalidMountPoint.driveLetterOccupied=Drive Letter "%s" is already in use.
unlock.error.customPath.message=Unable to mount vault to custom path
unlock.error.customPath.description.notSupported=If you wish to keep using the custom path, please go to the preferences and select a volume type that supports it. Otherwise, go to the vault options and choose a supported mount point.
unlock.error.customPath.description.notExists=The custom mount path does not exist. Either create it in your local filesystem or change it in the vault options.
unlock.error.customPath.description.generic=You have selected a custom mount path for this vault, but using it failed with the message: %s.
## Hub
hub.noKeychain.message=Unable to access device key
hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.