src/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.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;