src/java.base/share/classes/java/util/zip/ZipFile.java

Print this page




  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 
  54 import java.util.function.Consumer;
  55 import java.util.function.Function;
  56 import java.util.function.IntFunction;
  57 import java.util.jar.JarEntry;
  58 import java.util.stream.Stream;
  59 import java.util.stream.StreamSupport;
  60 import jdk.internal.misc.JavaUtilZipFileAccess;
  61 import jdk.internal.misc.SharedSecrets;
  62 import jdk.internal.misc.VM;
  63 import jdk.internal.perf.PerfCounter;

  64 
  65 import static java.util.zip.ZipConstants.*;
  66 import static java.util.zip.ZipConstants64.*;
  67 import static java.util.zip.ZipUtils.*;
  68 
  69 /**
  70  * This class is used to read entries from a zip file.
  71  *
  72  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
  73  * or method in this class will cause a {@link NullPointerException} to be
  74  * thrown.
  75  *








  76  * @author      David Connelly
  77  * @since 1.1
  78  */
  79 public
  80 class ZipFile implements ZipConstants, Closeable {
  81 
  82     private final String name;     // zip file name
  83     private volatile boolean closeRequested;
  84     private Source zsrc;
  85     private ZipCoder zc;
  86 







  87     private static final int STORED = ZipEntry.STORED;
  88     private static final int DEFLATED = ZipEntry.DEFLATED;
  89 
  90     /**
  91      * Mode flag to open a zip file for reading.
  92      */
  93     public static final int OPEN_READ = 0x1;
  94 
  95     /**
  96      * Mode flag to open a zip file and mark it for deletion.  The file will be
  97      * deleted some time between the moment that it is opened and the moment
  98      * that it is closed, but its contents will remain accessible via the
  99      * {@code ZipFile} object until either the close method is invoked or the
 100      * virtual machine exits.
 101      */
 102     public static final int OPEN_DELETE = 0x4;
 103 
 104     /**
 105      * Opens a zip file for reading.
 106      *


 197      * @see SecurityManager#checkRead(java.lang.String)
 198      *
 199      * @since 1.7
 200      */
 201     public ZipFile(File file, int mode, Charset charset) throws IOException
 202     {
 203         if (((mode & OPEN_READ) == 0) ||
 204             ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
 205             throw new IllegalArgumentException("Illegal mode: 0x"+
 206                                                Integer.toHexString(mode));
 207         }
 208         String name = file.getPath();
 209         SecurityManager sm = System.getSecurityManager();
 210         if (sm != null) {
 211             sm.checkRead(name);
 212             if ((mode & OPEN_DELETE) != 0) {
 213                 sm.checkDelete(name);
 214             }
 215         }
 216         Objects.requireNonNull(charset, "charset");

 217         this.zc = ZipCoder.get(charset);
 218         this.name = name;
 219         long t0 = System.nanoTime();
 220         this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0);


 221         PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
 222         PerfCounter.getZipFileCount().increment();
 223     }
 224 
 225     /**
 226      * Opens a zip file for reading.
 227      *
 228      * <p>First, if there is a security manager, its {@code checkRead}
 229      * method is called with the {@code name} argument as its argument
 230      * to ensure the read is allowed.
 231      *
 232      * @param name the name of the zip file
 233      * @param charset
 234      *        the {@linkplain java.nio.charset.Charset charset} to
 235      *        be used to decode the ZIP entry name and comment that are not
 236      *        encoded by using UTF-8 encoding (indicated by entry's general
 237      *        purpose flag).
 238      *
 239      * @throws ZipException if a ZIP format error has occurred
 240      * @throws IOException if an I/O error has occurred


 267      *
 268      * @since 1.7
 269      */
 270     public ZipFile(File file, Charset charset) throws IOException
 271     {
 272         this(file, OPEN_READ, charset);
 273     }
 274 
 275     /**
 276      * Returns the zip file comment, or null if none.
 277      *
 278      * @return the comment string for the zip file, or null if none
 279      *
 280      * @throws IllegalStateException if the zip file has been closed
 281      *
 282      * @since 1.7
 283      */
 284     public String getComment() {
 285         synchronized (this) {
 286             ensureOpen();
 287             if (zsrc.comment == null) {
 288                 return null;
 289             }
 290             return zc.toString(zsrc.comment);
 291         }
 292     }
 293 
 294     /**
 295      * Returns the zip file entry for the specified name, or null
 296      * if not found.
 297      *
 298      * @param name the name of the entry
 299      * @return the zip file entry, or null if not found
 300      * @throws IllegalStateException if the zip file has been closed
 301      */
 302     public ZipEntry getEntry(String name) {
 303         return getEntry(name, ZipEntry::new);
 304     }
 305 
 306     /*
 307      * Returns the zip file entry for the specified name, or null
 308      * if not found.
 309      *
 310      * @param name the name of the entry
 311      * @param func the function that creates the returned entry
 312      *
 313      * @return the zip file entry, or null if not found
 314      * @throws IllegalStateException if the zip file has been closed
 315      */
 316     private ZipEntry getEntry(String name, Function<String, ? extends ZipEntry> func) {
 317         Objects.requireNonNull(name, "name");
 318         synchronized (this) {
 319             ensureOpen();
 320             byte[] bname = zc.getBytes(name);
 321             int pos = zsrc.getEntryPos(bname, true);
 322             if (pos != -1) {
 323                 return getZipEntry(name, bname, pos, func);
 324             }
 325         }
 326         return null;
 327     }
 328 
 329     // The outstanding inputstreams that need to be closed,
 330     // mapped to the inflater objects they use.
 331     private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
 332 
 333     /**
 334      * Returns an input stream for reading the contents of the specified
 335      * zip file entry.
 336      * <p>
 337      * Closing this ZIP file will, in turn, close all input streams that
 338      * have been returned by invocations of this method.
 339      *
 340      * @param entry the zip file entry
 341      * @return the input stream for reading the contents of the specified
 342      * zip file entry.
 343      * @throws ZipException if a ZIP format error has occurred
 344      * @throws IOException if an I/O error has occurred
 345      * @throws IllegalStateException if the zip file has been closed
 346      */
 347     public InputStream getInputStream(ZipEntry entry) throws IOException {
 348         Objects.requireNonNull(entry, "entry");
 349         int pos = -1;
 350         ZipFileInputStream in = null;


 351         synchronized (this) {
 352             ensureOpen();
 353             if (Objects.equals(lastEntryName, entry.name)) {
 354                 pos = lastEntryPos;
 355             } else if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
 356                 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
 357             } else {
 358                 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
 359             }
 360             if (pos == -1) {
 361                 return null;
 362             }
 363             in = new ZipFileInputStream(zsrc.cen, pos);
 364             switch (CENHOW(zsrc.cen, pos)) {
 365             case STORED:
 366                 synchronized (streams) {
 367                     streams.put(in, null);
 368                 }
 369                 return in;
 370             case DEFLATED:
 371                 // Inflater likes a bit of slack
 372                 // MORE: Compute good size for inflater stream:
 373                 long size = CENLEN(zsrc.cen, pos) + 2;
 374                 if (size > 65536) {
 375                     size = 8192;
 376                 }
 377                 if (size <= 0) {
 378                     size = 4096;
 379                 }
 380                 Inflater inf = getInflater();
 381                 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
 382                 synchronized (streams) {
 383                     streams.put(is, inf);
 384                 }
 385                 return is;
 386             default:
 387                 throw new ZipException("invalid compression method");
 388             }
 389         }
 390     }
 391 
 392     private class ZipFileInflaterInputStream extends InflaterInputStream {
 393         private volatile boolean closeRequested;
 394         private boolean eof = false;






 395 
 396         ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
 397                 int size) {

 398             super(zfin, inf, size);


 399         }
 400 
 401         public void close() throws IOException {
 402             if (closeRequested)
 403                 return;
 404             closeRequested = true;
 405 
 406             super.close();
 407             Inflater inf;
 408             synchronized (streams) {
 409                 inf = streams.remove(this);
 410             }
 411             if (inf != null) {
 412                 releaseInflater(inf);
 413             }

 414         }
 415 
 416         // Override fill() method to provide an extra "dummy" byte
 417         // at the end of the input stream. This is required when
 418         // using the "nowrap" Inflater option.
 419         protected void fill() throws IOException {
 420             if (eof) {
 421                 throw new EOFException("Unexpected end of ZLIB input stream");
 422             }
 423             len = in.read(buf, 0, buf.length);
 424             if (len == -1) {
 425                 buf[0] = 0;
 426                 len = 1;
 427                 eof = true;
 428             }
 429             inf.setInput(buf, 0, len);
 430         }
 431 
 432         public int available() throws IOException {
 433             if (closeRequested)
 434                 return 0;
 435             long avail = ((ZipFileInputStream)in).size() - inf.getBytesWritten();
 436             return (avail > (long) Integer.MAX_VALUE ?
 437                     Integer.MAX_VALUE : (int) avail);
 438         }
 439 
 440         @SuppressWarnings("deprecation")
 441         protected void finalize() throws Throwable {
 442             close();
 443         }
 444     }
 445 
 446     /*
 447      * Gets an inflater from the list of available inflaters or allocates
 448      * a new one.
 449      */
 450     private Inflater getInflater() {
 451         Inflater inf;
 452         synchronized (inflaterCache) {
 453             while ((inf = inflaterCache.poll()) != null) {
 454                 if (!inf.ended()) {
 455                     return inf;
 456                 }
 457             }
 458         }
 459         return new Inflater(true);
 460     }
 461 
 462     /*
 463      * Releases the specified inflater to the list of available inflaters.
 464      */
 465     private void releaseInflater(Inflater inf) {
 466         if (!inf.ended()) {
 467             inf.reset();
 468             synchronized (inflaterCache) {
 469                 inflaterCache.add(inf);
 470             }
 471         }
 472     }
 473 
 474     // List of available Inflater objects for decompression
 475     private final Deque<Inflater> inflaterCache = new ArrayDeque<>();
 476 
 477     /**
 478      * Returns the path name of the ZIP file.
 479      * @return the path name of the ZIP file
 480      */
 481     public String getName() {
 482         return name;
 483     }
 484 
 485     private class ZipEntryIterator<T extends ZipEntry>
 486             implements Enumeration<T>, Iterator<T> {
 487 
 488         private int i = 0;
 489         private final int entryCount;
 490         private final Function<String, T> gen;
 491 
 492         public ZipEntryIterator(int entryCount, Function<String, T> gen) {
 493             this.entryCount = entryCount;
 494             this.gen = gen;
 495         }
 496 


 501 
 502         @Override
 503         public boolean hasNext() {
 504             return i < entryCount;
 505         }
 506 
 507         @Override
 508         public T nextElement() {
 509             return next();
 510         }
 511 
 512         @Override
 513         @SuppressWarnings("unchecked")
 514         public T  next() {
 515             synchronized (ZipFile.this) {
 516                 ensureOpen();
 517                 if (!hasNext()) {
 518                     throw new NoSuchElementException();
 519                 }
 520                 // each "entry" has 3 ints in table entries
 521                 return (T)getZipEntry(null, null, zsrc.getEntryPos(i++ * 3), gen);
 522             }
 523         }
 524 
 525         @Override
 526         public Iterator<T> asIterator() {
 527             return this;
 528         }
 529     }
 530 
 531     /**
 532      * Returns an enumeration of the ZIP file entries.
 533      * @return an enumeration of the ZIP file entries
 534      * @throws IllegalStateException if the zip file has been closed
 535      */
 536     public Enumeration<? extends ZipEntry> entries() {
 537         synchronized (this) {
 538             ensureOpen();
 539             return new ZipEntryIterator<ZipEntry>(zsrc.total, ZipEntry::new);
 540         }
 541     }
 542 
 543     private Enumeration<JarEntry> entries(Function<String, JarEntry> func) {
 544         synchronized (this) {
 545             ensureOpen();
 546             return new ZipEntryIterator<JarEntry>(zsrc.total, func);
 547         }
 548     }
 549 
 550     private class EntrySpliterator<T> extends Spliterators.AbstractSpliterator<T> {
 551         private int index;
 552         private final int fence;
 553         private final IntFunction<T> gen;
 554 
 555         EntrySpliterator(int index, int fence, IntFunction<T> gen) {
 556             super((long)fence,
 557                   Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE |
 558                   Spliterator.NONNULL);
 559             this.index = index;
 560             this.fence = fence;
 561             this.gen = gen;
 562         }
 563 
 564         @Override
 565         public boolean tryAdvance(Consumer<? super T> action) {
 566             if (action == null)
 567                 throw new NullPointerException();
 568             if (index >= 0 && index < fence) {
 569                 synchronized (ZipFile.this) {
 570                     ensureOpen();
 571                     action.accept(gen.apply(zsrc.getEntryPos(index++ * 3)));
 572                 }
 573                 return true;
 574             }
 575             return false;
 576         }
 577     }
 578 
 579     /**
 580      * Returns an ordered {@code Stream} over the ZIP file entries.
 581      *
 582      * Entries appear in the {@code Stream} in the order they appear in
 583      * the central directory of the ZIP file.
 584      *
 585      * @return an ordered {@code Stream} of entries in this ZIP file
 586      * @throws IllegalStateException if the zip file has been closed
 587      * @since 1.8
 588      */
 589     public Stream<? extends ZipEntry> stream() {
 590         synchronized (this) {
 591             ensureOpen();
 592             return StreamSupport.stream(new EntrySpliterator<>(0, zsrc.total,
 593                 pos -> getZipEntry(null, null, pos, ZipEntry::new)), false);
 594        }
 595     }
 596 
 597     private String getEntryName(int pos) {
 598         byte[] cen = zsrc.cen;
 599         int nlen = CENNAM(cen, pos);
 600         int clen = CENCOM(cen, pos);
 601         int flag = CENFLG(cen, pos);
 602         if (!zc.isUTF8() && (flag & EFS) != 0) {
 603             return zc.toStringUTF8(cen, pos + CENHDR, nlen);
 604         } else {
 605             return zc.toString(cen, pos + CENHDR, nlen);
 606         }
 607     }
 608 
 609     /*
 610      * Returns an ordered {@code Stream} over the zip file entry names.
 611      *
 612      * Entry names appear in the {@code Stream} in the order they appear in
 613      * the central directory of the ZIP file.
 614      *
 615      * @return an ordered {@code Stream} of entry names in this zip file
 616      * @throws IllegalStateException if the zip file has been closed
 617      * @since 10
 618      */
 619     private Stream<String> entryNameStream() {
 620         synchronized (this) {
 621             ensureOpen();
 622             return StreamSupport.stream(
 623                 new EntrySpliterator<>(0, zsrc.total, this::getEntryName), false);
 624         }
 625     }
 626 
 627     /*
 628      * Returns an ordered {@code Stream} over the zip file entries.
 629      *
 630      * Entries appear in the {@code Stream} in the order they appear in
 631      * the central directory of the jar file.
 632      *
 633      * @param func the function that creates the returned entry
 634      * @return an ordered {@code Stream} of entries in this zip file
 635      * @throws IllegalStateException if the zip file has been closed
 636      * @since 10
 637      */
 638     private Stream<JarEntry> stream(Function<String, JarEntry> func) {
 639         synchronized (this) {
 640             ensureOpen();
 641             return StreamSupport.stream(new EntrySpliterator<>(0, zsrc.total,
 642                 pos -> (JarEntry)getZipEntry(null, null, pos, func)), false);
 643         }
 644     }
 645 
 646     private String lastEntryName;
 647     private int lastEntryPos;
 648 
 649     /* Checks ensureOpen() before invoke this method */
 650     private ZipEntry getZipEntry(String name, byte[] bname, int pos,
 651                                  Function<String, ? extends ZipEntry> func) {
 652         byte[] cen = zsrc.cen;
 653         int nlen = CENNAM(cen, pos);
 654         int elen = CENEXT(cen, pos);
 655         int clen = CENCOM(cen, pos);
 656         int flag = CENFLG(cen, pos);
 657         if (name == null || bname.length != nlen) {
 658             // to use the entry name stored in cen, if the passed in name is
 659             // (1) null, invoked from iterator, or
 660             // (2) not equal to the name stored, a slash is appended during
 661             // getEntryPos() search.
 662             if (!zc.isUTF8() && (flag & EFS) != 0) {
 663                 name = zc.toStringUTF8(cen, pos + CENHDR, nlen);
 664             } else {
 665                 name = zc.toString(cen, pos + CENHDR, nlen);
 666             }
 667         }
 668         ZipEntry e = func.apply(name);    //ZipEntry e = new ZipEntry(name);
 669         e.flag = flag;
 670         e.xdostime = CENTIM(cen, pos);
 671         e.crc = CENCRC(cen, pos);
 672         e.size = CENLEN(cen, pos);


 681             if (!zc.isUTF8() && (flag & EFS) != 0) {
 682                 e.comment = zc.toStringUTF8(cen, start, clen);
 683             } else {
 684                 e.comment = zc.toString(cen, start, clen);
 685             }
 686         }
 687         lastEntryName = e.name;
 688         lastEntryPos = pos;
 689         return e;
 690     }
 691 
 692     /**
 693      * Returns the number of entries in the ZIP file.
 694      *
 695      * @return the number of entries in the ZIP file
 696      * @throws IllegalStateException if the zip file has been closed
 697      */
 698     public int size() {
 699         synchronized (this) {
 700             ensureOpen();
 701             return zsrc.total;





























































































































































 702         }
 703     }
 704 
 705     /**
 706      * Closes the ZIP file.

 707      * <p> Closing this ZIP file will close all of the input streams
 708      * previously returned by invocations of the {@link #getInputStream
 709      * getInputStream} method.
 710      *
 711      * @throws IOException if an I/O error has occurred
 712      */
 713     public void close() throws IOException {
 714         if (closeRequested) {
 715             return;
 716         }
 717         closeRequested = true;
 718 
 719         synchronized (this) {
 720             // Close streams, release their inflaters
 721             synchronized (streams) {
 722                 if (!streams.isEmpty()) {
 723                     Map<InputStream, Inflater> copy = new HashMap<>(streams);
 724                     streams.clear();
 725                     for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) {
 726                         e.getKey().close();
 727                         Inflater inf = e.getValue();
 728                         if (inf != null) {
 729                             inf.end();
 730                         }
 731                     }
 732                 }
 733             }
 734             // Release cached inflaters
 735             synchronized (inflaterCache) {
 736                 Inflater inf;
 737                 while ((inf = inflaterCache.poll()) != null) {
 738                     inf.end();
 739                 }
 740             }
 741             // Release zip src
 742             if (zsrc != null) {
 743                 Source.close(zsrc);
 744                 zsrc = null;
 745             }
 746         }
 747     }
 748 
 749     /**
 750      * Ensures that the system resources held by this ZipFile object are
 751      * released when there are no more references to it.
 752      *
 753      * <p>
 754      * Since the time when GC would invoke this method is undetermined,
 755      * it is strongly recommended that applications invoke the {@code close}
 756      * method as soon they have finished accessing this {@code ZipFile}.
 757      * This will prevent holding up system resources for an undetermined
 758      * length of time.
 759      *
 760      * @deprecated The {@code finalize} method has been deprecated.
 761      *     Subclasses that override {@code finalize} in order to perform cleanup
 762      *     should be modified to use alternative cleanup mechanisms and
 763      *     to remove the overriding {@code finalize} method.
 764      *     When overriding the {@code finalize} method, its implementation must explicitly
 765      *     ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
 766      *     See the specification for {@link Object#finalize()} for further
 767      *     information about migration options.
 768      * @throws IOException if an I/O error has occurred
 769      * @see    java.util.zip.ZipFile#close()
 770      */
 771     @Deprecated(since="9")
 772     protected void finalize() throws IOException {
 773         close();
 774     }
 775 
 776     private void ensureOpen() {
 777         if (closeRequested) {
 778             throw new IllegalStateException("zip file closed");
 779         }
 780         if (zsrc == null) {
 781             throw new IllegalStateException("The object is not initialized.");
 782         }
 783     }
 784 
 785     private void ensureOpenOrZipException() throws IOException {
 786         if (closeRequested) {
 787             throw new ZipException("ZipFile closed");
 788         }
 789     }
 790 
 791     /*
 792      * Inner class implementing the input stream used to read a
 793      * (possibly compressed) zip file entry.
 794      */
 795    private class ZipFileInputStream extends InputStream {
 796         private volatile boolean closeRequested;
 797         private   long pos;     // current position within entry data
 798         protected long rem;     // number of remaining bytes within entry
 799         protected long size;    // uncompressed size of this entry
 800 
 801         ZipFileInputStream(byte[] cen, int cenpos) throws IOException {
 802             rem = CENSIZ(cen, cenpos);
 803             size = CENLEN(cen, cenpos);
 804             pos = CENOFF(cen, cenpos);
 805             // zip64
 806             if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
 807                 pos == ZIP64_MAGICVAL) {
 808                 checkZIP64(cen, cenpos);
 809             }
 810             // negative for lazy initialization, see getDataOffset();
 811             pos = - (pos + ZipFile.this.zsrc.locpos);
 812         }
 813 
 814         private void checkZIP64(byte[] cen, int cenpos) throws IOException {
 815             int off = cenpos + CENHDR + CENNAM(cen, cenpos);
 816             int end = off + CENEXT(cen, cenpos);
 817             while (off + 4 < end) {
 818                 int tag = get16(cen, off);
 819                 int sz = get16(cen, off + 2);
 820                 off += 4;
 821                 if (off + sz > end)         // invalid data
 822                     break;
 823                 if (tag == EXTID_ZIP64) {
 824                     if (size == ZIP64_MAGICVAL) {
 825                         if (sz < 8 || (off + 8) > end)
 826                             break;
 827                         size = get64(cen, off);
 828                         sz -= 8;
 829                         off += 8;
 830                     }
 831                     if (rem == ZIP64_MAGICVAL) {
 832                         if (sz < 8 || (off + 8) > end)
 833                             break;
 834                         rem = get64(cen, off);


 840                             break;
 841                         pos = get64(cen, off);
 842                         sz -= 8;
 843                         off += 8;
 844                     }
 845                     break;
 846                 }
 847                 off += sz;
 848             }
 849         }
 850 
 851        /* The Zip file spec explicitly allows the LOC extra data size to
 852         * be different from the CEN extra data size. Since we cannot trust
 853         * the CEN extra data size, we need to read the LOC to determine
 854         * the entry data offset.
 855         */
 856         private long initDataOffset() throws IOException {
 857             if (pos <= 0) {
 858                 byte[] loc = new byte[LOCHDR];
 859                 pos = -pos;
 860                 int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos);
 861                 if (len != LOCHDR) {
 862                     throw new ZipException("ZipFile error reading zip file");
 863                 }
 864                 if (LOCSIG(loc) != LOCSIG) {
 865                     throw new ZipException("ZipFile invalid LOC header (bad signature)");
 866                 }
 867                 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
 868             }
 869             return pos;
 870         }
 871 
 872         public int read(byte b[], int off, int len) throws IOException {
 873             synchronized (ZipFile.this) {
 874                 ensureOpenOrZipException();
 875                 initDataOffset();
 876                 if (rem == 0) {
 877                     return -1;
 878                 }
 879                 if (len > rem) {
 880                     len = (int) rem;
 881                 }
 882                 if (len <= 0) {
 883                     return 0;
 884                 }
 885                 len = ZipFile.this.zsrc.readAt(b, off, len, pos);
 886                 if (len > 0) {
 887                     pos += len;
 888                     rem -= len;
 889                 }
 890             }
 891             if (rem == 0) {
 892                 close();
 893             }
 894             return len;
 895         }
 896 
 897         public int read() throws IOException {
 898             byte[] b = new byte[1];
 899             if (read(b, 0, 1) == 1) {
 900                 return b[0] & 0xff;
 901             } else {
 902                 return -1;
 903             }
 904         }
 905 


 915             if (rem == 0) {
 916                 close();
 917             }
 918             return n;
 919         }
 920 
 921         public int available() {
 922             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
 923         }
 924 
 925         public long size() {
 926             return size;
 927         }
 928 
 929         public void close() {
 930             if (closeRequested) {
 931                 return;
 932             }
 933             closeRequested = true;
 934             rem = 0;
 935             synchronized (streams) {
 936                 streams.remove(this);
 937             }
 938         }
 939 
 940         @SuppressWarnings("deprecation")
 941         protected void finalize() {
 942             close();
 943         }
 944     }
 945 
 946     /**
 947      * Returns the names of all non-directory entries that begin with
 948      * "META-INF/" (case ignored). This method is used in JarFile, via
 949      * SharedSecrets, as an optimization when looking up manifest and
 950      * signature file entries. Returns null if no entries were found.
 951      */
 952     private String[] getMetaInfEntryNames() {
 953         synchronized (this) {
 954             ensureOpen();

 955             if (zsrc.metanames == null) {
 956                 return null;
 957             }
 958             String[] names = new String[zsrc.metanames.length];
 959             byte[] cen = zsrc.cen;
 960             for (int i = 0; i < names.length; i++) {
 961                 int pos = zsrc.metanames[i];
 962                 names[i] = new String(cen, pos + CENHDR, CENNAM(cen, pos),
 963                                       StandardCharsets.UTF_8);
 964             }
 965             return names;
 966         }
 967     }
 968 
 969     private static boolean isWindows;
 970     static {
 971         SharedSecrets.setJavaUtilZipFileAccess(
 972             new JavaUtilZipFileAccess() {
 973                 @Override
 974                 public boolean startsWithLocHeader(ZipFile zip) {
 975                     return zip.zsrc.startsWithLoc;
 976                 }
 977                 @Override
 978                 public String[] getMetaInfEntryNames(ZipFile zip) {
 979                     return zip.getMetaInfEntryNames();
 980                 }
 981                 @Override
 982                 public JarEntry getEntry(ZipFile zip, String name,
 983                     Function<String, JarEntry> func) {
 984                     return (JarEntry)zip.getEntry(name, func);
 985                 }
 986                 @Override
 987                 public Enumeration<JarEntry> entries(ZipFile zip,
 988                     Function<String, JarEntry> func) {
 989                     return zip.entries(func);
 990                 }
 991                 @Override
 992                 public Stream<JarEntry> stream(ZipFile zip,
 993                     Function<String, JarEntry> func) {
 994                     return zip.stream(func);
 995                 }


1063 
1064             public boolean equals(Object obj) {
1065                 if (obj instanceof Key) {
1066                     Key key = (Key)obj;
1067                     if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) {
1068                         return false;
1069                     }
1070                     Object fk = attrs.fileKey();
1071                     if (fk != null) {
1072                         return  fk.equals(key.attrs.fileKey());
1073                     } else {
1074                         return file.equals(key.file);
1075                     }
1076                 }
1077                 return false;
1078             }
1079         }
1080         private static final HashMap<Key, Source> files = new HashMap<>();
1081 
1082 
1083         public static Source get(File file, boolean toDelete) throws IOException {
1084             Key key = new Key(file,
1085                               Files.readAttributes(file.toPath(), BasicFileAttributes.class));
1086             Source src = null;
1087             synchronized (files) {
1088                 src = files.get(key);
1089                 if (src != null) {
1090                     src.refs++;
1091                     return src;
1092                 }
1093             }
1094             src = new Source(key, toDelete);
1095 
1096             synchronized (files) {
1097                 if (files.containsKey(key)) {    // someone else put in first
1098                     src.close();                 // close the newly created one
1099                     src = files.get(key);
1100                     src.refs++;
1101                     return src;
1102                 }
1103                 files.put(key, src);
1104                 return src;
1105             }
1106         }
1107 
1108         private static void close(Source src) throws IOException {
1109             synchronized (files) {
1110                 if (--src.refs == 0) {
1111                     files.remove(src.key);
1112                     src.close();
1113                 }
1114             }
1115         }
1116 
1117         private Source(Key key, boolean toDelete) throws IOException {
1118             this.key = key;
1119             if (toDelete) {
1120                 if (isWindows) {
1121                     this.zfile = SharedSecrets.getJavaIORandomAccessFileAccess()
1122                                               .openAndDelete(key.file, "r");
1123                 } else {
1124                     this.zfile = new RandomAccessFile(key.file, "r");
1125                     key.file.delete();
1126                 }
1127             } else {
1128                 this.zfile = new RandomAccessFile(key.file, "r");
1129             }
1130             try {




  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.io.UncheckedIOException;
  35 import java.lang.ref.Cleaner.Cleanable;
  36 import java.nio.charset.Charset;
  37 import java.nio.charset.StandardCharsets;
  38 import java.nio.file.attribute.BasicFileAttributes;

  39 import java.nio.file.Files;
  40 
  41 import java.util.ArrayDeque;
  42 import java.util.ArrayList;
  43 import java.util.Arrays;
  44 import java.util.Collections;
  45 import java.util.Deque;
  46 import java.util.Enumeration;
  47 import java.util.HashMap;
  48 import java.util.Iterator;

  49 import java.util.Objects;
  50 import java.util.NoSuchElementException;
  51 import java.util.Set;
  52 import java.util.Spliterator;
  53 import java.util.Spliterators;
  54 import java.util.WeakHashMap;
  55 
  56 import java.util.function.Consumer;
  57 import java.util.function.Function;
  58 import java.util.function.IntFunction;
  59 import java.util.jar.JarEntry;
  60 import java.util.stream.Stream;
  61 import java.util.stream.StreamSupport;
  62 import jdk.internal.misc.JavaUtilZipFileAccess;
  63 import jdk.internal.misc.SharedSecrets;
  64 import jdk.internal.misc.VM;
  65 import jdk.internal.perf.PerfCounter;
  66 import jdk.internal.ref.CleanerFactory;
  67 

  68 import static java.util.zip.ZipConstants64.*;
  69 import static java.util.zip.ZipUtils.*;
  70 
  71 /**
  72  * This class is used to read entries from a zip file.
  73  *
  74  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
  75  * or method in this class will cause a {@link NullPointerException} to be
  76  * thrown.
  77  *
  78  * @apiNote
  79  * To release resources used by this {@code ZipFile}, the {@link #close()} method
  80  * should be called explicitly or by try-with-resources. Subclasses are responsible
  81  * for the cleanup of resources acquired by the subclass. Subclasses that override
  82  * {@link #finalize()} in order to perform cleanup should be modified to use alternative
  83  * cleanup mechanisms such as {@link java.lang.ref.Cleaner} and remove the overriding
  84  * {@code finalize} method.
  85  *
  86  * @author      David Connelly
  87  * @since 1.1
  88  */
  89 public
  90 class ZipFile implements ZipConstants, Closeable {
  91 
  92     private final String name;     // zip file name
  93     private volatile boolean closeRequested;

  94     private ZipCoder zc;
  95 
  96     // The "resource" used by this zip file that needs to be
  97     // cleaned after use.
  98     // a) the input streams that need to be closed
  99     // b) the list of cached Inflater objects
 100     // c) the "native" source of this zip file.
 101     private final CleanableResource res;
 102 
 103     private static final int STORED = ZipEntry.STORED;
 104     private static final int DEFLATED = ZipEntry.DEFLATED;
 105 
 106     /**
 107      * Mode flag to open a zip file for reading.
 108      */
 109     public static final int OPEN_READ = 0x1;
 110 
 111     /**
 112      * Mode flag to open a zip file and mark it for deletion.  The file will be
 113      * deleted some time between the moment that it is opened and the moment
 114      * that it is closed, but its contents will remain accessible via the
 115      * {@code ZipFile} object until either the close method is invoked or the
 116      * virtual machine exits.
 117      */
 118     public static final int OPEN_DELETE = 0x4;
 119 
 120     /**
 121      * Opens a zip file for reading.
 122      *


 213      * @see SecurityManager#checkRead(java.lang.String)
 214      *
 215      * @since 1.7
 216      */
 217     public ZipFile(File file, int mode, Charset charset) throws IOException
 218     {
 219         if (((mode & OPEN_READ) == 0) ||
 220             ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
 221             throw new IllegalArgumentException("Illegal mode: 0x"+
 222                                                Integer.toHexString(mode));
 223         }
 224         String name = file.getPath();
 225         SecurityManager sm = System.getSecurityManager();
 226         if (sm != null) {
 227             sm.checkRead(name);
 228             if ((mode & OPEN_DELETE) != 0) {
 229                 sm.checkDelete(name);
 230             }
 231         }
 232         Objects.requireNonNull(charset, "charset");
 233 
 234         this.zc = ZipCoder.get(charset);
 235         this.name = name;
 236         long t0 = System.nanoTime();
 237 
 238         this.res = CleanableResource.get(this, file, mode); 
 239 
 240         PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
 241         PerfCounter.getZipFileCount().increment();
 242     }
 243 
 244     /**
 245      * Opens a zip file for reading.
 246      *
 247      * <p>First, if there is a security manager, its {@code checkRead}
 248      * method is called with the {@code name} argument as its argument
 249      * to ensure the read is allowed.
 250      *
 251      * @param name the name of the zip file
 252      * @param charset
 253      *        the {@linkplain java.nio.charset.Charset charset} to
 254      *        be used to decode the ZIP entry name and comment that are not
 255      *        encoded by using UTF-8 encoding (indicated by entry's general
 256      *        purpose flag).
 257      *
 258      * @throws ZipException if a ZIP format error has occurred
 259      * @throws IOException if an I/O error has occurred


 286      *
 287      * @since 1.7
 288      */
 289     public ZipFile(File file, Charset charset) throws IOException
 290     {
 291         this(file, OPEN_READ, charset);
 292     }
 293 
 294     /**
 295      * Returns the zip file comment, or null if none.
 296      *
 297      * @return the comment string for the zip file, or null if none
 298      *
 299      * @throws IllegalStateException if the zip file has been closed
 300      *
 301      * @since 1.7
 302      */
 303     public String getComment() {
 304         synchronized (this) {
 305             ensureOpen();
 306             if (res.zsrc.comment == null) {
 307                 return null;
 308             }
 309             return zc.toString(res.zsrc.comment);
 310         }
 311     }
 312 
 313     /**
 314      * Returns the zip file entry for the specified name, or null
 315      * if not found.
 316      *
 317      * @param name the name of the entry
 318      * @return the zip file entry, or null if not found
 319      * @throws IllegalStateException if the zip file has been closed
 320      */
 321     public ZipEntry getEntry(String name) {
 322         return getEntry(name, ZipEntry::new);
 323     }
 324 
 325     /*
 326      * Returns the zip file entry for the specified name, or null
 327      * if not found.
 328      *
 329      * @param name the name of the entry
 330      * @param func the function that creates the returned entry
 331      *
 332      * @return the zip file entry, or null if not found
 333      * @throws IllegalStateException if the zip file has been closed
 334      */
 335     private ZipEntry getEntry(String name, Function<String, ? extends ZipEntry> func) {
 336         Objects.requireNonNull(name, "name");
 337         synchronized (this) {
 338             ensureOpen();
 339             byte[] bname = zc.getBytes(name);
 340             int pos = res.zsrc.getEntryPos(bname, true);
 341             if (pos != -1) {
 342                 return getZipEntry(name, bname, pos, func);
 343             }
 344         }
 345         return null;
 346     }
 347 




 348     /**
 349      * Returns an input stream for reading the contents of the specified
 350      * zip file entry.
 351      * <p>
 352      * Closing this ZIP file will, in turn, close all input streams that
 353      * have been returned by invocations of this method.
 354      *
 355      * @param entry the zip file entry
 356      * @return the input stream for reading the contents of the specified
 357      * zip file entry.
 358      * @throws ZipException if a ZIP format error has occurred
 359      * @throws IOException if an I/O error has occurred
 360      * @throws IllegalStateException if the zip file has been closed
 361      */
 362     public InputStream getInputStream(ZipEntry entry) throws IOException {
 363         Objects.requireNonNull(entry, "entry");
 364         int pos = -1;
 365         ZipFileInputStream in = null;
 366         Source zsrc = res.zsrc;
 367         Set<InputStream> istreams = res.istreams;
 368         synchronized (this) {
 369             ensureOpen();
 370             if (Objects.equals(lastEntryName, entry.name)) {
 371                 pos = lastEntryPos;
 372             } else if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
 373                 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
 374             } else {
 375                 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
 376             }
 377             if (pos == -1) {
 378                 return null;
 379             }
 380             in = new ZipFileInputStream(zsrc.cen, pos);
 381             switch (CENHOW(zsrc.cen, pos)) {
 382             case STORED:
 383                 synchronized (istreams) {
 384                     istreams.add(in);
 385                 }
 386                 return in;
 387             case DEFLATED:
 388                 // Inflater likes a bit of slack
 389                 // MORE: Compute good size for inflater stream:
 390                 long size = CENLEN(zsrc.cen, pos) + 2;
 391                 if (size > 65536) {
 392                     size = 8192;
 393                 }
 394                 if (size <= 0) {
 395                     size = 4096;
 396                 }
 397                 InputStream is = new ZipFileInflaterInputStream(in, res, (int)size);
 398                 synchronized (istreams) {
 399                     istreams.add(is);

 400                 }
 401                 return is;
 402             default:
 403                 throw new ZipException("invalid compression method");
 404             }
 405         }
 406     }
 407 
 408     private class ZipFileInflaterInputStream extends InflaterInputStream {
 409         private volatile boolean closeRequested;
 410         private boolean eof = false;
 411         private final Cleanable cleanable;
 412 
 413         ZipFileInflaterInputStream(ZipFileInputStream zfin,
 414                                    CleanableResource res, int size) {
 415             this(zfin, res, res.getInflater(), size);
 416         }
 417 
 418         private ZipFileInflaterInputStream(ZipFileInputStream zfin,
 419                                            CleanableResource res,
 420                                            Inflater inf, int size) {
 421             super(zfin, inf, size);
 422             this.cleanable = CleanerFactory.cleaner().register(this,
 423                     () -> res.releaseInflater(inf));
 424        }
 425 
 426         public void close() throws IOException {
 427             if (closeRequested)
 428                 return;
 429             closeRequested = true;

 430             super.close();
 431             synchronized (res.istreams) {
 432                 res.istreams.remove(this);




 433             }
 434             cleanable.clean();
 435         }
 436 
 437         // Override fill() method to provide an extra "dummy" byte
 438         // at the end of the input stream. This is required when
 439         // using the "nowrap" Inflater option.
 440         protected void fill() throws IOException {
 441             if (eof) {
 442                 throw new EOFException("Unexpected end of ZLIB input stream");
 443             }
 444             len = in.read(buf, 0, buf.length);
 445             if (len == -1) {
 446                 buf[0] = 0;
 447                 len = 1;
 448                 eof = true;
 449             }
 450             inf.setInput(buf, 0, len);
 451         }
 452 
 453         public int available() throws IOException {
 454             if (closeRequested)
 455                 return 0;
 456             long avail = ((ZipFileInputStream)in).size() - inf.getBytesWritten();
 457             return (avail > (long) Integer.MAX_VALUE ?
 458                     Integer.MAX_VALUE : (int) avail);
 459         }





















 460     }
 461 















 462     /**
 463      * Returns the path name of the ZIP file.
 464      * @return the path name of the ZIP file
 465      */
 466     public String getName() {
 467         return name;
 468     }
 469 
 470     private class ZipEntryIterator<T extends ZipEntry>
 471             implements Enumeration<T>, Iterator<T> {
 472 
 473         private int i = 0;
 474         private final int entryCount;
 475         private final Function<String, T> gen;
 476 
 477         public ZipEntryIterator(int entryCount, Function<String, T> gen) {
 478             this.entryCount = entryCount;
 479             this.gen = gen;
 480         }
 481 


 486 
 487         @Override
 488         public boolean hasNext() {
 489             return i < entryCount;
 490         }
 491 
 492         @Override
 493         public T nextElement() {
 494             return next();
 495         }
 496 
 497         @Override
 498         @SuppressWarnings("unchecked")
 499         public T  next() {
 500             synchronized (ZipFile.this) {
 501                 ensureOpen();
 502                 if (!hasNext()) {
 503                     throw new NoSuchElementException();
 504                 }
 505                 // each "entry" has 3 ints in table entries
 506                 return (T)getZipEntry(null, null, res.zsrc.getEntryPos(i++ * 3), gen);
 507             }
 508         }
 509 
 510         @Override
 511         public Iterator<T> asIterator() {
 512             return this;
 513         }
 514     }
 515 
 516     /**
 517      * Returns an enumeration of the ZIP file entries.
 518      * @return an enumeration of the ZIP file entries
 519      * @throws IllegalStateException if the zip file has been closed
 520      */
 521     public Enumeration<? extends ZipEntry> entries() {
 522         synchronized (this) {
 523             ensureOpen();
 524             return new ZipEntryIterator<ZipEntry>(res.zsrc.total, ZipEntry::new);
 525         }
 526     }
 527 
 528     private Enumeration<JarEntry> entries(Function<String, JarEntry> func) {
 529         synchronized (this) {
 530             ensureOpen();
 531             return new ZipEntryIterator<JarEntry>(res.zsrc.total, func);
 532         }
 533     }
 534 
 535     private class EntrySpliterator<T> extends Spliterators.AbstractSpliterator<T> {
 536         private int index;
 537         private final int fence;
 538         private final IntFunction<T> gen;
 539 
 540         EntrySpliterator(int index, int fence, IntFunction<T> gen) {
 541             super((long)fence,
 542                   Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE |
 543                   Spliterator.NONNULL);
 544             this.index = index;
 545             this.fence = fence;
 546             this.gen = gen;
 547         }
 548 
 549         @Override
 550         public boolean tryAdvance(Consumer<? super T> action) {
 551             if (action == null)
 552                 throw new NullPointerException();
 553             if (index >= 0 && index < fence) {
 554                 synchronized (ZipFile.this) {
 555                     ensureOpen();
 556                     action.accept(gen.apply(res.zsrc.getEntryPos(index++ * 3)));
 557                 }
 558                 return true;
 559             }
 560             return false;
 561         }
 562     }
 563 
 564     /**
 565      * Returns an ordered {@code Stream} over the ZIP file entries.
 566      *
 567      * Entries appear in the {@code Stream} in the order they appear in
 568      * the central directory of the ZIP file.
 569      *
 570      * @return an ordered {@code Stream} of entries in this ZIP file
 571      * @throws IllegalStateException if the zip file has been closed
 572      * @since 1.8
 573      */
 574     public Stream<? extends ZipEntry> stream() {
 575         synchronized (this) {
 576             ensureOpen();
 577             return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
 578                 pos -> getZipEntry(null, null, pos, ZipEntry::new)), false);
 579        }
 580     }
 581 
 582     private String getEntryName(int pos) {
 583         byte[] cen = res.zsrc.cen;
 584         int nlen = CENNAM(cen, pos);
 585         int clen = CENCOM(cen, pos);
 586         int flag = CENFLG(cen, pos);
 587         if (!zc.isUTF8() && (flag & EFS) != 0) {
 588             return zc.toStringUTF8(cen, pos + CENHDR, nlen);
 589         } else {
 590             return zc.toString(cen, pos + CENHDR, nlen);
 591         }
 592     }
 593 
 594     /*
 595      * Returns an ordered {@code Stream} over the zip file entry names.
 596      *
 597      * Entry names appear in the {@code Stream} in the order they appear in
 598      * the central directory of the ZIP file.
 599      *
 600      * @return an ordered {@code Stream} of entry names in this zip file
 601      * @throws IllegalStateException if the zip file has been closed
 602      * @since 10
 603      */
 604     private Stream<String> entryNameStream() {
 605         synchronized (this) {
 606             ensureOpen();
 607             return StreamSupport.stream(
 608                 new EntrySpliterator<>(0, res.zsrc.total, this::getEntryName), false);
 609         }
 610     }
 611 
 612     /*
 613      * Returns an ordered {@code Stream} over the zip file entries.
 614      *
 615      * Entries appear in the {@code Stream} in the order they appear in
 616      * the central directory of the jar file.
 617      *
 618      * @param func the function that creates the returned entry
 619      * @return an ordered {@code Stream} of entries in this zip file
 620      * @throws IllegalStateException if the zip file has been closed
 621      * @since 10
 622      */
 623     private Stream<JarEntry> stream(Function<String, JarEntry> func) {
 624         synchronized (this) {
 625             ensureOpen();
 626             return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
 627                 pos -> (JarEntry)getZipEntry(null, null, pos, func)), false);
 628         }
 629     }
 630 
 631     private String lastEntryName;
 632     private int lastEntryPos;
 633 
 634     /* Checks ensureOpen() before invoke this method */
 635     private ZipEntry getZipEntry(String name, byte[] bname, int pos,
 636                                  Function<String, ? extends ZipEntry> func) {
 637         byte[] cen = res.zsrc.cen;
 638         int nlen = CENNAM(cen, pos);
 639         int elen = CENEXT(cen, pos);
 640         int clen = CENCOM(cen, pos);
 641         int flag = CENFLG(cen, pos);
 642         if (name == null || bname.length != nlen) {
 643             // to use the entry name stored in cen, if the passed in name is
 644             // (1) null, invoked from iterator, or
 645             // (2) not equal to the name stored, a slash is appended during
 646             // getEntryPos() search.
 647             if (!zc.isUTF8() && (flag & EFS) != 0) {
 648                 name = zc.toStringUTF8(cen, pos + CENHDR, nlen);
 649             } else {
 650                 name = zc.toString(cen, pos + CENHDR, nlen);
 651             }
 652         }
 653         ZipEntry e = func.apply(name);    //ZipEntry e = new ZipEntry(name);
 654         e.flag = flag;
 655         e.xdostime = CENTIM(cen, pos);
 656         e.crc = CENCRC(cen, pos);
 657         e.size = CENLEN(cen, pos);


 666             if (!zc.isUTF8() && (flag & EFS) != 0) {
 667                 e.comment = zc.toStringUTF8(cen, start, clen);
 668             } else {
 669                 e.comment = zc.toString(cen, start, clen);
 670             }
 671         }
 672         lastEntryName = e.name;
 673         lastEntryPos = pos;
 674         return e;
 675     }
 676 
 677     /**
 678      * Returns the number of entries in the ZIP file.
 679      *
 680      * @return the number of entries in the ZIP file
 681      * @throws IllegalStateException if the zip file has been closed
 682      */
 683     public int size() {
 684         synchronized (this) {
 685             ensureOpen();
 686             return res.zsrc.total;
 687         }
 688     }
 689 
 690     private static class CleanableResource implements Runnable {
 691         // The outstanding inputstreams that need to be closed
 692         final Set<InputStream> istreams;
 693 
 694         // List of cached Inflater objects for decompression
 695         Deque<Inflater> inflaterCache;
 696 
 697         final Cleanable cleanable;
 698 
 699         Source zsrc;
 700 
 701         CleanableResource(ZipFile zf, File file, int mode) throws IOException {
 702             this.cleanable = CleanerFactory.cleaner().register(zf, this);
 703             this.istreams = Collections.newSetFromMap(new WeakHashMap<>());
 704             this.inflaterCache = new ArrayDeque<>();
 705             this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0);
 706         }
 707 
 708         void clean() {
 709             cleanable.clean();
 710         }
 711 
 712         /*
 713          * Gets an inflater from the list of available inflaters or allocates
 714          * a new one.
 715          */
 716         Inflater getInflater() {
 717             Inflater inf;
 718             synchronized (inflaterCache) {
 719                 if ((inf = inflaterCache.poll()) != null) {
 720                     return inf;
 721                 }
 722             }
 723             return new Inflater(true);
 724         }
 725 
 726         /*
 727          * Releases the specified inflater to the list of available inflaters.
 728          */
 729         void releaseInflater(Inflater inf) {
 730             Deque<Inflater> inflaters = this.inflaterCache;
 731             if (inflaters != null) {
 732                 synchronized (inflaters) {
 733                     // double checked!
 734                     if (inflaters == this.inflaterCache) {
 735                         inf.reset();
 736                         inflaters.add(inf);
 737                         return;
 738                     }
 739                 }
 740             }
 741             // inflaters cache already closed - just end it.
 742             inf.end();
 743         }
 744 
 745         public void run() {
 746             IOException ioe = null;
 747 
 748             // Release cached inflaters and close the cache first
 749             Deque<Inflater> inflaters = this.inflaterCache;
 750             if (inflaters != null) {
 751                 synchronized (inflaters) {
 752                     // no need to double-check as only one thread gets a
 753                     // chance to execute run() (Cleaner guarantee)...
 754                     Inflater inf;
 755                     while ((inf = inflaters.poll()) != null) {
 756                         inf.end();
 757                     }
 758                     // close inflaters cache
 759                     this.inflaterCache = null;
 760                 }
 761             }
 762 
 763             // Close streams, release their inflaters
 764             if (istreams != null) {
 765                 synchronized (istreams) {
 766                     if (!istreams.isEmpty()) {
 767                         InputStream[] copy = istreams.toArray(new InputStream[0]);
 768                         istreams.clear();
 769                         for (InputStream is : copy) {
 770                             try {
 771                                 is.close();
 772                             }  catch (IOException e) {
 773                                 if (ioe == null) ioe = e;
 774                                 else ioe.addSuppressed(e);
 775                             }
 776                         }
 777                     }
 778                 }
 779             }
 780 
 781             // Release zip src
 782             if (zsrc != null) {
 783                 synchronized (zsrc) {
 784                     try {
 785                         Source.release(zsrc);
 786                         zsrc = null;
 787                      }  catch (IOException e) {
 788                          if (ioe == null) ioe = e;
 789                          else ioe.addSuppressed(e);
 790                     }
 791                 }
 792             }
 793             if (ioe != null) {
 794                 throw new UncheckedIOException(ioe);
 795             }
 796         }
 797 
 798         CleanableResource(File file, int mode)
 799             throws IOException {
 800             this.cleanable = null;
 801             this.istreams = Collections.newSetFromMap(new WeakHashMap<>());
 802             this.inflaterCache = new ArrayDeque<>();
 803             this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0);
 804         }
 805 
 806         /*
 807          * If {@code ZipFile} has been subclassed and the {@code close} method is
 808          * overridden, uses the {@code finalizer} mechanism for resource cleanup.
 809          * So {@code close} method can be called when the the {@code ZipFile} is
 810          * unreachable. This mechanism will be removed when {@code finalize} method
 811          * is removed from {@code ZipFile}.
 812          */
 813         static CleanableResource get(ZipFile zf, File file, int mode)
 814             throws IOException {
 815             Class<?> clz = zf.getClass();
 816             while (clz != ZipFile.class) {
 817                 try {
 818                     clz.getDeclaredMethod("close");
 819                     return new FinalizableResource(zf, file, mode);
 820                 } catch (NoSuchMethodException nsme) {}
 821                 clz = clz.getSuperclass();
 822             }
 823             return new CleanableResource(zf, file, mode);
 824         }
 825 
 826         static class FinalizableResource extends CleanableResource {
 827             ZipFile zf;
 828             FinalizableResource(ZipFile zf, File file, int mode)
 829                 throws IOException {
 830                 super(file, mode);
 831                 this.zf = zf;
 832             }
 833 
 834             @Override
 835             void clean() {
 836                 run();
 837             }
 838 
 839             @Override
 840             @SuppressWarnings("deprecation")
 841             protected void finalize() throws IOException {
 842                 zf.close();
 843             }            
 844         }
 845     }
 846 
 847     /**
 848      * Closes the ZIP file.
 849      *
 850      * <p> Closing this ZIP file will close all of the input streams
 851      * previously returned by invocations of the {@link #getInputStream
 852      * getInputStream} method.
 853      *
 854      * @throws IOException if an I/O error has occurred
 855      */
 856     public void close() throws IOException {
 857         if (closeRequested) {
 858             return;
 859         }
 860         closeRequested = true;
 861 
 862         synchronized (this) {
 863             // Close streams, release their inflaters, release cached inflaters
 864             // and release zip source
 865             try {
 866                 res.clean();
 867             } catch (UncheckedIOException ioe) {
 868                 throw ioe.getCause();



















 869             }
 870         }
 871     }
 872 
 873     /**
 874      * Ensures that the system resources held by this ZipFile object are
 875      * released when there are no more references to it.
 876      *
 877      * @implSpec
 878      * If this {@code ZipFile} has been subclassed and the {@code close} method
 879      * has been overridden, the {@code close} method will be called when the
 880      * {@code ZipFile} is unreachable.
 881      *
 882      * @deprecated The {@code finalize} method has been deprecated and
 883      *     implemented as a no-op. Subclasses that override {@code finalize}
 884      *     in order to perform cleanup should be modified to use alternative
 885      *     cleanup mechanisms and to remove the overriding {@code finalize}
 886      *     method. The recommended cleanup for ZipFile object is to explicitly
 887      *     invoke {@code close} method when it is no longer in use, or use
 888      *     try-with-resources. If the {@code close} is not invoked explicitly
 889      *     the resources held by this object will be released when the instance
 890      *     becomes phantom-reachable.
 891      *
 892      * @throws IOException if an I/O error has occurred

 893      */
 894     @Deprecated(since="9", forRemoval=true)
 895     protected void finalize() throws IOException {}


 896 
 897     private void ensureOpen() {
 898         if (closeRequested) {
 899             throw new IllegalStateException("zip file closed");
 900         }
 901         if (res.zsrc == null) {
 902             throw new IllegalStateException("The object is not initialized.");
 903         }
 904     }
 905 
 906     private void ensureOpenOrZipException() throws IOException {
 907         if (closeRequested) {
 908             throw new ZipException("ZipFile closed");
 909         }
 910     }
 911 
 912     /*
 913      * Inner class implementing the input stream used to read a
 914      * (possibly compressed) zip file entry.
 915      */
 916    private class ZipFileInputStream extends InputStream {
 917         private volatile boolean closeRequested;
 918         private   long pos;     // current position within entry data
 919         protected long rem;     // number of remaining bytes within entry
 920         protected long size;    // uncompressed size of this entry
 921 
 922         ZipFileInputStream(byte[] cen, int cenpos) {
 923             rem = CENSIZ(cen, cenpos);
 924             size = CENLEN(cen, cenpos);
 925             pos = CENOFF(cen, cenpos);
 926             // zip64
 927             if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
 928                 pos == ZIP64_MAGICVAL) {
 929                 checkZIP64(cen, cenpos);
 930             }
 931             // negative for lazy initialization, see getDataOffset();
 932             pos = - (pos + ZipFile.this.res.zsrc.locpos);
 933         }
 934 
 935          private void checkZIP64(byte[] cen, int cenpos) {
 936             int off = cenpos + CENHDR + CENNAM(cen, cenpos);
 937             int end = off + CENEXT(cen, cenpos);
 938             while (off + 4 < end) {
 939                 int tag = get16(cen, off);
 940                 int sz = get16(cen, off + 2);
 941                 off += 4;
 942                 if (off + sz > end)         // invalid data
 943                     break;
 944                 if (tag == EXTID_ZIP64) {
 945                     if (size == ZIP64_MAGICVAL) {
 946                         if (sz < 8 || (off + 8) > end)
 947                             break;
 948                         size = get64(cen, off);
 949                         sz -= 8;
 950                         off += 8;
 951                     }
 952                     if (rem == ZIP64_MAGICVAL) {
 953                         if (sz < 8 || (off + 8) > end)
 954                             break;
 955                         rem = get64(cen, off);


 961                             break;
 962                         pos = get64(cen, off);
 963                         sz -= 8;
 964                         off += 8;
 965                     }
 966                     break;
 967                 }
 968                 off += sz;
 969             }
 970         }
 971 
 972        /* The Zip file spec explicitly allows the LOC extra data size to
 973         * be different from the CEN extra data size. Since we cannot trust
 974         * the CEN extra data size, we need to read the LOC to determine
 975         * the entry data offset.
 976         */
 977         private long initDataOffset() throws IOException {
 978             if (pos <= 0) {
 979                 byte[] loc = new byte[LOCHDR];
 980                 pos = -pos;
 981                 int len = ZipFile.this.res.zsrc.readFullyAt(loc, 0, loc.length, pos);
 982                 if (len != LOCHDR) {
 983                     throw new ZipException("ZipFile error reading zip file");
 984                 }
 985                 if (LOCSIG(loc) != LOCSIG) {
 986                     throw new ZipException("ZipFile invalid LOC header (bad signature)");
 987                 }
 988                 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
 989             }
 990             return pos;
 991         }
 992 
 993         public int read(byte b[], int off, int len) throws IOException {
 994             synchronized (ZipFile.this) {
 995                 ensureOpenOrZipException();
 996                 initDataOffset();
 997                 if (rem == 0) {
 998                     return -1;
 999                 }
1000                 if (len > rem) {
1001                     len = (int) rem;
1002                 }
1003                 if (len <= 0) {
1004                     return 0;
1005                 }
1006                 len = ZipFile.this.res.zsrc.readAt(b, off, len, pos);
1007                 if (len > 0) {
1008                     pos += len;
1009                     rem -= len;
1010                 }
1011             }
1012             if (rem == 0) {
1013                 close();
1014             }
1015             return len;
1016         }
1017 
1018         public int read() throws IOException {
1019             byte[] b = new byte[1];
1020             if (read(b, 0, 1) == 1) {
1021                 return b[0] & 0xff;
1022             } else {
1023                 return -1;
1024             }
1025         }
1026 


