From 544c2ba65e1f23ad41e61a3728b55a5b9f8425c2 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Wed, 21 Dec 2022 16:46:23 +0100 Subject: [PATCH 1/5] Added possibility to get the ciphertext path from a cleartext path in CryptoFileSystem --- .../org/cryptomator/cryptofs/CryptoFileSystem.java | 10 ++++++++++ .../org/cryptomator/cryptofs/CryptoFileSystemImpl.java | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java index 3a344d25..532440e5 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java @@ -1,5 +1,6 @@ package org.cryptomator.cryptofs; +import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; @@ -29,6 +30,15 @@ public abstract class CryptoFileSystem extends FileSystem { */ public abstract Path getPathToVault(); + /** + * Provides the {@link Path} to the ciphertext from a given cleartext path. + * + * @param cleartextPath the path to the cleartext file or folder belonging to this {@code CryptoFileSystem}, which internally is a {@code CryptoPath} + * @return the {@link Path} to ciphertext file or folder + * @throws IOException + */ + public abstract Path getPathToCiphertext(Path cleartextPath) throws IOException; + /** * Provides file system performance statistics. * diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java index f4523c67..e046b52b 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java @@ -136,6 +136,16 @@ public Path getPathToVault() { return pathToVault; } + @Override + public Path getPathToCiphertext(Path cleartextPath) throws IOException { + var cryptoPath = CryptoPath.cast(cleartextPath); + if (Files.isDirectory(cleartextPath)) { + return cryptoPathMapper.getCiphertextDir(cryptoPath).path; + } else { + return cryptoPathMapper.getCiphertextFilePath(cryptoPath).getRawPath(); + } + } + @Override public CryptoFileSystemStats getStats() { return stats; From 56073b8c1760fcd44ef49195f40e4116fe6ab325 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 30 Dec 2022 11:59:26 +0100 Subject: [PATCH 2/5] Second iteration for api and impl: * change method name * update doc * point always to encrypted _data_ --- .../org/cryptomator/cryptofs/CryptoFileSystem.java | 8 ++++---- .../cryptomator/cryptofs/CryptoFileSystemImpl.java | 14 +++++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java index 532440e5..4deee25f 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java @@ -31,13 +31,13 @@ public abstract class CryptoFileSystem extends FileSystem { public abstract Path getPathToVault(); /** - * Provides the {@link Path} to the ciphertext from a given cleartext path. + * Provides the {@link Path} to the (data) ciphertext from a given cleartext path. * - * @param cleartextPath the path to the cleartext file or folder belonging to this {@code CryptoFileSystem}, which internally is a {@code CryptoPath} - * @return the {@link Path} to ciphertext file or folder + * @param cleartextPath path to the cleartext file or folder belonging to this {@link CryptoFileSystem}. Internally the path must be an instance of {@link CryptoPath} + * @return the {@link Path} to ciphertext file or folder containing teh actual encrypted data * @throws IOException */ - public abstract Path getPathToCiphertext(Path cleartextPath) throws IOException; + public abstract Path getPathToDataCiphertext(Path cleartextPath) throws IOException; /** * Provides file system performance statistics. diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java index e046b52b..50852ec1 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java @@ -137,12 +137,16 @@ public Path getPathToVault() { } @Override - public Path getPathToCiphertext(Path cleartextPath) throws IOException { - var cryptoPath = CryptoPath.cast(cleartextPath); - if (Files.isDirectory(cleartextPath)) { - return cryptoPathMapper.getCiphertextDir(cryptoPath).path; + public Path getPathToDataCiphertext(Path cleartextPath) throws IOException { + var p = CryptoPath.castAndAssertAbsolute(cleartextPath); + var nodeType = cryptoPathMapper.getCiphertextFileType(p); + var cipherFile = cryptoPathMapper.getCiphertextFilePath(p); + if( nodeType == CiphertextFileType.DIRECTORY) { + return cryptoPathMapper.getCiphertextDir(p).path; + } else if( nodeType == CiphertextFileType.SYMLINK) { + return cipherFile.getSymlinkFilePath(); } else { - return cryptoPathMapper.getCiphertextFilePath(cryptoPath).getRawPath(); + return cipherFile.getFilePath(); } } From 971c1a8e6ca07ebe92fa129dd44069c31a83cd21 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Fri, 30 Dec 2022 12:58:59 +0100 Subject: [PATCH 3/5] Add unit tests and update doc --- .../cryptofs/CryptoFileSystem.java | 2 +- .../cryptofs/CryptoFileSystemImplTest.java | 94 ++++++++++++++++++- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java index 4deee25f..b0ccd05f 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java @@ -33,7 +33,7 @@ public abstract class CryptoFileSystem extends FileSystem { /** * Provides the {@link Path} to the (data) ciphertext from a given cleartext path. * - * @param cleartextPath path to the cleartext file or folder belonging to this {@link CryptoFileSystem}. Internally the path must be an instance of {@link CryptoPath} + * @param cleartextPath absolute path to the cleartext file or folder belonging to this {@link CryptoFileSystem}. Internally the path must be an instance of {@link CryptoPath} * @return the {@link Path} to ciphertext file or folder containing teh actual encrypted data * @throws IOException */ diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java index 5b0b1521..25bc169b 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java @@ -42,6 +42,7 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.PathMatcher; +import java.nio.file.ProviderMismatchException; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributeView; @@ -119,11 +120,11 @@ public void setup() { when(fileSystemProperties.maxCleartextNameLength()).thenReturn(32768); - inTest = new CryptoFileSystemImpl(provider, cryptoFileSystems, pathToVault, cryptor, - fileStore, stats, cryptoPathMapper, cryptoPathFactory, - pathMatcherFactory, directoryStreamFactory, dirIdProvider, dirIdBackup, - fileAttributeProvider, fileAttributeByNameProvider, fileAttributeViewProvider, - openCryptoFiles, symlinks, finallyUtil, ciphertextDirDeleter, readonlyFlag, + inTest = new CryptoFileSystemImpl(provider, cryptoFileSystems, pathToVault, cryptor, // + fileStore, stats, cryptoPathMapper, cryptoPathFactory, // + pathMatcherFactory, directoryStreamFactory, dirIdProvider, dirIdBackup, // + fileAttributeProvider, fileAttributeByNameProvider, fileAttributeViewProvider, // + openCryptoFiles, symlinks, finallyUtil, ciphertextDirDeleter, readonlyFlag, // fileSystemProperties); } @@ -188,6 +189,89 @@ public void testGetFileStoresReturnsFileStore() { Assertions.assertSame(fileStore, inTest.getFileStore()); } + @Nested + public class PathToDataCiphertext { + + @Test + @DisplayName("Getting data ciphertext path of directory returns ciphertext content dir") + public void testCleartextDirectory() throws IOException { + Path ciphertext = Mockito.mock(Path.class, "/d/AB/CD...XYZ/"); + Path cleartext = inTest.getPath("/"); + try (var cryptoPathMock = Mockito.mockStatic(CryptoPath.class)) { + cryptoPathMock.when(() -> CryptoPath.castAndAssertAbsolute(any())).thenReturn(cleartext); + when(cryptoPathMapper.getCiphertextFileType(any())).thenReturn(CiphertextFileType.DIRECTORY); + when(cryptoPathMapper.getCiphertextDir(any())).thenReturn(new CiphertextDirectory("foo", ciphertext)); + + Path result = inTest.getPathToDataCiphertext(cleartext); + Assertions.assertEquals(ciphertext, result); + } + } + + @Test + @DisplayName("Getting data ciphertext path of file returns ciphertext file") + public void testCleartextFile() throws IOException { + Path ciphertext = Mockito.mock(Path.class, "/d/AB/CD..XYZ/foo.c9r"); + Path cleartext = inTest.getPath("/foo.bar"); + try (var cryptoPathMock = Mockito.mockStatic(CryptoPath.class)) { + CiphertextFilePath p = Mockito.mock(CiphertextFilePath.class); + cryptoPathMock.when(() -> CryptoPath.castAndAssertAbsolute(any())).thenReturn(cleartext); + when(cryptoPathMapper.getCiphertextFileType(any())).thenReturn(CiphertextFileType.FILE); + when(cryptoPathMapper.getCiphertextFilePath(any())).thenReturn(p); + when(p.getFilePath()).thenReturn(ciphertext); + + Path result = inTest.getPathToDataCiphertext(cleartext); + Assertions.assertEquals(ciphertext, result); + } + } + + @Test + @DisplayName("Getting data ciphertext path of symlink returns ciphertext symlink.c9r") + public void testCleartextSymlink() throws IOException { + Path ciphertext = Mockito.mock(Path.class, "/d/AB/CD..XYZ/foo.c9s/symlink.c9r"); + Path cleartext = inTest.getPath("/foo.bar"); + try (var cryptoPathMock = Mockito.mockStatic(CryptoPath.class)) { + CiphertextFilePath p = Mockito.mock(CiphertextFilePath.class); + cryptoPathMock.when(() -> CryptoPath.castAndAssertAbsolute(any())).thenReturn(cleartext); + when(cryptoPathMapper.getCiphertextFileType(any())).thenReturn(CiphertextFileType.SYMLINK); + when(cryptoPathMapper.getCiphertextFilePath(any())).thenReturn(p); + when(p.getSymlinkFilePath()).thenReturn(ciphertext); + + Path result = inTest.getPathToDataCiphertext(cleartext); + Assertions.assertEquals(ciphertext, result); + } + } + + @Test + @DisplayName("Path not pointing into the vault throws exception") + public void testForeignPathThrows() throws IOException { + Path cleartext = Mockito.mock(Path.class, "/some.file"); + Assertions.assertThrows(ProviderMismatchException.class, () -> inTest.getPathToDataCiphertext(cleartext)); + } + + @Test + @DisplayName("Not existing resource throws NoSuchFileException") + public void testNoSuchFile() throws IOException { + Path cleartext = inTest.getPath("/i-do-not-exist"); + try (var cryptoPathMock = Mockito.mockStatic(CryptoPath.class)) { + cryptoPathMock.when(() -> CryptoPath.castAndAssertAbsolute(any())).thenReturn(cleartext); + when(cryptoPathMapper.getCiphertextFileType(any())).thenThrow(new NoSuchFileException("no such file")); + + Assertions.assertThrows(NoSuchFileException.class, () -> inTest.getPathToDataCiphertext(cleartext)); + } + } + + @Test + @DisplayName("Relative cleartext path throws exception") + public void testRelativePathException() throws IOException { + Path cleartext = inTest.getPath("relative/path"); + try (var cryptoPathMock = Mockito.mockStatic(CryptoPath.class)) { + cryptoPathMock.when(() -> CryptoPath.castAndAssertAbsolute(any())).thenThrow(new IllegalArgumentException()); + + Assertions.assertThrows(IllegalArgumentException.class, () -> inTest.getPathToDataCiphertext(cleartext)); + } + } + } + @Nested public class CloseAndIsOpen { From 8167d5d6dc60b36d1f7ee6baefb489c7cbdccf8e Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 4 Jan 2023 13:27:41 +0100 Subject: [PATCH 4/5] Rename method and improve doc --- .../org/cryptomator/cryptofs/CryptoFileSystem.java | 6 ++++-- .../cryptomator/cryptofs/CryptoFileSystemImpl.java | 2 +- .../cryptofs/CryptoFileSystemImplTest.java | 12 ++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java index b0ccd05f..7f82e2d6 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java @@ -35,9 +35,11 @@ public abstract class CryptoFileSystem extends FileSystem { * * @param cleartextPath absolute path to the cleartext file or folder belonging to this {@link CryptoFileSystem}. Internally the path must be an instance of {@link CryptoPath} * @return the {@link Path} to ciphertext file or folder containing teh actual encrypted data - * @throws IOException + * @throws java.nio.file.ProviderMismatchException if the cleartext path does not belong to this CryptoFileSystem + * @throws java.nio.file.NoSuchFileException if for the cleartext path no ciphertext resource exists + * @throws IOException if an I/O error occurs looking for the ciphertext resource */ - public abstract Path getPathToDataCiphertext(Path cleartextPath) throws IOException; + public abstract Path getCiphertextPath(Path cleartextPath) throws IOException; /** * Provides file system performance statistics. diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java index 50852ec1..35cb3359 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java @@ -137,7 +137,7 @@ public Path getPathToVault() { } @Override - public Path getPathToDataCiphertext(Path cleartextPath) throws IOException { + public Path getCiphertextPath(Path cleartextPath) throws IOException { var p = CryptoPath.castAndAssertAbsolute(cleartextPath); var nodeType = cryptoPathMapper.getCiphertextFileType(p); var cipherFile = cryptoPathMapper.getCiphertextFilePath(p); diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java index 25bc169b..9a8cc039 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java @@ -202,7 +202,7 @@ public void testCleartextDirectory() throws IOException { when(cryptoPathMapper.getCiphertextFileType(any())).thenReturn(CiphertextFileType.DIRECTORY); when(cryptoPathMapper.getCiphertextDir(any())).thenReturn(new CiphertextDirectory("foo", ciphertext)); - Path result = inTest.getPathToDataCiphertext(cleartext); + Path result = inTest.getCiphertextPath(cleartext); Assertions.assertEquals(ciphertext, result); } } @@ -219,7 +219,7 @@ public void testCleartextFile() throws IOException { when(cryptoPathMapper.getCiphertextFilePath(any())).thenReturn(p); when(p.getFilePath()).thenReturn(ciphertext); - Path result = inTest.getPathToDataCiphertext(cleartext); + Path result = inTest.getCiphertextPath(cleartext); Assertions.assertEquals(ciphertext, result); } } @@ -236,7 +236,7 @@ public void testCleartextSymlink() throws IOException { when(cryptoPathMapper.getCiphertextFilePath(any())).thenReturn(p); when(p.getSymlinkFilePath()).thenReturn(ciphertext); - Path result = inTest.getPathToDataCiphertext(cleartext); + Path result = inTest.getCiphertextPath(cleartext); Assertions.assertEquals(ciphertext, result); } } @@ -245,7 +245,7 @@ public void testCleartextSymlink() throws IOException { @DisplayName("Path not pointing into the vault throws exception") public void testForeignPathThrows() throws IOException { Path cleartext = Mockito.mock(Path.class, "/some.file"); - Assertions.assertThrows(ProviderMismatchException.class, () -> inTest.getPathToDataCiphertext(cleartext)); + Assertions.assertThrows(ProviderMismatchException.class, () -> inTest.getCiphertextPath(cleartext)); } @Test @@ -256,7 +256,7 @@ public void testNoSuchFile() throws IOException { cryptoPathMock.when(() -> CryptoPath.castAndAssertAbsolute(any())).thenReturn(cleartext); when(cryptoPathMapper.getCiphertextFileType(any())).thenThrow(new NoSuchFileException("no such file")); - Assertions.assertThrows(NoSuchFileException.class, () -> inTest.getPathToDataCiphertext(cleartext)); + Assertions.assertThrows(NoSuchFileException.class, () -> inTest.getCiphertextPath(cleartext)); } } @@ -267,7 +267,7 @@ public void testRelativePathException() throws IOException { try (var cryptoPathMock = Mockito.mockStatic(CryptoPath.class)) { cryptoPathMock.when(() -> CryptoPath.castAndAssertAbsolute(any())).thenThrow(new IllegalArgumentException()); - Assertions.assertThrows(IllegalArgumentException.class, () -> inTest.getPathToDataCiphertext(cleartext)); + Assertions.assertThrows(IllegalArgumentException.class, () -> inTest.getCiphertextPath(cleartext)); } } } From 665ef9fc731317b2a477fb6aff6411852c71f48c Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 4 Jan 2023 13:37:18 +0100 Subject: [PATCH 5/5] preparing 2.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c702f767..2fa7fcdd 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.cryptomator cryptofs - 2.6.0-SNAPSHOT + 2.6.0 Cryptomator Crypto Filesystem This library provides the Java filesystem provider used by Cryptomator. https://github.com/cryptomator/cryptofs