From a72ea1b9f559fdf22ad0418d05e9e44f2fa60d62 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 13 Jan 2023 11:20:23 +0100 Subject: [PATCH] move mount prepartions to own class --- .../org/cryptomator/common/mount/Mounter.java | 108 ++++++++++++++++ .../org/cryptomator/common/vaults/Vault.java | 119 +++--------------- 2 files changed, 126 insertions(+), 101 deletions(-) create mode 100644 src/main/java/org/cryptomator/common/mount/Mounter.java diff --git a/src/main/java/org/cryptomator/common/mount/Mounter.java b/src/main/java/org/cryptomator/common/mount/Mounter.java new file mode 100644 index 000000000..968e889d7 --- /dev/null +++ b/src/main/java/org/cryptomator/common/mount/Mounter.java @@ -0,0 +1,108 @@ +package org.cryptomator.common.mount; + +import org.cryptomator.common.Environment; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.settings.VaultSettings; +import org.cryptomator.integrations.mount.Mount; +import org.cryptomator.integrations.mount.MountFailedException; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javafx.beans.value.ObservableValue; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER; +import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR; +import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_SYSTEM_CHOSEN_PATH; +import static org.cryptomator.integrations.mount.MountCapability.MOUNT_WITHIN_EXISTING_PARENT; +import static org.cryptomator.integrations.mount.MountCapability.UNMOUNT_FORCED; + +@Singleton +public class Mounter { + + private final Settings settings; + private final Environment env; + private final WindowsDriveLetters driveLetters; + private final ObservableValue mountService; + + @Inject + public Mounter(Settings settings, Environment env, WindowsDriveLetters driveLetters, ObservableValue mountService) { + this.settings = settings; + this.env = env; + this.driveLetters = driveLetters; + this.mountService = mountService; + } + + public MountHandle mountAndcreateHandle(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException { + var mountService = this.mountService.getValue().service(); + var builder = mountService.forFileSystem(cryptoFsRoot); + boolean mountWithinParent = false; + + for (var capability : mountService.capabilities()) { + switch (capability) { + case FILE_SYSTEM_NAME -> builder.setFileSystemName("crypto"); + case LOOPBACK_PORT -> builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault) + case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName); + case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get()); + case MOUNT_FLAGS -> builder.setMountFlags(Objects.requireNonNullElse(vaultSettings.mountFlags().getValue(), mountService.getDefaultMountFlags())); + case VOLUME_ID -> builder.setVolumeId(vaultSettings.getId()); + case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get()); + } + } + + //TODO: refactor logic to own method + 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 (canMountToDriveLetter) { + builder.setMountpoint(driveLetters.getFirstDesiredAvailable().orElseThrow()); //TODO: catch exception + } else if (canMountToParent) { + Files.createDirectories(defaultMountPointBase); + builder.setMountpoint(defaultMountPointBase); + } else if (canMountToDir) { + var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get()); + Files.createDirectories(mountPoint); + builder.setMountpoint(mountPoint); + } + } else { + mountWithinParent = canMountToParent && !canMountToDir; + if(mountWithinParent) { + // TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT + } + try { + builder.setMountpoint(userChosenMountPoint); + } catch (IllegalArgumentException e) { + 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 new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), mountWithinParent); + } + + public record MountHandle(Mount mount, boolean supportsUnmountForced, boolean mountWithinParent) { + + } +} diff --git a/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java index adbe3e636..6356500c9 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -11,13 +11,8 @@ package org.cryptomator.common.vaults; import com.google.common.base.Strings; 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.Mounter; import org.cryptomator.common.mount.WindowsDriveLetters; -import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystem; import org.cryptomator.cryptofs.CryptoFileSystemProperties; @@ -27,11 +22,7 @@ import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; -import org.cryptomator.integrations.mount.Mount; -import org.cryptomator.integrations.mount.MountBuilder; -import org.cryptomator.integrations.mount.MountCapability; import org.cryptomator.integrations.mount.MountFailedException; -import org.cryptomator.integrations.mount.MountService; import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.mount.UnmountFailedException; import org.slf4j.Logger; @@ -48,9 +39,7 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.value.ObservableValue; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumSet; @@ -58,11 +47,6 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import static org.cryptomator.integrations.mount.MountCapability.MOUNT_AS_DRIVE_LETTER; -import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_EXISTING_DIR; -import static org.cryptomator.integrations.mount.MountCapability.MOUNT_TO_SYSTEM_CHOSEN_PATH; -import static org.cryptomator.integrations.mount.MountCapability.MOUNT_WITHIN_EXISTING_PARENT; - @PerVault public class Vault { @@ -70,13 +54,10 @@ public class Vault { private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME); private static final int UNLIMITED_FILENAME_LENGTH = Integer.MAX_VALUE; - private final Environment env; - private final Settings settings; private final VaultSettings vaultSettings; private final AtomicReference cryptoFileSystem; private final VaultState state; private final ObjectProperty lastKnownException; - private final ObservableValue mountService; private final VaultConfigCache configCache; private final VaultStats stats; private final StringBinding displayablePath; @@ -88,20 +69,18 @@ public class Vault { private final BooleanBinding unknownError; private final ObjectBinding mountPoint; private final WindowsDriveLetters windowsDriveLetters; + private final Mounter mounter; private final BooleanProperty showingStats; - private AtomicReference mountHandle = new AtomicReference<>(null); + private AtomicReference mountHandle = new AtomicReference<>(null); @Inject - Vault(Environment env, Settings settings, VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, ObservableValue mountService, VaultStats stats, WindowsDriveLetters windowsDriveLetters) { - this.env = env; - this.settings = settings; + Vault(VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats, WindowsDriveLetters windowsDriveLetters, Mounter mounter) { this.vaultSettings = vaultSettings; this.configCache = configCache; this.cryptoFileSystem = cryptoFileSystem; this.state = state; this.lastKnownException = lastKnownException; - this.mountService = mountService; this.stats = stats; this.displayablePath = Bindings.createStringBinding(this::getDisplayablePath, vaultSettings.path()); this.locked = Bindings.createBooleanBinding(this::isLocked, state); @@ -112,6 +91,7 @@ public class Vault { this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state); this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state); this.windowsDriveLetters = windowsDriveLetters; + this.mounter = mounter; this.showingStats = new SimpleBooleanProperty(false); } @@ -161,67 +141,6 @@ public class Vault { } } - private MountBuilder prepareMount(MountService mountService, Path cryptoRoot) throws IOException { - var builder = mountService.forFileSystem(cryptoRoot); - - for (var capability : mountService.capabilities()) { - switch (capability) { - case FILE_SYSTEM_NAME -> builder.setFileSystemName("crypto"); - case LOOPBACK_PORT -> builder.setLoopbackPort(settings.port().get()); //TODO: move port from settings to vaultsettings (see https://github.com/cryptomator/cryptomator/tree/feature/mount-setting-per-vault) - case LOOPBACK_HOST_NAME -> env.getLoopbackAlias().ifPresent(builder::setLoopbackHostName); - case READ_ONLY -> builder.setReadOnly(vaultSettings.usesReadOnlyMode().get()); - case MOUNT_FLAGS -> builder.setMountFlags(Objects.requireNonNullElse(vaultSettings.mountFlags().getValue(), mountService.getDefaultMountFlags())); - case VOLUME_ID -> builder.setVolumeId(vaultSettings.getId()); - case VOLUME_NAME -> builder.setVolumeName(vaultSettings.mountName().get()); - } - } - - 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 (canMountToDriveLetter) { - builder.setMountpoint(windowsDriveLetters.getFirstDesiredAvailable().orElseThrow()); - } else if (canMountToParent) { - Files.createDirectories(defaultMountPointBase); - builder.setMountpoint(defaultMountPointBase); - } else if (canMountToDir) { - var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName().get()); - Files.createDirectories(mountPoint); - builder.setMountpoint(mountPoint); - } - } else { - // TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT? - 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; - } - public synchronized void unlock(MasterkeyLoader keyLoader) throws CryptoException, IOException, MountFailedException { if (cryptoFileSystem.get() != null) { throw new IllegalStateException("Already unlocked."); @@ -231,9 +150,7 @@ public class Vault { try { cryptoFileSystem.set(fs); var rootPath = fs.getRootDirectories().iterator().next(); - var actualMountService = mountService.getValue().service(); - var supportsForcedUnmount = actualMountService.hasCapability(MountCapability.UNMOUNT_FORCED); - var mountHandle = new MountHandle(prepareMount(actualMountService, rootPath).mount(), supportsForcedUnmount); + var mountHandle = mounter.mountAndcreateHandle(vaultSettings, rootPath); success = this.mountHandle.compareAndSet(null, mountHandle); } finally { if (!success) { @@ -242,7 +159,6 @@ public class Vault { } } - public synchronized void lock(boolean forced) throws UnmountFailedException, IOException { var mountHandle = this.mountHandle.get(); if (mountHandle == null) { @@ -250,14 +166,17 @@ public class Vault { return; } - if (forced && mountHandle.supportsUnmountForced) { - mountHandle.mount.unmountForced(); + if (forced && mountHandle.supportsUnmountForced()) { + mountHandle.mount().unmountForced(); } else { - mountHandle.mount.unmount(); + mountHandle.mount().unmount(); } try { - mountHandle.mount.close(); + mountHandle.mount().close(); + if(mountHandle.mountWithinParent()) { + //TODO: cleanup + } } finally { destroyCryptoFileSystem(); } @@ -352,7 +271,7 @@ public class Vault { public Mountpoint getMountPoint() { var handle = mountHandle.get(); - return handle == null ? null : handle.mount.getMountpoint(); + return handle == null ? null : handle.mount().getMountpoint(); } public StringBinding displayablePathProperty() { @@ -434,16 +353,14 @@ public class Vault { } } - public boolean supportsForcedUnmount() { var mh = mountHandle.get(); - if(mh == null) { + if (mh == null) { + //TODO: or return false? throw new IllegalStateException("Vault is not mounted"); - }; + } + ; return mountHandle.get().supportsUnmountForced(); } - private record MountHandle(Mount mount, boolean supportsUnmountForced) { - - } } \ No newline at end of file