diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFileSystem.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFileSystem.java index ff84bd8e3..a2b9863df 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFileSystem.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFileSystem.java @@ -1,5 +1,7 @@ package org.cryptomator.filesystem.nio; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; @@ -18,14 +20,25 @@ public class NioFileSystem extends NioFolder implements FileSystem { @Override public Optional quotaUsedBytes() { - // TODO du -sh - return Optional.empty(); + try { + long availableBytes = Files.getFileStore(path).getUsableSpace(); + long totalBytes = Files.getFileStore(path).getTotalSpace(); + return Optional.of(totalBytes - availableBytes); + } catch (IOException e) { + e.printStackTrace(); + return Optional.empty(); + } } @Override public Optional quotaAvailableBytes() { - // TODO df -lh - return Optional.empty(); + try { + long availableBytes = Files.getFileStore(path).getUsableSpace(); + return Optional.of(availableBytes); + } catch (IOException e) { + e.printStackTrace(); + return Optional.empty(); + } } } diff --git a/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFile.java b/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFile.java index fc028f3f0..490865157 100644 --- a/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFile.java +++ b/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFile.java @@ -19,7 +19,7 @@ import org.cryptomator.filesystem.delegating.DelegatingFile; import org.cryptomator.filesystem.delegating.DelegatingReadableFile; import org.cryptomator.filesystem.delegating.DelegatingWritableFile; -public class StatsFile extends DelegatingFile { +class StatsFile extends DelegatingFile { private final Consumer readCounter; private final Consumer writeCounter; diff --git a/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFolder.java b/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFolder.java index e3882eada..587aeb988 100644 --- a/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFolder.java +++ b/main/filesystem-stats/src/main/java/org/cryptomator/filesystem/stats/StatsFolder.java @@ -14,7 +14,7 @@ import org.cryptomator.filesystem.File; import org.cryptomator.filesystem.Folder; import org.cryptomator.filesystem.delegating.DelegatingFolder; -public class StatsFolder extends DelegatingFolder { +class StatsFolder extends DelegatingFolder { private final Consumer readCounter; private final Consumer writeCounter; diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/MacOsXWebDavMounter.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/MacOsXWebDavMounter.java index 964b7faa1..e7f3d5e2d 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/MacOsXWebDavMounter.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/mount/MacOsXWebDavMounter.java @@ -9,20 +9,21 @@ ******************************************************************************/ package org.cryptomator.frontend.webdav.mount; +import java.io.IOException; import java.net.URI; -import java.nio.file.FileSystems; -import java.nio.file.Files; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Optional; -import java.util.UUID; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Singleton; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.frontend.CommandFailedException; import org.cryptomator.frontend.Frontend.MountParam; -import org.cryptomator.frontend.webdav.mount.command.Script; @Singleton final class MacOsXWebDavMounter implements WebDavMounterStrategy { @@ -43,42 +44,77 @@ final class MacOsXWebDavMounter implements WebDavMounterStrategy { @Override public WebDavMount mount(URI uri, Map> mountParams) throws CommandFailedException { - final String mountName = mountParams.get(MountParam.MOUNT_NAME).orElseThrow(() -> { - return new IllegalArgumentException("Missing mount parameter MOUNT_NAME."); - }); - - // we don't use the uri to derive a path, as it *could* be longer than 255 chars. - final String path = "/Volumes/Cryptomator_" + UUID.randomUUID().toString(); - final Script mountScript = Script.fromLines("mkdir \"$MOUNT_PATH\"", "mount_webdav -S -v $MOUNT_NAME \"$DAV_AUTHORITY$DAV_PATH\" \"$MOUNT_PATH\"").addEnv("DAV_AUTHORITY", uri.getRawAuthority()) - .addEnv("DAV_PATH", uri.getRawPath()).addEnv("MOUNT_PATH", path).addEnv("MOUNT_NAME", mountName); - mountScript.execute(); - return new MacWebDavMount(path); + try { + String mountAppleScript = String.format("mount volume \"%s\"", uri.toString()); + ProcessBuilder mount = new ProcessBuilder("/usr/bin/osascript", "-e", mountAppleScript); + Process mountProcess = mount.start(); + String stdout = IOUtils.toString(mountProcess.getInputStream(), StandardCharsets.UTF_8); + waitForProcessAndCheckSuccess(mountProcess, 1, TimeUnit.SECONDS); + String volumeIdentifier = StringUtils.trim(StringUtils.removeStart(stdout, "file ")); + String waitAppleScript1 = String.format("tell application \"Finder\" to repeat while not (\"%s\" exists)", volumeIdentifier); + String waitAppleScript2 = "delay 0.1"; + String waitAppleScript3 = "end repeat"; + ProcessBuilder wait = new ProcessBuilder("/usr/bin/osascript", "-e", waitAppleScript1, "-e", waitAppleScript2, "-e", waitAppleScript3); + Process waitProcess = wait.start(); + waitForProcessAndCheckSuccess(waitProcess, 5, TimeUnit.SECONDS); + return new MacWebDavMount(volumeIdentifier); + } catch (IOException e) { + throw new CommandFailedException(e); + } } private static class MacWebDavMount extends AbstractWebDavMount { - private final String mountPath; - private final Script revealScript; - private final Script unmountScript; + private final ProcessBuilder revealCommand; + private final ProcessBuilder unmountCommand; - private MacWebDavMount(String mountPath) { - this.mountPath = mountPath; - this.revealScript = Script.fromLines("open \"$MOUNT_PATH\"").addEnv("MOUNT_PATH", mountPath); - this.unmountScript = Script.fromLines("diskutil umount $MOUNT_PATH").addEnv("MOUNT_PATH", mountPath); + private MacWebDavMount(String volumeIdentifier) { + String openAppleScript = String.format("tell application \"Finder\" to open \"%s\"", volumeIdentifier); + String activateAppleScript = String.format("tell application \"Finder\" to activate \"%s\"", volumeIdentifier); + String ejectAppleScript = String.format("tell application \"Finder\" to if \"%s\" exists then eject \"%s\"", volumeIdentifier, volumeIdentifier); + + System.err.println("open: " + openAppleScript + "\nactivate: " + activateAppleScript + "\neject: " + ejectAppleScript); + + this.revealCommand = new ProcessBuilder("/usr/bin/osascript", "-e", openAppleScript, "-e", activateAppleScript); + this.unmountCommand = new ProcessBuilder("/usr/bin/osascript", "-e", ejectAppleScript); } @Override public void unmount() throws CommandFailedException { - // only attempt unmount if user didn't unmount manually: - if (Files.exists(FileSystems.getDefault().getPath(mountPath))) { - unmountScript.execute(); + try { + Process proc = unmountCommand.start(); + waitForProcessAndCheckSuccess(proc, 1, TimeUnit.SECONDS); + } catch (IOException e) { + throw new CommandFailedException(e); } } @Override public void reveal() throws CommandFailedException { - revealScript.execute(); + try { + Process proc = revealCommand.start(); + waitForProcessAndCheckSuccess(proc, 1, TimeUnit.SECONDS); + } catch (IOException e) { + throw new CommandFailedException(e); + } } } + private static void waitForProcessAndCheckSuccess(Process proc, long timeout, TimeUnit unit) throws CommandFailedException, IOException { + try { + boolean finishedInTime = proc.waitFor(timeout, unit); + if (!finishedInTime) { + proc.destroyForcibly(); + throw new CommandFailedException("Command timed out."); + } + int exitCode = proc.exitValue(); + if (exitCode != 0) { + String error = IOUtils.toString(proc.getErrorStream(), StandardCharsets.UTF_8); + throw new CommandFailedException("Command failed with exit code " + exitCode + ". Stderr: " + error); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + }