src/java.base/share/classes/java/util/zip/ZipFile.java
changeset 47216 71c04702a3d5
parent 45434 4582657c7260
child 47223 723486922bfe
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package java.util.zip;
       
    27 
       
    28 import java.io.Closeable;
       
    29 import java.io.InputStream;
       
    30 import java.io.IOException;
       
    31 import java.io.EOFException;
       
    32 import java.io.File;
       
    33 import java.io.RandomAccessFile;
       
    34 import java.nio.charset.Charset;
       
    35 import java.nio.charset.StandardCharsets;
       
    36 import java.nio.file.attribute.BasicFileAttributes;
       
    37 import java.nio.file.Path;
       
    38 import java.nio.file.Files;
       
    39 
       
    40 import java.util.ArrayDeque;
       
    41 import java.util.ArrayList;
       
    42 import java.util.Arrays;
       
    43 import java.util.Deque;
       
    44 import java.util.Enumeration;
       
    45 import java.util.HashMap;
       
    46 import java.util.Iterator;
       
    47 import java.util.Map;
       
    48 import java.util.Objects;
       
    49 import java.util.NoSuchElementException;
       
    50 import java.util.Spliterator;
       
    51 import java.util.Spliterators;
       
    52 import java.util.WeakHashMap;
       
    53 import java.util.stream.Stream;
       
    54 import java.util.stream.StreamSupport;
       
    55 import jdk.internal.misc.JavaUtilZipFileAccess;
       
    56 import jdk.internal.misc.SharedSecrets;
       
    57 import jdk.internal.misc.JavaIORandomAccessFileAccess;
       
    58 import jdk.internal.misc.VM;
       
    59 import jdk.internal.perf.PerfCounter;
       
    60 
       
    61 import static java.util.zip.ZipConstants.*;
       
    62 import static java.util.zip.ZipConstants64.*;
       
    63 import static java.util.zip.ZipUtils.*;
       
    64 
       
    65 /**
       
    66  * This class is used to read entries from a zip file.
       
    67  *
       
    68  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
       
    69  * or method in this class will cause a {@link NullPointerException} to be
       
    70  * thrown.
       
    71  *
       
    72  * @author      David Connelly
       
    73  * @since 1.1
       
    74  */
       
    75 public
       
    76 class ZipFile implements ZipConstants, Closeable {
       
    77 
       
    78     private final String name;     // zip file name
       
    79     private volatile boolean closeRequested;
       
    80     private Source zsrc;
       
    81     private ZipCoder zc;
       
    82 
       
    83     private static final int STORED = ZipEntry.STORED;
       
    84     private static final int DEFLATED = ZipEntry.DEFLATED;
       
    85 
       
    86     /**
       
    87      * Mode flag to open a zip file for reading.
       
    88      */
       
    89     public static final int OPEN_READ = 0x1;
       
    90 
       
    91     /**
       
    92      * Mode flag to open a zip file and mark it for deletion.  The file will be
       
    93      * deleted some time between the moment that it is opened and the moment
       
    94      * that it is closed, but its contents will remain accessible via the
       
    95      * {@code ZipFile} object until either the close method is invoked or the
       
    96      * virtual machine exits.
       
    97      */
       
    98     public static final int OPEN_DELETE = 0x4;
       
    99 
       
   100     /**
       
   101      * Opens a zip file for reading.
       
   102      *
       
   103      * <p>First, if there is a security manager, its {@code checkRead}
       
   104      * method is called with the {@code name} argument as its argument
       
   105      * to ensure the read is allowed.
       
   106      *
       
   107      * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
       
   108      * decode the entry names and comments.
       
   109      *
       
   110      * @param name the name of the zip file
       
   111      * @throws ZipException if a ZIP format error has occurred
       
   112      * @throws IOException if an I/O error has occurred
       
   113      * @throws SecurityException if a security manager exists and its
       
   114      *         {@code checkRead} method doesn't allow read access to the file.
       
   115      *
       
   116      * @see SecurityManager#checkRead(java.lang.String)
       
   117      */
       
   118     public ZipFile(String name) throws IOException {
       
   119         this(new File(name), OPEN_READ);
       
   120     }
       
   121 
       
   122     /**
       
   123      * Opens a new {@code ZipFile} to read from the specified
       
   124      * {@code File} object in the specified mode.  The mode argument
       
   125      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
       
   126      *
       
   127      * <p>First, if there is a security manager, its {@code checkRead}
       
   128      * method is called with the {@code name} argument as its argument to
       
   129      * ensure the read is allowed.
       
   130      *
       
   131      * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
       
   132      * decode the entry names and comments
       
   133      *
       
   134      * @param file the ZIP file to be opened for reading
       
   135      * @param mode the mode in which the file is to be opened
       
   136      * @throws ZipException if a ZIP format error has occurred
       
   137      * @throws IOException if an I/O error has occurred
       
   138      * @throws SecurityException if a security manager exists and
       
   139      *         its {@code checkRead} method
       
   140      *         doesn't allow read access to the file,
       
   141      *         or its {@code checkDelete} method doesn't allow deleting
       
   142      *         the file when the {@code OPEN_DELETE} flag is set.
       
   143      * @throws IllegalArgumentException if the {@code mode} argument is invalid
       
   144      * @see SecurityManager#checkRead(java.lang.String)
       
   145      * @since 1.3
       
   146      */
       
   147     public ZipFile(File file, int mode) throws IOException {
       
   148         this(file, mode, StandardCharsets.UTF_8);
       
   149     }
       
   150 
       
   151     /**
       
   152      * Opens a ZIP file for reading given the specified File object.
       
   153      *
       
   154      * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
       
   155      * decode the entry names and comments.
       
   156      *
       
   157      * @param file the ZIP file to be opened for reading
       
   158      * @throws ZipException if a ZIP format error has occurred
       
   159      * @throws IOException if an I/O error has occurred
       
   160      */
       
   161     public ZipFile(File file) throws ZipException, IOException {
       
   162         this(file, OPEN_READ);
       
   163     }
       
   164 
       
   165     /**
       
   166      * Opens a new {@code ZipFile} to read from the specified
       
   167      * {@code File} object in the specified mode.  The mode argument
       
   168      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
       
   169      *
       
   170      * <p>First, if there is a security manager, its {@code checkRead}
       
   171      * method is called with the {@code name} argument as its argument to
       
   172      * ensure the read is allowed.
       
   173      *
       
   174      * @param file the ZIP file to be opened for reading
       
   175      * @param mode the mode in which the file is to be opened
       
   176      * @param charset
       
   177      *        the {@linkplain java.nio.charset.Charset charset} to
       
   178      *        be used to decode the ZIP entry name and comment that are not
       
   179      *        encoded by using UTF-8 encoding (indicated by entry's general
       
   180      *        purpose flag).
       
   181      *
       
   182      * @throws ZipException if a ZIP format error has occurred
       
   183      * @throws IOException if an I/O error has occurred
       
   184      *
       
   185      * @throws SecurityException
       
   186      *         if a security manager exists and its {@code checkRead}
       
   187      *         method doesn't allow read access to the file,or its
       
   188      *         {@code checkDelete} method doesn't allow deleting the
       
   189      *         file when the {@code OPEN_DELETE} flag is set
       
   190      *
       
   191      * @throws IllegalArgumentException if the {@code mode} argument is invalid
       
   192      *
       
   193      * @see SecurityManager#checkRead(java.lang.String)
       
   194      *
       
   195      * @since 1.7
       
   196      */
       
   197     public ZipFile(File file, int mode, Charset charset) throws IOException
       
   198     {
       
   199         if (((mode & OPEN_READ) == 0) ||
       
   200             ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
       
   201             throw new IllegalArgumentException("Illegal mode: 0x"+
       
   202                                                Integer.toHexString(mode));
       
   203         }
       
   204         String name = file.getPath();
       
   205         SecurityManager sm = System.getSecurityManager();
       
   206         if (sm != null) {
       
   207             sm.checkRead(name);
       
   208             if ((mode & OPEN_DELETE) != 0) {
       
   209                 sm.checkDelete(name);
       
   210             }
       
   211         }
       
   212         Objects.requireNonNull(charset, "charset");
       
   213         this.zc = ZipCoder.get(charset);
       
   214         this.name = name;
       
   215         long t0 = System.nanoTime();
       
   216         this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0);
       
   217         PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
       
   218         PerfCounter.getZipFileCount().increment();
       
   219     }
       
   220 
       
   221     /**
       
   222      * Opens a zip file for reading.
       
   223      *
       
   224      * <p>First, if there is a security manager, its {@code checkRead}
       
   225      * method is called with the {@code name} argument as its argument
       
   226      * to ensure the read is allowed.
       
   227      *
       
   228      * @param name the name of the zip file
       
   229      * @param charset
       
   230      *        the {@linkplain java.nio.charset.Charset charset} to
       
   231      *        be used to decode the ZIP entry name and comment that are not
       
   232      *        encoded by using UTF-8 encoding (indicated by entry's general
       
   233      *        purpose flag).
       
   234      *
       
   235      * @throws ZipException if a ZIP format error has occurred
       
   236      * @throws IOException if an I/O error has occurred
       
   237      * @throws SecurityException
       
   238      *         if a security manager exists and its {@code checkRead}
       
   239      *         method doesn't allow read access to the file
       
   240      *
       
   241      * @see SecurityManager#checkRead(java.lang.String)
       
   242      *
       
   243      * @since 1.7
       
   244      */
       
   245     public ZipFile(String name, Charset charset) throws IOException
       
   246     {
       
   247         this(new File(name), OPEN_READ, charset);
       
   248     }
       
   249 
       
   250     /**
       
   251      * Opens a ZIP file for reading given the specified File object.
       
   252      *
       
   253      * @param file the ZIP file to be opened for reading
       
   254      * @param charset
       
   255      *        The {@linkplain java.nio.charset.Charset charset} to be
       
   256      *        used to decode the ZIP entry name and comment (ignored if
       
   257      *        the <a href="package-summary.html#lang_encoding"> language
       
   258      *        encoding bit</a> of the ZIP entry's general purpose bit
       
   259      *        flag is set).
       
   260      *
       
   261      * @throws ZipException if a ZIP format error has occurred
       
   262      * @throws IOException if an I/O error has occurred
       
   263      *
       
   264      * @since 1.7
       
   265      */
       
   266     public ZipFile(File file, Charset charset) throws IOException
       
   267     {
       
   268         this(file, OPEN_READ, charset);
       
   269     }
       
   270 
       
   271     /**
       
   272      * Returns the zip file comment, or null if none.
       
   273      *
       
   274      * @return the comment string for the zip file, or null if none
       
   275      *
       
   276      * @throws IllegalStateException if the zip file has been closed
       
   277      *
       
   278      * @since 1.7
       
   279      */
       
   280     public String getComment() {
       
   281         synchronized (this) {
       
   282             ensureOpen();
       
   283             if (zsrc.comment == null) {
       
   284                 return null;
       
   285             }
       
   286             return zc.toString(zsrc.comment);
       
   287         }
       
   288     }
       
   289 
       
   290     /**
       
   291      * Returns the zip file entry for the specified name, or null
       
   292      * if not found.
       
   293      *
       
   294      * @param name the name of the entry
       
   295      * @return the zip file entry, or null if not found
       
   296      * @throws IllegalStateException if the zip file has been closed
       
   297      */
       
   298     public ZipEntry getEntry(String name) {
       
   299         Objects.requireNonNull(name, "name");
       
   300         synchronized (this) {
       
   301             ensureOpen();
       
   302             byte[] bname = zc.getBytes(name);
       
   303             int pos = zsrc.getEntryPos(bname, true);
       
   304             if (pos != -1) {
       
   305                 return getZipEntry(name, bname, pos);
       
   306             }
       
   307         }
       
   308         return null;
       
   309     }
       
   310 
       
   311     // The outstanding inputstreams that need to be closed,
       
   312     // mapped to the inflater objects they use.
       
   313     private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
       
   314 
       
   315     /**
       
   316      * Returns an input stream for reading the contents of the specified
       
   317      * zip file entry.
       
   318      * <p>
       
   319      * Closing this ZIP file will, in turn, close all input streams that
       
   320      * have been returned by invocations of this method.
       
   321      *
       
   322      * @param entry the zip file entry
       
   323      * @return the input stream for reading the contents of the specified
       
   324      * zip file entry.
       
   325      * @throws ZipException if a ZIP format error has occurred
       
   326      * @throws IOException if an I/O error has occurred
       
   327      * @throws IllegalStateException if the zip file has been closed
       
   328      */
       
   329     public InputStream getInputStream(ZipEntry entry) throws IOException {
       
   330         Objects.requireNonNull(entry, "entry");
       
   331         int pos = -1;
       
   332         ZipFileInputStream in = null;
       
   333         synchronized (this) {
       
   334             ensureOpen();
       
   335             if (Objects.equals(lastEntryName, entry.name)) {
       
   336                 pos = lastEntryPos;
       
   337             } else if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
       
   338                 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
       
   339             } else {
       
   340                 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
       
   341             }
       
   342             if (pos == -1) {
       
   343                 return null;
       
   344             }
       
   345             in = new ZipFileInputStream(zsrc.cen, pos);
       
   346             switch (CENHOW(zsrc.cen, pos)) {
       
   347             case STORED:
       
   348                 synchronized (streams) {
       
   349                     streams.put(in, null);
       
   350                 }
       
   351                 return in;
       
   352             case DEFLATED:
       
   353                 // Inflater likes a bit of slack
       
   354                 // MORE: Compute good size for inflater stream:
       
   355                 long size = CENLEN(zsrc.cen, pos) + 2;
       
   356                 if (size > 65536) {
       
   357                     size = 8192;
       
   358                 }
       
   359                 if (size <= 0) {
       
   360                     size = 4096;
       
   361                 }
       
   362                 Inflater inf = getInflater();
       
   363                 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
       
   364                 synchronized (streams) {
       
   365                     streams.put(is, inf);
       
   366                 }
       
   367                 return is;
       
   368             default:
       
   369                 throw new ZipException("invalid compression method");
       
   370             }
       
   371         }
       
   372     }
       
   373 
       
   374     private class ZipFileInflaterInputStream extends InflaterInputStream {
       
   375         private volatile boolean closeRequested;
       
   376         private boolean eof = false;
       
   377         private final ZipFileInputStream zfin;
       
   378 
       
   379         ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
       
   380                 int size) {
       
   381             super(zfin, inf, size);
       
   382             this.zfin = zfin;
       
   383         }
       
   384 
       
   385         public void close() throws IOException {
       
   386             if (closeRequested)
       
   387                 return;
       
   388             closeRequested = true;
       
   389 
       
   390             super.close();
       
   391             Inflater inf;
       
   392             synchronized (streams) {
       
   393                 inf = streams.remove(this);
       
   394             }
       
   395             if (inf != null) {
       
   396                 releaseInflater(inf);
       
   397             }
       
   398         }
       
   399 
       
   400         // Override fill() method to provide an extra "dummy" byte
       
   401         // at the end of the input stream. This is required when
       
   402         // using the "nowrap" Inflater option.
       
   403         protected void fill() throws IOException {
       
   404             if (eof) {
       
   405                 throw new EOFException("Unexpected end of ZLIB input stream");
       
   406             }
       
   407             len = in.read(buf, 0, buf.length);
       
   408             if (len == -1) {
       
   409                 buf[0] = 0;
       
   410                 len = 1;
       
   411                 eof = true;
       
   412             }
       
   413             inf.setInput(buf, 0, len);
       
   414         }
       
   415 
       
   416         public int available() throws IOException {
       
   417             if (closeRequested)
       
   418                 return 0;
       
   419             long avail = zfin.size() - inf.getBytesWritten();
       
   420             return (avail > (long) Integer.MAX_VALUE ?
       
   421                     Integer.MAX_VALUE : (int) avail);
       
   422         }
       
   423 
       
   424         @SuppressWarnings("deprecation")
       
   425         protected void finalize() throws Throwable {
       
   426             close();
       
   427         }
       
   428     }
       
   429 
       
   430     /*
       
   431      * Gets an inflater from the list of available inflaters or allocates
       
   432      * a new one.
       
   433      */
       
   434     private Inflater getInflater() {
       
   435         Inflater inf;
       
   436         synchronized (inflaterCache) {
       
   437             while ((inf = inflaterCache.poll()) != null) {
       
   438                 if (!inf.ended()) {
       
   439                     return inf;
       
   440                 }
       
   441             }
       
   442         }
       
   443         return new Inflater(true);
       
   444     }
       
   445 
       
   446     /*
       
   447      * Releases the specified inflater to the list of available inflaters.
       
   448      */
       
   449     private void releaseInflater(Inflater inf) {
       
   450         if (!inf.ended()) {
       
   451             inf.reset();
       
   452             synchronized (inflaterCache) {
       
   453                 inflaterCache.add(inf);
       
   454             }
       
   455         }
       
   456     }
       
   457 
       
   458     // List of available Inflater objects for decompression
       
   459     private final Deque<Inflater> inflaterCache = new ArrayDeque<>();
       
   460 
       
   461     /**
       
   462      * Returns the path name of the ZIP file.
       
   463      * @return the path name of the ZIP file
       
   464      */
       
   465     public String getName() {
       
   466         return name;
       
   467     }
       
   468 
       
   469     private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
       
   470         private int i = 0;
       
   471         private final int entryCount;
       
   472 
       
   473         public ZipEntryIterator() {
       
   474             synchronized (ZipFile.this) {
       
   475                 ensureOpen();
       
   476                 this.entryCount = zsrc.total;
       
   477             }
       
   478         }
       
   479 
       
   480         public boolean hasMoreElements() {
       
   481             return hasNext();
       
   482         }
       
   483 
       
   484         public boolean hasNext() {
       
   485             return i < entryCount;
       
   486         }
       
   487 
       
   488         public ZipEntry nextElement() {
       
   489             return next();
       
   490         }
       
   491 
       
   492         public ZipEntry next() {
       
   493             synchronized (ZipFile.this) {
       
   494                 ensureOpen();
       
   495                 if (!hasNext()) {
       
   496                     throw new NoSuchElementException();
       
   497                 }
       
   498                 // each "entry" has 3 ints in table entries
       
   499                 return getZipEntry(null, null, zsrc.getEntryPos(i++ * 3));
       
   500             }
       
   501         }
       
   502 
       
   503         public Iterator<ZipEntry> asIterator() {
       
   504             return this;
       
   505         }
       
   506     }
       
   507 
       
   508     /**
       
   509      * Returns an enumeration of the ZIP file entries.
       
   510      * @return an enumeration of the ZIP file entries
       
   511      * @throws IllegalStateException if the zip file has been closed
       
   512      */
       
   513     public Enumeration<? extends ZipEntry> entries() {
       
   514         return new ZipEntryIterator();
       
   515     }
       
   516 
       
   517     /**
       
   518      * Returns an ordered {@code Stream} over the ZIP file entries.
       
   519      * Entries appear in the {@code Stream} in the order they appear in
       
   520      * the central directory of the ZIP file.
       
   521      *
       
   522      * @return an ordered {@code Stream} of entries in this ZIP file
       
   523      * @throws IllegalStateException if the zip file has been closed
       
   524      * @since 1.8
       
   525      */
       
   526     public Stream<? extends ZipEntry> stream() {
       
   527         return StreamSupport.stream(Spliterators.spliterator(
       
   528                 new ZipEntryIterator(), size(),
       
   529                 Spliterator.ORDERED | Spliterator.DISTINCT |
       
   530                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
       
   531     }
       
   532 
       
   533     private String lastEntryName;
       
   534     private int lastEntryPos;
       
   535 
       
   536     /* Checks ensureOpen() before invoke this method */
       
   537     private ZipEntry getZipEntry(String name, byte[] bname, int pos) {
       
   538         byte[] cen = zsrc.cen;
       
   539         int nlen = CENNAM(cen, pos);
       
   540         int elen = CENEXT(cen, pos);
       
   541         int clen = CENCOM(cen, pos);
       
   542         int flag = CENFLG(cen, pos);
       
   543         if (name == null || bname.length != nlen) {
       
   544             // to use the entry name stored in cen, if the passed in name is
       
   545             // (1) null, invoked from iterator, or
       
   546             // (2) not equal to the name stored, a slash is appended during
       
   547             // getEntryPos() search.
       
   548             if (!zc.isUTF8() && (flag & EFS) != 0) {
       
   549                 name = zc.toStringUTF8(cen, pos + CENHDR, nlen);
       
   550             } else {
       
   551                 name = zc.toString(cen, pos + CENHDR, nlen);
       
   552             }
       
   553         }
       
   554         ZipEntry e = new ZipEntry(name);
       
   555         e.flag = flag;
       
   556         e.xdostime = CENTIM(cen, pos);
       
   557         e.crc = CENCRC(cen, pos);
       
   558         e.size = CENLEN(cen, pos);
       
   559         e.csize = CENSIZ(cen, pos);
       
   560         e.method = CENHOW(cen, pos);
       
   561         if (elen != 0) {
       
   562             int start = pos + CENHDR + nlen;
       
   563             e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true);
       
   564         }
       
   565         if (clen != 0) {
       
   566             int start = pos + CENHDR + nlen + elen;
       
   567             if (!zc.isUTF8() && (flag & EFS) != 0) {
       
   568                 e.comment = zc.toStringUTF8(cen, start, clen);
       
   569             } else {
       
   570                 e.comment = zc.toString(cen, start, clen);
       
   571             }
       
   572         }
       
   573         lastEntryName = e.name;
       
   574         lastEntryPos = pos;
       
   575         return e;
       
   576     }
       
   577 
       
   578     /**
       
   579      * Returns the number of entries in the ZIP file.
       
   580      *
       
   581      * @return the number of entries in the ZIP file
       
   582      * @throws IllegalStateException if the zip file has been closed
       
   583      */
       
   584     public int size() {
       
   585         synchronized (this) {
       
   586             ensureOpen();
       
   587             return zsrc.total;
       
   588         }
       
   589     }
       
   590 
       
   591     /**
       
   592      * Closes the ZIP file.
       
   593      * <p> Closing this ZIP file will close all of the input streams
       
   594      * previously returned by invocations of the {@link #getInputStream
       
   595      * getInputStream} method.
       
   596      *
       
   597      * @throws IOException if an I/O error has occurred
       
   598      */
       
   599     public void close() throws IOException {
       
   600         if (closeRequested) {
       
   601             return;
       
   602         }
       
   603         closeRequested = true;
       
   604 
       
   605         synchronized (this) {
       
   606             // Close streams, release their inflaters
       
   607             synchronized (streams) {
       
   608                 if (!streams.isEmpty()) {
       
   609                     Map<InputStream, Inflater> copy = new HashMap<>(streams);
       
   610                     streams.clear();
       
   611                     for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) {
       
   612                         e.getKey().close();
       
   613                         Inflater inf = e.getValue();
       
   614                         if (inf != null) {
       
   615                             inf.end();
       
   616                         }
       
   617                     }
       
   618                 }
       
   619             }
       
   620             // Release cached inflaters
       
   621             synchronized (inflaterCache) {
       
   622                 Inflater inf;
       
   623                 while ((inf = inflaterCache.poll()) != null) {
       
   624                     inf.end();
       
   625                 }
       
   626             }
       
   627             // Release zip src
       
   628             if (zsrc != null) {
       
   629                 Source.close(zsrc);
       
   630                 zsrc = null;
       
   631             }
       
   632         }
       
   633     }
       
   634 
       
   635     /**
       
   636      * Ensures that the system resources held by this ZipFile object are
       
   637      * released when there are no more references to it.
       
   638      *
       
   639      * <p>
       
   640      * Since the time when GC would invoke this method is undetermined,
       
   641      * it is strongly recommended that applications invoke the {@code close}
       
   642      * method as soon they have finished accessing this {@code ZipFile}.
       
   643      * This will prevent holding up system resources for an undetermined
       
   644      * length of time.
       
   645      *
       
   646      * @deprecated The {@code finalize} method has been deprecated.
       
   647      *     Subclasses that override {@code finalize} in order to perform cleanup
       
   648      *     should be modified to use alternative cleanup mechanisms and
       
   649      *     to remove the overriding {@code finalize} method.
       
   650      *     When overriding the {@code finalize} method, its implementation must explicitly
       
   651      *     ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
       
   652      *     See the specification for {@link Object#finalize()} for further
       
   653      *     information about migration options.
       
   654      * @throws IOException if an I/O error has occurred
       
   655      * @see    java.util.zip.ZipFile#close()
       
   656      */
       
   657     @Deprecated(since="9")
       
   658     protected void finalize() throws IOException {
       
   659         close();
       
   660     }
       
   661 
       
   662     private void ensureOpen() {
       
   663         if (closeRequested) {
       
   664             throw new IllegalStateException("zip file closed");
       
   665         }
       
   666         if (zsrc == null) {
       
   667             throw new IllegalStateException("The object is not initialized.");
       
   668         }
       
   669     }
       
   670 
       
   671     private void ensureOpenOrZipException() throws IOException {
       
   672         if (closeRequested) {
       
   673             throw new ZipException("ZipFile closed");
       
   674         }
       
   675     }
       
   676 
       
   677     /*
       
   678      * Inner class implementing the input stream used to read a
       
   679      * (possibly compressed) zip file entry.
       
   680      */
       
   681    private class ZipFileInputStream extends InputStream {
       
   682         private volatile boolean closeRequested;
       
   683         private   long pos;     // current position within entry data
       
   684         protected long rem;     // number of remaining bytes within entry
       
   685         protected long size;    // uncompressed size of this entry
       
   686 
       
   687         ZipFileInputStream(byte[] cen, int cenpos) throws IOException {
       
   688             rem = CENSIZ(cen, cenpos);
       
   689             size = CENLEN(cen, cenpos);
       
   690             pos = CENOFF(cen, cenpos);
       
   691             // zip64
       
   692             if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
       
   693                 pos == ZIP64_MAGICVAL) {
       
   694                 checkZIP64(cen, cenpos);
       
   695             }
       
   696             // negative for lazy initialization, see getDataOffset();
       
   697             pos = - (pos + ZipFile.this.zsrc.locpos);
       
   698         }
       
   699 
       
   700         private void checkZIP64(byte[] cen, int cenpos) throws IOException {
       
   701             int off = cenpos + CENHDR + CENNAM(cen, cenpos);
       
   702             int end = off + CENEXT(cen, cenpos);
       
   703             while (off + 4 < end) {
       
   704                 int tag = get16(cen, off);
       
   705                 int sz = get16(cen, off + 2);
       
   706                 off += 4;
       
   707                 if (off + sz > end)         // invalid data
       
   708                     break;
       
   709                 if (tag == EXTID_ZIP64) {
       
   710                     if (size == ZIP64_MAGICVAL) {
       
   711                         if (sz < 8 || (off + 8) > end)
       
   712                             break;
       
   713                         size = get64(cen, off);
       
   714                         sz -= 8;
       
   715                         off += 8;
       
   716                     }
       
   717                     if (rem == ZIP64_MAGICVAL) {
       
   718                         if (sz < 8 || (off + 8) > end)
       
   719                             break;
       
   720                         rem = get64(cen, off);
       
   721                         sz -= 8;
       
   722                         off += 8;
       
   723                     }
       
   724                     if (pos == ZIP64_MAGICVAL) {
       
   725                         if (sz < 8 || (off + 8) > end)
       
   726                             break;
       
   727                         pos = get64(cen, off);
       
   728                         sz -= 8;
       
   729                         off += 8;
       
   730                     }
       
   731                     break;
       
   732                 }
       
   733                 off += sz;
       
   734             }
       
   735         }
       
   736 
       
   737        /* The Zip file spec explicitly allows the LOC extra data size to
       
   738         * be different from the CEN extra data size. Since we cannot trust
       
   739         * the CEN extra data size, we need to read the LOC to determine
       
   740         * the entry data offset.
       
   741         */
       
   742         private long initDataOffset() throws IOException {
       
   743             if (pos <= 0) {
       
   744                 byte[] loc = new byte[LOCHDR];
       
   745                 pos = -pos;
       
   746                 int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos);
       
   747                 if (len != LOCHDR) {
       
   748                     throw new ZipException("ZipFile error reading zip file");
       
   749                 }
       
   750                 if (LOCSIG(loc) != LOCSIG) {
       
   751                     throw new ZipException("ZipFile invalid LOC header (bad signature)");
       
   752                 }
       
   753                 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
       
   754             }
       
   755             return pos;
       
   756         }
       
   757 
       
   758         public int read(byte b[], int off, int len) throws IOException {
       
   759             synchronized (ZipFile.this) {
       
   760                 ensureOpenOrZipException();
       
   761                 initDataOffset();
       
   762                 if (rem == 0) {
       
   763                     return -1;
       
   764                 }
       
   765                 if (len > rem) {
       
   766                     len = (int) rem;
       
   767                 }
       
   768                 if (len <= 0) {
       
   769                     return 0;
       
   770                 }
       
   771                 len = ZipFile.this.zsrc.readAt(b, off, len, pos);
       
   772                 if (len > 0) {
       
   773                     pos += len;
       
   774                     rem -= len;
       
   775                 }
       
   776             }
       
   777             if (rem == 0) {
       
   778                 close();
       
   779             }
       
   780             return len;
       
   781         }
       
   782 
       
   783         public int read() throws IOException {
       
   784             byte[] b = new byte[1];
       
   785             if (read(b, 0, 1) == 1) {
       
   786                 return b[0] & 0xff;
       
   787             } else {
       
   788                 return -1;
       
   789             }
       
   790         }
       
   791 
       
   792         public long skip(long n) throws IOException {
       
   793             synchronized (ZipFile.this) {
       
   794                 ensureOpenOrZipException();
       
   795                 initDataOffset();
       
   796                 if (n > rem) {
       
   797                     n = rem;
       
   798                 }
       
   799                 pos += n;
       
   800                 rem -= n;
       
   801             }
       
   802             if (rem == 0) {
       
   803                 close();
       
   804             }
       
   805             return n;
       
   806         }
       
   807 
       
   808         public int available() {
       
   809             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
       
   810         }
       
   811 
       
   812         public long size() {
       
   813             return size;
       
   814         }
       
   815 
       
   816         public void close() {
       
   817             if (closeRequested) {
       
   818                 return;
       
   819             }
       
   820             closeRequested = true;
       
   821             rem = 0;
       
   822             synchronized (streams) {
       
   823                 streams.remove(this);
       
   824             }
       
   825         }
       
   826 
       
   827         @SuppressWarnings("deprecation")
       
   828         protected void finalize() {
       
   829             close();
       
   830         }
       
   831     }
       
   832 
       
   833     /**
       
   834      * Returns the names of all non-directory entries that begin with
       
   835      * "META-INF/" (case ignored). This method is used in JarFile, via
       
   836      * SharedSecrets, as an optimization when looking up manifest and
       
   837      * signature file entries. Returns null if no entries were found.
       
   838      */
       
   839     private String[] getMetaInfEntryNames() {
       
   840         synchronized (this) {
       
   841             ensureOpen();
       
   842             if (zsrc.metanames == null) {
       
   843                 return null;
       
   844             }
       
   845             String[] names = new String[zsrc.metanames.length];
       
   846             byte[] cen = zsrc.cen;
       
   847             for (int i = 0; i < names.length; i++) {
       
   848                 int pos = zsrc.metanames[i];
       
   849                 names[i] = new String(cen, pos + CENHDR, CENNAM(cen, pos),
       
   850                                       StandardCharsets.UTF_8);
       
   851             }
       
   852             return names;
       
   853         }
       
   854     }
       
   855 
       
   856     private static boolean isWindows;
       
   857     static {
       
   858         SharedSecrets.setJavaUtilZipFileAccess(
       
   859             new JavaUtilZipFileAccess() {
       
   860                 public boolean startsWithLocHeader(ZipFile zip) {
       
   861                     return zip.zsrc.startsWithLoc;
       
   862                 }
       
   863                 public String[] getMetaInfEntryNames(ZipFile zip) {
       
   864                     return zip.getMetaInfEntryNames();
       
   865                 }
       
   866              }
       
   867         );
       
   868         isWindows = VM.getSavedProperty("os.name").contains("Windows");
       
   869     }
       
   870 
       
   871     private static class Source {
       
   872         private final Key key;               // the key in files
       
   873         private int refs = 1;
       
   874 
       
   875         private RandomAccessFile zfile;      // zfile of the underlying zip file
       
   876         private byte[] cen;                  // CEN & ENDHDR
       
   877         private long locpos;                 // position of first LOC header (usually 0)
       
   878         private byte[] comment;              // zip file comment
       
   879                                              // list of meta entries in META-INF dir
       
   880         private int[] metanames;
       
   881         private final boolean startsWithLoc; // true, if zip file starts with LOCSIG (usually true)
       
   882 
       
   883         // A Hashmap for all entries.
       
   884         //
       
   885         // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR,
       
   886         // We might have a lot of these in a typical system. In order to save space we don't
       
   887         // keep the name in memory, but merely remember a 32 bit {@code hash} value of the
       
   888         // entry name and its offset {@code pos} in the central directory hdeader.
       
   889         //
       
   890         // private static class Entry {
       
   891         //     int hash;       // 32 bit hashcode on name
       
   892         //     int next;       // hash chain: index into entries
       
   893         //     int pos;        // Offset of central directory file header
       
   894         // }
       
   895         // private Entry[] entries;             // array of hashed cen entry
       
   896         //
       
   897         // To reduce the total size of entries further, we use a int[] here to store 3 "int"
       
   898         // {@code hash}, {@code next and {@code "pos for each entry. The entry can then be
       
   899         // referred by their index of their positions in the {@code entries}.
       
   900         //
       
   901         private int[] entries;                  // array of hashed cen entry
       
   902         private int addEntry(int index, int hash, int next, int pos) {
       
   903             entries[index++] = hash;
       
   904             entries[index++] = next;
       
   905             entries[index++] = pos;
       
   906             return index;
       
   907         }
       
   908         private int getEntryHash(int index) { return entries[index]; }
       
   909         private int getEntryNext(int index) { return entries[index + 1]; }
       
   910         private int getEntryPos(int index)  { return entries[index + 2]; }
       
   911         private static final int ZIP_ENDCHAIN  = -1;
       
   912         private int total;                   // total number of entries
       
   913         private int[] table;                 // Hash chain heads: indexes into entries
       
   914         private int tablelen;                // number of hash heads
       
   915 
       
   916         private static class Key {
       
   917             BasicFileAttributes attrs;
       
   918             File file;
       
   919 
       
   920             public Key(File file, BasicFileAttributes attrs) {
       
   921                 this.attrs = attrs;
       
   922                 this.file = file;
       
   923             }
       
   924 
       
   925             public int hashCode() {
       
   926                 long t = attrs.lastModifiedTime().toMillis();
       
   927                 return ((int)(t ^ (t >>> 32))) + file.hashCode();
       
   928             }
       
   929 
       
   930             public boolean equals(Object obj) {
       
   931                 if (obj instanceof Key) {
       
   932                     Key key = (Key)obj;
       
   933                     if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) {
       
   934                         return false;
       
   935                     }
       
   936                     Object fk = attrs.fileKey();
       
   937                     if (fk != null) {
       
   938                         return  fk.equals(key.attrs.fileKey());
       
   939                     } else {
       
   940                         return file.equals(key.file);
       
   941                     }
       
   942                 }
       
   943                 return false;
       
   944             }
       
   945         }
       
   946         private static final HashMap<Key, Source> files = new HashMap<>();
       
   947 
       
   948 
       
   949         public static Source get(File file, boolean toDelete) throws IOException {
       
   950             Key key = new Key(file,
       
   951                               Files.readAttributes(file.toPath(), BasicFileAttributes.class));
       
   952             Source src = null;
       
   953             synchronized (files) {
       
   954                 src = files.get(key);
       
   955                 if (src != null) {
       
   956                     src.refs++;
       
   957                     return src;
       
   958                 }
       
   959             }
       
   960             src = new Source(key, toDelete);
       
   961 
       
   962             synchronized (files) {
       
   963                 if (files.containsKey(key)) {    // someone else put in first
       
   964                     src.close();                 // close the newly created one
       
   965                     src = files.get(key);
       
   966                     src.refs++;
       
   967                     return src;
       
   968                 }
       
   969                 files.put(key, src);
       
   970                 return src;
       
   971             }
       
   972         }
       
   973 
       
   974         private static void close(Source src) throws IOException {
       
   975             synchronized (files) {
       
   976                 if (--src.refs == 0) {
       
   977                     files.remove(src.key);
       
   978                     src.close();
       
   979                 }
       
   980             }
       
   981         }
       
   982 
       
   983         private Source(Key key, boolean toDelete) throws IOException {
       
   984             this.key = key;
       
   985             if (toDelete) {
       
   986                 if (isWindows) {
       
   987                     this.zfile = SharedSecrets.getJavaIORandomAccessFileAccess()
       
   988                                               .openAndDelete(key.file, "r");
       
   989                 } else {
       
   990                     this.zfile = new RandomAccessFile(key.file, "r");
       
   991                     key.file.delete();
       
   992                 }
       
   993             } else {
       
   994                 this.zfile = new RandomAccessFile(key.file, "r");
       
   995             }
       
   996             try {
       
   997                 initCEN(-1);
       
   998                 byte[] buf = new byte[4];
       
   999                 readFullyAt(buf, 0, 4, 0);
       
  1000                 this.startsWithLoc = (LOCSIG(buf) == LOCSIG);
       
  1001             } catch (IOException x) {
       
  1002                 try {
       
  1003                     this.zfile.close();
       
  1004                 } catch (IOException xx) {}
       
  1005                 throw x;
       
  1006             }
       
  1007         }
       
  1008 
       
  1009         private void close() throws IOException {
       
  1010             zfile.close();
       
  1011             zfile = null;
       
  1012             cen = null;
       
  1013             entries = null;
       
  1014             table = null;
       
  1015             metanames = null;
       
  1016         }
       
  1017 
       
  1018         private static final int BUF_SIZE = 8192;
       
  1019         private final int readFullyAt(byte[] buf, int off, int len, long pos)
       
  1020             throws IOException
       
  1021         {
       
  1022             synchronized(zfile) {
       
  1023                 zfile.seek(pos);
       
  1024                 int N = len;
       
  1025                 while (N > 0) {
       
  1026                     int n = Math.min(BUF_SIZE, N);
       
  1027                     zfile.readFully(buf, off, n);
       
  1028                     off += n;
       
  1029                     N -= n;
       
  1030                 }
       
  1031                 return len;
       
  1032             }
       
  1033         }
       
  1034 
       
  1035         private final int readAt(byte[] buf, int off, int len, long pos)
       
  1036             throws IOException
       
  1037         {
       
  1038             synchronized(zfile) {
       
  1039                 zfile.seek(pos);
       
  1040                 return zfile.read(buf, off, len);
       
  1041             }
       
  1042         }
       
  1043 
       
  1044         private static final int hashN(byte[] a, int off, int len) {
       
  1045             int h = 1;
       
  1046             while (len-- > 0) {
       
  1047                 h = 31 * h + a[off++];
       
  1048             }
       
  1049             return h;
       
  1050         }
       
  1051 
       
  1052         private static final int hash_append(int hash, byte b) {
       
  1053             return hash * 31 + b;
       
  1054         }
       
  1055 
       
  1056         private static class End {
       
  1057             int  centot;     // 4 bytes
       
  1058             long cenlen;     // 4 bytes
       
  1059             long cenoff;     // 4 bytes
       
  1060             long endpos;     // 4 bytes
       
  1061         }
       
  1062 
       
  1063         /*
       
  1064          * Searches for end of central directory (END) header. The contents of
       
  1065          * the END header will be read and placed in endbuf. Returns the file
       
  1066          * position of the END header, otherwise returns -1 if the END header
       
  1067          * was not found or an error occurred.
       
  1068          */
       
  1069         private End findEND() throws IOException {
       
  1070             long ziplen = zfile.length();
       
  1071             if (ziplen <= 0)
       
  1072                 zerror("zip file is empty");
       
  1073             End end = new End();
       
  1074             byte[] buf = new byte[READBLOCKSZ];
       
  1075             long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0;
       
  1076             long minPos = minHDR - (buf.length - ENDHDR);
       
  1077             for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) {
       
  1078                 int off = 0;
       
  1079                 if (pos < 0) {
       
  1080                     // Pretend there are some NUL bytes before start of file
       
  1081                     off = (int)-pos;
       
  1082                     Arrays.fill(buf, 0, off, (byte)0);
       
  1083                 }
       
  1084                 int len = buf.length - off;
       
  1085                 if (readFullyAt(buf, off, len, pos + off) != len ) {
       
  1086                     zerror("zip END header not found");
       
  1087                 }
       
  1088                 // Now scan the block backwards for END header signature
       
  1089                 for (int i = buf.length - ENDHDR; i >= 0; i--) {
       
  1090                     if (buf[i+0] == (byte)'P'    &&
       
  1091                         buf[i+1] == (byte)'K'    &&
       
  1092                         buf[i+2] == (byte)'\005' &&
       
  1093                         buf[i+3] == (byte)'\006') {
       
  1094                         // Found ENDSIG header
       
  1095                         byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR);
       
  1096                         end.centot = ENDTOT(endbuf);
       
  1097                         end.cenlen = ENDSIZ(endbuf);
       
  1098                         end.cenoff = ENDOFF(endbuf);
       
  1099                         end.endpos = pos + i;
       
  1100                         int comlen = ENDCOM(endbuf);
       
  1101                         if (end.endpos + ENDHDR + comlen != ziplen) {
       
  1102                             // ENDSIG matched, however the size of file comment in it does
       
  1103                             // not match the real size. One "common" cause for this problem
       
  1104                             // is some "extra" bytes are padded at the end of the zipfile.
       
  1105                             // Let's do some extra verification, we don't care about the
       
  1106                             // performance in this situation.
       
  1107                             byte[] sbuf = new byte[4];
       
  1108                             long cenpos = end.endpos - end.cenlen;
       
  1109                             long locpos = cenpos - end.cenoff;
       
  1110                             if  (cenpos < 0 ||
       
  1111                                  locpos < 0 ||
       
  1112                                  readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 ||
       
  1113                                  GETSIG(sbuf) != CENSIG ||
       
  1114                                  readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 ||
       
  1115                                  GETSIG(sbuf) != LOCSIG) {
       
  1116                                 continue;
       
  1117                             }
       
  1118                         }
       
  1119                         if (comlen > 0) {    // this zip file has comlen
       
  1120                             comment = new byte[comlen];
       
  1121                             if (readFullyAt(comment, 0, comlen, end.endpos + ENDHDR) != comlen) {
       
  1122                                 zerror("zip comment read failed");
       
  1123                             }
       
  1124                         }
       
  1125                         if (end.cenlen == ZIP64_MAGICVAL ||
       
  1126                             end.cenoff == ZIP64_MAGICVAL ||
       
  1127                             end.centot == ZIP64_MAGICCOUNT)
       
  1128                         {
       
  1129                             // need to find the zip64 end;
       
  1130                             try {
       
  1131                                 byte[] loc64 = new byte[ZIP64_LOCHDR];
       
  1132                                 if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
       
  1133                                     != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) {
       
  1134                                     return end;
       
  1135                                 }
       
  1136                                 long end64pos = ZIP64_LOCOFF(loc64);
       
  1137                                 byte[] end64buf = new byte[ZIP64_ENDHDR];
       
  1138                                 if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
       
  1139                                     != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) {
       
  1140                                     return end;
       
  1141                                 }
       
  1142                                 // end64 found, re-calcualte everything.
       
  1143                                 end.cenlen = ZIP64_ENDSIZ(end64buf);
       
  1144                                 end.cenoff = ZIP64_ENDOFF(end64buf);
       
  1145                                 end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g
       
  1146                                 end.endpos = end64pos;
       
  1147                             } catch (IOException x) {}    // no zip64 loc/end
       
  1148                         }
       
  1149                         return end;
       
  1150                     }
       
  1151                 }
       
  1152             }
       
  1153             zerror("zip END header not found");
       
  1154             return null; //make compiler happy
       
  1155         }
       
  1156 
       
  1157         // Reads zip file central directory.
       
  1158         private void initCEN(int knownTotal) throws IOException {
       
  1159             if (knownTotal == -1) {
       
  1160                 End end = findEND();
       
  1161                 if (end.endpos == 0) {
       
  1162                     locpos = 0;
       
  1163                     total = 0;
       
  1164                     entries  = new int[0];
       
  1165                     cen = null;
       
  1166                     return;         // only END header present
       
  1167                 }
       
  1168                 if (end.cenlen > end.endpos)
       
  1169                     zerror("invalid END header (bad central directory size)");
       
  1170                 long cenpos = end.endpos - end.cenlen;     // position of CEN table
       
  1171                 // Get position of first local file (LOC) header, taking into
       
  1172                 // account that there may be a stub prefixed to the zip file.
       
  1173                 locpos = cenpos - end.cenoff;
       
  1174                 if (locpos < 0) {
       
  1175                     zerror("invalid END header (bad central directory offset)");
       
  1176                 }
       
  1177                 // read in the CEN and END
       
  1178                 cen = new byte[(int)(end.cenlen + ENDHDR)];
       
  1179                 if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) {
       
  1180                     zerror("read CEN tables failed");
       
  1181                 }
       
  1182                 total = end.centot;
       
  1183             } else {
       
  1184                 total = knownTotal;
       
  1185             }
       
  1186             // hash table for entries
       
  1187             entries  = new int[total * 3];
       
  1188             tablelen = ((total/2) | 1); // Odd -> fewer collisions
       
  1189             table    =  new int[tablelen];
       
  1190             Arrays.fill(table, ZIP_ENDCHAIN);
       
  1191             int idx = 0;
       
  1192             int hash = 0;
       
  1193             int next = -1;
       
  1194 
       
  1195             // list for all meta entries
       
  1196             ArrayList<Integer> metanamesList = null;
       
  1197 
       
  1198             // Iterate through the entries in the central directory
       
  1199             int i = 0;
       
  1200             int hsh = 0;
       
  1201             int pos = 0;
       
  1202             int limit = cen.length - ENDHDR;
       
  1203             while (pos + CENHDR  <= limit) {
       
  1204                 if (i >= total) {
       
  1205                     // This will only happen if the zip file has an incorrect
       
  1206                     // ENDTOT field, which usually means it contains more than
       
  1207                     // 65535 entries.
       
  1208                     initCEN(countCENHeaders(cen, limit));
       
  1209                     return;
       
  1210                 }
       
  1211                 if (CENSIG(cen, pos) != CENSIG)
       
  1212                     zerror("invalid CEN header (bad signature)");
       
  1213                 int method = CENHOW(cen, pos);
       
  1214                 int nlen   = CENNAM(cen, pos);
       
  1215                 int elen   = CENEXT(cen, pos);
       
  1216                 int clen   = CENCOM(cen, pos);
       
  1217                 if ((CENFLG(cen, pos) & 1) != 0)
       
  1218                     zerror("invalid CEN header (encrypted entry)");
       
  1219                 if (method != STORED && method != DEFLATED)
       
  1220                     zerror("invalid CEN header (bad compression method: " + method + ")");
       
  1221                 if (pos + CENHDR + nlen > limit)
       
  1222                     zerror("invalid CEN header (bad header size)");
       
  1223                 // Record the CEN offset and the name hash in our hash cell.
       
  1224                 hash = hashN(cen, pos + CENHDR, nlen);
       
  1225                 hsh = (hash & 0x7fffffff) % tablelen;
       
  1226                 next = table[hsh];
       
  1227                 table[hsh] = idx;
       
  1228                 idx = addEntry(idx, hash, next, pos);
       
  1229                 // Adds name to metanames.
       
  1230                 if (isMetaName(cen, pos + CENHDR, nlen)) {
       
  1231                     if (metanamesList == null)
       
  1232                         metanamesList = new ArrayList<>(4);
       
  1233                     metanamesList.add(pos);
       
  1234                 }
       
  1235                 // skip ext and comment
       
  1236                 pos += (CENHDR + nlen + elen + clen);
       
  1237                 i++;
       
  1238             }
       
  1239             total = i;
       
  1240             if (metanamesList != null) {
       
  1241                 metanames = new int[metanamesList.size()];
       
  1242                 for (int j = 0, len = metanames.length; j < len; j++) {
       
  1243                     metanames[j] = metanamesList.get(j);
       
  1244                 }
       
  1245             }
       
  1246             if (pos + ENDHDR != cen.length) {
       
  1247                 zerror("invalid CEN header (bad header size)");
       
  1248             }
       
  1249         }
       
  1250 
       
  1251         private static void zerror(String msg) throws ZipException {
       
  1252             throw new ZipException(msg);
       
  1253         }
       
  1254 
       
  1255         /*
       
  1256          * Returns the {@code pos} of the zip cen entry corresponding to the
       
  1257          * specified entry name, or -1 if not found.
       
  1258          */
       
  1259         private int getEntryPos(byte[] name, boolean addSlash) {
       
  1260             if (total == 0) {
       
  1261                 return -1;
       
  1262             }
       
  1263             int hsh = hashN(name, 0, name.length);
       
  1264             int idx = table[(hsh & 0x7fffffff) % tablelen];
       
  1265             /*
       
  1266              * This while loop is an optimization where a double lookup
       
  1267              * for name and name+/ is being performed. The name char
       
  1268              * array has enough room at the end to try again with a
       
  1269              * slash appended if the first table lookup does not succeed.
       
  1270              */
       
  1271             while(true) {
       
  1272                 /*
       
  1273                  * Search down the target hash chain for a entry whose
       
  1274                  * 32 bit hash matches the hashed name.
       
  1275                  */
       
  1276                 while (idx != ZIP_ENDCHAIN) {
       
  1277                     if (getEntryHash(idx) == hsh) {
       
  1278                         // The CEN name must match the specfied one
       
  1279                         int pos = getEntryPos(idx);
       
  1280                         if (name.length == CENNAM(cen, pos)) {
       
  1281                             boolean matched = true;
       
  1282                             int nameoff = pos + CENHDR;
       
  1283                             for (int i = 0; i < name.length; i++) {
       
  1284                                 if (name[i] != cen[nameoff++]) {
       
  1285                                     matched = false;
       
  1286                                     break;
       
  1287                                 }
       
  1288                             }
       
  1289                             if (matched) {
       
  1290                                 return pos;
       
  1291                             }
       
  1292                          }
       
  1293                     }
       
  1294                     idx = getEntryNext(idx);
       
  1295                 }
       
  1296                 /* If not addSlash, or slash is already there, we are done */
       
  1297                 if (!addSlash  || name.length == 0 || name[name.length - 1] == '/') {
       
  1298                      return -1;
       
  1299                 }
       
  1300                 /* Add slash and try once more */
       
  1301                 name = Arrays.copyOf(name, name.length + 1);
       
  1302                 name[name.length - 1] = '/';
       
  1303                 hsh = hash_append(hsh, (byte)'/');
       
  1304                 //idx = table[hsh % tablelen];
       
  1305                 idx = table[(hsh & 0x7fffffff) % tablelen];
       
  1306                 addSlash = false;
       
  1307             }
       
  1308         }
       
  1309 
       
  1310         /**
       
  1311          * Returns true if the bytes represent a non-directory name
       
  1312          * beginning with "META-INF/", disregarding ASCII case.
       
  1313          */
       
  1314         private static boolean isMetaName(byte[] name, int off, int len) {
       
  1315             // Use the "oldest ASCII trick in the book"
       
  1316             return len > 9                     // "META-INF/".length()
       
  1317                 && name[off + len - 1] != '/'  // non-directory
       
  1318                 && (name[off++] | 0x20) == 'm'
       
  1319                 && (name[off++] | 0x20) == 'e'
       
  1320                 && (name[off++] | 0x20) == 't'
       
  1321                 && (name[off++] | 0x20) == 'a'
       
  1322                 && (name[off++]       ) == '-'
       
  1323                 && (name[off++] | 0x20) == 'i'
       
  1324                 && (name[off++] | 0x20) == 'n'
       
  1325                 && (name[off++] | 0x20) == 'f'
       
  1326                 && (name[off]         ) == '/';
       
  1327         }
       
  1328 
       
  1329         /**
       
  1330          * Returns the number of CEN headers in a central directory.
       
  1331          * Will not throw, even if the zip file is corrupt.
       
  1332          *
       
  1333          * @param cen copy of the bytes in a zip file's central directory
       
  1334          * @param size number of bytes in central directory
       
  1335          */
       
  1336         private static int countCENHeaders(byte[] cen, int size) {
       
  1337             int count = 0;
       
  1338             for (int p = 0;
       
  1339                  p + CENHDR <= size;
       
  1340                  p += CENHDR + CENNAM(cen, p) + CENEXT(cen, p) + CENCOM(cen, p))
       
  1341                 count++;
       
  1342             return count;
       
  1343         }
       
  1344     }
       
  1345 }