diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f6bf7f3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+# maven
+target
+dependency-reduced-pom.xml
+
+# IDE
+.idea
+.settings
+.project
+.classpath
+
+# MacOS
+.DS_Store
+
+# Other
+.factorypath
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6894cf9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+Command line interface to access to a vault encrypted with [Cryptomator](https://github.com/cryptomator/cryptomator)
+
+If you find this project useful, consider donating to [CRYPTOMATOR](https://cryptomator.org/donate/)
+
+## Features
+
+- Access Cryptomator encrypted vaults opened with a Java command line on a platform compatible with [FUSE](https://en.wikipedia.org/wiki/Filesystem_in_Userspace)
+- Create a Debian package containing the application and a minimum JRE
+
+## Build
+
+This project requires [Maven](https://maven.apache.org/) and a JDK version 11.
+
+For a smaller package, the build should be done with the community version of Azul, [Zulu](https://www.azul.com/downloads/zulu-community/?version=java-11-lts&os=linux&package=jdk) (around deb 27MB / installed 47MB with Zulu11.39+15-CA (build 11.0.7+10-LTS), but deb 87MB/ installed 322MB with 11.0.8+10-post-Ubuntu-0ubuntu118.04.1).
+
+The command `dpkg-deb` is necessary to create the Debian package.
+
+## Usage
+
+The command `/opt/cryfsmount/bin/cryfsmount` can create a new vault or migrate an old vault to the latest format.
+
+Close the vault be stopping the process.
+
+If the mount point is not set, a new temporary directory is created.
+
+The passphrase of the vault can be typed, set in a file or in an environment variable.
+
+*WARNING*: when the passphrase is set in a file, make sure there is no trailer end-of-line. For the string `My pass`, create the file with
+```
+echo -n "My pass" > pass.txt
+```
+
+*WARNING*: the command `cryfsumount` stops ALL the running `cryfsmount` processes.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..130ddc6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,267 @@
+
+
+ 4.0.0
+ com.github.llbrt.cryptofssrv
+ cryfsmount
+ 1.0
+ jar
+
+
+ scm:git:git@github.com:llbrt/cryfsmount.git
+ scm:git:git@github.com:llbrt/cryfsmount.git
+ https://github.com/llbrt/cryfsmount
+
+
+
+
+ GNU Affero General Public License (AGPL) version 3.0
+ https://www.gnu.org/licenses/agpl.txt
+ manual
+
+
+
+
+ 11
+ UTF-8
+ ${java.version}
+ ${java.version}
+
+ cryfsmount
+ cryfsumount
+
+
+ 1.2.4
+ 1.9.12
+ 4.5.1
+ 1.2.3
+ 5.7.0
+
+
+
+
+
+ org.cryptomator
+ fuse-nio-adapter
+ ${cryptomator-fuse.version}
+
+
+ org.cryptomator
+ cryptofs
+ ${cryptomator-fs.version}
+
+
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+
+
+
+
+ info.picocli
+ picocli
+ ${picocli.version}
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.version}
+ test
+
+
+
+
+
+
+ src/main/resources
+ ${project.build.outputDirectory}
+ true
+
+
+ src/packaging/skel
+ ${project.build.directory}/packaging/opt/${cmd.mount}
+ true
+
+
+ src/packaging/deb
+ ${project.build.directory}/packaging/
+ true
+
+
+
+
+
+ maven-compiler-plugin
+
+ ${java.version}
+ true
+
+
+
+ maven-resources-plugin
+
+ \
+
+
+
+ maven-shade-plugin
+
+
+
+ shade
+
+
+ false
+
+
+ com.github.llbrt.cryptofs.CryFsMount
+
+
+ ${project.build.directory}/packaging/opt/${cmd.mount}/app/${cmd.mount}.jar
+
+
+
+
+
+ maven-antrun-plugin
+
+
+
+
+
+
+
+
+
+ run
+
+ package
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ package-jre
+
+ exec
+
+ package
+
+
+ jlink
+
+ --no-header-files
+ --no-man-pages
+ --compress=2
+ --strip-debug
+ --add-modules
+
+
+ java.base,java.compiler,java.logging,java.management,java.naming,java.sql,java.xml,jdk.unsupported,jdk.jcmd
+ --output
+ ${project.build.directory}/packaging/opt/${cmd.mount}/jre
+
+
+
+
+ create-deb-package
+
+ exec
+
+ package
+
+
+ dpkg-deb
+
+ --root-owner-group
+ --build
+ ${project.build.directory}/packaging
+ ${project.build.directory}/${cmd.mount}_${project.version}_amd64.deb
+
+
+
+
+
+
+ maven-enforcer-plugin
+
+
+ enforce-java
+
+ enforce
+
+
+
+
+ ${java.version}
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ versions-maven-plugin
+
+
+ compile
+
+ display-property-updates
+
+
+
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+
+ maven-dependency-plugin
+ 3.1.2
+
+
+ maven-resources-plugin
+ 3.2.0
+
+
+ maven-surefire-plugin
+ 2.22.2
+
+
+ maven-shade-plugin
+ 3.2.4
+
+
+ maven-antrun-plugin
+ 3.0.0
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.0.0
+
+
+ maven-enforcer-plugin
+ 3.0.0-M3
+
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ 2.8.1
+
+
+
+
+
diff --git a/src/main/java/com/github/llbrt/cryptofs/CryFsMount.java b/src/main/java/com/github/llbrt/cryptofs/CryFsMount.java
new file mode 100644
index 0000000..d40f88f
--- /dev/null
+++ b/src/main/java/com/github/llbrt/cryptofs/CryFsMount.java
@@ -0,0 +1,47 @@
+package com.github.llbrt.cryptofs;
+
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import picocli.CommandLine;
+
+public final class CryFsMount {
+
+ private static final Logger logger = LoggerFactory.getLogger(CryFsMount.class);
+
+ public static void main(String[] args) {
+ CommandLine cmd = new CommandLine(new Mount());
+ int exitCode = cmd.execute(args);
+ if (exitCode != 0) {
+ System.exit(exitCode);
+ }
+ MountedFs mountedFs = cmd.getExecutionResult();
+ String message = mountedFs + " mounted on " + mountedFs.getMountPoint();
+ logger.info(message);
+ System.out.println(message);
+
+ // Prepare umount
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> mountedFs.umount(), "Umounter"));
+
+ // Wait for kill in a separate thread
+ new Thread(() -> {
+ while (true) {
+ try {
+ TimeUnit.MINUTES.sleep(4);
+ } catch (InterruptedException e) {
+ // ignored
+ }
+ }
+ }, "Keep alive thread").start();
+
+ try {
+ System.in.close();
+ } catch (Exception e) {
+ logger.warn("Failed to close input stream", e);
+ }
+ System.out.close();
+ System.err.close();
+ }
+}
diff --git a/src/main/java/com/github/llbrt/cryptofs/Mount.java b/src/main/java/com/github/llbrt/cryptofs/Mount.java
new file mode 100644
index 0000000..597c523
--- /dev/null
+++ b/src/main/java/com/github/llbrt/cryptofs/Mount.java
@@ -0,0 +1,102 @@
+package com.github.llbrt.cryptofs;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.Callable;
+
+import com.github.llbrt.cryptofs.fuse.FuseCryptoFs;
+import com.github.llbrt.cryptofs.fuse.FuseCryptoFs.MountOptions;
+
+import picocli.CommandLine.ArgGroup;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Model.CommandSpec;
+import picocli.CommandLine.Option;
+import picocli.CommandLine.ParameterException;
+import picocli.CommandLine.Parameters;
+import picocli.CommandLine.Spec;
+
+@Command(name = "cryfsmount")
+public final class Mount implements Callable {
+
+ @ArgGroup(exclusive = true, multiplicity = "1")
+ private Passphrase passphrase;
+
+ static class Passphrase {
+ @Option(names = { "--passphrase:file" }, description = "Path of the file containing the passphrase")
+ private Path passphrasePath;
+
+ @Option(names = { "--passphrase:env" }, description = "Name of the environment variable containing the passphrase")
+ private String passphraseEnvironmentVariable;
+
+ @Option(names = { "-p", "--passphrase" }, interactive = true)
+ private char[] passphrase;
+ }
+
+ @Option(names = { "-c", "--create", "--initialize" }, description = "Creates a new vault")
+ private boolean initializeVault;
+ @Option(names = { "-m" }, description = "Migrates the vault if necessary")
+ private boolean migrateFs;
+ @Option(names = { "-r", "--read-only" }, description = "Mounts the vault read-only")
+ private boolean readOnly;
+
+ @Parameters(index = "0", description = "Path to the vault")
+ private Path vaultDir;
+
+ @Parameters(index = "1", arity = "0..1", description = "Mount point to access to the vault")
+ private Path mountPoint;
+
+ @Spec
+ private CommandSpec spec;
+
+ @Override
+ public MountedFs call() throws Exception {
+ char[] vaultPassphrase;
+ if (passphrase.passphrase != null) {
+ vaultPassphrase = passphrase.passphrase;
+ } else if (passphrase.passphraseEnvironmentVariable != null) {
+ String vaultPass = System.getenv(passphrase.passphraseEnvironmentVariable);
+ vaultPassphrase = vaultPass.toCharArray();
+ } else if (passphrase.passphrasePath != null) {
+ String vaultPass = new String(Files.readAllBytes(passphrase.passphrasePath));
+ vaultPassphrase = vaultPass.toCharArray();
+ } else {
+ throw new ParameterException(spec.commandLine(), "Password required");
+ }
+
+ // Vault must exist and not be empty except on initialization
+ checkIsDirectory(vaultDir, "Vauld directory", initializeVault, initializeVault);
+
+ // If set, the mount point must exist and be empty
+ if (mountPoint != null) {
+ checkIsDirectory(mountPoint, "Mount point", false, true);
+ }
+
+ MountOptions mo = FuseCryptoFs.mountOptions(vaultDir, vaultPassphrase)
+ .mountPoint(mountPoint);
+ if (initializeVault)
+ mo.initializeVault();
+ if (migrateFs)
+ mo.migrateFs();
+ if (readOnly)
+ mo.readOnly();
+
+ return mo.mount();
+ }
+
+ private final void checkIsDirectory(Path path, String name, boolean mayNotExist, boolean mustBeEmpty) {
+ var pathFile = path.toFile();
+ if (!pathFile.exists()) {
+ if (mayNotExist) {
+ // Done
+ return;
+ }
+ throw new ParameterException(spec.commandLine(), name + " not found");
+ }
+ if (!pathFile.isDirectory()) {
+ throw new ParameterException(spec.commandLine(), name + " is not a directory");
+ }
+ if (mustBeEmpty && pathFile.list().length > 0) {
+ throw new ParameterException(spec.commandLine(), name + " is not empty");
+ }
+ }
+}
diff --git a/src/main/java/com/github/llbrt/cryptofs/MountedFs.java b/src/main/java/com/github/llbrt/cryptofs/MountedFs.java
new file mode 100644
index 0000000..fb7111d
--- /dev/null
+++ b/src/main/java/com/github/llbrt/cryptofs/MountedFs.java
@@ -0,0 +1,14 @@
+package com.github.llbrt.cryptofs;
+
+import java.nio.file.Path;
+
+import org.cryptomator.cryptofs.CryptoFileSystem;
+
+public interface MountedFs {
+
+ CryptoFileSystem getFs();
+
+ Path getMountPoint();
+
+ void umount();
+}
diff --git a/src/main/java/com/github/llbrt/cryptofs/fuse/FuseCryptoFs.java b/src/main/java/com/github/llbrt/cryptofs/fuse/FuseCryptoFs.java
new file mode 100644
index 0000000..3b53d7e
--- /dev/null
+++ b/src/main/java/com/github/llbrt/cryptofs/fuse/FuseCryptoFs.java
@@ -0,0 +1,173 @@
+package com.github.llbrt.cryptofs.fuse;
+
+import static org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags.MIGRATE_IMPLICITLY;
+import static org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags.READONLY;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.cryptomator.cryptofs.CryptoFileSystem;
+import org.cryptomator.cryptofs.CryptoFileSystemProperties;
+import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
+import org.cryptomator.cryptofs.CryptoFileSystemProvider;
+import org.cryptomator.frontend.fuse.mount.CommandFailedException;
+import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
+import org.cryptomator.frontend.fuse.mount.FuseMountFactory;
+import org.cryptomator.frontend.fuse.mount.Mount;
+import org.cryptomator.frontend.fuse.mount.Mounter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.llbrt.cryptofs.MountedFs;
+
+public final class FuseCryptoFs implements MountedFs {
+ private static final Logger log = LoggerFactory.getLogger(FuseCryptoFs.class);
+
+ private static final FileSystemFlags[] EMPTY_FLAGS = new FileSystemFlags[0];
+
+ private static final String DEFAULT_MASTERKEY_FILENAME = "masterkey.cryptomator";
+
+ private final CryptoFileSystem fs;
+ private final Mount mount;
+ private final Path mountPoint;
+
+ private FuseCryptoFs(CryptoFileSystem fs, Mount mount, Path mountPoint) {
+ this.fs = fs;
+ this.mount = mount;
+ this.mountPoint = mountPoint;
+ }
+
+ @Override
+ public CryptoFileSystem getFs() {
+ return fs;
+ }
+
+ @Override
+ public Path getMountPoint() {
+ return mountPoint;
+ }
+
+ @Override
+ public void umount() {
+ try {
+ mount.unmount();
+ } catch (Exception e) {
+ try {
+ log.warn("umount failed, try to force umount", e);
+ mount.unmountForced();
+ } catch (Exception e1) {
+ log.error("Force umount failed", e);
+ }
+ } finally {
+ try {
+ mount.close();
+ } catch (Exception e) {
+ log.warn("close failed", e);
+ }
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fs.getPathToVault());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof FuseCryptoFs)) {
+ return false;
+ }
+ FuseCryptoFs other = (FuseCryptoFs) obj;
+ return Objects.equals(fs, other.fs);
+ }
+
+ @Override
+ public String toString() {
+ return fs.toString();
+ }
+
+ public static MountedFs mount(CryptoFileSystem fs, Path mountPoint) {
+ Mounter mounter = FuseMountFactory.getMounter();
+ EnvironmentVariables envVars = EnvironmentVariables.create()
+ .withFlags(mounter.defaultMountFlags())
+ .withMountPoint(mountPoint)
+ .build();
+ try {
+ return new FuseCryptoFs(fs, mounter.mount(fs.getPath("/"), envVars), mountPoint);
+ } catch (CommandFailedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static MountOptions mountOptions(Path vaultDir, char[] vaultPassphrase) {
+ return new MountOptions(vaultDir, new String(vaultPassphrase));
+ }
+
+ public static final class MountOptions {
+ private final Path vaultDir;
+ private final String passphrase;
+ private Path mountPoint;
+ private boolean initializeVault;
+ private boolean migrateFs;
+ private boolean readOnly;
+
+ MountOptions(Path vaultDir, String passphrase) {
+ this.vaultDir = vaultDir;
+ this.passphrase = passphrase;
+ }
+
+ public final MountOptions mountPoint(Path mountPoint) {
+ this.mountPoint = mountPoint;
+ return this;
+ }
+
+ public final MountOptions initializeVault() {
+ this.initializeVault = true;
+ return this;
+ }
+
+ public final MountOptions migrateFs() {
+ this.migrateFs = true;
+ return this;
+ }
+
+ public final MountOptions readOnly() {
+ this.readOnly = true;
+ return this;
+ }
+
+ public final MountedFs mount() throws IOException {
+ List flags = new ArrayList<>();
+ if (migrateFs) {
+ flags.add(MIGRATE_IMPLICITLY);
+ }
+ if (readOnly) {
+ flags.add(READONLY);
+ }
+ CryptoFileSystemProperties cryptoFileSystemProperties = CryptoFileSystemProperties
+ .withPassphrase(passphrase)
+ .withFlags(flags.toArray(EMPTY_FLAGS))
+ .build();
+
+ if (mountPoint == null) {
+ mountPoint = Files.createTempDirectory("cryfsmount-");
+ log.info("Mount point created: " + mountPoint);
+ } else {
+ log.info("Mount point: " + mountPoint);
+ }
+ if (initializeVault) {
+ Files.createDirectories(vaultDir);
+ CryptoFileSystemProvider.initialize(vaultDir, DEFAULT_MASTERKEY_FILENAME, passphrase);
+ }
+ CryptoFileSystem fs = CryptoFileSystemProvider.newFileSystem(vaultDir, cryptoFileSystemProperties);
+ return FuseCryptoFs.mount(fs, mountPoint);
+ }
+ }
+}
diff --git a/src/main/java/com/github/llbrt/utils/logs/ExtendedPatternLayoutEncoder.java b/src/main/java/com/github/llbrt/utils/logs/ExtendedPatternLayoutEncoder.java
new file mode 100644
index 0000000..273aa15
--- /dev/null
+++ b/src/main/java/com/github/llbrt/utils/logs/ExtendedPatternLayoutEncoder.java
@@ -0,0 +1,17 @@
+package com.github.llbrt.utils.logs;
+
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+
+/**
+ * Declares a new converter to add the process id in the logs.
+ */
+public final class ExtendedPatternLayoutEncoder extends PatternLayoutEncoder {
+ @Override
+ public void start() {
+ // Declare new converter
+ PatternLayout.defaultConverterMap.put(
+ "processId", ProcessIdConverter.class.getName());
+ super.start();
+ }
+}
diff --git a/src/main/java/com/github/llbrt/utils/logs/ProcessIdConverter.java b/src/main/java/com/github/llbrt/utils/logs/ProcessIdConverter.java
new file mode 100644
index 0000000..e7a6c7c
--- /dev/null
+++ b/src/main/java/com/github/llbrt/utils/logs/ProcessIdConverter.java
@@ -0,0 +1,20 @@
+package com.github.llbrt.utils.logs;
+
+import java.lang.management.ManagementFactory;
+
+import ch.qos.logback.classic.pattern.ClassicConverter;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+
+/**
+ * Converts the process id pattern declares by {@link ExtendedPatternLayoutEncoder}.
+ */
+public final class ProcessIdConverter extends ClassicConverter {
+ private static final String PROCESS_ID = Long.toString(ManagementFactory.getRuntimeMXBean().getPid());
+
+ @Override
+ public String convert(final ILoggingEvent event) {
+ // for every logging event return processId from mx bean
+ // (or better alternative)
+ return PROCESS_ID;
+ }
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 0000000..961540b
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ /opt/${cmd.mount}/logs/\${user.name}.log
+
+ /opt/${cmd.mount}/logs/roll-by-size/\${user.name}.%i.log.gz
+ 1
+ 9
+ 200MB
+ true
+
+
+ 16MB
+
+
+ %processId %d [%thread] %-5level %logger{35} - %msg%n
+
+
+
+
+
+
+
diff --git a/src/packaging/deb/DEBIAN/control b/src/packaging/deb/DEBIAN/control
new file mode 100644
index 0000000..680a968
--- /dev/null
+++ b/src/packaging/deb/DEBIAN/control
@@ -0,0 +1,10 @@
+Package: ${cmd.mount}
+Version: ${project.version}
+Architecture: amd64
+Maintainer: llbrt
+Installed-Size: 47747072
+Section: contrib/admin
+Priority: optional
+Essential: no
+Homepage: https://github.com/llbrt/cryptofssrv
+Description: Mounts a filesystem ciphered by Cryptomator
diff --git a/src/packaging/deb/DEBIAN/postinst b/src/packaging/deb/DEBIAN/postinst
new file mode 100755
index 0000000..1b735ad
--- /dev/null
+++ b/src/packaging/deb/DEBIAN/postinst
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -e
+
+# Create log directory
+LOGS_DIR=/opt/${cmd.mount}/logs
+mkdir -p $LOGS_DIR
+chmod a+r $LOGS_DIR
+chmod a+w $LOGS_DIR
+chmod a+x $LOGS_DIR
+
+exit 0
diff --git a/src/packaging/skel/bin/mount.sh b/src/packaging/skel/bin/mount.sh
new file mode 100755
index 0000000..40654f9
--- /dev/null
+++ b/src/packaging/skel/bin/mount.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+exec /opt/${cmd.mount}/jre/bin/java -jar /opt/${cmd.mount}/app/${cmd.mount}.jar "$@"
\ No newline at end of file
diff --git a/src/packaging/skel/bin/umount.sh b/src/packaging/skel/bin/umount.sh
new file mode 100755
index 0000000..c51e10a
--- /dev/null
+++ b/src/packaging/skel/bin/umount.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+KILLSIG=" "
+while getopts ":f" opt; do
+ case ${opt} in
+ f ) # force kill
+ KILLSIG=-9
+ ;;
+ \? ) echo "Usage: ${BASH_SOURCE##*/} [-f]" && exit 1
+ ;;
+ esac
+done
+
+MOUNTED=`/opt/${cmd.mount}/jre/bin/jps | grep ${cmd.mount}.jar | cut -d ' ' -f 1`
+[ -z "$MOUNTED" ] || kill $KILLSIG $MOUNTED
\ No newline at end of file
diff --git a/src/test/java/org/github/llbrt/cryptofssrv/fuse/TestFuseCryptoFs.java b/src/test/java/org/github/llbrt/cryptofssrv/fuse/TestFuseCryptoFs.java
new file mode 100644
index 0000000..db66df2
--- /dev/null
+++ b/src/test/java/org/github/llbrt/cryptofssrv/fuse/TestFuseCryptoFs.java
@@ -0,0 +1,284 @@
+package org.github.llbrt.cryptofssrv.fuse;
+
+import static java.nio.file.FileVisitResult.CONTINUE;
+import static java.nio.file.StandardOpenOption.CREATE_NEW;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileSystemException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.cryptomator.cryptofs.FileSystemNeedsMigrationException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import com.github.llbrt.cryptofs.MountedFs;
+import com.github.llbrt.cryptofs.fuse.FuseCryptoFs;
+import com.github.llbrt.cryptofs.fuse.FuseCryptoFs.MountOptions;
+import com.google.common.io.BaseEncoding;
+
+public class TestFuseCryptoFs {
+
+ private static final char[] PASSPHRASE = "T€st-Un1t".toCharArray();
+
+ private static final String VAULT_OLDER_FORMAT = "vault-1.4.11";
+ private static final String VAULT_CURRENT_FORMAT = "vault-1.5.6";
+
+ private static final String SUM_MD5_FILE = "sum.md5";
+ private static final String CREATED_DIR = "newDirectory";
+ private static final String CREATED_FILE = "newFile";
+ private static final int WRITTEN = 12;
+
+ @TempDir
+ public Path tempDirRoot;
+
+ private Path mountPoint;
+
+ @BeforeEach
+ public void createMountPoint() throws IOException {
+ mountPoint = tempDirRoot.resolve("mountPoint");
+ Files.createDirectory(mountPoint);
+ }
+
+ @Test
+ public void testMountOlderVersion_fails() throws IOException {
+ assertThrows(FileSystemNeedsMigrationException.class,
+ () -> prepareTestVault(VAULT_OLDER_FORMAT)
+ .mount());
+ }
+
+ @Test
+ public void testMountOlderVersion_migrates() throws IOException {
+ Path oldFormatForMigration = copyVault(VAULT_OLDER_FORMAT, "toMigrate");
+ MountedFs mounted = prepareTestVault(oldFormatForMigration)
+ .migrateFs()
+ .mount();
+ testFilledMountedFs(mounted, false);
+ }
+
+ @Test
+ public void testMountOlderVersion_readOnly_fails() throws IOException {
+ assertThrows(FileSystemNeedsMigrationException.class,
+ () -> prepareTestVault(VAULT_OLDER_FORMAT)
+ .readOnly()
+ .mount());
+ }
+
+ @Test
+ public void testMountCurrentVersion() throws IOException {
+ Path writableVault = copyVault(VAULT_CURRENT_FORMAT, "writable");
+ MountedFs mounted = prepareTestVault(writableVault)
+ .mount();
+ testFilledMountedFs(mounted, false);
+ }
+
+ @Test
+ public void testMountCurrentVersion_readOnly_cannotWrite() throws IOException {
+ Path notWritableVault = copyVault(VAULT_CURRENT_FORMAT, "not-writable");
+ MountedFs mounted = prepareTestVault(notWritableVault)
+ .readOnly()
+ .mount();
+ testFilledMountedFs(mounted, true);
+ }
+
+ @Test
+ public void testMountCurrentVersion_mountPointUnset() throws IOException {
+ Path preMountPoint = mountPoint;
+ mountPoint = null;
+ Path vault = copyVault(VAULT_CURRENT_FORMAT, "dyn-mount-point");
+ MountedFs mounted = prepareTestVault(vault)
+ .mount();
+ mountPoint = mounted.getMountPoint();
+ assertNotEquals(preMountPoint, mountPoint);
+ testFilledMountedFs(mounted, false);
+
+ // After umount, the mount point must be empty and can be deleted
+ Files.delete(mountPoint);
+ }
+
+ @Test
+ public void testMountNewVault() throws IOException {
+ Path vault = tempDirRoot.resolve("empty-vault");
+ Files.createDirectory(vault);
+ MountedFs mounted = prepareTestVault(vault)
+ .initializeVault()
+ .mount();
+ testFillEmptyMountedFs(mounted);
+ testFilledEmptyMountedFs(FuseCryptoFs.mount(mounted.getFs(), mounted.getMountPoint()));
+ }
+
+ @Test
+ public void testMountNewVault_notInitialized_fails() throws IOException {
+ Path vault = tempDirRoot.resolve("empty-vault");
+ Files.createDirectory(vault);
+ assertThrows(NoSuchFileException.class, () -> prepareTestVault(vault)
+ .mount());
+ }
+
+ @Test
+ public void testMountCurrentVersion_initialize_fails() throws IOException {
+ Path initializedVault = copyVault(VAULT_CURRENT_FORMAT, "initialized");
+ assertThrows(FileAlreadyExistsException.class, () -> prepareTestVault(initializedVault)
+ .initializeVault()
+ .mount());
+ }
+
+ private void testFilledMountedFs(MountedFs mounted, boolean expectWriteFailure) throws IOException {
+ try {
+ checkReferenceSums();
+
+ Path mountPoint = mounted.getMountPoint();
+ Path newFile = mountPoint.resolve(CREATED_FILE);
+ if (expectWriteFailure) {
+ assertThrows(FileSystemException.class, () -> Files.copy(sumFile(), newFile));
+ } else {
+ Files.copy(sumFile(), newFile);
+ }
+ } finally {
+ mounted.umount();
+ }
+ }
+
+ private void testFillEmptyMountedFs(MountedFs mounted) throws IOException {
+ try {
+ Path mountPoint = mounted.getMountPoint();
+ // Creates a file
+ Path newFile = mountPoint.resolve(CREATED_FILE);
+ try (OutputStream os = Files.newOutputStream(newFile, CREATE_NEW)) {
+ os.write(WRITTEN);
+ }
+ // Creates a directory
+ Path newDir = mountPoint.resolve(CREATED_DIR);
+ Files.createDirectory(newDir);
+ } finally {
+ mounted.umount();
+ }
+ }
+
+ private void testFilledEmptyMountedFs(MountedFs mounted) throws IOException {
+ try {
+ Path mountPoint = mounted.getMountPoint();
+ // Created file
+ Path createdFile = mountPoint.resolve(CREATED_FILE);
+ assertTrue(Files.isRegularFile(createdFile));
+ try (InputStream is = Files.newInputStream(createdFile, CREATE_NEW)) {
+ assertEquals(WRITTEN, is.read());
+ }
+ // Created directory
+ Path createdDir = mountPoint.resolve(CREATED_DIR);
+ assertTrue(Files.isDirectory(createdDir));
+ } finally {
+ mounted.umount();
+ }
+ }
+
+ private MountOptions prepareTestVault(String name) {
+ return prepareTestVault(getVaultPath(name));
+ }
+
+ private MountOptions prepareTestVault(Path vaultDir) {
+ return FuseCryptoFs.mountOptions(vaultDir, PASSPHRASE)
+ .mountPoint(mountPoint);
+ }
+
+ private Path getVaultPath(String name) {
+ ClassLoader classLoader = getClass().getClassLoader();
+ File file = new File(classLoader.getResource(name).getFile());
+ assertTrue(file.isDirectory());
+ return file.toPath();
+ }
+
+ private Map loadSums() throws IOException {
+ Map result = new HashMap<>();
+ try (Stream stream = Files.lines(sumFile())) {
+ stream.forEach(s -> {
+ // Line format: ./dir1/.../file
+ String[] split = s.split(" ");
+ result.put(mountPoint.resolve(split[1].substring(2)), split[0]);
+ });
+ }
+ return result;
+ }
+
+ private Path sumFile() {
+ return mountPoint.resolve(SUM_MD5_FILE);
+ }
+
+ private void checkReferenceSums() throws IOException {
+ Map sums = loadSums();
+ List remainingFiles = Files.walk(mountPoint)
+ .filter(p -> p.toFile().isFile())
+ .filter(p -> md5SumMatches(p, sums))
+ .collect(Collectors.toList());
+
+ // Only the sum file should remain
+ assertEquals(1, remainingFiles.size());
+ assertEquals(sumFile(), remainingFiles.get(0));
+ }
+
+ private boolean md5SumMatches(Path path, Map sums) {
+ try {
+ String sum = sums.remove(path);
+ if (sum == null) {
+ return true;
+ }
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ try (InputStream is = Files.newInputStream(path);
+ DigestInputStream dis = new DigestInputStream(is, md)) {
+ dis.readAllBytes();
+ }
+ assertEquals(sum, BaseEncoding.base16().encode(md.digest()).toLowerCase(), path.toString());
+ return false;
+ } catch (Exception e) {
+ fail(e);
+ return true;
+ }
+ }
+
+ private Path copyVault(String source, String dirName) throws IOException {
+ Path oldFormatForMigration = tempDirRoot.resolve(dirName);
+
+ Path root = getVaultPath(source);
+ Files.walkFileTree(root, new SimpleFileVisitor() {
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ Files.createDirectory(toCreate(dir));
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.copy(file, toCreate(file));
+ return CONTINUE;
+ }
+
+ private Path toCreate(Path path) {
+ return oldFormatForMigration.resolve(root.relativize(path));
+ }
+ });
+
+ return oldFormatForMigration;
+ }
+}
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..18b778a
--- /dev/null
+++ b/src/test/resources/logback-test.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ %processId %d [%thread] %-5level %logger{35} - %msg%n
+
+
+
+
+
+
+
diff --git a/src/test/resources/vault-1.4.11/d/2N/ARVQMSFDNU7VTXXW3VLTAC26L76GMK/VSKBJCIKFVDDBWAQ24WOCLA4K6E36WHGYXGM5NI= b/src/test/resources/vault-1.4.11/d/2N/ARVQMSFDNU7VTXXW3VLTAC26L76GMK/VSKBJCIKFVDDBWAQ24WOCLA4K6E36WHGYXGM5NI=
new file mode 100644
index 0000000..1e53267
Binary files /dev/null and b/src/test/resources/vault-1.4.11/d/2N/ARVQMSFDNU7VTXXW3VLTAC26L76GMK/VSKBJCIKFVDDBWAQ24WOCLA4K6E36WHGYXGM5NI= differ
diff --git a/src/test/resources/vault-1.4.11/d/3H/7JM2ZIBYYMH2VXIQOLJMXAOJJ6JBEQ/GENM2A5Q4BIU2AGQP73HEE256DIYP4QY43UEGBTX4OCA==== b/src/test/resources/vault-1.4.11/d/3H/7JM2ZIBYYMH2VXIQOLJMXAOJJ6JBEQ/GENM2A5Q4BIU2AGQP73HEE256DIYP4QY43UEGBTX4OCA====
new file mode 100644
index 0000000..b7b1376
Binary files /dev/null and b/src/test/resources/vault-1.4.11/d/3H/7JM2ZIBYYMH2VXIQOLJMXAOJJ6JBEQ/GENM2A5Q4BIU2AGQP73HEE256DIYP4QY43UEGBTX4OCA==== differ
diff --git a/src/test/resources/vault-1.4.11/d/5H/X4FDBSRHSZP3HFRC5IPC5ERSZBBBF4/0SOG6ED7XNDF5NO5T2RXHRIBV7QEKUKRSITVO57A= b/src/test/resources/vault-1.4.11/d/5H/X4FDBSRHSZP3HFRC5IPC5ERSZBBBF4/0SOG6ED7XNDF5NO5T2RXHRIBV7QEKUKRSITVO57A=
new file mode 100644
index 0000000..4f49b8a
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/5H/X4FDBSRHSZP3HFRC5IPC5ERSZBBBF4/0SOG6ED7XNDF5NO5T2RXHRIBV7QEKUKRSITVO57A=
@@ -0,0 +1 @@
+98fcfd41-b3a6-455f-99e8-b550a1bd8957
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/6E/S2D66AGOWZH722U2UU4RG3SHADCTSQ/0F6CTZEZQ2ZFWW3RGNSWJQQ7ZAR2PK7NXNATN2=== b/src/test/resources/vault-1.4.11/d/6E/S2D66AGOWZH722U2UU4RG3SHADCTSQ/0F6CTZEZQ2ZFWW3RGNSWJQQ7ZAR2PK7NXNATN2===
new file mode 100644
index 0000000..3a13359
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/6E/S2D66AGOWZH722U2UU4RG3SHADCTSQ/0F6CTZEZQ2ZFWW3RGNSWJQQ7ZAR2PK7NXNATN2===
@@ -0,0 +1 @@
+83595c6f-f041-4019-9c54-9039a8cbf0f0
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/7L/JUFSL7NIUUAFH465HFVMHM5ZXYL3K2/0MGUROH7EY4MLYPCL66RBAED74XOMMC7L3BPLQ=== b/src/test/resources/vault-1.4.11/d/7L/JUFSL7NIUUAFH465HFVMHM5ZXYL3K2/0MGUROH7EY4MLYPCL66RBAED74XOMMC7L3BPLQ===
new file mode 100644
index 0000000..bf38b3d
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/7L/JUFSL7NIUUAFH465HFVMHM5ZXYL3K2/0MGUROH7EY4MLYPCL66RBAED74XOMMC7L3BPLQ===
@@ -0,0 +1 @@
+1c17f918-7206-4c7d-89bb-3dfd084ec98e
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/AB/XH65AGE7ZBAQFTJGMI6RF5NYPOIHKA/0QXKJKKGXC2TOZGJF3I7S3STA6OQ52WJ3NWOQ==== b/src/test/resources/vault-1.4.11/d/AB/XH65AGE7ZBAQFTJGMI6RF5NYPOIHKA/0QXKJKKGXC2TOZGJF3I7S3STA6OQ52WJ3NWOQ====
new file mode 100644
index 0000000..f4bc13c
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/AB/XH65AGE7ZBAQFTJGMI6RF5NYPOIHKA/0QXKJKKGXC2TOZGJF3I7S3STA6OQ52WJ3NWOQ====
@@ -0,0 +1 @@
+5465d6ba-9ff4-4048-9302-cf213ba866e7
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/AQ/E6NNJXCM457VSUZMAXNBNA7KHIEOZ3/0R2PPBYN6HIZCASSFSXJV7U7BYV3H3QJUS7NTA=== b/src/test/resources/vault-1.4.11/d/AQ/E6NNJXCM457VSUZMAXNBNA7KHIEOZ3/0R2PPBYN6HIZCASSFSXJV7U7BYV3H3QJUS7NTA===
new file mode 100644
index 0000000..611bac1
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/AQ/E6NNJXCM457VSUZMAXNBNA7KHIEOZ3/0R2PPBYN6HIZCASSFSXJV7U7BYV3H3QJUS7NTA===
@@ -0,0 +1 @@
+841e8a54-37e4-488b-a630-2e3651ba04d0
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/AS7CUZTWKWUMMH4H45YL5RR7REUVRWLFM2KVFZYDSEZDO=== b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/AS7CUZTWKWUMMH4H45YL5RR7REUVRWLFM2KVFZYDSEZDO===
new file mode 100644
index 0000000..ac2b721
Binary files /dev/null and b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/AS7CUZTWKWUMMH4H45YL5RR7REUVRWLFM2KVFZYDSEZDO=== differ
diff --git a/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/EDJMFHWNTR7R7VERV4WNYTZDKER4CBM75C2MDSUTL4XQ==== b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/EDJMFHWNTR7R7VERV4WNYTZDKER4CBM75C2MDSUTL4XQ====
new file mode 100644
index 0000000..530f96c
Binary files /dev/null and b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/EDJMFHWNTR7R7VERV4WNYTZDKER4CBM75C2MDSUTL4XQ==== differ
diff --git a/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/HG6I44H7UU2TXDTBEMNHK4BKV4DFAMKUATYEES2IFHVVI=== b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/HG6I44H7UU2TXDTBEMNHK4BKV4DFAMKUATYEES2IFHVVI===
new file mode 100644
index 0000000..0cf615b
Binary files /dev/null and b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/HG6I44H7UU2TXDTBEMNHK4BKV4DFAMKUATYEES2IFHVVI=== differ
diff --git a/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/LQ5RQJ5P2HZBAS6VCZ42RNCDXJYCIIXY3O5FSNIDDXYBY=== b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/LQ5RQJ5P2HZBAS6VCZ42RNCDXJYCIIXY3O5FSNIDDXYBY===
new file mode 100644
index 0000000..2f1bb3a
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/LQ5RQJ5P2HZBAS6VCZ42RNCDXJYCIIXY3O5FSNIDDXYBY===
@@ -0,0 +1 @@
+p,"Dӂ-̚~Ы){"dS饮:)[TY-d5plF^]Ѫ+i!V\H4J0@>B9e8r>Dfk^e N'Eh&<WŇ鈄=~r?R_`k* 8-զZsi{z,lsI
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/NHK2VWUHSDGRVQ5IHFTSNQMXYGXCLED7L6DKCZ4B5BYPU=== b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/NHK2VWUHSDGRVQ5IHFTSNQMXYGXCLED7L6DKCZ4B5BYPU===
new file mode 100644
index 0000000..be5d3fc
Binary files /dev/null and b/src/test/resources/vault-1.4.11/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/NHK2VWUHSDGRVQ5IHFTSNQMXYGXCLED7L6DKCZ4B5BYPU=== differ
diff --git a/src/test/resources/vault-1.4.11/d/CG/I2MBG742BT5YTH5Y35OSRTXUTGNOSY/0VBAVTBFUKKZASGSOB67OV6ZIQ7CZR5LLDILCNGSINZ6A==== b/src/test/resources/vault-1.4.11/d/CG/I2MBG742BT5YTH5Y35OSRTXUTGNOSY/0VBAVTBFUKKZASGSOB67OV6ZIQ7CZR5LLDILCNGSINZ6A====
new file mode 100644
index 0000000..ce88abd
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/CG/I2MBG742BT5YTH5Y35OSRTXUTGNOSY/0VBAVTBFUKKZASGSOB67OV6ZIQ7CZR5LLDILCNGSINZ6A====
@@ -0,0 +1 @@
+5e7c2585-fc49-48c6-80f3-86da27c18733
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/CS/KTWMTKBU5N23M4R27JNWNNDG62JPL6/0EV4U4SOUJU3ZA25JOWQWPWX3U56G3ILXL3Z72FCE4Q====== b/src/test/resources/vault-1.4.11/d/CS/KTWMTKBU5N23M4R27JNWNNDG62JPL6/0EV4U4SOUJU3ZA25JOWQWPWX3U56G3ILXL3Z72FCE4Q======
new file mode 100644
index 0000000..51f19e2
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/CS/KTWMTKBU5N23M4R27JNWNNDG62JPL6/0EV4U4SOUJU3ZA25JOWQWPWX3U56G3ILXL3Z72FCE4Q======
@@ -0,0 +1 @@
+42304c2a-a216-4f79-b303-c4f6938e5879
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/FY/OYN4UD6H5GBTHMI5REI6R6DOHEICRP/0HGKAHJZK5UKJGV4GASAAE36HEXWLPVQI b/src/test/resources/vault-1.4.11/d/FY/OYN4UD6H5GBTHMI5REI6R6DOHEICRP/0HGKAHJZK5UKJGV4GASAAE36HEXWLPVQI
new file mode 100644
index 0000000..2c29188
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/FY/OYN4UD6H5GBTHMI5REI6R6DOHEICRP/0HGKAHJZK5UKJGV4GASAAE36HEXWLPVQI
@@ -0,0 +1 @@
+21af5b76-3a9e-42d2-964e-75e8b0beb9a6
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/H3/5LNUVNWKCXCYQIB3MBY7NOU3L5WAIM/0PRMTWX6KCKRYISXXZOKN45IRCVUGSZY7Q2TEPV6A b/src/test/resources/vault-1.4.11/d/H3/5LNUVNWKCXCYQIB3MBY7NOU3L5WAIM/0PRMTWX6KCKRYISXXZOKN45IRCVUGSZY7Q2TEPV6A
new file mode 100644
index 0000000..fcfc14f
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/H3/5LNUVNWKCXCYQIB3MBY7NOU3L5WAIM/0PRMTWX6KCKRYISXXZOKN45IRCVUGSZY7Q2TEPV6A
@@ -0,0 +1 @@
+30515163-95b7-478b-b404-7e8976572d81
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/MC/LYZV56PWZ4JHLQQ7KLGQGKIXONMTV5/0HUK2CMVONFE35C25F7ZHUWVY6WAWG5HQ7UO5E=== b/src/test/resources/vault-1.4.11/d/MC/LYZV56PWZ4JHLQQ7KLGQGKIXONMTV5/0HUK2CMVONFE35C25F7ZHUWVY6WAWG5HQ7UO5E===
new file mode 100644
index 0000000..fa763b1
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/MC/LYZV56PWZ4JHLQQ7KLGQGKIXONMTV5/0HUK2CMVONFE35C25F7ZHUWVY6WAWG5HQ7UO5E===
@@ -0,0 +1 @@
+c250e0ec-2bf5-41bd-8f0f-f6e6e72e8410
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/RG/QNMEGW7JXXMG4TC6NXFCIHQU344RPL/0EVBQCGV3GCWH532YDU2HFJQCSEPRW5Y7LI4Q4=== b/src/test/resources/vault-1.4.11/d/RG/QNMEGW7JXXMG4TC6NXFCIHQU344RPL/0EVBQCGV3GCWH532YDU2HFJQCSEPRW5Y7LI4Q4===
new file mode 100644
index 0000000..0df8271
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/RG/QNMEGW7JXXMG4TC6NXFCIHQU344RPL/0EVBQCGV3GCWH532YDU2HFJQCSEPRW5Y7LI4Q4===
@@ -0,0 +1 @@
+18c4d001-6460-446a-9d61-cccdd4aa768b
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/W3/6PMUW63NWFWJUVBCL4DWTH4DKPH4KR/0E4VEN7KZHAXQFRVA7BB64Q5RGEZFUPTQ7ACLY=== b/src/test/resources/vault-1.4.11/d/W3/6PMUW63NWFWJUVBCL4DWTH4DKPH4KR/0E4VEN7KZHAXQFRVA7BB64Q5RGEZFUPTQ7ACLY===
new file mode 100644
index 0000000..50473fd
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/W3/6PMUW63NWFWJUVBCL4DWTH4DKPH4KR/0E4VEN7KZHAXQFRVA7BB64Q5RGEZFUPTQ7ACLY===
@@ -0,0 +1 @@
+829e4dd6-cf20-429e-9a4f-3e68d175b67b
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/WI/LVZJ37SR7HUL4QWJK7NFNFLBZRKXOV/0X27FM3XNO52WS3YAYZIFCRN5BBO3ULBJU4I7Y=== b/src/test/resources/vault-1.4.11/d/WI/LVZJ37SR7HUL4QWJK7NFNFLBZRKXOV/0X27FM3XNO52WS3YAYZIFCRN5BBO3ULBJU4I7Y===
new file mode 100644
index 0000000..7879621
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/WI/LVZJ37SR7HUL4QWJK7NFNFLBZRKXOV/0X27FM3XNO52WS3YAYZIFCRN5BBO3ULBJU4I7Y===
@@ -0,0 +1 @@
+39c1ff64-2b12-47bf-ad97-2ba526e34def
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/WL/LU26VNR5KHTDSMGHA7HG3EJBJBR2QG/0SU52FYOW33CFVUUK3IFMBF4BXHLGDHMI6E====== b/src/test/resources/vault-1.4.11/d/WL/LU26VNR5KHTDSMGHA7HG3EJBJBR2QG/0SU52FYOW33CFVUUK3IFMBF4BXHLGDHMI6E======
new file mode 100644
index 0000000..205d1d9
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/WL/LU26VNR5KHTDSMGHA7HG3EJBJBR2QG/0SU52FYOW33CFVUUK3IFMBF4BXHLGDHMI6E======
@@ -0,0 +1 @@
+bc98da68-b39c-46e5-83aa-4c3507f4c75d
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/03AV43G77N2FVFN7WKSLPJGP2NGZB7M5ZE5IQ==== b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/03AV43G77N2FVFN7WKSLPJGP2NGZB7M5ZE5IQ====
new file mode 100644
index 0000000..3a295d4
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/03AV43G77N2FVFN7WKSLPJGP2NGZB7M5ZE5IQ====
@@ -0,0 +1 @@
+502d3d87-0d53-4cbc-acda-38b149c46b0a
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/0HH7CCK7VSO7Y45XH762LR6CVJWALU3U4 b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/0HH7CCK7VSO7Y45XH762LR6CVJWALU3U4
new file mode 100644
index 0000000..b0d7236
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/0HH7CCK7VSO7Y45XH762LR6CVJWALU3U4
@@ -0,0 +1 @@
+75d952da-39c2-4875-8510-5e00394d33a3
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/NALTC32EWY7CMS6QPVA45FDJU6QYN6GR4BVIE=== b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/NALTC32EWY7CMS6QPVA45FDJU6QYN6GR4BVIE===
new file mode 100644
index 0000000..9bbb26d
Binary files /dev/null and b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/NALTC32EWY7CMS6QPVA45FDJU6QYN6GR4BVIE=== differ
diff --git a/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/YHXPS2N7377SWPKVIW5WMGNBHBHKLKPU.lng b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/YHXPS2N7377SWPKVIW5WMGNBHBHKLKPU.lng
new file mode 100644
index 0000000..02292f9
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/YHXPS2N7377SWPKVIW5WMGNBHBHKLKPU.lng
@@ -0,0 +1 @@
+4b0c2a3a-6bd5-4fae-8090-5b119553bbee
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/m/YH/XP/YHXPS2N7377SWPKVIW5WMGNBHBHKLKPU.lng b/src/test/resources/vault-1.4.11/m/YH/XP/YHXPS2N7377SWPKVIW5WMGNBHBHKLKPU.lng
new file mode 100644
index 0000000..e4f7c01
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/m/YH/XP/YHXPS2N7377SWPKVIW5WMGNBHBHKLKPU.lng
@@ -0,0 +1 @@
+024ZFFAPPONWBJ72NIIEWCGRLEIVRIY2H53RUCUA4GGWW35PJTIBXTRNPNPSGXE4ZNFDV2JQ6K5PTZXFCRCHSWJT2GZALE5ZNR4J43SJIEHGIWAESGHDBGRHLD3IFOALD4LRJUWJSY3MHV7D2LPE7EAUZH5O4DW4ZMFPQRANSIN2I77KS3RTX3C2ISEO6Q42Y4OGANIU5RKEQRRTMIT7N33UESH6A4JNKT2APH23EOE3Q2WTXN2AXAFCS3C6RJRZ6TGHBFRY3GTKQYPNKGV3RICO7OEN7GYJ7O64DMPBTMX747PE2K6S7EF23RURMO5IDJYIJJKFAUTWET5Z4GCJXUHK53U2Z664T4NVMHJFJH63TCXRHZCTLM4KOAKBRM5WEAVIBZC3XIOWDZPYPRIVPC5RQK7MMH4AHKIBZ2EYDKCSTZOQ2D5LOGOFSKN4NEK4MAGFJUH5C5GCVZQ3K4BLKP463LPWP3ZMJ3J5FRUD45V5IMEPX2X7YVBCMHBMI2BA2RF6WWGKNBR2Y2HJV5ZGVVQBCPP4AZ2HB
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/masterkey.cryptomator b/src/test/resources/vault-1.4.11/masterkey.cryptomator
new file mode 100644
index 0000000..262894a
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/masterkey.cryptomator
@@ -0,0 +1,9 @@
+{
+ "scryptSalt": "c+0ijA8emRU=",
+ "scryptCostParam": 32768,
+ "scryptBlockSize": 8,
+ "primaryMasterKey": "LGdflvoaQNgH/gmih3mR2s5R03/i7ILv1rVWoz+rS+ggpJ374pTa2g==",
+ "hmacMasterKey": "iXURU9NOX1NtIpycBjL9htwHcMBrlC0DUmMlmpa/cw8QG4PMRZMzfg==",
+ "versionMac": "jUUdjhO4nip/AR+Xh/ydbinC99qPmszZsGzkyqDWCN0=",
+ "version": 6
+}
\ No newline at end of file
diff --git a/src/test/resources/vault-1.4.11/masterkey.cryptomator.bkup b/src/test/resources/vault-1.4.11/masterkey.cryptomator.bkup
new file mode 100644
index 0000000..262894a
--- /dev/null
+++ b/src/test/resources/vault-1.4.11/masterkey.cryptomator.bkup
@@ -0,0 +1,9 @@
+{
+ "scryptSalt": "c+0ijA8emRU=",
+ "scryptCostParam": 32768,
+ "scryptBlockSize": 8,
+ "primaryMasterKey": "LGdflvoaQNgH/gmih3mR2s5R03/i7ILv1rVWoz+rS+ggpJ374pTa2g==",
+ "hmacMasterKey": "iXURU9NOX1NtIpycBjL9htwHcMBrlC0DUmMlmpa/cw8QG4PMRZMzfg==",
+ "versionMac": "jUUdjhO4nip/AR+Xh/ydbinC99qPmszZsGzkyqDWCN0=",
+ "version": 6
+}
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/2N/ARVQMSFDNU7VTXXW3VLTAC26L76GMK/rJQUiQotRjDYENcs4SwcV4m_WObFzM61.c9r b/src/test/resources/vault-1.5.6/d/2N/ARVQMSFDNU7VTXXW3VLTAC26L76GMK/rJQUiQotRjDYENcs4SwcV4m_WObFzM61.c9r
new file mode 100644
index 0000000..1e53267
Binary files /dev/null and b/src/test/resources/vault-1.5.6/d/2N/ARVQMSFDNU7VTXXW3VLTAC26L76GMK/rJQUiQotRjDYENcs4SwcV4m_WObFzM61.c9r differ
diff --git a/src/test/resources/vault-1.5.6/d/3H/7JM2ZIBYYMH2VXIQOLJMXAOJJ6JBEQ/MRrNA7DgUU0A0H_2chNd8NGH8hjm6EMGd-OE.c9r b/src/test/resources/vault-1.5.6/d/3H/7JM2ZIBYYMH2VXIQOLJMXAOJJ6JBEQ/MRrNA7DgUU0A0H_2chNd8NGH8hjm6EMGd-OE.c9r
new file mode 100644
index 0000000..b7b1376
Binary files /dev/null and b/src/test/resources/vault-1.5.6/d/3H/7JM2ZIBYYMH2VXIQOLJMXAOJJ6JBEQ/MRrNA7DgUU0A0H_2chNd8NGH8hjm6EMGd-OE.c9r differ
diff --git a/src/test/resources/vault-1.5.6/d/5H/X4FDBSRHSZP3HFRC5IPC5ERSZBBBF4/k43iD_doy9a7s9RueKA1_AiqKjJE6u78.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/5H/X4FDBSRHSZP3HFRC5IPC5ERSZBBBF4/k43iD_doy9a7s9RueKA1_AiqKjJE6u78.c9r/dir.c9r
new file mode 100644
index 0000000..4f49b8a
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/5H/X4FDBSRHSZP3HFRC5IPC5ERSZBBBF4/k43iD_doy9a7s9RueKA1_AiqKjJE6u78.c9r/dir.c9r
@@ -0,0 +1 @@
+98fcfd41-b3a6-455f-99e8-b550a1bd8957
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/6E/S2D66AGOWZH722U2UU4RG3SHADCTSQ/L4U8kzDWS2tuJmysmEP5BHT1fbdoJt0=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/6E/S2D66AGOWZH722U2UU4RG3SHADCTSQ/L4U8kzDWS2tuJmysmEP5BHT1fbdoJt0=.c9r/dir.c9r
new file mode 100644
index 0000000..3a13359
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/6E/S2D66AGOWZH722U2UU4RG3SHADCTSQ/L4U8kzDWS2tuJmysmEP5BHT1fbdoJt0=.c9r/dir.c9r
@@ -0,0 +1 @@
+83595c6f-f041-4019-9c54-9039a8cbf0f0
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/7L/JUFSL7NIUUAFH465HFVMHM5ZXYL3K2/YakXH-THGLw8S_eiEBB_5dzGC-vYXrg=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/7L/JUFSL7NIUUAFH465HFVMHM5ZXYL3K2/YakXH-THGLw8S_eiEBB_5dzGC-vYXrg=.c9r/dir.c9r
new file mode 100644
index 0000000..bf38b3d
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/7L/JUFSL7NIUUAFH465HFVMHM5ZXYL3K2/YakXH-THGLw8S_eiEBB_5dzGC-vYXrg=.c9r/dir.c9r
@@ -0,0 +1 @@
+1c17f918-7206-4c7d-89bb-3dfd084ec98e
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/AB/XH65AGE7ZBAQFTJGMI6RF5NYPOIHKA/hdSVKNcWpuyZJdo_Lcpg86HdWTttnQ==.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/AB/XH65AGE7ZBAQFTJGMI6RF5NYPOIHKA/hdSVKNcWpuyZJdo_Lcpg86HdWTttnQ==.c9r/dir.c9r
new file mode 100644
index 0000000..f4bc13c
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/AB/XH65AGE7ZBAQFTJGMI6RF5NYPOIHKA/hdSVKNcWpuyZJdo_Lcpg86HdWTttnQ==.c9r/dir.c9r
@@ -0,0 +1 @@
+5465d6ba-9ff4-4048-9302-cf213ba866e7
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/AQ/E6NNJXCM457VSUZMAXNBNA7KHIEOZ3/jp7w4b46MiBKRZXTX9PhxXZ9wTSX2zA=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/AQ/E6NNJXCM457VSUZMAXNBNA7KHIEOZ3/jp7w4b46MiBKRZXTX9PhxXZ9wTSX2zA=.c9r/dir.c9r
new file mode 100644
index 0000000..611bac1
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/AQ/E6NNJXCM457VSUZMAXNBNA7KHIEOZ3/jp7w4b46MiBKRZXTX9PhxXZ9wTSX2zA=.c9r/dir.c9r
@@ -0,0 +1 @@
+841e8a54-37e4-488b-a630-2e3651ba04d0
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/BL4qZnZVqMYfh-dwvsY_iSlY2WVmlVLnA5EyNw==.c9r b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/BL4qZnZVqMYfh-dwvsY_iSlY2WVmlVLnA5EyNw==.c9r
new file mode 100644
index 0000000..ac2b721
Binary files /dev/null and b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/BL4qZnZVqMYfh-dwvsY_iSlY2WVmlVLnA5EyNw==.c9r differ
diff --git a/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/INLCns2cfx_Uka8s3E8jUSPBBZ_otMHKk18v.c9r b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/INLCns2cfx_Uka8s3E8jUSPBBZ_otMHKk18v.c9r
new file mode 100644
index 0000000..530f96c
Binary files /dev/null and b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/INLCns2cfx_Uka8s3E8jUSPBBZ_otMHKk18v.c9r differ
diff --git a/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/ObyOcP-lNTuOYSMadXAqrwZQMVQE8EJLSCnrVA==.c9r b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/ObyOcP-lNTuOYSMadXAqrwZQMVQE8EJLSCnrVA==.c9r
new file mode 100644
index 0000000..0cf615b
Binary files /dev/null and b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/ObyOcP-lNTuOYSMadXAqrwZQMVQE8EJLSCnrVA==.c9r differ
diff --git a/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/XDsYJ6_R8hBL1RZ5qLRDunAkIvjbulk1Ax3wHA==.c9r b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/XDsYJ6_R8hBL1RZ5qLRDunAkIvjbulk1Ax3wHA==.c9r
new file mode 100644
index 0000000..2f1bb3a
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/XDsYJ6_R8hBL1RZ5qLRDunAkIvjbulk1Ax3wHA==.c9r
@@ -0,0 +1 @@
+p,"Dӂ-̚~Ы){"dS饮:)[TY-d5plF^]Ѫ+i!V\H4J0@>B9e8r>Dfk^e N'Eh&<WŇ鈄=~r?R_`k* 8-զZsi{z,lsI
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/adWq2oeQzRrDqDlnJsGXwa4lkH9fhqFngehw-g==.c9r b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/adWq2oeQzRrDqDlnJsGXwa4lkH9fhqFngehw-g==.c9r
new file mode 100644
index 0000000..be5d3fc
Binary files /dev/null and b/src/test/resources/vault-1.5.6/d/BK/KRGVIOJMQ26ZXRHBFYZD4Q5VXQUMT6/adWq2oeQzRrDqDlnJsGXwa4lkH9fhqFngehw-g==.c9r differ
diff --git a/src/test/resources/vault-1.5.6/d/CG/I2MBG742BT5YTH5Y35OSRTXUTGNOSY/qEFZhLRSsgkaTg--6vsoh8WY9WsaFiaaSG58.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/CG/I2MBG742BT5YTH5Y35OSRTXUTGNOSY/qEFZhLRSsgkaTg--6vsoh8WY9WsaFiaaSG58.c9r/dir.c9r
new file mode 100644
index 0000000..ce88abd
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/CG/I2MBG742BT5YTH5Y35OSRTXUTGNOSY/qEFZhLRSsgkaTg--6vsoh8WY9WsaFiaaSG58.c9r/dir.c9r
@@ -0,0 +1 @@
+5e7c2585-fc49-48c6-80f3-86da27c18733
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/CS/KTWMTKBU5N23M4R27JNWNNDG62JPL6/JXlOSdRNN5BrqXWhZ9r7p3xtoXde8_0UROQ=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/CS/KTWMTKBU5N23M4R27JNWNNDG62JPL6/JXlOSdRNN5BrqXWhZ9r7p3xtoXde8_0UROQ=.c9r/dir.c9r
new file mode 100644
index 0000000..51f19e2
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/CS/KTWMTKBU5N23M4R27JNWNNDG62JPL6/JXlOSdRNN5BrqXWhZ9r7p3xtoXde8_0UROQ=.c9r/dir.c9r
@@ -0,0 +1 @@
+42304c2a-a216-4f79-b303-c4f6938e5879
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/FY/OYN4UD6H5GBTHMI5REI6R6DOHEICRP/OZQDpyrtFJNXhgSAAm_HJey31gg=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/FY/OYN4UD6H5GBTHMI5REI6R6DOHEICRP/OZQDpyrtFJNXhgSAAm_HJey31gg=.c9r/dir.c9r
new file mode 100644
index 0000000..2c29188
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/FY/OYN4UD6H5GBTHMI5REI6R6DOHEICRP/OZQDpyrtFJNXhgSAAm_HJey31gg=.c9r/dir.c9r
@@ -0,0 +1 @@
+21af5b76-3a9e-42d2-964e-75e8b0beb9a6
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/H3/5LNUVNWKCXCYQIB3MBY7NOU3L5WAIM/fFk7X8oSo4RK98uU3nURFWhpZx-GpkfXwA==.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/H3/5LNUVNWKCXCYQIB3MBY7NOU3L5WAIM/fFk7X8oSo4RK98uU3nURFWhpZx-GpkfXwA==.c9r/dir.c9r
new file mode 100644
index 0000000..fcfc14f
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/H3/5LNUVNWKCXCYQIB3MBY7NOU3L5WAIM/fFk7X8oSo4RK98uU3nURFWhpZx-GpkfXwA==.c9r/dir.c9r
@@ -0,0 +1 @@
+30515163-95b7-478b-b404-7e8976572d81
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/MC/LYZV56PWZ4JHLQQ7KLGQGKIXONMTV5/PRWhMq5pSb6LXS_yelq49YFjdPD9HdI=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/MC/LYZV56PWZ4JHLQQ7KLGQGKIXONMTV5/PRWhMq5pSb6LXS_yelq49YFjdPD9HdI=.c9r/dir.c9r
new file mode 100644
index 0000000..fa763b1
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/MC/LYZV56PWZ4JHLQQ7KLGQGKIXONMTV5/PRWhMq5pSb6LXS_yelq49YFjdPD9HdI=.c9r/dir.c9r
@@ -0,0 +1 @@
+c250e0ec-2bf5-41bd-8f0f-f6e6e72e8410
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/RG/QNMEGW7JXXMG4TC6NXFCIHQU344RPL/JUMBGrswrH7vWB00cqYCkR8bdx9aOQ4=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/RG/QNMEGW7JXXMG4TC6NXFCIHQU344RPL/JUMBGrswrH7vWB00cqYCkR8bdx9aOQ4=.c9r/dir.c9r
new file mode 100644
index 0000000..0df8271
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/RG/QNMEGW7JXXMG4TC6NXFCIHQU344RPL/JUMBGrswrH7vWB00cqYCkR8bdx9aOQ4=.c9r/dir.c9r
@@ -0,0 +1 @@
+18c4d001-6460-446a-9d61-cccdd4aa768b
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/W3/6PMUW63NWFWJUVBCL4DWTH4DKPH4KR/JypG_Vk4LwLGoPhD7kOxMTJaPnD4BLw=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/W3/6PMUW63NWFWJUVBCL4DWTH4DKPH4KR/JypG_Vk4LwLGoPhD7kOxMTJaPnD4BLw=.c9r/dir.c9r
new file mode 100644
index 0000000..50473fd
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/W3/6PMUW63NWFWJUVBCL4DWTH4DKPH4KR/JypG_Vk4LwLGoPhD7kOxMTJaPnD4BLw=.c9r/dir.c9r
@@ -0,0 +1 @@
+829e4dd6-cf20-429e-9a4f-3e68d175b67b
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/WI/LVZJ37SR7HUL4QWJK7NFNFLBZRKXOV/vr5Wbu13dWlvAMZQUUW9CF26LCmnEfw=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/WI/LVZJ37SR7HUL4QWJK7NFNFLBZRKXOV/vr5Wbu13dWlvAMZQUUW9CF26LCmnEfw=.c9r/dir.c9r
new file mode 100644
index 0000000..7879621
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/WI/LVZJ37SR7HUL4QWJK7NFNFLBZRKXOV/vr5Wbu13dWlvAMZQUUW9CF26LCmnEfw=.c9r/dir.c9r
@@ -0,0 +1 @@
+39c1ff64-2b12-47bf-ad97-2ba526e34def
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/WL/LU26VNR5KHTDSMGHA7HG3EJBJBR2QG/lTui4dbexFrSitoKwJeBudZhnYjx.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/WL/LU26VNR5KHTDSMGHA7HG3EJBJBR2QG/lTui4dbexFrSitoKwJeBudZhnYjx.c9r/dir.c9r
new file mode 100644
index 0000000..205d1d9
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/WL/LU26VNR5KHTDSMGHA7HG3EJBJBR2QG/lTui4dbexFrSitoKwJeBudZhnYjx.c9r/dir.c9r
@@ -0,0 +1 @@
+bc98da68-b39c-46e5-83aa-4c3507f4c75d
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/2CvNm_9ui1K39lSW9Jn6abIfs7knUQ==.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/2CvNm_9ui1K39lSW9Jn6abIfs7knUQ==.c9r/dir.c9r
new file mode 100644
index 0000000..3a295d4
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/2CvNm_9ui1K39lSW9Jn6abIfs7knUQ==.c9r/dir.c9r
@@ -0,0 +1 @@
+502d3d87-0d53-4cbc-acda-38b149c46b0a
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/Of4hK_WTv4525_-0uPhVTYC6bpw=.c9r/dir.c9r b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/Of4hK_WTv4525_-0uPhVTYC6bpw=.c9r/dir.c9r
new file mode 100644
index 0000000..b0d7236
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/Of4hK_WTv4525_-0uPhVTYC6bpw=.c9r/dir.c9r
@@ -0,0 +1 @@
+75d952da-39c2-4875-8510-5e00394d33a3
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/aBcxb0S2PiZL0H1BzpRpp6GG-NHgaoI=.c9r b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/aBcxb0S2PiZL0H1BzpRpp6GG-NHgaoI=.c9r
new file mode 100644
index 0000000..9bbb26d
Binary files /dev/null and b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/aBcxb0S2PiZL0H1BzpRpp6GG-NHgaoI=.c9r differ
diff --git a/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/nJNMg3EQSgooXOSRuWvWKd-_iFM=.c9s/dir.c9r b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/nJNMg3EQSgooXOSRuWvWKd-_iFM=.c9s/dir.c9r
new file mode 100644
index 0000000..02292f9
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/nJNMg3EQSgooXOSRuWvWKd-_iFM=.c9s/dir.c9r
@@ -0,0 +1 @@
+4b0c2a3a-6bd5-4fae-8090-5b119553bbee
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/nJNMg3EQSgooXOSRuWvWKd-_iFM=.c9s/name.c9s b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/nJNMg3EQSgooXOSRuWvWKd-_iFM=.c9s/name.c9s
new file mode 100644
index 0000000..afe6f71
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/d/XF/K7A4X4N6WZUEJBTC3W3LMWH7MAZQJ4/nJNMg3EQSgooXOSRuWvWKd-_iFM=.c9s/name.c9s
@@ -0,0 +1 @@
+1zJSge9zbBT_TUIJYRorIisUY0fu40FQHDGtbfXpmgN5xa9r5GuTmWlHXSYeV1883KKIjysmejZAsnctjxPNySghzIsAkjHGE0TrHtBXAWPi4ppZMsbYevx6W8nyApk_XcHbmWFfCIGyQ3SP_VLcZ32LSJEd6HNY44wGop2KiQjGbET-3e6EkfwOJaqegPPrZHE3DVp3boFwFFLYvRTHPpmOEscbNNUMPao1dxQJ33Eb82E_d7g2PDNl_8-8mlel8hdbjSLHdQNOEJSooKTsSfc8MJN6HV3dNZ97k-Nqw6SpP7cxXifIprZxTgKDFnbEBVAci3dDrDy_D4oq8XYwV9jD8AdSA50TA1ClPLoaH1bjOLJTeNIrjAGKmh-i6YVcw2rgVqfz21vs_eWJ2npY0HzteoYR99X_ioRMOFiNBBqJfWsZTQx1jR017k1awCJ7-Azo4Q==.c9r
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/masterkey.cryptomator b/src/test/resources/vault-1.5.6/masterkey.cryptomator
new file mode 100644
index 0000000..6d23fd6
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/masterkey.cryptomator
@@ -0,0 +1,9 @@
+{
+ "scryptSalt": "8lWy3HMnXj8=",
+ "scryptCostParam": 32768,
+ "scryptBlockSize": 8,
+ "primaryMasterKey": "CJtudF8uwxjo63z276NsmVcvLlPX8clT0HIheziq+hxbuIG/4TfF+g==",
+ "hmacMasterKey": "nkLL86sArb2qxZkmP39ssuOO+QqdCub6nVxw2CzSpw5UDDWJpx0G5w==",
+ "versionMac": "8LYUYfvZSljmAHDDvCbesDjSiYftDoxaC82yLCzrmSI=",
+ "version": 7
+}
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/masterkey.cryptomator.3EADC5A7.bkup b/src/test/resources/vault-1.5.6/masterkey.cryptomator.3EADC5A7.bkup
new file mode 100644
index 0000000..262894a
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/masterkey.cryptomator.3EADC5A7.bkup
@@ -0,0 +1,9 @@
+{
+ "scryptSalt": "c+0ijA8emRU=",
+ "scryptCostParam": 32768,
+ "scryptBlockSize": 8,
+ "primaryMasterKey": "LGdflvoaQNgH/gmih3mR2s5R03/i7ILv1rVWoz+rS+ggpJ374pTa2g==",
+ "hmacMasterKey": "iXURU9NOX1NtIpycBjL9htwHcMBrlC0DUmMlmpa/cw8QG4PMRZMzfg==",
+ "versionMac": "jUUdjhO4nip/AR+Xh/ydbinC99qPmszZsGzkyqDWCN0=",
+ "version": 6
+}
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/masterkey.cryptomator.82B1A750.bkup b/src/test/resources/vault-1.5.6/masterkey.cryptomator.82B1A750.bkup
new file mode 100644
index 0000000..6d23fd6
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/masterkey.cryptomator.82B1A750.bkup
@@ -0,0 +1,9 @@
+{
+ "scryptSalt": "8lWy3HMnXj8=",
+ "scryptCostParam": 32768,
+ "scryptBlockSize": 8,
+ "primaryMasterKey": "CJtudF8uwxjo63z276NsmVcvLlPX8clT0HIheziq+hxbuIG/4TfF+g==",
+ "hmacMasterKey": "nkLL86sArb2qxZkmP39ssuOO+QqdCub6nVxw2CzSpw5UDDWJpx0G5w==",
+ "versionMac": "8LYUYfvZSljmAHDDvCbesDjSiYftDoxaC82yLCzrmSI=",
+ "version": 7
+}
\ No newline at end of file
diff --git a/src/test/resources/vault-1.5.6/masterkey.cryptomator.bkup b/src/test/resources/vault-1.5.6/masterkey.cryptomator.bkup
new file mode 100644
index 0000000..262894a
--- /dev/null
+++ b/src/test/resources/vault-1.5.6/masterkey.cryptomator.bkup
@@ -0,0 +1,9 @@
+{
+ "scryptSalt": "c+0ijA8emRU=",
+ "scryptCostParam": 32768,
+ "scryptBlockSize": 8,
+ "primaryMasterKey": "LGdflvoaQNgH/gmih3mR2s5R03/i7ILv1rVWoz+rS+ggpJ374pTa2g==",
+ "hmacMasterKey": "iXURU9NOX1NtIpycBjL9htwHcMBrlC0DUmMlmpa/cw8QG4PMRZMzfg==",
+ "versionMac": "jUUdjhO4nip/AR+Xh/ydbinC99qPmszZsGzkyqDWCN0=",
+ "version": 6
+}
\ No newline at end of file