From 1810bdfcc81a44e51367b223df1a5a34cf10dafc Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 17 Nov 2016 18:55:27 +0100 Subject: [PATCH 1/8] Fixed issue with dos file attributes * DosFileAttributes can not be set and get by name --- .../CryptoFileAttributeByNameProvider.java | 28 ++++++----- ...ileSystemFileAttributeIntegrationTest.java | 2 +- ...yptoFileSystemProviderIntegrationTest.java | 21 ++++++++ .../cryptofs/DeletingFileVisitor.java | 49 ++++++++++++++++++- 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java index 5a394ef8..88b6cb56 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java @@ -16,10 +16,14 @@ import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.DosFileAttributeView; import java.nio.file.attribute.DosFileAttributes; import java.nio.file.attribute.FileAttributeView; import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.GroupPrincipal; +import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.UserPrincipal; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -41,17 +45,17 @@ class CryptoFileAttributeByNameProvider { attribute("basic:lastModifiedTime", BasicFileAttributes.class, BasicFileAttributes::lastModifiedTime); attribute("basic:lastAccessTime", BasicFileAttributes.class, BasicFileAttributes::lastAccessTime); attribute("basic:creationTime", BasicFileAttributes.class, BasicFileAttributes::creationTime); - attribute("basic:isRegularFile", BasicFileAttributes.class, BasicFileAttributes::isRegularFile); + attribute("basic:regularFile", BasicFileAttributes.class, BasicFileAttributes::isRegularFile); attribute("basic:isDirectory", BasicFileAttributes.class, BasicFileAttributes::isDirectory); attribute("basic:isSymbolicLink", BasicFileAttributes.class, BasicFileAttributes::isSymbolicLink); attribute("basic:isOther", BasicFileAttributes.class, BasicFileAttributes::isOther); attribute("basic:size", BasicFileAttributes.class, BasicFileAttributes::size); attribute("basic:fileKey", BasicFileAttributes.class, BasicFileAttributes::fileKey); - attribute("dos:isReadOnly", DosFileAttributes.class, DosFileAttributes::isReadOnly); - attribute("dos:isHidden", DosFileAttributes.class, DosFileAttributes::isHidden); - attribute("dos:isArchive", DosFileAttributes.class, DosFileAttributes::isArchive); - attribute("dos:isSystem", DosFileAttributes.class, DosFileAttributes::isSystem); + attribute("dos:readOnly", DosFileAttributes.class, DosFileAttributes::isReadOnly); + attribute("dos:hidden", DosFileAttributes.class, DosFileAttributes::isHidden); + attribute("dos:archive", DosFileAttributes.class, DosFileAttributes::isArchive); + attribute("dos:system", DosFileAttributes.class, DosFileAttributes::isSystem); attribute("posix:owner", PosixFileAttributes.class, PosixFileAttributes::owner); attribute("posix:group", PosixFileAttributes.class, PosixFileAttributes::group); @@ -64,14 +68,14 @@ class CryptoFileAttributeByNameProvider { attribute("basic:lastAccessTime", BasicFileAttributeView.class, FileTime.class, (view, lastAccessTime) -> view.setTimes(null, lastAccessTime, null)); attribute("basic:creationTime", BasicFileAttributeView.class, FileTime.class, (view, creationTime) -> view.setTimes(null, null, creationTime)); - attribute("dos:isReadOnly", DosFileAttributes.class, DosFileAttributes::isReadOnly); - attribute("dos:isHidden", DosFileAttributes.class, DosFileAttributes::isHidden); - attribute("dos:isArchive", DosFileAttributes.class, DosFileAttributes::isArchive); - attribute("dos:isSystem", DosFileAttributes.class, DosFileAttributes::isSystem); + attribute("dos:readOnly", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setReadOnly); + attribute("dos:hidden", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setHidden); + attribute("dos:archive", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setArchive); + attribute("dos:system", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setSystem); - attribute("posix:owner", PosixFileAttributes.class, PosixFileAttributes::owner); - attribute("posix:group", PosixFileAttributes.class, PosixFileAttributes::group); - attribute("posix:permissions", PosixFileAttributes.class, PosixFileAttributes::permissions); + attribute("posix:owner", PosixFileAttributeView.class, UserPrincipal.class, PosixFileAttributeView::setOwner); + attribute("posix:group", PosixFileAttributeView.class, GroupPrincipal.class, PosixFileAttributeView::setGroup); + attribute("posix:permissions", PosixFileAttributeView.class, Set.class, PosixFileAttributeView::setPermissions); } private void attribute(String name, Class type, Function getter) { diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java index a0db1a73..811a789e 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java @@ -89,7 +89,7 @@ public void testReadDirectoryAttributesByName() throws IOException { assertThat((FileTime) result.get("lastModifiedTime"), is(greaterThan(FileTime.fromMillis(currentTimeMillis() - 10000)))); assertThat((FileTime) result.get("lastModifiedTime"), is(lessThan(FileTime.fromMillis(currentTimeMillis() + 10000)))); - assertThat((Boolean) result.get("isDirectory"), is(TRUE)); + assertThat((Boolean) result.get("directory"), is(TRUE)); } @Test diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java index 38e7f7ad..d880cbc1 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderIntegrationTest.java @@ -10,6 +10,7 @@ import static java.nio.file.Files.readAllBytes; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS; import static org.cryptomator.cryptofs.CryptoFileSystemProperties.cryptoFileSystemProperties; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @@ -26,6 +27,7 @@ import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -129,4 +131,23 @@ public void testMoveFileFromOneCryptoFileSystemToAnother() throws IOException { assertThat(readAllBytes(file2), is(data)); } + @Test + public void testDosFileAttributes() throws IOException { + Assume.assumeTrue(IS_OS_WINDOWS); + + FileSystem fs = CryptoFileSystemProvider.newFileSystem(tmpPath, cryptoFileSystemProperties().withPassphrase("asd").build()); + Path file = fs.getPath("/test"); + Files.write(file, new byte[1]); + + Files.setAttribute(file, "dos:hidden", true); + Files.setAttribute(file, "dos:system", true); + Files.setAttribute(file, "dos:archive", true); + Files.setAttribute(file, "dos:readOnly", true); + + assertThat(Files.getAttribute(file, "dos:hidden"), is(true)); + assertThat(Files.getAttribute(file, "dos:system"), is(true)); + assertThat(Files.getAttribute(file, "dos:archive"), is(true)); + assertThat(Files.getAttribute(file, "dos:readOnly"), is(true)); + } + } diff --git a/src/test/java/org/cryptomator/cryptofs/DeletingFileVisitor.java b/src/test/java/org/cryptomator/cryptofs/DeletingFileVisitor.java index fb3cee48..13b780fc 100644 --- a/src/test/java/org/cryptomator/cryptofs/DeletingFileVisitor.java +++ b/src/test/java/org/cryptomator/cryptofs/DeletingFileVisitor.java @@ -8,25 +8,70 @@ *******************************************************************************/ package org.cryptomator.cryptofs; +import static java.nio.file.attribute.PosixFilePermission.GROUP_READ; +import static java.nio.file.attribute.PosixFilePermission.GROUP_WRITE; +import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ; +import static java.nio.file.attribute.PosixFilePermission.OTHERS_WRITE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static java.util.Arrays.asList; + import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.DosFileAttributeView; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.util.HashSet; +import java.util.Set; class DeletingFileVisitor extends SimpleFileVisitor { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.deleteIfExists(file); + forceDelete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.deleteIfExists(dir); + forceDelete(dir); return FileVisitResult.CONTINUE; } + private void forceDelete(Path path) throws IOException { + setWritableSilently(path); + Files.deleteIfExists(path); + } + + private void setWritableSilently(Path path) { + try { + setWritable(path); + } catch (IOException e) { + // ignore + } + } + + private void setWritable(Path path) throws IOException { + DosFileAttributeView dosAttributes = Files.getFileAttributeView(path, DosFileAttributeView.class); + PosixFileAttributeView posixAttributes = Files.getFileAttributeView(path, PosixFileAttributeView.class); + if (dosAttributes != null) { + dosAttributes.setReadOnly(false); + dosAttributes.setSystem(false); + } + if (posixAttributes != null) { + posixAttributes.setPermissions(readWritePermissions()); + } + } + + private Set readWritePermissions() { + return new HashSet<>(asList( // + OWNER_READ, OWNER_WRITE, // + GROUP_READ, GROUP_WRITE, // + OTHERS_READ, OTHERS_WRITE)); + } + } From be4a6ebfb42e5dd284ef0d595d0d724c2a9ad1af Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 17 Nov 2016 18:59:36 +0100 Subject: [PATCH 2/8] Fixed file attribute basic:isRegularFile --- .../cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java index 88b6cb56..cada47f4 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileAttributeByNameProvider.java @@ -45,7 +45,7 @@ class CryptoFileAttributeByNameProvider { attribute("basic:lastModifiedTime", BasicFileAttributes.class, BasicFileAttributes::lastModifiedTime); attribute("basic:lastAccessTime", BasicFileAttributes.class, BasicFileAttributes::lastAccessTime); attribute("basic:creationTime", BasicFileAttributes.class, BasicFileAttributes::creationTime); - attribute("basic:regularFile", BasicFileAttributes.class, BasicFileAttributes::isRegularFile); + attribute("basic:isRegularFile", BasicFileAttributes.class, BasicFileAttributes::isRegularFile); attribute("basic:isDirectory", BasicFileAttributes.class, BasicFileAttributes::isDirectory); attribute("basic:isSymbolicLink", BasicFileAttributes.class, BasicFileAttributes::isSymbolicLink); attribute("basic:isOther", BasicFileAttributes.class, BasicFileAttributes::isOther); From a96d2ed26d8f9cfa3f06562c66073c3547d9e7ca Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 17 Nov 2016 19:05:49 +0100 Subject: [PATCH 3/8] Fixed broken test --- .../cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java index 811a789e..a0db1a73 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemFileAttributeIntegrationTest.java @@ -89,7 +89,7 @@ public void testReadDirectoryAttributesByName() throws IOException { assertThat((FileTime) result.get("lastModifiedTime"), is(greaterThan(FileTime.fromMillis(currentTimeMillis() - 10000)))); assertThat((FileTime) result.get("lastModifiedTime"), is(lessThan(FileTime.fromMillis(currentTimeMillis() + 10000)))); - assertThat((Boolean) result.get("directory"), is(TRUE)); + assertThat((Boolean) result.get("isDirectory"), is(TRUE)); } @Test From c9f7e77a0b464feae4fcc5df6362952137890334 Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 17 Nov 2016 19:57:01 +0100 Subject: [PATCH 4/8] Specific exception when operating on relative path File operations which require an absolute path now throw an IllegalArgumentException with specific message instead of a generic NPE when invoked with a relative path. --- .../cryptofs/CryptoFileSystemProvider.java | 24 +++--- .../org/cryptomator/cryptofs/CryptoPath.java | 8 ++ .../CryptoFileSystemProviderTest.java | 78 +++++++++++++------ 3 files changed, 76 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java index 97637822..f3ca712c 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java @@ -156,7 +156,7 @@ public AsynchronousFileChannel newAsynchronousFileChannel(Path cleartextPath, Se @Override public FileChannel newFileChannel(Path cleartextPath, Set optionsSet, FileAttribute... attrs) throws IOException { - return fileSystem(cleartextPath).newFileChannel(CryptoPath.cast(cleartextPath), optionsSet, attrs); + return fileSystem(cleartextPath).newFileChannel(CryptoPath.castAndAssertAbsolute(cleartextPath), optionsSet, attrs); } @Override @@ -166,27 +166,27 @@ public SeekableByteChannel newByteChannel(Path cleartextPath, Set newDirectoryStream(Path cleartextDir, Filter filter) throws IOException { - return fileSystem(cleartextDir).newDirectoryStream(CryptoPath.cast(cleartextDir), filter); + return fileSystem(cleartextDir).newDirectoryStream(CryptoPath.castAndAssertAbsolute(cleartextDir), filter); } @Override public void createDirectory(Path cleartextDir, FileAttribute... attrs) throws IOException { - fileSystem(cleartextDir).createDirectory(CryptoPath.cast(cleartextDir), attrs); + fileSystem(cleartextDir).createDirectory(CryptoPath.castAndAssertAbsolute(cleartextDir), attrs); } @Override public void delete(Path cleartextPath) throws IOException { - fileSystem(cleartextPath).delete(CryptoPath.cast(cleartextPath)); + fileSystem(cleartextPath).delete(CryptoPath.castAndAssertAbsolute(cleartextPath)); } @Override public void copy(Path cleartextSource, Path cleartextTarget, CopyOption... options) throws IOException { - copyAndMoveOperations.copy(CryptoPath.cast(cleartextSource), CryptoPath.cast(cleartextTarget), options); + copyAndMoveOperations.copy(CryptoPath.castAndAssertAbsolute(cleartextSource), CryptoPath.castAndAssertAbsolute(cleartextTarget), options); } @Override public void move(Path cleartextSource, Path cleartextTarget, CopyOption... options) throws IOException { - copyAndMoveOperations.move(CryptoPath.cast(cleartextSource), CryptoPath.cast(cleartextTarget), options); + copyAndMoveOperations.move(CryptoPath.castAndAssertAbsolute(cleartextSource), CryptoPath.castAndAssertAbsolute(cleartextTarget), options); } @Override @@ -197,7 +197,7 @@ public boolean isSameFile(Path cleartextPath, Path cleartextPath2) throws IOExce @Override public boolean isHidden(Path cleartextPath) throws IOException { - return fileSystem(cleartextPath).isHidden(CryptoPath.cast(cleartextPath)); + return fileSystem(cleartextPath).isHidden(CryptoPath.castAndAssertAbsolute(cleartextPath)); } @Override @@ -207,27 +207,27 @@ public FileStore getFileStore(Path cleartextPath) throws IOException { @Override public void checkAccess(Path cleartextPath, AccessMode... modes) throws IOException { - fileSystem(cleartextPath).checkAccess(CryptoPath.cast(cleartextPath), modes); + fileSystem(cleartextPath).checkAccess(CryptoPath.castAndAssertAbsolute(cleartextPath), modes); } @Override public V getFileAttributeView(Path cleartextPath, Class type, LinkOption... options) { - return fileSystem(cleartextPath).getFileAttributeView(CryptoPath.cast(cleartextPath), type, options); + return fileSystem(cleartextPath).getFileAttributeView(CryptoPath.castAndAssertAbsolute(cleartextPath), type, options); } @Override public A readAttributes(Path cleartextPath, Class type, LinkOption... options) throws IOException { - return fileSystem(cleartextPath).readAttributes(CryptoPath.cast(cleartextPath), type, options); + return fileSystem(cleartextPath).readAttributes(CryptoPath.castAndAssertAbsolute(cleartextPath), type, options); } @Override public Map readAttributes(Path cleartextPath, String attributes, LinkOption... options) throws IOException { - return fileSystem(cleartextPath).readAttributes(CryptoPath.cast(cleartextPath), attributes, options); + return fileSystem(cleartextPath).readAttributes(CryptoPath.castAndAssertAbsolute(cleartextPath), attributes, options); } @Override public void setAttribute(Path cleartextPath, String attribute, Object value, LinkOption... options) throws IOException { - fileSystem(cleartextPath).setAttribute(CryptoPath.cast(cleartextPath), attribute, value, options); + fileSystem(cleartextPath).setAttribute(CryptoPath.castAndAssertAbsolute(cleartextPath), attribute, value, options); } private CryptoFileSystem fileSystem(Path path) { diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoPath.java b/src/main/java/org/cryptomator/cryptofs/CryptoPath.java index b524aa8b..1bf3582a 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoPath.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoPath.java @@ -41,6 +41,14 @@ public CryptoPath(CryptoFileSystem fileSystem, List elements, boolean ab this.absolute = absolute; } + public static CryptoPath castAndAssertAbsolute(Path path) { + CryptoPath result = cast(path); + if (!result.isAbsolute()) { + throw new IllegalArgumentException("Path must be absolute but was " + path); + } + return result; + } + public static CryptoPath cast(Path path) { if (path instanceof CryptoPath) { CryptoPath cryptoPath = (CryptoPath) path; diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java index a91b3ea2..4273a2ef 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java @@ -43,6 +43,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.FromDataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.rules.ExpectedException; @@ -63,6 +64,7 @@ public class CryptoFileSystemProviderTest { private CryptoPath cryptoPath = mock(CryptoPath.class); private CryptoPath secondCryptoPath = mock(CryptoPath.class); + private CryptoPath relativeCryptoPath = mock(CryptoPath.class); private CryptoFileSystem cryptoFileSystem = mock(CryptoFileSystem.class); private CopyAndMoveOperations copyAndMoveOperations = mock(CopyAndMoveOperations.class); @@ -72,24 +74,43 @@ public class CryptoFileSystemProviderTest { private CryptoFileSystemProvider inTest; - @DataPoints + @DataPoints("shouldFailWithProviderMismatch") @SuppressWarnings("unchecked") - public static final List INVOCATIONS_FAILING_WITH_PROVIDER_MISMATCH = asList( // - shouldFailWithProviderMismatch("newAsynchronousFileChannel", (inTest, otherPath) -> inTest.newAsynchronousFileChannel(otherPath, new HashSet<>(), mock(ExecutorService.class))), // - shouldFailWithProviderMismatch("newFileChannel", (inTest, otherPath) -> inTest.newFileChannel(otherPath, new HashSet<>())), // - shouldFailWithProviderMismatch("newByteChannel", (inTest, otherPath) -> inTest.newByteChannel(otherPath, new HashSet<>())), // - shouldFailWithProviderMismatch("newDirectoryStream", (inTest, otherPath) -> inTest.newDirectoryStream(otherPath, mock(Filter.class))), // - shouldFailWithProviderMismatch("createDirectory", (inTest, otherPath) -> inTest.createDirectory(otherPath)), // - shouldFailWithProviderMismatch("delete", (inTest, otherPath) -> inTest.delete(otherPath)), // - shouldFailWithProviderMismatch("copy", (inTest, otherPath) -> inTest.copy(otherPath, otherPath)), // - shouldFailWithProviderMismatch("move", (inTest, otherPath) -> inTest.move(otherPath, otherPath)), // - shouldFailWithProviderMismatch("isHidden", (inTest, otherPath) -> inTest.isHidden(otherPath)), // - shouldFailWithProviderMismatch("getFileStore", (inTest, otherPath) -> inTest.getFileStore(otherPath)), // - shouldFailWithProviderMismatch("checkAccess", (inTest, otherPath) -> inTest.checkAccess(otherPath)), // - shouldFailWithProviderMismatch("getFileAttributeView", (inTest, otherPath) -> inTest.getFileAttributeView(otherPath, FileAttributeView.class)), // - shouldFailWithProviderMismatch("readAttributesWithClass", (inTest, otherPath) -> inTest.readAttributes(otherPath, BasicFileAttributes.class)), // - shouldFailWithProviderMismatch("readAttributesWithString", (inTest, otherPath) -> inTest.readAttributes(otherPath, "fooBar")), // - shouldFailWithProviderMismatch("setAttribute", (inTest, otherPath) -> inTest.setAttribute(otherPath, "a", "b")) // + public static final List INVOCATIONS_FAILING_WITH_PROVIDER_MISMATCH = asList( // + invocation("newAsynchronousFileChannel", (inTest, path) -> inTest.newAsynchronousFileChannel(path, new HashSet<>(), mock(ExecutorService.class))), // + invocation("newFileChannel", (inTest, path) -> inTest.newFileChannel(path, new HashSet<>())), // + invocation("newByteChannel", (inTest, path) -> inTest.newByteChannel(path, new HashSet<>())), // + invocation("newDirectoryStream", (inTest, path) -> inTest.newDirectoryStream(path, mock(Filter.class))), // + invocation("createDirectory", (inTest, path) -> inTest.createDirectory(path)), // + invocation("delete", (inTest, path) -> inTest.delete(path)), // + invocation("copy", (inTest, path) -> inTest.copy(path, path)), // + invocation("move", (inTest, path) -> inTest.move(path, path)), // + invocation("isHidden", (inTest, path) -> inTest.isHidden(path)), // + invocation("getFileStore", (inTest, path) -> inTest.getFileStore(path)), // + invocation("checkAccess", (inTest, path) -> inTest.checkAccess(path)), // + invocation("getFileAttributeView", (inTest, path) -> inTest.getFileAttributeView(path, FileAttributeView.class)), // + invocation("readAttributesWithClass", (inTest, path) -> inTest.readAttributes(path, BasicFileAttributes.class)), // + invocation("readAttributesWithString", (inTest, path) -> inTest.readAttributes(path, "fooBar")), // + invocation("setAttribute", (inTest, path) -> inTest.setAttribute(path, "a", "b")) // + ); + + @DataPoints("shouldFailWithRelativePath") + @SuppressWarnings("unchecked") + public static final List INVOCATIONS_FAILING_WITH_RELATIVE_PATH = asList( // + invocation("newAsynchronousFileChannel", (inTest, path) -> inTest.newAsynchronousFileChannel(path, new HashSet<>(), mock(ExecutorService.class))), // + invocation("newFileChannel", (inTest, path) -> inTest.newFileChannel(path, new HashSet<>())), // + invocation("newByteChannel", (inTest, path) -> inTest.newByteChannel(path, new HashSet<>())), // + invocation("newDirectoryStream", (inTest, path) -> inTest.newDirectoryStream(path, mock(Filter.class))), // + invocation("createDirectory", (inTest, path) -> inTest.createDirectory(path)), // + invocation("delete", (inTest, path) -> inTest.delete(path)), // + invocation("copy", (inTest, path) -> inTest.copy(path, path)), // + invocation("move", (inTest, path) -> inTest.move(path, path)), // + invocation("isHidden", (inTest, path) -> inTest.isHidden(path)), // + invocation("checkAccess", (inTest, path) -> inTest.checkAccess(path)), // + invocation("getFileAttributeView", (inTest, path) -> inTest.getFileAttributeView(path, FileAttributeView.class)), // + invocation("readAttributesWithClass", (inTest, path) -> inTest.readAttributes(path, BasicFileAttributes.class)), // + invocation("readAttributesWithString", (inTest, path) -> inTest.readAttributes(path, "fooBar")), // + invocation("setAttribute", (inTest, path) -> inTest.setAttribute(path, "a", "b")) // ); @Before @@ -100,21 +121,34 @@ public void setup() { when(component.copyAndMoveOperations()).thenReturn(copyAndMoveOperations); inTest = new CryptoFileSystemProvider(component); + when(cryptoPath.isAbsolute()).thenReturn(true); when(cryptoPath.getFileSystem()).thenReturn(cryptoFileSystem); + when(secondCryptoPath.isAbsolute()).thenReturn(true); when(secondCryptoPath.getFileSystem()).thenReturn(cryptoFileSystem); + when(relativeCryptoPath.isAbsolute()).thenReturn(false); + when(relativeCryptoPath.getFileSystem()).thenReturn(cryptoFileSystem); when(cryptoFileSystem.provider()).thenReturn(inTest); + when(otherPath.isAbsolute()).thenReturn(true); when(otherPath.getFileSystem()).thenReturn(otherFileSystem); when(otherFileSystem.provider()).thenReturn(otherProvider); } @Theory - public void testInvocationsWithPathFromOtherProviderFailWithProviderMismatchException(InvocationWhichShouldFailWithProviderMismatch shouldFailWithProviderMismatch) throws IOException { + public void testInvocationsWithPathFromOtherProviderFailWithProviderMismatchException(@FromDataPoints("shouldFailWithProviderMismatch") InvocationWhichShouldFail shouldFailWithProviderMismatch) throws IOException { thrown.expect(ProviderMismatchException.class); shouldFailWithProviderMismatch.invoke(inTest, otherPath); } + @Theory + public void testInvocationsWithRelativePathFailWithIllegalArgumentException(@FromDataPoints("shouldFailWithRelativePath") InvocationWhichShouldFail shouldFailWithRelativePath) throws IOException { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Path must be absolute"); + + shouldFailWithRelativePath.invoke(inTest, relativeCryptoPath); + } + @Test @SuppressWarnings("deprecation") public void testFileSystemsIsMock() { @@ -360,16 +394,16 @@ public void testSetAttributeDelegatesToFileSystem() throws IOException { verify(cryptoFileSystem).setAttribute(cryptoPath, attribute, value, option); } - private static InvocationWhichShouldFailWithProviderMismatch shouldFailWithProviderMismatch(String name, Invocation invocation) { - return new InvocationWhichShouldFailWithProviderMismatch(name, invocation); + private static InvocationWhichShouldFail invocation(String name, Invocation invocation) { + return new InvocationWhichShouldFail(name, invocation); } - private static class InvocationWhichShouldFailWithProviderMismatch { + private static class InvocationWhichShouldFail { private final String name; private final Invocation invocation; - public InvocationWhichShouldFailWithProviderMismatch(String name, Invocation invocation) { + public InvocationWhichShouldFail(String name, Invocation invocation) { this.name = name; this.invocation = invocation; } From d87e96a0c17f199c0e6a8a90141f682b32ec6371 Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 17 Nov 2016 22:17:56 +0100 Subject: [PATCH 5/8] Implemented truncate --- .../org/cryptomator/cryptofs/ChunkData.java | 7 ++ .../cryptomator/cryptofs/OpenCryptoFile.java | 16 +++-- .../cryptofs/OpenCryptoFileFactoryModule.java | 13 +++- .../cryptofs/PatternPathMatcher.java | 8 +++ ...toFileChannelWriteReadIntegrationTest.java | 33 ++++++++++ .../cryptofs/PathMatcherFactoryTest.java | 66 +++++++++++++++++++ 6 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 src/test/java/org/cryptomator/cryptofs/PathMatcherFactoryTest.java diff --git a/src/main/java/org/cryptomator/cryptofs/ChunkData.java b/src/main/java/org/cryptomator/cryptofs/ChunkData.java index aec3eb71..273c62f1 100644 --- a/src/main/java/org/cryptomator/cryptofs/ChunkData.java +++ b/src/main/java/org/cryptomator/cryptofs/ChunkData.java @@ -38,6 +38,13 @@ public boolean wasWritten() { return written; } + public void truncate(int length) { + if (this.length > length) { + this.length = length; + this.written = true; + } + } + public CopyWithoutDirection copyData() { return copyDataStartingAt(0); } diff --git a/src/main/java/org/cryptomator/cryptofs/OpenCryptoFile.java b/src/main/java/org/cryptomator/cryptofs/OpenCryptoFile.java index 1fd7413c..c2c705f3 100644 --- a/src/main/java/org/cryptomator/cryptofs/OpenCryptoFile.java +++ b/src/main/java/org/cryptomator/cryptofs/OpenCryptoFile.java @@ -12,6 +12,7 @@ import static java.lang.Math.min; import static org.cryptomator.cryptofs.OpenCounter.OpenState.ALREADY_CLOSED; import static org.cryptomator.cryptofs.OpenCounter.OpenState.WAS_OPEN; +import static org.cryptomator.cryptolib.Cryptors.ciphertextSize; import java.io.IOException; import java.nio.ByteBuffer; @@ -51,8 +52,6 @@ public OpenCryptoFile(EffectiveOpenOptions options, Cryptor cryptor, FileChannel this.header = header; this.size = size; this.stats = stats; - - size.set(header.getFilesize()); } public FileChannel newFileChannel(EffectiveOpenOptions options) throws IOException { @@ -135,13 +134,22 @@ public long size() { } public synchronized void truncate(long size) throws IOException { - // TODO + long originalSize = this.size.getAndUpdate(current -> min(size, current)); + if (originalSize > size) { + int cleartextChunkSize = cryptor.fileContentCryptor().cleartextChunkSize(); + long indexOfLastChunk = (size + cleartextChunkSize - 1) / cleartextChunkSize - 1; + int sizeOfIncompleteChunk = (int) (size % cleartextChunkSize); + if (sizeOfIncompleteChunk > 0) { + chunkCache.get(indexOfLastChunk).truncate(sizeOfIncompleteChunk); + } + long ciphertextFileSize = cryptor.fileHeaderCryptor().headerSize() + ciphertextSize(size, cryptor); + channel.truncate(ciphertextFileSize); + } } public synchronized void force(boolean metaData, EffectiveOpenOptions options) throws IOException { chunkCache.invalidateAll(); // TODO increase performance by writing chunks but keeping them cached if (options.writable()) { - header.setFilesize(size.get()); channel.write(cryptor.fileHeaderCryptor().encryptHeader(header), 0); } channel.force(metaData); diff --git a/src/main/java/org/cryptomator/cryptofs/OpenCryptoFileFactoryModule.java b/src/main/java/org/cryptomator/cryptofs/OpenCryptoFileFactoryModule.java index f47d2af8..4cf13fc1 100644 --- a/src/main/java/org/cryptomator/cryptofs/OpenCryptoFileFactoryModule.java +++ b/src/main/java/org/cryptomator/cryptofs/OpenCryptoFileFactoryModule.java @@ -1,6 +1,7 @@ package org.cryptomator.cryptofs; import static org.cryptomator.cryptofs.UncheckedThrows.rethrowUnchecked; +import static org.cryptomator.cryptolib.Cryptors.cleartextSize; import java.io.IOException; import java.nio.ByteBuffer; @@ -26,8 +27,16 @@ public FileChannel provideFileChannel(@OpenFilePath Path path, EffectiveOpenOpti @Provides @PerOpenFile @OpenFileSize - public AtomicLong provideFileSize() { - return new AtomicLong(); + public AtomicLong provideFileSize(FileChannel channel, Cryptor cryptor) { + return rethrowUnchecked(IOException.class).from(() -> { + long size = channel.size(); + if (size == 0) { + return new AtomicLong(); + } else { + int headerSize = cryptor.fileHeaderCryptor().headerSize(); + return new AtomicLong(cleartextSize(size - headerSize, cryptor)); + } + }); } @Provides diff --git a/src/main/java/org/cryptomator/cryptofs/PatternPathMatcher.java b/src/main/java/org/cryptomator/cryptofs/PatternPathMatcher.java index e1cb3f60..41369b98 100644 --- a/src/main/java/org/cryptomator/cryptofs/PatternPathMatcher.java +++ b/src/main/java/org/cryptomator/cryptofs/PatternPathMatcher.java @@ -12,6 +12,14 @@ public PatternPathMatcher(Pattern pattern) { this.pattern = pattern; } + /** + * @deprecated for testing + */ + @Deprecated + Pattern getPattern() { + return pattern; + } + @Override public boolean matches(Path path) { return pattern.matcher(path.toString()).matches(); diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java index 042f6994..0d5b58d4 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java @@ -8,6 +8,7 @@ *******************************************************************************/ package org.cryptomator.cryptofs; +import static java.lang.Math.min; import static java.lang.String.format; import static java.nio.file.StandardOpenOption.APPEND; import static java.nio.file.StandardOpenOption.CREATE; @@ -88,6 +89,38 @@ public void testWriteAndReadNothing() throws IOException { } } + @Theory + public void testWriteDataAndTruncateToOffset(@FromDataPoints("dataSizes") int cleartextSize, @FromDataPoints("writeOffsets") int truncateToSize) throws IOException { + long fileId = nextFileId(); + + int targetSize = min(truncateToSize, cleartextSize); + + try (FileChannel channel = writableChannel(fileId)) { + assertEquals(0, channel.size()); + channel.write(repeat(1).times(cleartextSize).asByteBuffer()); + assertEquals(cleartextSize, channel.size()); + } + + try (FileChannel channel = writableChannel(fileId)) { + assertEquals(cleartextSize, channel.size()); + channel.truncate(truncateToSize); + assertEquals(targetSize, channel.size()); + } + + try (FileChannel channel = readableChannel(fileId)) { + if (targetSize > 0) { + ByteBuffer buffer = ByteBuffer.allocate(targetSize); + int result = channel.read(buffer); + assertEquals(targetSize, result); + buffer.flip(); + for (int i = 0; i < targetSize; i++) { + assertEquals(format("byte(%d) = 1", i), 1, buffer.get(i)); + } + } + assertEquals(EOF, channel.read(ByteBuffer.allocate(0))); + } + } + @Theory public void testWithWritingOffset(@FromDataPoints("dataSizes") int dataSize, @FromDataPoints("writeOffsets") int writeOffset) throws IOException { assumeTrue(dataSize != 0 || writeOffset != 0); diff --git a/src/test/java/org/cryptomator/cryptofs/PathMatcherFactoryTest.java b/src/test/java/org/cryptomator/cryptofs/PathMatcherFactoryTest.java new file mode 100644 index 00000000..6d210c09 --- /dev/null +++ b/src/test/java/org/cryptomator/cryptofs/PathMatcherFactoryTest.java @@ -0,0 +1,66 @@ +package org.cryptomator.cryptofs; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.nio.file.PathMatcher; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class PathMatcherFactoryTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private GlobToRegexConverter globToRegexConverter = mock(GlobToRegexConverter.class); + + private PathMatcherFactory inTest = new PathMatcherFactory(globToRegexConverter); + + @Test + public void testSyntaxAndPatternNotStartingWithGlobOrRegexThrowsUnsupportedOperationException() { + thrown.expect(UnsupportedOperationException.class); + + inTest.pathMatcherFrom("fail"); + } + + @Test + @SuppressWarnings("deprecation") + public void testSyntaxAndPatternStartingWithRegexCreatesPatternPathMatcherWithCorrectPattern() { + PathMatcher pathMatcher = inTest.pathMatcherFrom("regex:test[02]"); + + assertThat(pathMatcher, is(instanceOf(PatternPathMatcher.class))); + assertThat(((PatternPathMatcher) pathMatcher).getPattern().pattern(), is("test[02]")); + } + + @Test + @SuppressWarnings("deprecation") + public void testSyntaxAndPatternStartingWithGlobCreatesPatternPathMatcherWithCorrectPattern() { + String regexp = "test[abcd]*"; + when(globToRegexConverter.convert("abcd")).thenReturn(regexp); + + PathMatcher pathMatcher = inTest.pathMatcherFrom("glob:abcd"); + + assertThat(pathMatcher, is(instanceOf(PatternPathMatcher.class))); + assertThat(((PatternPathMatcher) pathMatcher).getPattern().pattern(), is(regexp)); + } + + @Test + public void testSyntaxAndPatternIgnoresCase() { + when(globToRegexConverter.convert(anyString())).thenReturn("a"); + + inTest.pathMatcherFrom("reGEx:a"); + inTest.pathMatcherFrom("gLOb:a"); + } + +} From bdd9633b7c2ae8593c5f0b255d1bc5fa314f8d7c Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 17 Nov 2016 22:42:37 +0100 Subject: [PATCH 6/8] Corrected shortening threshold --- src/main/java/org/cryptomator/cryptofs/Constants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/cryptofs/Constants.java b/src/main/java/org/cryptomator/cryptofs/Constants.java index 1a64d3c9..6b54cfa9 100644 --- a/src/main/java/org/cryptomator/cryptofs/Constants.java +++ b/src/main/java/org/cryptomator/cryptofs/Constants.java @@ -15,7 +15,7 @@ final class Constants { public static final String DATA_DIR_NAME = "d"; public static final String METADATA_DIR_NAME = "m"; public static final String DIR_PREFIX = "0"; - public static final int NAME_SHORTENING_THRESHOLD = 80; // TODO markuskreusch: set correct value + public static final int NAME_SHORTENING_THRESHOLD = 129; public static final int VAULT_VERSION = 4; public static final String SEPARATOR = "/"; From e6a264480853477c7702cdf6733c57e3cbaf601d Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 19 Nov 2016 21:09:44 +0100 Subject: [PATCH 7/8] Fixed directory moving (severe dir id caching issues), corrected vault version number --- .../org/cryptomator/cryptofs/Constants.java | 2 +- .../cryptofs/CryptoFileSystem.java | 7 +++--- .../cryptofs/DirectoryIdProvider.java | 23 ++++++++++++++++++- .../cryptofs/CryptoFileSystemTest.java | 13 +++++------ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/Constants.java b/src/main/java/org/cryptomator/cryptofs/Constants.java index 6b54cfa9..4926c480 100644 --- a/src/main/java/org/cryptomator/cryptofs/Constants.java +++ b/src/main/java/org/cryptomator/cryptofs/Constants.java @@ -16,7 +16,7 @@ final class Constants { public static final String METADATA_DIR_NAME = "m"; public static final String DIR_PREFIX = "0"; public static final int NAME_SHORTENING_THRESHOLD = 129; - public static final int VAULT_VERSION = 4; + public static final int VAULT_VERSION = 5; public static final String SEPARATOR = "/"; diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java index ab0aa141..959d5592 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java @@ -322,7 +322,7 @@ void createDirectory(CryptoPath cleartextDir, FileAttribute... attrs) throws } finally { if (!success) { Files.delete(ciphertextDirFile); - dirIdProvider.invalidate(ciphertextDirFile); + dirIdProvider.delete(ciphertextDirFile); } } } @@ -350,7 +350,7 @@ void delete(CryptoPath cleartextPath) throws IOException { // should not happen. Nevertheless this is a valid state, so who no big deal... LOG.warn("Successfully deleted dir {}, but didn't find corresponding dir file {}", ciphertextDir, ciphertextDirFile); } - dirIdProvider.invalidate(ciphertextDirFile); + dirIdProvider.delete(ciphertextDirFile); } catch (NoSuchFileException e) { // translate ciphertext path to cleartext path throw new NoSuchFileException(cleartextPath.toString()); @@ -458,11 +458,10 @@ void move(CryptoPath cleartextSource, CryptoPath cleartextTarget, CopyOption... } } Files.delete(ciphertextTargetDir); - dirIdProvider.invalidate(ciphertextTargetDirFile); } Files.move(ciphertextSourceDirFile, ciphertextTargetDirFile, options); } - dirIdProvider.invalidate(ciphertextSourceDirFile); + dirIdProvider.move(ciphertextSourceDirFile, ciphertextTargetDirFile); } else { throw new NoSuchFileException(cleartextSource.toString()); } diff --git a/src/main/java/org/cryptomator/cryptofs/DirectoryIdProvider.java b/src/main/java/org/cryptomator/cryptofs/DirectoryIdProvider.java index e57020f3..510240c6 100644 --- a/src/main/java/org/cryptomator/cryptofs/DirectoryIdProvider.java +++ b/src/main/java/org/cryptomator/cryptofs/DirectoryIdProvider.java @@ -54,8 +54,29 @@ public String load(Path dirFilePath) throws IOException { } } - public void invalidate(Path dirFilePath) { + /** + * Removes the id currently associated with dirFilePath from cache. Useful during folder delete operations. + * This method has no effect if the content of the given dirFile is not currently cached. + * + * @param dirFilePath The dirFile for which the cache should be deleted. + */ + public void delete(Path dirFilePath) { ids.invalidate(dirFilePath); } + /** + * Transfers ownership from the id currently associated with srcDirFilePath to dstDirFilePath. Usefule during folder move operations. + * This method has no effect if the content of the source dirFile is not currently cached. + * + * @param srcDirFilePath The dirFile that contained the cached id until now. + * @param dstDirFilePath The dirFile that will contain the id from now on. + */ + public void move(Path srcDirFilePath, Path dstDirFilePath) { + String id = ids.getIfPresent(srcDirFilePath); + if (id != null) { + ids.put(dstDirFilePath, id); + ids.invalidate(srcDirFilePath); + } + } + } diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemTest.java index 70e60cd7..9c3067e5 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemTest.java @@ -241,7 +241,7 @@ public void testDeleteExistingDirectory() throws IOException { inTest.delete(cleartextPath); verify(physicalFsProv).delete(ciphertextDirPath); verify(physicalFsProv).deleteIfExists(ciphertextDirFilePath); - verify(dirIdProvider).invalidate(ciphertextDirFilePath); + verify(dirIdProvider).delete(ciphertextDirFilePath); } @Test @@ -332,7 +332,7 @@ public void moveDirectoryDontReplaceExisting() throws IOException { CopyOption option2 = mock(CopyOption.class); inTest.move(cleartextSource, cleartextTarget, option1, option2); verify(physicalFsProv).move(ciphertextSourceDirFile, ciphertextTargetDirFile, option1, option2); - verify(dirIdProvider).invalidate(ciphertextSourceDirFile); + verify(dirIdProvider).move(ciphertextSourceDirFile, ciphertextTargetDirFile); } @Test @@ -348,8 +348,7 @@ public void moveDirectoryReplaceExisting() throws IOException { inTest.move(cleartextSource, cleartextTarget, StandardCopyOption.REPLACE_EXISTING); verify(physicalFsProv).delete(ciphertextTargetDir); verify(physicalFsProv).move(ciphertextSourceDirFile, ciphertextTargetDirFile, StandardCopyOption.REPLACE_EXISTING); - verify(dirIdProvider).invalidate(ciphertextSourceDirFile); - verify(dirIdProvider).invalidate(ciphertextTargetDirFile); + verify(dirIdProvider).move(ciphertextSourceDirFile, ciphertextTargetDirFile); } @Test @@ -444,7 +443,7 @@ public void copyDirectory() throws IOException { inTest.copy(cleartextSource, cleartextTarget); verify(ciphertextTargetDirFileChannel).write(any(ByteBuffer.class)); verify(physicalFsProv).createDirectory(ciphertextTargetDir); - verify(dirIdProvider, Mockito.never()).invalidate(Mockito.any()); + verify(dirIdProvider, Mockito.never()).delete(Mockito.any()); } @Test @@ -461,7 +460,7 @@ public void copyDirectoryReplaceExisting() throws IOException { inTest.copy(cleartextSource, cleartextTarget, StandardCopyOption.REPLACE_EXISTING); verify(ciphertextTargetDirFileChannel, Mockito.never()).write(any(ByteBuffer.class)); verify(physicalFsProv, Mockito.never()).createDirectory(Mockito.any()); - verify(dirIdProvider, Mockito.never()).invalidate(Mockito.any()); + verify(dirIdProvider, Mockito.never()).delete(Mockito.any()); } @Test @@ -562,7 +561,7 @@ public void moveDirectoryReplaceExistingNonEmpty() throws IOException { } finally { verify(ciphertextTargetDirFileChannel, Mockito.never()).write(any(ByteBuffer.class)); verify(physicalFsProv, Mockito.never()).createDirectory(Mockito.any()); - verify(dirIdProvider, Mockito.never()).invalidate(Mockito.any()); + verify(dirIdProvider, Mockito.never()).delete(Mockito.any()); } } From 940e5b8b527e6b27691e33ab1fe4b28744163455 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 19 Nov 2016 21:14:10 +0100 Subject: [PATCH 8/8] updated version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6bb53a49..92e91b9a 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.cryptomator cryptofs - 0.2.0-SNAPSHOT + 0.1.6 Cryptomator Crypto Filesystem This library provides the Java filesystem provider used by Cryptomator. https://github.com/cryptomator/cryptofs