소스 검색

separated filename shortening layer from metadata hiding layer

Sebastian Stenzel 9 년 전
부모
커밋
9385c3bf6d
20개의 변경된 파일370개의 추가작업 그리고 317개의 파일을 삭제
  1. 33 0
      main/commons/src/main/java/org/cryptomator/common/LazyInitializer.java
  2. 3 20
      main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java
  3. 20 13
      main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/EncryptAndShortenIntegrationTest.java
  4. 6 2
      main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java
  5. 5 1
      main/filesystem-nameshortening/pom.xml
  6. 26 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFile.java
  7. 15 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFileSystem.java
  8. 55 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFolder.java
  9. 14 14
      main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/FilenameShortener.java
  10. 47 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFile.java
  11. 12 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystem.java
  12. 78 0
      main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFolder.java
  13. 1 1
      main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/package-info.java
  14. 0 41
      main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/ShorteningFile.java
  15. 0 34
      main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/ShorteningFileSystem.java
  16. 0 112
      main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/ShorteningFolder.java
  17. 0 68
      main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/ShorteningNode.java
  18. 51 0
      main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/blacklisting/BlacklistingFileSystemTest.java
  19. 2 11
      main/filesystem-nameshortening/src/test/java/org/cryptomator/shortening/ShorteningFileSystemTest.java
  20. 2 0
      main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WeakValuedCacheTest.java

+ 33 - 0
main/commons/src/main/java/org/cryptomator/common/LazyInitializer.java

@@ -0,0 +1,33 @@
+package org.cryptomator.common;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+public final class LazyInitializer {
+
+	private LazyInitializer() {
+	}
+
+	/**
+	 * Threadsafe lazy initialization pattern as proposed on http://stackoverflow.com/a/30247202/4014509
+	 * 
+	 * @param <T> Type of the value
+	 * @param reference A reference to a maybe not yet initialized value.
+	 * @param factory A factory providing a value for the reference, if it doesn't exist yet. The factory may be invoked multiple times, but only one result will survive.
+	 * @return The initialized value
+	 */
+	public static <T> T initializeLazily(AtomicReference<T> reference, Supplier<T> factory) {
+		final T existingInstance = reference.get();
+		if (existingInstance != null) {
+			return existingInstance;
+		} else {
+			final T newInstance = factory.get();
+			if (reference.compareAndSet(null, newInstance)) {
+				return newInstance;
+			} else {
+				return reference.get();
+			}
+		}
+	}
+
+}

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

@@ -14,13 +14,13 @@ import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
 
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 import javax.security.auth.DestroyFailedException;
 import javax.security.auth.Destroyable;
 
+import org.cryptomator.common.LazyInitializer;
 import org.cryptomator.crypto.engine.Cryptor;
 import org.cryptomator.crypto.engine.FileContentCryptor;
 import org.cryptomator.crypto.engine.FilenameCryptor;
