Browse Source

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

Sebastian Stenzel 9 years ago
parent
commit
99fce8d0b7

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

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

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

@@ -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() {

+ 51 - 3
main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/ConflictResolverTest.java

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