mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-22 20:51:27 +00:00
Allow custom mount point for winfsp
This commit is contained in:
@@ -4,6 +4,7 @@ import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.settings.VolumeImpl;
|
||||
import org.cryptomator.common.vaults.MountPointRequirement;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -12,9 +13,7 @@ import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -35,7 +34,6 @@ class CustomMountPointChooser implements MountPointChooser {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Volume caller) {
|
||||
//Disable if useExperimentalFuse is required (Win + Fuse), but set to false
|
||||
return caller.getImplementationType() != VolumeImpl.FUSE || !SystemUtils.IS_OS_WINDOWS || environment.useExperimentalFuse();
|
||||
}
|
||||
|
||||
@@ -48,8 +46,16 @@ class CustomMountPointChooser implements MountPointChooser {
|
||||
@Override
|
||||
public boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
|
||||
switch (caller.getMountPointRequirement()) {
|
||||
case PARENT_NO_MOUNT_POINT -> prepareParentNoMountPoint(mountPoint);
|
||||
case EMPTY_MOUNT_POINT -> prepareEmptyMountPoint(mountPoint);
|
||||
case PARENT_NO_MOUNT_POINT -> {
|
||||
prepareParentNoMountPoint(mountPoint);
|
||||
LOG.debug("Successfully checked custom mount point: {}", mountPoint);
|
||||
return true;
|
||||
}
|
||||
case EMPTY_MOUNT_POINT -> {
|
||||
prepareEmptyMountPoint(mountPoint);
|
||||
LOG.debug("Successfully checked custom mount point: {}", mountPoint);
|
||||
return false;
|
||||
}
|
||||
case NONE -> {
|
||||
//Requirement "NONE" doesn't make any sense here.
|
||||
//No need to prepare/verify a Mountpoint without requiring one...
|
||||
@@ -60,21 +66,26 @@ class CustomMountPointChooser implements MountPointChooser {
|
||||
throw new InvalidMountPointException(new IllegalStateException("Not implemented"));
|
||||
}
|
||||
}
|
||||
LOG.debug("Successfully checked custom mount point: {}", mountPoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void prepareParentNoMountPoint(Path mountPoint) throws InvalidMountPointException {
|
||||
//This the case on Windows when using FUSE
|
||||
//See https://github.com/billziss-gh/winfsp/issues/320
|
||||
Path parent = mountPoint.getParent();
|
||||
if (!Files.isDirectory(parent)) {
|
||||
throw new InvalidMountPointException(new NotDirectoryException(parent.toString()));
|
||||
}
|
||||
//We must use #notExists() here because notExists =/= !exists (see docs)
|
||||
if (!Files.notExists(mountPoint, LinkOption.NOFOLLOW_LINKS)) {
|
||||
//File exists OR can't be determined
|
||||
throw new InvalidMountPointException(new FileAlreadyExistsException(mountPoint.toString()));
|
||||
assert SystemUtils.IS_OS_WINDOWS;
|
||||
|
||||
Path hideaway = getHideaway(mountPoint);
|
||||
if (Files.exists(hideaway)) {
|
||||
LOG.info("Mountpoint {} for winfsp mount seems to be not properly cleaned up. Will be fixed on unmount.");
|
||||
} else if (!Files.isDirectory(mountPoint)) {
|
||||
throw new InvalidMountPointException(new NotDirectoryException(mountPoint.toString())); //simulate we need a directory
|
||||
} else {
|
||||
//TODO: should we require it to be empty?
|
||||
try {
|
||||
Files.move(mountPoint, hideaway);
|
||||
Files.setAttribute(hideaway, "dos:hidden", true);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidMountPointException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,4 +103,21 @@ class CustomMountPointChooser implements MountPointChooser {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(Volume caller, Path mountPoint) {
|
||||
if (VolumeImpl.FUSE == caller.getImplementationType() && MountPointRequirement.PARENT_NO_MOUNT_POINT == caller.getMountPointRequirement()) {
|
||||
Path hideaway = getHideaway(mountPoint);
|
||||
try {
|
||||
Files.move(hideaway, mountPoint);
|
||||
Files.setAttribute(mountPoint, "dos:hidden", false);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to clean up mountpoint {} for Winfsp mounting.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Path getHideaway(Path mountPoint) {
|
||||
return mountPoint.resolveSibling(mountPoint.getFileName().toString() + "_tmp");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@ import org.cryptomator.ui.common.FxController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -32,18 +29,16 @@ import java.nio.file.Path;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* TODO: if WebDav is selected on a windows system, custom mount directory is _not_ supported. This is currently not indicated/shown/etc in the ui
|
||||
*/
|
||||
@VaultOptionsScoped
|
||||
public class MountOptionsController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final BooleanProperty osIsWindows = new SimpleBooleanProperty(SystemUtils.IS_OS_WINDOWS);
|
||||
private final BooleanBinding webDavAndWindows;
|
||||
private final boolean webDavAndWindows;
|
||||
private final boolean fuseAndWindows;
|
||||
private final WindowsDriveLetters windowsDriveLetters;
|
||||
private final ResourceBundle resourceBundle;
|
||||
|
||||
public CheckBox readOnlyCheckbox;
|
||||
public CheckBox customMountFlagsCheckbox;
|
||||
public TextField mountFlags;
|
||||
@@ -53,20 +48,14 @@ public class MountOptionsController implements FxController {
|
||||
public RadioButton mountPointCustomDir;
|
||||
public ChoiceBox<String> driveLetterSelection;
|
||||
|
||||
//FUSE + Windows -> Disable some (experimental) features for the user because they are unstable
|
||||
//Use argument Dfuse.experimental="true" to override
|
||||
private final BooleanBinding restrictToStableFuseOnWindows;
|
||||
|
||||
@Inject
|
||||
MountOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, Settings settings, WindowsDriveLetters windowsDriveLetters, ResourceBundle resourceBundle, Environment environment) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.webDavAndWindows = settings.preferredVolumeImpl().isEqualTo(VolumeImpl.WEBDAV).and(osIsWindows);
|
||||
this.webDavAndWindows = settings.preferredVolumeImpl().get() == VolumeImpl.WEBDAV && SystemUtils.IS_OS_WINDOWS;
|
||||
this.fuseAndWindows = settings.preferredVolumeImpl().get() == VolumeImpl.FUSE && SystemUtils.IS_OS_WINDOWS;
|
||||
this.windowsDriveLetters = windowsDriveLetters;
|
||||
this.resourceBundle = resourceBundle;
|
||||
|
||||
BooleanBinding isFuseOnWindows = settings.preferredVolumeImpl().isEqualTo(VolumeImpl.FUSE).and(osIsWindows);
|
||||
this.restrictToStableFuseOnWindows = isFuseOnWindows.and(new SimpleBooleanProperty(!environment.useExperimentalFuse())); //Is FUSE on Win and is NOT experimental fuse enabled
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -74,10 +63,11 @@ public class MountOptionsController implements FxController {
|
||||
|
||||
// readonly:
|
||||
readOnlyCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().usesReadOnlyMode());
|
||||
if (getRestrictToStableFuseOnWindows()) {
|
||||
//TODO: support this feature on Windows
|
||||
if (fuseAndWindows) {
|
||||
readOnlyCheckbox.setSelected(false); // to prevent invalid states
|
||||
readOnlyCheckbox.setDisable(true);
|
||||
}
|
||||
readOnlyCheckbox.disableProperty().bind(customMountFlagsCheckbox.selectedProperty().or(restrictToStableFuseOnWindows));
|
||||
|
||||
// custom mount flags:
|
||||
mountFlags.disableProperty().bind(customMountFlagsCheckbox.selectedProperty().not());
|
||||
@@ -95,9 +85,7 @@ public class MountOptionsController implements FxController {
|
||||
driveLetterSelection.setConverter(new WinDriveLetterLabelConverter(windowsDriveLetters, resourceBundle));
|
||||
driveLetterSelection.setValue(vault.getVaultSettings().winDriveLetter().get());
|
||||
|
||||
if (vault.getVaultSettings().useCustomMountPath().get()
|
||||
&& vault.getVaultSettings().getCustomMountPath().isPresent()
|
||||
&& !getRestrictToStableFuseOnWindows() /* to prevent invalid states */) {
|
||||
if (vault.getVaultSettings().useCustomMountPath().get() && vault.getVaultSettings().getCustomMountPath().isPresent()) {
|
||||
mountPoint.selectToggle(mountPointCustomDir);
|
||||
} else if (!Strings.isNullOrEmpty(vault.getVaultSettings().winDriveLetter().get())) {
|
||||
mountPoint.selectToggle(mountPointWinDriveLetter);
|
||||
@@ -188,20 +176,16 @@ public class MountOptionsController implements FxController {
|
||||
|
||||
// Getter & Setter
|
||||
|
||||
public BooleanProperty osIsWindowsProperty() {
|
||||
return osIsWindows;
|
||||
}
|
||||
|
||||
public boolean getOsIsWindows() {
|
||||
return osIsWindows.get();
|
||||
return SystemUtils.IS_OS_WINDOWS;
|
||||
}
|
||||
|
||||
public BooleanBinding webDavAndWindowsProperty() {
|
||||
public boolean getCustomMountPointSupported() {
|
||||
return webDavAndWindows;
|
||||
}
|
||||
|
||||
public boolean isWebDavAndWindows() {
|
||||
return webDavAndWindows.get();
|
||||
public boolean getReadOnlySupported() {
|
||||
return fuseAndWindows;
|
||||
}
|
||||
|
||||
public StringProperty customMountPathProperty() {
|
||||
@@ -212,8 +196,4 @@ public class MountOptionsController implements FxController {
|
||||
return vault.getVaultSettings().customMountPath().get();
|
||||
}
|
||||
|
||||
public Boolean getRestrictToStableFuseOnWindows() {
|
||||
return restrictToStableFuseOnWindows.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user