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 trueif, 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
Powered by JavaMelody