mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-19 17:16:53 -04:00
- determine available space in NIO file system (fixes #97)
- mount volumes via Finder, not via shell script. this makes creating volumes manually unnecessary
This commit is contained in:
@@ -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<Long> 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<Long> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<StatsFolder> {
|
||||
class StatsFile extends DelegatingFile<StatsFolder> {
|
||||
|
||||
private final Consumer<Long> readCounter;
|
||||
private final Consumer<Long> writeCounter;
|
||||
|
||||
@@ -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<StatsFolder, StatsFile> {
|
||||
class StatsFolder extends DelegatingFolder<StatsFolder, StatsFile> {
|
||||
|
||||
private final Consumer<Long> readCounter;
|
||||
private final Consumer<Long> writeCounter;
|
||||
|
||||
@@ -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<MountParam, Optional<String>> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user