1036             if (rem == 0) {
1037                 close();
1038             }
1039             return n;
1040         }
1041 
1042         public int available() {
1043             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
1044         }
1045 
1046         public long size() {
1047             return size;
1048         }
1049 
1050         public void close() {
1051             if (closeRequested) {
1052                 return;
1053             }
1054             closeRequested = true;
1055             rem = 0;
1056             synchronized (res.istreams) {
1057                 res.istreams.remove(this);
1058             }
1059         }
1060 




1061     }
1062 
1063     /**
1064      * Returns the names of all non-directory entries that begin with
1065      * "META-INF/" (case ignored). This method is used in JarFile, via
1066      * SharedSecrets, as an optimization when looking up manifest and
1067      * signature file entries. Returns null if no entries were found.
1068      */
1069     private String[] getMetaInfEntryNames() {
1070         synchronized (this) {
1071             ensureOpen();
1072             Source zsrc = res.zsrc;
1073             if (zsrc.metanames == null) {
1074                 return null;
1075             }
1076             String[] names = new String[zsrc.metanames.length];
1077             byte[] cen = zsrc.cen;
1078             for (int i = 0; i < names.length; i++) {
1079                 int pos = zsrc.metanames[i];
1080                 names[i] = new String(cen, pos + CENHDR, CENNAM(cen, pos),
1081                                       StandardCharsets.UTF_8);
1082             }
1083             return names;
1084         }
1085     }
1086 
1087     private static boolean isWindows;
1088     static {
1089         SharedSecrets.setJavaUtilZipFileAccess(
1090             new JavaUtilZipFileAccess() {
1091                 @Override
1092                 public boolean startsWithLocHeader(ZipFile zip) {
1093                     return zip.res.zsrc.startsWithLoc;
1094                 }
1095                 @Override
1096                 public String[] getMetaInfEntryNames(ZipFile zip) {
1097                     return zip.getMetaInfEntryNames();
1098                 }
1099                 @Override
1100                 public JarEntry getEntry(ZipFile zip, String name,
1101                     Function<String, JarEntry> func) {
1102                     return (JarEntry)zip.getEntry(name, func);
1103                 }
1104                 @Override
1105                 public Enumeration<JarEntry> entries(ZipFile zip,
1106                     Function<String, JarEntry> func) {
1107                     return zip.entries(func);
1108                 }
1109                 @Override
1110                 public Stream<JarEntry> stream(ZipFile zip,
1111                     Function<String, JarEntry> func) {
1112                     return zip.stream(func);
1113                 }


1181 
1182             public boolean equals(Object obj) {
1183                 if (obj instanceof Key) {
1184                     Key key = (Key)obj;
1185                     if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) {
1186                         return false;
1187                     }
1188                     Object fk = attrs.fileKey();
1189                     if (fk != null) {
1190                         return  fk.equals(key.attrs.fileKey());
1191                     } else {
1192                         return file.equals(key.file);
1193                     }
1194                 }
1195                 return false;
1196             }
1197         }
1198         private static final HashMap<Key, Source> files = new HashMap<>();
1199 
1200 
1201         static Source get(File file, boolean toDelete) throws IOException {
1202             Key key = new Key(file,
1203                               Files.readAttributes(file.toPath(), BasicFileAttributes.class));
1204             Source src = null;
1205             synchronized (files) {
1206                 src = files.get(key);
1207                 if (src != null) {
1208                     src.refs++;
1209                     return src;
1210                 }
1211             }
1212             src = new Source(key, toDelete);
1213 
1214             synchronized (files) {
1215                 if (files.containsKey(key)) {    // someone else put in first
1216                     src.close();                 // close the newly created one
1217                     src = files.get(key);
1218                     src.refs++;
1219                     return src;
1220                 }
1221                 files.put(key, src);
1222                 return src;
1223             }
1224         }
1225 
1226         static void release(Source src) throws IOException {
1227             synchronized (files) {
1228                 if (src != null && --src.refs == 0) {
1229                     files.remove(src.key);
1230                     src.close();
1231                 }
1232             }
1233         }
1234 
1235         private Source(Key key, boolean toDelete) throws IOException {
1236             this.key = key;
1237             if (toDelete) {
1238                 if (isWindows) {
1239                     this.zfile = SharedSecrets.getJavaIORandomAccessFileAccess()
1240                                               .openAndDelete(key.file, "r");
1241                 } else {
1242                     this.zfile = new RandomAccessFile(key.file, "r");
1243                     key.file.delete();
1244                 }
1245             } else {
1246                 this.zfile = new RandomAccessFile(key.file, "r");
1247             }
1248             try {