Просмотр исходного кода

Using DI to stack up filesystem layers

Sebastian Stenzel 9 лет назад
Родитель
Сommit
ddf6353729
23 измененных файлов с 339 добавлено и 298 удалено
  1. 0 15
      main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoComponent.java
  2. 0 27
      main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoFileSystemFactory.java
  3. 3 3
      main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptoModule.java
  4. 1 1
      main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java
  5. 1 1
      main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystem.java
  6. 20 0
      main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystemFactory.java
  7. 1 1
      main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystem.java
  8. 15 0
      main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystemComponent.java
  9. 32 0
      main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystemFactory.java
  10. 0 60
      main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoFileSystemIntegrationTest.java
  11. 0 15
      main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoTestComponent.java
  12. 23 0
      main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptoEngineTestModule.java
  13. 0 31
      main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptoTestModule.java
  14. 104 0
      main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/CryptoFileSystemComponentIntegrationTest.java
  15. 0 37
      main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/DirectoryPrinter.java
  16. 0 105
      main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/EncryptAndShortenIntegrationTest.java
  17. 11 0
      main/filesystem-nameshortening/pom.xml
  18. 1 1
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFileSystem.java
  19. 22 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFileSystemFactory.java
  20. 1 1
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystem.java
  21. 32 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactory.java
  22. 59 0
      main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactoryTest.java
  23. 13 0
      main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemTestComponent.java

+ 0 - 15
main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoComponent.java

@@ -1,15 +0,0 @@
-package org.cryptomator.crypto;
-
-import javax.inject.Singleton;
-
-import org.cryptomator.crypto.engine.impl.CryptoModule;
-
-import dagger.Component;
-
-@Singleton
-@Component(modules = CryptoModule.class)
-interface CryptoComponent {
-
-	CryptoFileSystemFactory cryptoFileSystemFactory();
-
-}

+ 0 - 27
main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoFileSystemFactory.java

@@ -1,27 +0,0 @@
-package org.cryptomator.crypto;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-import org.cryptomator.crypto.engine.Cryptor;
-import org.cryptomator.crypto.engine.impl.FileContentCryptorImpl;
-import org.cryptomator.filesystem.FileSystem;
-import org.cryptomator.filesystem.Folder;
-import org.cryptomator.filesystem.blockaligned.BlockAlignedFileSystem;
-import org.cryptomator.filesystem.crypto.CryptoFileSystem;
-
-@Singleton
-public class CryptoFileSystemFactory {
-
-	private final Provider<Cryptor> cryptorProvider;
-
-	@Inject
-	public CryptoFileSystemFactory(Provider<Cryptor> cryptorProvider) {
-		this.cryptorProvider = cryptorProvider;
-	}
-
-	public FileSystem get(Folder root, CharSequence passphrase) {
-		return new BlockAlignedFileSystem(new CryptoFileSystem(root, cryptorProvider.get(), passphrase), FileContentCryptorImpl.CHUNK_SIZE);
-	}
-}

+ 3 - 3
main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptoModule.java

@@ -9,15 +9,15 @@ import dagger.Module;
 import dagger.Provides;
 
 @Module
