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