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 |