All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.truezip.fs.archive.zip.KeyController Maven / Gradle / Ivy

Go to download

The file system driver family for ZIP and related archive file types. Add the JAR artifact of this module to the run time class path to make its file system drivers available for service location in the client API modules.

There is a newer version: 7.7.10
Show newest version
/*
 * Copyright (C) 2005-2013 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package de.schlichtherle.truezip.fs.archive.zip;

import de.schlichtherle.truezip.entry.Entry;
import static de.schlichtherle.truezip.entry.Entry.Type.SPECIAL;
import static de.schlichtherle.truezip.fs.FsEntryName.ROOT;
import de.schlichtherle.truezip.fs.*;
import de.schlichtherle.truezip.key.KeyManager;
import de.schlichtherle.truezip.key.KeyProvider;
import de.schlichtherle.truezip.key.SafeKeyManager;
import de.schlichtherle.truezip.util.BitField;
import de.schlichtherle.truezip.util.ControlFlowException;
import java.io.IOException;
import java.net.URI;
import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;

/**
 * This file system controller decorates another file system controller in
 * order to manage the keys required for accessing encrypted ZIP files.
 * 
 * @param   the type of the file system model.
 * @param   the type of the ZIP driver.
 * @author Christian Schlichtherle
 */
@Immutable
public abstract class KeyController<
        M extends FsModel,
        D extends ZipDriver>
extends FsDecoratingController> {

    private static final String ROOT_PATH = ROOT.getPath();

    protected final D driver;
    private volatile KeyManager manager;

    /**
     * Constructs a new key manager controller.
     *
     * @param controller the non-{@code null} file system controller to
     *        decorate.
     * @param driver the ZIP driver.
     */
    protected KeyController(
            final FsController controller,
            final D driver) {
        super(controller);
        if (null == driver)
            throw new NullPointerException();
        this.driver = driver;
    }

    protected abstract Class getKeyType();

    protected abstract Class getKeyExceptionType();

    private KeyManager getKeyManager() {
        final KeyManager manager = this.manager;
        return null != manager
                ? manager
                : (this.manager = driver.getKeyManagerProvider()
                    .get(getKeyType()));
    }

    @Override
    public FsEntry getEntry(final FsEntryName name)
    throws IOException {
        try {
            return delegate.getEntry(name);
        } catch (final ControlFlowException ex) {
            if (!name.isRoot() || null == findKeyException(ex)) throw ex;
            Entry entry = getParent().getEntry(
                    getModel()
                        .getMountPoint()
                        .getPath()
                        .resolve(name)
                        .getEntryName());
            // We're not holding any locks, so it's possible that someone else
            // has concurrently modified the parent file system.
            if (null == entry) return null;
            // The entry is inaccessible for some reason.
            // This may be because the cipher key is not available.
            // Now mask the entry as a special file.
            while (entry instanceof FsCovariantEntry)
                entry = ((FsCovariantEntry) entry).getEntry();
            final FsCovariantEntry
                    special = new FsCovariantEntry(ROOT_PATH);
            special.put(SPECIAL, driver.newEntry(ROOT_PATH, SPECIAL, entry));
            return special;
        }
    }

    @Override
    public void unlink(final FsEntryName name, BitField options)
    throws IOException {
        try {
            delegate.unlink(name, options);
        } catch (final ControlFlowException ex) {
            // If the exception is caused by a key exception, then throw this
            // cause instead in order to avoid treating the target archive file
            // like a false positive and routing this operation to the parent
            // file system.
            // This prevents the application from inadvertently deleting an
            // encrypted ZIP file just because the user cancelled key prompting.
            final IOException keyEx = findKeyException(ex);
            if (null != keyEx) throw keyEx;
            throw ex;
        }
        if (name.isRoot())
            getKeyManager().removeKeyProvider(
                    driver.resourceUri(getModel(), name.toString()));
    }

    private @CheckForNull IOException findKeyException(Throwable ex) {
        final Class clazz = getKeyExceptionType();
        do {
            if (clazz.isInstance(ex)) return clazz.cast(ex);
        } while (null != (ex = ex.getCause()));
        return null;
    }

    @Override
    public void sync(final BitField options)
    throws FsSyncException {
        delegate.sync(options);
        final KeyManager manager = getKeyManager();
        final URI resource = driver.mountPointUri(getModel());
        final KeyProvider provider;
        if (manager instanceof SafeKeyManager) {
            // Don't create a key provider if there wasn't one mapped already.
            provider = ((SafeKeyManager) manager).getMappedKeyProvider(resource);
        } else {
            // TODO: This might create a memory leak.
            // It's unlikely that a third party implements the KeyManager
            // interface and does NOT use extend the SafeKeyManager class,
            // though.
            provider = manager.getKeyProvider(resource);
        }
        if (null != provider)
            driver.getKeyProviderSyncStrategy().sync(provider);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy