cryptomator / cryptofs

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

Wrong usage of Caffeine cache #180

Closed infeo closed 11 months ago

infeo commented 12 months ago

With PR https://github.com/cryptomator/cryptofs/pull/164 we switched our caching library from Google's Guave implementation to Caffeine.

Unfortunately, caffeine does not allow recursive updates of the cache (see https://github.com/ben-manes/caffeine/wiki/Faq#recursive-computations), but we are doing it anyway in the CryptoPathMapper class:

    public CiphertextFilePath getCiphertextFilePath(CryptoPath cleartextPath) throws IOException {
        CryptoPath parentPath = cleartextPath.getParent();
        if (parentPath == null) {
            throw new IllegalArgumentException("Invalid file path (must have a parent): " + cleartextPath);
        }
//---------HERE: we call getCiphertextDir(...)
        CiphertextDirectory parent = getCiphertextDir(parentPath);
        String cleartextName = cleartextPath.getFileName().toString();
        return getCiphertextFilePath(parent.path, parent.dirId, cleartextName);
    }

    public CiphertextDirectory getCiphertextDir(CryptoPath cleartextPath) throws IOException {
        assert cleartextPath.isAbsolute();
        CryptoPath parentPath = cleartextPath.getParent();
        if (parentPath == null) {
            return rootDirectory;
        } else {
            try {
//--------------------cache.get() is an if-cached-return-else-create-and-return operation
                return ciphertextDirectories.get(cleartextPath, p -> {
                    try {
//-------------------------------HERE: we call getCiphertextFilepath(...)
                        Path dirFile = getCiphertextFilePath(p).getDirFilePath();
                        return resolveDirectory(dirFile);
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                });
            } catch (UncheckedIOException e) {
                throw new IOException(e);
            }
        }
    }

This leads to the following stacktrace when accessing deeper folderstructures:


java.lang.IllegalStateException: Recursive update

    at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1991)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.BoundedLocalCache.doComputeIfAbsent(BoundedLocalCache.java:2666)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.BoundedLocalCache.computeIfAbsent(BoundedLocalCache.java:2649)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.LocalCache.computeIfAbsent(LocalCache.java:112)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.LocalManualCache.get(LocalManualCache.java:62)
    at org.cryptomator.cryptofs/org.cryptomator.cryptofs.CryptoPathMapper.getCiphertextDir(CryptoPathMapper.java:161)
    at org.cryptomator.cryptofs/org.cryptomator.cryptofs.CryptoPathMapper.getCiphertextFilePath(CryptoPathMapper.java:122)
....//more repetetions of the above stacktrace
    at org.cryptomator.cryptofs/org.cryptomator.cryptofs.CryptoPathMapper.lambda$getCiphertextDir$0(CryptoPathMapper.java:163)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.BoundedLocalCache.lambda$doComputeIfAbsent$14(BoundedLocalCache.java:2668)
    at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.BoundedLocalCache.doComputeIfAbsent(BoundedLocalCache.java:2666)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.BoundedLocalCache.computeIfAbsent(BoundedLocalCache.java:2649)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.LocalCache.computeIfAbsent(LocalCache.java:112)
    at com.github.benmanes.caffeine/com.github.benmanes.caffeine.cache.LocalManualCache.get(LocalManualCache.java:62)
    at org.cryptomator.cryptofs/org.cryptomator.cryptofs.CryptoPathMapper.getCiphertextDir(CryptoPathMapper.java:161)
    at org.cryptomator.cryptofs/org.cryptomator.cryptofs.CryptoPathMapper.getCiphertextFilePath(CryptoPathMapper.java:122)
    at org.cryptomator.cryptofs/org.cryptomator.cryptofs.CryptoPathMapperTest.testPathEncryptionDEEP(CryptoPathMapperTest.java:173)