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

net.java.truevfs.comp.zipdriver.AbstractKeyController Maven / Gradle / Ivy

/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package net.java.truevfs.comp.zipdriver;

import java.io.IOException;
import java.net.URI;
import java.util.Objects;
import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;
import net.java.truecommons.cio.Entry;
import net.java.truecommons.cio.Entry.Access;
import static net.java.truecommons.cio.Entry.Type.SPECIAL;
import net.java.truecommons.shed.BitField;
import net.java.truecommons.shed.ControlFlowException;
import net.java.truevfs.kernel.spec.*;
import static net.java.truevfs.kernel.spec.FsNodeName.ROOT;
import net.java.truecommons.key.spec.KeyManager;
import net.java.truecommons.key.spec.KeyManagerMap;

/**
 * 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 ZIP driver.
 * @author Christian Schlichtherle
 */
@Immutable
public abstract class AbstractKeyController>
extends FsDecoratingController {

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

    protected final D driver;
    private volatile KeyManagerMap container;

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

    protected abstract Class getKeyType();

    protected abstract Class getKeyExceptionType();

    private KeyManager getKeyManager() {
        final KeyManagerMap c = this.container;
        return (null != c ? c : (this.container = driver.getKeyManagerMap()))
                .manager(getKeyType());
    }

    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 final FsNode node(
            final BitField options,
            final FsNodeName name)
    throws IOException {
        try {
            return controller.node(options, name);
        } catch (final ControlFlowException ex) {
            if (!name.isRoot() || null == findKeyException(ex))
                throw ex;
            Entry node = getParent().node(
                    options, getModel()
                                 .getMountPoint()
                                 .getPath()
                                 .resolve(name)
                                 .getNodeName());
            // We're not holding any locks, so it's possible that someone else
            // has concurrently modified the parent file system.
            if (null == node)
                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.
            if (node instanceof FsCovariantNode)
                node = ((FsCovariantNode) node).getEntry();
            final FsCovariantNode
                    special = new FsCovariantNode<>(ROOT_PATH);
            special.put(SPECIAL, driver.newEntry(ROOT_PATH, SPECIAL, node));
            return special;
        }
    }

    @Override
    public void checkAccess(
            final BitField options, final FsNodeName name, final BitField types)
    throws IOException {
        try {
            controller.checkAccess(options, name, types);
        } catch (final ControlFlowException ex) {
            if (!name.isRoot() || null == findKeyException(ex))
                throw ex;
            getParent().checkAccess(
                    options, getModel()
                                 .getMountPoint()
                                 .getPath()
                                 .resolve(name)
                                 .getNodeName(),
                    types);
        }
    }

    @Override
    public final void unlink(
            final BitField options,
            final FsNodeName name)
    throws IOException {
        try {
            controller.unlink(options, name);
        } 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 ex;
            throw keyEx;
        }
        final FsModel model = getModel();
        final URI mpu = driver.mountPointUri(model);
        final URI fsu = driver.fileSystemUri(model, name.toString());
        if (!fsu.equals(mpu) || name.isRoot())
            getKeyManager().unlink(fsu);
    }

    @Override
    public void sync(final BitField options)
    throws FsSyncWarningException, FsSyncException {
        final FsSyncExceptionBuilder builder = new FsSyncExceptionBuilder();
        try {
            controller.sync(options);
        } catch (FsSyncWarningException ex) {
            builder.warn(ex);
        }
        getKeyManager().release(driver.mountPointUri(getModel()));
        builder.check();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy