M .idea/runConfigurations/Cryptomator_macOS.xml => .idea/runConfigurations/Cryptomator_macOS.xml +1 -2
@@ 5,8 5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
- <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
- <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.ipcPortPath="~/Library/Application Support/Cryptomator/ipcPort.bin" -Dcryptomator.logDir="~/Library/Logs/Cryptomator" -Dcryptomator.mountPointsDir="/Volumes/" -Xss2m -Xmx512m -ea" />
+ <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.ipcPortPath="~/Library/Application Support/Cryptomator/ipcPort.bin" -Dcryptomator.logDir="~/Library/Logs/Cryptomator" -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>
M .idea/runConfigurations/Cryptomator_macOS_Dev.xml => .idea/runConfigurations/Cryptomator_macOS_Dev.xml +1 -1
@@ 5,7 5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="launcher" />
- <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.ipcPortPath="~/Library/Application Support/Cryptomator-Dev/ipcPort.bin" -Dcryptomator.logDir="~/Library/Logs/Cryptomator-Dev" -Dcryptomator.mountPointsDir="/Volumes/" -Dfuse.experimental="true" -Xss2m -Xmx512m -ea" />
+ <option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.ipcPortPath="~/Library/Application Support/Cryptomator-Dev/ipcPort.bin" -Dcryptomator.logDir="~/Library/Logs/Cryptomator-Dev" -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>
M main/buildkit/pom.xml => main/buildkit/pom.xml +1 -1
@@ 4,7 4,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
- <version>1.5.9</version>
+ <version>1.5.10</version>
</parent>
<artifactId>buildkit</artifactId>
<packaging>pom</packaging>
M main/commons/pom.xml => main/commons/pom.xml +1 -1
@@ 4,7 4,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
- <version>1.5.9</version>
+ <version>1.5.10</version>
</parent>
<artifactId>commons</artifactId>
<name>Cryptomator Commons</name>
M main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/AvailableDriveLetterChooser.java +1 -8
@@ 8,9 8,7 @@ import javax.inject.Inject;
import java.nio.file.Path;
import java.util.Optional;
-public class AvailableDriveLetterChooser implements MountPointChooser {
-
- public static final int PRIORITY = 200;
+class AvailableDriveLetterChooser implements MountPointChooser {
private final WindowsDriveLetters windowsDriveLetters;
@@ 28,9 26,4 @@ public class AvailableDriveLetterChooser implements MountPointChooser {
public Optional<Path> chooseMountPoint(Volume caller) {
return this.windowsDriveLetters.getAvailableDriveLetterPath();
}
-
- @Override
- public int getPriority() {
- return PRIORITY;
- }
}
M main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomDriveLetterChooser.java +1 -8
@@ 9,9 9,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
-public class CustomDriveLetterChooser implements MountPointChooser {
-
- public static final int PRIORITY = 100;
+class CustomDriveLetterChooser implements MountPointChooser {
private final VaultSettings vaultSettings;
@@ 29,9 27,4 @@ public class CustomDriveLetterChooser implements MountPointChooser {
public Optional<Path> chooseMountPoint(Volume caller) {
return this.vaultSettings.getWinDriveLetter().map(letter -> letter.charAt(0) + ":\\").map(Paths::get);
}
-
- @Override
- public int getPriority() {
- return PRIORITY;
- }
}
M main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/CustomMountPointChooser.java +1 -7
@@ 20,9 20,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
-public class CustomMountPointChooser implements MountPointChooser {
-
- public static final int PRIORITY = 0;
+class CustomMountPointChooser implements MountPointChooser {
private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class);
@@ 94,8 92,4 @@ public class CustomMountPointChooser implements MountPointChooser {
}
}
- @Override
- public int getPriority() {
- return PRIORITY;
- }
}
M main/commons/src/main/java/org/cryptomator/common/mountpoint/IrregularUnmountCleaner.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/IrregularUnmountCleaner.java +30 -5
@@ 1,20 1,44 @@
package org.cryptomator.common.mountpoint;
+import org.cryptomator.common.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Optional;
-public class IrregularUnmountCleaner {
+@Singleton
+class IrregularUnmountCleaner {
public static Logger LOG = LoggerFactory.getLogger(IrregularUnmountCleaner.class);
- public static void removeIrregularUnmountDebris(Path dirContainingMountPoints) {
+ private final Optional<Path> tmpMountPointDir;
+ private volatile boolean alreadyChecked = false;
+
+ @Inject
+ public IrregularUnmountCleaner(Environment env) {
+ this.tmpMountPointDir = env.getMountPointsDir();
+ }
+
+
+ public synchronized void clearIrregularUnmountDebrisIfNeeded() {
+ if (alreadyChecked || tmpMountPointDir.isEmpty()) {
+ return; //nuthin to do
+ }
+ if (Files.exists(tmpMountPointDir.get(), LinkOption.NOFOLLOW_LINKS)) {
+ clearIrregularUnmountDebris(tmpMountPointDir.get());
+ }
+ alreadyChecked = true;
+ }
+
+ private void clearIrregularUnmountDebris(Path dirContainingMountPoints) {
IOException cleanupFailed = new IOException("Cleanup failed");
try {
@@ 41,11 65,12 @@ public class IrregularUnmountCleaner {
}
} catch (IOException e) {
LOG.warn("Unable to perform cleanup of mountpoint dir {}.", dirContainingMountPoints, e);
+ } finally {
+ alreadyChecked = true;
}
-
}
- private static void deleteEmptyDir(Path dir) throws IOException {
+ private void deleteEmptyDir(Path dir) throws IOException {
assert Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS);
try {
Files.delete(dir); // attempt to delete dir non-recursively (will fail, if there are contents)
@@ 54,7 79,7 @@ public class IrregularUnmountCleaner {
}
}
- private static void deleteDeadLink(Path symlink) throws IOException {
+ private void deleteDeadLink(Path symlink) throws IOException {
assert Files.isSymbolicLink(symlink);
if (Files.notExists(symlink)) { // following link: target does not exist
Files.delete(symlink);
A main/commons/src/main/java/org/cryptomator/common/mountpoint/MacVolumeMountChooser.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/MacVolumeMountChooser.java +64 -0
@@ 0,0 1,64 @@
+package org.cryptomator.common.mountpoint;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.cryptomator.common.settings.VaultSettings;
+import org.cryptomator.common.vaults.Volume;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Optional;
+
+class MacVolumeMountChooser implements MountPointChooser {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MacVolumeMountChooser.class);
+ private static final int MAX_MOUNTPOINT_CREATION_RETRIES = 10;
+ private static final Path VOLUME_PATH = Path.of("/Volumes");
+
+ private final VaultSettings vaultSettings;
+
+ @Inject
+ public MacVolumeMountChooser(VaultSettings vaultSettings) {
+ this.vaultSettings = vaultSettings;
+ }
+
+ @Override
+ public boolean isApplicable(Volume caller) {
+ return SystemUtils.IS_OS_MAC;
+ }
+
+ @Override
+ public Optional<Path> chooseMountPoint(Volume caller) {
+ String basename = this.vaultSettings.mountName().get();
+ // regular
+ Path mountPoint = VOLUME_PATH.resolve(basename);
+ if (Files.notExists(mountPoint)) {
+ return Optional.of(mountPoint);
+ }
+ // with id
+ mountPoint = VOLUME_PATH.resolve(basename + " (" + vaultSettings.getId() + ")");
+ if (Files.notExists(mountPoint)) {
+ return Optional.of(mountPoint);
+ }
+ // with id and count
+ for (int i = 1; i < MAX_MOUNTPOINT_CREATION_RETRIES; i++) {
+ mountPoint = VOLUME_PATH.resolve(basename + "_(" + vaultSettings.getId() + ")_" + i);
+ if (Files.notExists(mountPoint)) {
+ return Optional.of(mountPoint);
+ }
+ }
+ LOG.error("Failed to find feasible mountpoint at /Volumes/{}_x. Giving up after {} attempts.", basename, MAX_MOUNTPOINT_CREATION_RETRIES);
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean prepare(Volume caller, Path mountPoint) {
+ // https://github.com/osxfuse/osxfuse/issues/306#issuecomment-245114592:
+ // In order to allow non-admin users to mount FUSE volumes in `/Volumes`,
+ // starting with version 3.5.0, FUSE will create non-existent mount points automatically.
+ // Therefore we don't need to prepare anything.
+ return false;
+ }
+}
M main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooser.java +1 -32
@@ 47,7 47,7 @@ import java.util.SortedSet;
* If the preparation succeeds {@link #cleanup(Volume, Path)} can be used after unmount to do any
* remaining cleanup.
*/
-public interface MountPointChooser extends Comparable<MountPointChooser> {
+public interface MountPointChooser {
/**
* Called by the {@link Volume} to determine whether this MountPointChooser is
@@ 135,35 135,4 @@ public interface MountPointChooser extends Comparable<MountPointChooser> {
//NO-OP
}
- /**
- * Called by the {@link MountPointChooserModule} to sort the available MPCs
- * and determine their execution order.
- * The priority must be defined by the developer to reflect a useful execution order.
- * MPCs with lower priorities will be placed at lower indices in the resulting
- * {@link SortedSet} and will be executed with higher probability.<br>
- * A specific priority <b>must not</b> be assigned to more than one MPC at a time;
- * the result of having two MPCs with equal priority is undefined.
- *
- * @return the priority of this MPC.
- */
- int getPriority();
-
- /**
- * Called by the {@link Volume} to determine the execution order of the registered MPCs.
- * <b>Implementations usually may not override this method.</b> This default implementation
- * sorts the MPCs in ascending order of their {@link #getPriority() priority.}<br>
- * <br>
- * <b>Original description:</b>
- * <p>{@inheritDoc}
- *
- * @implNote This default implementation sorts the MPCs in ascending order
- * of their {@link #getPriority() priority.}
- */
- @Override
- default int compareTo(MountPointChooser other) {
- Preconditions.checkNotNull(other, "Other must not be null!");
-
- //Sort by priority (ascending order)
- return Integer.compare(this.getPriority(), other.getPriority());
- }
}
M main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/MountPointChooserModule.java +23 -11
@@ 1,15 1,17 @@
package org.cryptomator.common.mountpoint;
-import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Iterables;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.IntoSet;
+import dagger.multibindings.IntKey;
+import dagger.multibindings.IntoMap;
import org.cryptomator.common.vaults.PerVault;
import javax.inject.Named;
-import java.util.Set;
-import java.util.SortedSet;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
/**
* Dagger-Module for {@link MountPointChooser MountPointChoosers.}<br>
@@ 21,30 23,40 @@ import java.util.SortedSet;
public abstract class MountPointChooserModule {
@Binds
- @IntoSet
+ @IntoMap
+ @IntKey(0)
@PerVault
public abstract MountPointChooser bindCustomMountPointChooser(CustomMountPointChooser chooser);
@Binds
- @IntoSet
+ @IntoMap
+ @IntKey(100)
@PerVault
public abstract MountPointChooser bindCustomDriveLetterChooser(CustomDriveLetterChooser chooser);
@Binds
- @IntoSet
+ @IntoMap
+ @IntKey(101)
+ @PerVault
+ public abstract MountPointChooser bindMacVolumeMountChooser(MacVolumeMountChooser chooser);
+
+ @Binds
+ @IntoMap
+ @IntKey(200)
@PerVault
public abstract MountPointChooser bindAvailableDriveLetterChooser(AvailableDriveLetterChooser chooser);
@Binds
- @IntoSet
+ @IntoMap
+ @IntKey(999)
@PerVault
public abstract MountPointChooser bindTemporaryMountPointChooser(TemporaryMountPointChooser chooser);
@Provides
@PerVault
@Named("orderedMountPointChoosers")
- public static SortedSet<MountPointChooser> provideOrderedMountPointChoosers(Set<MountPointChooser> choosers) {
- //Sort by natural order. The natural order is defined by MountPointChooser#compareTo
- return ImmutableSortedSet.copyOf(choosers);
+ public static Iterable<MountPointChooser> provideOrderedMountPointChoosers(Map<Integer, MountPointChooser> choosers) {
+ SortedMap<Integer, MountPointChooser> sortedChoosers = new TreeMap<>(choosers);
+ return Iterables.unmodifiableIterable(sortedChoosers.values());
}
}
M main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java => main/commons/src/main/java/org/cryptomator/common/mountpoint/TemporaryMountPointChooser.java +12 -19
@@ 1,6 1,5 @@
package org.cryptomator.common.mountpoint;
-import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume;
@@ 12,23 11,23 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Optional;
-public class TemporaryMountPointChooser implements MountPointChooser {
-
- public static final int PRIORITY = 300;
+class TemporaryMountPointChooser implements MountPointChooser {
private static final Logger LOG = LoggerFactory.getLogger(TemporaryMountPointChooser.class);
private static final int MAX_TMPMOUNTPOINT_CREATION_RETRIES = 10;
private final VaultSettings vaultSettings;
private final Environment environment;
+ private final IrregularUnmountCleaner cleaner;
+ private volatile boolean clearedDebris;
@Inject
- public TemporaryMountPointChooser(VaultSettings vaultSettings, Environment environment) {
+ public TemporaryMountPointChooser(VaultSettings vaultSettings, Environment environment, IrregularUnmountCleaner cleaner) {
this.vaultSettings = vaultSettings;
this.environment = environment;
+ this.cleaner = cleaner;
}
@Override
@@ 42,9 41,14 @@ public class TemporaryMountPointChooser implements MountPointChooser {
@Override
public Optional<Path> chooseMountPoint(Volume caller) {
+ assert environment.getMountPointsDir().isPresent();
+ //clean leftovers of not-regularly unmounted vaults
+ //see https://github.com/cryptomator/cryptomator/issues/1013 and https://github.com/cryptomator/cryptomator/issues/1061
+ cleaner.clearIrregularUnmountDebrisIfNeeded();
return this.environment.getMountPointsDir().map(this::choose);
}
+
private Path choose(Path parent) {
String basename = this.vaultSettings.mountName().get();
//regular
@@ 53,13 57,13 @@ public class TemporaryMountPointChooser implements MountPointChooser {
return mountPoint;
}
//with id
- mountPoint = parent.resolve(basename + " (" +vaultSettings.getId() + ")");
+ mountPoint = parent.resolve(basename + " (" + vaultSettings.getId() + ")");
if (Files.notExists(mountPoint)) {
return mountPoint;
}
//with id and count
for (int i = 1; i < MAX_TMPMOUNTPOINT_CREATION_RETRIES; i++) {
- mountPoint = parent.resolve(basename + "_(" +vaultSettings.getId() + ")_"+i);
+ mountPoint = parent.resolve(basename + "_(" + vaultSettings.getId() + ")_" + i);
if (Files.notExists(mountPoint)) {
return mountPoint;
}
@@ 70,13 74,6 @@ public class TemporaryMountPointChooser implements MountPointChooser {
@Override
public boolean prepare(Volume caller, Path mountPoint) throws InvalidMountPointException {
- // https://github.com/osxfuse/osxfuse/issues/306#issuecomment-245114592:
- // In order to allow non-admin users to mount FUSE volumes in `/Volumes`,
- // starting with version 3.5.0, FUSE will create non-existent mount points automatically.
- if (SystemUtils.IS_OS_MAC && mountPoint.getParent().equals(Paths.get("/Volumes"))) {
- return false;
- }
-
try {
switch (caller.getMountPointRequirement()) {
case PARENT_NO_MOUNT_POINT -> {
@@ 114,8 111,4 @@ public class TemporaryMountPointChooser implements MountPointChooser {
}
}
- @Override
- public int getPriority() {
- return PRIORITY;
- }
}
M main/commons/src/main/java/org/cryptomator/common/vaults/AbstractVolume.java => main/commons/src/main/java/org/cryptomator/common/vaults/AbstractVolume.java +7 -22
@@ 1,50 1,35 @@
package org.cryptomator.common.vaults;
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import java.nio.file.Path;
import java.util.Optional;
-import java.util.SortedSet;
-import java.util.TreeSet;
public abstract class AbstractVolume implements Volume {
- private final SortedSet<MountPointChooser> choosers;
+ private final Iterable<MountPointChooser> choosers;
protected Path mountPoint;
-
- //Cleanup
private boolean cleanupRequired;
private MountPointChooser usedChooser;
- public AbstractVolume(SortedSet<MountPointChooser> choosers) {
+ public AbstractVolume(Iterable<MountPointChooser> choosers) {
this.choosers = choosers;
}
protected Path determineMountPoint() throws InvalidMountPointException {
- SortedSet<MountPointChooser> checkedChoosers = new TreeSet<>(); //Natural order
- for (MountPointChooser chooser : this.choosers) {
- if (!chooser.isApplicable(this)) {
- continue;
- }
-
+ for (var chooser : Iterables.filter(choosers, c -> c.isApplicable(this))) {
Optional<Path> chosenPath = chooser.chooseMountPoint(this);
- checkedChoosers.add(chooser); //Consider a chooser checked if it's #chooseMountPoint() method was called
- if (chosenPath.isEmpty()) {
- //Chooser was applicable, but couldn't find a feasible mountpoint
+ if (chosenPath.isEmpty()) { // chooser couldn't find a feasible mountpoint
continue;
}
- this.cleanupRequired = chooser.prepare(this, chosenPath.get()); //Fail entirely if an Exception occurs
+ this.cleanupRequired = chooser.prepare(this, chosenPath.get());
this.usedChooser = chooser;
return chosenPath.get();
}
- //SortedSet#stream() should return a sorted stream (that's what it's docs and the docs of #spliterator() say, even if they are not 100% clear for me.)
- //We want to keep that order, that's why we use ImmutableSet#toImmutableSet() to collect (even if it doesn't implement SortedSet, it's docs promise use encounter ordering.)
- String checked = Joiner.on(", ").join(checkedChoosers.stream().map((mpc) -> mpc.getClass().getTypeName()).collect(ImmutableSet.toImmutableSet()));
- throw new InvalidMountPointException(String.format("No feasible MountPoint found! Checked %s", checked.isBlank() ? "<No applicable MPC>" : checked));
+ throw new InvalidMountPointException("No feasible MountPoint found!");
}
protected void cleanupMountPoint() {
M main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java => main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java +1 -1
@@ 28,7 28,7 @@ public class DokanyVolume extends AbstractVolume {
private Mount mount;
@Inject
- public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, @Named("orderedMountPointChoosers") SortedSet<MountPointChooser> choosers) {
+ public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, @Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
super(choosers);
this.vaultSettings = vaultSettings;
this.mountFactory = new MountFactory(executorService);
M main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java => main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java +21 -3
@@ 1,6 1,6 @@
package org.cryptomator.common.vaults;
-import com.google.common.base.Splitter;
+import com.google.common.collect.Iterators;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
@@ 18,16 18,20 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
import java.util.SortedSet;
+import java.util.regex.Pattern;
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 Mount mount;
@Inject
- public FuseVolume(@Named("orderedMountPointChoosers") SortedSet<MountPointChooser> choosers) {
+ public FuseVolume(@Named("orderedMountPointChoosers") Iterable<MountPointChooser> choosers) {
super(choosers);
}
@@ 51,7 55,21 @@ public class FuseVolume extends AbstractVolume {
}
private String[] splitFlags(String str) {
- return Splitter.on(' ').splitToList(str).toArray(String[]::new);
+ List<String> flags = new ArrayList<>();
+ var matches = Iterators.peekingIterator(NON_WHITESPACE_OR_QUOTED.matcher(str).results().iterator());
+ while (matches.hasNext()) {
+ String flag = matches.next().group();
+ // check if flag is missing its argument:
+ if (flag.endsWith("=") && matches.hasNext() && matches.peek().group(1) != null) { // next is "double quoted"
+ // next is "double quoted" and flag is missing its argument
+ flag += matches.next().group(1);
+ } else if (flag.endsWith("=") && matches.hasNext() && matches.peek().group(2) != null) {
+ // next is 'single quoted' and flag is missing its argument
+ flag += matches.next().group(2);
+ }
+ flags.add(flag);
+ }
+ return flags.toArray(String[]::new);
}
@Override
M main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java => main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java +2 -2
@@ 101,7 101,7 @@ public class VaultModule {
if (readOnly.get()) {
flags.append(" -ordonly");
}
- flags.append(" -ovolname=").append(mountName.get());
+ flags.append(" -ovolname=").append('"').append(mountName.get()).append('"');
flags.append(" -oatomic_o_trunc");
flags.append(" -oauto_xattr");
flags.append(" -oauto_cache");
@@ 158,7 158,7 @@ public class VaultModule {
flags.append(" -ouid=-1");
flags.append(" -ogid=-1");
}
- flags.append(" -ovolname=").append(mountName.get());
+ flags.append(" -ovolname=").append('"').append(mountName.get()).append('"');
//Dokany requires this option to be set, WinFSP doesn't seem to share this peculiarity,
//but the option exists. Let's keep this here in case we need it.
// flags.append(" -oThreadCount=").append(5);
M main/launcher/pom.xml => main/launcher/pom.xml +1 -1
@@ 4,7 4,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
- <version>1.5.9</version>
+ <version>1.5.10</version>
</parent>
<artifactId>launcher</artifactId>
<name>Cryptomator Launcher</name>
M main/pom.xml => main/pom.xml +1 -1
@@ 3,7 3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
- <version>1.5.9</version>
+ <version>1.5.10</version>
<packaging>pom</packaging>
<name>Cryptomator</name>
M main/ui/pom.xml => main/ui/pom.xml +1 -1
@@ 4,7 4,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
- <version>1.5.9</version>
+ <version>1.5.10</version>
</parent>
<artifactId>ui</artifactId>
<name>Cryptomator GUI</name>
M main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java => main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncher.java +1 -11
@@ 1,7 1,5 @@
package org.cryptomator.ui.launcher;
-import org.cryptomator.common.Environment;
-import org.cryptomator.common.mountpoint.IrregularUnmountCleaner;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
@@ 16,8 14,6 @@ import javafx.collections.ObservableList;
import java.awt.Desktop;
import java.awt.SystemTray;
import java.awt.desktop.AppReopenedListener;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
import java.util.Collection;
import java.util.Optional;
@@ 32,17 28,15 @@ public class UiLauncher {
private final FxApplicationStarter fxApplicationStarter;
private final AppLaunchEventHandler launchEventHandler;
private final Optional<TrayIntegrationProvider> trayIntegration;
- private final Environment env;
@Inject
- public UiLauncher(Settings settings, ObservableList<Vault> vaults, TrayMenuComponent.Builder trayComponent, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<TrayIntegrationProvider> trayIntegration, Environment env) {
+ public UiLauncher(Settings settings, ObservableList<Vault> vaults, TrayMenuComponent.Builder trayComponent, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<TrayIntegrationProvider> trayIntegration) {
this.settings = settings;
this.vaults = vaults;
this.trayComponent = trayComponent;
this.fxApplicationStarter = fxApplicationStarter;
this.launchEventHandler = launchEventHandler;
this.trayIntegration = trayIntegration;
- this.env = env;
}
public void launch() {
@@ 65,10 59,6 @@ public class UiLauncher {
// register app reopen listener
Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(hasTrayIcon));
- //clean leftovers of not-regularly unmounted vaults
- //see https://github.com/cryptomator/cryptomator/issues/1013 and https://github.com/cryptomator/cryptomator/issues/1061
- env.getMountPointsDir().filter(path -> Files.exists(path, LinkOption.NOFOLLOW_LINKS)).ifPresent(IrregularUnmountCleaner::removeIrregularUnmountDebris);
-
// auto unlock
Collection<Vault> vaultsToAutoUnlock = vaults.filtered(this::shouldAttemptAutoUnlock);
if (!vaultsToAutoUnlock.isEmpty()) {
M main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java => main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +9 -4
@@ 31,6 31,7 @@ import javafx.scene.image.ImageView;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
import javafx.util.Duration;
import java.util.Arrays;
import java.util.Optional;
@@ 78,7 79,7 @@ public class UnlockController implements FxController {
this.userInteractionDisabled = passwordEntryLock.awaitingInteraction().not();
this.unlockButtonDisabled = new SimpleBooleanProperty();
this.vaultName = WeakBindings.bindString(vault.displayNameProperty());
- this.window.setOnCloseRequest(windowEvent -> cancel());
+ this.window.setOnHiding(this::windowClosed);
}
@FXML
@@ 128,14 129,18 @@ public class UnlockController implements FxController {
passwordEntryLock.awaitingInteraction().addListener(observable -> stopUnlockAnimation());
}
-
@FXML
public void cancel() {
- LOG.debug("Unlock canceled by user.");
window.close();
- passwordEntryLock.interacted(UnlockModule.PasswordEntry.CANCELED);
}
+ private void windowClosed(WindowEvent windowEvent) {
+ // if not already interacted, mark this workflow as cancelled:
+ if (passwordEntryLock.awaitingInteraction().get()) {
+ LOG.debug("Unlock canceled by user.");
+ passwordEntryLock.interacted(UnlockModule.PasswordEntry.CANCELED);
+ }
+ }
@FXML
public void unlock() {