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.util.Vector; 35 import java.util.Enumeration; 36 import java.util.Set; 37 import java.util.HashSet; 38 import java.util.NoSuchElementException; 39 import java.security.AccessController; 40 import sun.security.action.GetPropertyAction; 41 import static java.util.zip.ZipConstants64.*; 42 43 /** 44 * This class is used to read entries from a zip file. 45 * 46 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor 47 * or method in this class will cause a {@link NullPointerException} to be 48 * thrown. 49 * 50 * @author David Connelly 51 */ 52 public 53 class ZipFile implements ZipConstants, Closeable { 54 private long jzfile; // address of jzfile data 55 private String name; // zip file name 56 private int total; // total number of entries 57 private boolean closeRequested; 58 59 private static final int STORED = ZipEntry.STORED; 60 private static final int DEFLATED = ZipEntry.DEFLATED; 61 62 /** 63 * Mode flag to open a zip file for reading. 64 */ 65 public static final int OPEN_READ = 0x1; 66 67 /** 68 * Mode flag to open a zip file and mark it for deletion. The file will be 69 * deleted some time between the moment that it is opened and the moment 70 * that it is closed, but its contents will remain accessible via the 71 * <tt>ZipFile</tt> object until either the close method is invoked or the 72 * virtual machine exits. 73 */ 74 public static final int OPEN_DELETE = 0x4; 75 76 static { 77 /* Zip library is loaded from System.initializeSystemClass */ 297 } 298 long jzentry = 0; 299 synchronized (this) { 300 ensureOpen(); 301 jzentry = getEntry(jzfile, zc.getBytes(name), true); 302 if (jzentry != 0) { 303 ZipEntry ze = getZipEntry(name, jzentry); 304 freeEntry(jzfile, jzentry); 305 return ze; 306 } 307 } 308 return null; 309 } 310 311 private static native long getEntry(long jzfile, byte[] name, 312 boolean addSlash); 313 314 // freeEntry releases the C jzentry struct. 315 private static native void freeEntry(long jzfile, long jzentry); 316 317 // the outstanding inputstreams that need to be closed. 318 private Set<InputStream> streams = new HashSet<>(); 319 320 /** 321 * Returns an input stream for reading the contents of the specified 322 * zip file entry. 323 * 324 * <p> Closing this ZIP file will, in turn, close all input 325 * streams that have been returned by invocations of this method. 326 * 327 * @param entry the zip file entry 328 * @return the input stream for reading the contents of the specified 329 * zip file entry. 330 * @throws ZipException if a ZIP format error has occurred 331 * @throws IOException if an I/O error has occurred 332 * @throws IllegalStateException if the zip file has been closed 333 */ 334 public InputStream getInputStream(ZipEntry entry) throws IOException { 335 if (entry == null) { 336 throw new NullPointerException("entry"); 337 } 338 long jzentry = 0; 339 ZipFileInputStream in = null; 340 synchronized (this) { 341 ensureOpen(); 342 if (!zc.isUTF8() && (entry.flag & EFS) != 0) { 343 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); 344 } else { 345 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); 346 } 347 if (jzentry == 0) { 348 return null; 349 } 350 in = new ZipFileInputStream(jzentry); 351 352 switch (getEntryMethod(jzentry)) { 353 case STORED: 354 streams.add(in); 355 return in; 356 case DEFLATED: 357 final ZipFileInputStream zfin = in; 358 // MORE: Compute good size for inflater stream: 359 long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack 360 if (size > 65536) size = 8192; 361 if (size <= 0) size = 4096; 362 InputStream is = new InflaterInputStream(zfin, getInflater(), (int)size) { 363 private boolean isClosed = false; 364 365 public void close() throws IOException { 366 if (!isClosed) { 367 super.close(); 368 releaseInflater(inf); 369 isClosed = true; 370 } 371 } 372 // Override fill() method to provide an extra "dummy" byte 373 // at the end of the input stream. This is required when 374 // using the "nowrap" Inflater option. 375 protected void fill() throws IOException { 376 if (eof) { 377 throw new EOFException( 378 "Unexpected end of ZLIB input stream"); 379 } 380 len = this.in.read(buf, 0, buf.length); 381 if (len == -1) { 382 buf[0] = 0; 383 len = 1; 384 eof = true; 385 } 386 inf.setInput(buf, 0, len); 387 } 388 private boolean eof; 389 390 public int available() throws IOException { 391 if (isClosed) 392 return 0; 393 long avail = zfin.size() - inf.getBytesWritten(); 394 return avail > (long) Integer.MAX_VALUE ? 395 Integer.MAX_VALUE : (int) avail; 396 } 397 }; 398 streams.add(is); 399 return is; 400 default: 401 throw new ZipException("invalid compression method"); 402 } 403 } 404 } 405 406 /* 407 * Gets an inflater from the list of available inflaters or allocates 408 * a new one. 409 */ 410 private Inflater getInflater() { 411 synchronized (inflaters) { 412 int size = inflaters.size(); 413 if (size > 0) { 414 Inflater inf = (Inflater)inflaters.remove(size - 1); 415 return inf; 416 } else { 417 return new Inflater(true); 418 } 419 } 420 } 421 422 /* 423 * Releases the specified inflater to the list of available inflaters. 424 */ 425 private void releaseInflater(Inflater inf) { 426 synchronized (inflaters) { 427 if (inf.ended()) 428 return; 429 inf.reset(); 430 inflaters.add(inf); 431 } 432 } 433 434 // List of available Inflater objects for decompression 435 private Vector inflaters = new Vector(); 436 437 /** 438 * Returns the path name of the ZIP file. 439 * @return the path name of the ZIP file 440 */ 441 public String getName() { 442 return name; 443 } 444 445 /** 446 * Returns an enumeration of the ZIP file entries. 447 * @return an enumeration of the ZIP file entries 448 * @throws IllegalStateException if the zip file has been closed 449 */ 450 public Enumeration<? extends ZipEntry> entries() { 451 ensureOpen(); 452 return new Enumeration<ZipEntry>() { 453 private int i = 0; 454 public boolean hasMoreElements() { 455 synchronized (ZipFile.this) { 523 524 /** 525 * Returns the number of entries in the ZIP file. 526 * @return the number of entries in the ZIP file 527 * @throws IllegalStateException if the zip file has been closed 528 */ 529 public int size() { 530 ensureOpen(); 531 return total; 532 } 533 534 /** 535 * Closes the ZIP file. 536 * <p> Closing this ZIP file will close all of the input streams 537 * previously returned by invocations of the {@link #getInputStream 538 * getInputStream} method. 539 * 540 * @throws IOException if an I/O error has occurred 541 */ 542 public void close() throws IOException { 543 synchronized (this) { 544 closeRequested = true; 545 546 if (streams.size() !=0) { 547 Set<InputStream> copy = streams; 548 streams = new HashSet<>(); 549 for (InputStream is: copy) 550 is.close(); 551 } 552 553 if (jzfile != 0) { 554 // Close the zip file 555 long zf = this.jzfile; 556 jzfile = 0; 557 558 close(zf); 559 560 // Release inflaters 561 synchronized (inflaters) { 562 int size = inflaters.size(); 563 for (int i = 0; i < size; i++) { 564 Inflater inf = (Inflater)inflaters.get(i); 565 inf.end(); 566 } 567 } 568 } 569 } 570 } 571 572 573 /** 574 * Ensures that the <code>close</code> method of this ZIP file is 575 * called when there are no more references to it. 576 * 577 * <p> 578 * Since the time when GC would invoke this method is undetermined, 579 * it is strongly recommended that applications invoke the <code>close</code> 580 * method as soon they have finished accessing this <code>ZipFile</code>. 581 * This will prevent holding up system resources for an undetermined 582 * length of time. 583 * 584 * @throws IOException if an I/O error has occurred 585 * @see java.util.zip.ZipFile#close() 586 */ 587 protected void finalize() throws IOException { 588 close(); 589 } 590 591 private static native void close(long jzfile); 592 593 private void ensureOpen() { 594 if (closeRequested) { 595 throw new IllegalStateException("zip file closed"); 596 } 597 598 if (jzfile == 0) { 599 throw new IllegalStateException("The object is not initialized."); 600 } 601 } 602 603 private void ensureOpenOrZipException() throws IOException { 604 if (closeRequested) { 605 throw new ZipException("ZipFile closed"); 606 } 607 } 608 609 /* 610 * Inner class implementing the input stream used to read a 611 * (possibly compressed) zip file entry. 612 */ 613 private class ZipFileInputStream extends InputStream { 614 protected long jzentry; // address of jzentry data 615 private long pos; // current position within entry data 616 protected long rem; // number of remaining bytes within entry 617 protected long size; // uncompressed size of this entry 618 619 ZipFileInputStream(long jzentry) { 620 pos = 0; 621 rem = getEntryCSize(jzentry); 622 size = getEntrySize(jzentry); 623 this.jzentry = jzentry; 624 } 625 626 public int read(byte b[], int off, int len) throws IOException { 627 if (rem == 0) { 628 return -1; 629 } 630 if (len <= 0) { 631 return 0; 632 } 633 if (len > rem) { 661 public long skip(long n) { 662 if (n > rem) 663 n = rem; 664 pos += n; 665 rem -= n; 666 if (rem == 0) { 667 close(); 668 } 669 return n; 670 } 671 672 public int available() { 673 return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem; 674 } 675 676 public long size() { 677 return size; 678 } 679 680 public void close() { 681 rem = 0; 682 synchronized (ZipFile.this) { 683 if (jzentry != 0 && ZipFile.this.jzfile != 0) { 684 freeEntry(ZipFile.this.jzfile, jzentry); 685 jzentry = 0; 686 } 687 streams.remove(this); 688 } 689 } 690 } 691 692 693 private static native long open(String name, int mode, long lastModified, 694 boolean usemmap) throws IOException; 695 private static native int getTotal(long jzfile); 696 private static native int read(long jzfile, long jzentry, 697 long pos, byte[] b, int off, int len); 698 699 // access to the native zentry object 700 private static native long getEntryTime(long jzentry); 701 private static native long getEntryCrc(long jzentry); 702 private static native long getEntryCSize(long jzentry); 703 private static native long getEntrySize(long jzentry); 704 private static native int getEntryMethod(long jzentry); 705 private static native int getEntryFlag(long jzentry); 706 private static native byte[] getCommentBytes(long jzfile); 707 708 private static final int JZENTRY_NAME = 0; 709 private static final int JZENTRY_EXTRA = 1; | 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.util.ArrayDeque; 35 import java.util.Deque; 36 import java.util.Enumeration; 37 import java.util.HashMap; 38 import java.util.Map; 39 import java.util.NoSuchElementException; 40 import java.util.WeakHashMap; 41 import java.security.AccessController; 42 import sun.security.action.GetPropertyAction; 43 import static java.util.zip.ZipConstants64.*; 44 45 /** 46 * This class is used to read entries from a zip file. 47 * 48 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor 49 * or method in this class will cause a {@link NullPointerException} to be 50 * thrown. 51 * 52 * @author David Connelly 53 */ 54 public 55 class ZipFile implements ZipConstants, Closeable { 56 private long jzfile; // address of jzfile data 57 private String name; // zip file name 58 private int total; // total number of entries 59 private volatile boolean closeRequested = false; 60 61 private static final int STORED = ZipEntry.STORED; 62 private static final int DEFLATED = ZipEntry.DEFLATED; 63 64 /** 65 * Mode flag to open a zip file for reading. 66 */ 67 public static final int OPEN_READ = 0x1; 68 69 /** 70 * Mode flag to open a zip file and mark it for deletion. The file will be 71 * deleted some time between the moment that it is opened and the moment 72 * that it is closed, but its contents will remain accessible via the 73 * <tt>ZipFile</tt> object until either the close method is invoked or the 74 * virtual machine exits. 75 */ 76 public static final int OPEN_DELETE = 0x4; 77 78 static { 79 /* Zip library is loaded from System.initializeSystemClass */ 299 } 300 long jzentry = 0; 301 synchronized (this) { 302 ensureOpen(); 303 jzentry = getEntry(jzfile, zc.getBytes(name), true); 304 if (jzentry != 0) { 305 ZipEntry ze = getZipEntry(name, jzentry); 306 freeEntry(jzfile, jzentry); 307 return ze; 308 } 309 } 310 return null; 311 } 312 313 private static native long getEntry(long jzfile, byte[] name, 314 boolean addSlash); 315 316 // freeEntry releases the C jzentry struct. 317 private static native void freeEntry(long jzfile, long jzentry); 318 319 // the outstanding inputstreams that need to be closed, 320 // mapped to the inflater objects they use. 321 private final Map<InputStream, Inflater> streams = new WeakHashMap<>(); 322 323 /** 324 * Returns an input stream for reading the contents of the specified 325 * zip file entry. 326 * 327 * <p> Closing this ZIP file will, in turn, close all input 328 * streams that have been returned by invocations of this method. 329 * 330 * @param entry the zip file entry 331 * @return the input stream for reading the contents of the specified 332 * zip file entry. 333 * @throws ZipException if a ZIP format error has occurred 334 * @throws IOException if an I/O error has occurred 335 * @throws IllegalStateException if the zip file has been closed 336 */ 337 public InputStream getInputStream(ZipEntry entry) throws IOException { 338 if (entry == null) { 339 throw new NullPointerException("entry"); 340 } 341 long jzentry = 0; 342 ZipFileInputStream in = null; 343 synchronized (this) { 344 ensureOpen(); 345 if (!zc.isUTF8() && (entry.flag & EFS) != 0) { 346 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); 347 } else { 348 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); 349 } 350 if (jzentry == 0) { 351 return null; 352 } 353 in = new ZipFileInputStream(jzentry); 354 355 switch (getEntryMethod(jzentry)) { 356 case STORED: 357 synchronized (streams) { 358 streams.put(in, null); 359 } 360 return in; 361 case DEFLATED: 362 // MORE: Compute good size for inflater stream: 363 long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack 364 if (size > 65536) size = 8192; 365 if (size <= 0) size = 4096; 366 Inflater inf = getInflater(); 367 InputStream is = 368 new ZipFileInflaterInputStream(in, inf, (int)size); 369 synchronized (streams) { 370 streams.put(is, inf); 371 } 372 return is; 373 default: 374 throw new ZipException("invalid compression method"); 375 } 376 } 377 } 378 379 private class ZipFileInflaterInputStream extends InflaterInputStream { 380 private volatile boolean closeRequested = false; 381 private boolean eof = false; 382 private final ZipFileInputStream zfin; 383 384 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf, 385 int size) { 386 super(zfin, inf, size); 387 this.zfin = zfin; 388 } 389 390 public void close() throws IOException { 391 if (closeRequested) 392 return; 393 closeRequested = true; 394 395 super.close(); 396 Inflater inf; 397 synchronized (streams) { 398 inf = streams.remove(this); 399 } 400 if (inf != null) { 401 releaseInflater(inf); 402 } 403 } 404 405 // Override fill() method to provide an extra "dummy" byte 406 // at the end of the input stream. This is required when 407 // using the "nowrap" Inflater option. 408 protected void fill() throws IOException { 409 if (eof) { 410 throw new EOFException("Unexpected end of ZLIB input stream"); 411 } 412 len = in.read(buf, 0, buf.length); 413 if (len == -1) { 414 buf[0] = 0; 415 len = 1; 416 eof = true; 417 } 418 inf.setInput(buf, 0, len); 419 } 420 421 public int available() throws IOException { 422 if (closeRequested) 423 return 0; 424 long avail = zfin.size() - inf.getBytesWritten(); 425 return (avail > (long) Integer.MAX_VALUE ? 426 Integer.MAX_VALUE : (int) avail); 427 } 428 429 protected void finalize() throws Throwable { 430 close(); 431 } 432 } 433 434 /* 435 * Gets an inflater from the list of available inflaters or allocates 436 * a new one. 437 */ 438 private Inflater getInflater() { 439 Inflater inf; 440 synchronized (inflaterCache) { 441 while (null != (inf = inflaterCache.poll())) { 442 if (false == inf.ended()) { 443 return inf; 444 } 445 } 446 } 447 return new Inflater(true); 448 } 449 450 /* 451 * Releases the specified inflater to the list of available inflaters. 452 */ 453 private void releaseInflater(Inflater inf) { 454 if (false == inf.ended()) { 455 inf.reset(); 456 synchronized (inflaterCache) { 457 inflaterCache.add(inf); 458 } 459 } 460 } 461 462 // List of available Inflater objects for decompression 463 private Deque<Inflater> inflaterCache = new ArrayDeque<>(); 464 465 /** 466 * Returns the path name of the ZIP file. 467 * @return the path name of the ZIP file 468 */ 469 public String getName() { 470 return name; 471 } 472 473 /** 474 * Returns an enumeration of the ZIP file entries. 475 * @return an enumeration of the ZIP file entries 476 * @throws IllegalStateException if the zip file has been closed 477 */ 478 public Enumeration<? extends ZipEntry> entries() { 479 ensureOpen(); 480 return new Enumeration<ZipEntry>() { 481 private int i = 0; 482 public boolean hasMoreElements() { 483 synchronized (ZipFile.this) { 551 552 /** 553 * Returns the number of entries in the ZIP file. 554 * @return the number of entries in the ZIP file 555 * @throws IllegalStateException if the zip file has been closed 556 */ 557 public int size() { 558 ensureOpen(); 559 return total; 560 } 561 562 /** 563 * Closes the ZIP file. 564 * <p> Closing this ZIP file will close all of the input streams 565 * previously returned by invocations of the {@link #getInputStream 566 * getInputStream} method. 567 * 568 * @throws IOException if an I/O error has occurred 569 */ 570 public void close() throws IOException { 571 if (closeRequested) 572 return; 573 closeRequested = true; 574 575 synchronized (this) { 576 // Close streams, release their inflaters 577 synchronized (streams) { 578 if (false == streams.isEmpty()) { 579 Map<InputStream, Inflater> copy = new HashMap<>(streams); 580 streams.clear(); 581 for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) { 582 e.getKey().close(); 583 Inflater inf = e.getValue(); 584 if (inf != null) { 585 inf.end(); 586 } 587 } 588 } 589 } 590 591 // Release cached inflaters 592 Inflater inf; 593 synchronized (inflaterCache) { 594 while (null != (inf = inflaterCache.poll())) { 595 inf.end(); 596 } 597 } 598 599 if (jzfile != 0) { 600 // Close the zip file 601 long zf = this.jzfile; 602 jzfile = 0; 603 604 close(zf); 605 } 606 } 607 } 608 609 /** 610 * Ensures that the system resources held by this ZipFile object are 611 * released when there are no more references to it. 612 * 613 * <p> 614 * Since the time when GC would invoke this method is undetermined, 615 * it is strongly recommended that applications invoke the <code>close</code> 616 * method as soon they have finished accessing this <code>ZipFile</code>. 617 * This will prevent holding up system resources for an undetermined 618 * length of time. 619 * 620 * @throws IOException if an I/O error has occurred 621 * @see java.util.zip.ZipFile#close() 622 */ 623 protected void finalize() throws IOException { 624 close(); 625 } 626 627 private static native void close(long jzfile); 628 629 private void ensureOpen() { 630 if (closeRequested) { 631 throw new IllegalStateException("zip file closed"); 632 } 633 634 if (jzfile == 0) { 635 throw new IllegalStateException("The object is not initialized."); 636 } 637 } 638 639 private void ensureOpenOrZipException() throws IOException { 640 if (closeRequested) { 641 throw new ZipException("ZipFile closed"); 642 } 643 } 644 645 /* 646 * Inner class implementing the input stream used to read a 647 * (possibly compressed) zip file entry. 648 */ 649 private class ZipFileInputStream extends InputStream { 650 private volatile boolean closeRequested = false; 651 protected long jzentry; // address of jzentry data 652 private long pos; // current position within entry data 653 protected long rem; // number of remaining bytes within entry 654 protected long size; // uncompressed size of this entry 655 656 ZipFileInputStream(long jzentry) { 657 pos = 0; 658 rem = getEntryCSize(jzentry); 659 size = getEntrySize(jzentry); 660 this.jzentry = jzentry; 661 } 662 663 public int read(byte b[], int off, int len) throws IOException { 664 if (rem == 0) { 665 return -1; 666 } 667 if (len <= 0) { 668 return 0; 669 } 670 if (len > rem) { 698 public long skip(long n) { 699 if (n > rem) 700 n = rem; 701 pos += n; 702 rem -= n; 703 if (rem == 0) { 704 close(); 705 } 706 return n; 707 } 708 709 public int available() { 710 return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem; 711 } 712 713 public long size() { 714 return size; 715 } 716 717 public void close() { 718 if (closeRequested) 719 return; 720 closeRequested = true; 721 722 rem = 0; 723 synchronized (ZipFile.this) { 724 if (jzentry != 0 && ZipFile.this.jzfile != 0) { 725 freeEntry(ZipFile.this.jzfile, jzentry); 726 jzentry = 0; 727 } 728 } 729 synchronized (streams) { 730 streams.remove(this); 731 } 732 } 733 734 protected void finalize() { 735 close(); 736 } 737 } 738 739 740 private static native long open(String name, int mode, long lastModified, 741 boolean usemmap) throws IOException; 742 private static native int getTotal(long jzfile); 743 private static native int read(long jzfile, long jzentry, 744 long pos, byte[] b, int off, int len); 745 746 // access to the native zentry object 747 private static native long getEntryTime(long jzentry); 748 private static native long getEntryCrc(long jzentry); 749 private static native long getEntryCSize(long jzentry); 750 private static native long getEntrySize(long jzentry); 751 private static native int getEntryMethod(long jzentry); 752 private static native int getEntryFlag(long jzentry); 753 private static native byte[] getCommentBytes(long jzfile); 754 755 private static final int JZENTRY_NAME = 0; 756 private static final int JZENTRY_EXTRA = 1; |