1 /*
2 * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
3 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4 *
5 *
6 *
7 *
8 *
9 *
10 *
11 *
12 *
13 *
14 *
15 *
16 *
17 *
18 *
19 *
20 *
21 *
22 *
23 *
24 */
25
26 package java.util.zip;
27
28 import java.io.Closeable;
29 import java.io.InputStream;
30 import java.io.IOException;
31 import java.io.EOFException;
32 import java.io.File;
33 import java.nio.charset.Charset;
34 import java.nio.charset.StandardCharsets;
35 import java.util.ArrayDeque;
36 import java.util.Deque;
37 import java.util.Enumeration;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.Map;
41 import java.util.NoSuchElementException;
42 import java.util.Spliterator;
43 import java.util.Spliterators;
44 import java.util.WeakHashMap;
45 import java.util.stream.Stream;
46 import java.util.stream.StreamSupport;
47
48 import static java.util.zip.ZipConstants64.*;
49
50 /**
51 * This class is used to read entries from a zip file.
52 *
53 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
54 * or method in this class will cause a {@link NullPointerException} to be
55 * thrown.
56 *
57 * @author David Connelly
58 */
59 public
60 class ZipFile implements ZipConstants, Closeable {
61 private long jzfile; // address of jzfile data
62 private final String name; // zip file name
63 private final int total; // total number of entries
64 private final boolean locsig; // if zip file starts with LOCSIG (usually true)
65 private volatile boolean closeRequested = false;
66
67 private static final int STORED = ZipEntry.STORED;
68 private static final int DEFLATED = ZipEntry.DEFLATED;
69
70 /**
71 * Mode flag to open a zip file for reading.
72 */
73 public static final int OPEN_READ = 0x1;
74
75 /**
76 * Mode flag to open a zip file and mark it for deletion. The file will be
77 * deleted some time between the moment that it is opened and the moment
78 * that it is closed, but its contents will remain accessible via the
79 * <tt>ZipFile</tt> object until either the close method is invoked or the
80 * virtual machine exits.
81 */
82 public static final int OPEN_DELETE = 0x4;
83
84 static {
85 /* Zip library is loaded from System.initializeSystemClass */
86 initIDs();
87 }
88
89 private static native void initIDs();
90
91 private static final boolean usemmap;
92
93 private static final boolean ensuretrailingslash;
94
95 static {
96 // A system prpperty to disable mmap use to avoid vm crash when
97 // in-use zip file is accidently overwritten by others.
98 String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping");
99 usemmap = (prop == null ||
100 !(prop.length() == 0 || prop.equalsIgnoreCase("true")));
101
102 // see getEntry() for details
103 prop = sun.misc.VM.getSavedProperty("jdk.util.zip.ensureTrailingSlash");
104 ensuretrailingslash = prop == null || !prop.equalsIgnoreCase("false");
105 }
106
107 /**
108 * Opens a zip file for reading.
109 *
110 * <p>First, if there is a security manager, its <code>checkRead</code>
111 * method is called with the <code>name</code> argument as its argument
112 * to ensure the read is allowed.
113 *
114 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
115 * decode the entry names and comments.
116 *
117 * @param name the name of the zip file
118 * @throws ZipException if a ZIP format error has occurred
119 * @throws IOException if an I/O error has occurred
120 * @throws SecurityException if a security manager exists and its
121 * <code>checkRead</code> method doesn't allow read access to the file.
122 *
123 * @see SecurityManager#checkRead(java.lang.String)
124 */
125 public ZipFile(String name) throws IOException {
126 this(new File(name), OPEN_READ);
127 }
128
129 /**
130 * Opens a new <code>ZipFile</code> to read from the specified
131 * <code>File</code> object in the specified mode. The mode argument
132 * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
133 *
134 * <p>First, if there is a security manager, its <code>checkRead</code>
135 * method is called with the <code>name</code> argument as its argument to
136 * ensure the read is allowed.
137 *
138 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
139 * decode the entry names and comments
140 *
141 * @param file the ZIP file to be opened for reading
142 * @param mode the mode in which the file is to be opened
143 * @throws ZipException if a ZIP format error has occurred
144 * @throws IOException if an I/O error has occurred
145 * @throws SecurityException if a security manager exists and
146 * its <code>checkRead</code> method
147 * doesn't allow read access to the file,
148 * or its <code>checkDelete</code> method doesn't allow deleting
149 * the file when the <tt>OPEN_DELETE</tt> flag is set.
150 * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
151 * @see SecurityManager#checkRead(java.lang.String)
152 * @since 1.3
153 */
154 public ZipFile(File file, int mode) throws IOException {
155 this(file, mode, StandardCharsets.UTF_8);
156 }
157
158 /**
159 * Opens a ZIP file for reading given the specified File object.
160 *
161 * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
162 * decode the entry names and comments.
163 *
164 * @param file the ZIP file to be opened for reading
165 * @throws ZipException if a ZIP format error has occurred
166 * @throws IOException if an I/O error has occurred
167 */
168 public ZipFile(File file) throws ZipException, IOException {
169 this(file, OPEN_READ);
170 }
171
172 private ZipCoder zc;
173
174 /**
175 * Opens a new <code>ZipFile</code> to read from the specified
176 * <code>File</code> object in the specified mode. The mode argument
177 * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
178 *
179 * <p>First, if there is a security manager, its <code>checkRead</code>
180 * method is called with the <code>name</code> argument as its argument to
181 * ensure the read is allowed.
182 *
183 * @param file the ZIP file to be opened for reading
184 * @param mode the mode in which the file is to be opened
185 * @param charset
186 * the {@linkplain java.nio.charset.Charset charset} to
187 * be used to decode the ZIP entry name and comment that are not
188 * encoded by using UTF-8 encoding (indicated by entry's general
189 * purpose flag).
190 *
191 * @throws ZipException if a ZIP format error has occurred
192 * @throws IOException if an I/O error has occurred
193 *
194 * @throws SecurityException
195 * if a security manager exists and its <code>checkRead</code>
196 * method doesn't allow read access to the file,or its
197 * <code>checkDelete</code> method doesn't allow deleting the
198 * file when the <tt>OPEN_DELETE</tt> flag is set
199 *
200 * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
201 *
202 * @see SecurityManager#checkRead(java.lang.String)
203 *
204 * @since 1.7
205 */
206 public ZipFile(File file, int mode, Charset charset) throws IOException
207 {
208 if (((mode & OPEN_READ) == 0) ||
209 ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
210 throw new IllegalArgumentException("Illegal mode: 0x"+
211 Integer.toHexString(mode));
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 long t0 = System.nanoTime();
225 jzfile = open(name, mode, file.lastModified(), usemmap);
226 sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
227 sun.misc.PerfCounter.getZipFileCount().increment();
228 this.name = name;
229 this.total = getTotal(jzfile);
230 this.locsig = startsWithLOC(jzfile);
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 // If no entry is found for the specified 'name' and
319 // the 'name' does not end with a forward slash '/',
320 // the implementation tries to find the entry with a
321 // slash '/' appended to the end of the 'name', before
322 // returning null. When such entry is found, the name
323 // that actually is found (with a slash '/' attached)
324 // is used
325 // (disabled if jdk.util.zip.ensureTrailingSlash=false)
326 ZipEntry ze = ensuretrailingslash ? getZipEntry(null, jzentry)
327 : getZipEntry(name, jzentry);
328 freeEntry(jzfile, jzentry);
329 return ze;
330 }
331 }
332 return null;
333 }
334
335 private static native long getEntry(long jzfile, byte[] name,
336 boolean addSlash);
337
338 // freeEntry releases the C jzentry struct.
339 private static native void freeEntry(long jzfile, long jzentry);
340
341 // the outstanding inputstreams that need to be closed,
342 // mapped to the inflater objects they use.
343 private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
344
345 /**
346 * Returns an input stream for reading the contents of the specified
347 * zip file entry.
348 *
349 * <p> Closing this ZIP file will, in turn, close all input
350 * streams that have been returned by invocations of this method.
351 *
352 * @param entry the zip file entry
353 * @return the input stream for reading the contents of the specified
354 * zip file entry.
355 * @throws ZipException if a ZIP format error has occurred
356 * @throws IOException if an I/O error has occurred
357 * @throws IllegalStateException if the zip file has been closed
358 */
359 public InputStream getInputStream(ZipEntry entry) throws IOException {
360 if (entry == null) {
361 throw new NullPointerException("entry");
362 }
363 long jzentry = 0;
364 ZipFileInputStream in = null;
365 synchronized (this) {
366 ensureOpen();
367 if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
368 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
369 } else {
370 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
371 }
372 if (jzentry == 0) {
373 return null;
374 }
375 in = new ZipFileInputStream(jzentry);
376
377 switch (getEntryMethod(jzentry)) {
378 case STORED:
379 synchronized (streams) {
380 streams.put(in, null);
381 }
382 return in;
383 case DEFLATED:
384 // MORE: Compute good size for inflater stream:
385 long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack
386 if (size > 65536) size = 8192;
387 if (size <= 0) size = 4096;
388 Inflater inf = getInflater();
389 InputStream is =
390 new ZipFileInflaterInputStream(in, inf, (int)size);
391 synchronized (streams) {
392 streams.put(is, inf);
393 }
394 return is;
395 default:
396 throw new ZipException("invalid compression method");
397 }
398 }
399 }
400
401 private class ZipFileInflaterInputStream extends InflaterInputStream {
402 private volatile boolean closeRequested = false;
403 private boolean eof = false;
404 private final ZipFileInputStream zfin;
405
406 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
407 int size) {
408 super(zfin, inf, size);
409 this.zfin = zfin;
410 }
411
412 public void close() throws IOException {
413 if (closeRequested)
414 return;
415 closeRequested = true;
416
417 super.close();
418 Inflater inf;
419 synchronized (streams) {
420 inf = streams.remove(this);
421 }
422 if (inf != null) {
423 releaseInflater(inf);
424 }
425 }
426
427 // Override fill() method to provide an extra "dummy" byte
428 // at the end of the input stream. This is required when
429 // using the "nowrap" Inflater option.
430 protected void fill() throws IOException {
431 if (eof) {
432 throw new EOFException("Unexpected end of ZLIB input stream");
433 }
434 len = in.read(buf, 0, buf.length);
435 if (len == -1) {
436 buf[0] = 0;
437 len = 1;
438 eof = true;
439 }
440 inf.setInput(buf, 0, len);
441 }
442
443 public int available() throws IOException {
444 if (closeRequested)
445 return 0;
446 long avail = zfin.size() - inf.getBytesWritten();
447 return (avail > (long) Integer.MAX_VALUE ?
448 Integer.MAX_VALUE : (int) avail);
449 }
450
451 protected void finalize() throws Throwable {
452 close();
453 }
454 }
455
456 /*
457 * Gets an inflater from the list of available inflaters or allocates
458 * a new one.
459 */
460 private Inflater getInflater() {
461 Inflater inf;
462 synchronized (inflaterCache) {
463 while (null != (inf = inflaterCache.poll())) {
464 if (false == inf.ended()) {
465 return inf;
466 }
467 }
468 }
469 return new Inflater(true);
470 }
471
472 /*
473 * Releases the specified inflater to the list of available inflaters.
474 */
475 private void releaseInflater(Inflater inf) {
476 if (false == inf.ended()) {
477 inf.reset();
478 synchronized (inflaterCache) {
479 inflaterCache.add(inf);
480 }
481 }
482 }
483
484 // List of available Inflater objects for decompression
485 private Deque<Inflater> inflaterCache = new ArrayDeque<>();
486
487 /**
488 * Returns the path name of the ZIP file.
489 * @return the path name of the ZIP file
490 */
491 public String getName() {
492 return name;
493 }
494
495 private class ZipEntryIterator implements Enumeration<ZipEntry>, Iterator<ZipEntry> {
496 private int i = 0;
497
498 public ZipEntryIterator() {
499 ensureOpen();
500 }
501
502 public boolean hasMoreElements() {
503 return hasNext();
504 }
505
506 public boolean hasNext() {
507 synchronized (ZipFile.this) {
508 ensureOpen();
509 return i < total;
510 }
511 }
512
513 public ZipEntry nextElement() {
514 return next();
515 }
516
517 public ZipEntry next() {
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 * Returns an enumeration of the ZIP file entries.
548 * @return an enumeration of the ZIP file entries
549 * @throws IllegalStateException if the zip file has been closed
550 */
551 public Enumeration<? extends ZipEntry> entries() {
552 return new ZipEntryIterator();
553 }
554
555 /**
556 * Return an ordered {@code Stream} over the ZIP file entries.
557 * Entries appear in the {@code Stream} in the order they appear in
558 * the central directory of the ZIP file.
559 *
560 * @return an ordered {@code Stream} of entries in this ZIP file
561 * @throws IllegalStateException if the zip file has been closed
562 * @since 1.8
563 */
564 public Stream<? extends ZipEntry> stream() {
565 return StreamSupport.stream(Spliterators.spliterator(
566 new ZipEntryIterator(), size(),
567 Spliterator.ORDERED | Spliterator.DISTINCT |
568 Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
569 }
570
571 private ZipEntry getZipEntry(String name, long jzentry) {
572 ZipEntry e = new ZipEntry();
573 e.flag = getEntryFlag(jzentry); // get the flag first
574 if (name != null) {
575 e.name = name;
576 } else {
577 byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME);
578 if (bname == null) {
579 e.name = ""; // length 0 empty name
580 } else if (!zc.isUTF8() && (e.flag & EFS) != 0) {
581 e.name = zc.toStringUTF8(bname, bname.length);
582 } else {
583 e.name = zc.toString(bname, bname.length);
584 }
585 }
586 e.xdostime = getEntryTime(jzentry);
587 e.crc = getEntryCrc(jzentry);
588 e.size = getEntrySize(jzentry);
589 e.csize = getEntryCSize(jzentry);
590 e.method = getEntryMethod(jzentry);
591 e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false);
592 byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT);
593 if (bcomm == null) {
594 e.comment = null;
595 } else {
596 if (!zc.isUTF8() && (e.flag & EFS) != 0) {
597 e.comment = zc.toStringUTF8(bcomm, bcomm.length);
598 } else {
599 e.comment = zc.toString(bcomm, bcomm.length);
600 }
601 }
602 return e;
603 }
604
605 private static native long getNextEntry(long jzfile, int i);
606
607 /**
608 * Returns the number of entries in the ZIP file.
609 * @return the number of entries in the ZIP file
610 * @throws IllegalStateException if the zip file has been closed
611 */
612 public int size() {
613 ensureOpen();
614 return total;
615 }
616
617 /**
618 * Closes the ZIP file.
619 * <p> Closing this ZIP file will close all of the input streams
620 * previously returned by invocations of the {@link #getInputStream
621 * getInputStream} method.
622 *
623 * @throws IOException if an I/O error has occurred
624 */
625 public void close() throws IOException {
626 if (closeRequested)
627 return;
628 closeRequested = true;
629
630 synchronized (this) {
631 // Close streams, release their inflaters
632 synchronized (streams) {
633 if (false == streams.isEmpty()) {
634 Map<InputStream, Inflater> copy = new HashMap<>(streams);
635 streams.clear();
636 for (Map.Entry<InputStream, Inflater> e : copy.entrySet()) {
637 e.getKey().close();
638 Inflater inf = e.getValue();
639 if (inf != null) {
640 inf.end();
641 }
642 }
643 }
644 }
645
646 // Release cached inflaters
647 Inflater inf;
648 synchronized (inflaterCache) {
649 while (null != (inf = inflaterCache.poll())) {
650 inf.end();
651 }
652 }
653
654 if (jzfile != 0) {
655 // Close the zip file
656 long zf = this.jzfile;
657 jzfile = 0;
658
659 close(zf);
660 }
661 }
662 }
663
664 /**
665 * Ensures that the system resources held by this ZipFile object are
666 * released when there are no more references to it.
667 *
668 * <p>
669 * Since the time when GC would invoke this method is undetermined,
670 * it is strongly recommended that applications invoke the <code>close</code>
671 * method as soon they have finished accessing this <code>ZipFile</code>.
672 * This will prevent holding up system resources for an undetermined
673 * length of time.
674 *
675 * @throws IOException if an I/O error has occurred
676 * @see java.util.zip.ZipFile#close()
677 */
678 protected void finalize() throws IOException {
679 close();
680 }
681
682 private static native void close(long jzfile);
683
684 private void ensureOpen() {
685 if (closeRequested) {
686 throw new IllegalStateException("zip file closed");
687 }
688
689 if (jzfile == 0) {
690 throw new IllegalStateException("The object is not initialized.");
691 }
692 }
693
694 private void ensureOpenOrZipException() throws IOException {
695 if (closeRequested) {
696 throw new ZipException("ZipFile closed");
697 }
698 }
699
700 /*
701 * Inner class implementing the input stream used to read a
702 * (possibly compressed) zip file entry.
703 */
704 private class ZipFileInputStream extends InputStream {
705 private volatile boolean zfisCloseRequested = false;
706 protected long jzentry; // address of jzentry data
707 private long pos; // current position within entry data
708 protected long rem; // number of remaining bytes within entry
709 protected long size; // uncompressed size of this entry
710
711 ZipFileInputStream(long jzentry) {
712 pos = 0;
713 rem = getEntryCSize(jzentry);
714 size = getEntrySize(jzentry);
715 this.jzentry = jzentry;
716 }
717
718 public int read(byte b[], int off, int len) throws IOException {
719 synchronized (ZipFile.this) {
720 long rem = this.rem;
721 long pos = this.pos;
722 if (rem == 0) {
723 return -1;
724 }
725 if (len <= 0) {
726 return 0;
727 }
728 if (len > rem) {
729 len = (int) rem;
730 }
731
732 // Check if ZipFile open
733 ensureOpenOrZipException();
734 len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b,
735 off, len);
736 if (len > 0) {
737 this.pos = (pos + len);
738 this.rem = (rem - len);
739 }
740 }
741 if (rem == 0) {
742 close();
743 }
744 return len;
745 }
746
747 public int read() throws IOException {
748 byte[] b = new byte[1];
749 if (read(b, 0, 1) == 1) {
750 return b[0] & 0xff;
751 } else {
752 return -1;
753 }
754 }
755
756 public long skip(long n) {
757 if (n > rem)
758 n = rem;
759 pos += n;
760 rem -= n;
761 if (rem == 0) {
762 close();
763 }
764 return n;
765 }
766
767 public int available() {
768 return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
769 }
770
771 public long size() {
772 return size;
773 }
774
775 public void close() {
776 if (zfisCloseRequested)
777 return;
778 zfisCloseRequested = true;
779
780 rem = 0;
781 synchronized (ZipFile.this) {
782 if (jzentry != 0 && ZipFile.this.jzfile != 0) {
783 freeEntry(ZipFile.this.jzfile, jzentry);
784 jzentry = 0;
785 }
786 }
787 synchronized (streams) {
788 streams.remove(this);
789 }
790 }
791
792 protected void finalize() {
793 close();
794 }
795 }
796
797 static {
798 sun.misc.SharedSecrets.setJavaUtilZipFileAccess(
799 new sun.misc.JavaUtilZipFileAccess() {
800 public boolean startsWithLocHeader(ZipFile zip) {
801 return zip.startsWithLocHeader();
802 }
803 }
804 );
805 }
806
807 /**
808 * Returns {@code true} if, and only if, the zip file begins with {@code
809 * LOCSIG}.
810 */
811 private boolean startsWithLocHeader() {
812 return locsig;
813 }
814
815 private static native long open(String name, int mode, long lastModified,
816 boolean usemmap) throws IOException;
817 private static native int getTotal(long jzfile);
818 private static native boolean startsWithLOC(long jzfile);
819 private static native int read(long jzfile, long jzentry,
820 long pos, byte[] b, int off, int len);
821
822 // access to the native zentry object
823 private static native long getEntryTime(long jzentry);
824 private static native long getEntryCrc(long jzentry);
825 private static native long getEntryCSize(long jzentry);
826 private static native long getEntrySize(long jzentry);
827 private static native int getEntryMethod(long jzentry);
828 private static native int getEntryFlag(long jzentry);
829 private static native byte[] getCommentBytes(long jzfile);
830
831 private static final int JZENTRY_NAME = 0;
832 private static final int JZENTRY_EXTRA = 1;
833 private static final int JZENTRY_COMMENT = 2;
834 private static native byte[] getEntryBytes(long jzentry, int type);
835
836 private static native String getZipMessage(long jzfile);
837 }
838