From 1689c581426a8e6f79391c6e3bbfbb298d91760d Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 29 Jan 2025 15:34:38 +0100 Subject: [PATCH] add conflictResolved event --- .../cryptofs/CryptoFileSystemModule.java | 1 - .../cryptofs/dir/C9rConflictResolver.java | 12 +++++++++++- .../event/ConflictResolutionFailedEvent.java | 5 +++++ .../cryptofs/event/ConflictResolvedEvent.java | 11 +++++++++++ .../cryptomator/cryptofs/fh/ChunkLoader.java | 2 +- .../cryptofs/fh/FileHeaderHolder.java | 2 +- .../cryptofs/dir/C9rConflictResolverTest.java | 19 ++++++++++++++++--- 7 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/cryptomator/cryptofs/event/ConflictResolutionFailedEvent.java create mode 100644 src/main/java/org/cryptomator/cryptofs/event/ConflictResolvedEvent.java diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java index 8d861b47..8ae0842f 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java @@ -41,7 +41,6 @@ public Optional provideNativeFileStore(@PathToVault Path pathToVault) @Provides @CryptoFileSystemScoped - @Named("Babadook") public Consumer provideFilesystemEventConsumer(CryptoFileSystemProperties fsProps) { return (Consumer) fsProps.get(CryptoFileSystemProperties.PROPERTY_NOTIFY_METHOD); } diff --git a/src/main/java/org/cryptomator/cryptofs/dir/C9rConflictResolver.java b/src/main/java/org/cryptomator/cryptofs/dir/C9rConflictResolver.java index 4a7db946..bb6eca97 100644 --- a/src/main/java/org/cryptomator/cryptofs/dir/C9rConflictResolver.java +++ b/src/main/java/org/cryptomator/cryptofs/dir/C9rConflictResolver.java @@ -6,6 +6,8 @@ import com.google.common.io.RecursiveDeleteOption; import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.common.Constants; +import org.cryptomator.cryptofs.event.ConflictResolvedEvent; +import org.cryptomator.cryptofs.event.FilesystemEvent; import org.cryptomator.cryptolib.api.Cryptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,10 +16,12 @@ import javax.inject.Named; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.function.Consumer; import java.util.stream.Stream; import static org.cryptomator.cryptofs.common.Constants.DIR_FILE_NAME; @@ -33,14 +37,18 @@ class C9rConflictResolver { private final Cryptor cryptor; private final byte[] dirId; private final int maxC9rFileNameLength; + private final Path cleartextPath; private final int maxCleartextFileNameLength; + private final Consumer eventConsumer; @Inject - public C9rConflictResolver(Cryptor cryptor, @Named("dirId") String dirId, VaultConfig vaultConfig) { + public C9rConflictResolver(Cryptor cryptor, @Named("dirId") String dirId, VaultConfig vaultConfig, Consumer eventConsumer, @Named("cleartextPath") Path cleartextPath) { this.cryptor = cryptor; this.dirId = dirId.getBytes(StandardCharsets.US_ASCII); this.maxC9rFileNameLength = vaultConfig.getShorteningThreshold(); + this.cleartextPath = cleartextPath; this.maxCleartextFileNameLength = (maxC9rFileNameLength - 4) / 4 * 3 - 16; // math from FileSystemCapabilityChecker.determineSupportedCleartextFileNameLength() + this.eventConsumer = eventConsumer; } public Stream process(Node node) { @@ -62,6 +70,7 @@ public Stream process(Node node) { return resolveConflict(node, canonicalPath); } catch (IOException e) { LOG.error("Failed to resolve conflict for " + node.ciphertextPath, e); + //TODO: notify! return Stream.empty(); } } @@ -111,6 +120,7 @@ private Node renameConflictingFile(Path canonicalPath, Path conflictingPath, Str Node node = new Node(alternativePath); node.cleartextName = alternativeCleartext; node.extractedCiphertext = alternativeCiphertext; + eventConsumer.accept(new ConflictResolvedEvent(cleartextPath.resolve(cleartext), canonicalPath, cleartextPath.resolve(alternativeCleartext),alternativePath)); return node; } diff --git a/src/main/java/org/cryptomator/cryptofs/event/ConflictResolutionFailedEvent.java b/src/main/java/org/cryptomator/cryptofs/event/ConflictResolutionFailedEvent.java new file mode 100644 index 00000000..07ce7310 --- /dev/null +++ b/src/main/java/org/cryptomator/cryptofs/event/ConflictResolutionFailedEvent.java @@ -0,0 +1,5 @@ +package org.cryptomator.cryptofs.event; + +public class ConflictResolutionFailedEvent { + +} diff --git a/src/main/java/org/cryptomator/cryptofs/event/ConflictResolvedEvent.java b/src/main/java/org/cryptomator/cryptofs/event/ConflictResolvedEvent.java new file mode 100644 index 00000000..d8bc1b3b --- /dev/null +++ b/src/main/java/org/cryptomator/cryptofs/event/ConflictResolvedEvent.java @@ -0,0 +1,11 @@ +package org.cryptomator.cryptofs.event; + +import java.nio.file.Path; + +public record ConflictResolvedEvent(Path cleartextPath, Path ciphertextPath, Path oldVersionCleartextPath, Path oldVersionCiphertextPath) implements FilesystemEvent{ + + @Override + public Type getType() { + return Type.CONFLICT_RESOLVED; + } +} diff --git a/src/main/java/org/cryptomator/cryptofs/fh/ChunkLoader.java b/src/main/java/org/cryptomator/cryptofs/fh/ChunkLoader.java index ed646a7d..92b88274 100644 --- a/src/main/java/org/cryptomator/cryptofs/fh/ChunkLoader.java +++ b/src/main/java/org/cryptomator/cryptofs/fh/ChunkLoader.java @@ -26,7 +26,7 @@ class ChunkLoader { private final BufferPool bufferPool; @Inject - public ChunkLoader(@Named("Babadook") Consumer eventConsumer, @CurrentOpenFilePath AtomicReference path, Cryptor cryptor, ChunkIO ciphertext, FileHeaderHolder headerHolder, CryptoFileSystemStats stats, BufferPool bufferPool) { + public ChunkLoader(Consumer eventConsumer, @CurrentOpenFilePath AtomicReference path, Cryptor cryptor, ChunkIO ciphertext, FileHeaderHolder headerHolder, CryptoFileSystemStats stats, BufferPool bufferPool) { this.eventConsumer = eventConsumer; this.path = path; this.cryptor = cryptor; diff --git a/src/main/java/org/cryptomator/cryptofs/fh/FileHeaderHolder.java b/src/main/java/org/cryptomator/cryptofs/fh/FileHeaderHolder.java index cea429fb..3ed5f4cd 100644 --- a/src/main/java/org/cryptomator/cryptofs/fh/FileHeaderHolder.java +++ b/src/main/java/org/cryptomator/cryptofs/fh/FileHeaderHolder.java @@ -32,7 +32,7 @@ public class FileHeaderHolder { private final AtomicBoolean isPersisted = new AtomicBoolean(); @Inject - public FileHeaderHolder(@Named("Babadook") Consumer eventConsumer, Cryptor cryptor, @CurrentOpenFilePath AtomicReference path) { + public FileHeaderHolder(Consumer eventConsumer, Cryptor cryptor, @CurrentOpenFilePath AtomicReference path) { this.eventConsumer = eventConsumer; this.cryptor = cryptor; this.path = path; diff --git a/src/test/java/org/cryptomator/cryptofs/dir/C9rConflictResolverTest.java b/src/test/java/org/cryptomator/cryptofs/dir/C9rConflictResolverTest.java index fc868950..2c3f4dd5 100644 --- a/src/test/java/org/cryptomator/cryptofs/dir/C9rConflictResolverTest.java +++ b/src/test/java/org/cryptomator/cryptofs/dir/C9rConflictResolverTest.java @@ -1,6 +1,8 @@ package org.cryptomator.cryptofs.dir; import org.cryptomator.cryptofs.VaultConfig; +import org.cryptomator.cryptofs.event.ConflictResolvedEvent; +import org.cryptomator.cryptofs.event.FilesystemEvent; import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.api.FileNameCryptor; import org.junit.jupiter.api.Assertions; @@ -8,21 +10,27 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentMatcher; +import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.function.Consumer; import java.util.stream.Stream; +import static org.mockito.Mockito.verify; + public class C9rConflictResolverTest { private Cryptor cryptor; private FileNameCryptor fileNameCryptor; private VaultConfig vaultConfig; + private Consumer eventConsumer = Mockito.mock(Consumer.class); + private Path cleartextPath = Mockito.mock(Path.class, "/clear/text/path/"); private C9rConflictResolver conflictResolver; @BeforeEach @@ -32,9 +40,10 @@ public void setup() { vaultConfig = Mockito.mock(VaultConfig.class); Mockito.when(cryptor.fileNameCryptor()).thenReturn(fileNameCryptor); Mockito.when(vaultConfig.getShorteningThreshold()).thenReturn(44); // results in max cleartext size = 14 - conflictResolver = new C9rConflictResolver(cryptor, "foo", vaultConfig); + Mockito.when(cleartextPath.resolve(Mockito.anyString())).thenReturn(cleartextPath); + conflictResolver = new C9rConflictResolver(cryptor, "foo", vaultConfig, eventConsumer, cleartextPath); } - + @Test public void testResolveNonConflictingNode() { Node unresolved = new Node(Paths.get("foo.c9r")); @@ -75,6 +84,8 @@ public void testResolveConflictingFileByChoosingNewName(@TempDir Path dir) throw Assertions.assertEquals("bar (1).txt", resolved.cleartextName); Assertions.assertTrue(Files.exists(resolved.ciphertextPath)); Assertions.assertFalse(Files.exists(unresolved.ciphertextPath)); + var isConflictResolvedEvent = (ArgumentMatcher) ev -> ev instanceof ConflictResolvedEvent; + verify(eventConsumer).accept(ArgumentMatchers.argThat(isConflictResolvedEvent)); } @Test @@ -94,6 +105,8 @@ public void testResolveConflictingFileByChoosingNewLengthLimitedName(@TempDir Pa Assertions.assertEquals("hello (1).txt", resolved.cleartextName); Assertions.assertTrue(Files.exists(resolved.ciphertextPath)); Assertions.assertFalse(Files.exists(unresolved.ciphertextPath)); + var isConflictResolvedEvent = (ArgumentMatcher) ev -> ev instanceof ConflictResolvedEvent; + verify(eventConsumer).accept(ArgumentMatchers.argThat(isConflictResolvedEvent)); } @Test