cryptomator / cryptofs

Java Filesystem Provider with integrated encryption
GNU Affero General Public License v3.0
94 stars 35 forks source link

Closing file channel of deleted file throws exception #169

Closed infeo closed 1 year ago

infeo commented 1 year ago

Consider the following test:

    public void testClosingChannelOfDeletedFile() throws IOException {
                var file = cryptoFileSystem.getPath("/file");
        Assertions.assertDoesNotThrow(() -> {
            try (var ch = FileChannel.open(file, CREATE_NEW, WRITE)) {
                ch.write(ByteBuffer.wrap("delete me".getBytes(StandardCharsets.UTF_8)));
                Files.delete(file);
            }
        });
        Assertions.assertTrue(Files.notExists(file));
    }

It fails with

Caused by: java.nio.file.NoSuchFileException: C:\work\vault\d\KG\6TFDGKXGZEGWRZOGTDFDF4YEGAZO6Q\LXBV6TKI3yH3CMtJLyAcHkOOcMcMeBuT.c9r
    at com.google.common.jimfs.DirectoryEntry.requireExists(DirectoryEntry.java:66)
    at com.google.common.jimfs.FileSystemView$1.lookup(FileSystemView.java:708)
    at com.google.common.jimfs.AbstractAttributeView.lookupFile(AbstractAttributeView.java:39)
    at com.google.common.jimfs.BasicAttributeProvider$View.setTimes(BasicAttributeProvider.java:155)
    at org.cryptomator.cryptofs@2.7.0-SNAPSHOT/org.cryptomator.cryptofs.ch.CleartextFileChannel.persistLastModified(CleartextFileChannel.java:250)
    at org.cryptomator.cryptofs@2.7.0-SNAPSHOT/org.cryptomator.cryptofs.ch.CleartextFileChannel.implCloseChannel(CleartextFileChannel.java:321)
    at java.base/java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:112)
    at org.cryptomator.cryptofs@2.7.0-SNAPSHOT/org.cryptomator.cryptofs.CryptoFileChannelWriteReadIntegrationTest$PlatformIndependent.lambda$testClosingChannelOfDeletedFile$4(CryptoFileChannelWriteReadIntegrationTest.java:580)
    at org.junit.jupiter.api.AssertDoesNotThrow.assertDoesNotThrow(AssertDoesNotThrow.java:49)
    ... 83 more

The reason is, that we set the last modified date explicitly when closing the file channel (due to caching of chunks, otherwise last modified might set to the time when closing the channel and flush the chunk cache).

Default OS filesystems behave differently, by just closing the channel without any exception.

infeo commented 1 year ago

We discussed how we can handle this case. Three options were possible:

  1. Wrap setting the lastModified on close in a try-catch-block, ignoring errors
  2. Introduce a "isDeleted" state and skip last modified persistence when file is deleted
  3. Rework the lastModified architecture

Because 2.6.4 contains important fixes, we decided to go for option 1. for now, because setting the last modified is a best effort base anyway.