mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-21 04:01:27 +00:00
automatically resolve conflicts for directory files, that contain the same directory ID
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user