From a9610bfc6b000aa684214e98ca726b7c9b76396e Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 5 Feb 2020 15:13:20 +0100 Subject: [PATCH 1/6] fixed test where illegal character in file name was used --- .../java/org/cryptomator/cryptofs/dir/C9rDecryptorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/cryptomator/cryptofs/dir/C9rDecryptorTest.java b/src/test/java/org/cryptomator/cryptofs/dir/C9rDecryptorTest.java index 1d214212..3c1e0ba6 100644 --- a/src/test/java/org/cryptomator/cryptofs/dir/C9rDecryptorTest.java +++ b/src/test/java/org/cryptomator/cryptofs/dir/C9rDecryptorTest.java @@ -104,7 +104,7 @@ public void testProcessPartialMatch(String filename) { @ValueSource(strings = { "foo.bar", "foo.c9r", - "aaaaBBBB????DDDDeeeeFFFF.c9r", + "aaaaBBBB$$$$DDDDeeeeFFFF.c9r", "aaaaBBBBxxxxDDDDeeeeFFFF.c9r", }) public void testProcessNoMatch(String filename) { From b6690573e07de9e6c31e22faab132f1e25a27949 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Wed, 5 Feb 2020 15:16:00 +0100 Subject: [PATCH 2/6] fixes #69 --- .../cryptofs/ch/CleartextFileChannel.java | 16 +++++-- ...toFileChannelWriteReadIntegrationTest.java | 42 +++++++++++++++++-- .../cryptofs/ch/CleartextFileChannelTest.java | 26 ++++++++++-- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/ch/CleartextFileChannel.java b/src/main/java/org/cryptomator/cryptofs/ch/CleartextFileChannel.java index ccf205c6..75abe860 100644 --- a/src/main/java/org/cryptomator/cryptofs/ch/CleartextFileChannel.java +++ b/src/main/java/org/cryptomator/cryptofs/ch/CleartextFileChannel.java @@ -211,9 +211,7 @@ private void forceInternal(boolean metaData) throws IOException { flush(); ciphertextFileChannel.force(metaData); if (metaData) { - FileTime lastModifiedTime = isWritable() ? FileTime.from(lastModified.get()) : null; - FileTime lastAccessTime = FileTime.from(Instant.now()); - attrViewProvider.get().setTimes(lastModifiedTime, lastAccessTime, null); + persistLastModified(); } } @@ -229,6 +227,17 @@ private void flush() throws IOException { } } + /** + * Corrects the last modified and access date due to possible cache invalidation (i.e. write operation!) + * + * @throws IOException + */ + private void persistLastModified() throws IOException { + FileTime lastModifiedTime = isWritable() ? FileTime.from(lastModified.get()) : null; + FileTime lastAccessTime = FileTime.from(Instant.now()); + attrViewProvider.get().setTimes(lastModifiedTime, lastAccessTime, null); + } + @Override public MappedByteBuffer map(MapMode mode, long position, long size) { throw new UnsupportedOperationException(); @@ -294,6 +303,7 @@ long beginOfChunk(long cleartextPos) { protected void implCloseChannel() throws IOException { try { flush(); + persistLastModified(); } finally { super.implCloseChannel(); closeListener.closed(this); diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java index 5f4aae6c..538d488f 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileChannelWriteReadIntegrationTest.java @@ -63,9 +63,9 @@ public void setupClass(@TempDir Path tmpDir) throws IOException { fileSystem = new CryptoFileSystemProvider().newFileSystem(create(tmpDir), cryptoFileSystemProperties().withPassphrase("asd").build()); } - // tests https://github.com/cryptomator/cryptofs/issues/56 + // tests https://github.com/cryptomator/cryptofs/issues/69 @Test - public void testForceDoesntBumpModifiedDate() throws IOException { + public void testCloseDoesNotBumpModifiedDate() throws IOException { Path file = fileSystem.getPath("/file.txt"); Instant t0, t1; @@ -76,7 +76,43 @@ public void testForceDoesntBumpModifiedDate() throws IOException { } t1 = Files.getLastModifiedTime(file).toInstant().truncatedTo(ChronoUnit.SECONDS); - Assertions.assertTrue(t1.equals(t0)); + Assertions.assertEquals(t0, t1); + } + + @Test + public void testLastModifiedIsPreservedOverSeveralOperations() throws IOException, InterruptedException { + Path file = fileSystem.getPath("/file2.txt"); + + Instant t0, t1, t2, t3, t4, t5; + t0 = Instant.ofEpochSecond(123456789).truncatedTo(ChronoUnit.SECONDS); + ByteBuffer data = ByteBuffer.wrap("CryptoFS".getBytes()); + + try (FileChannel ch = FileChannel.open(file, CREATE_NEW, WRITE)) { + t1 = Files.getLastModifiedTime(file).toInstant().truncatedTo(ChronoUnit.MILLIS); + Thread.currentThread().sleep(50); + + ch.write(data); + ch.force(true); + Thread.currentThread().sleep(50); + t2 = Files.getLastModifiedTime(file).toInstant().truncatedTo(ChronoUnit.MILLIS); + + Files.setLastModifiedTime(file, FileTime.from(t0)); + ch.force(true); + Thread.currentThread().sleep(50); + t3 = Files.getLastModifiedTime(file).toInstant().truncatedTo(ChronoUnit.MILLIS); + + ch.write(data); + ch.force(true); + Thread.currentThread().sleep(1000); + t4 = Files.getLastModifiedTime(file).toInstant().truncatedTo(ChronoUnit.SECONDS); + + } + + t5 = Files.getLastModifiedTime(file).toInstant().truncatedTo(ChronoUnit.SECONDS); + Assertions.assertNotEquals(t1, t2); + Assertions.assertEquals(t0, t3); + Assertions.assertNotEquals(t4, t3); + Assertions.assertEquals(t4, t5); } } diff --git a/src/test/java/org/cryptomator/cryptofs/ch/CleartextFileChannelTest.java b/src/test/java/org/cryptomator/cryptofs/ch/CleartextFileChannelTest.java index ac41721e..b38f4458 100644 --- a/src/test/java/org/cryptomator/cryptofs/ch/CleartextFileChannelTest.java +++ b/src/test/java/org/cryptomator/cryptofs/ch/CleartextFileChannelTest.java @@ -222,6 +222,26 @@ public void testCloseTriggersCloseListener() throws IOException { verify(closeListener).closed(inTest); } + + @Test + public void testCloseUpdatesLastModifiedTimeIfWriteable() throws IOException { + when(options.writable()).thenReturn(true); + lastModified.set(Instant.ofEpochMilli(123456789000l)); + FileTime fileTime = FileTime.from(lastModified.get()); + + inTest.implCloseChannel(); + + verify(attributeView).setTimes(Mockito.eq(fileTime), Mockito.any(), Mockito.isNull()); + } + + @Test + public void testCloseDoesNotUpdateLastModifiedTimeIfReadOnly() throws IOException { + when(options.writable()).thenReturn(false); + + inTest.implCloseChannel(); + + verify(attributeView).setTimes(Mockito.isNull(), Mockito.any(), Mockito.isNull()); + } } @Test @@ -267,7 +287,7 @@ public void testTryLockReturnsNullIfDelegateReturnsNull() throws IOException { @Test @DisplayName("successful tryLock()") public void testTryLockReturnsCryptoFileLockWrappingDelegate() throws IOException { - when(ciphertextFileChannel.tryLock(380l, 4670l+110l-380l, true)).thenReturn(delegate); + when(ciphertextFileChannel.tryLock(380l, 4670l + 110l - 380l, true)).thenReturn(delegate); FileLock result = inTest.tryLock(372l, 3828l, true); @@ -283,7 +303,7 @@ public void testTryLockReturnsCryptoFileLockWrappingDelegate() throws IOExceptio @Test @DisplayName("successful lock()") public void testLockReturnsCryptoFileLockWrappingDelegate() throws IOException { - when(ciphertextFileChannel.lock(380l, 4670l+110l-380l, true)).thenReturn(delegate); + when(ciphertextFileChannel.lock(380l, 4670l + 110l - 380l, true)).thenReturn(delegate); FileLock result = inTest.lock(372l, 3828l, true); @@ -472,7 +492,7 @@ public void testDontRewriteHeader() throws IOException { inTest = new CleartextFileChannel(ciphertextFileChannel, header, false, readWriteLock, cryptor, chunkCache, options, fileSize, lastModified, attributeViewSupplier, exceptionsDuringWrite, closeListener, stats); inTest.force(true); - + Mockito.verify(ciphertextFileChannel, Mockito.never()).write(Mockito.any(), Mockito.eq(0l)); } From 927666de520508295a163dc1aef5089741af37f3 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 17 Feb 2020 16:01:07 +0100 Subject: [PATCH 3/6] fixes #70 --- .../cryptofs/CryptoFileSystemProvider.java | 7 ++ .../common/FileSystemCapabilityChecker.java | 88 +++++++++++++++++++ .../cryptofs/migration/MigrationModule.java | 6 ++ .../cryptofs/migration/Migrators.java | 8 +- .../cryptofs/migration/MigratorsTest.java | 26 ++++-- 5 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/cryptomator/cryptofs/common/FileSystemCapabilityChecker.java diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java index 7285f69d..9cc9de44 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java @@ -10,6 +10,7 @@ import org.cryptomator.cryptofs.ch.AsyncDelegatingFileChannel; import org.cryptomator.cryptofs.common.Constants; +import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptofs.common.MasterkeyBackupFileHasher; import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.cryptolib.Cryptors; @@ -124,6 +125,7 @@ private static SecureRandom strongSecureRandom() { * @param properties Parameters used during initialization of the file system * @return a new file system * @throws FileSystemNeedsMigrationException if the vault format needs to get updated and properties did not contain a flag for implicit migration. + * @throws FileSystemCapabilityChecker.MissingCapabilityException If the underlying filesystem lacks features required to store a vault * @throws IOException if an I/O error occurs creating the file system */ public static CryptoFileSystem newFileSystem(Path pathToVault, CryptoFileSystemProperties properties) throws FileSystemNeedsMigrationException, IOException { @@ -138,6 +140,7 @@ public static CryptoFileSystem newFileSystem(Path pathToVault, CryptoFileSystemP * @param masterkeyFilename Name of the masterkey file * @param passphrase Passphrase that should be used to unlock the vault * @throws NotDirectoryException If the given path is not an existing directory. + * @throws FileSystemCapabilityChecker.MissingCapabilityException If the underlying filesystem lacks features required to store a vault * @throws IOException If the vault structure could not be initialized due to I/O errors * @since 1.3.0 */ @@ -153,6 +156,7 @@ public static void initialize(Path pathToVault, String masterkeyFilename, CharSe * @param pepper Application-specific pepper used during key derivation * @param passphrase Passphrase that should be used to unlock the vault * @throws NotDirectoryException If the given path is not an existing directory. + * @throws FileSystemCapabilityChecker.MissingCapabilityException If the underlying filesystem lacks features required to store a vault * @throws IOException If the vault structure could not be initialized due to I/O errors * @since 1.3.2 */ @@ -160,6 +164,7 @@ public static void initialize(Path pathToVault, String masterkeyFilename, byte[] if (!Files.isDirectory(pathToVault)) { throw new NotDirectoryException(pathToVault.toString()); } + new FileSystemCapabilityChecker().checkCapabilities(pathToVault); try (Cryptor cryptor = CRYPTOR_PROVIDER.createNew()) { // save masterkey file: Path masterKeyPath = pathToVault.resolve(masterkeyFilename); @@ -288,6 +293,8 @@ public String getScheme() { public CryptoFileSystem newFileSystem(URI uri, Map rawProperties) throws IOException { CryptoFileSystemUri parsedUri = CryptoFileSystemUri.parse(uri); CryptoFileSystemProperties properties = CryptoFileSystemProperties.wrap(rawProperties); + + new FileSystemCapabilityChecker().checkCapabilities(parsedUri.pathToVault()); // TODO remove implicit initialization in 2.0.0 initializeFileSystemIfRequired(parsedUri, properties); diff --git a/src/main/java/org/cryptomator/cryptofs/common/FileSystemCapabilityChecker.java b/src/main/java/org/cryptomator/cryptofs/common/FileSystemCapabilityChecker.java new file mode 100644 index 00000000..d9531a6d --- /dev/null +++ b/src/main/java/org/cryptomator/cryptofs/common/FileSystemCapabilityChecker.java @@ -0,0 +1,88 @@ +package org.cryptomator.cryptofs.common; + +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import com.google.common.io.MoreFiles; +import com.google.common.io.RecursiveDeleteOption; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class FileSystemCapabilityChecker { + + private static final Logger LOG = LoggerFactory.getLogger(FileSystemCapabilityChecker.class); + + public enum Capability { + /** + * File system supports filenames with ≥ 230 chars. + */ + LONG_FILENAMES, + + /** + * File system supports paths with ≥ 400 chars. + */ + LONG_PATHS, + } + + /** + * Checks whether the underlying filesystem has all required capabilities. + * + * @param pathToVault Path to a vault's storage location + * @throws MissingCapabilityException if any check fails + * @implNote Only short-running tests with constant time are performed + * @since 1.9.2 + */ + public void checkCapabilities(Path pathToVault) throws MissingCapabilityException { + Path checkDir = pathToVault.resolve("c"); + try { + checkLongFilenames(checkDir); + checkLongFilePaths(checkDir); + } finally { + try { + MoreFiles.deleteRecursively(checkDir, RecursiveDeleteOption.ALLOW_INSECURE); + } catch (IOException e) { + LOG.warn("Failed to clean up " + checkDir, e); + } + } + } + + private void checkLongFilenames(Path checkDir) throws MissingCapabilityException { + String longFileName = Strings.repeat("a", 226) + ".c9r"; + Path p = checkDir.resolve(longFileName); + try { + Files.createDirectories(p); + } catch (IOException e) { + throw new MissingCapabilityException(p, Capability.LONG_FILENAMES); + } + } + + private void checkLongFilePaths(Path checkDir) throws MissingCapabilityException { + String longFileName = Strings.repeat("a", 96) + ".c9r"; + String longPath = Joiner.on('/').join(longFileName, longFileName, longFileName, longFileName); + Path p = checkDir.resolve(longPath); + try { + Files.createDirectories(p); + } catch (IOException e) { + throw new MissingCapabilityException(p, Capability.LONG_PATHS); + } + } + + public static class MissingCapabilityException extends FileSystemException { + + private final Capability missingCapability; + + public MissingCapabilityException(Path path, Capability missingCapability) { + super(path.toString(), null, "Filesystem doesn't support " + missingCapability); + this.missingCapability = missingCapability; + } + + public Capability getMissingCapability() { + return missingCapability; + } + } + +} diff --git a/src/main/java/org/cryptomator/cryptofs/migration/MigrationModule.java b/src/main/java/org/cryptomator/cryptofs/migration/MigrationModule.java index d69c2efc..0d700add 100644 --- a/src/main/java/org/cryptomator/cryptofs/migration/MigrationModule.java +++ b/src/main/java/org/cryptomator/cryptofs/migration/MigrationModule.java @@ -13,6 +13,7 @@ import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; +import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptofs.migration.api.Migrator; import org.cryptomator.cryptofs.migration.v6.Version6Migrator; import org.cryptomator.cryptofs.migration.v7.Version7Migrator; @@ -34,6 +35,11 @@ class MigrationModule { CryptorProvider provideVersion1CryptorProvider() { return version1Cryptor; } + + @Provides + FileSystemCapabilityChecker provideFileSystemCapabilityChecker() { + return new FileSystemCapabilityChecker(); + } @Provides @IntoMap diff --git a/src/main/java/org/cryptomator/cryptofs/migration/Migrators.java b/src/main/java/org/cryptomator/cryptofs/migration/Migrators.java index e8506250..8937eb60 100644 --- a/src/main/java/org/cryptomator/cryptofs/migration/Migrators.java +++ b/src/main/java/org/cryptomator/cryptofs/migration/Migrators.java @@ -16,6 +16,7 @@ import javax.inject.Inject; import org.cryptomator.cryptofs.common.Constants; +import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptofs.migration.api.MigrationProgressListener; import org.cryptomator.cryptofs.migration.api.Migrator; import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException; @@ -46,10 +47,12 @@ public class Migrators { .build(); private final Map migrators; + private final FileSystemCapabilityChecker fsCapabilityChecker; @Inject - Migrators(Map migrators) { + Migrators(Map migrators, FileSystemCapabilityChecker fsCapabilityChecker) { this.migrators = migrators; + this.fsCapabilityChecker = fsCapabilityChecker; } private static SecureRandom strongSecureRandom() { @@ -87,9 +90,12 @@ public boolean needsMigration(Path pathToVault, String masterkeyFilename) throws * @param passphrase The passphrase needed to unlock the vault * @throws NoApplicableMigratorException If the vault can not be migrated, because no migrator could be found * @throws InvalidPassphraseException If the passphrase could not be used to unlock the vault + * @throws FileSystemCapabilityChecker.MissingCapabilityException If the underlying filesystem lacks features required to store a vault * @throws IOException if an I/O error occurs migrating the vault */ public void migrate(Path pathToVault, String masterkeyFilename, CharSequence passphrase, MigrationProgressListener progressListener) throws NoApplicableMigratorException, InvalidPassphraseException, IOException { + fsCapabilityChecker.checkCapabilities(pathToVault); + Path masterKeyPath = pathToVault.resolve(masterkeyFilename); byte[] keyFileContents = Files.readAllBytes(masterKeyPath); KeyFile keyFile = KeyFile.parse(keyFileContents); diff --git a/src/test/java/org/cryptomator/cryptofs/migration/MigratorsTest.java b/src/test/java/org/cryptomator/cryptofs/migration/MigratorsTest.java index df8f3e29..f15ae16f 100644 --- a/src/test/java/org/cryptomator/cryptofs/migration/MigratorsTest.java +++ b/src/test/java/org/cryptomator/cryptofs/migration/MigratorsTest.java @@ -5,6 +5,7 @@ *******************************************************************************/ package org.cryptomator.cryptofs.migration; +import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptofs.migration.api.MigrationProgressListener; import org.cryptomator.cryptofs.migration.api.Migrator; import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException; @@ -29,11 +30,13 @@ public class MigratorsTest { private ByteBuffer keyFile; private Path pathToVault; + private FileSystemCapabilityChecker fsCapabilityChecker; @BeforeEach public void setup() throws IOException { keyFile = StandardCharsets.UTF_8.encode("{\"version\": 0000}"); pathToVault = Mockito.mock(Path.class); + fsCapabilityChecker = Mockito.mock(FileSystemCapabilityChecker.class); Path pathToMasterkey = Mockito.mock(Path.class); FileSystem fs = Mockito.mock(FileSystem.class); @@ -57,7 +60,7 @@ public void setup() throws IOException { @Test public void testNeedsMigration() throws IOException { - Migrators migrators = new Migrators(Collections.emptyMap()); + Migrators migrators = new Migrators(Collections.emptyMap(), fsCapabilityChecker); boolean result = migrators.needsMigration(pathToVault, "masterkey.cryptomator"); Assertions.assertTrue(result); @@ -67,7 +70,7 @@ public void testNeedsMigration() throws IOException { public void testNeedsNoMigration() throws IOException { keyFile = StandardCharsets.UTF_8.encode("{\"version\": 9999}"); - Migrators migrators = new Migrators(Collections.emptyMap()); + Migrators migrators = new Migrators(Collections.emptyMap(), fsCapabilityChecker); boolean result = migrators.needsMigration(pathToVault, "masterkey.cryptomator"); Assertions.assertFalse(result); @@ -75,12 +78,25 @@ public void testNeedsNoMigration() throws IOException { @Test public void testMigrateWithoutMigrators() throws IOException { - Migrators migrators = new Migrators(Collections.emptyMap()); + Migrators migrators = new Migrators(Collections.emptyMap(), fsCapabilityChecker); Assertions.assertThrows(NoApplicableMigratorException.class, () -> { migrators.migrate(pathToVault, "masterkey.cryptomator", "secret", (state, progress) -> {}); }); } + @Test + public void testMigrateWithFailingCapabilitiesCheck() throws IOException { + Migrators migrators = new Migrators(Collections.emptyMap(), fsCapabilityChecker); + + Exception expected = new FileSystemCapabilityChecker.MissingCapabilityException(pathToVault, FileSystemCapabilityChecker.Capability.LONG_FILENAMES); + Mockito.doThrow(expected).when(fsCapabilityChecker).checkCapabilities(pathToVault); + + Exception thrown = Assertions.assertThrows(FileSystemCapabilityChecker.MissingCapabilityException.class, () -> { + migrators.migrate(pathToVault, "masterkey.cryptomator", "secret", (state, progress) -> {}); + }); + Assertions.assertEquals(expected, thrown); + } + @Test @SuppressWarnings("deprecation") public void testMigrate() throws NoApplicableMigratorException, InvalidPassphraseException, IOException { @@ -90,7 +106,7 @@ public void testMigrate() throws NoApplicableMigratorException, InvalidPassphras { put(Migration.ZERO_TO_ONE, migrator); } - }); + }, fsCapabilityChecker); migrators.migrate(pathToVault, "masterkey.cryptomator", "secret", listener); Mockito.verify(migrator).migrate(pathToVault, "masterkey.cryptomator", "secret", listener); } @@ -103,7 +119,7 @@ public void testMigrateUnsupportedVaultFormat() throws NoApplicableMigratorExcep { put(Migration.ZERO_TO_ONE, migrator); } - }); + }, fsCapabilityChecker); Mockito.doThrow(new UnsupportedVaultFormatException(Integer.MAX_VALUE, 1)).when(migrator).migrate(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); Assertions.assertThrows(IllegalStateException.class, () -> { migrators.migrate(pathToVault, "masterkey.cryptomator", "secret", (state, progress) -> {}); From 8eeb38d196c386dec6635b610bfee940be395ba6 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 17 Feb 2020 16:07:35 +0100 Subject: [PATCH 4/6] dependency update --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e0b4dd37..8afeec62 100644 --- a/pom.xml +++ b/pom.xml @@ -15,11 +15,11 @@ 1.3.0 - 2.25.4 + 2.26 28.2-jre 1.7.30 - 5.5.2 + 5.6.0 3.2.4 2.2 UTF-8 From 3430e17fab104a06616abcc75f48cfd2f0033911 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 17 Feb 2020 16:08:16 +0100 Subject: [PATCH 5/6] Prepare release 1.9.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8afeec62..8aa3f5c8 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.cryptomator cryptofs - 1.10.0-SNAPSHOT + 1.9.2 Cryptomator Crypto Filesystem This library provides the Java filesystem provider used by Cryptomator. https://github.com/cryptomator/cryptofs From a251ad2a22d65d4820f7ceed579f7ee4be452865 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 17 Feb 2020 16:45:40 +0100 Subject: [PATCH 6/6] allow using @implNote in JavaDoc (why is it unknown? this is standard since java 8...) --- pom.xml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pom.xml b/pom.xml index 8aa3f5c8..1f92d11b 100644 --- a/pom.xml +++ b/pom.xml @@ -239,6 +239,31 @@ + + + + apiNote + a + API Note: + + + implSpec + a + Implementation Requirements: + + + implNote + a + Implementation Note: + + param + return + throws + since + version + serialData + see + javax.annotation