Przeglądaj źródła

Implemented some mount options (references #931)

Sebastian Stenzel 5 lat temu
rodzic
commit
17f45c6dab

+ 14 - 8
main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java

@@ -15,7 +15,6 @@ import javafx.beans.binding.Bindings;
 import javafx.beans.binding.BooleanBinding;
 import javafx.beans.binding.StringBinding;
 import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.ReadOnlyObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.SystemUtils;
@@ -45,7 +44,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Predicate;
-import java.util.function.Supplier;
 
 @PerVault
 public class Vault {
@@ -57,7 +55,7 @@ public class Vault {
 
 	private final VaultSettings vaultSettings;
 	private final Provider<Volume> volumeProvider;
-	private final Supplier<String> defaultMountFlags;
+	private final StringBinding defaultMountFlags;
 	private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
 	private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
 	private final StringBinding displayableName;
@@ -72,7 +70,7 @@ public class Vault {
 	}
 
 	@Inject
-	Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags Supplier<String> defaultMountFlags) {
+	Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags) {
 		this.vaultSettings = vaultSettings;
 		this.volumeProvider = volumeProvider;
 		this.defaultMountFlags = defaultMountFlags;
@@ -122,7 +120,7 @@ public class Vault {
 		}
 		CryptoFileSystem fs = getCryptoFileSystem(passphrase);
 		volume = volumeProvider.get();
-		volume.mount(fs, getMountFlags());
+		volume.mount(fs, getEffectiveMountFlags());
 	}
 
 	public synchronized void lock(boolean forced) throws Volume.VolumeException {
@@ -318,16 +316,24 @@ public class Vault {
 		return !Strings.isNullOrEmpty(vaultSettings.mountFlags().get());
 	}
 
-	public String getMountFlags() {
+	public StringBinding defaultMountFlagsProperty() {
+		return defaultMountFlags;
+	}
+
+	public String getDefaultMountFlags() {
+		return defaultMountFlags.get();
+	}
+
+	public String getEffectiveMountFlags() {
 		String mountFlags = vaultSettings.mountFlags().get();
 		if (Strings.isNullOrEmpty(mountFlags)) {
-			return defaultMountFlags.get();
+			return getDefaultMountFlags();
 		} else {
 			return mountFlags;
 		}
 	}
 
-	public void setMountFlags(String mountFlags) {
+	public void setCustomMountFlags(String mountFlags) {
 		vaultSettings.mountFlags().set(mountFlags);
 	}
 

+ 82 - 70
main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java

@@ -7,6 +7,13 @@ package org.cryptomator.common.vaults;
 
 import dagger.Module;
 import dagger.Provides;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.BooleanBinding;
+import javafx.beans.binding.StringBinding;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanWrapper;
+import javafx.beans.property.StringProperty;
 import org.apache.commons.lang3.SystemUtils;
 import org.cryptomator.common.settings.Settings;
 import org.cryptomator.common.settings.VaultSettings;
@@ -44,90 +51,95 @@ public class VaultModule {
 	@Provides
 	@PerVault
 	@DefaultMountFlags
-	public Supplier<String> provideDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
-		return () -> {
-			VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
-			switch (preferredImpl) {
-				case FUSE:
-					if (SystemUtils.IS_OS_MAC_OSX) {
-						return getMacFuseDefaultMountFlags(settings, vaultSettings);
-					} else if (SystemUtils.IS_OS_LINUX) {
-						return getLinuxFuseDefaultMountFlags(settings, vaultSettings);
-					}
-				case DOKANY:
-					return getDokanyDefaultMountFlags(settings, vaultSettings);
-				default:
-					return "--flags-supported-on-FUSE-or-DOKANY-only";
-			}
-		};
+	public StringBinding provideDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
+		BooleanBinding isMacFuse = new ReadOnlyBooleanWrapper(SystemUtils.IS_OS_MAC_OSX).and(settings.preferredVolumeImpl().isEqualTo(VolumeImpl.FUSE));
+		BooleanBinding isLinuxFuse = new ReadOnlyBooleanWrapper(SystemUtils.IS_OS_LINUX).and(settings.preferredVolumeImpl().isEqualTo(VolumeImpl.FUSE));
+		BooleanBinding isWinDokany = new ReadOnlyBooleanWrapper(SystemUtils.IS_OS_WINDOWS).and(settings.preferredVolumeImpl().isEqualTo(VolumeImpl.DOKANY));
+
+		return Bindings.when(isMacFuse) // IF isMacFuse
+				.then(getMacFuseDefaultMountFlags(vaultSettings)) //
+				.otherwise(Bindings.when(isLinuxFuse) // ELSE IF isLinuxFuse
+						.then(getLinuxFuseDefaultMountFlags(vaultSettings)) //
+						.otherwise(Bindings.when(isWinDokany) // ELSE IF isWinDokany
+								.then(getDokanyDefaultMountFlags(vaultSettings)) //
+								.otherwise("--flags-supported-on-FUSE-or-DOKANY-only") // ELSE
+						) //
+				);
 	}
 
 	// see: https://github.com/osxfuse/osxfuse/wiki/Mount-options
-	private String getMacFuseDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
+	private StringBinding getMacFuseDefaultMountFlags(VaultSettings vaultSettings) {
 		assert SystemUtils.IS_OS_MAC_OSX;
+		StringProperty mountName = vaultSettings.mountName();
+		BooleanProperty readOnly = vaultSettings.usesReadOnlyMode();
+		return Bindings.createStringBinding(() -> {
+			StringBuilder flags = new StringBuilder();
+			if (readOnly.get()) {
+				flags.append(" -ordonly");
+			}
+			flags.append(" -ovolname=").append(mountName.get());
+			flags.append(" -oatomic_o_trunc");
+			flags.append(" -oauto_xattr");
+			flags.append(" -oauto_cache");
+			flags.append(" -omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC"); // show files names in Unicode NFD encoding
+			flags.append(" -onoappledouble"); // vastly impacts performance for some reason...
+			flags.append(" -odefault_permissions"); // let the kernel assume permissions based on file attributes etc
+
+			try {
+				Path userHome = Paths.get(System.getProperty("user.home"));
+				int uid = (int) Files.getAttribute(userHome, "unix:uid");
+				int gid = (int) Files.getAttribute(userHome, "unix:gid");
+				flags.append(" -ouid=").append(uid);
+				flags.append(" -ogid=").append(gid);
+			} catch (IOException e) {
+				LOG.error("Could not read uid/gid from USER_HOME", e);
+			}
 
-		StringBuilder flags = new StringBuilder();
-		if (vaultSettings.usesReadOnlyMode().get()) {
-			flags.append(" -ordonly");
-		}
-		flags.append(" -ovolname=").append(vaultSettings.mountName().get());
-		flags.append(" -oatomic_o_trunc");
-		flags.append(" -oauto_xattr");
-		flags.append(" -oauto_cache");
-		flags.append(" -omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC"); // show files names in Unicode NFD encoding
-		flags.append(" -onoappledouble"); // vastly impacts performance for some reason...
-		flags.append(" -odefault_permissions"); // let the kernel assume permissions based on file attributes etc
-
-		try {
-			Path userHome = Paths.get(System.getProperty("user.home"));
-			int uid = (int) Files.getAttribute(userHome, "unix:uid");
-			int gid = (int) Files.getAttribute(userHome, "unix:gid");
-			flags.append(" -ouid=").append(uid);
-			flags.append(" -ogid=").append(gid);
-		} catch (IOException e) {
-			LOG.error("Could not read uid/gid from USER_HOME", e);
-		}
-
-		return flags.toString().strip();
+			return flags.toString().strip();
+		}, mountName, readOnly);
 	}
 
 	// see https://manpages.debian.org/testing/fuse/mount.fuse.8.en.html
-	private String getLinuxFuseDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
+	private StringBinding getLinuxFuseDefaultMountFlags(VaultSettings vaultSettings) {
 		assert SystemUtils.IS_OS_LINUX;
+		BooleanProperty readOnly = vaultSettings.usesReadOnlyMode();
+		return Bindings.createStringBinding(() -> {
+			StringBuilder flags = new StringBuilder();
+			if (readOnly.get()) {
+				flags.append(" -oro");
+			}
+			flags.append(" -oauto_unmount");
+
+			try {
+				Path userHome = Paths.get(System.getProperty("user.home"));
+				int uid = (int) Files.getAttribute(userHome, "unix:uid");
+				int gid = (int) Files.getAttribute(userHome, "unix:gid");
+				flags.append(" -ouid=").append(uid);
+				flags.append(" -ogid=").append(gid);
+			} catch (IOException e) {
+				LOG.error("Could not read uid/gid from USER_HOME", e);
+			}
 
-		StringBuilder flags = new StringBuilder();
-		if (vaultSettings.usesReadOnlyMode().get()) {
-			flags.append(" -oro");
-		}
-		flags.append(" -oauto_unmount");
-
-		try {
-			Path userHome = Paths.get(System.getProperty("user.home"));
-			int uid = (int) Files.getAttribute(userHome, "unix:uid");
-			int gid = (int) Files.getAttribute(userHome, "unix:gid");
-			flags.append(" -ouid=").append(uid);
-			flags.append(" -ogid=").append(gid);
-		} catch (IOException e) {
-			LOG.error("Could not read uid/gid from USER_HOME", e);
-		}
-
-		return flags.toString().strip();
+			return flags.toString().strip();
+		}, readOnly);
 	}
 
 	// see https://github.com/cryptomator/dokany-nio-adapter/blob/develop/src/main/java/org/cryptomator/frontend/dokany/MountUtil.java#L30-L34
-	private String getDokanyDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
+	private StringBinding getDokanyDefaultMountFlags(VaultSettings vaultSettings) {
 		assert SystemUtils.IS_OS_WINDOWS;
-
-		StringBuilder flags = new StringBuilder();
-		flags.append(" --options CURRENT_SESSION");
-		if (vaultSettings.usesReadOnlyMode().get()) {
-			flags.append(",WRITE_PROTECTION");
-		}
-		flags.append(" --thread-count 5");
-		flags.append(" --timeout 10000");
-		flags.append(" --allocation-unit-size 4096");
-		flags.append(" --sector-size 4096");
-		return flags.toString().strip();
+		BooleanProperty readOnly = vaultSettings.usesReadOnlyMode();
+		return Bindings.createStringBinding(() -> {
+			StringBuilder flags = new StringBuilder();
+			flags.append(" --options CURRENT_SESSION");
+			if (readOnly.get()) {
+				flags.append(",WRITE_PROTECTION");
+			}
+			flags.append(" --thread-count 5");
+			flags.append(" --timeout 10000");
+			flags.append(" --allocation-unit-size 4096");
+			flags.append(" --sector-size 4096");
+			return flags.toString().strip();
+		}, readOnly);
 	}
 
 }

+ 6 - 6
main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java

@@ -211,7 +211,7 @@ public class UnlockController implements ViewController {
 		downloadsPageLink.setManaged(false);
 		mountName.setText(vault.getMountName());
 		useCustomMountFlags.setSelected(vault.isHavingCustomMountFlags());
-		mountFlags.setText(vault.getMountFlags());
+		mountFlags.setText(vault.getEffectiveMountFlags());
 		savePassword.setSelected(false);
 		// auto-fill pw from keychain:
 		if (keychainAccess.isPresent()) {
@@ -321,28 +321,28 @@ public class UnlockController implements ViewController {
 			vault.setMountName(newValue);
 		}
 		if (!useCustomMountFlags.isSelected()) {
-			mountFlags.setText(vault.getMountFlags()); // update default flags
+			mountFlags.setText(vault.getEffectiveMountFlags()); // update default flags
 		}
 	}
 
 	private void useReadOnlyDidChange(@SuppressWarnings("unused") ObservableValue<? extends Boolean> property, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) {
 		vault.getVaultSettings().usesReadOnlyMode().setValue(newValue);
 		if (!useCustomMountFlags.isSelected()) {
-			mountFlags.setText(vault.getMountFlags()); // update default flags
+			mountFlags.setText(vault.getEffectiveMountFlags()); // update default flags
 		}
 	}
 
 
 	private void useCustomMountFlagsDidChange(@SuppressWarnings("unused") ObservableValue<? extends Boolean> property, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) {
 		if (!newValue) {
-			vault.setMountFlags(VaultSettings.DEFAULT_MOUNT_FLAGS);
-			mountFlags.setText(vault.getMountFlags());
+			vault.setCustomMountFlags(VaultSettings.DEFAULT_MOUNT_FLAGS);
+			mountFlags.setText(vault.getEffectiveMountFlags());
 		}
 	}
 
 	private void mountFlagsDidChange(@SuppressWarnings("unused") ObservableValue<? extends String> property, @SuppressWarnings("unused") String oldValue, String newValue) {
 		if (useCustomMountFlags.isSelected()) {
-			vault.setMountFlags(newValue);
+			vault.setCustomMountFlags(newValue);
 		}
 	}
 

+ 20 - 0
main/ui/src/main/java/org/cryptomator/ui/controls/AlphanumericTextField.java

@@ -0,0 +1,20 @@
+package org.cryptomator.ui.controls;
+
+import javafx.scene.control.TextField;
+import javafx.scene.control.TextFormatter;
+
+import java.util.regex.Pattern;
+
+public class AlphanumericTextField extends TextField {
+
+	private final static Pattern DIGIT_PATTERN = Pattern.compile("\\w*");
+
+	public AlphanumericTextField() {
+		this.setTextFormatter(new TextFormatter<>(this::filterNumericTextChange));
+	}
+
+	private TextFormatter.Change filterNumericTextChange(TextFormatter.Change change) {
+		return DIGIT_PATTERN.matcher(change.getText()).matches() ? change : null;
+	}
+
+}

+ 40 - 2
main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java

@@ -1,13 +1,51 @@
 package org.cryptomator.ui.vaultoptions;
 
+import javafx.fxml.FXML;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.TextField;
+import org.cryptomator.common.vaults.Vault;
 import org.cryptomator.ui.common.FxController;
 
 import javax.inject.Inject;
 
 @VaultOptionsScoped
 public class MountOptionsController implements FxController {
-	
+
+	private final Vault vault;
+	public TextField driveName;
+	public CheckBox readOnlyCheckbox;
+	public CheckBox customMountFlagsCheckbox;
+	public TextField mountFlags;
+
 	@Inject
-	MountOptionsController(){}
+	MountOptionsController(@VaultOptionsWindow Vault vault) {
+		this.vault = vault;
+	}
+
+	@FXML
+	public void initialize() {
+		driveName.textProperty().bindBidirectional(vault.getVaultSettings().mountName());
+		readOnlyCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().usesReadOnlyMode());
+		mountFlags.disableProperty().bind(customMountFlagsCheckbox.selectedProperty().not());
+
+		customMountFlagsCheckbox.setSelected(vault.isHavingCustomMountFlags());
+		if (vault.isHavingCustomMountFlags()) {
+			mountFlags.textProperty().bindBidirectional(vault.getVaultSettings().mountFlags());
+		} else {
+			mountFlags.textProperty().bind(vault.defaultMountFlagsProperty());
+		}
+	}
 
+	@FXML
+	public void toggleUseCustomMountFlags() {
+		if (customMountFlagsCheckbox.isSelected()) {
+			mountFlags.textProperty().unbind();
+			vault.setCustomMountFlags(vault.defaultMountFlagsProperty().get());
+			mountFlags.textProperty().bindBidirectional(vault.getVaultSettings().mountFlags());
+		} else {
+			mountFlags.textProperty().unbindBidirectional(vault.getVaultSettings().mountFlags());
+			vault.setCustomMountFlags(null);
+			mountFlags.textProperty().bind(vault.defaultMountFlagsProperty());
+		}
+	}
 }

+ 15 - 1
main/ui/src/main/resources/fxml/vault_options_mount.fxml

@@ -2,7 +2,11 @@
 
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Label?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.VBox?>
+<?import javafx.scene.control.CheckBox?>
+<?import org.cryptomator.ui.controls.AlphanumericTextField?>
 <VBox xmlns="http://javafx.com/javafx"
 	  xmlns:fx="http://javafx.com/fxml"
 	  fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
@@ -11,6 +15,16 @@
 		<Insets bottom="12" left="12" right="12" top="12"/>
 	</padding>
 	<children>
-		<Label text="todo mount options"/>
+		<HBox spacing="12" alignment="BASELINE_LEFT">
+			<Label text="%vaultOptions.mount.driveName"/>
+			<AlphanumericTextField fx:id="driveName"/>
+		</HBox>
+
+		<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly"/>
+
+		<HBox spacing="12" alignment="BASELINE_LEFT">
+			<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
+			<TextField fx:id="mountFlags" HBox.hgrow="ALWAYS" maxWidth="Infinity"/>
+		</HBox>
 	</children>
 </VBox>

+ 4 - 1
main/ui/src/main/resources/i18n/strings.properties

@@ -16,4 +16,7 @@ preferences.volumeType=Volume type
 unlock.deleteSavedPasswordDialog.title=Delete Saved Password
 unlock.deleteSavedPasswordDialog.header=Do you really want to delete the saved password of this vault?
 unlock.deleteSavedPasswordDialog.content=The saved password of this vault will be immediately deleted from your system keychain. If you'd like to save your password again, you'd have to unlock your vault with the "Save Password" option enabled.
-vaultlist.emptyList.onboardingInstruction=Click here to add a vault
+vaultlist.emptyList.onboardingInstruction=Click here to add a vault
+vaultOptions.mount.readonly=Read-Only
+vaultOptions.mount.driveName=Drive Name
+vaultOptions.mount.customMountFlags=Custom Mount Flags

+ 4 - 1
main/ui/src/main/resources/i18n/strings_en.properties

@@ -12,4 +12,7 @@ preferences.debugLogging=Enable debug logging
 preferences.startHidden=Hide window when starting Cryptomator
 preferences.theme=Look & Feel
 preferences.volumeType=Volume type
-vaultlist.emptyList.onboardingInstruction=Click here to add a vault
+vaultlist.emptyList.onboardingInstruction=Click here to add a vault
+vaultOptions.mount.readonly=Read-Only
+vaultOptions.mount.driveName=Drive Name
+vaultOptions.mount.customMountFlags=Custom Mount Flags