1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.util.zip; 28 29 import java.io.Closeable; 30 import java.io.InputStream; 31 import java.io.IOException; 32 import java.io.EOFException; 33 import java.io.File; 34 import java.nio.charset.Charset; 35 import java.nio.charset.StandardCharsets; 36 import java.util.ArrayDeque; 37 import java.util.Deque; 38 import java.util.Enumeration; 39 import java.util.HashMap; 40 import java.util.HashSet; 41 import java.util.Map; 42 import java.util.NoSuchElementException; 43 import java.util.Set; 44 import java.util.WeakHashMap; 45 import java.security.AccessController; 46 47 import dalvik.system.CloseGuard; 48 import sun.security.action.GetPropertyAction; 49 50 import static java.util.zip.ZipConstants64.*; 51 52 /** 53 * This class is used to read entries from a zip file. 54 * 55 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor 56 * or method in this class will cause a {@link NullPointerException} to be 57 * thrown. 58 * 59 * @author David Connelly 60 */ 61 public 62 class ZipFile implements ZipConstants, Closeable { 63 private long jzfile; // address of jzfile data 64 private final String name; // zip file name 65 private final int total; // total number of entries 66 private final boolean locsig; // if zip file starts with LOCSIG (usually true) 67 private volatile boolean closeRequested = false; 68 69 private final CloseGuard guard = CloseGuard.get(); 70 71 // Android changed, needed for alternative OPEN_DELETE implementation 72 // that doesn't use unlink before closing the file. 73 private final File fileToRemoveOnClose; 74 75 private static final int STORED = ZipEntry.STORED; 76 private static final int DEFLATED = ZipEntry.DEFLATED; 77 78 /** 79 * Mode flag to open a zip file for reading. 80 */ 81 public static final int OPEN_READ = 0x1; 82 83 /** 84 * Mode flag to open a zip file and mark it for deletion. The file will be 85 * deleted some time between the moment that it is opened and the moment 86 * that it is closed, but its contents will remain accessible via the 87 * <tt>ZipFile</tt> object until either the close method is invoked or the 88 * virtual machine exits. 89 */ 90 public static final int OPEN_DELETE = 0x4; 91 92 private static final boolean usemmap; 93 94 static { 95 // Android-changed: always use mmap. 96 usemmap = true; 97 } 98 99 /** 100 * Opens a zip file for reading. 101 * 102 * <p>First, if there is a security manager, its <code>checkRead</code> 103 * method is called with the <code>name</code> argument as its argument 104 * to ensure the read is allowed. 105 * 106 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to 107 * decode the entry names and comments. 108 * 109 * @param name the name of the zip file 110 * @throws ZipException if a ZIP format error has occurred 111 * @throws IOException if an I/O error has occurred 112 * @throws SecurityException if a security manager exists and its 113 * <code>checkRead</code> method doesn't allow read access to the file. 114 * 115 * @see SecurityManager#checkRead(java.lang.String) 116 */ 117 public ZipFile(String name) throws IOException { 118 this(new File(name), OPEN_READ); 119 } 120 121 /** 122 * Opens a new <code>ZipFile</code> to read from the specified 123 * <code>File</code> object in the specified mode. The mode argument 124 * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>. 125 * 126 * <p>First, if there is a security manager, its <code>checkRead</code> 127 * method is called with the <code>name</code> argument as its argument to 128 * ensure the read is allowed. 129 * 130 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to 131 * decode the entry names and comments 132 * 133 * @param file the ZIP file to be opened for reading 134 * @param mode the mode in which the file is to be opened 135 * @throws ZipException if a ZIP format error has occurred 136 * @throws IOException if an I/O error has occurred 137 * @throws SecurityException if a security manager exists and 138 * its <code>checkRead</code> method 139 * doesn't allow read access to the file, 140 * or its <code>checkDelete</code> method doesn't allow deleting 141 * the file when the <tt>OPEN_DELETE</tt> flag is set. 142 * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid 143 * @see SecurityManager#checkRead(java.lang.String) 144 * @since 1.3 145 */ 146 public ZipFile(File file, int mode) throws IOException { 147 this(file, mode, StandardCharsets.UTF_8); 148 } 149 150 /** 151 * Opens a ZIP file for reading given the specified File object. 152 * 153 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to 154 * decode the entry names and comments. 155 * 156 * @param file the ZIP file to be opened for reading 157 * @throws ZipException if a ZIP format error has occurred 158 * @throws IOException if an I/O error has occurred 159 */ 160 public ZipFile(File file) throws ZipException, IOException { 161 this(file, OPEN_READ); 162 } 163 164 private ZipCoder zc; 165 166 /** 167 * Opens a new <code>ZipFile</code> to read from the specified 168 * <code>File</code> object in the specified mode. The mode argument 169 * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>. 170 * 171 * <p>First, if there is a security manager, its <code>checkRead</code> 172 * method is called with the <code>name</code> argument as its argument to 173 * ensure the read is allowed. 174 * 175 * @param file the ZIP file to be opened for reading 176 * @param mode the mode in which the file is to be opened 177 * @param charset 178 * the {@linkplain java.nio.charset.Charset charset} to 179 * be used to decode the ZIP entry name and comment that are not 180 * encoded by using UTF-8 encoding (indicated by entry's general 181 * purpose flag). 182 * 183 * @throws ZipException if a ZIP format error has occurred 184 * @throws IOException if an I/O error has occurred 185 * 186 * @throws SecurityException 187 * if a security manager exists and its <code>checkRead</code> 188 * method doesn't allow read access to the file,or its 189 * <code>checkDelete</code> method doesn't allow deleting the 190 * file when the <tt>OPEN_DELETE</tt> flag is set 191 * 192 * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid 193 * 194 * @see SecurityManager#checkRead(java.lang.String) 195 * 196 * @since 1.7 197 */ 198 public ZipFile(File file, int mode, Charset charset) throws IOException 199 { 200 if (((mode & OPEN_READ) == 0) || 201 ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) { 202 throw new IllegalArgumentException("Illegal mode: 0x"+ 203 Integer.toHexString(mode)); 204 } 205 206 // Android-changed: Error out early if the file is too short. 207 if (file.length() < ZipConstants.ENDHDR) { 208 throw new ZipException("File too short to be a zip file: " + file.length()); 209 } 210 String name = file.getPath(); 211 212 // Android changed, handle OPEN_DELETE case in #close(). 213 fileToRemoveOnClose = ((mode & OPEN_DELETE) != 0) ? file : null; 214 215 if (charset == null) 216 throw new NullPointerException("charset is null"); 217 this.zc = ZipCoder.get(charset); 218 jzfile = open(name, mode, file.lastModified(), usemmap); 219 this.name = name; 220 this.total = getTotal(jzfile); 221 this.locsig = startsWithLOC(jzfile); 222 Enumeration<? extends ZipEntry> entries = entries(); 223 224 // Android-changed: Error out early if the zipfile has no entries. 225 if (size() == 0 || !entries.hasMoreElements()) { 226 close(); 227 throw new ZipException("No entries"); 228 } 229 230 guard.open("close"); 231 } 232 233 /** 234 * Opens a zip file for reading. 235 * 236 * <p>First, if there is a security manager, its <code>checkRead</code> 237 * method is called with the <code>name</code> argument as its argument 238 * to ensure the read is allowed. 239 * 240 * @param name the name of the zip file 241 * @param charset 242 * the {@linkplain java.nio.charset.Charset charset} to 243 * be used to decode the ZIP entry name and comment that are not 244 * encoded by using UTF-8 encoding (indicated by entry's general 245 * purpose flag). 246 * 247 * @throws ZipException if a ZIP format error has occurred 248 * @throws IOException if an I/O error has occurred 249 * @throws SecurityException 250 * if a security manager exists and its <code>checkRead</code> 251 * method doesn't allow read access to the file 252 * 253 * @see SecurityManager#checkRead(java.lang.String) 254 * 255 * @since 1.7 256 */ 257 public ZipFile(String name, Charset charset) throws IOException 258 { 259 this(new File(name), OPEN_READ, charset); 260 } 261 262 /** 263 * Opens a ZIP file for reading given the specified File object. 264 * @param file the ZIP file to be opened for reading 265 * @param charset 266 * The {@linkplain java.nio.charset.Charset charset} to be 267 * used to decode the ZIP entry name and comment (ignored if 268 * the <a href="package-summary.html#lang_encoding"> language 269 * encoding bit</a> of the ZIP entry's general purpose bit 270 * flag is set). 271 * 272 * @throws ZipException if a ZIP format error has occurred 273 * @throws IOException if an I/O error has occurred 274 * 275 * @since 1.7 276 */ 277 public ZipFile(File file, Charset charset) throws IOException 278 { 279 this(file, OPEN_READ, charset); 280 } 281 282 /** 283 * Returns the zip file comment, or null if none. 284 * 285 * @return the comment string for the zip file, or null if none 286 * 287 * @throws IllegalStateException if the zip file has been closed 288 * 289 * Since 1.7 290 */ 291 public String getComment() { 292 synchronized (this) { 293 ensureOpen(); 294 byte[] bcomm = getCommentBytes(jzfile); 295 if (bcomm == null) 296 return null; 297 return zc.toString(bcomm, bcomm.length); 298 } 299 } 300 301 /** 302 * Returns the zip file entry for the specified name, or null 303 * if not found. 304 * 305 * @param name the name of the entry 306 * @return the zip file entry, or null if not found 307 * @throws IllegalStateException if the zip file has been closed 308 */ 309 public ZipEntry getEntry(String name) { 310 if (name == null) { 311 throw new NullPointerException("name"); 312 } 313 long jzentry = 0; 314 synchronized (this) { 315 ensureOpen(); 316 jzentry = getEntry(jzfile, zc.getBytes(name), true); 317 if (jzentry != 0) { 318 ZipEntry ze = getZipEntry(name, jzentry); 319 freeEntry(jzfile, jzentry); 320 return ze; 321 } 322 } 323 return null; 324 } 325 326 private static native long getEntry(long jzfile, byte[] name, 327 boolean addSlash); 328 329 // freeEntry releases the C jzentry struct. 330 private static native void freeEntry(long jzfile, long jzentry); 331 332 // the outstanding inputstreams that need to be closed, 333 // mapped to the inflater objects they use. 334 private final Map<InputStream, Inflater> streams = new WeakHashMap<>(); 335 336 /** 337 * Returns an input stream for reading the contents of the specified 338 * zip file entry. 339 * 340 * <p> Closing this ZIP file will, in turn, close all input 341 * streams that have been returned by invocations of this method. 342 * 343 * @param entry the zip file entry 344 * @return the input stream for reading the contents of the specified 345 * zip file entry. 346 * @throws ZipException if a ZIP format error has occurred 347 * @throws IOException if an I/O error has occurred 348 * @throws IllegalStateException if the zip file has been closed 349 */ 350 public InputStream getInputStream(ZipEntry entry) throws IOException { 351 if (entry == null) { 352 throw new NullPointerException("entry"); 353 } 354 long jzentry = 0; 355 ZipFileInputStream in = null; 356 synchronized (this) { 357 ensureOpen(); 358 if (!zc.isUTF8() && (entry.flag & EFS) != 0) { 359 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), true); 360 } else { 361 jzentry = getEntry(jzfile, zc.getBytes(entry.name), true); 362 } 363 if (jzentry == 0) { 364 return null; 365 } 366 in = new ZipFileInputStream(jzentry); 367 368 switch (getEntryMethod(jzentry)) { 369 case STORED: 370 synchronized (streams) { 371 streams.put(in, null); 372 } 373 return in; 374 case DEFLATED: 375 // MORE: Compute good size for inflater stream: 376 long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack 377 if (size > 65536) size = 8192; 378 if (size <= 0) size = 4096; 379 Inflater inf = getInflater(); 380 InputStream is = 381 new ZipFileInflaterInputStream(in, inf, (int)size); 382 synchronized (streams) { 383 streams.put(is, inf); 384 } 385 return is; 386 default: 387 throw new ZipException("invalid compression method"); 388 } 389 } 390 } 391 392 private class ZipFileInflaterInputStream extends InflaterInputStream { 393 private volatile boolean closeRequested = false; 394 private boolean eof = false; 395 private final ZipFileInputStream zfin; 396 397 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf, 398 int size) { 399 super(zfin, inf, size); 400 this.zfin = zfin; 401 } 402 403 public void close() throws IOException { 404 if (closeRequested) 405 return; 406 closeRequested = true; 407 408 super.close(); 409 Inflater inf; 410 synchronized (streams) { 411 inf = streams.remove(this); 412 } 413 if (inf != null) { 414 releaseInflater(inf); 415 } 416 } 417 418 // Override fill() method to provide an extra "dummy" byte 419 // at the end of the input stream. This is required when 420 // using the "nowrap" Inflater option. 421 protected void fill() throws IOException { 422 if (eof) { 423 throw new EOFException("Unexpected end of ZLIB input stream"); 424 } 425 len = in.read(buf, 0, buf.length); 426 if (len == -1) { 427 buf[0] = 0; 428 len = 1; 429 eof = true; 430 } 431 inf.setInput(buf, 0, len); 432 } 433 434 public int available() throws IOException { 435 if (closeRequested) 436 return 0; 437 long avail = zfin.size() - inf.getBytesWritten(); 438 return (avail > (long) Integer.MAX_VALUE ? 439 Integer.MAX_VALUE : (int) avail); 440 } 441 442 protected void finalize() throws Throwable { 443 close(); 444 } 445 } 446 447 /* 448 * Gets an inflater from the list of available inflaters or allocates 449 * a new one. 450 */ 451 private Inflater getInflater() { 452 Inflater inf; 453 synchronized (inflaterCache) { 454 while (null != (inf = inflaterCache.poll())) { 455 if (false == inf.ended()) { 456 return inf; 457 } 458 } 459 } 460 return new Inflater(true); 461 } 462 463 /* 464 * Releases the specified inflater to the list of available inflaters. 465 */ 466 private void releaseInflater(Inflater inf) { 467 if (false == inf.ended()) { 468 inf.reset(); 469 synchronized (inflaterCache) { 470 inflaterCache.add(inf); 471 } 472 } 473 } 474 475 // List of available Inflater objects for decompression 476 private Deque<Inflater> inflaterCache = new ArrayDeque<>(); 477 478 /** 479 * Returns the path name of the ZIP file. 480 * @return the path name of the ZIP file 481 */ 482 public String getName() { 483 return name; 484 } 485 486 /** 487 * Returns an enumeration of the ZIP file entries. 488 * @return an enumeration of the ZIP file entries 489 * @throws IllegalStateException if the zip file has been closed 490 */ 491 public Enumeration<? extends ZipEntry> entries() { 492 ensureOpen(); 493 return new Enumeration<ZipEntry>() { 494 private int i = 0; 495 public boolean hasMoreElements() { 496 synchronized (ZipFile.this) { 497 ensureOpen(); 498 return i < total; 499 } 500 } 501 public ZipEntry nextElement() throws NoSuchElementException { 502 synchronized (ZipFile.this) { 503 ensureOpen(); 504 if (i >= total) { 505 throw new NoSuchElementException(); 506 } 507 long jzentry = getNextEntry(jzfile, i++); 508 if (jzentry == 0) { 509 String message; 510 if (closeRequested) { 511 message = "ZipFile concurrently closed"; 512 } else { 513 message = getZipMessage(ZipFile.this.jzfile); 514 } 515 throw new ZipError("jzentry == 0" + 516 ",\n jzfile = " + ZipFile.this.jzfile + 517 ",\n total = " + ZipFile.this.total + 518 ",\n name = " + ZipFile.this.name + 519 ",\n i = " + i + 520 ",\n message = " + message 521 ); 522 } 523 ZipEntry ze = getZipEntry(null, jzentry); 524 freeEntry(jzfile, jzentry); 525 return ze; 526 } 527 } 528 }; 529 } 530 531 private ZipEntry getZipEntry(String name, long jzentry) { 532 ZipEntry e = new ZipEntry(); 533 e.flag = getEntryFlag(jzentry); // get the flag first 534 if (name != null) { 535 e.name = name; 536 } else { 537 byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME); 538 if (!zc.isUTF8() && (e.flag & EFS) != 0) { 539 e.name = zc.toStringUTF8(bname, bname.length); 540 } else { 541 e.name = zc.toString(bname, bname.length); 542 } 543 } 544 e.time = getEntryTime(jzentry); 545 e.crc = getEntryCrc(jzentry); 546 e.size = getEntrySize(jzentry); 547 e. csize = getEntryCSize(jzentry); 548 e.method = getEntryMethod(jzentry); 549 e.extra = getEntryBytes(jzentry, JZENTRY_EXTRA); 550 byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); 551 if (bcomm == null) { 552 e.comment = null; 553 } else { 554 if (!zc.isUTF8() && (e.flag & EFS) != 0) { 555 e.comment = zc.toStringUTF8(bcomm, bcomm.length); 556 } else { 557 e.comment = zc.toString(bcomm, bcomm.length); 558 } 559 } 560 return e; 561 } 562 563 private static native long getNextEntry(long jzfile, int i); 564 565 /** 566 * Returns the number of entries in the ZIP file. 567 * @return the number of entries in the ZIP file 568 * @throws IllegalStateException if the zip file has been closed 569 */ 570 public int size() { 571 ensureOpen(); 572 return total; 573 } 574 575 /** 576 * Closes the ZIP file. 577 * <p> Closing this ZIP file will close all of the input streams 578 * previously returned by invocations of the {@link #getInputStream 579 * getInputStream} method. 580 * 581 * @throws IOException if an I/O error has occurred 582 */ 583 public void close() throws IOException { 584 if (closeRequested) 585 return; 586 guard.close(); 587 closeRequested = true; 588 589 synchronized (this) { 590 // Close streams, release their inflaters 591 synchronized (streams) { 592 if (false == streams.isEmpty()) { 593 Map<InputStream, Inflater> copy = new HashMap<>(streams); 594 streams.clear(); 595 for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) { 596 e.getKey().close(); 597 Inflater inf = e.getValue(); 598 if (inf != null) { 599 inf.end(); 600 } 601 } 602 } 603 } 604 605 // Release cached inflaters 606 Inflater inf; 607 synchronized (inflaterCache) { 608 while (null != (inf = inflaterCache.poll())) { 609 inf.end(); 610 } 611 } 612 613 if (jzfile != 0) { 614 // Close the zip file 615 long zf = this.jzfile; 616 jzfile = 0; 617 618 close(zf); 619 } 620 621 // Android-changed, explicit delete for OPEN_DELETE ZipFile. 622 if (fileToRemoveOnClose != null) { 623 fileToRemoveOnClose.delete(); 624 } 625 } 626 } 627 628 /** 629 * Ensures that the system resources held by this ZipFile object are 630 * released when there are no more references to it. 631 * 632 * <p> 633 * Since the time when GC would invoke this method is undetermined, 634 * it is strongly recommended that applications invoke the <code>close</code> 635 * method as soon they have finished accessing this <code>ZipFile</code>. 636 * This will prevent holding up system resources for an undetermined 637 * length of time. 638 * 639 * @throws IOException if an I/O error has occurred 640 * @see java.util.zip.ZipFile#close() 641 */ 642 protected void finalize() throws IOException { 643 if (guard != null) { 644 guard.warnIfOpen(); 645 } 646 647 close(); 648 } 649 650 private static native void close(long jzfile); 651 652 private void ensureOpen() { 653 if (closeRequested) { 654 throw new IllegalStateException("zip file closed"); 655 } 656 657 if (jzfile == 0) { 658 throw new IllegalStateException("The object is not initialized."); 659 } 660 } 661 662 private void ensureOpenOrZipException() throws IOException { 663 if (closeRequested) { 664 throw new ZipException("ZipFile closed"); 665 } 666 } 667 668 /* 669 * Inner class implementing the input stream used to read a 670 * (possibly compressed) zip file entry. 671 */ 672 private class ZipFileInputStream extends InputStream { 673 private volatile boolean closeRequested = false; 674 protected long jzentry; // address of jzentry data 675 private long pos; // current position within entry data 676 protected long rem; // number of remaining bytes within entry 677 protected long size; // uncompressed size of this entry 678 679 ZipFileInputStream(long jzentry) { 680 pos = 0; 681 rem = getEntryCSize(jzentry); 682 size = getEntrySize(jzentry); 683 this.jzentry = jzentry; 684 } 685 686 public int read(byte b[], int off, int len) throws IOException { 687 // Android-changed : Always throw an exception on read if the zipfile 688 // has already been closed. 689 ensureOpenOrZipException(); 690 691 if (rem == 0) { 692 return -1; 693 } 694 if (len <= 0) { 695 return 0; 696 } 697 if (len > rem) { 698 len = (int) rem; 699 } 700 synchronized (ZipFile.this) { 701 len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, 702 off, len); 703 } 704 if (len > 0) { 705 pos += len; 706 rem -= len; 707 } 708 if (rem == 0) { 709 close(); 710 } 711 return len; 712 } 713 714 public int read() throws IOException { 715 byte[] b = new byte[1]; 716 if (read(b, 0, 1) == 1) { 717 return b[0] & 0xff; 718 } else { 719 return -1; 720 } 721 } 722 723 public long skip(long n) { 724 if (n > rem) 725 n = rem; 726 pos += n; 727 rem -= n; 728 if (rem == 0) { 729 close(); 730 } 731 return n; 732 } 733 734 public int available() { 735 return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem; 736 } 737 738 public long size() { 739 return size; 740 } 741 742 public void close() { 743 if (closeRequested) 744 return; 745 closeRequested = true; 746 747 rem = 0; 748 synchronized (ZipFile.this) { 749 if (jzentry != 0 && ZipFile.this.jzfile != 0) { 750 freeEntry(ZipFile.this.jzfile, jzentry); 751 jzentry = 0; 752 } 753 } 754 synchronized (streams) { 755 streams.remove(this); 756 } 757 } 758 759 protected void finalize() { 760 close(); 761 } 762 } 763 764 /** 765 * Returns {@code true} if, and only if, the zip file begins with {@code 766 * LOCSIG}. 767 * 768 * @hide 769 */ 770 public boolean startsWithLocHeader() { 771 return locsig; 772 } 773 774 /** @hide */ 775 // @VisibleForTesting 776 public int getFileDescriptor() { 777 return getFileDescriptor(jzfile); 778 } 779 780 private static native int getFileDescriptor(long jzfile); 781 782 private static native long open(String name, int mode, long lastModified, 783 boolean usemmap) throws IOException; 784 private static native int getTotal(long jzfile); 785 private static native boolean startsWithLOC(long jzfile); 786 private static native int read(long jzfile, long jzentry, 787 long pos, byte[] b, int off, int len); 788 789 // access to the native zentry object 790 private static native long getEntryTime(long jzentry); 791 private static native long getEntryCrc(long jzentry); 792 private static native long getEntryCSize(long jzentry); 793 private static native long getEntrySize(long jzentry); 794 private static native int getEntryMethod(long jzentry); 795 private static native int getEntryFlag(long jzentry); 796 private static native byte[] getCommentBytes(long jzfile); 797 798 private static final int JZENTRY_NAME = 0; 799 private static final int JZENTRY_EXTRA = 1; 800 private static final int JZENTRY_COMMENT = 2; 801 private static native byte[] getEntryBytes(long jzentry, int type); 802 803 private static native String getZipMessage(long jzfile); 804 } 805