automatically resolve conflicts for directory files, that contain the same directory ID

This commit is contained in:
Sebastian Stenzel
2016-05-23 11:02:44 +02:00
parent bf05c59c3b
commit 99fce8d0b7
3 changed files with 60 additions and 5 deletions

View File

@@ -12,6 +12,7 @@ import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.io.FileContents;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,7 +53,13 @@ final class ConflictResolver {
Folder folder = conflictingFile.parent().get();
File canonicalFile = folder.file(isDirectory ? ciphertext + DIR_SUFFIX : ciphertext);
if (canonicalFile.exists()) {
// conflict detected! look for an alternative name:
// there must not be two directories pointing to the same directory id. In this case no human interaction is needed to resolve this conflict:
if (isDirectory && FileContents.UTF_8.readContents(canonicalFile).equals(FileContents.UTF_8.readContents(conflictingFile))) {
conflictingFile.delete();
return canonicalFile;
}
// conventional conflict detected! look for an alternative name:
File alternativeFile;
String conflictId;
do {

View File

@@ -89,7 +89,7 @@ class CryptoFolder extends CryptoNode implements Folder {
private Stream<File> nonConflictingFiles() {
final Stream<? extends File> files = physicalFolder().filter(Folder::exists).map(Folder::files).orElse(Stream.empty());
return files.filter(containsEncryptedName()).map(conflictResolver::resolveIfNecessary);
return files.filter(containsEncryptedName()).map(conflictResolver::resolveIfNecessary).distinct();
}
private Predicate<File> containsEncryptedName() {

View File

@@ -1,5 +1,6 @@
package org.cryptomator.filesystem.crypto;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.function.Function;
@@ -9,10 +10,13 @@ import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.BaseNCodec;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.ReadableFile;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class ConflictResolverTest {
@@ -101,10 +105,54 @@ public class ConflictResolverTest {
}
@Test
public void testConflictingFolder() {
File resolved = conflictResolver.resolveIfNecessary(conflictingFolder);
public void testConflictingFolderWithDifferentId() {
ReadableFile directoryId1 = Mockito.mock(ReadableFile.class);
ReadableFile directoryId2 = Mockito.mock(ReadableFile.class);
Mockito.when(canonicalFolder.openReadable()).thenReturn(directoryId1);
Mockito.when(conflictingFolder.openReadable()).thenReturn(directoryId2);
Mockito.when(directoryId1.read(Mockito.any())).thenAnswer(new FillBufferAnswer("id1"));
Mockito.when(directoryId2.read(Mockito.any())).thenAnswer(new FillBufferAnswer("id2"));
File result = conflictResolver.resolveIfNecessary(conflictingFolder);
Mockito.verify(conflictingFolder).moveTo(resolved);
Assert.assertSame(resolved, resolved);
Assert.assertSame(resolved, result);
}
@Test
public void testConflictingFolderWithSameId() {
ReadableFile directoryId1 = Mockito.mock(ReadableFile.class);
Mockito.when(canonicalFolder.openReadable()).thenReturn(directoryId1);
Mockito.when(conflictingFolder.openReadable()).thenReturn(directoryId1);
Mockito.when(directoryId1.read(Mockito.any())).thenAnswer(new FillBufferAnswer("id1"));
File result = conflictResolver.resolveIfNecessary(conflictingFolder);
Mockito.verify(conflictingFolder).delete();
Assert.assertSame(canonicalFolder, result);
}
private static class FillBufferAnswer implements Answer<Integer> {
private final byte[] content;
private int bytesRead = 0;
public FillBufferAnswer(String content) {
this.content = content.getBytes(StandardCharsets.UTF_8);
}
@Override
public Integer answer(InvocationOnMock invocation) throws Throwable {
if (bytesRead >= content.length) {
bytesRead = 0;
return -1;
} else {
ByteBuffer buf = invocation.getArgumentAt(0, ByteBuffer.class);
int delta = Math.min(content.length - bytesRead, buf.remaining());
buf.put(content, bytesRead, delta);
bytesRead += delta;
return content.length;
}
}
}
}