From 24df3c380906d10fa067f64fe43fb2b8253d7944 Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Fri, 12 Aug 2016 14:11:49 +0200 Subject: [PATCH 01/11] GvfsMounters now use correct protocol. --- .../cryptomator/frontend/webdav/mount/LinuxGvfsDavMounter.java | 2 +- .../frontend/webdav/mount/LinuxGvfsWebDavMounter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsDavMounter.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsDavMounter.java index d8c5224f6..51834277c 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsDavMounter.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsDavMounter.java @@ -86,7 +86,7 @@ final class LinuxGvfsDavMounter implements WebDavMounterStrategy { @Override public void reveal() throws CommandFailedException { - Script.fromLines("set -x", "xdg-open \"webdav:$DAV_SSP\"").addEnv("DAV_SSP", webDavUri.getRawSchemeSpecificPart()).execute(); + Script.fromLines("set -x", "gvfs-open \"dav:$DAV_SSP\"").addEnv("DAV_SSP", webDavUri.getRawSchemeSpecificPart()).execute(); } } diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsWebDavMounter.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsWebDavMounter.java index cbeffd5b2..219709294 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsWebDavMounter.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/LinuxGvfsWebDavMounter.java @@ -86,7 +86,7 @@ final class LinuxGvfsWebDavMounter implements WebDavMounterStrategy { @Override public void reveal() throws CommandFailedException { - Script.fromLines("set -x", "xdg-open \"webdav:$DAV_SSP\"").addEnv("DAV_SSP", webDavUri.getRawSchemeSpecificPart()).execute(); + Script.fromLines("set -x", "gvfs-open \"webdav:$DAV_SSP\"").addEnv("DAV_SSP", webDavUri.getRawSchemeSpecificPart()).execute(); } } From 28f275c22df15f3a196c76ab28e5ccb1c0a62d96 Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Fri, 12 Aug 2016 15:11:54 +0200 Subject: [PATCH 02/11] Requests on parent folders of valid vault urls no longer get delayed --- .../cryptomator/frontend/FrontendFactory.java | 5 +- .../org/cryptomator/frontend/FrontendId.java | 83 +++++++++++++++++++ .../frontend/webdav/ContextPathBuilder.java | 30 +++++++ .../cryptomator/frontend/webdav/Tarpit.java | 41 +++++++-- .../frontend/webdav/WebDavFrontend.java | 13 ++- .../frontend/webdav/WebDavServer.java | 18 ++-- .../java/org/cryptomator/ui/model/Vault.java | 14 ++-- .../cryptomator/ui/model/VaultFactory.java | 35 +------- 8 files changed, 178 insertions(+), 61 deletions(-) create mode 100644 main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java create mode 100644 main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java diff --git a/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendFactory.java b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendFactory.java index 9401355d0..37fb053cd 100644 --- a/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendFactory.java +++ b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendFactory.java @@ -16,10 +16,11 @@ public interface FrontendFactory { * Provides a new frontend to access the given folder. * * @param root Root resource accessible through this frontend. - * @param uniqueName Name of the frontend, i.e. used to create subresources for the different frontends inside of a common virtual drive. + * @param id unique id of the frontend, i.e. used to generate a unique uri + * @param name Name of the frontend, i.e. used to generate a readable/recognizable name of a common virtual drive * @return A new frontend * @throws FrontendCreationFailedException If creation was not possible. */ - Frontend create(Folder root, String uniqueName) throws FrontendCreationFailedException; + Frontend create(Folder root, FrontendId id, String name) throws FrontendCreationFailedException; } diff --git a/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java new file mode 100644 index 000000000..97915c074 --- /dev/null +++ b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java @@ -0,0 +1,83 @@ +package org.cryptomator.frontend; + +import static java.util.UUID.randomUUID; + +import java.nio.ByteBuffer; +import java.util.Base64; +import java.util.UUID; + +public class FrontendId { + + public static final String FRONTEND_ID_PATTERN = "[a-zA-Z0-9_-]{12}"; + + public static FrontendId generate() { + return new FrontendId(); + } + + public static FrontendId from(String value) { + return new FrontendId(value); + } + + private final String value; + + private FrontendId() { + this(generateId()); + } + + private FrontendId(String value) { + if (!value.matches(FRONTEND_ID_PATTERN)) { + throw new IllegalArgumentException("Invalid frontend id " + value); + } + this.value = value; + } + + private static String generateId() { + return asBase64String(nineBytesFrom(randomUUID())); + } + + private static String asBase64String(ByteBuffer bytes) { + ByteBuffer base64Buffer = Base64.getUrlEncoder().encode(bytes); + return new String(asByteArray(base64Buffer)); + } + + private static ByteBuffer nineBytesFrom(UUID uuid) { + ByteBuffer uuidBuffer = ByteBuffer.allocate(9); + uuidBuffer.putLong(uuid.getMostSignificantBits()); + uuidBuffer.put((byte) (uuid.getLeastSignificantBits() & 0xFF)); + uuidBuffer.flip(); + return uuidBuffer; + } + + private static byte[] asByteArray(ByteBuffer buffer) { + if (buffer.hasArray()) { + return buffer.array(); + } else { + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + return bytes; + } + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) { + return false; + } + return obj == this || internalEquals((FrontendId) obj); + } + + private boolean internalEquals(FrontendId obj) { + return value.equals(obj.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value; + } + +} diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java new file mode 100644 index 000000000..ee036d221 --- /dev/null +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/ContextPathBuilder.java @@ -0,0 +1,30 @@ +package org.cryptomator.frontend.webdav; + +import static java.lang.String.format; +import static org.cryptomator.frontend.FrontendId.FRONTEND_ID_PATTERN; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.cryptomator.frontend.FrontendId; + +class ContextPaths { + + private static final Pattern SERVLET_PATH_WITH_FRONTEND_ID_PATTERN = Pattern.compile("^/(" + FRONTEND_ID_PATTERN + ")(/.*)?$"); + private static final int FRONTEND_ID_GROUP = 1; + + public static String from(FrontendId id, String name) { + return format("/%s/%s", id, name); + } + + public static Optional extractFrontendId(String path) { + Matcher matcher = SERVLET_PATH_WITH_FRONTEND_ID_PATTERN.matcher(path); + if (matcher.matches()) { + return Optional.of(FrontendId.from(matcher.group(FRONTEND_ID_GROUP))); + } else { + return Optional.empty(); + } + } + +} diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java index bedccc780..42226b3db 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java @@ -8,24 +8,50 @@ package org.cryptomator.frontend.webdav; import static java.lang.Math.max; import static java.lang.System.currentTimeMillis; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + import javax.inject.Inject; import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; +import org.cryptomator.frontend.FrontendId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + @Singleton class Tarpit { - + + private static final Logger LOG = LoggerFactory.getLogger(Tarpit.class); private static final long DELAY_MS = 10000; - + + private final Set validFrontendIds = new HashSet<>(); + @Inject - public Tarpit() {} - + public Tarpit() { + } + + public void register(FrontendId frontendId) { + validFrontendIds.add(frontendId); + } + + public void unregister(FrontendId frontendId) { + validFrontendIds.remove(frontendId); + } + public void handle(HttpServletRequest req) { - if (isRequestWithVaultId(req)) { + if (isRequestWithInvalidVaultId(req)) { delayExecutionUninterruptibly(); + LOG.debug("Delayed request to " + req.getRequestURI() + " by " + DELAY_MS + "ms"); } } + private boolean isRequestWithInvalidVaultId(HttpServletRequest req) { + Optional frontendId = ContextPaths.extractFrontendId(req.getServletPath()); + return frontendId.isPresent() && !isValid(frontendId.get()); + } + private void delayExecutionUninterruptibly() { long expected = currentTimeMillis() + DELAY_MS; long sleepTime = DELAY_MS; @@ -38,9 +64,8 @@ class Tarpit { } } - private boolean isRequestWithVaultId(HttpServletRequest req) { - String path = req.getServletPath(); - return path.matches("^/[a-zA-Z0-9_-]{12}/.*$"); + private boolean isValid(FrontendId frontendId) { + return validFrontendIds.contains(frontendId); } } diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java index 50081c846..a657cc376 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java @@ -24,12 +24,15 @@ class WebDavFrontend implements Frontend { private final WebDavMounterProvider webdavMounterProvider; private final ServletContextHandler handler; private final URI uri; + private final Runnable afterClose; + private WebDavMount mount; - public WebDavFrontend(WebDavMounterProvider webdavMounterProvider, ServletContextHandler handler, URI uri) throws FrontendCreationFailedException { + public WebDavFrontend(WebDavMounterProvider webdavMounterProvider, ServletContextHandler handler, URI uri, Runnable afterUnmount) throws FrontendCreationFailedException { this.webdavMounterProvider = webdavMounterProvider; this.handler = handler; this.uri = uri; + this.afterClose = afterUnmount; try { handler.start(); } catch (Exception e) { @@ -39,8 +42,12 @@ class WebDavFrontend implements Frontend { @Override public void close() throws Exception { - unmount(); - handler.stop(); + try { + unmount(); + handler.stop(); + } finally { + afterClose.run(); + } } @Override diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java index 0ec50abe3..0f18bbab5 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavServer.java @@ -8,6 +8,8 @@ *******************************************************************************/ package org.cryptomator.frontend.webdav; +import static java.lang.String.format; + import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.BlockingQueue; @@ -20,6 +22,7 @@ import org.cryptomator.filesystem.Folder; import org.cryptomator.frontend.Frontend; import org.cryptomator.frontend.FrontendCreationFailedException; import org.cryptomator.frontend.FrontendFactory; +import org.cryptomator.frontend.FrontendId; import org.cryptomator.frontend.webdav.mount.WebDavMounterProvider; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; @@ -45,9 +48,10 @@ public class WebDavServer implements FrontendFactory { private final ContextHandlerCollection servletCollection; private final WebDavServletContextFactory servletContextFactory; private final WebDavMounterProvider webdavMounterProvider; + private final Tarpit tarpit; @Inject - WebDavServer(WebDavServletContextFactory servletContextFactory, WebDavMounterProvider webdavMounterProvider, DefaultServlet defaultServlet) { + WebDavServer(WebDavServletContextFactory servletContextFactory, WebDavMounterProvider webdavMounterProvider, DefaultServlet defaultServlet, Tarpit tarpit) { final BlockingQueue queue = new LinkedBlockingQueue<>(MAX_PENDING_REQUESTS); final ThreadPool tp = new QueuedThreadPool(MAX_THREADS, MIN_THREADS, THREAD_IDLE_SECONDS, queue); this.server = new Server(tp); @@ -55,7 +59,8 @@ public class WebDavServer implements FrontendFactory { this.servletCollection = new ContextHandlerCollection(); this.servletContextFactory = servletContextFactory; this.webdavMounterProvider = webdavMounterProvider; - + this.tarpit = tarpit; + servletCollection.addHandler(defaultServlet.createServletContextHandler()); server.setConnectors(new Connector[] {localConnector}); server.setHandler(servletCollection); @@ -103,10 +108,8 @@ public class WebDavServer implements FrontendFactory { } @Override - public Frontend create(Folder root, String contextPath) throws FrontendCreationFailedException { - if (!contextPath.startsWith("/")) { - throw new IllegalArgumentException("contextPath must begin with '/'"); - } + public Frontend create(Folder root, FrontendId id, String name) throws FrontendCreationFailedException { + String contextPath = format("/%s/%s", id, name); final URI uri; try { uri = new URI("http", null, "localhost", getPort(), contextPath, null, null); @@ -114,8 +117,9 @@ public class WebDavServer implements FrontendFactory { throw new IllegalStateException(e); } final ServletContextHandler handler = addServlet(root, uri); + tarpit.register(id); LOG.info("Servlet available under " + uri); - return new WebDavFrontend(webdavMounterProvider, handler, uri); + return new WebDavFrontend(webdavMounterProvider, handler, uri, () -> tarpit.unregister(id)); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java index f4e94a214..3647e8054 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java @@ -42,6 +42,7 @@ import org.cryptomator.frontend.Frontend; import org.cryptomator.frontend.Frontend.MountParam; import org.cryptomator.frontend.FrontendCreationFailedException; import org.cryptomator.frontend.FrontendFactory; +import org.cryptomator.frontend.FrontendId; import org.cryptomator.ui.settings.Settings; import org.cryptomator.ui.util.DeferredClosable; import org.cryptomator.ui.util.DeferredCloser; @@ -73,7 +74,7 @@ public class Vault implements CryptoFileSystemDelegate { private final Set whitelistedResourcesWithInvalidMac = new HashSet<>(); private final AtomicReference nioFileSystem = new AtomicReference<>(); private final String id; - + private String mountName; private Character winDriveLetter; private Optional statsFileSystem = Optional.empty(); @@ -81,7 +82,8 @@ public class Vault implements CryptoFileSystemDelegate { /** * Package private constructor, use {@link VaultFactory}. - * @param string + * + * @param string */ Vault(String id, Path vaultDirectoryPath, ShorteningFileSystemFactory shorteningFileSystemFactory, CryptoFileSystemFactory cryptoFileSystemFactory, DeferredCloser closer) { this.path = new SimpleObjectProperty(vaultDirectoryPath); @@ -133,7 +135,7 @@ public class Vault implements CryptoFileSystemDelegate { FileSystem normalizingFs = new NormalizedNameFileSystem(cryptoFs, SystemUtils.IS_OS_MAC_OSX ? Form.NFD : Form.NFC); StatsFileSystem statsFs = new StatsFileSystem(normalizingFs); statsFileSystem = Optional.of(statsFs); - Frontend frontend = frontendFactory.create(statsFs, contextPath()); + Frontend frontend = frontendFactory.create(statsFs, FrontendId.from(id), stripStart(mountName, "/")); filesystemFrontend = closer.closeLater(frontend); frontend.mount(getMountParams(settings)); success = true; @@ -146,10 +148,6 @@ public class Vault implements CryptoFileSystemDelegate { } } - private String contextPath() { - return String.format("/%s/%s", id, stripStart(mountName, "/")); - } - public synchronized void deactivateFrontend() { filesystemFrontend.close(); statsFileSystem = Optional.empty(); @@ -312,7 +310,7 @@ public class Vault implements CryptoFileSystemDelegate { public void setWinDriveLetter(Character winDriveLetter) { this.winDriveLetter = winDriveLetter; } - + public String getId() { return id; } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java b/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java index 148a4317a..cf1dcc756 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/VaultFactory.java @@ -8,18 +8,14 @@ *******************************************************************************/ package org.cryptomator.ui.model; -import static java.util.UUID.randomUUID; - -import java.nio.ByteBuffer; import java.nio.file.Path; -import java.util.Base64; -import java.util.UUID; import javax.inject.Inject; import javax.inject.Singleton; import org.cryptomator.filesystem.crypto.CryptoFileSystemFactory; import org.cryptomator.filesystem.shortening.ShorteningFileSystemFactory; +import org.cryptomator.frontend.FrontendId; import org.cryptomator.ui.util.DeferredCloser; @Singleton @@ -41,34 +37,7 @@ public class VaultFactory { } public Vault createVault(Path path) { - return createVault(generateId(), path); - } - - private String generateId() { - return asBase64String(nineBytesFrom(randomUUID())); - } - - private String asBase64String(ByteBuffer bytes) { - ByteBuffer base64Buffer = Base64.getUrlEncoder().encode(bytes); - return new String(asByteArray(base64Buffer)); - } - - private ByteBuffer nineBytesFrom(UUID uuid) { - ByteBuffer uuidBuffer = ByteBuffer.allocate(9); - uuidBuffer.putLong(uuid.getMostSignificantBits()); - uuidBuffer.put((byte)(uuid.getLeastSignificantBits() & 0xFF)); - uuidBuffer.flip(); - return uuidBuffer; - } - - private byte[] asByteArray(ByteBuffer buffer) { - if (buffer.hasArray()) { - return buffer.array(); - } else { - byte[] bytes = new byte[buffer.remaining()]; - buffer.get(bytes); - return bytes; - } + return createVault(FrontendId.generate().toString(), path); } } From 4c3c60060df194d39b557a04e125f3b0b677a420 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 13:55:51 +0200 Subject: [PATCH 03/11] Graceful unmounting on Windows and improved error handling of deferred closables. --- .../org/cryptomator/frontend/Frontend.java | 5 +- .../frontend/webdav/WebDavFrontend.java | 4 +- .../webdav/mount/WindowsWebDavMounter.java | 2 +- .../org/cryptomator/ui/CryptomatorModule.java | 11 +++- .../org/cryptomator/ui/MainApplication.java | 7 ++- .../ui/controllers/UnlockedController.java | 7 +-- .../java/org/cryptomator/ui/model/Vault.java | 6 +- .../cryptomator/ui/util/DeferredClosable.java | 6 -- .../cryptomator/ui/util/DeferredCloser.java | 55 +++++++++++-------- 9 files changed, 58 insertions(+), 45 deletions(-) diff --git a/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java b/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java index 25d0290ca..78978bf7b 100644 --- a/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java +++ b/main/frontend-api/src/main/java/org/cryptomator/frontend/Frontend.java @@ -24,7 +24,10 @@ public interface Frontend extends AutoCloseable { void mount(Map> map) throws CommandFailedException; - void unmount() throws CommandFailedException; + /** + * Unmounts the file system and stops any file system handler threads. + */ + void close() throws Exception; void reveal() throws CommandFailedException; diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java index a657cc376..f80e5543b 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WebDavFrontend.java @@ -55,10 +55,10 @@ class WebDavFrontend implements Frontend { mount = webdavMounterProvider.chooseMounter(mountParams).mount(uri, mountParams); } - @Override - public void unmount() throws CommandFailedException { + private void unmount() throws CommandFailedException { if (mount != null) { mount.unmount(); + mount = null; } } diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java index 931b33ae4..bea6a5cdd 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsWebDavMounter.java @@ -160,7 +160,7 @@ final class WindowsWebDavMounter implements WebDavMounterStrategy { private WindowsWebDavMount(String driveLetter) { this.driveLetter = CharUtils.toCharacterObject(driveLetter); this.openExplorerScript = fromLines("start explorer.exe " + driveLetter + ":"); - this.unmountScript = fromLines("net use " + driveLetter + ": /delete"); + this.unmountScript = fromLines("net use " + driveLetter + ": /delete /no"); } @Override diff --git a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java index 457cd6d71..b62f631c4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/CryptomatorModule.java @@ -23,6 +23,8 @@ import org.cryptomator.ui.model.VaultObjectMapperProvider; import org.cryptomator.ui.settings.Settings; import org.cryptomator.ui.settings.SettingsProvider; import org.cryptomator.ui.util.DeferredCloser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; @@ -34,6 +36,7 @@ import javafx.stage.Stage; @Module(includes = {CryptoEngineModule.class, CommonsModule.class, WebDavModule.class}) class CryptomatorModule { + private static final Logger LOG = LoggerFactory.getLogger(CryptomatorModule.class); private final Application application; private final Stage mainWindow; @@ -59,7 +62,13 @@ class CryptomatorModule { @Singleton DeferredCloser provideDeferredCloser() { DeferredCloser closer = new DeferredCloser(); - Cryptomator.addShutdownTask(closer::close); + Cryptomator.addShutdownTask(() -> { + try { + closer.close(); + } catch (Exception e) { + LOG.error("Error during shutdown.", e); + } + }); return closer; } diff --git a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java index 86c16261b..d28721ffa 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java +++ b/main/ui/src/main/java/org/cryptomator/ui/MainApplication.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.ExecutionException; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.controllers.MainController; @@ -126,7 +127,11 @@ public class MainApplication extends Application { @Override public void stop() { - closer.close(); + try { + closer.close(); + } catch (ExecutionException e) { + LOG.error("Error closing ressources", e); + } } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java index c0e671d8e..3d0e58aa4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java @@ -117,12 +117,11 @@ public class UnlockedController extends LocalizedFXMLViewController { @FXML private void didClickLockVault(ActionEvent event) { asyncTaskService.asyncTaskOf(() -> { - vault.get().unmount(); vault.get().deactivateFrontend(); - }).onError(CommandFailedException.class, () -> { - messageLabel.setText(localization.getString("unlocked.label.unmountFailed")); - }).andFinally(() -> { + }).onSuccess(() -> { listener.ifPresent(listener -> listener.didLock(this)); + }).onError(Exception.class, () -> { + messageLabel.setText(localization.getString("unlocked.label.unmountFailed")); }).run(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java index 3647e8054..07c83530d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java @@ -148,7 +148,7 @@ public class Vault implements CryptoFileSystemDelegate { } } - public synchronized void deactivateFrontend() { + public synchronized void deactivateFrontend() throws Exception { filesystemFrontend.close(); statsFileSystem = Optional.empty(); Platform.runLater(() -> unlocked.set(false)); @@ -168,10 +168,6 @@ public class Vault implements CryptoFileSystemDelegate { Optionals.ifPresent(filesystemFrontend.get(), Frontend::reveal); } - public void unmount() throws CommandFailedException { - Optionals.ifPresent(filesystemFrontend.get(), Frontend::unmount); - } - // ****************************************************************************** // Delegate methods // ********************************************************************************/ diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java index fadb9f7dc..fa8c415ab 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredClosable.java @@ -28,12 +28,6 @@ public interface DeferredClosable extends AutoCloseable { */ public Optional get(); - /** - * Quietly closes the Object. If the object was closed before, nothing - * happens. - */ - public void close(); - /** * @return an empty object. */ diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java index c738e9f59..f2de9324f 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java +++ b/main/ui/src/main/java/org/cryptomator/ui/util/DeferredCloser.java @@ -13,12 +13,10 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; import org.cryptomator.common.ConsumerThrowingException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; @@ -31,7 +29,7 @@ import com.google.common.annotations.VisibleForTesting; * *

* If you have a {@link DeferredCloser} instance present, call - * {@link #closeLater(Object, Closer)} immediately after you have opened the + * {@link #closeLater(Object, ConsumerThrowingException)} immediately after you have opened the * resource and return a resource handle. If {@link #close()} is called, the * resource will be closed. Calling {@link DeferredClosable#close()} on the resource * handle will also close the resource and prevent a second closing by @@ -42,8 +40,6 @@ import com.google.common.annotations.VisibleForTesting; */ public class DeferredCloser implements AutoCloseable { - private static final Logger LOG = LoggerFactory.getLogger(DeferredCloser.class); - @VisibleForTesting final Map> cleanups = new ConcurrentSkipListMap<>(); @@ -51,33 +47,32 @@ public class DeferredCloser implements AutoCloseable { final AtomicLong counter = new AtomicLong(); private class ManagedResource implements DeferredClosable { + private final long number = counter.incrementAndGet(); - - private final AtomicReference object = new AtomicReference<>(); + private final T object; private final ConsumerThrowingException closer; + private boolean closed = false; public ManagedResource(T object, ConsumerThrowingException closer) { super(); - this.object.set(object); - this.closer = closer; + this.object = Objects.requireNonNull(object); + this.closer = Objects.requireNonNull(closer); } @Override - public void close() { - final T oldObject = object.getAndSet(null); - if (oldObject != null) { - cleanups.remove(number); - try { - closer.accept(oldObject); - } catch (Exception e) { - LOG.error("Closing resource failed.", e); - } - } + public synchronized void close() throws Exception { + closer.accept(object); + cleanups.remove(number); + closed = true; } @Override public Optional get() throws IllegalStateException { - return Optional.ofNullable(object.get()); + if (closed) { + return Optional.empty(); + } else { + return Optional.of(object); + } } } @@ -85,11 +80,23 @@ public class DeferredCloser implements AutoCloseable { * Closes all added objects which have not been closed before and releases references. */ @Override - public void close() { + public void close() throws ExecutionException { + ExecutionException exception = null; for (Iterator> iterator = cleanups.values().iterator(); iterator.hasNext();) { final ManagedResource closableProvider = iterator.next(); - closableProvider.close(); - iterator.remove(); + try { + closableProvider.close(); + iterator.remove(); + } catch (Exception e) { + if (exception == null) { + exception = new ExecutionException(e); + } else { + exception.addSuppressed(e); + } + } + } + if (exception != null) { + throw exception; } } From 3f0373b08f547cb239ba4a6b07bb782230febf2d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 13:57:52 +0200 Subject: [PATCH 04/11] removed xdg-utils dependencies, using gvfs-open instead of xdg-open. --- main/ant-kit/src/main/resources/package/linux/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/ant-kit/src/main/resources/package/linux/control b/main/ant-kit/src/main/resources/package/linux/control index 91a49094a..95d2a7148 100644 --- a/main/ant-kit/src/main/resources/package/linux/control +++ b/main/ant-kit/src/main/resources/package/linux/control @@ -9,7 +9,7 @@ Priority: optional Architecture: APPLICATION_ARCH Provides: APPLICATION_PACKAGE Installed-Size: APPLICATION_INSTALLED_SIZE -Depends: gvfs-bin, gvfs-backends, gvfs-fuse, xdg-utils +Depends: gvfs-bin, gvfs-backends, gvfs-fuse Description: Multi-platform client-side encryption of your cloud files. Cryptomator provides free client-side AES encryption for your cloud files. Create encrypted vaults, which get mounted as virtual volumes. Whatever From f6ebbb23d17760abd8e7767ba356ecd4c0c3c857 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 14:16:59 +0200 Subject: [PATCH 05/11] fixes coverity issue 141860 --- .../webdav/mount/WindowsDriveLetters.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsDriveLetters.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsDriveLetters.java index 10692e0da..76ed8d71d 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsDriveLetters.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/WindowsDriveLetters.java @@ -9,11 +9,11 @@ package org.cryptomator.frontend.webdav.mount; import static java.util.stream.Collectors.toSet; -import static java.util.stream.IntStream.rangeClosed; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.Set; +import java.util.stream.IntStream; import java.util.stream.StreamSupport; import javax.inject.Inject; @@ -24,16 +24,21 @@ import org.apache.commons.lang3.SystemUtils; import com.google.common.collect.Sets; - @Singleton public final class WindowsDriveLetters { - - private static final Set A_TO_Z = rangeClosed('A', 'Z').mapToObj(i -> (char) i).collect(toSet()); - + + private static final Set A_TO_Z; + + static { + try (IntStream stream = IntStream.rangeClosed('A', 'Z')) { + A_TO_Z = stream.mapToObj(i -> (char) i).collect(toSet()); + } + } + @Inject public WindowsDriveLetters() { } - + public Set getOccupiedDriveLetters() { if (!SystemUtils.IS_OS_WINDOWS) { throw new UnsupportedOperationException("This method is only defined for Windows file systems"); @@ -41,7 +46,7 @@ public final class WindowsDriveLetters { Iterable rootDirs = FileSystems.getDefault().getRootDirectories(); return StreamSupport.stream(rootDirs.spliterator(), false).map(Path::toString).map(CharUtils::toChar).map(Character::toUpperCase).collect(toSet()); } - + public Set getAvailableDriveLetters() { return Sets.difference(A_TO_Z, getOccupiedDriveLetters()); } From 5b5dd756b1ebd7a69ea30bfaffd778d83fd8edd2 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 14:27:48 +0200 Subject: [PATCH 06/11] fixes coverity issue 141858 --- .../src/main/java/org/cryptomator/frontend/FrontendId.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java index 97915c074..2d79eefbc 100644 --- a/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java +++ b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java @@ -3,6 +3,7 @@ package org.cryptomator.frontend; import static java.util.UUID.randomUUID; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.UUID; @@ -37,7 +38,7 @@ public class FrontendId { private static String asBase64String(ByteBuffer bytes) { ByteBuffer base64Buffer = Base64.getUrlEncoder().encode(bytes); - return new String(asByteArray(base64Buffer)); + return new String(asByteArray(base64Buffer), StandardCharsets.US_ASCII); } private static ByteBuffer nineBytesFrom(UUID uuid) { From 31368f0cba5fd97747f77ee2df23a90d5112f9f9 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 14:28:52 +0200 Subject: [PATCH 07/11] fixes coverity issue 141848 --- .../src/main/java/org/cryptomator/frontend/FrontendId.java | 3 ++- .../src/main/java/org/cryptomator/frontend/webdav/Tarpit.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java index 2d79eefbc..d5608bdac 100644 --- a/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java +++ b/main/frontend-api/src/main/java/org/cryptomator/frontend/FrontendId.java @@ -2,12 +2,13 @@ package org.cryptomator.frontend; import static java.util.UUID.randomUUID; +import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.UUID; -public class FrontendId { +public class FrontendId implements Serializable { public static final String FRONTEND_ID_PATTERN = "[a-zA-Z0-9_-]{12}"; diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java index 42226b3db..011adddc6 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/Tarpit.java @@ -8,6 +8,7 @@ package org.cryptomator.frontend.webdav; import static java.lang.Math.max; import static java.lang.System.currentTimeMillis; +import java.io.Serializable; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -21,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton -class Tarpit { +class Tarpit implements Serializable { private static final Logger LOG = LoggerFactory.getLogger(Tarpit.class); private static final long DELAY_MS = 10000; From 505b6542c77326a68c0e27904df784cb083b4298 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 14:30:48 +0200 Subject: [PATCH 08/11] fixes coverity issue 141844 --- .../filesystem/crypto/ConflictResolver.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java index 6b8a1f428..25b85e4dc 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java @@ -85,11 +85,15 @@ final class ConflictResolver { } else { ByteBuffer beginOfFile1 = ByteBuffer.allocate(sampleSize); ByteBuffer beginOfFile2 = ByteBuffer.allocate(sampleSize); - r1.read(beginOfFile1); - r2.read(beginOfFile2); - beginOfFile1.flip(); - beginOfFile2.flip(); - return beginOfFile1.equals(beginOfFile2); + int bytesRead1 = r1.read(beginOfFile1); + int bytesRead2 = r2.read(beginOfFile2); + if (bytesRead1 == sampleSize && bytesRead2 == sampleSize) { + beginOfFile1.flip(); + beginOfFile2.flip(); + return beginOfFile1.equals(beginOfFile2); + } else { + return false; + } } } } From 3b3ebd2196eba823714a3d2c30460c676f5ca869 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 14:39:05 +0200 Subject: [PATCH 09/11] fixes coverity issue 141838 --- .../frontend/webdav/mount/command/CommandRunner.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/command/CommandRunner.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/command/CommandRunner.java index 0184358c6..8df93e237 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/command/CommandRunner.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/command/CommandRunner.java @@ -61,13 +61,18 @@ final class CommandRunner { static CommandResult execute(Script script, long timeout, TimeUnit unit) throws CommandFailedException { try { final List env = script.environment().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.toList()); + final String[] lines = script.getLines(); + if (lines.length == 0) { + throw new IllegalArgumentException("Invalid script"); + } CommandResult result = null; - for (final String line : script.getLines()) { + for (final String line : lines) { final String[] cmds = ArrayUtils.add(determineCli(), line); final Process proc = Runtime.getRuntime().exec(cmds, env.toArray(new String[0])); result = run(proc, timeout, unit); result.assertOk(); } + assert result != null; return result; } catch (IOException e) { throw new CommandFailedException(e); From 6730a83caceb8c37084d64ee1c128b4b8f275420 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 14:45:38 +0200 Subject: [PATCH 10/11] fixes coverity issue 141842 --- .../webdav/jackrabbitservlet/ExclusiveSharedLockManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/ExclusiveSharedLockManager.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/ExclusiveSharedLockManager.java index b94ee9ed8..88225c855 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/ExclusiveSharedLockManager.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/jackrabbitservlet/ExclusiveSharedLockManager.java @@ -69,7 +69,8 @@ class ExclusiveSharedLockManager implements LockManager { } String token = DavConstants.OPAQUE_LOCK_TOKEN_PREFIX + UUID.randomUUID(); - return lockedResources.computeIfAbsent(locator, loc -> new HashMap<>()).computeIfAbsent(token, t -> new ExclusiveSharedLock(t, lockInfo)); + Map lockMap = Objects.requireNonNull(lockedResources.computeIfAbsent(locator, loc -> new HashMap<>())); + return lockMap.computeIfAbsent(token, t -> new ExclusiveSharedLock(t, lockInfo)); } private void removedExpiredLocksInLocatorHierarchy(FileSystemResourceLocator locator) { From a18c406cf0c08e5cf32ac3dfec5d329e33614ea7 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 14 Aug 2016 14:56:44 +0200 Subject: [PATCH 11/11] fixed ConflictResolver --- .../org/cryptomator/filesystem/crypto/ConflictResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java index 25b85e4dc..138ec226f 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java @@ -87,7 +87,7 @@ final class ConflictResolver { ByteBuffer beginOfFile2 = ByteBuffer.allocate(sampleSize); int bytesRead1 = r1.read(beginOfFile1); int bytesRead2 = r2.read(beginOfFile2); - if (bytesRead1 == sampleSize && bytesRead2 == sampleSize) { + if (bytesRead1 == bytesRead2) { beginOfFile1.flip(); beginOfFile2.flip(); return beginOfFile1.equals(beginOfFile2);