diff --git a/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java b/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java index 1a42aa5ad..02f75d4a1 100644 --- a/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java +++ b/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java @@ -5,6 +5,9 @@ import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.vaults.Volume; import javax.inject.Inject; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; @@ -27,4 +30,13 @@ class CustomDriveLetterChooser implements MountPointChooser { public Optional chooseMountPoint(Volume caller) { return this.vaultSettings.getWinDriveLetter().map(letter -> letter.charAt(0) + ":\\").map(Paths::get); } + + @Override + public boolean prepare(Volume caller, Path driveLetter) throws InvalidMountPointException { + if (!Files.notExists(driveLetter, LinkOption.NOFOLLOW_LINKS)) { + //Drive already exists OR can't be determined + throw new InvalidMountPointException(new FileAlreadyExistsException(driveLetter.toString())); + } + return false; + } } diff --git a/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java b/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java index 5f1a7fedd..c55ede640 100644 --- a/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java +++ b/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java @@ -56,7 +56,7 @@ class CustomMountPointChooser implements MountPointChooser { throw new InvalidMountPointException(new IllegalStateException("Illegal MountPointRequirement")); } default -> { - //Currently the case for "PARENT_OPT_MOUNT_POINT" + //Currently the case for "UNUSED_ROOT_DIR, PARENT_OPT_MOUNT_POINT" throw new InvalidMountPointException(new IllegalStateException("Not implemented")); } } diff --git a/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java b/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java index eb1d8d0b1..bcda3d8f2 100644 --- a/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java +++ b/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java @@ -65,7 +65,7 @@ class TemporaryMountPointChooser implements MountPointChooser { throw new InvalidMountPointException(new IllegalStateException("Illegal MountPointRequirement")); } default -> { - //Currently the case for "PARENT_OPT_MOUNT_POINT" + //Currently the case for "UNUSED_ROOT_DIR, PARENT_OPT_MOUNT_POINT" throw new InvalidMountPointException(new IllegalStateException("Not implemented")); } } diff --git a/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java b/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java index c998761d0..c08642073 100644 --- a/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java +++ b/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java @@ -86,7 +86,7 @@ public class DokanyVolume extends AbstractVolume { @Override public MountPointRequirement getMountPointRequirement() { - return MountPointRequirement.EMPTY_MOUNT_POINT; + return this.vaultSettings.getWinDriveLetter().isPresent() ? MountPointRequirement.UNUSED_ROOT_DIR : MountPointRequirement.EMPTY_MOUNT_POINT; } public static boolean isSupportedStatic() { diff --git a/src/main/java/org/cryptomator/common/vaults/FuseVolume.java b/src/main/java/org/cryptomator/common/vaults/FuseVolume.java index a1579fdaf..0321cfa0f 100644 --- a/src/main/java/org/cryptomator/common/vaults/FuseVolume.java +++ b/src/main/java/org/cryptomator/common/vaults/FuseVolume.java @@ -4,6 +4,7 @@ import com.google.common.collect.Iterators; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.mountpoint.InvalidMountPointException; import org.cryptomator.common.mountpoint.MountPointChooser; +import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.settings.VolumeImpl; import org.cryptomator.cryptofs.CryptoFileSystem; import org.cryptomator.frontend.fuse.mount.EnvironmentVariables; @@ -28,11 +29,14 @@ public class FuseVolume extends AbstractVolume { private static final Logger LOG = LoggerFactory.getLogger(FuseVolume.class); private static final Pattern NON_WHITESPACE_OR_QUOTED = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); // Thanks to https://stackoverflow.com/a/366532 + private final VaultSettings vaultSettings; + private Mount mount; @Inject - public FuseVolume(@Named("orderedMountPointChoosers") Iterable choosers) { + public FuseVolume(VaultSettings vaultSettings, @Named("orderedMountPointChoosers") Iterable choosers) { super(choosers); + this.vaultSettings = vaultSettings; } @Override @@ -50,7 +54,7 @@ public class FuseVolume extends AbstractVolume { .withFileNameTranscoder(mounter.defaultFileNameTranscoder()) // .build(); this.mount = mounter.mount(root, envVars, onExitAction); - } catch ( FuseMountException | FuseNotSupportedException e) { + } catch (FuseMountException | FuseNotSupportedException e) { throw new VolumeException("Unable to mount Filesystem", e); } } @@ -119,7 +123,10 @@ public class FuseVolume extends AbstractVolume { @Override public MountPointRequirement getMountPointRequirement() { - return SystemUtils.IS_OS_WINDOWS ? MountPointRequirement.PARENT_NO_MOUNT_POINT : MountPointRequirement.EMPTY_MOUNT_POINT; + if (!SystemUtils.IS_OS_WINDOWS) { + return MountPointRequirement.EMPTY_MOUNT_POINT; + } + return this.vaultSettings.getWinDriveLetter().isPresent() ? MountPointRequirement.UNUSED_ROOT_DIR : MountPointRequirement.PARENT_NO_MOUNT_POINT; } public static boolean isSupportedStatic() { diff --git a/src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java b/src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java index 84a798e59..deec61e1a 100644 --- a/src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java +++ b/src/main/java/org/cryptomator/common/vaults/MountPointRequirement.java @@ -6,6 +6,11 @@ package org.cryptomator.common.vaults; */ public enum MountPointRequirement { + /** + * The Mountpoint needs to be a filesystem root and must not exist. + */ + UNUSED_ROOT_DIR, + /** * No Mountpoint on the local filesystem required. (e.g. WebDAV) */ diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java index fd85db988..234bac65b 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockInvalidMountPointController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.unlock; +import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.vaults.MountPointRequirement; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; @@ -32,12 +33,24 @@ public class UnlockInvalidMountPointController implements FxController { return vault.getVaultSettings().getCustomMountPath().orElse("AUTO"); } - public boolean getMustExist() { - MountPointRequirement requirement = vault.getVolume().orElseThrow(() -> new IllegalStateException("Invalid Mountpoint without a Volume?!")).getMountPointRequirement(); - assert requirement != MountPointRequirement.NONE; //An invalid MountPoint with no required MountPoint doesn't seem sensible - assert requirement != MountPointRequirement.PARENT_OPT_MOUNT_POINT; //Not implemented anywhere (yet) - - return requirement == MountPointRequirement.EMPTY_MOUNT_POINT; + public boolean getNotExisting() { + return getMountPointRequirement() == MountPointRequirement.EMPTY_MOUNT_POINT; } -} + public boolean getExisting() { + return getMountPointRequirement() == MountPointRequirement.PARENT_NO_MOUNT_POINT; + } + + public boolean getDriveLetterOccupied() { + return getMountPointRequirement() == MountPointRequirement.UNUSED_ROOT_DIR; + } + + private MountPointRequirement getMountPointRequirement() { + var requirement = vault.getVolume().orElseThrow(() -> new IllegalStateException("Invalid Mountpoint without a Volume?!")).getMountPointRequirement(); + assert requirement != MountPointRequirement.NONE; //An invalid MountPoint with no required MountPoint doesn't seem sensible + assert requirement != MountPointRequirement.PARENT_OPT_MOUNT_POINT; //Not implemented anywhere (yet) + assert requirement != MountPointRequirement.UNUSED_ROOT_DIR || SystemUtils.IS_OS_WINDOWS; //Not implemented anywhere, but on Windows + + return requirement; + } +} \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index 073258d80..6964c3c86 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java @@ -2,6 +2,7 @@ package org.cryptomator.ui.unlock; import com.google.common.base.Throwables; import dagger.Lazy; +import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.mountpoint.InvalidMountPointException; import org.cryptomator.common.vaults.MountPointRequirement; import org.cryptomator.common.vaults.Vault; @@ -79,9 +80,10 @@ public class UnlockWorkflow extends Task { } private void handleInvalidMountPoint(InvalidMountPointException impExc) { - MountPointRequirement requirement = vault.getVolume().orElseThrow(() -> new IllegalStateException("Invalid Mountpoint without a Volume?!", impExc)).getMountPointRequirement(); + var requirement = vault.getVolume().orElseThrow(() -> new IllegalStateException("Invalid Mountpoint without a Volume?!", impExc)).getMountPointRequirement(); assert requirement != MountPointRequirement.NONE; //An invalid MountPoint with no required MountPoint doesn't seem sensible assert requirement != MountPointRequirement.PARENT_OPT_MOUNT_POINT; //Not implemented anywhere (yet) + assert requirement != MountPointRequirement.UNUSED_ROOT_DIR || SystemUtils.IS_OS_WINDOWS; //Not implemented anywhere, but on Windows Throwable cause = impExc.getCause(); // TODO: apply https://openjdk.java.net/jeps/8213076 in future JDK versions @@ -93,7 +95,11 @@ public class UnlockWorkflow extends Task { } showInvalidMountPointScene(); } else if (cause instanceof FileAlreadyExistsException) { - LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage()); + if (requirement == MountPointRequirement.UNUSED_ROOT_DIR) { + LOG.error("Unlock failed. Drive Letter already in use: {}", cause.getMessage()); + } else { + LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage()); + } showInvalidMountPointScene(); } else if (cause instanceof DirectoryNotEmptyException) { LOG.error("Unlock failed. Mountpoint not an empty directory: {}", cause.getMessage()); diff --git a/src/main/resources/fxml/unlock_invalid_mount_point.fxml b/src/main/resources/fxml/unlock_invalid_mount_point.fxml index 253ff5704..062981304 100644 --- a/src/main/resources/fxml/unlock_invalid_mount_point.fxml +++ b/src/main/resources/fxml/unlock_invalid_mount_point.fxml @@ -29,8 +29,9 @@ - - + + + diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index 63e2bbecf..36932b877 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -116,6 +116,7 @@ unlock.error.heading=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. # Lock ## Force