< prev index next >

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

Print this page




  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 
  48 import static java.util.zip.ZipConstants64.*;
  49 
  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();


 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() {
 492             synchronized (ZipFile.this) {


 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 


  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.BufferOverflowException;
  34 import java.nio.ByteBuffer;
  35 import java.nio.ReadOnlyBufferException;
  36 import java.nio.charset.Charset;
  37 import java.nio.charset.StandardCharsets;
  38 import java.util.ArrayDeque;
  39 import java.util.Arrays;
  40 import java.util.Deque;
  41 import java.util.Enumeration;
  42 import java.util.HashMap;
  43 import java.util.Iterator;
  44 import java.util.Map;
  45 import java.util.NoSuchElementException;
  46 import java.util.Spliterator;
  47 import java.util.Spliterators;
  48 import java.util.WeakHashMap;
  49 import java.util.stream.Stream;
  50 import java.util.stream.StreamSupport;
  51 
  52 import static java.util.zip.ZipConstants64.*;
  53 
  54 /**
  55  * This class is used to read entries from a zip file.
  56  *
  57  * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
  58  * or method in this class will cause a {@link NullPointerException} to be
  59  * thrown.
  60  *
  61  * @author      David Connelly
  62  */
  63 public
  64 class ZipFile implements ZipConstants, Closeable {
  65     private long jzfile;           // address of jzfile data
  66     private final String name;     // zip file name
  67     private final int total;       // total number of entries
  68     private final boolean locsig;  // if zip file starts with LOCSIG (usually true)
  69     private volatile boolean closeRequested = false;
  70 
  71     private static final int STORED = ZipEntry.STORED;
  72     private static final int DEFLATED = ZipEntry.DEFLATED;
  73 
  74     // Max buffer size when returning bytebuffers directly
  75     private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
  76 
  77     /**
  78      * Mode flag to open a zip file for reading.
  79      */
  80     public static final int OPEN_READ = 0x1;
  81 
  82     /**
  83      * Mode flag to open a zip file and mark it for deletion.  The file will be
  84      * deleted some time between the moment that it is opened and the moment
  85      * that it is closed, but its contents will remain accessible via the
  86      * <tt>ZipFile</tt> object until either the close method is invoked or the
  87      * virtual machine exits.
  88      */
  89     public static final int OPEN_DELETE = 0x4;
  90 
  91     static {
  92         /* Zip library is loaded from System.initializeSystemClass */
  93         initIDs();
  94     }
  95 
  96     private static native void initIDs();


 460         }
 461         return new Inflater(true);
 462     }
 463 
 464     /*
 465      * Releases the specified inflater to the list of available inflaters.
 466      */
 467     private void releaseInflater(Inflater inf) {
 468         if (false == inf.ended()) {
 469             inf.reset();
 470             synchronized (inflaterCache) {
 471                 inflaterCache.add(inf);
 472             }
 473         }
 474     }
 475 
 476     // List of available Inflater objects for decompression
 477     private Deque<Inflater> inflaterCache = new ArrayDeque<>();
 478 
 479     /**
 480      * Uncompress the zip entry into a new ByteBuffer.
 481      *
 482      * The returned ByteBuffer will have position 0 and limit set to the length
 483      * of the uncompressed bytes of the zip entry.
 484      *
 485      * This method can only read entries smaller than 2GB, for larger entries
 486      * use getInputStream.
 487      *
 488      * @param entry the zip file entry
 489      *
 490      * @return the ByteBuffer with the contents of the specified zip file entry.
 491      *
 492      * @throws ZipException if a ZIP format error has occurred
 493      * @throws IOException if an I/O error has occurred
 494      * @throws IllegalStateException if the zip file has been closed
 495      * @throws BufferOverflowException if the zip entry is larger than the
 496      * supplied buffer
 497      * @throws ReadOnlyBufferException if the supplied buffer is read-only
 498      */
 499     private ByteBuffer getByteBuffer(ZipEntry entry) throws IOException {
 500         if (entry == null) {
 501             throw new NullPointerException("entry");
 502         }
 503 
 504         long size = entry.getSize();
 505 
 506         if (size < 0) {
 507             // Uknown size of zip entry (-1)
 508             return getByteBufferUnknownSize(entry);
 509         }
 510 
 511         if (size > MAX_BUFFER_SIZE) {
 512             throw new BufferOverflowException();
 513         }
 514 
 515         return fillByteBuffer(entry, ByteBuffer.allocate((int) size)).flip();
 516     }
 517 
 518     /**
 519      * Fill a ByteBuffer with the uncompressed contents of the specified zip
 520      * file entry.
 521      *
 522      * This method transfers the zip entry bytes into this buffer. If there are
 523      * more bytes to be written than remain in this buffer, that is, if
 524      * ZipEntry.getSize() > remaining(), then no bytes are transferred and a
 525      * BufferOverflowException is thrown.
 526      *
 527      * Otherwise, this method copies length bytes from the given array into this
 528      * buffer, starting at the given offset in the array and at the current
 529      * position of this buffer. The position of this buffer is then incremented
 530      * by length.
 531      *
 532      * This method can only read entries smaller than 2GB, for larger entries
 533      * use getInputStream.
 534      *
 535      * @param entry the zip file entry
 536      * @param buffer ByteBuffer to write the zip entry to
 537      *
 538      * @return the ByteBuffer with the contents of the specified zip file entry.
 539      *
 540      * @throws ZipException if a ZIP format error has occurred
 541      * @throws IOException if an I/O error has occurred
 542      * @throws IllegalStateException if the zip file has been closed
 543      * @throws BufferOverflowException if the zip entry is larger than the
 544      * supplied buffer
 545      * @throws ReadOnlyBufferException if the supplied buffer is read-only
 546      */
 547     private ByteBuffer fillByteBuffer(ZipEntry entry, ByteBuffer buffer) throws IOException {
 548         if (entry == null) {
 549             throw new NullPointerException("entry");
 550         }
 551         if (buffer.isReadOnly()) {
 552             throw new ReadOnlyBufferException();
 553         }
 554 
 555         long jzentry = 0;
 556         try {
 557             long csize;
 558             long size;
 559             int cmethod;
 560 
 561             synchronized (this) {
 562                 ensureOpen();
 563                 if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
 564                     jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
 565                 } else {
 566                     jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
 567                 }
 568                 if (jzentry == 0) {
 569                     return null;
 570                 }
 571 
 572                 csize = getEntryCSize(jzentry);
 573                 size = getEntrySize(jzentry);
 574                 cmethod = getEntryMethod(jzentry);
 575             }
 576 
 577             if (csize < 0 || size < 0) {
 578                 // Uknown size of zip entry (-1)
 579                 ByteBuffer tempBuffer = getByteBufferUnknownSize(entry);
 580                 buffer.put(tempBuffer); // Throws BufferOverflowException if too large
 581                 return buffer;
 582             }
 583             if (csize > MAX_BUFFER_SIZE || size > MAX_BUFFER_SIZE) {
 584                 throw new BufferOverflowException();
 585             }
 586 
 587             switch (cmethod) {
 588                 case STORED:
 589                     if (csize > buffer.remaining()) {
 590                         throw new BufferOverflowException();
 591                     }
 592                     return getByteBufferStored(jzentry, (int) csize, buffer);
 593                 case DEFLATED:
 594                     if (size > buffer.remaining()) {
 595                         throw new BufferOverflowException();
 596                     }
 597 
 598                     byte[] cbytes = new byte[(int) csize];
 599                     readCompressedBytes(jzentry, (int) csize, cbytes, 0);
 600 
 601                     Inflater inf = getInflater();
 602                     try {
 603                         inf.setInput(cbytes);
 604                         if (buffer.hasArray()) {
 605                             inf.inflate(buffer.array(), buffer.arrayOffset(), buffer.remaining());
 606                             buffer.position(buffer.position() + (int) size);
 607                         } else {
 608                             byte[] bytes = new byte[(int) size];
 609                             inf.inflate(bytes);
 610                             buffer.put(bytes);
 611                         }
 612                     } catch (DataFormatException e) {
 613                         String s = e.getMessage();
 614                         throw new ZipException(s != null ? s : "Invalid ZLIB data format");
 615                     } finally {
 616                         releaseInflater(inf);
 617                     }
 618                     return buffer;
 619                 default:
 620                     throw new ZipException("invalid compression method");
 621 
 622             }
 623         } finally {
 624             if (jzentry != 0) {
 625                 synchronized (this) {
 626                     if (jzfile != 0) {
 627                         freeEntry(jzfile, jzentry);
 628                     }
 629                 }
 630             }
 631         }
 632     }
 633 
 634     private ByteBuffer getByteBufferStored(long jzentry, int csize, ByteBuffer buffer)
 635             throws IOException {
 636         assert csize >= buffer.remaining();
 637         if (buffer.hasArray()) {
 638             int nread = readCompressedBytes(jzentry, csize, buffer.array(),
 639                                             buffer.arrayOffset());
 640             buffer.position(buffer.position() + nread);
 641         } else {
 642             byte[] cbytes = new byte[csize];
 643             readCompressedBytes(jzentry, csize, cbytes, 0);
 644             buffer.put(cbytes);
 645         }
 646         return buffer;
 647     }
 648 
 649     private int readCompressedBytes(long jzentry, int csize, byte[] buf, int off)
 650             throws IOException {
 651         assert csize >= buf.length - off;
 652         synchronized (this) {
 653             ensureOpenOrZipException();
 654             int n = 0;
 655             int nread = 0;
 656             while (nread < csize
 657                    && (n = read(jzfile, jzentry, nread, buf, off + nread, csize - nread)) > 0) {
 658                 nread += n;
 659             }
 660             return nread;
 661         }
 662     }
 663 
 664     private ByteBuffer getByteBufferUnknownSize(ZipEntry entry) throws IOException {
 665         final int BUFFER_SIZE = 8192;
 666         InputStream in = getInputStream(entry);
 667 
 668         if (in == null) {
 669             // not found
 670             return null;
 671         }
 672 
 673         try (in) {
 674             int capacity = in.available();
 675             if (capacity == 0) {
 676                 capacity = BUFFER_SIZE;
 677             }
 678 
 679             byte[] buf = new byte[capacity];
 680             int nread = 0;
 681             int n;
 682             for (;;) {
 683                 // read to EOF
 684                 while ((n = in.read(buf, nread, capacity - nread)) > 0) {
 685                     nread += n;
 686                 }
 687 
 688                 // if last call to source.read() returned -1, we are done
 689                 // otherwise, try to read one more byte; if that failed we're done too
 690                 if (n < 0 || (n = in.read()) < 0) {
 691                     break;
 692                 }
 693 
 694                 // one more byte was read; need to allocate a larger buffer
 695                 if (capacity < MAX_BUFFER_SIZE) {
 696                     capacity = Math.min(capacity << 1, MAX_BUFFER_SIZE);
 697                 } else {
 698                     throw new BufferOverflowException();
 699                 }
 700                 buf = Arrays.copyOf(buf, capacity);
 701                 buf[nread++] = (byte) n;
 702             }
 703 
 704             return ByteBuffer.wrap(buf, 0, nread);
 705         }
 706     }
 707 
 708     /**
 709      * Returns the path name of the ZIP file.
 710      * @return the path name of the ZIP file
 711      */
 712     public String getName() {
 713         return name;
 714     }
 715 
 716     private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
 717         private int i = 0;
 718 
 719         public ZipEntryIterator() {
 720             ensureOpen();
 721         }
 722 
 723         public boolean hasMoreElements() {
 724             return hasNext();
 725         }
 726 
 727         public boolean hasNext() {
 728             synchronized (ZipFile.this) {


 998             rem = 0;
 999             synchronized (ZipFile.this) {
1000                 if (jzentry != 0 && ZipFile.this.jzfile != 0) {
1001                     freeEntry(ZipFile.this.jzfile, jzentry);
1002                     jzentry = 0;
1003                 }
1004             }
1005             synchronized (streams) {
1006                 streams.remove(this);
1007             }
1008         }
1009 
1010         protected void finalize() {
1011             close();
1012         }
1013     }
1014 
1015     static {
1016         sun.misc.SharedSecrets.setJavaUtilZipFileAccess(
1017             new sun.misc.JavaUtilZipFileAccess() {
1018                 @Override
1019                 public boolean startsWithLocHeader(ZipFile zip) {
1020                     return zip.startsWithLocHeader();
1021                 }
1022 
1023                 @Override
1024                 public ByteBuffer getByteBuffer(ZipFile zip, ZipEntry entry)
1025                         throws IOException {
1026                     return zip.getByteBuffer(entry);
1027                 }
1028 
1029                 @Override
1030                 public ByteBuffer fillByteBuffer(ZipFile zip, ZipEntry entry,
1031                                                  ByteBuffer buffer) throws IOException {
1032                     return zip.fillByteBuffer(entry, buffer);
1033                 }
1034              }
1035         );
1036     }
1037 
1038     /**
1039      * Returns {@code true} if, and only if, the zip file begins with {@code
1040      * LOCSIG}.
1041      */
1042     private boolean startsWithLocHeader() {
1043         return locsig;
1044     }
1045 
1046     private static native long open(String name, int mode, long lastModified,
1047                                     boolean usemmap) throws IOException;
1048     private static native int getTotal(long jzfile);
1049     private static native boolean startsWithLOC(long jzfile);
1050     private static native int read(long jzfile, long jzentry,
1051                                    long pos, byte[] b, int off, int len);
1052 
< prev index next >