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