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