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