Implemented some mount options (references #931)

This commit is contained in:
Sebastian Stenzel
2019-08-07 17:04:56 +02:00
parent 86745c5962
commit 17f45c6dab
8 changed files with 184 additions and 88 deletions

View File

@@ -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);
}

View File

@@ -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
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);
}
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");
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);
}
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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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 {
@Inject
MountOptionsController(){}
private final Vault vault;
public TextField driveName;
public CheckBox readOnlyCheckbox;
public CheckBox customMountFlagsCheckbox;
public TextField mountFlags;
@Inject
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());
}
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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