org.cryptomator.cryptofs.LongFileNameProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.cryptomator.cryptofs.LongFileNameProvider.java

Source

/*******************************************************************************
 * Copyright (c) 2016 Sebastian Stenzel and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the accompanying LICENSE.txt.
 *
 * Contributors:
 *     Sebastian Stenzel - initial API and implementation
 *******************************************************************************/
package org.cryptomator.cryptofs;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.cryptomator.cryptofs.Constants.METADATA_DIR_NAME;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;

import javax.inject.Inject;

import org.cryptomator.cryptolib.common.MessageDigestSupplier;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.io.BaseEncoding;
import com.google.common.util.concurrent.UncheckedExecutionException;

@PerFileSystem
class LongFileNameProvider {

    private static final BaseEncoding BASE32 = BaseEncoding.base32();
    private static final int MAX_CACHE_SIZE = 5000;
    public static final String LONG_NAME_FILE_EXT = ".lng";

    private final Path metadataRoot;
    private final LoadingCache<String, String> ids;

    @Inject
    public LongFileNameProvider(@PathToVault Path pathToVault) {
        this.metadataRoot = pathToVault.resolve(METADATA_DIR_NAME);
        this.ids = CacheBuilder.newBuilder().maximumSize(MAX_CACHE_SIZE).build(new Loader());
    }

    private class Loader extends CacheLoader<String, String> {

        @Override
        public String load(String shortName) throws IOException {
            Path file = resolveMetadataFile(shortName);
            return new String(Files.readAllBytes(file), UTF_8);
        }

    }

    public static boolean isDeflated(String possiblyDeflatedFileName) {
        return possiblyDeflatedFileName.endsWith(LONG_NAME_FILE_EXT);
    }

    public String inflate(String shortFileName) throws IOException {
        try {
            return ids.get(shortFileName);
        } catch (ExecutionException e) {
            if (e.getCause() instanceof IOException || e.getCause() instanceof UncheckedIOException) {
                throw new IOException(e);
            } else {
                throw new UncheckedExecutionException("Unexpected exception", e);
            }
        }
    }

    public String deflate(String longFileName) throws IOException {
        byte[] longFileNameBytes = longFileName.getBytes(UTF_8);
        byte[] hash = MessageDigestSupplier.SHA1.get().digest(longFileNameBytes);
        String shortName = BASE32.encode(hash) + LONG_NAME_FILE_EXT;
        if (ids.getIfPresent(shortName) == null) {
            ids.put(shortName, longFileName);
            // TODO markuskreusch, overheadhunter: do we really want to persist this at this point?...
            // ...maybe the caller only wanted to know if a file exists without creating anything.
            Path file = resolveMetadataFile(shortName);
            Path fileDir = file.getParent();
            assert fileDir != null : "resolveMetadataFile returned path to a file";
            Files.createDirectories(fileDir);
            Files.write(file, longFileNameBytes);
        }
        return shortName;
    }

    private Path resolveMetadataFile(String shortName) {
        return metadataRoot.resolve(shortName.substring(0, 2)).resolve(shortName.substring(2, 4))
                .resolve(shortName);
    }

}