< prev index next >

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

Print this page




   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.nio.charset.Charset;
  34 import java.nio.charset.StandardCharsets;
  35 import java.util.ArrayDeque;
  36 import java.util.Deque;
  37 import java.util.Enumeration;
  38 import java.util.HashMap;
  39 import java.util.Iterator;
  40 import java.util.Map;
  41 import java.util.NoSuchElementException;
  42 import java.util.Spliterator;
  43 import java.util.Spliterators;
  44 import java.util.WeakHashMap;
  45 import java.util.stream.Stream;
  46 import java.util.stream.StreamSupport;
  47 


  50 /**
  51  * This class is used to read entries from a zip file.
  52  *
  53  * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
  54  * or method in this class will cause a {@link NullPointerException} to be
  55  * thrown.
  56  *
  57  * @author      David Connelly
  58  */
  59 public
  60 class ZipFile implements ZipConstants, Closeable {
  61     private long jzfile;           // address of jzfile data
  62     private final String name;     // zip file name
  63     private final int total;       // total number of entries
  64     private final boolean locsig;  // if zip file starts with LOCSIG (usually true)
  65     private volatile boolean closeRequested = false;
  66 
  67     private static final int STORED = ZipEntry.STORED;
  68     private static final int DEFLATED = ZipEntry.DEFLATED;
  69 



  70     /**
  71      * Mode flag to open a zip file for reading.
  72      */
  73     public static final int OPEN_READ = 0x1;
  74 
  75     /**
  76      * Mode flag to open a zip file and mark it for deletion.  The file will be
  77      * deleted some time between the moment that it is opened and the moment
  78      * that it is closed, but its contents will remain accessible via the
  79      * <tt>ZipFile</tt> object until either the close method is invoked or the
  80      * virtual machine exits.
  81      */
  82     public static final int OPEN_DELETE = 0x4;
  83 
  84     static {
  85         /* Zip library is loaded from System.initializeSystemClass */
  86         initIDs();
  87     }
  88 
  89     private static native void initIDs();


 328     private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
 329 
 330     /**
 331      * Returns an input stream for reading the contents of the specified
 332      * zip file entry.
 333      *
 334      * <p> Closing this ZIP file will, in turn, close all input
 335      * streams that have been returned by invocations of this method.
 336      *
 337      * @param entry the zip file entry
 338      * @return the input stream for reading the contents of the specified
 339      * zip file entry.
 340      * @throws ZipException if a ZIP format error has occurred
 341      * @throws IOException if an I/O error has occurred
 342      * @throws IllegalStateException if the zip file has been closed
 343      */
 344     public InputStream getInputStream(ZipEntry entry) throws IOException {
 345         if (entry == null) {
 346             throw new NullPointerException("entry");
 347         }
 348         long jzentry = 0;
 349         ZipFileInputStream in = null;

 350         synchronized (this) {
 351             ensureOpen();
 352             if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
 353                 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
 354             } else {
 355                 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
 356             }
 357             if (jzentry == 0) {
 358                 return null;
 359             }
 360             in = new ZipFileInputStream(jzentry);



 361 
 362             switch (getEntryMethod(jzentry)) {














 363             case STORED:
 364                 synchronized (streams) {
 365                     streams.put(in, null);
 366                 }
 367                 return in;
 368             case DEFLATED:
 369                 // MORE: Compute good size for inflater stream:
 370                 long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack
 371                 if (size > 65536) size = 8192;
 372                 if (size <= 0) size = 4096;
 373                 Inflater inf = getInflater();
 374                 InputStream is =
 375                     new ZipFileInflaterInputStream(in, inf, (int)size);
 376                 synchronized (streams) {
 377                     streams.put(is, inf);
 378                 }
 379                 return is;
 380             default:
 381                 throw new ZipException("invalid compression method");
 382             }
 383         }
 384     }
 385 
 386     private class ZipFileInflaterInputStream extends InflaterInputStream {
 387         private volatile boolean closeRequested = false;
 388         private boolean eof = false;
 389         private final ZipFileInputStream zfin;
 390 
 391         ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
 392                 int size) {
 393             super(zfin, inf, size);
 394             this.zfin = zfin;
 395         }
 396 
 397         public void close() throws IOException {
 398             if (closeRequested)
 399                 return;
 400             closeRequested = true;
 401 
 402             super.close();
 403             Inflater inf;
 404             synchronized (streams) {


 452             }
 453         }
 454         return new Inflater(true);
 455     }
 456 
 457     /*
 458      * Releases the specified inflater to the list of available inflaters.
 459      */
 460     private void releaseInflater(Inflater inf) {
 461         if (false == inf.ended()) {
 462             inf.reset();
 463             synchronized (inflaterCache) {
 464                 inflaterCache.add(inf);
 465             }
 466         }
 467     }
 468 
 469     // List of available Inflater objects for decompression
 470     private Deque<Inflater> inflaterCache = new ArrayDeque<>();
 471 

































































































 472     /**
 473      * Returns the path name of the ZIP file.
 474      * @return the path name of the ZIP file
 475      */
 476     public String getName() {
 477         return name;
 478     }
 479 
 480     private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
 481         private int i = 0;
 482 
 483         public ZipEntryIterator() {
 484             ensureOpen();
 485         }
 486 
 487         public boolean hasMoreElements() {
 488             return hasNext();
 489         }
 490 
 491         public boolean hasNext() {


 674         }
 675     }
 676 
 677     private void ensureOpenOrZipException() throws IOException {
 678         if (closeRequested) {
 679             throw new ZipException("ZipFile closed");
 680         }
 681     }
 682 
 683     /*
 684      * Inner class implementing the input stream used to read a
 685      * (possibly compressed) zip file entry.
 686      */
 687    private class ZipFileInputStream extends InputStream {
 688         private volatile boolean closeRequested = false;
 689         protected long jzentry; // address of jzentry data
 690         private   long pos;     // current position within entry data
 691         protected long rem;     // number of remaining bytes within entry
 692         protected long size;    // uncompressed size of this entry
 693 
 694         ZipFileInputStream(long jzentry) {
 695             pos = 0;
 696             rem = getEntryCSize(jzentry);
 697             size = getEntrySize(jzentry);
 698             this.jzentry = jzentry;
 699         }
 700 
 701         public int read(byte b[], int off, int len) throws IOException {
 702             synchronized (ZipFile.this) {
 703                 long rem = this.rem;
 704                 long pos = this.pos;
 705                 if (rem == 0) {
 706                     return -1;
 707                 }
 708                 if (len <= 0) {
 709                     return 0;
 710                 }
 711                 if (len > rem) {
 712                     len = (int) rem;
 713                 }
 714 
 715                 ensureOpenOrZipException();
 716                 len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b,
 717                                    off, len);


 762             rem = 0;
 763             synchronized (ZipFile.this) {
 764                 if (jzentry != 0 && ZipFile.this.jzfile != 0) {
 765                     freeEntry(ZipFile.this.jzfile, jzentry);
 766                     jzentry = 0;
 767                 }
 768             }
 769             synchronized (streams) {
 770                 streams.remove(this);
 771             }
 772         }
 773 
 774         protected void finalize() {
 775             close();
 776         }
 777     }
 778 
 779     static {
 780         sun.misc.SharedSecrets.setJavaUtilZipFileAccess(
 781             new sun.misc.JavaUtilZipFileAccess() {

 782                 public boolean startsWithLocHeader(ZipFile zip) {
 783                     return zip.startsWithLocHeader();






 784                 }
 785              }
 786         );
 787     }
 788 
 789     /**
 790      * Returns {@code true} if, and only if, the zip file begins with {@code
 791      * LOCSIG}.
 792      */
 793     private boolean startsWithLocHeader() {
 794         return locsig;
 795     }
 796 
 797     private static native long open(String name, int mode, long lastModified,
 798                                     boolean usemmap) throws IOException;
 799     private static native int getTotal(long jzfile);
 800     private static native boolean startsWithLOC(long jzfile);
 801     private static native int read(long jzfile, long jzentry,
 802                                    long pos, byte[] b, int off, int len);
 803 


   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.ByteArrayInputStream;
  29 import java.io.Closeable;
  30 import java.io.InputStream;
  31 import java.io.IOException;
  32 import java.io.EOFException;
  33 import java.io.File;
  34 import java.nio.charset.Charset;
  35 import java.nio.charset.StandardCharsets;
  36 import java.util.ArrayDeque;
  37 import java.util.Deque;
  38 import java.util.Enumeration;
  39 import java.util.HashMap;
  40 import java.util.Iterator;
  41 import java.util.Map;
  42 import java.util.NoSuchElementException;
  43 import java.util.Spliterator;
  44 import java.util.Spliterators;
  45 import java.util.WeakHashMap;
  46 import java.util.stream.Stream;
  47 import java.util.stream.StreamSupport;
  48 


  51 /**
  52  * This class is used to read entries from a zip file.
  53  *
  54  * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
  55  * or method in this class will cause a {@link NullPointerException} to be
  56  * thrown.
  57  *
  58  * @author      David Connelly
  59  */
  60 public
  61 class ZipFile implements ZipConstants, Closeable {
  62     private long jzfile;           // address of jzfile data
  63     private final String name;     // zip file name
  64     private final int total;       // total number of entries
  65     private final boolean locsig;  // if zip file starts with LOCSIG (usually true)
  66     private volatile boolean closeRequested = false;
  67 
  68     private static final int STORED = ZipEntry.STORED;
  69     private static final int DEFLATED = ZipEntry.DEFLATED;
  70 
  71     // Max buffer size when returning bytebuffers directly
  72     private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
  73 
  74     /**
  75      * Mode flag to open a zip file for reading.
  76      */
  77     public static final int OPEN_READ = 0x1;
  78 
  79     /**
  80      * Mode flag to open a zip file and mark it for deletion.  The file will be
  81      * deleted some time between the moment that it is opened and the moment
  82      * that it is closed, but its contents will remain accessible via the
  83      * <tt>ZipFile</tt> object until either the close method is invoked or the
  84      * virtual machine exits.
  85      */
  86     public static final int OPEN_DELETE = 0x4;
  87 
  88     static {
  89         /* Zip library is loaded from System.initializeSystemClass */
  90         initIDs();
  91     }
  92 
  93     private static native void initIDs();


 332     private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
 333 
 334     /**
 335      * Returns an input stream for reading the contents of the specified
 336      * zip file entry.
 337      *
 338      * <p> Closing this ZIP file will, in turn, close all input
 339      * streams that have been returned by invocations of this method.
 340      *
 341      * @param entry the zip file entry
 342      * @return the input stream for reading the contents of the specified
 343      * zip file entry.
 344      * @throws ZipException if a ZIP format error has occurred
 345      * @throws IOException if an I/O error has occurred
 346      * @throws IllegalStateException if the zip file has been closed
 347      */
 348     public InputStream getInputStream(ZipEntry entry) throws IOException {
 349         if (entry == null) {
 350             throw new NullPointerException("entry");
 351         }
 352 
 353         long jzentry, csize, size;
 354         int cmethod;
 355         synchronized (this) {
 356             ensureOpen();
 357             if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
 358                 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
 359             } else {
 360                 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
 361             }
 362             if (jzentry == 0) {
 363                 return null;
 364             }
 365             size = getEntrySize(jzentry);
 366             csize = getEntryCSize(jzentry);
 367             cmethod = getEntryMethod(jzentry);
 368         }
 369 
 370         if (csize >= 0 && size > 0 && size < 128 * 1024) {
 371             try {
 372                 return new ByteArrayInputStream(getBytes(jzentry, csize, size, cmethod));
 373             } finally {
 374                 synchronized (this) {
 375                     if (jzfile != 0) {
 376                         freeEntry(jzfile, jzentry);
 377                     }
 378                 }
 379             }
 380         }
 381 
 382         ZipFileInputStream in = new ZipFileInputStream(jzentry, csize, size);
 383 
 384         switch (cmethod) {
 385         case STORED:
 386             synchronized (streams) {
 387                 streams.put(in, null);
 388             }
 389             return in;
 390         case DEFLATED:
 391             // MORE: Compute good size for inflater stream:
 392             size += 2; // Inflater likes a bit of slack
 393             if (size > 65536) size = 8192;
 394             if (size <= 0) size = 4096;
 395             Inflater inf = getInflater();
 396             InputStream is =
 397                 new ZipFileInflaterInputStream(in, inf, (int)size);
 398             synchronized (streams) {
 399                 streams.put(is, inf);
 400             }
 401             return is;
 402         default:
 403             throw new ZipException("invalid compression method");
 404         }
 405     }

 406 
 407     private class ZipFileInflaterInputStream extends InflaterInputStream {
 408         private volatile boolean closeRequested = false;
 409         private boolean eof = false;
 410         private final ZipFileInputStream zfin;
 411 
 412         ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
 413                 int size) {
 414             super(zfin, inf, size);
 415             this.zfin = zfin;
 416         }
 417 
 418         public void close() throws IOException {
 419             if (closeRequested)
 420                 return;
 421             closeRequested = true;
 422 
 423             super.close();
 424             Inflater inf;
 425             synchronized (streams) {


 473             }
 474         }
 475         return new Inflater(true);
 476     }
 477 
 478     /*
 479      * Releases the specified inflater to the list of available inflaters.
 480      */
 481     private void releaseInflater(Inflater inf) {
 482         if (false == inf.ended()) {
 483             inf.reset();
 484             synchronized (inflaterCache) {
 485                 inflaterCache.add(inf);
 486             }
 487         }
 488     }
 489 
 490     // List of available Inflater objects for decompression
 491     private Deque<Inflater> inflaterCache = new ArrayDeque<>();
 492 
 493     /*
 494      * Uncompress the zip entry into a new byte[].
 495      *
 496      * This method can only read entries smaller than 2GB, for larger entries
 497      * use getInputStream.
 498      *
 499      * @param entry the zip file entry
 500      *
 501      * @return the byte[] with the deflated contents of the specified zip file entry.
 502      *
 503      * @throws ZipException if a ZIP format error has occurred
 504      * @throws IOException if an I/O error has occurred
 505      * @throws IllegalStateException if the zip file has been closed
 506      * @throws OutOfMemory if the zip entry is larger than 2GB
 507      */
 508     private byte[] getBytes(ZipEntry entry) throws IOException {
 509         if (entry == null) {
 510             throw new NullPointerException("entry");
 511         }
 512 
 513         long jzentry, csize, size;
 514         int cmethod;
 515         synchronized (this) {
 516             ensureOpen();
 517             if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
 518                 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
 519             } else {
 520                 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
 521             }
 522             if (jzentry == 0) {
 523                 return null;
 524             }
 525 
 526             csize = getEntryCSize(jzentry);
 527             size = getEntrySize(jzentry);
 528             cmethod = getEntryMethod(jzentry);
 529         }
 530 
 531         try {
 532             return getBytes(jzentry, csize, size, cmethod);
 533         } finally {
 534             synchronized (this) {
 535                 if (jzfile != 0) {
 536                     freeEntry(jzfile, jzentry);
 537                 }
 538             }
 539         }
 540     }
 541 
 542     private byte[] getBytes(long jzentry, long csize, long size, int cmethod)
 543             throws IOException {
 544 
 545         if (csize < 0 || size < 0) {
 546             throw new ZipException("Unknown size of ZipEntry");
 547         }
 548         if (csize > MAX_BUFFER_SIZE || size > MAX_BUFFER_SIZE) {
 549             throw new OutOfMemoryError("ZipEntry too large");
 550         }
 551 
 552         byte[] cbytes = new byte[(int) csize];
 553         readCompressedBytes(jzentry, (int) csize, cbytes);
 554         switch (cmethod) {
 555             case STORED:
 556                 return cbytes;
 557             case DEFLATED:
 558                 byte[] bytes = new byte[(int) size];
 559                 Inflater inf = getInflater();
 560                 try {
 561                     inf.setInput(cbytes);
 562                     inf.inflate(bytes);
 563                 } catch (DataFormatException e) {
 564                     String s = e.getMessage();
 565                     throw new ZipException(s != null ? s : "Invalid ZLIB data format");
 566                 } finally {
 567                     releaseInflater(inf);
 568                 }
 569                 return bytes;
 570             default:
 571                 throw new ZipException("invalid compression method");
 572         }
 573     }
 574 
 575     private int readCompressedBytes(long jzentry, int csize, byte[] buf)
 576             throws IOException {
 577         assert csize == buf.length;
 578         synchronized (this) {
 579             ensureOpenOrZipException();
 580             int n = 0;
 581             int nread = 0;
 582             while (nread < csize
 583                    && (n = read(jzfile, jzentry, nread, buf, nread, csize - nread)) > 0) {
 584                 nread += n;
 585             }
 586             return nread;
 587         }
 588     }
 589 
 590     /**
 591      * Returns the path name of the ZIP file.
 592      * @return the path name of the ZIP file
 593      */
 594     public String getName() {
 595         return name;
 596     }
 597 
 598     private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
 599         private int i = 0;
 600 
 601         public ZipEntryIterator() {
 602             ensureOpen();
 603         }
 604 
 605         public boolean hasMoreElements() {
 606             return hasNext();
 607         }
 608 
 609         public boolean hasNext() {


 792         }
 793     }
 794 
 795     private void ensureOpenOrZipException() throws IOException {
 796         if (closeRequested) {
 797             throw new ZipException("ZipFile closed");
 798         }
 799     }
 800 
 801     /*
 802      * Inner class implementing the input stream used to read a
 803      * (possibly compressed) zip file entry.
 804      */
 805    private class ZipFileInputStream extends InputStream {
 806         private volatile boolean closeRequested = false;
 807         protected long jzentry; // address of jzentry data
 808         private   long pos;     // current position within entry data
 809         protected long rem;     // number of remaining bytes within entry
 810         protected long size;    // uncompressed size of this entry
 811 
 812         ZipFileInputStream(long jzentry, long csize, long size) {
 813             pos = 0;
 814             rem = csize;
 815             this.size = size;
 816             this.jzentry = jzentry;
 817         }
 818 
 819         public int read(byte b[], int off, int len) throws IOException {
 820             synchronized (ZipFile.this) {
 821                 long rem = this.rem;
 822                 long pos = this.pos;
 823                 if (rem == 0) {
 824                     return -1;
 825                 }
 826                 if (len <= 0) {
 827                     return 0;
 828                 }
 829                 if (len > rem) {
 830                     len = (int) rem;
 831                 }
 832 
 833                 ensureOpenOrZipException();
 834                 len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b,
 835                                    off, len);


 880             rem = 0;
 881             synchronized (ZipFile.this) {
 882                 if (jzentry != 0 && ZipFile.this.jzfile != 0) {
 883                     freeEntry(ZipFile.this.jzfile, jzentry);
 884                     jzentry = 0;
 885                 }
 886             }
 887             synchronized (streams) {
 888                 streams.remove(this);
 889             }
 890         }
 891 
 892         protected void finalize() {
 893             close();
 894         }
 895     }
 896 
 897     static {
 898         sun.misc.SharedSecrets.setJavaUtilZipFileAccess(
 899             new sun.misc.JavaUtilZipFileAccess() {
 900                 @Override
 901                 public boolean startsWithLocHeader(ZipFile zip) {
 902                     return zip.startsWithLocHeader();
 903                 }
 904 
 905                 @Override
 906                 public byte[] getBytes(ZipFile zip, ZipEntry entry)
 907                         throws IOException {
 908                     return zip.getBytes(entry);
 909                 }
 910              }
 911         );
 912     }
 913 
 914     /**
 915      * Returns {@code true} if, and only if, the zip file begins with {@code
 916      * LOCSIG}.
 917      */
 918     private boolean startsWithLocHeader() {
 919         return locsig;
 920     }
 921 
 922     private static native long open(String name, int mode, long lastModified,
 923                                     boolean usemmap) throws IOException;
 924     private static native int getTotal(long jzfile);
 925     private static native boolean startsWithLOC(long jzfile);
 926     private static native int read(long jzfile, long jzentry,
 927                                    long pos, byte[] b, int off, int len);
 928 
< prev index next >