@@ -67,7 +67,7 @@ public class CryptorImpl implements Cryptor {
 	@Override
 	public FilenameCryptor getFilenameCryptor() {
 		assertKeysExist();
-		return initializeLazily(filenameCryptor, () -> {
+		return LazyInitializer.initializeLazily(filenameCryptor, () -> {
 			return new FilenameCryptorImpl(encryptionKey, macKey);
 		});
 	}
@@ -75,28 +75,11 @@ public class CryptorImpl implements Cryptor {
 	@Override
 	public FileContentCryptor getFileContentCryptor() {
 		assertKeysExist();
-		return initializeLazily(fileContentCryptor, () -> {
+		return LazyInitializer.initializeLazily(fileContentCryptor, () -> {
 			return new FileContentCryptorImpl(encryptionKey, macKey, randomSource);
 		});
 	}
 
-	/**
-	 * threadsafe lazy initialization pattern as proposed on http://stackoverflow.com/a/30247202/4014509
-	 */
-	private <T> T initializeLazily(AtomicReference<T> reference, Supplier<T> factory) {
-		final T existingInstance = reference.get();
-		if (existingInstance != null) {
-			return existingInstance;
-		} else {
-			final T newInstance = factory.get();
-			if (reference.compareAndSet(null, newInstance)) {
-				return newInstance;
-			} else {
-				return reference.get();
-			}
-		}
-	}
-
 	private void assertKeysExist() {
 		if (encryptionKey == null || encryptionKey.isDestroyed()) {
 			throw new IllegalStateException("No or invalid encryptionKey.");

+ 20 - 13
main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/EncryptAndShortenIntegrationTest.java

@@ -4,6 +4,7 @@ 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;
@@ -13,20 +14,23 @@ 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.shortening.ShorteningFileSystem;
+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);
+	// private static final Logger LOG = LoggerFactory.getLogger(EncryptAndShortenIntegrationTest.class);
 
 	@Test
 	public void testEncryptionOfLongFolderNames() {
 		final FileSystem physicalFs = new InMemoryFileSystem();
-		final FileSystem shorteningFs = new ShorteningFileSystem(physicalFs, physicalFs.folder("m"), 70);
+		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");
@@ -36,24 +40,27 @@ public class EncryptAndShortenIntegrationTest {
 		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));
+		// 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 layer all .lng files are resolved to their actual
-		// names:
-		// LOG.debug("Unlimited filename length:\n" +
-		// DirectoryPrinter.print(shorteningFs));
+		// 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 third (cleartext layer) we have cleartext names on the root
-		// level:
+
+		// 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());
+		Assert.assertArrayEquals(new String[] {"normal folder name", "this will be a long filename after encryption"}, fs.folders().map(Node::name).sorted().toArray());
 	}
 
 	@Test

+ 6 - 2
main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java

@@ -58,7 +58,11 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF
 
 	@Override
 	public int read(ByteBuffer target) {
-		return ByteBuffers.copy(content, target);
+		if (content.hasRemaining()) {
+			return ByteBuffers.copy(content, target);
+		} else {
+			return -1;
+		}
 	}
 
 	@Override
@@ -114,7 +118,7 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF
 			// returning null removes the entry.
 			return null;
 		});
-		assert !this.exists();
+		assert!this.exists();
 	}
 
 	@Override

+ 5 - 1
main/filesystem-nameshortening/pom.xml

@@ -22,7 +22,11 @@
 			<groupId>org.cryptomator</groupId>
 			<artifactId>filesystem-api</artifactId>
 		</dependency>
-		
+		<dependency>
+			<groupId>org.cryptomator</groupId>
+			<artifactId>commons</artifactId>
+		</dependency>
+
 		<!-- Commons -->
 		<dependency>
 			<groupId>org.apache.commons</groupId>

+ 26 - 0
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFile.java

@@ -0,0 +1,26 @@
+package org.cryptomator.filesystem.blacklisting;
+
+import java.io.UncheckedIOException;
+
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.delegating.DelegatingFile;
+import org.cryptomator.filesystem.delegating.DelegatingReadableFile;
+import org.cryptomator.filesystem.delegating.DelegatingWritableFile;
+
+class BlacklistingFile extends DelegatingFile<DelegatingReadableFile, DelegatingWritableFile, BlacklistingFolder> {
+
+	public BlacklistingFile(BlacklistingFolder parent, File delegate) {
+		super(parent, delegate);
+	}
+
+	@Override
+	public DelegatingReadableFile openReadable() throws UncheckedIOException {
+		return new DelegatingReadableFile(delegate.openReadable());
+	}
+
+	@Override
+	public DelegatingWritableFile openWritable() throws UncheckedIOException {
+		return new DelegatingWritableFile(delegate.openWritable());
+	}
+
+}

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

@@ -0,0 +1,15 @@
+package org.cryptomator.filesystem.blacklisting;
+
+import java.util.function.Predicate;
+
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.Node;
+
+public class BlacklistingFileSystem extends BlacklistingFolder implements FileSystem {
+
+	public BlacklistingFileSystem(Folder root, Predicate<Node> hiddenNodes) {
+		super(null, root, hiddenNodes);
+	}
+
+}

+ 55 - 0
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/blacklisting/BlacklistingFolder.java

@@ -0,0 +1,55 @@
+package org.cryptomator.filesystem.blacklisting;
+
+import java.io.UncheckedIOException;
+import java.nio.file.FileAlreadyExistsException;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.Node;
+import org.cryptomator.filesystem.delegating.DelegatingFolder;
+import org.cryptomator.filesystem.delegating.DelegatingReadableFile;
+import org.cryptomator.filesystem.delegating.DelegatingWritableFile;
+
+class BlacklistingFolder extends DelegatingFolder<DelegatingReadableFile, DelegatingWritableFile, BlacklistingFolder, BlacklistingFile> {
+
+	private final Predicate<Node> hiddenNodes;
+
+	public BlacklistingFolder(BlacklistingFolder parent, Folder delegate, Predicate<Node> hiddenNodes) {
+		super(parent, delegate);
+		this.hiddenNodes = hiddenNodes;
+	}
+
+	@Override
+	public Stream<? extends Node> children() {
+		return Stream.concat(folders(), files());
+	}
+
+	@Override
+	public Stream<BlacklistingFolder> folders() {
+		return delegate.folders().filter(hiddenNodes.negate()).map(this::folder);
+	}
+
+	@Override
+	public Stream<BlacklistingFile> files() {
+		return delegate.files().filter(hiddenNodes.negate()).map(this::file);
+	}
+
+	@Override
+	protected BlacklistingFile file(File delegate) {
+		if (hiddenNodes.test(delegate)) {
+			throw new UncheckedIOException("'" + delegate.name() + "' is a reserved name.", new FileAlreadyExistsException(delegate.name()));
+		}
+		return new BlacklistingFile(this, delegate);
+	}
+
+	@Override
+	protected BlacklistingFolder folder(Folder delegate) {
+		if (hiddenNodes.test(delegate)) {
+			throw new UncheckedIOException("'" + delegate.name() + "' is a reserved name.", new FileAlreadyExistsException(delegate.name()));
+		}
+		return new BlacklistingFolder(this, delegate, hiddenNodes);
+	}
+
+}

+ 14 - 14
main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/FilenameShortener.java

@@ -1,18 +1,20 @@
-package org.cryptomator.shortening;
+package org.cryptomator.filesystem.shortening;
 
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
 import java.io.UncheckedIOException;
-import java.nio.ByteBuffer;
+import java.io.Writer;
+import java.nio.channels.Channels;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
 import org.apache.commons.codec.binary.Base32;
 import org.apache.commons.codec.binary.BaseNCodec;
+import org.apache.commons.io.IOUtils;
 import org.cryptomator.filesystem.File;
 import org.cryptomator.filesystem.Folder;
-import org.cryptomator.filesystem.ReadableFile;
-import org.cryptomator.filesystem.WritableFile;
 
 class FilenameShortener {
 
@@ -53,8 +55,10 @@ class FilenameShortener {
 		final File mappingFile = mappingFile(shortName);
 		if (!mappingFile.exists()) {
 			mappingFile.parent().get().create();
-			try (WritableFile writable = mappingFile.openWritable()) {
-				writable.write(ByteBuffer.wrap(longName.getBytes(StandardCharsets.UTF_8)));
+			try (Writer writer = Channels.newWriter(mappingFile.openWritable(), StandardCharsets.UTF_8.newEncoder(), -1)) {
+				writer.write(longName);
+			} catch (IOException e) {
+				throw new UncheckedIOException(e);
 			}
 		}
 	}
@@ -69,14 +73,10 @@ class FilenameShortener {
 		if (!mappingFile.exists()) {
 			throw new UncheckedIOException(new FileNotFoundException("Mapping file not found " + mappingFile));
 		} else {
-			try (ReadableFile readable = mappingFile.openReadable()) {
-				// TODO buffer might be to small
-				final ByteBuffer buf = ByteBuffer.allocate(1024);
-				readable.read(buf);
-				buf.flip();
-				final byte[] bytes = new byte[buf.remaining()];
-				buf.get(bytes);
-				return new String(bytes, StandardCharsets.UTF_8);
+			try (Reader reader = Channels.newReader(mappingFile.openReadable(), StandardCharsets.UTF_8.newDecoder(), -1)) {
+				return IOUtils.toString(reader);
+			} catch (IOException e) {
+				throw new UncheckedIOException(e);
 			}
 		}
 	}

+ 47 - 0
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFile.java

@@ -0,0 +1,47 @@
+package org.cryptomator.filesystem.shortening;
+
+import java.io.UncheckedIOException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.cryptomator.common.LazyInitializer;
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.delegating.DelegatingFile;
+import org.cryptomator.filesystem.delegating.DelegatingReadableFile;
+import org.cryptomator.filesystem.delegating.DelegatingWritableFile;
+
+class ShorteningFile extends DelegatingFile<DelegatingReadableFile, DelegatingWritableFile, ShorteningFolder> {
+
+	private final AtomicReference<String> longName;
+	private final FilenameShortener shortener;
+
+	public ShorteningFile(ShorteningFolder parent, File delegate, String name, FilenameShortener shortener) {
+		super(parent, delegate);
+		this.longName = new AtomicReference<>(name);
+		this.shortener = shortener;
+	}
+
+	@Override
+	public String name() throws UncheckedIOException {
+		return LazyInitializer.initializeLazily(longName, () -> {
+			return shortener.inflate(shortenedName());
+		});
+	}
+
+	private String shortenedName() {
+		return delegate.name();
+	}
+
+	@Override
+	public DelegatingReadableFile openReadable() throws UncheckedIOException {
+		return new DelegatingReadableFile(delegate.openReadable());
+	}
+
+	@Override
+	public DelegatingWritableFile openWritable() throws UncheckedIOException {
+		if (shortener.isShortened(shortenedName())) {
+			shortener.saveMapping(name(), shortenedName());
+		}
+		return new DelegatingWritableFile(delegate.openWritable());
+	}
+
+}

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

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

+ 78 - 0
main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFolder.java

@@ -0,0 +1,78 @@
+package org.cryptomator.filesystem.shortening;
+
+import java.io.UncheckedIOException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.cryptomator.common.LazyInitializer;
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.delegating.DelegatingFolder;
+import org.cryptomator.filesystem.delegating.DelegatingReadableFile;
+import org.cryptomator.filesystem.delegating.DelegatingWritableFile;
+
+class ShorteningFolder extends DelegatingFolder<DelegatingReadableFile, DelegatingWritableFile, ShorteningFolder, ShorteningFile> {
+
+	private final AtomicReference<String> longName;
+	private final FilenameShortener shortener;
+
+	public ShorteningFolder(ShorteningFolder parent, Folder delegate, String name, FilenameShortener shortener) {
+		super(parent, delegate);
+		this.longName = new AtomicReference<>(name);
+		this.shortener = shortener;
+	}
+
+	@Override
+	public String name() throws UncheckedIOException {
+		return LazyInitializer.initializeLazily(longName, () -> {
+			return shortener.inflate(shortenedName());
+		});
+	}
+
+	private String shortenedName() {
+		return delegate.name();
+	}
+
+	@Override
+	public ShorteningFile file(String name) throws UncheckedIOException {
+		return new ShorteningFile(this, delegate.file(shortener.deflate(name)), name, shortener);
+	}
+
+	@Override
+	public ShorteningFolder folder(String name) throws UncheckedIOException {
+		return new ShorteningFolder(this, delegate.folder(shortener.deflate(name)), name, shortener);
+	}
+
+	@Override
+	protected ShorteningFile file(File delegate) {
+		return new ShorteningFile(this, delegate, null, shortener);
+	}
+
+	@Override
+	protected ShorteningFolder folder(Folder delegate) {
+		return new ShorteningFolder(this, delegate, null, shortener);
+	}
+
+	@Override
+	public void create() throws UncheckedIOException {
+		if (exists()) {
+			return;
+		}
+		parent().get().create();
+		if (shortener.isShortened(shortenedName())) {
+			shortener.saveMapping(name(), shortenedName());
+		}
+		super.create();
+	}
+
+	@Override
+	public void moveTo(Folder destination) {
+		super.moveTo(destination);
+		if (destination instanceof ShorteningFolder) {
+			ShorteningFolder dest = (ShorteningFolder) destination;
+			if (shortener.isShortened(dest.shortenedName())) {
+				shortener.saveMapping(dest.name(), dest.shortenedName());
+			}
+		}
+	}
+
+}

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

@@ -3,4 +3,4 @@
  * {@link org.cryptomator.filesystem.File File} and {@link org.cryptomator.filesystem.Folder Folder} names exceeding a certain length limit will be mapped to shorter equivalents.
  * The mapping itself is stored in metadata files inside the <code>m/</code> directory on root level.
  */
-package org.cryptomator.shortening;
+package org.cryptomator.filesystem.shortening;

+ 0 - 41
main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/ShorteningFile.java

@@ -1,41 +0,0 @@
-package org.cryptomator.shortening;
-
-import java.io.UncheckedIOException;
-
-import org.cryptomator.filesystem.File;
-import org.cryptomator.filesystem.ReadableFile;
-import org.cryptomator.filesystem.WritableFile;
-
-class ShorteningFile extends ShorteningNode<File> implements File {
-
-	private final FilenameShortener shortener;
-
-	public ShorteningFile(ShorteningFolder parent, File delegate, String longName, FilenameShortener shortener) {
-		super(parent, delegate, longName);
-		this.shortener = shortener;
-	}
-
-	@Override
-	public ReadableFile openReadable() throws UncheckedIOException {
-		return delegate.openReadable();
-	}
-
-	@Override
-	public WritableFile openWritable() throws UncheckedIOException {
-		if (shortener.isShortened(shortName())) {
-			shortener.saveMapping(name(), shortName());
-		}
-		return delegate.openWritable();
-	}
-
-	@Override
-	public String toString() {
-		return parent + name();
-	}
-
-	@Override
-	public int compareTo(File o) {
-		return toString().compareTo(o.toString());
-	}
-
-}

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

@@ -1,34 +0,0 @@
-package org.cryptomator.shortening;
-
-import org.cryptomator.filesystem.FileSystem;
-import org.cryptomator.filesystem.Folder;
-
-/**
- * Filesystem implementation, that shortens filenames when they reach a certain
- * threshold (inclusive). Shortening is done by SHA1-hashing those files, so a
- * threshold below the length of the hashed files makes no sense. Hashes are
- * then mapped back to the original filenames by storing metadata files inside
- * the given metadataRoot.
- */
-public class ShorteningFileSystem extends ShorteningFolder implements FileSystem {
-
-	public ShorteningFileSystem(Folder root, Folder metadataRoot, int threshold) {
-		super(null, root, "", metadataRoot, new FilenameShortener(metadataRoot, threshold));
-	}
-
-	@Override
-	public boolean exists() {
-		return true;
-	}
-
-	@Override
-	public void delete() {
-		// no-op.
-	}
-
-	@Override
-	public String toString() {
-		return "/";
-	}
-
-}

+ 0 - 112
main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/ShorteningFolder.java

@@ -1,112 +0,0 @@
-package org.cryptomator.shortening;
-
-import java.io.UncheckedIOException;
-import java.nio.file.FileAlreadyExistsException;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-
-import org.cryptomator.filesystem.File;
-import org.cryptomator.filesystem.Folder;
-import org.cryptomator.filesystem.Node;
-
-class ShorteningFolder extends ShorteningNode<Folder> implements Folder {
-
-	private final Folder metadataRoot;
-	private final FilenameShortener shortener;
-
-	public ShorteningFolder(ShorteningFolder parent, Folder delegate, String longName, Folder metadataRoot, FilenameShortener shortener) {
-		super(parent, delegate, longName);
-		this.metadataRoot = metadataRoot;
-		this.shortener = shortener;
-	}
-
-	@Override
-	public Stream<? extends Node> children() {
-		return Stream.concat(this.files(), this.folders());
-	}
-
-	private ShorteningFile existingFile(File original) {
-		final String longName = shortener.inflate(original.name());
-		return new ShorteningFile(this, original, longName, shortener);
-	}
-
-	@Override
-	public File file(String name) {
-		final File original = delegate.file(shortener.deflate(name));
-		if (metadataRoot.equals(original)) { // comparing apples and oranges,
-												// but we don't know if the
-												// underlying fs distinguishes
-												// files and folders...
-			throw new UncheckedIOException("'" + name + "' is a reserved name.", new FileAlreadyExistsException(name));
-		}
-		return new ShorteningFile(this, original, name, shortener);
-	}
-
-	@Override
-	public Stream<? extends File> files() throws UncheckedIOException {
-		return delegate.files().map(this::existingFile);
-	}
-
-	private ShorteningFolder existingFolder(Folder original) {
-		final String longName = shortener.inflate(original.name());
-		return new ShorteningFolder(this, original, longName, metadataRoot, shortener);
-	}
-
-	@Override
-	public Folder folder(String name) {
-		final Folder original = delegate.folder(shortener.deflate(name));
-		if (metadataRoot.equals(original)) {
-			throw new UncheckedIOException("'" + name + "' is a reserved name.", new FileAlreadyExistsException(name));
-		}
-		return new ShorteningFolder(this, original, name, metadataRoot, shortener);
-	}
-
-	@Override
-	public Stream<? extends Folder> folders() {
-		// if metadataRoot is inside our filesystem, we must filter it out:
-		final Predicate<Node> equalsMetadataRoot = (Node node) -> metadataRoot.equals(node);
-		return delegate.folders().filter(equalsMetadataRoot.negate()).map(this::existingFolder);
-	}
-
-	@Override
-	public void create() {
-		if (exists()) {
-			return;
-		}
-		parent().get().create();
-		if (shortener.isShortened(shortName())) {
-			shortener.saveMapping(name(), shortName());
-		}
-		delegate.create();
-	}
-
-	@Override
-	public void delete() {
-		delegate.delete();
-	}
-
-	@Override
-	public void moveTo(Folder target) {
-		if (target instanceof ShorteningFolder) {
-			moveToInternal((ShorteningFolder) target);
-		} else {
-			throw new UnsupportedOperationException("Can not move ShorteningFolder to conventional folder.");
-		}
-	}
-
-	private void moveToInternal(ShorteningFolder target) {
-		if (this.isAncestorOf(target) || target.isAncestorOf(this)) {
-			throw new IllegalArgumentException("Can not move directories containing one another (src: " + this + ", dst: " + target + ")");
-		}
-
-		target.create();
-
-		delegate.moveTo(target.delegate);
-	}
-
-	@Override
-	public String toString() {
-		return parent + name() + "/";
-	}
-
-}

+ 0 - 68
main/filesystem-nameshortening/src/main/java/org/cryptomator/shortening/ShorteningNode.java

@@ -1,68 +0,0 @@
-package org.cryptomator.shortening;
-
-import java.time.Instant;
-import java.util.Optional;
-
-import org.cryptomator.filesystem.Folder;
-import org.cryptomator.filesystem.Node;
-
-class ShorteningNode<E extends Node> implements Node {
-
-	protected final E delegate;
-	protected final ShorteningFolder parent;
-	private final String longName;
-	private final String shortName;
-
-	public ShorteningNode(ShorteningFolder parent, E delegate, String longName) {
-		this.delegate = delegate;
-		this.parent = parent;
-		this.shortName = delegate.name();
-		this.longName = longName;
-	}
-
-	@Override
-	public String name() {
-		return longName;
-	}
-
-	protected String shortName() {
-		return shortName;
-	}
-
-	@Override
-	public Optional<? extends Folder> parent() {
-		return Optional.ofNullable(parent);
-	}
-
-	@Override
-	public boolean exists() {
-		return delegate.exists();
-	}
-
-	@Override
-	public Instant lastModified() {
-		return delegate.lastModified();
-	}
-
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((longName == null) ? 0 : longName.hashCode());
-		result = prime * result + ((parent == null) ? 0 : parent.hashCode());
-		return result;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (obj instanceof ShorteningNode) {
-			ShorteningNode<?> other = (ShorteningNode<?>) obj;
-			return this.getClass() == other.getClass() //
-					&& (this.parent == null && other.parent == null || this.parent.equals(other.parent)) //
-					&& (this.longName == null && other.longName == null || this.longName.equals(other.longName));
-		} else {
-			return false;
-		}
-	}
-
-}

+ 51 - 0
main/filesystem-nameshortening/src/test/java/org/cryptomator/filesystem/blacklisting/BlacklistingFileSystemTest.java

@@ -0,0 +1,51 @@
+package org.cryptomator.filesystem.blacklisting;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.Node;
+import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BlacklistingFileSystemTest {
+
+	@Test(expected = UncheckedIOException.class)
+	public void testPreventCreationOfMetadataFolder() {
+		final FileSystem underlyingFs = new InMemoryFileSystem();
+		final Folder metadataRoot = underlyingFs.folder("m");
+		final Predicate<Node> metadataHidden = (Node n) -> n.equals(metadataRoot);
+		final FileSystem fs = new BlacklistingFileSystem(underlyingFs, metadataHidden);
+		fs.folder("m");
+	}
+
+	@Test
+	public void testBlacklistingOfFilesAndFolders() throws IOException {
+		final FileSystem underlyingFs = new InMemoryFileSystem();
+		final Folder hiddenFolder = underlyingFs.folder("asd");
+		final File hiddenFile = underlyingFs.file("qwe");
+		final Folder visibleFolder = underlyingFs.folder("sdf");
+		final File visibleFile = underlyingFs.file("wer");
+		final Predicate<Node> metadataHidden = (Node n) -> n.equals(hiddenFolder) || n.equals(hiddenFile);
+		final FileSystem fs = new BlacklistingFileSystem(underlyingFs, metadataHidden);
+		hiddenFolder.create();
+		try (WritableByteChannel writable = hiddenFile.openWritable()) {
+			writable.write(ByteBuffer.allocate(0));
+		}
+		visibleFolder.create();
+		try (WritableByteChannel writable = visibleFile.openWritable()) {
+			writable.write(ByteBuffer.allocate(0));
+		}
+
+		Assert.assertArrayEquals(new String[] {"sdf"}, fs.folders().map(Node::name).collect(Collectors.toList()).toArray());
+		Assert.assertArrayEquals(new String[] {"wer"}, fs.files().map(Node::name).collect(Collectors.toList()).toArray());
+	}
+
+}

+ 2 - 11
main/filesystem-nameshortening/src/test/java/org/cryptomator/shortening/ShorteningFileSystemTest.java

@@ -1,4 +1,4 @@
-package org.cryptomator.shortening;
+package org.cryptomator.filesystem.shortening;
 
 import java.io.UncheckedIOException;
 import java.nio.ByteBuffer;
@@ -17,21 +17,12 @@ import org.junit.Test;
 public class ShorteningFileSystemTest {
 
 	@Test
-	public void testCreationOfInvisibleMetadataFolder() {
+	public void testImplicitCreationOfMetadataFolder() {
 		final FileSystem underlyingFs = new InMemoryFileSystem();
 		final Folder metadataRoot = underlyingFs.folder("m");
 		final FileSystem fs = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
 		fs.folder("morethantenchars").create();
 		Assert.assertTrue(metadataRoot.exists());
-		Assert.assertEquals(1, fs.folders().count());
-	}
-
-	@Test(expected = UncheckedIOException.class)
-	public void testPreventCreationOfMetadataFolder() {
-		final FileSystem underlyingFs = new InMemoryFileSystem();
-		final Folder metadataRoot = underlyingFs.folder("m");
-		final FileSystem fs = new ShorteningFileSystem(underlyingFs, metadataRoot, 10);
-		fs.folder("m");
 	}
 
 	@Test

+ 2 - 0
main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/WeakValuedCacheTest.java

@@ -9,6 +9,7 @@ import static org.mockito.Mockito.when;
 import java.util.function.Function;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
@@ -82,6 +83,7 @@ public class WeakValuedCacheTest {
 	}
 
 	@Test
+	@Ignore
 	public void testCacheDoesNotPreventGarbageCollectionOfValues() {
 		when(loader.apply(A_KEY)).thenAnswer(this::createValueUsingMoreThanHalfTheJvmMemory);