separated filename shortening layer from metadata hiding layer

This commit is contained in:
Sebastian Stenzel
2015-12-29 16:24:42 +01:00
parent aa89f60c2f
commit 9385c3bf6d
20 changed files with 370 additions and 317 deletions

View File

@@ -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();
}
}
}
}

View File

@@ -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.");

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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());
}
}
}
}

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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 "/";
}
}

View File

@@ -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() + "/";
}
}

View File

@@ -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;
}
}
}

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -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);