18a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/*
28a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Copyright (C) 2013 The Android Open Source Project
38a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak *
48a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Licensed under the Apache License, Version 2.0 (the "License");
58a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * you may not use this file except in compliance with the License.
68a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * You may obtain a copy of the License at
78a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak *
88a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak *      http://www.apache.org/licenses/LICENSE-2.0
98a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak *
108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Unless required by applicable law or agreed to in writing, software
118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * distributed under the License is distributed on an "AS IS" BASIS,
128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * See the License for the specific language governing permissions and
148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * limitations under the License.
158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */
168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakpackage android.util.jar;
198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
206c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewskiimport android.system.ErrnoException;
216c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewskiimport android.system.Os;
226c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewskiimport android.system.OsConstants;
236c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski
248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport dalvik.system.CloseGuard;
256c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewskiimport java.io.FileDescriptor;
268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.FilterInputStream;
278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.IOException;
288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.InputStream;
298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.security.cert.Certificate;
308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.HashMap;
318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Iterator;
328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Set;
336c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewskiimport java.util.jar.JarFile;
348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.zip.Inflater;
358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.zip.InflaterInputStream;
368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.zip.ZipEntry;
376c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewskiimport libcore.io.IoBridge;
388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport libcore.io.IoUtils;
398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport libcore.io.Streams;
408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/**
428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * A subset of the JarFile API implemented as a thin wrapper over
438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * system/core/libziparchive.
448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak *
458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * @hide for internal use only. Not API compatible (or as forgiving) as
468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak *        {@link java.util.jar.JarFile}
478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */
488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakpublic final class StrictJarFile {
498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private final long nativeHandle;
518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    // NOTE: It's possible to share a file descriptor with the native
538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    // code, at the cost of some additional complexity.
546c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    private final FileDescriptor fd;
558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private final StrictJarManifest manifest;
578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private final StrictJarVerifier verifier;
588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private final boolean isSigned;
608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private final CloseGuard guard = CloseGuard.get();
628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private boolean closed;
638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
64e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin    public StrictJarFile(String fileName)
65e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin            throws IOException, SecurityException {
669b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin        this(fileName, true, true);
67e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin    }
68e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin
696c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    public StrictJarFile(FileDescriptor fd)
706c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            throws IOException, SecurityException {
716c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski        this(fd, true, true);
726c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    }
736c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski
746c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    public StrictJarFile(FileDescriptor fd,
756c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            boolean verify,
766c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            boolean signatureSchemeRollbackProtectionsEnforced)
776c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                    throws IOException, SecurityException {
786c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski        this("[fd:" + fd.getInt$() + "]", fd, verify,
796c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                signatureSchemeRollbackProtectionsEnforced);
806c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    }
816c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski
826c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    public StrictJarFile(String fileName,
836c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            boolean verify,
846c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            boolean signatureSchemeRollbackProtectionsEnforced)
856c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                    throws IOException, SecurityException {
866c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski        this(fileName, IoBridge.open(fileName, OsConstants.O_RDONLY),
876c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                verify, signatureSchemeRollbackProtectionsEnforced);
886c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    }
896c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski
909b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin    /**
916c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski     * @param name of the archive (not necessarily a path).
926c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski     * @param fd seekable file descriptor for the JAR file.
939b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin     * @param verify whether to verify the file's JAR signatures and collect the corresponding
949b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin     *        signer certificates.
959b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin     * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
969b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin     *        stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or
979b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin     *        {@code false} to ignore any such protections. This parameter is ignored when
989b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin     *        {@code verify} is {@code false}.
999b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin     */
1006c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    private StrictJarFile(String name,
1016c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            FileDescriptor fd,
1029b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin            boolean verify,
1039b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin            boolean signatureSchemeRollbackProtectionsEnforced)
1049b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin                    throws IOException, SecurityException {
1056c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski        this.nativeHandle = nativeOpenJarFile(name, fd.getInt$());
1066c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski        this.fd = fd;
1078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        try {
1098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            // Read the MANIFEST and signature files up front and try to
1108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            // parse them. We never want to accept a JAR File with broken signatures
1118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            // or manifests, so it's best to throw as early as possible.
112e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin            if (verify) {
113e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                HashMap<String, byte[]> metaEntries = getMetaEntries();
114e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
1159b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin                this.verifier =
1169b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin                        new StrictJarVerifier(
1176c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                                name,
1189b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin                                manifest,
1199b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin                                metaEntries,
1209b59bc459b4cb5415909641bd1e981100bfafb2bAlex Klyubin                                signatureSchemeRollbackProtectionsEnforced);
121e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                Set<String> files = manifest.getEntries().keySet();
122e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                for (String file : files) {
123e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                    if (findEntry(file) == null) {
1246c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                        throw new SecurityException("File " + file + " in manifest does not exist");
125e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                    }
1268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
1278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
128e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                isSigned = verifier.readCertificates() && verifier.isSignedJar();
129e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin            } else {
130e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                isSigned = false;
131e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                this.manifest = null;
132e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin                this.verifier = null;
133e415718502897a4e5385af47d3bbe8c8257c2e5dAlex Klyubin            }
1348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        } catch (IOException | SecurityException e) {
1358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            nativeClose(this.nativeHandle);
1366c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            IoUtils.closeQuietly(fd);
137b061fc2bb5b6e8397c7f3a40be521badad91e9afTomasz Mikolajewski            closed = true;
1388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            throw e;
1398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
1408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        guard.open("close");
1428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
1438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public StrictJarManifest getManifest() {
1458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        return manifest;
1468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
1478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public Iterator<ZipEntry> iterator() throws IOException {
1498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        return new EntryIterator(nativeHandle, "");
1508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
1518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public ZipEntry findEntry(String name) {
1538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        return nativeFindEntry(nativeHandle, name);
1548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
1558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    /**
1578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * Return all certificate chains for a given {@link ZipEntry} belonging to this jar.
1588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * This method MUST be called only after fully exhausting the InputStream belonging
1598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * to this entry.
1608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     *
1618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * Returns {@code null} if this jar file isn't signed or if this method is
1628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * called before the stream is processed.
1638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     */
1648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public Certificate[][] getCertificateChains(ZipEntry ze) {
1658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        if (isSigned) {
1668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return verifier.getCertificateChains(ze.getName());
1678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
1688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        return null;
1708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
1718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    /**
1738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * Return all certificates for a given {@link ZipEntry} belonging to this jar.
1748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * This method MUST be called only after fully exhausting the InputStream belonging
1758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * to this entry.
1768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     *
1778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * Returns {@code null} if this jar file isn't signed or if this method is
1788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * called before the stream is processed.
1798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     *
1808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * @deprecated Switch callers to use getCertificateChains instead
1818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     */
1828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    @Deprecated
1838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public Certificate[] getCertificates(ZipEntry ze) {
1848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        if (isSigned) {
1858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            Certificate[][] certChains = verifier.getCertificateChains(ze.getName());
1868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            // Measure number of certs.
1888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            int count = 0;
1898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            for (Certificate[] chain : certChains) {
1908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                count += chain.length;
1918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
1928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
1938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            // Create new array and copy all the certs into it.
1948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            Certificate[] certs = new Certificate[count];
1958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            int i = 0;
1968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            for (Certificate[] chain : certChains) {
1978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                System.arraycopy(chain, 0, certs, i, chain.length);
1988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                i += chain.length;
1998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
2008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return certs;
2028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        return null;
2058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
2068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public InputStream getInputStream(ZipEntry ze) {
2088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        final InputStream is = getZipInputStream(ze);
2098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        if (isSigned) {
2118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            StrictJarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName());
2128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (entry == null) {
2138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return is;
2148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
2158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return new JarFileInputStream(is, ze.getSize(), entry);
2178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        return is;
2208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
2218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public void close() throws IOException {
2238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        if (!closed) {
2246c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            if (guard != null) {
2256c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                guard.close();
2266c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            }
2278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            nativeClose(nativeHandle);
2296c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            IoUtils.closeQuietly(fd);
2308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            closed = true;
2318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
2338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
234b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller    @Override
235b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller    protected void finalize() throws Throwable {
236b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller        try {
237b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller            if (guard != null) {
238b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller                guard.warnIfOpen();
239b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller            }
240b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller            close();
241b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller        } finally {
242b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller            super.finalize();
243b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller        }
244b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller    }
245b69f61472a25cff07cb9cc139e26e50c5af20394Neil Fuller
2468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private InputStream getZipInputStream(ZipEntry ze) {
2478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        if (ze.getMethod() == ZipEntry.STORED) {
2486c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            return new FDStream(fd, ze.getDataOffset(),
2498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    ze.getDataOffset() + ze.getSize());
2508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        } else {
2516c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            final FDStream wrapped = new FDStream(
2526c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                    fd, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize());
2538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            int bufSize = Math.max(1024, (int) Math.min(ze.getSize(), 65535L));
2558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return new ZipInflaterInputStream(wrapped, new Inflater(true), bufSize, ze);
2568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
2588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    static final class EntryIterator implements Iterator<ZipEntry> {
2608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private final long iterationHandle;
2618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private ZipEntry nextEntry;
2628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        EntryIterator(long nativeHandle, String prefix) throws IOException {
2648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            iterationHandle = nativeStartIteration(nativeHandle, prefix);
2658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public ZipEntry next() {
2688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (nextEntry != null) {
2698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                final ZipEntry ze = nextEntry;
2708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                nextEntry = null;
2718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return ze;
2728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
2738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return nativeNextEntry(iterationHandle);
2758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public boolean hasNext() {
2788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (nextEntry != null) {
2798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return true;
2808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
2818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            final ZipEntry ze = nativeNextEntry(iterationHandle);
2838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (ze == null) {
2848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return false;
2858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
2868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            nextEntry = ze;
2888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return true;
2898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public void remove() {
2928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            throw new UnsupportedOperationException();
2938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
2948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
2958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private HashMap<String, byte[]> getMetaEntries() throws IOException {
2978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>();
2988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
2998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        Iterator<ZipEntry> entryIterator = new EntryIterator(nativeHandle, "META-INF/");
3008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        while (entryIterator.hasNext()) {
3018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            final ZipEntry entry = entryIterator.next();
3028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            metaEntries.put(entry.getName(), Streams.readFully(getInputStream(entry)));
3038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
3048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        return metaEntries;
3068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
3078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    static final class JarFileInputStream extends FilterInputStream {
3098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private final StrictJarVerifier.VerifierEntry entry;
3108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private long count;
3128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private boolean done = false;
3138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        JarFileInputStream(InputStream is, long size, StrictJarVerifier.VerifierEntry e) {
3158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            super(is);
3168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            entry = e;
3178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            count = size;
3198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
3208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override
3228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public int read() throws IOException {
3238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (done) {
3248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return -1;
3258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
3268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (count > 0) {
3278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                int r = super.read();
3288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                if (r != -1) {
3298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    entry.write(r);
3308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    count--;
3318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                } else {
3328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    count = 0;
3338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
3348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                if (count == 0) {
3358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    done = true;
3368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    entry.verify();
3378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
3388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return r;
3398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            } else {
3408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                done = true;
3418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                entry.verify();
3428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return -1;
3438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
3448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
3458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override
3478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
3488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (done) {
3498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return -1;
3508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
3518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (count > 0) {
3528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                int r = super.read(buffer, byteOffset, byteCount);
3538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                if (r != -1) {
3548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    int size = r;
3558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    if (count < size) {
3568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                        size = (int) count;
3578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    }
3588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    entry.write(buffer, byteOffset, size);
3598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    count -= size;
3608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                } else {
3618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    count = 0;
3628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
3638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                if (count == 0) {
3648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    done = true;
3658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    entry.verify();
3668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
3678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return r;
3688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            } else {
3698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                done = true;
3708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                entry.verify();
3718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return -1;
3728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
3738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
3748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override
3768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public int available() throws IOException {
3778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (done) {
3788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return 0;
3798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
3808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return super.available();
3818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
3828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override
3848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public long skip(long byteCount) throws IOException {
3858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return Streams.skipByReading(this, byteCount);
3868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
3878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
3888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    /** @hide */
3908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    public static class ZipInflaterInputStream extends InflaterInputStream {
3918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private final ZipEntry entry;
3928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private long bytesRead = 0;
3938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        public ZipInflaterInputStream(InputStream is, Inflater inf, int bsize, ZipEntry entry) {
3958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            super(is, inf, bsize);
3968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            this.entry = entry;
3978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
3988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
3998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
4008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            final int i;
4018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            try {
4028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                i = super.read(buffer, byteOffset, byteCount);
4038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            } catch (IOException e) {
4048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                throw new IOException("Error reading data for " + entry.getName() + " near offset "
4058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                        + bytesRead, e);
4068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
4078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (i == -1) {
4088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                if (entry.getSize() != bytesRead) {
4098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    throw new IOException("Size mismatch on inflated file: " + bytesRead + " vs "
4108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                            + entry.getSize());
4118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
4128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            } else {
4138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                bytesRead += i;
4148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
4158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return i;
4168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
4178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override public int available() throws IOException {
4198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (closed) {
4208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                // Our superclass will throw an exception, but there's a jtreg test that
4218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                // explicitly checks that the InputStream returned from ZipFile.getInputStream
4228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                // returns 0 even when closed.
4238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                return 0;
4248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
4258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
4268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
4278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
4288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    /**
4306c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski     * Wrap a stream around a FileDescriptor.  The file descriptor is shared
4318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * among all streams returned by getInputStream(), so we have to synchronize
4328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * access to it.  (We can optimize this by adding buffering here to reduce
4338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * collisions.)
4348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     *
4358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * <p>We could support mark/reset, but we don't currently need them.
4368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     *
4378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     * @hide
4388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak     */
4396c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    public static class FDStream extends InputStream {
4406c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski        private final FileDescriptor fd;
4418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private long endOffset;
4428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        private long offset;
4438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4446c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski        public FDStream(FileDescriptor fd, long initialOffset, long endOffset) {
4456c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            this.fd = fd;
4468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            offset = initialOffset;
4478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            this.endOffset = endOffset;
4488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
4498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override public int available() throws IOException {
4518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return (offset < endOffset ? 1 : 0);
4528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
4538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override public int read() throws IOException {
4558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return Streams.readSingleByte(this);
4568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
4578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
4596c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            synchronized (this.fd) {
4608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                final long length = endOffset - offset;
4618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                if (byteCount > length) {
4628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    byteCount = (int) length;
4638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
4646c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                try {
4656c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                    Os.lseek(fd, offset, OsConstants.SEEK_SET);
4666c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                } catch (ErrnoException e) {
4676c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                    throw new IOException(e);
4686c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                }
4696c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski                int count = IoBridge.read(fd, buffer, byteOffset, byteCount);
4708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                if (count > 0) {
4718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    offset += count;
4728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    return count;
4738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                } else {
4748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                    return -1;
4758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                }
4768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
4778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
4788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        @Override public long skip(long byteCount) throws IOException {
4808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            if (byteCount > endOffset - offset) {
4818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak                byteCount = endOffset - offset;
4828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            }
4838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            offset += byteCount;
4848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak            return byteCount;
4858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak        }
4868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    }
4878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak
4886c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski    private static native long nativeOpenJarFile(String name, int fd)
4896c3df1543cb85d5ff7ebb2b026d7a34184465c7bTomasz Mikolajewski            throws IOException;
4908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private static native long nativeStartIteration(long nativeHandle, String prefix);
4918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private static native ZipEntry nativeNextEntry(long iterationHandle);
4928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private static native ZipEntry nativeFindEntry(long nativeHandle, String entryName);
4938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak    private static native void nativeClose(long nativeHandle);
4948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak}
495