-public class CryptoModule {
+public class CryptoEngineModule {
 
 	@Provides
-	Cryptor provideCryptor(SecureRandom secureRandom) {
+	public Cryptor provideCryptor(SecureRandom secureRandom) {
 		return new CryptorImpl(secureRandom);
 	}
 
 	@Provides
-	SecureRandom provideSecureRandom() {
+	public SecureRandom provideSecureRandom() {
 		try {
 			return SecureRandom.getInstanceStrong();
 		} catch (NoSuchAlgorithmException e) {

+ 1 - 1
main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java

@@ -21,7 +21,7 @@ import org.cryptomator.crypto.engine.FileContentEncryptor;
 public class FileContentCryptorImpl implements FileContentCryptor {
 
 	public static final int CHUNK_SIZE = 32 * 1024;
-	static final int MAC_SIZE = 32;
+	public static final int MAC_SIZE = 32;
 
 	private final SecretKey encryptionKey;
 	private final SecretKey macKey;

+ 1 - 1
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystem.java

@@ -11,7 +11,7 @@ package org.cryptomator.filesystem.blockaligned;
 import org.cryptomator.filesystem.FileSystem;
 import org.cryptomator.filesystem.Folder;
 
-public class BlockAlignedFileSystem extends BlockAlignedFolder implements FileSystem {
+class BlockAlignedFileSystem extends BlockAlignedFolder implements FileSystem {
 
 	public BlockAlignedFileSystem(Folder delegate, int blockSize) {
 		super(null, delegate, blockSize);

+ 20 - 0
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystemFactory.java

@@ -0,0 +1,20 @@
+package org.cryptomator.filesystem.blockaligned;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.cryptomator.crypto.engine.impl.FileContentCryptorImpl;
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+
+@Singleton
+public class BlockAlignedFileSystemFactory {
+
+	@Inject
+	public BlockAlignedFileSystemFactory() {
+	}
+
+	public FileSystem get(Folder root) {
+		return new BlockAlignedFileSystem(root, FileContentCryptorImpl.CHUNK_SIZE);
+	}
+}

+ 1 - 1
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystem.java

@@ -19,7 +19,7 @@ import org.cryptomator.filesystem.Folder;
 import org.cryptomator.filesystem.ReadableFile;
 import org.cryptomator.filesystem.WritableFile;
 
-public class CryptoFileSystem extends CryptoFolder implements FileSystem {
+class CryptoFileSystem extends CryptoFolder implements FileSystem {
 
 	private static final String DATA_ROOT_DIR = "d";
 	private static final String ROOT_DIR_FILE = "root";

+ 15 - 0
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystemComponent.java

@@ -0,0 +1,15 @@
+package org.cryptomator.filesystem.crypto;
+
+import javax.inject.Singleton;
+
+import org.cryptomator.crypto.engine.impl.CryptoEngineModule;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = CryptoEngineModule.class)
+interface CryptoFileSystemComponent {
+
+	CryptoFileSystemFactory cryptoFileSystemFactory();
+
+}

+ 32 - 0
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystemFactory.java

@@ -0,0 +1,32 @@
+package org.cryptomator.filesystem.crypto;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.cryptomator.crypto.engine.Cryptor;
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.blockaligned.BlockAlignedFileSystemFactory;
+import org.cryptomator.filesystem.shortening.ShorteningFileSystemFactory;
+
+@Singleton
+public class CryptoFileSystemFactory {
+
+	private final Provider<Cryptor> cryptorProvider;
+	private final ShorteningFileSystemFactory shorteningFileSystemFactory;
+	private final BlockAlignedFileSystemFactory blockAlignedFileSystemFactory;
+
+	@Inject
+	public CryptoFileSystemFactory(Provider<Cryptor> cryptorProvider, ShorteningFileSystemFactory shorteningFileSystemFactory, BlockAlignedFileSystemFactory blockAlignedFileSystemFactory) {
+		this.cryptorProvider = cryptorProvider;
+		this.shorteningFileSystemFactory = shorteningFileSystemFactory;
+		this.blockAlignedFileSystemFactory = blockAlignedFileSystemFactory;
+	}
+
+	public FileSystem get(Folder root, CharSequence passphrase) {
+		final FileSystem nameShorteningFs = shorteningFileSystemFactory.get(root);
+		final FileSystem cryptoFs = new CryptoFileSystem(nameShorteningFs, cryptorProvider.get(), passphrase);
+		return blockAlignedFileSystemFactory.get(cryptoFs);
+	}
+}

+ 0 - 60
main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoFileSystemIntegrationTest.java

@@ -1,60 +0,0 @@
-package org.cryptomator.crypto;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-import org.cryptomator.filesystem.File;
-import org.cryptomator.filesystem.FileSystem;
-import org.cryptomator.filesystem.Folder;
-import org.cryptomator.filesystem.ReadableFile;
-import org.cryptomator.filesystem.WritableFile;
-import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class CryptoFileSystemIntegrationTest {
-
-	private FileSystem ciphertextFs;
-	private FileSystem cleartextFs;
-
-	@Before
-	public void setupFileSystems() {
-		ciphertextFs = new InMemoryFileSystem();
-		// final Predicate<Node> isMetadataFolder = (Node node) -> node.equals(physicalFs.folder("m"));
-		// final FileSystem metadataHidingFs = new BlacklistingFileSystem(physicalFs, isMetadataFolder);
-		// final FileSystem shorteningFs = new ShorteningFileSystem(metadataHidingFs, physicalFs.folder("m"), 70);
-		cleartextFs = DaggerCryptoTestComponent.create().cryptoFileSystemFactory().get(ciphertextFs, "TopSecret");
-		cleartextFs.create();
-	}
-
-	@Test
-	public void testRandomAccess() {
-		File cleartextFile = cleartextFs.file("test");
-		try (WritableFile writable = cleartextFile.openWritable()) {
-			ByteBuffer buf = ByteBuffer.allocate(25000);
-			for (int i = 0; i < 40; i++) { // 40 * 25k = 1M
-				buf.clear();
-				Arrays.fill(buf.array(), (byte) i);
-				writable.write(buf);
-			}
-		}
-
-		Folder ciphertextRootFolder = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get();
-		Assert.assertTrue(ciphertextRootFolder.exists());
-		File ciphertextFile = ciphertextRootFolder.files().findAny().get();
-		Assert.assertTrue(ciphertextFile.exists());
-
-		try (ReadableFile readable = cleartextFile.openReadable()) {
-			ByteBuffer buf = ByteBuffer.allocate(1);
-			for (int i = 0; i < 40; i++) {
-				buf.clear();
-				readable.position(i * 25000 + (long) Math.random() * 24999);
-				readable.read(buf);
-				buf.flip();
-				Assert.assertEquals(i, buf.get());
-			}
-		}
-	}
-
-}

+ 0 - 15
main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoTestComponent.java

@@ -1,15 +0,0 @@
-package org.cryptomator.crypto;
-
-import javax.inject.Singleton;
-
-import org.cryptomator.crypto.engine.impl.CryptoTestModule;
-
-import dagger.Component;
-
-@Singleton
-@Component(modules = CryptoTestModule.class)
-interface CryptoTestComponent {
-
-	CryptoFileSystemFactory cryptoFileSystemFactory();
-
-}

+ 23 - 0
main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptoEngineTestModule.java

@@ -0,0 +1,23 @@
+package org.cryptomator.crypto.engine.impl;
+
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+/**
+ * Used as drop-in-replacement for {@link CryptoEngineModule} during unit tests.
+ */
+public class CryptoEngineTestModule extends CryptoEngineModule {
+
+	@Override
+	public SecureRandom provideSecureRandom() {
+		return new SecureRandom() {
+
+			@Override
+			public void nextBytes(byte[] bytes) {
+				Arrays.fill(bytes, (byte) 0x00);
+			}
+
+		};
+	}
+
+}

+ 0 - 31
main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptoTestModule.java

@@ -1,31 +0,0 @@
-package org.cryptomator.crypto.engine.impl;
-
-import java.security.SecureRandom;
-import java.util.Arrays;
-
-import org.cryptomator.crypto.engine.Cryptor;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-public class CryptoTestModule {
-
-	@Provides
-	Cryptor provideCryptor(SecureRandom secureRandom) {
-		return new CryptorImpl(secureRandom);
-	}
-
-	@Provides
-	SecureRandom provideSecureRandom() {
-		return new SecureRandom() {
-
-			@Override
-			public void nextBytes(byte[] bytes) {
-				Arrays.fill(bytes, (byte) 0x00);
-			}
-
-		};
-	}
-
-}

+ 104 - 0
main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/CryptoFileSystemComponentIntegrationTest.java

@@ -0,0 +1,104 @@
+package org.cryptomator.filesystem.crypto;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.cryptomator.crypto.engine.impl.CryptoEngineTestModule;
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.Node;
+import org.cryptomator.filesystem.ReadableFile;
+import org.cryptomator.filesystem.WritableFile;
+import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CryptoFileSystemComponentIntegrationTest {
+
+	private static final CryptoFileSystemComponent cryptoFsComp = DaggerCryptoFileSystemComponent.builder().cryptoEngineModule(new CryptoEngineTestModule()).build();
+
+	private FileSystem ciphertextFs;
+	private FileSystem cleartextFs;
+
+	@Before
+	public void setupFileSystems() {
+		ciphertextFs = new InMemoryFileSystem();
+		cleartextFs = cryptoFsComp.cryptoFileSystemFactory().get(ciphertextFs, "TopSecret");
+		cleartextFs.create();
+	}
+
+	@Test
+	public void testEncryptionOfLongFolderNames() {
+		final String shortName = "normal folder name";
+		final String longName = "this will be a long filename after encryption, because its encrypted name is longer than onehundredandeighty characters";
+
+		final Folder shortFolder = cleartextFs.folder(shortName);
+		final Folder longFolder = cleartextFs.folder(longName);
+
+		shortFolder.create();
+		longFolder.create();
+
+		// because of the long file, a metadata folder should exist on the physical layer:
+		Assert.assertEquals(1, ciphertextFs.folder("m").folders().count());
+		Assert.assertTrue(ciphertextFs.folder("m").exists());
+
+		// but the shortened filenames must not be visible on the cleartext layer:
+		Assert.assertArrayEquals(new String[] {shortName, longName}, cleartextFs.folders().map(Node::name).sorted().toArray());
+	}
+
+	@Test
+	public void testEncryptionAndDecryptionOfFiles() {
+		// write test content to encrypted file
+		try (WritableFile writable = cleartextFs.file("test1.txt").openWritable()) {
+			writable.write(ByteBuffer.wrap("Hello ".getBytes()));
+			writable.write(ByteBuffer.wrap("World".getBytes()));
+		}
+
+		File physicalFile = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get().files().findAny().get();
+		Assert.assertTrue(physicalFile.exists());
+
+		// read test content from decrypted file
+		try (ReadableFile readable = cleartextFs.file("test1.txt").openReadable()) {
+			ByteBuffer buf1 = ByteBuffer.allocate(5);
+			readable.read(buf1);
+			buf1.flip();
+			Assert.assertEquals("Hello", new String(buf1.array(), 0, buf1.remaining()));
+			ByteBuffer buf2 = ByteBuffer.allocate(10);
+			readable.read(buf2);
+			buf2.flip();
+			Assert.assertArrayEquals(" World".getBytes(), Arrays.copyOfRange(buf2.array(), 0, buf2.remaining()));
+		}
+	}
+
+	@Test
+	public void testRandomAccess() {
+		File cleartextFile = cleartextFs.file("test");
+		try (WritableFile writable = cleartextFile.openWritable()) {
+			ByteBuffer buf = ByteBuffer.allocate(25000);
+			for (int i = 0; i < 40; i++) { // 40 * 25k = 1M
+				buf.clear();
+				Arrays.fill(buf.array(), (byte) i);
+				writable.write(buf);
+			}
+		}
+
+		Folder ciphertextRootFolder = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get();
+		Assert.assertTrue(ciphertextRootFolder.exists());
+		File ciphertextFile = ciphertextRootFolder.files().findAny().get();
+		Assert.assertTrue(ciphertextFile.exists());
+
+		try (ReadableFile readable = cleartextFile.openReadable()) {
+			ByteBuffer buf = ByteBuffer.allocate(1);
+			for (int i = 0; i < 40; i++) {
+				buf.clear();
+				readable.position(i * 25000 + (long) Math.random() * 24999); // "random access", told you so.
+				readable.read(buf);
+				buf.flip();
+				Assert.assertEquals(i, buf.get());
+			}
+		}
+	}
+
+}

+ 0 - 37
main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/DirectoryPrinter.java

@@ -1,37 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 Sebastian Stenzel and others.
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- *     Sebastian Stenzel - initial API and implementation
- *******************************************************************************/
-package org.cryptomator.filesystem.crypto;
-
-import static org.cryptomator.filesystem.FileSystemVisitor.fileSystemVisitor;
-
-import org.cryptomator.filesystem.Folder;
-
-public final class DirectoryPrinter {
-
-	private DirectoryPrinter() {
-	}
-
-	public static String print(Folder rootFolder) {
-		StringBuilder result = new StringBuilder();
-		StringBuilder indentation = new StringBuilder();
-		fileSystemVisitor() //
-				.beforeFolder(folder -> {
-					result.append(indentation).append(folder.name()).append("/\n");
-					indentation.append("  ");
-				}) //
-				.afterFolder(folder -> {
-					indentation.delete(indentation.length() - 2, indentation.length());
-				}).forEachFile(file -> {
-					result.append(indentation).append(file.name()).append('\n');
-				}) //
-				.visit(rootFolder);
-		return result.toString();
-	}
-
-}

+ 0 - 105
main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/EncryptAndShortenIntegrationTest.java

@@ -1,105 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 Sebastian Stenzel and others.
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- *     Sebastian Stenzel - initial API and implementation
- *******************************************************************************/
-package org.cryptomator.filesystem.crypto;
-
-import static org.cryptomator.filesystem.FileSystemVisitor.fileSystemVisitor;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.function.Predicate;
-
-import org.cryptomator.crypto.engine.Cryptor;
-import org.cryptomator.crypto.engine.impl.TestCryptorImplFactory;
-import org.cryptomator.filesystem.File;
-import org.cryptomator.filesystem.FileSystem;
-import org.cryptomator.filesystem.Folder;
-import org.cryptomator.filesystem.Node;
-import org.cryptomator.filesystem.ReadableFile;
-import org.cryptomator.filesystem.WritableFile;
-import org.cryptomator.filesystem.blacklisting.BlacklistingFileSystem;
-import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
-import org.cryptomator.filesystem.shortening.ShorteningFileSystem;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class EncryptAndShortenIntegrationTest {
-
-	// private static final Logger LOG = LoggerFactory.getLogger(EncryptAndShortenIntegrationTest.class);
-
-	@Test
-	public void testEncryptionOfLongFolderNames() {
-		final FileSystem physicalFs = new InMemoryFileSystem();
-		final Predicate<Node> isMetadataFolder = (Node node) -> node.equals(physicalFs.folder("m"));
-		final FileSystem metadataHidingFs = new BlacklistingFileSystem(physicalFs, isMetadataFolder);
-		final FileSystem shorteningFs = new ShorteningFileSystem(metadataHidingFs, physicalFs.folder("m"), 70);
-
-		final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
-		cryptor.randomizeMasterkey();
-		final FileSystem fs = new CryptoFileSystem(shorteningFs, cryptor, "foo");
-		fs.create();
-		final Folder shortFolder = fs.folder("normal folder name");
-		shortFolder.create();
-		final Folder longFolder = fs.folder("this will be a long filename after encryption");
-		longFolder.create();
-
-		// on the first (physical) layer all files including metadata files are visible:
-		// the long name will produce a metadata file on the physical layer:
-		// LOG.debug("Physical file system:\n" + DirectoryPrinter.print(physicalFs));
-		Assert.assertEquals(1, physicalFs.folder("m").folders().count());
-		Assert.assertTrue(physicalFs.folder("m").exists());
-
-		// on the second (blacklisting) layer we hide the metadata folder:
-		// LOG.debug("Filtered files:\n" + DirectoryPrinter.print(metadataHidingFs));
-		Assert.assertEquals(1, metadataHidingFs.folders().count()); // only "d", no "m".
-
-		// on the third layer all .lng files are resolved to their actual names:
-		// LOG.debug("Unlimited filename length:\n" + DirectoryPrinter.print(shorteningFs));
-		fileSystemVisitor() //
-				.forEachNode(node -> {
-					Assert.assertFalse(node.name().endsWith(".lng"));
-				}) //
-				.visit(shorteningFs);
-
-		// on the fourth (cleartext) layer we have cleartext names on the root level:
-		// LOG.debug("Cleartext files:\n" + DirectoryPrinter.print(fs));
-		Assert.assertArrayEquals(new String[] {"normal folder name", "this will be a long filename after encryption"}, fs.folders().map(Node::name).sorted().toArray());
-	}
-
-	@Test
-	public void testEncryptionAndDecryptionOfFiles() {
-		final FileSystem physicalFs = new InMemoryFileSystem();
-		final FileSystem shorteningFs = new ShorteningFileSystem(physicalFs, physicalFs.folder("m"), 70);
-		final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
-		cryptor.randomizeMasterkey();
-		final FileSystem fs = new CryptoFileSystem(shorteningFs, cryptor, "foo");
-		fs.create();
-
-		// write test content to encrypted file
-		try (WritableFile writable = fs.file("test1.txt").openWritable()) {
-			writable.write(ByteBuffer.wrap("Hello ".getBytes()));
-			writable.write(ByteBuffer.wrap("World".getBytes()));
-		}
-
-		File physicalFile = physicalFs.folder("d").folders().findAny().get().folders().findAny().get().files().findAny().get();
-		Assert.assertTrue(physicalFile.exists());
-
-		// read test content from decrypted file
-		try (ReadableFile readable = fs.file("test1.txt").openReadable()) {
-			ByteBuffer buf1 = ByteBuffer.allocate(5);
-			readable.read(buf1);
-			buf1.flip();
-			Assert.assertEquals("Hello", new String(buf1.array(), 0, buf1.remaining()));
-			ByteBuffer buf2 = ByteBuffer.allocate(10);
-			readable.read(buf2);
-			buf2.flip();
-			Assert.assertArrayEquals(" World".getBytes(), Arrays.copyOfRange(buf2.array(), 0, buf2.remaining()));
-		}
-	}
-
-}

+ 11 - 0
main/filesystem-nameshortening/pom.xml

@@ -42,6 +42,17 @@
 			<groupId>com.fasterxml.jackson.core</groupId>
 			<artifactId>jackson-databind</artifactId>
 		</dependency>
+		
+		<!-- DI -->
+		<dependency>
+			<groupId>com.google.dagger</groupId>
+			<artifactId>dagger</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.google.dagger</groupId>
+			<artifactId>dagger-compiler</artifactId>
+			<scope>provided</scope>
+		</dependency>
 
 		<!-- Tests -->
 		<dependency>

+ 1 - 1
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFileSystem.java

@@ -6,7 +6,7 @@ import org.cryptomator.filesystem.FileSystem;
 import org.cryptomator.filesystem.Folder;
 import org.cryptomator.filesystem.Node;
 
-public class BlacklistingFileSystem extends BlacklistingFolder implements FileSystem {
+class BlacklistingFileSystem extends BlacklistingFolder implements FileSystem {
 
 	public BlacklistingFileSystem(Folder root, Predicate<Node> hiddenNodes) {
 		super(null, root, hiddenNodes);

+ 22 - 0
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFileSystemFactory.java

@@ -0,0 +1,22 @@
+package org.cryptomator.filesystem.blacklisting;
+
+import java.util.function.Predicate;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.Node;
+
+@Singleton
+public class BlacklistingFileSystemFactory {
+
+	@Inject
+	public BlacklistingFileSystemFactory() {
+	}
+
+	public FileSystem get(Folder root, Predicate<Node> hiddenFiles) {
+		return new BlacklistingFileSystem(root, hiddenFiles);
+	}
+}

+ 1 - 1
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystem.java

@@ -3,7 +3,7 @@ package org.cryptomator.filesystem.shortening;
 import org.cryptomator.filesystem.FileSystem;
 import org.cryptomator.filesystem.Folder;
 
-public class ShorteningFileSystem extends ShorteningFolder implements FileSystem {
+class ShorteningFileSystem extends ShorteningFolder implements FileSystem {
 
 	public ShorteningFileSystem(Folder root, Folder metadataRoot, int threshold) {
 		super(null, root, "", new FilenameShortener(metadataRoot, threshold));

+ 32 - 0
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactory.java

@@ -0,0 +1,32 @@
+package org.cryptomator.filesystem.shortening;
+
+import java.util.function.Predicate;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.Node;
+import org.cryptomator.filesystem.blacklisting.BlacklistingFileSystemFactory;
+
+@Singleton
+public class ShorteningFileSystemFactory {
+
+	private static final int SHORTENING_THRESHOLD = 140;
+	private static final String METADATA_FOLDER_NAME = "m";
+
+	private final BlacklistingFileSystemFactory blacklistingFileSystemFactory;
+
+	@Inject
+	public ShorteningFileSystemFactory(BlacklistingFileSystemFactory blacklistingFileSystemFactory) {
+		this.blacklistingFileSystemFactory = blacklistingFileSystemFactory;
+	}
+
+	public FileSystem get(Folder root) {
+		final Folder metadataFolder = root.folder(METADATA_FOLDER_NAME);
+		final Predicate<Node> isMetadataFolder = (Node node) -> metadataFolder.equals(node);
+		final FileSystem metadataHidingFs = blacklistingFileSystemFactory.get(root, isMetadataFolder);
+		return new ShorteningFileSystem(metadataHidingFs, metadataFolder, SHORTENING_THRESHOLD);
+	}
+}

+ 59 - 0
main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactoryTest.java

@@ -0,0 +1,59 @@
+package org.cryptomator.filesystem.shortening;
+
+import java.nio.ByteBuffer;
+
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.WritableFile;
+import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ShorteningFileSystemFactoryTest {
+
+	private static final ShorteningFileSystemFactory shorteningFsFactory = DaggerShorteningFileSystemTestComponent.create().shorteningFileSystemFactory();
+	private static final String LONG_NAME = "aaaaabbbbbcccccdddddeeeeefffffggggghhhhhiiiiijjjjjkkkkklllll" //
+			+ "mmmmmnnnnnooooopppppqqqqqrrrrrssssstttttuuuuuvvvvvwwwwwxxxxxyyyyyzzzzz" //
+			+ "00000111112222233333444445555566666777778888899999";
+	private static final String SHORTENED_FILE = "UYPJJ35VGP2JJ4YISC5S2XQLENLR5MVC.lng"; // base32(sha1(LONG_NAME)) + '.lng'
+	private static final String CORRESPONDING_METADATA_FILE = "m/UY/PJ/UYPJJ35VGP2JJ4YISC5S2XQLENLR5MVC.lng";
+
+	private FileSystem physicalFs;
+	private FileSystem shortenedFs;
+
+	@Before
+	public void setupFileSystems() {
+		physicalFs = new InMemoryFileSystem();
+		shortenedFs = shorteningFsFactory.get(physicalFs);
+		shortenedFs.create();
+	}
+
+	@Test
+	public void testFileCreation() {
+		File file = shortenedFs.file(LONG_NAME);
+		Assert.assertFalse(file.exists());
+		Assert.assertFalse(physicalFs.resolveFile(SHORTENED_FILE).exists());
+		Assert.assertFalse(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
+		try (WritableFile w = file.openWritable()) {
+			w.write(ByteBuffer.allocate(0));
+		}
+		Assert.assertTrue(file.exists());
+		Assert.assertTrue(physicalFs.resolveFile(SHORTENED_FILE).exists());
+		Assert.assertTrue(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
+	}
+
+	@Test
+	public void testFolderCreation() {
+		Folder folder = shortenedFs.folder(LONG_NAME);
+		Assert.assertFalse(folder.exists());
+		Assert.assertFalse(physicalFs.resolveFolder(SHORTENED_FILE).exists());
+		Assert.assertFalse(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
+		folder.create();
+		Assert.assertTrue(folder.exists());
+		Assert.assertTrue(physicalFs.resolveFolder(SHORTENED_FILE).exists());
+		Assert.assertTrue(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
+	}
+
+}

+ 13 - 0
main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemTestComponent.java

@@ -0,0 +1,13 @@
+package org.cryptomator.filesystem.shortening;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component
+public interface ShorteningFileSystemTestComponent {
+
+	ShorteningFileSystemFactory shorteningFileSystemFactory();
+
+}