mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-20 01:26:52 -04:00
Merge branch 'hotfix/1.5.10'
This commit is contained in:
3
.idea/runConfigurations/Cryptomator_macOS.xml
generated
3
.idea/runConfigurations/Cryptomator_macOS.xml
generated
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user