mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-20 03:31:27 +00:00
Merge branch 'develop' into feature/hub
# Conflicts: # .github/workflows/release.yml # pom.xml # src/main/resources/license/THIRD-PARTY.txt
This commit is contained in:
@@ -49,6 +49,12 @@ public class KeychainManager implements KeychainAccessProvider {
|
||||
setPassphraseStored(key, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
|
||||
getKeychainOrFail().storePassphrase(key, displayName, passphrase);
|
||||
setPassphraseStored(key, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] loadPassphrase(String key) throws KeychainAccessException {
|
||||
char[] passphrase = getKeychainOrFail().loadPassphrase(key);
|
||||
@@ -70,6 +76,14 @@ public class KeychainManager implements KeychainAccessProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
|
||||
if (isPassphraseStored(key)) {
|
||||
getKeychainOrFail().changePassphrase(key, displayName, passphrase);
|
||||
setPassphraseStored(key, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return keychain.getValue() != null;
|
||||
|
||||
@@ -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<Path> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ class MountPointHelper {
|
||||
private void clearIrregularUnmountDebris(Path dirContainingMountPoints) {
|
||||
IOException cleanupFailed = new IOException("Cleanup failed");
|
||||
|
||||
try {
|
||||
try (var ds = Files.newDirectoryStream(dirContainingMountPoints)) {
|
||||
LOG.debug("Performing cleanup of mountpoint dir {}.", dirContainingMountPoints);
|
||||
for (Path p : Files.newDirectoryStream(dirContainingMountPoints)) {
|
||||
for (Path p : ds) {
|
||||
try {
|
||||
var attr = Files.readAttributes(p, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
|
||||
if (attr.isOther() && attr.isDirectory()) { // yes, this is possible with windows junction points -.-
|
||||
@@ -113,8 +113,10 @@ class MountPointHelper {
|
||||
}
|
||||
|
||||
private void ensureIsEmpty(Path dir) throws IOException {
|
||||
if (Files.newDirectoryStream(dir).iterator().hasNext()) {
|
||||
throw new DirectoryNotEmptyException(dir.toString());
|
||||
try (var ds = Files.newDirectoryStream(dir)) {
|
||||
if (ds.iterator().hasNext()){
|
||||
throw new DirectoryNotEmptyException(dir.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<MountPointChooser> choosers) {
|
||||
public FuseVolume(VaultSettings vaultSettings, @Named("orderedMountPointChoosers") Iterable<MountPointChooser> 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() {
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -7,9 +7,11 @@ import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.UnixDomainSocketAddress;
|
||||
import java.nio.channels.AlreadyBoundException;
|
||||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.UnsupportedAddressTypeException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -29,10 +31,18 @@ class Server implements IpcCommunicator {
|
||||
public static Server create(Path socketPath) throws IOException {
|
||||
Files.createDirectories(socketPath.getParent());
|
||||
var address = UnixDomainSocketAddress.of(socketPath);
|
||||
var serverSocketChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
|
||||
serverSocketChannel.bind(address);
|
||||
LOG.info("Spawning IPC server listening on socket {}", socketPath);
|
||||
return new Server(serverSocketChannel, socketPath);
|
||||
ServerSocketChannel ch = null;
|
||||
try {
|
||||
ch = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
|
||||
ch.bind(address);
|
||||
LOG.info("Spawning IPC server listening on socket {}", socketPath);
|
||||
return new Server(ch, socketPath);
|
||||
} catch (IOException | AlreadyBoundException | UnsupportedAddressTypeException e) {
|
||||
if (ch != null) {
|
||||
ch.close();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,8 +79,9 @@ public class LoggerModule {
|
||||
@Singleton
|
||||
@Named("fileAppender")
|
||||
static Appender<ILoggingEvent> provideFileAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) {
|
||||
if (environment.getLogDir().isPresent()) {
|
||||
Path logDir = environment.getLogDir().get();
|
||||
var optionalLogDir = environment.getLogDir();
|
||||
if (optionalLogDir.isPresent()) {
|
||||
Path logDir = optionalLogDir.get();
|
||||
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
|
||||
appender.setContext(context);
|
||||
appender.setFile(logDir.resolve(LOGFILE_NAME).toString());
|
||||
@@ -110,9 +111,10 @@ public class LoggerModule {
|
||||
@Singleton
|
||||
@Named("upgradeAppender")
|
||||
static Appender<ILoggingEvent> provideUpgradeAppender(LoggerContext context, PatternLayoutEncoder encoder, Environment environment) {
|
||||
if (environment.getLogDir().isPresent()) {
|
||||
var optionalLogDir = environment.getLogDir();
|
||||
if (optionalLogDir.isPresent()) {
|
||||
FileAppender<ILoggingEvent> appender = new FileAppender<>();
|
||||
appender.setFile(environment.getLogDir().get().resolve(UPGRADE_FILENAME).toString());
|
||||
appender.setFile(optionalLogDir.get().resolve(UPGRADE_FILENAME).toString());
|
||||
appender.setContext(context);
|
||||
appender.setEncoder(encoder);
|
||||
appender.start();
|
||||
|
||||
@@ -102,7 +102,7 @@ public class ChangePasswordController implements FxController {
|
||||
private void updatePasswordInSystemkeychain() {
|
||||
if (keychain.isSupported() && !keychain.isLocked()) {
|
||||
try {
|
||||
keychain.changePassphrase(vault.getId(), newPasswordController.passwordField.getCharacters());
|
||||
keychain.changePassphrase(vault.getId(), vault.getDisplayName(), newPasswordController.passwordField.getCharacters());
|
||||
LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayName());
|
||||
} catch (KeychainAccessException e) {
|
||||
LOG.error("Failed to update password in system keychain.", e);
|
||||
|
||||
@@ -4,30 +4,35 @@ import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class UserInteractionLock<E extends Enum> {
|
||||
public class UserInteractionLock<E extends Enum<E>> {
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private final Condition condition = lock.newCondition();
|
||||
private final BooleanProperty awaitingInteraction = new SimpleBooleanProperty();
|
||||
private volatile E state;
|
||||
private final AtomicBoolean interacted = new AtomicBoolean();
|
||||
private final AtomicReference<E> state;
|
||||
|
||||
public UserInteractionLock(E initialValue) {
|
||||
this.state = initialValue;
|
||||
this.state = new AtomicReference<>(initialValue);
|
||||
}
|
||||
|
||||
public synchronized void reset(E value) {
|
||||
this.state = value;
|
||||
state.set(value);
|
||||
interacted.set(false);
|
||||
}
|
||||
|
||||
public void interacted(E result) {
|
||||
assert Platform.isFxApplicationThread();
|
||||
lock.lock();
|
||||
try {
|
||||
state = result;
|
||||
state.set(result);
|
||||
interacted.set(true);
|
||||
awaitingInteraction.set(false);
|
||||
condition.signal();
|
||||
} finally {
|
||||
@@ -40,8 +45,10 @@ public class UserInteractionLock<E extends Enum> {
|
||||
lock.lock();
|
||||
try {
|
||||
Platform.runLater(() -> awaitingInteraction.set(true));
|
||||
condition.await();
|
||||
return state;
|
||||
while (!interacted.get()) {
|
||||
condition.await();
|
||||
}
|
||||
return state.get();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class UpdateCheckerTask extends Task<String> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerTask.class);
|
||||
|
||||
private static final long MAX_RESPONSE_SIZE = 10 * 1024; // 10kb should be sufficient. protect against flooding
|
||||
private static final long MAX_RESPONSE_SIZE = 10L * 1024; // 10kb should be sufficient. protect against flooding
|
||||
private static final Gson GSON = new GsonBuilder().setLenient().create();
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
@@ -47,7 +47,7 @@ class MasterkeyFileLoadingFinisher {
|
||||
private void savePasswordToSystemkeychain() {
|
||||
if (keychain.isSupported()) {
|
||||
try {
|
||||
keychain.storePassphrase(vault.getId(), CharBuffer.wrap(enteredPassword.get()));
|
||||
keychain.storePassphrase(vault.getId(), vault.getDisplayName(), CharBuffer.wrap(enteredPassword.get()));
|
||||
} catch (KeychainAccessException e) {
|
||||
LOG.error("Failed to store passphrase in system keychain.", e);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -93,15 +95,27 @@ public abstract class MasterkeyFileLoadingModule {
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideUnlockScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.UNLOCK_ENTER_PASSWORD);
|
||||
static Scene provideUnlockScene(@KeyLoading FxmlLoaderFactory fxmlLoaders, @KeyLoading Stage window, @KeyLoading Vault v, ResourceBundle resourceBundle) {
|
||||
var scene = fxmlLoaders.createScene(FxmlFile.UNLOCK_ENTER_PASSWORD);
|
||||
scene.windowProperty().addListener((prop, oldVal, newVal) -> {
|
||||
if (window.equals(newVal)) {
|
||||
window.setTitle(String.format(resourceBundle.getString("unlock.title"), v.getDisplayName()));
|
||||
}
|
||||
});
|
||||
return scene;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideUnlockSelectMasterkeyFileScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE);
|
||||
static Scene provideUnlockSelectMasterkeyFileScene(@KeyLoading FxmlLoaderFactory fxmlLoaders, @KeyLoading Stage window, @KeyLoading Vault v, ResourceBundle resourceBundle) {
|
||||
var scene = fxmlLoaders.createScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE);
|
||||
scene.windowProperty().addListener((prop, oldVal, newVal) -> {
|
||||
if (window.equals(newVal)) {
|
||||
window.setTitle(String.format(resourceBundle.getString("unlock.chooseMasterkey.title"), v.getDisplayName()));
|
||||
}
|
||||
});
|
||||
return scene;
|
||||
}
|
||||
|
||||
@Binds
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.cryptomator.ui.keyloading.masterkeyfile;
|
||||
import com.google.common.base.Preconditions;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.common.BackupHelper;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
@@ -20,6 +21,7 @@ import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.file.Files;
|
||||
@@ -61,14 +63,24 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
||||
@Override
|
||||
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
|
||||
Preconditions.checkArgument(SCHEME.equalsIgnoreCase(keyId.getScheme()), "Only supports keys with scheme " + SCHEME);
|
||||
|
||||
try {
|
||||
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
|
||||
if (!Files.exists(filePath)) {
|
||||
filePath = getAlternateMasterkeyFilePath();
|
||||
}
|
||||
CharSequence passphrase = getPassphrase();
|
||||
return masterkeyFileAccess.load(filePath, passphrase);
|
||||
var masterkey = masterkeyFileAccess.load(filePath, passphrase);
|
||||
//backup
|
||||
if (filePath.startsWith(vault.getPath())) {
|
||||
try {
|
||||
BackupHelper.attemptBackup(filePath);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Unable to create backup for masterkey file.");
|
||||
}
|
||||
} else {
|
||||
LOG.info("Masterkey file not stored inside vault. Not creating a backup.");
|
||||
}
|
||||
return masterkey;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new UnlockCancelledException("Unlock interrupted", e);
|
||||
|
||||
@@ -127,10 +127,10 @@ public class VaultStatisticsController implements FxController {
|
||||
encryptedBytesWrite.getData().add(new Data<>(currentStep, encBytes));
|
||||
|
||||
// adjust ranges:
|
||||
readChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS);
|
||||
readChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS * 1.0);
|
||||
readChartXAxis.setUpperBound(currentStep);
|
||||
readChartYAxis.setUpperBound(allTimeMax);
|
||||
writeChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS);
|
||||
writeChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS * 1.0);
|
||||
writeChartXAxis.setUpperBound(currentStep);
|
||||
writeChartYAxis.setUpperBound(allTimeMax);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<Boolean> {
|
||||
}
|
||||
|
||||
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<Boolean> {
|
||||
}
|
||||
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());
|
||||
|
||||
Reference in New Issue
Block a user